Merge branch 'master' into poljar

This commit is contained in:
Hubert Chathi 2018-10-12 17:02:51 -04:00
commit 5cf074d337
33 changed files with 562 additions and 210 deletions

27
.circleci/config.yml Normal file
View file

@ -0,0 +1,27 @@
version: 2
jobs:
build:
docker:
- image: trzeci/emscripten
working_directory: ~/repo
steps:
- checkout
- run:
name: Native Compile
command: make
- run:
name: Native Tests
command: make test
- run:
name: JS Compile
command: make js
- run:
name: Install JS Deps
working_directory: ~/repo/javascript
command: npm install
- run:
name: JS Tests
working_directory: ~/repo/javascript
command: npm run test

View file

@ -1,3 +1,15 @@
Changes in latest release
BREAKING CHANGE: Olm now uses WebAssembly which means it needs
to load the wasm file asynchronously, and therefore needs to be
started up asynchronously. The imported module now has an init()
method which returns a promise. The library cannot be used until
this promise resolves. It will reject if the library fails to start.
olm_pk_generate_key() and olm_pk_generate_key_random_length() have
been removed: to generate a random key, use olm_pk_key_from_private()
with random bytes as the private key.
Changes in `2.3.0 <http://matrix.org/git/olm/commit/?h=2.3.0>`_ Changes in `2.3.0 <http://matrix.org/git/olm/commit/?h=2.3.0>`_
This release includes the following changes since 2.2.2: This release includes the following changes since 2.2.2:

View file

@ -14,12 +14,25 @@ AFL_CC = afl-gcc
AFL_CXX = afl-g++ AFL_CXX = afl-g++
AR = ar AR = ar
RELEASE_TARGET := $(BUILD_DIR)/libolm.so.$(VERSION) UNAME := $(shell uname)
ifeq ($(UNAME),Darwin)
SO := dylib
OLM_LDFLAGS :=
else
SO := so
OLM_LDFLAGS := -Wl,-soname,libolm.so.$(MAJOR) \
-Wl,--version-script,version_script.ver
endif
RELEASE_TARGET := $(BUILD_DIR)/libolm.$(SO).$(VERSION)
STATIC_RELEASE_TARGET := $(BUILD_DIR)/libolm.a STATIC_RELEASE_TARGET := $(BUILD_DIR)/libolm.a
DEBUG_TARGET := $(BUILD_DIR)/libolm_debug.so.$(VERSION) DEBUG_TARGET := $(BUILD_DIR)/libolm_debug.$(SO).$(VERSION)
JS_TARGET := javascript/olm.js JS_WASM_TARGET := javascript/olm.js
JS_ASMJS_TARGET := javascript/olm_legacy.js
JS_EXPORTED_FUNCTIONS := javascript/exported_functions.json JS_EXPORTED_FUNCTIONS := javascript/exported_functions.json
JS_EXTRA_EXPORTED_RUNTIME_METHODS := ALLOC_STACK
JS_EXTERNS := javascript/externs.js
PUBLIC_HEADERS := include/olm/olm.h include/olm/outbound_group_session.h include/olm/inbound_group_session.h include/olm/pk.h PUBLIC_HEADERS := include/olm/olm.h include/olm/outbound_group_session.h include/olm/inbound_group_session.h include/olm/pk.h
@ -39,11 +52,22 @@ FUZZER_BINARIES := $(addprefix $(BUILD_DIR)/,$(basename $(FUZZER_SOURCES)))
FUZZER_DEBUG_BINARIES := $(patsubst $(BUILD_DIR)/fuzzers/fuzz_%,$(BUILD_DIR)/fuzzers/debug_%,$(FUZZER_BINARIES)) FUZZER_DEBUG_BINARIES := $(patsubst $(BUILD_DIR)/fuzzers/fuzz_%,$(BUILD_DIR)/fuzzers/debug_%,$(FUZZER_BINARIES))
TEST_BINARIES := $(patsubst tests/%,$(BUILD_DIR)/tests/%,$(basename $(TEST_SOURCES))) TEST_BINARIES := $(patsubst tests/%,$(BUILD_DIR)/tests/%,$(basename $(TEST_SOURCES)))
JS_OBJECTS := $(addprefix $(BUILD_DIR)/javascript/,$(OBJECTS)) JS_OBJECTS := $(addprefix $(BUILD_DIR)/javascript/,$(OBJECTS))
# pre & post are the js-pre/js-post options to emcc.
# They are injected inside the modularised code and
# processed by the optimiser.
JS_PRE := $(wildcard javascript/*pre.js) JS_PRE := $(wildcard javascript/*pre.js)
JS_POST := javascript/olm_outbound_group_session.js \ JS_POST := javascript/olm_outbound_group_session.js \
javascript/olm_inbound_group_session.js \ javascript/olm_inbound_group_session.js \
javascript/olm_pk.js \ javascript/olm_pk.js \
javascript/olm_post.js javascript/olm_post.js
# The prefix & suffix are just added onto the start & end
# of what comes out emcc, so are outside of the modularised
# code and not seen by the opimiser.
JS_PREFIX := javascript/olm_prefix.js
JS_SUFFIX := javascript/olm_suffix.js
DOCS := tracing/README.html \ DOCS := tracing/README.html \
docs/megolm.html \ docs/megolm.html \
docs/olm.html \ docs/olm.html \
@ -60,11 +84,23 @@ CFLAGS += -Wall -Werror -std=c99 -fPIC
CXXFLAGS += -Wall -Werror -std=c++11 -fPIC CXXFLAGS += -Wall -Werror -std=c++11 -fPIC
LDFLAGS += -Wall -Werror LDFLAGS += -Wall -Werror
EMCCFLAGS = --closure 1 --memory-init-file 0 -s NO_FILESYSTEM=1 -s INVOKE_RUN=0 EMCCFLAGS = --closure 1 --memory-init-file 0 -s NO_FILESYSTEM=1 -s INVOKE_RUN=0 -s MODULARIZE=1
# NO_BROWSER is kept for compatibility with emscripten 1.35.24, but is no # NO_BROWSER is kept for compatibility with emscripten 1.35.24, but is no
# longer needed. # longer needed.
EMCCFLAGS += -s NO_BROWSER=1 EMCCFLAGS += -s NO_BROWSER=1
# Olm generally doesn't need a lot of memory to encrypt / decrypt its usual
# payloads (ie. Matrix messages), but we do need about 128K of heap to encrypt
# a 64K event (enough to store the ciphertext and the plaintext, bearing in
# mind that the plaintext can only be 48K because base64). We also have about
# 36K of statics. So let's have 256K of memory.
# (This can't be changed by the app with wasm since it's baked into the wasm).
# (emscripten also mandates at least 16MB of memory for asm.js now, so
# we don't use this for the legacy build.)
EMCCFLAGS_WASM += -s TOTAL_STACK=65536 -s TOTAL_MEMORY=262144
EMCCFLAGS_ASMJS += -s WASM=0
EMCC.c = $(EMCC) $(CFLAGS) $(CPPFLAGS) -c EMCC.c = $(EMCC) $(CFLAGS) $(CPPFLAGS) -c
EMCC.cc = $(EMCC) $(CXXFLAGS) $(CPPFLAGS) -c EMCC.cc = $(EMCC) $(CXXFLAGS) $(CPPFLAGS) -c
EMCC_LINK = $(EMCC) $(LDFLAGS) $(EMCCFLAGS) EMCC_LINK = $(EMCC) $(LDFLAGS) $(EMCCFLAGS)
@ -99,7 +135,8 @@ $(FUZZER_DEBUG_BINARIES): LDFLAGS += $(DEBUG_OPTIMIZE_FLAGS)
$(JS_OBJECTS): CFLAGS += $(JS_OPTIMIZE_FLAGS) $(JS_OBJECTS): CFLAGS += $(JS_OPTIMIZE_FLAGS)
$(JS_OBJECTS): CXXFLAGS += $(JS_OPTIMIZE_FLAGS) $(JS_OBJECTS): CXXFLAGS += $(JS_OPTIMIZE_FLAGS)
$(JS_TARGET): LDFLAGS += $(JS_OPTIMIZE_FLAGS) $(JS_WASM_TARGET): LDFLAGS += $(JS_OPTIMIZE_FLAGS)
$(JS_ASMJS_TARGET): LDFLAGS += $(JS_OPTIMIZE_FLAGS)
### Fix to make mkdir work on windows and linux ### Fix to make mkdir work on windows and linux
ifeq ($(shell echo "check_quotes"),"check_quotes") ifeq ($(shell echo "check_quotes"),"check_quotes")
@ -121,21 +158,19 @@ lib: $(RELEASE_TARGET)
$(RELEASE_TARGET): $(RELEASE_OBJECTS) $(RELEASE_TARGET): $(RELEASE_OBJECTS)
$(CXX) $(LDFLAGS) --shared -fPIC \ $(CXX) $(LDFLAGS) --shared -fPIC \
-Wl,-soname,libolm.so.$(MAJOR) \ $(OLM_LDFLAGS) \
-Wl,--version-script,version_script.ver \
$(OUTPUT_OPTION) $(RELEASE_OBJECTS) $(OUTPUT_OPTION) $(RELEASE_OBJECTS)
ln -sf libolm.so.$(VERSION) $(BUILD_DIR)/libolm.so.$(MAJOR) ln -sf libolm.$(SO).$(VERSION) $(BUILD_DIR)/libolm.$(SO).$(MAJOR)
ln -sf libolm.so.$(VERSION) $(BUILD_DIR)/libolm.so ln -sf libolm.$(SO).$(VERSION) $(BUILD_DIR)/libolm.$(SO)
debug: $(DEBUG_TARGET) debug: $(DEBUG_TARGET)
.PHONY: debug .PHONY: debug
$(DEBUG_TARGET): $(DEBUG_OBJECTS) $(DEBUG_TARGET): $(DEBUG_OBJECTS)
$(CXX) $(LDFLAGS) --shared -fPIC \ $(CXX) $(LDFLAGS) --shared -fPIC \
-Wl,-soname,libolm_debug.so.$(MAJOR) \ $(OLM_LDFLAGS) \
-Wl,--version-script,version_script.ver \
$(OUTPUT_OPTION) $(DEBUG_OBJECTS) $(OUTPUT_OPTION) $(DEBUG_OBJECTS)
ln -sf libolm_debug.so.$(VERSION) $(BUILD_DIR)/libolm_debug.so.$(MAJOR) ln -sf libolm_debug.$(SO).$(VERSION) $(BUILD_DIR)/libolm_debug.$(SO).$(MAJOR)
static: $(STATIC_RELEASE_TARGET) static: $(STATIC_RELEASE_TARGET)
.PHONY: static .PHONY: static
@ -143,15 +178,35 @@ static: $(STATIC_RELEASE_TARGET)
$(STATIC_RELEASE_TARGET): $(RELEASE_OBJECTS) $(STATIC_RELEASE_TARGET): $(RELEASE_OBJECTS)
$(AR) rcs $@ $^ $(AR) rcs $@ $^
js: $(JS_TARGET) js: $(JS_WASM_TARGET) $(JS_ASMJS_TARGET)
.PHONY: js .PHONY: js
$(JS_TARGET): $(JS_OBJECTS) $(JS_PRE) $(JS_POST) $(JS_EXPORTED_FUNCTIONS) # Note that the output file we give to emcc determines the name of the
$(EMCC_LINK) \ # wasm file baked into the js, hence messing around outputting to olm.js
# and then renaming it.
$(JS_WASM_TARGET): $(JS_OBJECTS) $(JS_PRE) $(JS_POST) $(JS_EXPORTED_FUNCTIONS) $(JS_PREFIX) $(JS_SUFFIX)
EMCC_CLOSURE_ARGS="--externs $(JS_EXTERNS)" $(EMCC_LINK) \
$(EMCCFLAGS_WASM) \
$(foreach f,$(JS_PRE),--pre-js $(f)) \ $(foreach f,$(JS_PRE),--pre-js $(f)) \
$(foreach f,$(JS_POST),--post-js $(f)) \ $(foreach f,$(JS_POST),--post-js $(f)) \
-s "EXPORTED_FUNCTIONS=@$(JS_EXPORTED_FUNCTIONS)" \ -s "EXPORTED_FUNCTIONS=@$(JS_EXPORTED_FUNCTIONS)" \
-s "EXTRA_EXPORTED_RUNTIME_METHODS=$(JS_EXTRA_EXPORTED_RUNTIME_METHODS)" \
$(JS_OBJECTS) -o $@ $(JS_OBJECTS) -o $@
mv $@ javascript/olmtmp.js
cat $(JS_PREFIX) javascript/olmtmp.js $(JS_SUFFIX) > $@
rm javascript/olmtmp.js
$(JS_ASMJS_TARGET): $(JS_OBJECTS) $(JS_PRE) $(JS_POST) $(JS_EXPORTED_FUNCTIONS) $(JS_PREFIX) $(JS_SUFFIX)
EMCC_CLOSURE_ARGS="--externs $(JS_EXTERNS)" $(EMCC_LINK) \
$(EMCCFLAGS_ASMJS) \
$(foreach f,$(JS_PRE),--pre-js $(f)) \
$(foreach f,$(JS_POST),--post-js $(f)) \
-s "EXPORTED_FUNCTIONS=@$(JS_EXPORTED_FUNCTIONS)" \
-s "EXTRA_EXPORTED_RUNTIME_METHODS=$(JS_EXTRA_EXPORTED_RUNTIME_METHODS)" \
$(JS_OBJECTS) -o $@
mv $@ javascript/olmtmp.js
cat $(JS_PREFIX) javascript/olmtmp.js $(JS_SUFFIX) > $@
rm javascript/olmtmp.js
build_tests: $(TEST_BINARIES) build_tests: $(TEST_BINARIES)
@ -165,7 +220,7 @@ fuzzers: $(FUZZER_BINARIES) $(FUZZER_DEBUG_BINARIES)
.PHONY: fuzzers .PHONY: fuzzers
$(JS_EXPORTED_FUNCTIONS): $(PUBLIC_HEADERS) $(JS_EXPORTED_FUNCTIONS): $(PUBLIC_HEADERS)
perl -MJSON -ne '$$f{"_$$1"}=1 if /(olm_[^( ]*)\(/; END { @f=sort keys %f; print encode_json \@f }' $^ > $@.tmp ./exports.py $^ > $@.tmp
mv $@.tmp $@ mv $@.tmp $@
all: test js lib debug doc all: test js lib debug doc
@ -178,16 +233,16 @@ install-headers: $(PUBLIC_HEADERS)
install-debug: debug install-headers install-debug: debug install-headers
test -d $(DESTDIR)$(PREFIX)/lib || $(call mkdir,$(DESTDIR)$(PREFIX)/lib) test -d $(DESTDIR)$(PREFIX)/lib || $(call mkdir,$(DESTDIR)$(PREFIX)/lib)
install -Dm755 $(DEBUG_TARGET) $(DESTDIR)$(PREFIX)/lib/libolm_debug.so.$(VERSION) install -Dm755 $(DEBUG_TARGET) $(DESTDIR)$(PREFIX)/lib/libolm_debug.$(SO).$(VERSION)
ln -s libolm_debug.so.$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm_debug.so.$(MAJOR) ln -s libolm_debug.$(SO).$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm_debug.$(SO).$(MAJOR)
ln -s libolm_debug.so.$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm_debug.so ln -s libolm_debug.$(SO).$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm_debug.$(SO)
.PHONY: install-debug .PHONY: install-debug
install: lib install-headers install: lib install-headers
test -d $(DESTDIR)$(PREFIX)/lib || $(call mkdir,$(DESTDIR)$(PREFIX)/lib) test -d $(DESTDIR)$(PREFIX)/lib || $(call mkdir,$(DESTDIR)$(PREFIX)/lib)
install -Dm755 $(RELEASE_TARGET) $(DESTDIR)$(PREFIX)/lib/libolm.so.$(VERSION) install -Dm755 $(RELEASE_TARGET) $(DESTDIR)$(PREFIX)/lib/libolm.$(SO).$(VERSION)
ln -s libolm.so.$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm.so.$(MAJOR) ln -s libolm.$(SO).$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm.$(SO).$(MAJOR)
ln -s libolm.so.$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm.so ln -s libolm.$(SO).$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm.$(SO)
.PHONY: install .PHONY: install
clean:; clean:;

View file

@ -31,6 +31,9 @@ To build the javascript bindings, install emscripten from http://kripken.github.
make js make js
Note that if you run emscripten in a docker container, you need to pass through
the EMCC_CLOSURE_ARGS environment variable.
To build the android project for Android bindings, run: To build the android project for Android bindings, run:
.. code:: bash .. code:: bash

View file

@ -25,6 +25,8 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.io.Serializable; import java.io.Serializable;
import java.util.Arrays;
/** /**
* Class used to create an inbound <a href="http://matrix.org/docs/guides/e2e_implementation.html#handling-an-m-room-key-event">Megolm session</a>.<br> * Class used to create an inbound <a href="http://matrix.org/docs/guides/e2e_implementation.html#handling-an-m-room-key-event">Megolm session</a>.<br>
* Counter part of the outbound group session {@link OlmOutboundGroupSession}, this class decrypts the messages sent by the outbound side. * Counter part of the outbound group session {@link OlmOutboundGroupSession}, this class decrypts the messages sent by the outbound side.
@ -236,7 +238,7 @@ public class OlmInboundGroupSession extends CommonSerializeUtils implements Seri
* In case of error, null is returned and an error message description is provided in aErrorMsg. * In case of error, null is returned and an error message description is provided in aErrorMsg.
* @param aEncryptedMsg the message to be decrypted * @param aEncryptedMsg the message to be decrypted
* @return the decrypted message information * @return the decrypted message information
* @exception OlmException teh failure reason * @exception OlmException the failure reason
*/ */
public DecryptMessageResult decryptMessage(String aEncryptedMsg) throws OlmException { public DecryptMessageResult decryptMessage(String aEncryptedMsg) throws OlmException {
DecryptMessageResult result = new DecryptMessageResult(); DecryptMessageResult result = new DecryptMessageResult();
@ -246,6 +248,7 @@ public class OlmInboundGroupSession extends CommonSerializeUtils implements Seri
if (null != decryptedMessageBuffer) { if (null != decryptedMessageBuffer) {
result.mDecryptedMessage = new String(decryptedMessageBuffer, "UTF-8"); result.mDecryptedMessage = new String(decryptedMessageBuffer, "UTF-8");
Arrays.fill(decryptedMessageBuffer, (byte) 0);
} }
} catch (Exception e) { } catch (Exception e) {
Log.e(LOG_TAG, "## decryptMessage() failed " + e.getMessage()); Log.e(LOG_TAG, "## decryptMessage() failed " + e.getMessage());

View file

@ -26,6 +26,8 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.io.Serializable; import java.io.Serializable;
import java.util.Arrays;
/** /**
* Class used to create an outbound a <a href="http://matrix.org/docs/guides/e2e_implementation.html#starting-a-megolm-session">Megolm session</a>.<br> * Class used to create an outbound a <a href="http://matrix.org/docs/guides/e2e_implementation.html#starting-a-megolm-session">Megolm session</a>.<br>
* To send a first message in an encrypted room, the client should start a new outbound Megolm session. * To send a first message in an encrypted room, the client should start a new outbound Megolm session.
@ -166,7 +168,9 @@ public class OlmOutboundGroupSession extends CommonSerializeUtils implements Ser
if (!TextUtils.isEmpty(aClearMsg)) { if (!TextUtils.isEmpty(aClearMsg)) {
try { try {
byte[] encryptedBuffer = encryptMessageJni(aClearMsg.getBytes("UTF-8")); byte[] clearMsgBuffer = aClearMsg.getBytes("UTF-8");
byte[] encryptedBuffer = encryptMessageJni(clearMsgBuffer);
Arrays.fill(clearMsgBuffer, (byte) 0);
if (null != encryptedBuffer) { if (null != encryptedBuffer) {
retValue = new String(encryptedBuffer , "UTF-8"); retValue = new String(encryptedBuffer , "UTF-8");

View file

@ -18,6 +18,8 @@ package org.matrix.olm;
import android.util.Log; import android.util.Log;
import java.util.Arrays;
public class OlmPkDecryption { public class OlmPkDecryption {
private static final String LOG_TAG = "OlmPkDecryption"; private static final String LOG_TAG = "OlmPkDecryption";
@ -67,7 +69,10 @@ public class OlmPkDecryption {
} }
try { try {
return new String(decryptJni(aMessage), "UTF-8"); byte[] plaintextBuffer = decryptJni(aMessage);
String plaintext = new String(plaintextBuffer, "UTF-8");
Arrays.fill(plaintextBuffer, (byte) 0);
return plaintext;
} catch (Exception e) { } catch (Exception e) {
Log.e(LOG_TAG, "## pkDecrypt(): failed " + e.getMessage()); Log.e(LOG_TAG, "## pkDecrypt(): failed " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_PK_DECRYPTION_DECRYPT, e.getMessage()); throw new OlmException(OlmException.EXCEPTION_CODE_PK_DECRYPTION_DECRYPT, e.getMessage());

View file

@ -18,6 +18,8 @@ package org.matrix.olm;
import android.util.Log; import android.util.Log;
import java.util.Arrays;
public class OlmPkEncryption { public class OlmPkEncryption {
private static final String LOG_TAG = "OlmPkEncryption"; private static final String LOG_TAG = "OlmPkEncryption";
@ -72,7 +74,9 @@ public class OlmPkEncryption {
OlmPkMessage encryptedMsgRetValue = new OlmPkMessage(); OlmPkMessage encryptedMsgRetValue = new OlmPkMessage();
try { try {
byte[] ciphertextBuffer = encryptJni(aPlaintext.getBytes("UTF-8"), encryptedMsgRetValue); byte[] plaintextBuffer = aPlaintext.getBytes("UTF-8");
byte[] ciphertextBuffer = encryptJni(plaintextBuffer, encryptedMsgRetValue);
Arrays.fill(plaintextBuffer, (byte) 0);
if (null != ciphertextBuffer) { if (null != ciphertextBuffer) {
encryptedMsgRetValue.mCipherText = new String(ciphertextBuffer, "UTF-8"); encryptedMsgRetValue.mCipherText = new String(ciphertextBuffer, "UTF-8");

View file

@ -25,6 +25,8 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.io.Serializable; import java.io.Serializable;
import java.util.Arrays;
/** /**
* Session class used to create Olm sessions in conjunction with {@link OlmAccount} class.<br> * Session class used to create Olm sessions in conjunction with {@link OlmAccount} class.<br>
* Olm session is used to encrypt data between devices, especially to create Olm group sessions (see {@link OlmOutboundGroupSession} and {@link OlmInboundGroupSession}).<br> * Olm session is used to encrypt data between devices, especially to create Olm group sessions (see {@link OlmOutboundGroupSession} and {@link OlmInboundGroupSession}).<br>
@ -295,7 +297,9 @@ public class OlmSession extends CommonSerializeUtils implements Serializable {
OlmMessage encryptedMsgRetValue = new OlmMessage(); OlmMessage encryptedMsgRetValue = new OlmMessage();
try { try {
byte[] encryptedMessageBuffer = encryptMessageJni(aClearMsg.getBytes("UTF-8"), encryptedMsgRetValue); byte[] clearMsgBuffer = aClearMsg.getBytes("UTF-8");
byte[] encryptedMessageBuffer = encryptMessageJni(clearMsgBuffer, encryptedMsgRetValue);
Arrays.fill(clearMsgBuffer, (byte) 0);
if (null != encryptedMessageBuffer) { if (null != encryptedMessageBuffer) {
encryptedMsgRetValue.mCipherText = new String(encryptedMessageBuffer, "UTF-8"); encryptedMsgRetValue.mCipherText = new String(encryptedMessageBuffer, "UTF-8");
@ -330,7 +334,10 @@ public class OlmSession extends CommonSerializeUtils implements Serializable {
} }
try { try {
return new String(decryptMessageJni(aEncryptedMsg), "UTF-8"); byte[] plaintextBuffer = decryptMessageJni(aEncryptedMsg);
String plaintext = new String(plaintextBuffer, "UTF-8");
Arrays.fill(plaintextBuffer, (byte) 0);
return plaintext;
} catch (Exception e) { } catch (Exception e) {
Log.e(LOG_TAG, "## decryptMessage(): failed " + e.getMessage()); Log.e(LOG_TAG, "## decryptMessage(): failed " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_DECRYPT_MESSAGE, e.getMessage()); throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_DECRYPT_MESSAGE, e.getMessage());

View file

@ -297,6 +297,7 @@ JNIEXPORT jbyteArray OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(encryptMessageJni)(JNIE
OlmOutboundGroupSession *sessionPtr = NULL; OlmOutboundGroupSession *sessionPtr = NULL;
jbyte* clearMsgPtr = NULL; jbyte* clearMsgPtr = NULL;
jboolean clearMsgIsCopied = JNI_FALSE;
if (!(sessionPtr = (OlmOutboundGroupSession*)getOutboundGroupSessionInstanceId(env,thiz))) if (!(sessionPtr = (OlmOutboundGroupSession*)getOutboundGroupSessionInstanceId(env,thiz)))
{ {
@ -308,7 +309,7 @@ JNIEXPORT jbyteArray OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(encryptMessageJni)(JNIE
LOGE(" ## encryptMessageJni(): failure - invalid clear message"); LOGE(" ## encryptMessageJni(): failure - invalid clear message");
errorMessage = "invalid clear message"; errorMessage = "invalid clear message";
} }
else if (!(clearMsgPtr = env->GetByteArrayElements(aClearMsgBuffer, NULL))) else if (!(clearMsgPtr = env->GetByteArrayElements(aClearMsgBuffer, &clearMsgIsCopied)))
{ {
LOGE(" ## encryptMessageJni(): failure - clear message JNI allocation OOM"); LOGE(" ## encryptMessageJni(): failure - clear message JNI allocation OOM");
errorMessage = "clear message JNI allocation OOM"; errorMessage = "clear message JNI allocation OOM";
@ -359,6 +360,10 @@ JNIEXPORT jbyteArray OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(encryptMessageJni)(JNIE
// free alloc // free alloc
if (clearMsgPtr) if (clearMsgPtr)
{ {
if (clearMsgIsCopied)
{
memset(clearMsgPtr, 0, (size_t)env->GetArrayLength(aClearMsgBuffer));
}
env->ReleaseByteArrayElements(aClearMsgBuffer, clearMsgPtr, JNI_ABORT); env->ReleaseByteArrayElements(aClearMsgBuffer, clearMsgPtr, JNI_ABORT);
} }

View file

@ -29,7 +29,10 @@ OlmPkEncryption * initializePkEncryptionMemory()
{ {
// init encryption object // init encryption object
encryptionPtr = olm_pk_encryption(encryptionPtr); encryptionPtr = olm_pk_encryption(encryptionPtr);
LOGD("## initializePkEncryptionMemory(): success - OLM encryption size=%lu",static_cast<long unsigned int>(encryptionSize)); LOGD(
"## initializePkEncryptionMemory(): success - OLM encryption size=%lu",
static_cast<long unsigned int>(encryptionSize)
);
} }
else else
{ {
@ -53,7 +56,10 @@ JNIEXPORT jlong OLM_PK_ENCRYPTION_FUNC_DEF(createNewPkEncryptionJni)(JNIEnv *env
else else
{ {
LOGD("## createNewPkEncryptionJni(): success - OLM encryption created"); LOGD("## createNewPkEncryptionJni(): success - OLM encryption created");
LOGD("## createNewPkEncryptionJni(): encryptionPtr=%p (jlong)(intptr_t)encryptionPtr=%lld", encryptionPtr, (jlong)(intptr_t)encryptionPtr); LOGD(
"## createNewPkEncryptionJni(): encryptionPtr=%p (jlong)(intptr_t)encryptionPtr=%lld",
encryptionPtr, (jlong)(intptr_t)encryptionPtr
);
} }
if (errorMessage) if (errorMessage)
@ -93,8 +99,9 @@ JNIEXPORT void OLM_PK_ENCRYPTION_FUNC_DEF(releasePkEncryptionJni)(JNIEnv *env, j
} }
} }
JNIEXPORT void OLM_PK_ENCRYPTION_FUNC_DEF(setRecipientKeyJni)(JNIEnv *env, jobject thiz, jbyteArray aKeyBuffer) JNIEXPORT void OLM_PK_ENCRYPTION_FUNC_DEF(setRecipientKeyJni)(
{ JNIEnv *env, jobject thiz, jbyteArray aKeyBuffer
) {
const char *errorMessage = NULL; const char *errorMessage = NULL;
jbyte *keyPtr = NULL; jbyte *keyPtr = NULL;
@ -116,10 +123,13 @@ JNIEXPORT void OLM_PK_ENCRYPTION_FUNC_DEF(setRecipientKeyJni)(JNIEnv *env, jobje
} }
else else
{ {
if(olm_pk_encryption_set_recipient_key(encryptionPtr, keyPtr, (size_t)env->GetArrayLength(aKeyBuffer)) == olm_error()) if (olm_pk_encryption_set_recipient_key(encryptionPtr, keyPtr, (size_t)env->GetArrayLength(aKeyBuffer)) == olm_error())
{ {
errorMessage = olm_pk_encryption_last_error(encryptionPtr); errorMessage = olm_pk_encryption_last_error(encryptionPtr);
LOGE(" ## pkSetRecipientKeyJni(): failure - olm_pk_encryption_set_recipient_key Msg=%s", errorMessage); LOGE(
" ## pkSetRecipientKeyJni(): failure - olm_pk_encryption_set_recipient_key Msg=%s",
errorMessage
);
} }
} }
@ -134,11 +144,13 @@ JNIEXPORT void OLM_PK_ENCRYPTION_FUNC_DEF(setRecipientKeyJni)(JNIEnv *env, jobje
} }
} }
JNIEXPORT jbyteArray OLM_PK_ENCRYPTION_FUNC_DEF(encryptJni)(JNIEnv *env, jobject thiz, jbyteArray aPlaintextBuffer, jobject aEncryptedMsg) JNIEXPORT jbyteArray OLM_PK_ENCRYPTION_FUNC_DEF(encryptJni)(
{ JNIEnv *env, jobject thiz, jbyteArray aPlaintextBuffer, jobject aEncryptedMsg
) {
jbyteArray encryptedMsgRet = 0; jbyteArray encryptedMsgRet = 0;
const char* errorMessage = NULL; const char* errorMessage = NULL;
jbyte *plaintextPtr = NULL; jbyte *plaintextPtr = NULL;
jboolean plaintextIsCopied = JNI_FALSE;
OlmPkEncryption *encryptionPtr = getPkEncryptionInstanceId(env, thiz); OlmPkEncryption *encryptionPtr = getPkEncryptionInstanceId(env, thiz);
jclass encryptedMsgJClass = 0; jclass encryptedMsgJClass = 0;
@ -154,15 +166,15 @@ JNIEXPORT jbyteArray OLM_PK_ENCRYPTION_FUNC_DEF(encryptJni)(JNIEnv *env, jobject
LOGE(" ## pkEncryptJni(): failure - invalid clear message"); LOGE(" ## pkEncryptJni(): failure - invalid clear message");
errorMessage = "invalid clear message"; errorMessage = "invalid clear message";
} }
else if (!(plaintextPtr = env->GetByteArrayElements(aPlaintextBuffer, 0))) else if (!(plaintextPtr = env->GetByteArrayElements(aPlaintextBuffer, &plaintextIsCopied)))
{ {
LOGE(" ## pkEncryptJni(): failure - plaintext JNI allocation OOM"); LOGE(" ## pkEncryptJni(): failure - plaintext JNI allocation OOM");
errorMessage = "plaintext JNI allocation OOM"; errorMessage = "plaintext JNI allocation OOM";
} }
else if (!(encryptedMsgJClass = env->GetObjectClass(aEncryptedMsg))) else if (!(encryptedMsgJClass = env->GetObjectClass(aEncryptedMsg)))
{ {
LOGE(" ## pkEncryptJni(): failure - unable to get crypted message class"); LOGE(" ## pkEncryptJni(): failure - unable to get encrypted message class");
errorMessage = "unable to get crypted message class"; errorMessage = "unable to get encrypted message class";
} }
else if (!(macFieldId = env->GetFieldID(encryptedMsgJClass, "mMac", "Ljava/lang/String;"))) else if (!(macFieldId = env->GetFieldID(encryptedMsgJClass, "mMac", "Ljava/lang/String;")))
{ {
@ -226,7 +238,9 @@ JNIEXPORT jbyteArray OLM_PK_ENCRYPTION_FUNC_DEF(encryptJni)(JNIEnv *env, jobject
else else
{ {
encryptedMsgRet = env->NewByteArray(ciphertextLength); encryptedMsgRet = env->NewByteArray(ciphertextLength);
env->SetByteArrayRegion(encryptedMsgRet, 0, ciphertextLength, (jbyte*)ciphertextPtr); env->SetByteArrayRegion(
encryptedMsgRet, 0, ciphertextLength, (jbyte*)ciphertextPtr
);
jstring macStr = env->NewStringUTF((char*)macPtr); jstring macStr = env->NewStringUTF((char*)macPtr);
env->SetObjectField(aEncryptedMsg, macFieldId, macStr); env->SetObjectField(aEncryptedMsg, macFieldId, macStr);
@ -256,6 +270,10 @@ JNIEXPORT jbyteArray OLM_PK_ENCRYPTION_FUNC_DEF(encryptJni)(JNIEnv *env, jobject
if (plaintextPtr) if (plaintextPtr)
{ {
if (plaintextIsCopied)
{
memset(plaintextPtr, 0, (size_t)env->GetArrayLength(aPlaintextBuffer));
}
env->ReleaseByteArrayElements(aPlaintextBuffer, plaintextPtr, JNI_ABORT); env->ReleaseByteArrayElements(aPlaintextBuffer, plaintextPtr, JNI_ABORT);
} }
@ -276,7 +294,10 @@ OlmPkDecryption * initializePkDecryptionMemory()
{ {
// init decryption object // init decryption object
decryptionPtr = olm_pk_decryption(decryptionPtr); decryptionPtr = olm_pk_decryption(decryptionPtr);
LOGD("## initializePkDecryptionMemory(): success - OLM decryption size=%lu",static_cast<long unsigned int>(decryptionSize)); LOGD(
"## initializePkDecryptionMemory(): success - OLM decryption size=%lu",
static_cast<long unsigned int>(decryptionSize)
);
} }
else else
{ {
@ -300,7 +321,10 @@ JNIEXPORT jlong OLM_PK_DECRYPTION_FUNC_DEF(createNewPkDecryptionJni)(JNIEnv *env
else else
{ {
LOGD("## createNewPkDecryptionJni(): success - OLM decryption created"); LOGD("## createNewPkDecryptionJni(): success - OLM decryption created");
LOGD("## createNewPkDecryptionJni(): decryptionPtr=%p (jlong)(intptr_t)decryptionPtr=%lld", decryptionPtr, (jlong)(intptr_t)decryptionPtr); LOGD(
"## createNewPkDecryptionJni(): decryptionPtr=%p (jlong)(intptr_t)decryptionPtr=%lld",
decryptionPtr, (jlong)(intptr_t)decryptionPtr
);
} }
if (errorMessage) if (errorMessage)
@ -402,8 +426,9 @@ JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(generateKeyJni)(JNIEnv *env, job
return publicKeyRet; return publicKeyRet;
} }
JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(decryptJni)(JNIEnv *env, jobject thiz, jobject aEncryptedMsg) JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(decryptJni)(
{ JNIEnv *env, jobject thiz, jobject aEncryptedMsg
) {
const char* errorMessage = NULL; const char* errorMessage = NULL;
OlmPkDecryption *decryptionPtr = getPkDecryptionInstanceId(env, thiz); OlmPkDecryption *decryptionPtr = getPkDecryptionInstanceId(env, thiz);
@ -528,7 +553,10 @@ JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(decryptJni)(JNIEnv *env, jobject
{ {
decryptedMsgRet = env->NewByteArray(plaintextLength); decryptedMsgRet = env->NewByteArray(plaintextLength);
env->SetByteArrayRegion(decryptedMsgRet, 0, plaintextLength, (jbyte*)plaintextPtr); env->SetByteArrayRegion(decryptedMsgRet, 0, plaintextLength, (jbyte*)plaintextPtr);
LOGD("## pkDecryptJni(): success returnedLg=%lu OK", static_cast<long unsigned int>(plaintextLength)); LOGD(
"## pkDecryptJni(): success returnedLg=%lu OK",
static_cast<long unsigned int>(plaintextLength)
);
} }
} }
@ -538,6 +566,7 @@ JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(decryptJni)(JNIEnv *env, jobject
} }
if (plaintextPtr) if (plaintextPtr)
{ {
memset(plaintextPtr, 0, maxPlaintextLength);
free(plaintextPtr); free(plaintextPtr);
} }
} }

View file

@ -472,6 +472,7 @@ JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobjec
OlmSession *sessionPtr = getSessionInstanceId(env, thiz); OlmSession *sessionPtr = getSessionInstanceId(env, thiz);
jbyte *clearMsgPtr = NULL; jbyte *clearMsgPtr = NULL;
jboolean clearMsgIsCopied = JNI_FALSE;
jclass encryptedMsgJClass = 0; jclass encryptedMsgJClass = 0;
jfieldID typeMsgFieldId; jfieldID typeMsgFieldId;
@ -490,8 +491,9 @@ JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobjec
else if (!aEncryptedMsg) else if (!aEncryptedMsg)
{ {
LOGE("## encryptMessageJni(): failure - invalid encrypted message"); LOGE("## encryptMessageJni(): failure - invalid encrypted message");
errorMessage = "invalid encrypted message";
} }
else if (!(clearMsgPtr = env->GetByteArrayElements(aClearMsgBuffer, 0))) else if (!(clearMsgPtr = env->GetByteArrayElements(aClearMsgBuffer, &clearMsgIsCopied)))
{ {
LOGE("## encryptMessageJni(): failure - clear message JNI allocation OOM"); LOGE("## encryptMessageJni(): failure - clear message JNI allocation OOM");
errorMessage = "clear message JNI allocation OOM"; errorMessage = "clear message JNI allocation OOM";
@ -580,6 +582,10 @@ JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobjec
// free alloc // free alloc
if (clearMsgPtr) if (clearMsgPtr)
{ {
if (clearMsgIsCopied)
{
memset(clearMsgPtr, 0, (size_t)env->GetArrayLength(aClearMsgBuffer));
}
env->ReleaseByteArrayElements(aClearMsgBuffer, clearMsgPtr, JNI_ABORT); env->ReleaseByteArrayElements(aClearMsgBuffer, clearMsgPtr, JNI_ABORT);
} }
@ -702,6 +708,8 @@ JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(decryptMessageJni)(JNIEnv *env, jobjec
LOGD(" ## decryptMessageJni(): UTF-8 Conversion - decrypted returnedLg=%lu OK",static_cast<long unsigned int>(plaintextLength)); LOGD(" ## decryptMessageJni(): UTF-8 Conversion - decrypted returnedLg=%lu OK",static_cast<long unsigned int>(plaintextLength));
} }
memset(plainTextMsgPtr, 0, maxPlainTextLength);
} }
} }
@ -958,4 +966,4 @@ JNIEXPORT jlong OLM_SESSION_FUNC_DEF(deserializeJni)(JNIEnv *env, jobject thiz,
} }
return (jlong)(intptr_t)sessionPtr; return (jlong)(intptr_t)sessionPtr;
} }

View file

@ -72,7 +72,7 @@ info.
Advancing the chain key Advancing the chain key
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
Advancing a chain key takes the previous chain key, :math:`C_{i,j-i}`. The next Advancing a chain key takes the previous chain key, :math:`C_{i,j-1}`. The next
chain key, :math:`C_{i,j}`, is the HMAC-SHA-256_ of ``"\x02"`` using the chain key, :math:`C_{i,j}`, is the HMAC-SHA-256_ of ``"\x02"`` using the
previous chain key as the key. previous chain key as the key.
@ -338,7 +338,7 @@ The Olm specification (this document) is hereby placed in the public domain.
Feedback Feedback
-------- --------
Can be sent to mark at matrix.org. Can be sent to olm at matrix.org.
Acknowledgements Acknowledgements
---------------- ----------------

View file

@ -113,6 +113,6 @@ This document is licensed under the `Apache License, Version 2.0
Feedback Feedback
-------- --------
Questions and feedback can be sent to richard at matrix.org. Questions and feedback can be sent to olm at matrix.org.
.. _`Ed25519`: http://ed25519.cr.yp.to/ .. _`Ed25519`: http://ed25519.cr.yp.to/

18
exports.py Executable file
View file

@ -0,0 +1,18 @@
#!/usr/bin/env python
import sys
import re
import json
expr = re.compile(r"(olm_[^( ]*)\(")
exports = set()
for f in sys.argv[1:]:
with open(f) as fp:
for line in fp:
matches = expr.search(line)
if matches is not None:
exports.add('_%s' % (matches.group(1),))
json.dump(sorted(exports), sys.stdout)

View file

@ -51,6 +51,13 @@ enum OlmErrorCode {
*/ */
OLM_BAD_SIGNATURE = 14, OLM_BAD_SIGNATURE = 14,
OLM_INPUT_BUFFER_TOO_SMALL = 15,
// Not an error code, just here to pad out the enum past 16 because
// otherwise the compiler warns about a redunant check. If you're
// adding an error code, replace this one!
OLM_ERROR_NOT_INVENTED_YET = 16,
/* remember to update the list of string constants in error.c when updating /* remember to update the list of string constants in error.c when updating
* this list. */ * this list. */
}; };

View file

@ -72,11 +72,15 @@ size_t olm_pk_encrypt_random_length(
); );
/** Encrypt a plaintext for the recipient set using /** Encrypt a plaintext for the recipient set using
* olm_pk_encryption_set_recipient_key. Returns olm_error() on failure. If the * olm_pk_encryption_set_recipient_key. Writes to the ciphertext, mac, and
* ciphertext, mac, or ephemeral_key buffers were too small then * ephemeral_key buffers, whose values should be sent to the recipient. mac is
* olm_pk_encryption_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". If there * a Message Authentication Code to ensure that the data is received and
* weren't enough random bytes then olm_pk_encryption_last_error() will be * decrypted properly. ephemeral_key is the public part of the ephemeral key
* "NOT_ENOUGH_RANDOM". */ * used (together with the recipient's key) to generate a symmetric encryption
* key. Returns olm_error() on failure. If the ciphertext, mac, or
* ephemeral_key buffers were too small then olm_pk_encryption_last_error()
* will be "OUTPUT_BUFFER_TOO_SMALL". If there weren't enough random bytes then
* olm_pk_encryption_last_error() will be "OLM_INPUT_BUFFER_TOO_SMALL". */
size_t olm_pk_encrypt( size_t olm_pk_encrypt(
OlmPkEncryption *encryption, OlmPkEncryption *encryption,
void const * plaintext, size_t plaintext_length, void const * plaintext, size_t plaintext_length,
@ -108,18 +112,36 @@ size_t olm_clear_pk_decryption(
OlmPkDecryption *decryption OlmPkDecryption *decryption
); );
/** The number of random bytes needed to generate a new key. */ /** Get the number of bytes required to store an olm private key
*/
size_t olm_pk_private_key_length();
/** DEPRECATED: Use olm_pk_private_key_length()
*/
size_t olm_pk_generate_key_random_length(void); size_t olm_pk_generate_key_random_length(void);
/** Generate a new key to use for decrypting messages. The associated public /** Initialise the key from the private part of a key as returned by
* key will be written to the pubkey buffer. Returns olm_error() on failure. If * olm_pk_get_private_key(). The associated public key will be written to the
* the pubkey buffer is too small then olm_pk_decryption_last_error() will be * pubkey buffer. Returns olm_error() on failure. If the pubkey buffer is too
* "OUTPUT_BUFFER_TOO_SMALL". If there weren't enough random bytes then * small then olm_pk_decryption_last_error() will be "OUTPUT_BUFFER_TOO_SMALL".
* olm_pk_decryption_last_error() will be "NOT_ENOUGH_RANDOM". */ * If the private key was not long enough then olm_pk_decryption_last_error()
* will be "OLM_INPUT_BUFFER_TOO_SMALL".
*
* Note that the pubkey is a base64 encoded string, but the private key is
* an unencoded byte array
*/
size_t olm_pk_key_from_private(
OlmPkDecryption * decryption,
void * pubkey, size_t pubkey_length,
void * privkey, size_t privkey_length
);
/** DEPRECATED: Use olm_pk_key_from_private
*/
size_t olm_pk_generate_key( size_t olm_pk_generate_key(
OlmPkDecryption * decryption, OlmPkDecryption * decryption,
void * pubkey, size_t pubkey_length, void * pubkey, size_t pubkey_length,
void * random, size_t random_length void * privkey, size_t privkey_length
); );
/** Returns the number of bytes needed to store a decryption object. */ /** Returns the number of bytes needed to store a decryption object. */
@ -159,18 +181,32 @@ size_t olm_pk_max_plaintext_length(
size_t ciphertext_length size_t ciphertext_length
); );
/** Decrypt a ciphertext. The input ciphertext buffer is destroyed. Returns /** Decrypt a ciphertext. The input ciphertext buffer is destroyed. See the
* the length of the plaintext on success. Returns olm_error() on failure. If * olm_pk_encrypt function for descriptions of the ephemeral_key and mac
* the plaintext buffer is too small then olm_pk_encryption_last_error() will * arguments. Returns the length of the plaintext on success. Returns
* be "OUTPUT_BUFFER_TOO_SMALL". */ * olm_error() on failure. If the plaintext buffer is too small then
* olm_pk_encryption_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". */
size_t olm_pk_decrypt( size_t olm_pk_decrypt(
OlmPkDecryption * decrytion, OlmPkDecryption * decryption,
void const * ephemeral_key, size_t ephemeral_key_length, void const * ephemeral_key, size_t ephemeral_key_length,
void const * mac, size_t mac_length, void const * mac, size_t mac_length,
void * ciphertext, size_t ciphertext_length, void * ciphertext, size_t ciphertext_length,
void * plaintext, size_t max_plaintext_length void * plaintext, size_t max_plaintext_length
); );
/**
* Get the private key for an OlmDecryption object as an unencoded byte array
* private_key must be a pointer to a buffer of at least
* olm_pk_private_key_length() bytes and this length must be passed in
* private_key_length. If the given buffer is too small, returns olm_error()
* and olm_pk_encryption_last_error() will be "OUTPUT_BUFFER_TOO_SMALL".
* Returns the number of bytes written.
*/
size_t olm_pk_get_private_key(
OlmPkDecryption * decryption,
void *private_key, size_t private_key_length
);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -2,4 +2,5 @@
/node_modules /node_modules
/npm-debug.log /npm-debug.log
/olm.js /olm.js
/olm.wasm
/reports /reports

4
javascript/externs.js Normal file
View file

@ -0,0 +1,4 @@
var OLM_OPTIONS;
var olm_exports;
var onInitSuccess;
var onInitFail;

View file

@ -1,9 +1,3 @@
/* The 'length' argument to Pointer_stringify doesn't work if the input includes
* characters >= 128; we therefore need to add a NULL character to all of our
* strings. This acts as a symbolic constant to help show what we're doing.
*/
var NULL_BYTE_PADDING_LENGTH = 1;
function InboundGroupSession() { function InboundGroupSession() {
var size = Module['_olm_inbound_group_session_size'](); var size = Module['_olm_inbound_group_session_size']();
this.buf = malloc(size); this.buf = malloc(size);
@ -77,14 +71,14 @@ InboundGroupSession.prototype['decrypt'] = restore_stack(function(
try { try {
message_buffer = malloc(message.length); message_buffer = malloc(message.length);
Module['writeAsciiToMemory'](message, message_buffer, true); writeAsciiToMemory(message, message_buffer, true);
var max_plaintext_length = inbound_group_session_method( var max_plaintext_length = inbound_group_session_method(
Module['_olm_group_decrypt_max_plaintext_length'] Module['_olm_group_decrypt_max_plaintext_length']
)(this.ptr, message_buffer, message.length); )(this.ptr, message_buffer, message.length);
// caculating the length destroys the input buffer, so we need to re-copy it. // caculating the length destroys the input buffer, so we need to re-copy it.
Module['writeAsciiToMemory'](message, message_buffer, true); writeAsciiToMemory(message, message_buffer, true);
plaintext_buffer = malloc(max_plaintext_length + NULL_BYTE_PADDING_LENGTH); plaintext_buffer = malloc(max_plaintext_length + NULL_BYTE_PADDING_LENGTH);
var message_index = stack(4); var message_index = stack(4);
@ -100,14 +94,14 @@ InboundGroupSession.prototype['decrypt'] = restore_stack(function(
// UTF8ToString requires a null-terminated argument, so add the // UTF8ToString requires a null-terminated argument, so add the
// null terminator. // null terminator.
Module['setValue']( setValue(
plaintext_buffer+plaintext_length, plaintext_buffer+plaintext_length,
0, "i8" 0, "i8"
); );
return { return {
"plaintext": UTF8ToString(plaintext_buffer), "plaintext": UTF8ToString(plaintext_buffer),
"message_index": Module['getValue'](message_index, "i32") "message_index": getValue(message_index, "i32")
} }
} finally { } finally {
if (message_buffer !== undefined) { if (message_buffer !== undefined) {

View file

@ -1,10 +1,3 @@
/* The 'length' argument to Pointer_stringify doesn't work if the input includes
* characters >= 128; we therefore need to add a NULL character to all of our
* strings. This acts as a symbolic constant to help show what we're doing.
*/
var NULL_BYTE_PADDING_LENGTH = 1;
function OutboundGroupSession() { function OutboundGroupSession() {
var size = Module['_olm_outbound_group_session_size'](); var size = Module['_olm_outbound_group_session_size']();
this.buf = malloc(size); this.buf = malloc(size);
@ -66,7 +59,7 @@ OutboundGroupSession.prototype['create'] = restore_stack(function() {
OutboundGroupSession.prototype['encrypt'] = function(plaintext) { OutboundGroupSession.prototype['encrypt'] = function(plaintext) {
var plaintext_buffer, message_buffer, plaintext_length; var plaintext_buffer, message_buffer, plaintext_length;
try { try {
plaintext_length = Module['lengthBytesUTF8'](plaintext); plaintext_length = lengthBytesUTF8(plaintext);
var message_length = outbound_group_session_method( var message_length = outbound_group_session_method(
Module['_olm_group_encrypt_message_length'] Module['_olm_group_encrypt_message_length']
@ -75,7 +68,7 @@ OutboundGroupSession.prototype['encrypt'] = function(plaintext) {
// need to allow space for the terminator (which stringToUTF8 always // need to allow space for the terminator (which stringToUTF8 always
// writes), hence + 1. // writes), hence + 1.
plaintext_buffer = malloc(plaintext_length + 1); plaintext_buffer = malloc(plaintext_length + 1);
Module['stringToUTF8'](plaintext, plaintext_buffer, plaintext_length + 1); stringToUTF8(plaintext, plaintext_buffer, plaintext_length + 1);
message_buffer = malloc(message_length + NULL_BYTE_PADDING_LENGTH); message_buffer = malloc(message_length + NULL_BYTE_PADDING_LENGTH);
outbound_group_session_method(Module['_olm_group_encrypt'])( outbound_group_session_method(Module['_olm_group_encrypt'])(
@ -86,12 +79,12 @@ OutboundGroupSession.prototype['encrypt'] = function(plaintext) {
// UTF8ToString requires a null-terminated argument, so add the // UTF8ToString requires a null-terminated argument, so add the
// null terminator. // null terminator.
Module['setValue']( setValue(
message_buffer+message_length, message_buffer+message_length,
0, "i8" 0, "i8"
); );
return Module['UTF8ToString'](message_buffer); return UTF8ToString(message_buffer);
} finally { } finally {
if (plaintext_buffer !== undefined) { if (plaintext_buffer !== undefined) {
// don't leave a copy of the plaintext in the heap. // don't leave a copy of the plaintext in the heap.

View file

@ -35,9 +35,9 @@ PkEncryption.prototype['encrypt'] = restore_stack(function(
) { ) {
var plaintext_buffer, ciphertext_buffer, plaintext_length; var plaintext_buffer, ciphertext_buffer, plaintext_length;
try { try {
plaintext_length = Module['lengthBytesUTF8'](plaintext) plaintext_length = lengthBytesUTF8(plaintext)
plaintext_buffer = malloc(plaintext_length + 1); plaintext_buffer = malloc(plaintext_length + 1);
Module['stringToUTF8'](plaintext, plaintext_buffer, plaintext_length + 1); stringToUTF8(plaintext, plaintext_buffer, plaintext_length + 1);
var random_length = pk_encryption_method( var random_length = pk_encryption_method(
Module['_olm_pk_encrypt_random_length'] Module['_olm_pk_encrypt_random_length']
)(); )();
@ -50,16 +50,16 @@ PkEncryption.prototype['encrypt'] = restore_stack(function(
Module['_olm_pk_mac_length'] Module['_olm_pk_mac_length']
)(this.ptr); )(this.ptr);
var mac_buffer = stack(mac_length + NULL_BYTE_PADDING_LENGTH); var mac_buffer = stack(mac_length + NULL_BYTE_PADDING_LENGTH);
Module['setValue']( setValue(
mac_buffer+mac_length, mac_buffer + mac_length,
0, "i8" 0, "i8"
); );
var ephemeral_length = pk_encryption_method( var ephemeral_length = pk_encryption_method(
Module['_olm_pk_key_length'] Module['_olm_pk_key_length']
)(); )();
var ephemeral_buffer = stack(ephemeral_length + NULL_BYTE_PADDING_LENGTH); var ephemeral_buffer = stack(ephemeral_length + NULL_BYTE_PADDING_LENGTH);
Module['setValue']( setValue(
ephemeral_buffer+ephemeral_length, ephemeral_buffer + ephemeral_length,
0, "i8" 0, "i8"
); );
pk_encryption_method(Module['_olm_pk_encrypt'])( pk_encryption_method(Module['_olm_pk_encrypt'])(
@ -72,12 +72,12 @@ PkEncryption.prototype['encrypt'] = restore_stack(function(
); );
// UTF8ToString requires a null-terminated argument, so add the // UTF8ToString requires a null-terminated argument, so add the
// null terminator. // null terminator.
Module['setValue']( setValue(
ciphertext_buffer+ciphertext_length, ciphertext_buffer + ciphertext_length,
0, "i8" 0, "i8"
); );
return { return {
"ciphertext": Module['UTF8ToString'](ciphertext_buffer), "ciphertext": UTF8ToString(ciphertext_buffer),
"mac": Pointer_stringify(mac_buffer), "mac": Pointer_stringify(mac_buffer),
"ephemeral": Pointer_stringify(ephemeral_buffer) "ephemeral": Pointer_stringify(ephemeral_buffer)
}; };
@ -118,16 +118,32 @@ PkDecryption.prototype['free'] = function() {
free(this.ptr); free(this.ptr);
} }
PkDecryption.prototype['generate_key'] = restore_stack(function () { PkDecryption.prototype['init_with_private_key'] = restore_stack(function (private_key) {
var random_length = pk_decryption_method( var private_key_buffer = stack(private_key.length);
Module['_olm_pk_generate_key_random_length'] Module['HEAPU8'].set(private_key, private_key_buffer);
)();
var random_buffer = random_stack(random_length); var pubkey_length = pk_decryption_method(
var pubkey_length = pk_encryption_method(
Module['_olm_pk_key_length'] Module['_olm_pk_key_length']
)(); )();
var pubkey_buffer = stack(pubkey_length + NULL_BYTE_PADDING_LENGTH); var pubkey_buffer = stack(pubkey_length + NULL_BYTE_PADDING_LENGTH);
pk_decryption_method(Module['_olm_pk_generate_key'])( pk_decryption_method(Module['_olm_pk_key_from_private'])(
this.ptr,
pubkey_buffer, pubkey_length,
private_key_buffer, private_key.length
);
return Pointer_stringify(pubkey_buffer);
});
PkDecryption.prototype['generate_key'] = restore_stack(function () {
var random_length = pk_decryption_method(
Module['_olm_pk_private_key_length']
)();
var random_buffer = random_stack(random_length);
var pubkey_length = pk_decryption_method(
Module['_olm_pk_key_length']
)();
var pubkey_buffer = stack(pubkey_length + NULL_BYTE_PADDING_LENGTH);
pk_decryption_method(Module['_olm_pk_key_from_private'])(
this.ptr, this.ptr,
pubkey_buffer, pubkey_length, pubkey_buffer, pubkey_length,
random_buffer, random_length random_buffer, random_length
@ -135,6 +151,18 @@ PkDecryption.prototype['generate_key'] = restore_stack(function () {
return Pointer_stringify(pubkey_buffer); return Pointer_stringify(pubkey_buffer);
}); });
PkDecryption.prototype['get_private_key'] = restore_stack(function () {
var privkey_length = pk_encryption_method(
Module['_olm_pk_private_key_length']
)();
var privkey_buffer = stack(privkey_length);
pk_decryption_method(Module['_olm_pk_get_private_key'])(
this.ptr,
privkey_buffer, privkey_length
);
return new Uint8Array(Module['HEAPU8'].buffer, privkey_buffer, privkey_length);
});
PkDecryption.prototype['pickle'] = restore_stack(function (key) { PkDecryption.prototype['pickle'] = restore_stack(function (key) {
var key_array = array_from_string(key); var key_array = array_from_string(key);
var pickle_length = pk_decryption_method( var pickle_length = pk_decryption_method(
@ -169,9 +197,9 @@ PkDecryption.prototype['decrypt'] = restore_stack(function (
) { ) {
var plaintext_buffer, ciphertext_buffer, plaintext_max_length; var plaintext_buffer, ciphertext_buffer, plaintext_max_length;
try { try {
ciphertext_length = Module['lengthBytesUTF8'](ciphertext) var ciphertext_length = lengthBytesUTF8(ciphertext)
ciphertext_buffer = malloc(ciphertext_length + 1); ciphertext_buffer = malloc(ciphertext_length + 1);
Module['stringToUTF8'](ciphertext, ciphertext_buffer, ciphertext_length + 1); stringToUTF8(ciphertext, ciphertext_buffer, ciphertext_length + 1);
var ephemeralkey_array = array_from_string(ephemeral_key); var ephemeralkey_array = array_from_string(ephemeral_key);
var ephemeralkey_buffer = stack(ephemeralkey_array); var ephemeralkey_buffer = stack(ephemeralkey_array);
var mac_array = array_from_string(mac); var mac_array = array_from_string(mac);
@ -190,11 +218,11 @@ PkDecryption.prototype['decrypt'] = restore_stack(function (
); );
// UTF8ToString requires a null-terminated argument, so add the // UTF8ToString requires a null-terminated argument, so add the
// null terminator. // null terminator.
Module['setValue']( setValue(
plaintext_buffer+plaintext_length, plaintext_buffer + plaintext_length,
0, "i8" 0, "i8"
); );
return Module['UTF8ToString'](plaintext_buffer); return UTF8ToString(plaintext_buffer);
} finally { } finally {
if (plaintext_buffer !== undefined) { if (plaintext_buffer !== undefined) {
// don't leave a copy of the plaintext in the heap. // don't leave a copy of the plaintext in the heap.

View file

@ -1,27 +1,17 @@
var runtime = Module['Runtime'];
var malloc = Module['_malloc']; var malloc = Module['_malloc'];
var free = Module['_free']; var free = Module['_free'];
var Pointer_stringify = Module['Pointer_stringify']; var OLM_ERROR;
var OLM_ERROR = Module['_olm_error']();
/* The 'length' argument to Pointer_stringify doesn't work if the input
* includes characters >= 128, which makes Pointer_stringify unreliable. We
* could use it on strings which are known to be ascii, but that seems
* dangerous. Instead we add a NULL character to all of our strings and just
* use UTF8ToString.
*/
var NULL_BYTE_PADDING_LENGTH = 1;
/* allocate a number of bytes of storage on the stack. /* allocate a number of bytes of storage on the stack.
* *
* If size_or_array is a Number, allocates that number of zero-initialised bytes. * If size_or_array is a Number, allocates that number of zero-initialised bytes.
*/ */
function stack(size_or_array) { function stack(size_or_array) {
return Module['allocate'](size_or_array, 'i8', Module['ALLOC_STACK']); return allocate(size_or_array, 'i8', Module['ALLOC_STACK']);
} }
function array_from_string(string) { function array_from_string(string) {
return Module['intArrayFromString'](string, true); return intArrayFromString(string, true);
} }
function random_stack(size) { function random_stack(size) {
@ -33,11 +23,11 @@ function random_stack(size) {
function restore_stack(wrapped) { function restore_stack(wrapped) {
return function() { return function() {
var sp = runtime.stackSave(); var sp = stackSave();
try { try {
return wrapped.apply(this, arguments); return wrapped.apply(this, arguments);
} finally { } finally {
runtime.stackRestore(sp); stackRestore(sp);
} }
} }
} }
@ -315,7 +305,7 @@ Session.prototype['encrypt'] = restore_stack(function(
Module['_olm_encrypt_message_type'] Module['_olm_encrypt_message_type']
)(this.ptr); )(this.ptr);
plaintext_length = Module['lengthBytesUTF8'](plaintext); plaintext_length = lengthBytesUTF8(plaintext);
var message_length = session_method( var message_length = session_method(
Module['_olm_encrypt_message_length'] Module['_olm_encrypt_message_length']
)(this.ptr, plaintext_length); )(this.ptr, plaintext_length);
@ -325,7 +315,7 @@ Session.prototype['encrypt'] = restore_stack(function(
// need to allow space for the terminator (which stringToUTF8 always // need to allow space for the terminator (which stringToUTF8 always
// writes), hence + 1. // writes), hence + 1.
plaintext_buffer = malloc(plaintext_length + 1); plaintext_buffer = malloc(plaintext_length + 1);
Module['stringToUTF8'](plaintext, plaintext_buffer, plaintext_length + 1); stringToUTF8(plaintext, plaintext_buffer, plaintext_length + 1);
message_buffer = malloc(message_length + NULL_BYTE_PADDING_LENGTH); message_buffer = malloc(message_length + NULL_BYTE_PADDING_LENGTH);
@ -338,14 +328,14 @@ Session.prototype['encrypt'] = restore_stack(function(
// UTF8ToString requires a null-terminated argument, so add the // UTF8ToString requires a null-terminated argument, so add the
// null terminator. // null terminator.
Module['setValue']( setValue(
message_buffer+message_length, message_buffer+message_length,
0, "i8" 0, "i8"
); );
return { return {
"type": message_type, "type": message_type,
"body": Module['UTF8ToString'](message_buffer), "body": UTF8ToString(message_buffer),
}; };
} finally { } finally {
if (plaintext_buffer !== undefined) { if (plaintext_buffer !== undefined) {
@ -366,14 +356,14 @@ Session.prototype['decrypt'] = restore_stack(function(
try { try {
message_buffer = malloc(message.length); message_buffer = malloc(message.length);
Module['writeAsciiToMemory'](message, message_buffer, true); writeAsciiToMemory(message, message_buffer, true);
max_plaintext_length = session_method( max_plaintext_length = session_method(
Module['_olm_decrypt_max_plaintext_length'] Module['_olm_decrypt_max_plaintext_length']
)(this.ptr, message_type, message_buffer, message.length); )(this.ptr, message_type, message_buffer, message.length);
// caculating the length destroys the input buffer, so we need to re-copy it. // caculating the length destroys the input buffer, so we need to re-copy it.
Module['writeAsciiToMemory'](message, message_buffer, true); writeAsciiToMemory(message, message_buffer, true);
plaintext_buffer = malloc(max_plaintext_length + NULL_BYTE_PADDING_LENGTH); plaintext_buffer = malloc(max_plaintext_length + NULL_BYTE_PADDING_LENGTH);
@ -385,7 +375,7 @@ Session.prototype['decrypt'] = restore_stack(function(
// UTF8ToString requires a null-terminated argument, so add the // UTF8ToString requires a null-terminated argument, so add the
// null terminator. // null terminator.
Module['setValue']( setValue(
plaintext_buffer+plaintext_length, plaintext_buffer+plaintext_length,
0, "i8" 0, "i8"
); );
@ -473,22 +463,3 @@ olm_exports["get_library_version"] = restore_stack(function() {
getValue(buf+2, 'i8'), getValue(buf+2, 'i8'),
]; ];
}); });
})();
// export the olm functions into the environment.
//
// make sure that we do this *after* populating olm_exports, so that we don't
// get a half-built window.Olm if there is an exception.
if (typeof module !== 'undefined' && module.exports) {
// node / browserify
module.exports = olm_exports;
}
if (typeof(window) !== 'undefined') {
// We've been imported directly into a browser. Define the global 'Olm' object.
// (we do this even if module.exports was defined, because it's useful to have
// Olm in the global scope for browserified and webpacked apps.)
window["Olm"] = olm_exports;
}

View file

@ -1,10 +1,7 @@
var olm_exports = {};
var get_random_values; var get_random_values;
var process; // Shadow the process object so that emscripten won't get
// confused by browserify
if (typeof(window) !== 'undefined') { if (typeof(window) !== 'undefined') {
// We've in a browser (directly, via browserify, or via webpack). // We're in a browser (directly, via browserify, or via webpack).
get_random_values = function(buf) { get_random_values = function(buf) {
window.crypto.getRandomValues(buf); window.crypto.getRandomValues(buf);
}; };
@ -12,7 +9,9 @@ if (typeof(window) !== 'undefined') {
// We're running in node. // We're running in node.
var nodeCrypto = require("crypto"); var nodeCrypto = require("crypto");
get_random_values = function(buf) { get_random_values = function(buf) {
var bytes = nodeCrypto.randomBytes(buf.length); // [''] syntax needed here rather than '.' to prevent
// closure compiler from mangling the import(!)
var bytes = nodeCrypto['randomBytes'](buf.length);
buf.set(bytes); buf.set(bytes);
}; };
process = global["process"]; process = global["process"];
@ -20,14 +19,31 @@ if (typeof(window) !== 'undefined') {
throw new Error("Cannot find global to attach library to"); throw new Error("Cannot find global to attach library to");
} }
(function() { /* applications should define OLM_OPTIONS in the environment to override
/* applications should define OLM_OPTIONS in the environment to override * emscripten module settings
* emscripten module settings */ */
var Module = {}; if (typeof(OLM_OPTIONS) !== 'undefined') {
if (typeof(OLM_OPTIONS) !== 'undefined') { for (var olm_option_key in OLM_OPTIONS) {
for (var key in OLM_OPTIONS) { if (OLM_OPTIONS.hasOwnProperty(olm_option_key)) {
if (OLM_OPTIONS.hasOwnProperty(key)) { Module[olm_option_key] = OLM_OPTIONS[olm_option_key];
Module[key] = OLM_OPTIONS[key];
}
} }
} }
}
/* The 'length' argument to Pointer_stringify doesn't work if the input
* includes characters >= 128, which makes Pointer_stringify unreliable. We
* could use it on strings which are known to be ascii, but that seems
* dangerous. Instead we add a NULL character to all of our strings and just
* use UTF8ToString.
*/
var NULL_BYTE_PADDING_LENGTH = 1;
Module['onRuntimeInitialized'] = function() {
OLM_ERROR = Module['_olm_error']();
olm_exports["PRIVATE_KEY_LENGTH"] = Module['_olm_pk_private_key_length']();
if (onInitSuccess) onInitSuccess();
};
Module['onAbort'] = function(err) {
if (onInitFail) onInitFail(err);
};

3
javascript/olm_prefix.js Normal file
View file

@ -0,0 +1,3 @@
var olm_exports = {};
var onInitSuccess;
var onInitFail;

32
javascript/olm_suffix.js Normal file
View file

@ -0,0 +1,32 @@
var olmInitPromise;
olm_exports['init'] = function(opts) {
if (olmInitPromise) return olmInitPromise;
if (opts) OLM_OPTIONS = opts;
olmInitPromise = new Promise(function(resolve, reject) {
onInitSuccess = function() {
resolve();
};
onInitFail = function(err) {
reject(err);
};
Module();
});
return olmInitPromise;
};
if (typeof(window) !== 'undefined') {
// We've been imported directly into a browser. Define the global 'Olm' object.
// (we do this even if module.exports was defined, because it's useful to have
// Olm in the global scope for browserified and webpacked apps.)
window["Olm"] = olm_exports;
}
if (typeof module === 'object') {
// Emscripten sets the module exports to be its module
// with wrapped c functions. Clobber it with our higher
// level wrapper class.
module.exports = olm_exports;
}

View file

@ -5,6 +5,7 @@
"main": "olm.js", "main": "olm.js",
"files": [ "files": [
"olm.js", "olm.js",
"olm.wasm",
"README.md" "README.md"
], ],
"scripts": { "scripts": {

View file

@ -1,5 +1,6 @@
/* /*
Copyright 2016 OpenMarket Ltd Copyright 2016 OpenMarket Ltd
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -21,9 +22,13 @@ var Olm = require('../olm');
describe("megolm", function() { describe("megolm", function() {
var aliceSession, bobSession; var aliceSession, bobSession;
beforeEach(function() { beforeEach(function(done) {
aliceSession = new Olm.OutboundGroupSession(); Olm.init().then(function() {
bobSession = new Olm.InboundGroupSession(); aliceSession = new Olm.OutboundGroupSession();
bobSession = new Olm.InboundGroupSession();
done();
});
}); });
afterEach(function() { afterEach(function() {

View file

@ -1,5 +1,6 @@
/* /*
Copyright 2016 OpenMarket Ltd Copyright 2016 OpenMarket Ltd
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -30,11 +31,18 @@ describe("olm", function() {
var aliceAccount, bobAccount; var aliceAccount, bobAccount;
var aliceSession, bobSession; var aliceSession, bobSession;
beforeEach(function() { beforeEach(function(done) {
aliceAccount = new Olm.Account(); // This should really be in a beforeAll, but jasmine-node
bobAccount = new Olm.Account(); // doesn't support that
aliceSession = new Olm.Session(); debugger;
bobSession = new Olm.Session(); Olm.init().then(function() {
aliceAccount = new Olm.Account();
bobAccount = new Olm.Account();
aliceSession = new Olm.Session();
bobSession = new Olm.Session();
done();
});
}); });
afterEach(function() { afterEach(function() {

View file

@ -18,20 +18,16 @@ limitations under the License.
var Olm = require('../olm'); var Olm = require('../olm');
if (!Object.keys) {
Object.keys = function(o) {
var k=[], p;
for (p in o) if (Object.prototype.hasOwnProperty.call(o,p)) k.push(p);
return k;
}
}
describe("pk", function() { describe("pk", function() {
var encryption, decryption; var encryption, decryption;
beforeEach(function() { beforeEach(function(done) {
encryption = new Olm.PkEncryption(); Olm.init().then(function() {
decryption = new Olm.PkDecryption(); encryption = new Olm.PkEncryption();
decryption = new Olm.PkDecryption();
done();
});
}); });
afterEach(function () { afterEach(function () {
@ -45,6 +41,20 @@ describe("pk", function() {
} }
}); });
it('should import & export keys from private parts', function () {
var alice_private = new Uint8Array([
0x77, 0x07, 0x6D, 0x0A, 0x73, 0x18, 0xA5, 0x7D,
0x3C, 0x16, 0xC1, 0x72, 0x51, 0xB2, 0x66, 0x45,
0xDF, 0x4C, 0x2F, 0x87, 0xEB, 0xC0, 0x99, 0x2A,
0xB1, 0x77, 0xFB, 0xA5, 0x1D, 0xB9, 0x2C, 0x2A
]);
var alice_public = decryption.init_with_private_key(alice_private);
expect(alice_public).toEqual("hSDwCYkwp1R0i33ctD73Wg2/Og0mOBr066SpjqqbTmo");
var alice_private_out = decryption.get_private_key();
expect(alice_private_out).toEqual(alice_private);
});
it('should encrypt and decrypt', function () { it('should encrypt and decrypt', function () {
var TEST_TEXT='têst1'; var TEST_TEXT='têst1';
var pubkey = decryption.generate_key(); var pubkey = decryption.generate_key();

View file

@ -31,6 +31,7 @@ static const char * ERRORS[] = {
"UNKNOWN_MESSAGE_INDEX", "UNKNOWN_MESSAGE_INDEX",
"BAD_LEGACY_ACCOUNT_PICKLE", "BAD_LEGACY_ACCOUNT_PICKLE",
"BAD_SIGNATURE", "BAD_SIGNATURE",
"OLM_INPUT_BUFFER_TOO_SMALL",
}; };
const char * _olm_error_to_string(enum OlmErrorCode error) const char * _olm_error_to_string(enum OlmErrorCode error)

View file

@ -22,15 +22,15 @@
#include "olm/pickle_encoding.h" #include "olm/pickle_encoding.h"
#include "olm/pickle.hh" #include "olm/pickle.hh"
extern "C" {
static const std::size_t MAC_LENGTH = 8; static const std::size_t MAC_LENGTH = 8;
const struct _olm_cipher_aes_sha_256 olm_pk_cipher_aes_sha256 = const struct _olm_cipher_aes_sha_256 olm_pk_cipher_aes_sha256 =
OLM_CIPHER_INIT_AES_SHA_256(""); OLM_CIPHER_INIT_AES_SHA_256("");
const struct _olm_cipher *olm_pk_cipher = const struct _olm_cipher *olm_pk_cipher =
OLM_CIPHER_BASE(&olm_pk_cipher_aes_sha256); OLM_CIPHER_BASE(&olm_pk_cipher_aes_sha256);
extern "C" {
struct OlmPkEncryption { struct OlmPkEncryption {
OlmErrorCode last_error; OlmErrorCode last_error;
_olm_curve25519_public_key recipient_key; _olm_curve25519_public_key recipient_key;
@ -73,7 +73,11 @@ size_t olm_pk_encryption_set_recipient_key (
OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; // FIXME: OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; // FIXME:
return std::size_t(-1); return std::size_t(-1);
} }
olm::decode_base64((const uint8_t*)key, olm_pk_key_length(), (uint8_t *)encryption->recipient_key.public_key); olm::decode_base64(
(const uint8_t*)key,
olm_pk_key_length(),
(uint8_t *)encryption->recipient_key.public_key
);
return 0; return 0;
} }
@ -81,7 +85,9 @@ size_t olm_pk_ciphertext_length(
OlmPkEncryption *encryption, OlmPkEncryption *encryption,
size_t plaintext_length size_t plaintext_length
) { ) {
return olm::encode_base64_length(_olm_cipher_aes_sha_256_ops.encrypt_ciphertext_length(olm_pk_cipher, plaintext_length)); return olm::encode_base64_length(
_olm_cipher_aes_sha_256_ops.encrypt_ciphertext_length(olm_pk_cipher, plaintext_length)
);
} }
size_t olm_pk_mac_length( size_t olm_pk_mac_length(
@ -106,9 +112,9 @@ size_t olm_pk_encrypt(
) { ) {
if (ciphertext_length if (ciphertext_length
< olm_pk_ciphertext_length(encryption, plaintext_length) < olm_pk_ciphertext_length(encryption, plaintext_length)
|| mac_length || mac_length
< _olm_cipher_aes_sha_256_ops.mac_length(olm_pk_cipher) < _olm_cipher_aes_sha_256_ops.mac_length(olm_pk_cipher)
|| ephemeral_key_size || ephemeral_key_size
< olm_pk_key_length()) { < olm_pk_key_length()) {
encryption->last_error = encryption->last_error =
OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
@ -122,11 +128,16 @@ size_t olm_pk_encrypt(
_olm_curve25519_key_pair ephemeral_keypair; _olm_curve25519_key_pair ephemeral_keypair;
_olm_crypto_curve25519_generate_key((uint8_t *) random, &ephemeral_keypair); _olm_crypto_curve25519_generate_key((uint8_t *) random, &ephemeral_keypair);
olm::encode_base64((const uint8_t *)ephemeral_keypair.public_key.public_key, CURVE25519_KEY_LENGTH, (uint8_t *)ephemeral_key); olm::encode_base64(
(const uint8_t *)ephemeral_keypair.public_key.public_key,
CURVE25519_KEY_LENGTH,
(uint8_t *)ephemeral_key
);
olm::SharedKey secret; olm::SharedKey secret;
_olm_crypto_curve25519_shared_secret(&ephemeral_keypair, &encryption->recipient_key, secret); _olm_crypto_curve25519_shared_secret(&ephemeral_keypair, &encryption->recipient_key, secret);
size_t raw_ciphertext_length = _olm_cipher_aes_sha_256_ops.encrypt_ciphertext_length(olm_pk_cipher, plaintext_length); size_t raw_ciphertext_length =
_olm_cipher_aes_sha_256_ops.encrypt_ciphertext_length(olm_pk_cipher, plaintext_length);
uint8_t *ciphertext_pos = (uint8_t *) ciphertext + ciphertext_length - raw_ciphertext_length; uint8_t *ciphertext_pos = (uint8_t *) ciphertext + ciphertext_length - raw_ciphertext_length;
uint8_t raw_mac[MAC_LENGTH]; uint8_t raw_mac[MAC_LENGTH];
size_t result = _olm_cipher_aes_sha_256_ops.encrypt( size_t result = _olm_cipher_aes_sha_256_ops.encrypt(
@ -176,35 +187,51 @@ size_t olm_clear_pk_decryption(
return sizeof(OlmPkDecryption); return sizeof(OlmPkDecryption);
} }
size_t olm_pk_generate_key_random_length(void) { size_t olm_pk_private_key_length(void) {
return CURVE25519_KEY_LENGTH; return CURVE25519_KEY_LENGTH;
} }
size_t olm_pk_generate_key_random_length(void) {
return olm_pk_private_key_length();
}
size_t olm_pk_key_length(void) { size_t olm_pk_key_length(void) {
return olm::encode_base64_length(CURVE25519_KEY_LENGTH); return olm::encode_base64_length(CURVE25519_KEY_LENGTH);
} }
size_t olm_pk_generate_key( size_t olm_pk_key_from_private(
OlmPkDecryption * decryption, OlmPkDecryption * decryption,
void * pubkey, size_t pubkey_length, void * pubkey, size_t pubkey_length,
void * random, size_t random_length void * privkey, size_t privkey_length
) { ) {
if (pubkey_length < CURVE25519_KEY_LENGTH) { if (pubkey_length < olm_pk_key_length()) {
decryption->last_error = decryption->last_error =
OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
return std::size_t(-1); return std::size_t(-1);
} }
if (random_length < olm_pk_generate_key_random_length()) { if (privkey_length < olm_pk_private_key_length()) {
decryption->last_error = decryption->last_error =
OlmErrorCode::OLM_NOT_ENOUGH_RANDOM; OlmErrorCode::OLM_INPUT_BUFFER_TOO_SMALL;
return std::size_t(-1); return std::size_t(-1);
} }
_olm_crypto_curve25519_generate_key((uint8_t *) random, &decryption->key_pair); _olm_crypto_curve25519_generate_key((uint8_t *) privkey, &decryption->key_pair);
olm::encode_base64((const uint8_t *)decryption->key_pair.public_key.public_key, CURVE25519_KEY_LENGTH, (uint8_t *)pubkey); olm::encode_base64(
(const uint8_t *)decryption->key_pair.public_key.public_key,
CURVE25519_KEY_LENGTH,
(uint8_t *)pubkey
);
return 0; return 0;
} }
size_t olm_pk_generate_key(
OlmPkDecryption * decryption,
void * pubkey, size_t pubkey_length,
void * privkey, size_t privkey_length
) {
return olm_pk_key_from_private(decryption, pubkey, pubkey_length, privkey, privkey_length);
}
namespace { namespace {
static const std::uint32_t PK_DECRYPTION_PICKLE_VERSION = 1; static const std::uint32_t PK_DECRYPTION_PICKLE_VERSION = 1;
@ -267,7 +294,10 @@ size_t olm_pickle_pk_decryption(
return std::size_t(-1); return std::size_t(-1);
} }
pickle(_olm_enc_output_pos(reinterpret_cast<std::uint8_t *>(pickled), raw_length), object); pickle(_olm_enc_output_pos(reinterpret_cast<std::uint8_t *>(pickled), raw_length), object);
return _olm_enc_output(reinterpret_cast<std::uint8_t const *>(key), key_length, reinterpret_cast<std::uint8_t *>(pickled), raw_length); return _olm_enc_output(
reinterpret_cast<std::uint8_t const *>(key), key_length,
reinterpret_cast<std::uint8_t *>(pickled), raw_length
);
} }
size_t olm_unpickle_pk_decryption( size_t olm_unpickle_pk_decryption(
@ -283,7 +313,8 @@ size_t olm_unpickle_pk_decryption(
} }
std::uint8_t * const pos = reinterpret_cast<std::uint8_t *>(pickled); std::uint8_t * const pos = reinterpret_cast<std::uint8_t *>(pickled);
std::size_t raw_length = _olm_enc_input( std::size_t raw_length = _olm_enc_input(
reinterpret_cast<std::uint8_t const *>(key), key_length, pos, pickled_length, &object.last_error reinterpret_cast<std::uint8_t const *>(key), key_length,
pos, pickled_length, &object.last_error
); );
if (raw_length == std::size_t(-1)) { if (raw_length == std::size_t(-1)) {
return std::size_t(-1); return std::size_t(-1);
@ -300,7 +331,11 @@ size_t olm_unpickle_pk_decryption(
return std::size_t(-1); return std::size_t(-1);
} }
if (pubkey != NULL) { if (pubkey != NULL) {
olm::encode_base64((const uint8_t *)object.key_pair.public_key.public_key, CURVE25519_KEY_LENGTH, (uint8_t *)pubkey); olm::encode_base64(
(const uint8_t *)object.key_pair.public_key.public_key,
CURVE25519_KEY_LENGTH,
(uint8_t *)pubkey
);
} }
return pickled_length; return pickled_length;
} }
@ -309,7 +344,9 @@ size_t olm_pk_max_plaintext_length(
OlmPkDecryption * decryption, OlmPkDecryption * decryption,
size_t ciphertext_length size_t ciphertext_length
) { ) {
return _olm_cipher_aes_sha_256_ops.decrypt_max_plaintext_length(olm_pk_cipher, olm::decode_base64_length(ciphertext_length)); return _olm_cipher_aes_sha_256_ops.decrypt_max_plaintext_length(
olm_pk_cipher, olm::decode_base64_length(ciphertext_length)
);
} }
size_t olm_pk_decrypt( size_t olm_pk_decrypt(
@ -327,7 +364,10 @@ size_t olm_pk_decrypt(
} }
struct _olm_curve25519_public_key ephemeral; struct _olm_curve25519_public_key ephemeral;
olm::decode_base64((const uint8_t*)ephemeral_key, ephemeral_key_length, (uint8_t *)ephemeral.public_key); olm::decode_base64(
(const uint8_t*)ephemeral_key, ephemeral_key_length,
(uint8_t *)ephemeral.public_key
);
olm::SharedKey secret; olm::SharedKey secret;
_olm_crypto_curve25519_shared_secret(&decryption->key_pair, &ephemeral, secret); _olm_crypto_curve25519_shared_secret(&decryption->key_pair, &ephemeral, secret);
uint8_t raw_mac[MAC_LENGTH]; uint8_t raw_mac[MAC_LENGTH];
@ -352,4 +392,21 @@ size_t olm_pk_decrypt(
} }
} }
size_t olm_pk_get_private_key(
OlmPkDecryption * decryption,
void *private_key, size_t private_key_length
) {
if (private_key_length < olm_pk_private_key_length()) {
decryption->last_error =
OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
return std::size_t(-1);
}
std::memcpy(
private_key,
decryption->key_pair.private_key.private_key,
olm_pk_private_key_length()
);
return olm_pk_private_key_length();
}
} }

View file

@ -23,7 +23,7 @@ std::uint8_t alice_private[32] = {
0xB1, 0x77, 0xFB, 0xA5, 0x1D, 0xB9, 0x2C, 0x2A 0xB1, 0x77, 0xFB, 0xA5, 0x1D, 0xB9, 0x2C, 0x2A
}; };
const std::uint8_t *alice_public = (std::uint8_t *) "hSDwCYkwp1R0i33ctD73Wg2/Og0mOBr066SpjqqbTmoK"; const std::uint8_t *alice_public = (std::uint8_t *) "hSDwCYkwp1R0i33ctD73Wg2/Og0mOBr066SpjqqbTmo";
std::uint8_t bob_private[32] = { std::uint8_t bob_private[32] = {
0x5D, 0xAB, 0x08, 0x7E, 0x62, 0x4A, 0x8A, 0x4B, 0x5D, 0xAB, 0x08, 0x7E, 0x62, 0x4A, 0x8A, 0x4B,
@ -32,11 +32,11 @@ std::uint8_t bob_private[32] = {
0x1C, 0x2F, 0x8B, 0x27, 0xFF, 0x88, 0xE0, 0xEB 0x1C, 0x2F, 0x8B, 0x27, 0xFF, 0x88, 0xE0, 0xEB
}; };
const std::uint8_t *bob_public = (std::uint8_t *) "3p7bfXt9wbTTW2HC7OQ1Nz+DQ8hbeGdNrfx+FG+IK08K"; const std::uint8_t *bob_public = (std::uint8_t *) "3p7bfXt9wbTTW2HC7OQ1Nz+DQ8hbeGdNrfx+FG+IK08";
std::uint8_t pubkey[::olm_pk_key_length()]; std::uint8_t pubkey[::olm_pk_key_length()];
olm_pk_generate_key( olm_pk_key_from_private(
decryption, decryption,
pubkey, sizeof(pubkey), pubkey, sizeof(pubkey),
alice_private, sizeof(alice_private) alice_private, sizeof(alice_private)
@ -44,6 +44,11 @@ olm_pk_generate_key(
assert_equals(alice_public, pubkey, olm_pk_key_length()); assert_equals(alice_public, pubkey, olm_pk_key_length());
uint8_t *alice_private_back_out = (uint8_t *)malloc(olm_pk_private_key_length());
olm_pk_get_private_key(decryption, alice_private_back_out, olm_pk_private_key_length());
assert_equals(alice_private, alice_private_back_out, olm_pk_private_key_length());
free(alice_private_back_out);
std::uint8_t encryption_buffer[olm_pk_encryption_size()]; std::uint8_t encryption_buffer[olm_pk_encryption_size()];
OlmPkEncryption *encryption = olm_pk_encryption(encryption_buffer); OlmPkEncryption *encryption = olm_pk_encryption(encryption_buffer);
@ -105,7 +110,7 @@ const std::uint8_t *alice_public = (std::uint8_t *) "hSDwCYkwp1R0i33ctD73Wg2/Og0
std::uint8_t pubkey[olm_pk_key_length()]; std::uint8_t pubkey[olm_pk_key_length()];
olm_pk_generate_key( olm_pk_key_from_private(
decryption, decryption,
pubkey, sizeof(pubkey), pubkey, sizeof(pubkey),
alice_private, sizeof(alice_private) alice_private, sizeof(alice_private)