From ed02c217e6f7e95e495c241c8ff27ef6b5dd8417 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Wed, 15 Aug 2018 22:02:29 -0400 Subject: [PATCH 01/27] update address for sending feedback for olm --- docs/olm.rst | 2 +- docs/signing.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/olm.rst b/docs/olm.rst index a18662d..9f82c8e 100644 --- a/docs/olm.rst +++ b/docs/olm.rst @@ -338,7 +338,7 @@ The Olm specification (this document) is hereby placed in the public domain. Feedback -------- -Can be sent to mark at matrix.org. +Can be sent to olm at matrix.org. Acknowledgements ---------------- diff --git a/docs/signing.rst b/docs/signing.rst index 7387794..05c55eb 100644 --- a/docs/signing.rst +++ b/docs/signing.rst @@ -113,6 +113,6 @@ This document is licensed under the `Apache License, Version 2.0 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/ From 65d4ac19c82478f7719a47879b1c0ffa99dc19d8 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 19 Sep 2018 14:10:12 +0100 Subject: [PATCH 02/27] Fix output buffer length check ...when generating a key in PkDecryption. The pubkey is base64ed on the output, so will be longer. --- src/pk.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pk.cpp b/src/pk.cpp index b8fe95b..e646dc4 100644 --- a/src/pk.cpp +++ b/src/pk.cpp @@ -189,7 +189,7 @@ size_t olm_pk_generate_key( void * pubkey, size_t pubkey_length, void * random, size_t random_length ) { - if (pubkey_length < CURVE25519_KEY_LENGTH) { + if (pubkey_length < olm_pk_key_length()) { decryption->last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); From 122867c45c7f41b82a550a9665d34b7dda1c3ffa Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 21 Sep 2018 16:01:51 +0100 Subject: [PATCH 03/27] WebAssembly support! Quite a lot going on in this PR: * Updates to support recent emscripten, switching to WASM which is now the default * Use emscripten's MODULARIZE option rather than wrapping it ourself, since doing so in pre-post js doesn't work anymore. * Most changes are moving the emscripten runtime functions to top-level calls rather than in the Module object. * Get rid of duplicated NULL_BYTE_PADDING_LENGTH * Fix ciphertext_length used without being declared * Fix things that caused the closure compiler to error, eg. using OLM_OPTIONS without a declaration. * Wait until module is inited to do OLM_ERROR = olm_error() The main BREAKING CHANGE here is that the module now needs to initialise asyncronously (because it has to load the wasm file). require()ing olm now gives a function which needs to be called to create an instance. The resulting object has a promise-like then() method that can be used to detect when the module is ready. (We could use MODULARIZE_INSTANCE to return the module directly as before, rather than the function, but then we don't get the .then() method). --- Makefile | 4 ++- javascript/.gitignore | 1 + javascript/olm_inbound_group_session.js | 14 +++----- javascript/olm_outbound_group_session.js | 15 +++----- javascript/olm_pk.js | 20 +++++------ javascript/olm_post.js | 46 +++++++++++------------- javascript/olm_pre.js | 35 +++++++++++------- javascript/test/megolm.spec.js | 7 ++-- javascript/test/olm.spec.js | 10 ++++-- javascript/test/pk.spec.js | 8 +++-- 10 files changed, 84 insertions(+), 76 deletions(-) diff --git a/Makefile b/Makefile index 154954c..f6c2ab4 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,7 @@ DEBUG_TARGET := $(BUILD_DIR)/libolm_debug.so.$(VERSION) JS_TARGET := javascript/olm.js JS_EXPORTED_FUNCTIONS := javascript/exported_functions.json +JS_EXTRA_EXPORTED_RUNTIME_METHODS := ALLOC_STACK PUBLIC_HEADERS := include/olm/olm.h include/olm/outbound_group_session.h include/olm/inbound_group_session.h include/olm/pk.h @@ -60,7 +61,7 @@ CFLAGS += -Wall -Werror -std=c99 -fPIC CXXFLAGS += -Wall -Werror -std=c++11 -fPIC 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 # longer needed. EMCCFLAGS += -s NO_BROWSER=1 @@ -150,6 +151,7 @@ $(JS_TARGET): $(JS_OBJECTS) $(JS_PRE) $(JS_POST) $(JS_EXPORTED_FUNCTIONS) $(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 $@ build_tests: $(TEST_BINARIES) diff --git a/javascript/.gitignore b/javascript/.gitignore index ec22345..3437f73 100644 --- a/javascript/.gitignore +++ b/javascript/.gitignore @@ -2,4 +2,5 @@ /node_modules /npm-debug.log /olm.js +/olm.wasm /reports diff --git a/javascript/olm_inbound_group_session.js b/javascript/olm_inbound_group_session.js index 6bc745d..7d9e401 100644 --- a/javascript/olm_inbound_group_session.js +++ b/javascript/olm_inbound_group_session.js @@ -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() { var size = Module['_olm_inbound_group_session_size'](); this.buf = malloc(size); @@ -77,14 +71,14 @@ InboundGroupSession.prototype['decrypt'] = restore_stack(function( try { message_buffer = malloc(message.length); - Module['writeAsciiToMemory'](message, message_buffer, true); + writeAsciiToMemory(message, message_buffer, true); var max_plaintext_length = inbound_group_session_method( Module['_olm_group_decrypt_max_plaintext_length'] )(this.ptr, message_buffer, message.length); // 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); var message_index = stack(4); @@ -100,14 +94,14 @@ InboundGroupSession.prototype['decrypt'] = restore_stack(function( // UTF8ToString requires a null-terminated argument, so add the // null terminator. - Module['setValue']( + setValue( plaintext_buffer+plaintext_length, 0, "i8" ); return { "plaintext": UTF8ToString(plaintext_buffer), - "message_index": Module['getValue'](message_index, "i32") + "message_index": getValue(message_index, "i32") } } finally { if (message_buffer !== undefined) { diff --git a/javascript/olm_outbound_group_session.js b/javascript/olm_outbound_group_session.js index 24ea644..e232883 100644 --- a/javascript/olm_outbound_group_session.js +++ b/javascript/olm_outbound_group_session.js @@ -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() { var size = Module['_olm_outbound_group_session_size'](); this.buf = malloc(size); @@ -66,7 +59,7 @@ OutboundGroupSession.prototype['create'] = restore_stack(function() { OutboundGroupSession.prototype['encrypt'] = function(plaintext) { var plaintext_buffer, message_buffer, plaintext_length; try { - plaintext_length = Module['lengthBytesUTF8'](plaintext); + plaintext_length = lengthBytesUTF8(plaintext); var message_length = outbound_group_session_method( 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 // writes), hence + 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); 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 // null terminator. - Module['setValue']( + setValue( message_buffer+message_length, 0, "i8" ); - return Module['UTF8ToString'](message_buffer); + return UTF8ToString(message_buffer); } finally { if (plaintext_buffer !== undefined) { // don't leave a copy of the plaintext in the heap. diff --git a/javascript/olm_pk.js b/javascript/olm_pk.js index 2542707..25e0fee 100644 --- a/javascript/olm_pk.js +++ b/javascript/olm_pk.js @@ -35,9 +35,9 @@ PkEncryption.prototype['encrypt'] = restore_stack(function( ) { var plaintext_buffer, ciphertext_buffer, plaintext_length; try { - plaintext_length = Module['lengthBytesUTF8'](plaintext) + plaintext_length = lengthBytesUTF8(plaintext) 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( Module['_olm_pk_encrypt_random_length'] )(); @@ -50,7 +50,7 @@ PkEncryption.prototype['encrypt'] = restore_stack(function( Module['_olm_pk_mac_length'] )(this.ptr); var mac_buffer = stack(mac_length + NULL_BYTE_PADDING_LENGTH); - Module['setValue']( + setValue( mac_buffer+mac_length, 0, "i8" ); @@ -58,7 +58,7 @@ PkEncryption.prototype['encrypt'] = restore_stack(function( Module['_olm_pk_key_length'] )(); var ephemeral_buffer = stack(ephemeral_length + NULL_BYTE_PADDING_LENGTH); - Module['setValue']( + setValue( ephemeral_buffer+ephemeral_length, 0, "i8" ); @@ -72,12 +72,12 @@ PkEncryption.prototype['encrypt'] = restore_stack(function( ); // UTF8ToString requires a null-terminated argument, so add the // null terminator. - Module['setValue']( + setValue( ciphertext_buffer+ciphertext_length, 0, "i8" ); return { - "ciphertext": Module['UTF8ToString'](ciphertext_buffer), + "ciphertext": UTF8ToString(ciphertext_buffer), "mac": Pointer_stringify(mac_buffer), "ephemeral": Pointer_stringify(ephemeral_buffer) }; @@ -169,9 +169,9 @@ PkDecryption.prototype['decrypt'] = restore_stack(function ( ) { var plaintext_buffer, ciphertext_buffer, plaintext_max_length; try { - ciphertext_length = Module['lengthBytesUTF8'](ciphertext) + var ciphertext_length = lengthBytesUTF8(ciphertext) 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_buffer = stack(ephemeralkey_array); var mac_array = array_from_string(mac); @@ -190,11 +190,11 @@ PkDecryption.prototype['decrypt'] = restore_stack(function ( ); // UTF8ToString requires a null-terminated argument, so add the // null terminator. - Module['setValue']( + setValue( plaintext_buffer+plaintext_length, 0, "i8" ); - return Module['UTF8ToString'](plaintext_buffer); + return UTF8ToString(plaintext_buffer); } finally { if (plaintext_buffer !== undefined) { // don't leave a copy of the plaintext in the heap. diff --git a/javascript/olm_post.js b/javascript/olm_post.js index 7a1d284..071021c 100644 --- a/javascript/olm_post.js +++ b/javascript/olm_post.js @@ -1,27 +1,17 @@ -var runtime = Module['Runtime']; var malloc = Module['_malloc']; var free = Module['_free']; -var Pointer_stringify = Module['Pointer_stringify']; -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; +var OLM_ERROR; /* allocate a number of bytes of storage on the stack. * * If size_or_array is a Number, allocates that number of zero-initialised bytes. */ 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) { - return Module['intArrayFromString'](string, true); + return intArrayFromString(string, true); } function random_stack(size) { @@ -33,11 +23,11 @@ function random_stack(size) { function restore_stack(wrapped) { return function() { - var sp = runtime.stackSave(); + var sp = stackSave(); try { return wrapped.apply(this, arguments); } finally { - runtime.stackRestore(sp); + stackRestore(sp); } } } @@ -315,7 +305,7 @@ Session.prototype['encrypt'] = restore_stack(function( Module['_olm_encrypt_message_type'] )(this.ptr); - plaintext_length = Module['lengthBytesUTF8'](plaintext); + plaintext_length = lengthBytesUTF8(plaintext); var message_length = session_method( Module['_olm_encrypt_message_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 // writes), hence + 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); @@ -338,14 +328,14 @@ Session.prototype['encrypt'] = restore_stack(function( // UTF8ToString requires a null-terminated argument, so add the // null terminator. - Module['setValue']( + setValue( message_buffer+message_length, 0, "i8" ); return { "type": message_type, - "body": Module['UTF8ToString'](message_buffer), + "body": UTF8ToString(message_buffer), }; } finally { if (plaintext_buffer !== undefined) { @@ -366,14 +356,14 @@ Session.prototype['decrypt'] = restore_stack(function( try { message_buffer = malloc(message.length); - Module['writeAsciiToMemory'](message, message_buffer, true); + writeAsciiToMemory(message, message_buffer, true); max_plaintext_length = session_method( Module['_olm_decrypt_max_plaintext_length'] )(this.ptr, message_type, message_buffer, message.length); // 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); @@ -385,7 +375,7 @@ Session.prototype['decrypt'] = restore_stack(function( // UTF8ToString requires a null-terminated argument, so add the // null terminator. - Module['setValue']( + setValue( plaintext_buffer+plaintext_length, 0, "i8" ); @@ -474,8 +464,6 @@ olm_exports["get_library_version"] = restore_stack(function() { ]; }); -})(); - // export the olm functions into the environment. // // make sure that we do this *after* populating olm_exports, so that we don't @@ -483,7 +471,11 @@ olm_exports["get_library_version"] = restore_stack(function() { if (typeof module !== 'undefined' && module.exports) { // node / browserify - module.exports = olm_exports; + for (var olm_export in olm_exports) { + if (olm_exports.hasOwnProperty(olm_export)) { + Module[olm_export] = olm_exports[olm_export]; + } + } } if (typeof(window) !== 'undefined') { @@ -492,3 +484,7 @@ if (typeof(window) !== 'undefined') { // Olm in the global scope for browserified and webpacked apps.) window["Olm"] = olm_exports; } + +Module.then(function() { + OLM_ERROR = Module['_olm_error'](); +}); diff --git a/javascript/olm_pre.js b/javascript/olm_pre.js index ae7aba5..5e8ed12 100644 --- a/javascript/olm_pre.js +++ b/javascript/olm_pre.js @@ -1,10 +1,8 @@ var olm_exports = {}; var get_random_values; -var process; // Shadow the process object so that emscripten won't get - // confused by browserify 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) { window.crypto.getRandomValues(buf); }; @@ -12,7 +10,9 @@ if (typeof(window) !== 'undefined') { // We're running in node. var nodeCrypto = require("crypto"); 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); }; process = global["process"]; @@ -20,14 +20,23 @@ if (typeof(window) !== 'undefined') { throw new Error("Cannot find global to attach library to"); } -(function() { - /* applications should define OLM_OPTIONS in the environment to override - * emscripten module settings */ - var Module = {}; - if (typeof(OLM_OPTIONS) !== 'undefined') { - for (var key in OLM_OPTIONS) { - if (OLM_OPTIONS.hasOwnProperty(key)) { - Module[key] = OLM_OPTIONS[key]; - } +/* applications should define OLM_OPTIONS in the environment to override + * emscripten module settings (we still need to (re) declare the variable + * otherwise the closure compiler becomes sad). + */ +var OLM_OPTIONS; +if (typeof(OLM_OPTIONS) !== 'undefined') { + for (var olm_option_key in OLM_OPTIONS) { + if (OLM_OPTIONS.hasOwnProperty(olm_option_key)) { + Module[olm_option_key] = OLM_OPTIONS[olm_option_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; diff --git a/javascript/test/megolm.spec.js b/javascript/test/megolm.spec.js index 8f9d24a..9d5eb72 100644 --- a/javascript/test/megolm.spec.js +++ b/javascript/test/megolm.spec.js @@ -16,12 +16,15 @@ limitations under the License. "use strict"; -var Olm = require('../olm'); +var Olm = require('../olm')(); describe("megolm", function() { var aliceSession, bobSession; - beforeEach(function() { + beforeEach(function(done) { + Olm.then(function() { + done(); + }); aliceSession = new Olm.OutboundGroupSession(); bobSession = new Olm.InboundGroupSession(); }); diff --git a/javascript/test/olm.spec.js b/javascript/test/olm.spec.js index b7cc3ae..94fa87b 100644 --- a/javascript/test/olm.spec.js +++ b/javascript/test/olm.spec.js @@ -16,7 +16,7 @@ limitations under the License. "use strict"; -var Olm = require('../olm'); +var Olm = require('../olm')(); if (!Object.keys) { Object.keys = function(o) { @@ -30,7 +30,13 @@ describe("olm", function() { var aliceAccount, bobAccount; var aliceSession, bobSession; - beforeEach(function() { + beforeEach(function(done) { + // This should really be in a beforeAll, but jasmine-node + // doesn't support that + Olm.then(function() { + done(); + }); + aliceAccount = new Olm.Account(); bobAccount = new Olm.Account(); aliceSession = new Olm.Session(); diff --git a/javascript/test/pk.spec.js b/javascript/test/pk.spec.js index aec90ac..9f7dbfd 100644 --- a/javascript/test/pk.spec.js +++ b/javascript/test/pk.spec.js @@ -16,7 +16,7 @@ limitations under the License. "use strict"; -var Olm = require('../olm'); +var Olm = require('../olm')(); if (!Object.keys) { Object.keys = function(o) { @@ -29,7 +29,11 @@ if (!Object.keys) { describe("pk", function() { var encryption, decryption; - beforeEach(function() { + beforeEach(function(done) { + Olm.then(function() { + done(); + }); + encryption = new Olm.PkEncryption(); decryption = new Olm.PkDecryption(); }); From 5e87db615a5e430627b17da5dfbd52f0ef7f4db9 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 21 Sep 2018 16:35:17 +0100 Subject: [PATCH 04/27] Make OLM_OPTIONS work again The closure compiler was just renaming the variable so it never would have picked them up. Make it an extern so it knows what to do. --- Makefile | 3 ++- javascript/externs.js | 1 + javascript/olm_pre.js | 4 +--- 3 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 javascript/externs.js diff --git a/Makefile b/Makefile index f6c2ab4..dcd5cc1 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ JS_TARGET := javascript/olm.js 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 @@ -147,7 +148,7 @@ js: $(JS_TARGET) .PHONY: js $(JS_TARGET): $(JS_OBJECTS) $(JS_PRE) $(JS_POST) $(JS_EXPORTED_FUNCTIONS) - $(EMCC_LINK) \ + EMCC_CLOSURE_ARGS="--externs $(JS_EXTERNS)" $(EMCC_LINK) \ $(foreach f,$(JS_PRE),--pre-js $(f)) \ $(foreach f,$(JS_POST),--post-js $(f)) \ -s "EXPORTED_FUNCTIONS=@$(JS_EXPORTED_FUNCTIONS)" \ diff --git a/javascript/externs.js b/javascript/externs.js new file mode 100644 index 0000000..8ec5b02 --- /dev/null +++ b/javascript/externs.js @@ -0,0 +1 @@ +var OLM_OPTIONS; diff --git a/javascript/olm_pre.js b/javascript/olm_pre.js index 5e8ed12..673b868 100644 --- a/javascript/olm_pre.js +++ b/javascript/olm_pre.js @@ -21,10 +21,8 @@ if (typeof(window) !== 'undefined') { } /* applications should define OLM_OPTIONS in the environment to override - * emscripten module settings (we still need to (re) declare the variable - * otherwise the closure compiler becomes sad). + * emscripten module settings */ -var OLM_OPTIONS; if (typeof(OLM_OPTIONS) !== 'undefined') { for (var olm_option_key in OLM_OPTIONS) { if (OLM_OPTIONS.hasOwnProperty(olm_option_key)) { From f29d8cdd7bf1faf8294f51624c633fb05c9a0e2f Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 21 Sep 2018 16:39:04 +0100 Subject: [PATCH 05/27] Also ship the wasm file --- javascript/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/javascript/package.json b/javascript/package.json index 9cae60e..efe3705 100644 --- a/javascript/package.json +++ b/javascript/package.json @@ -5,6 +5,7 @@ "main": "olm.js", "files": [ "olm.js", + "olm.wasm", "README.md" ], "scripts": { From 263b94428a24caaa5b899ed7f73b896620e6cdf4 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 25 Sep 2018 17:13:29 +0100 Subject: [PATCH 06/27] Another day, another interface Change the interface again, hopefully this time a bit more normal. Now we wrap the emscripten module completely and just expose the high level objects. The olm library export is now imported as normal (ie. returns a module rather than a function returning a module) but has an `init` method which *must* be called. This returns a promise which resolves when the module is ready. It also rejects if the module failed to set up, unlike before (and unlike the promise-not-a-promise that emscripten returns). Generally catch failures to init the module. --- Makefile | 28 +++++++++++++++++++++++++++- javascript/externs.js | 3 +++ javascript/olm_post.js | 30 +++++++----------------------- javascript/olm_pre.js | 1 - javascript/olm_prefix.js | 3 +++ javascript/olm_suffix.js | 23 +++++++++++++++++++++++ javascript/test/megolm.spec.js | 10 ++++++---- javascript/test/olm.spec.js | 16 +++++++++------- javascript/test/pk.spec.js | 10 +++++----- 9 files changed, 83 insertions(+), 41 deletions(-) create mode 100644 javascript/olm_prefix.js create mode 100644 javascript/olm_suffix.js diff --git a/Makefile b/Makefile index dcd5cc1..d99c8fc 100644 --- a/Makefile +++ b/Makefile @@ -41,11 +41,22 @@ FUZZER_BINARIES := $(addprefix $(BUILD_DIR)/,$(basename $(FUZZER_SOURCES))) FUZZER_DEBUG_BINARIES := $(patsubst $(BUILD_DIR)/fuzzers/fuzz_%,$(BUILD_DIR)/fuzzers/debug_%,$(FUZZER_BINARIES)) TEST_BINARIES := $(patsubst tests/%,$(BUILD_DIR)/tests/%,$(basename $(TEST_SOURCES))) 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_POST := javascript/olm_outbound_group_session.js \ javascript/olm_inbound_group_session.js \ javascript/olm_pk.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/megolm.html \ docs/olm.html \ @@ -67,6 +78,15 @@ EMCCFLAGS = --closure 1 --memory-init-file 0 -s NO_FILESYSTEM=1 -s INVOKE_RUN=0 # longer needed. 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). +EMCCFLAGS += -s TOTAL_STACK=65536 -s TOTAL_MEMORY=262144 + + EMCC.c = $(EMCC) $(CFLAGS) $(CPPFLAGS) -c EMCC.cc = $(EMCC) $(CXXFLAGS) $(CPPFLAGS) -c EMCC_LINK = $(EMCC) $(LDFLAGS) $(EMCCFLAGS) @@ -147,13 +167,19 @@ $(STATIC_RELEASE_TARGET): $(RELEASE_OBJECTS) js: $(JS_TARGET) .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 +# wasm file baked into the js, hence messing around outputting to olm.js +# and then renaming it. +$(JS_TARGET): $(JS_OBJECTS) $(JS_PRE) $(JS_POST) $(JS_EXPORTED_FUNCTIONS) $(JS_PREFIX) $(JS_SUFFIX) EMCC_CLOSURE_ARGS="--externs $(JS_EXTERNS)" $(EMCC_LINK) \ $(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) diff --git a/javascript/externs.js b/javascript/externs.js index 8ec5b02..752e937 100644 --- a/javascript/externs.js +++ b/javascript/externs.js @@ -1 +1,4 @@ var OLM_OPTIONS; +var olm_exports; +var onInitSuccess; +var onInitFail; diff --git a/javascript/olm_post.js b/javascript/olm_post.js index 071021c..9e0294a 100644 --- a/javascript/olm_post.js +++ b/javascript/olm_post.js @@ -464,27 +464,11 @@ olm_exports["get_library_version"] = restore_stack(function() { ]; }); -// 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 - for (var olm_export in olm_exports) { - if (olm_exports.hasOwnProperty(olm_export)) { - Module[olm_export] = olm_exports[olm_export]; - } - } -} - -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; -} - -Module.then(function() { +Module['onRuntimeInitialized'] = function() { OLM_ERROR = Module['_olm_error'](); -}); + if (onInitSuccess) onInitSuccess(); +}; + +Module['onAbort'] = function(err) { + if (onInitFail) onInitFail(err); +}; diff --git a/javascript/olm_pre.js b/javascript/olm_pre.js index 673b868..4feff97 100644 --- a/javascript/olm_pre.js +++ b/javascript/olm_pre.js @@ -1,4 +1,3 @@ -var olm_exports = {}; var get_random_values; if (typeof(window) !== 'undefined') { diff --git a/javascript/olm_prefix.js b/javascript/olm_prefix.js new file mode 100644 index 0000000..b33dfe9 --- /dev/null +++ b/javascript/olm_prefix.js @@ -0,0 +1,3 @@ +var olm_exports = {}; +var onInitSuccess; +var onInitFail; diff --git a/javascript/olm_suffix.js b/javascript/olm_suffix.js new file mode 100644 index 0000000..023c0a5 --- /dev/null +++ b/javascript/olm_suffix.js @@ -0,0 +1,23 @@ +olm_exports['init'] = function() { + return new Promise(function(resolve, reject) { + onInitSuccess = function() { + resolve(); + }; + onInitFail = function(err) { + reject(err); + }; + Module(); + }); +}; + +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; +} + +// 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; diff --git a/javascript/test/megolm.spec.js b/javascript/test/megolm.spec.js index 9d5eb72..241d4bd 100644 --- a/javascript/test/megolm.spec.js +++ b/javascript/test/megolm.spec.js @@ -1,5 +1,6 @@ /* Copyright 2016 OpenMarket Ltd +Copyright 2018 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,17 +17,18 @@ limitations under the License. "use strict"; -var Olm = require('../olm')(); +var Olm = require('../olm'); describe("megolm", function() { var aliceSession, bobSession; beforeEach(function(done) { - Olm.then(function() { + Olm.init().then(function() { + aliceSession = new Olm.OutboundGroupSession(); + bobSession = new Olm.InboundGroupSession(); + done(); }); - aliceSession = new Olm.OutboundGroupSession(); - bobSession = new Olm.InboundGroupSession(); }); afterEach(function() { diff --git a/javascript/test/olm.spec.js b/javascript/test/olm.spec.js index 94fa87b..77dd712 100644 --- a/javascript/test/olm.spec.js +++ b/javascript/test/olm.spec.js @@ -1,5 +1,6 @@ /* Copyright 2016 OpenMarket Ltd +Copyright 2018 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,7 +17,7 @@ limitations under the License. "use strict"; -var Olm = require('../olm')(); +var Olm = require('../olm'); if (!Object.keys) { Object.keys = function(o) { @@ -33,14 +34,15 @@ describe("olm", function() { beforeEach(function(done) { // This should really be in a beforeAll, but jasmine-node // doesn't support that - Olm.then(function() { + debugger; + Olm.init().then(function() { + aliceAccount = new Olm.Account(); + bobAccount = new Olm.Account(); + aliceSession = new Olm.Session(); + bobSession = new Olm.Session(); + done(); }); - - aliceAccount = new Olm.Account(); - bobAccount = new Olm.Account(); - aliceSession = new Olm.Session(); - bobSession = new Olm.Session(); }); afterEach(function() { diff --git a/javascript/test/pk.spec.js b/javascript/test/pk.spec.js index 9f7dbfd..007882f 100644 --- a/javascript/test/pk.spec.js +++ b/javascript/test/pk.spec.js @@ -16,7 +16,7 @@ limitations under the License. "use strict"; -var Olm = require('../olm')(); +var Olm = require('../olm'); if (!Object.keys) { Object.keys = function(o) { @@ -30,12 +30,12 @@ describe("pk", function() { var encryption, decryption; beforeEach(function(done) { - Olm.then(function() { + Olm.init().then(function() { + encryption = new Olm.PkEncryption(); + decryption = new Olm.PkDecryption(); + done(); }); - - encryption = new Olm.PkEncryption(); - decryption = new Olm.PkDecryption(); }); afterEach(function () { From dfbe8a4796747b0a732f0eb322a37de99a2d2eb9 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 25 Sep 2018 17:48:17 +0100 Subject: [PATCH 07/27] Return same promise if init() called many times So we only init the library once. --- javascript/olm_suffix.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/javascript/olm_suffix.js b/javascript/olm_suffix.js index 023c0a5..ec0e39b 100644 --- a/javascript/olm_suffix.js +++ b/javascript/olm_suffix.js @@ -1,5 +1,8 @@ +var olmInitPromise; + olm_exports['init'] = function() { - return new Promise(function(resolve, reject) { + if (olmInitPromise) return olmInitPromise; + olmInitPromise = new Promise(function(resolve, reject) { onInitSuccess = function() { resolve(); }; @@ -8,6 +11,7 @@ olm_exports['init'] = function() { }; Module(); }); + return olmInitPromise; }; if (typeof(window) !== 'undefined') { From 498562fa65c7ba790b3cdaf0e8e4568765bdfc8f Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 25 Sep 2018 18:03:31 +0100 Subject: [PATCH 08/27] Breking change --- CHANGELOG.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index fa1eccb..9160ff1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,11 @@ +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. + Changes in `2.3.0 `_ This release includes the following changes since 2.2.2: From c4a39186862b61915cab1e98e6eed417878020cd Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 26 Sep 2018 16:38:39 +0100 Subject: [PATCH 09/27] Support passing olm options into init() --- javascript/olm_suffix.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/javascript/olm_suffix.js b/javascript/olm_suffix.js index ec0e39b..7f19953 100644 --- a/javascript/olm_suffix.js +++ b/javascript/olm_suffix.js @@ -1,7 +1,10 @@ var olmInitPromise; -olm_exports['init'] = function() { +olm_exports['init'] = function(opts) { if (olmInitPromise) return olmInitPromise; + + if (opts) OLM_OPTIONS = opts; + olmInitPromise = new Promise(function(resolve, reject) { onInitSuccess = function() { resolve(); From 8f6e0557eeb3afe1088ce1abfc7351eb697eea24 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 27 Sep 2018 18:45:00 +0100 Subject: [PATCH 10/27] oops, fix typo - thanks to @dest4 --- docs/olm.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/olm.rst b/docs/olm.rst index 9f82c8e..9c13478 100644 --- a/docs/olm.rst +++ b/docs/olm.rst @@ -72,7 +72,7 @@ info. 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 previous chain key as the key. From 0ad32c9896864963d61f8c3723819295991a51d5 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 1 Oct 2018 13:22:04 +0100 Subject: [PATCH 11/27] Call appropriate wrapper function Don't think this matters since there's no PkEncryption / PkDecryption object being passed, but for the sake of consistency --- javascript/olm_pk.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/olm_pk.js b/javascript/olm_pk.js index 2542707..25db29a 100644 --- a/javascript/olm_pk.js +++ b/javascript/olm_pk.js @@ -123,7 +123,7 @@ PkDecryption.prototype['generate_key'] = restore_stack(function () { Module['_olm_pk_generate_key_random_length'] )(); var random_buffer = random_stack(random_length); - var pubkey_length = pk_encryption_method( + var pubkey_length = pk_decryption_method( Module['_olm_pk_key_length'] )(); var pubkey_buffer = stack(pubkey_length + NULL_BYTE_PADDING_LENGTH); From 2835110cee6c9a47b869a13c069d07d9ed9b2833 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 1 Oct 2018 20:01:47 +0100 Subject: [PATCH 12/27] Remove trailing letter 'K's from the test pubkeys base64 encoded newlines somehow? --- tests/test_pk.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_pk.cpp b/tests/test_pk.cpp index ab1f477..ee12603 100644 --- a/tests/test_pk.cpp +++ b/tests/test_pk.cpp @@ -23,7 +23,7 @@ std::uint8_t alice_private[32] = { 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] = { 0x5D, 0xAB, 0x08, 0x7E, 0x62, 0x4A, 0x8A, 0x4B, @@ -32,7 +32,7 @@ std::uint8_t bob_private[32] = { 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()]; From 0346145a813cfb719fdf218956cb2f29030134a8 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Oct 2018 12:02:56 +0100 Subject: [PATCH 13/27] Work with PkDecryption keys by their private keys Change interface to allow the app to get the private part of the key and instantiate a decryption object from just the private part of the key. Changes the function generating a key from random bytes to be initialising a key with a private key (because it's exactly the same thing). Exports & imports private key parts as ArrayBuffer at JS level rather than base64 assuming we are moving that way in general. --- include/olm/error.h | 7 +++++++ include/olm/pk.h | 39 ++++++++++++++++++++++++++++---------- javascript/olm_pk.js | 32 +++++++++++++++++++++++++++++-- javascript/test/pk.spec.js | 14 ++++++++++++++ src/error.c | 1 + src/pk.cpp | 29 ++++++++++++++++++++++------ tests/test_pk.cpp | 9 +++++++-- 7 files changed, 111 insertions(+), 20 deletions(-) diff --git a/include/olm/error.h b/include/olm/error.h index 9d44a94..ee2187c 100644 --- a/include/olm/error.h +++ b/include/olm/error.h @@ -51,6 +51,13 @@ enum OlmErrorCode { */ 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 * this list. */ }; diff --git a/include/olm/pk.h b/include/olm/pk.h index 1f3f9ff..5e779ce 100644 --- a/include/olm/pk.h +++ b/include/olm/pk.h @@ -76,7 +76,7 @@ size_t olm_pk_encrypt_random_length( * 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 - * "NOT_ENOUGH_RANDOM". */ + * "OLM_INPUT_BUFFER_TOO_SMALL". */ size_t olm_pk_encrypt( OlmPkEncryption *encryption, void const * plaintext, size_t plaintext_length, @@ -108,18 +108,24 @@ size_t olm_clear_pk_decryption( OlmPkDecryption *decryption ); -/** The number of random bytes needed to generate a new key. */ -size_t olm_pk_generate_key_random_length(void); +/** Get the number of bytes required to store an olm private key + */ +size_t olm_pk_private_key_length(); -/** Generate a new key to use for decrypting messages. The associated public - * key will be written to the pubkey buffer. Returns olm_error() on failure. If - * the pubkey buffer is too small then olm_pk_decryption_last_error() will be - * "OUTPUT_BUFFER_TOO_SMALL". If there weren't enough random bytes then - * olm_pk_decryption_last_error() will be "NOT_ENOUGH_RANDOM". */ -size_t olm_pk_generate_key( +/** Initialise the key from the private part of a key as returned by + * olm_pk_get_private_key(). The associated public key will be written to the + * pubkey buffer. Returns olm_error() on failure. If the pubkey buffer is too + * small then olm_pk_decryption_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". + * 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 * random, size_t random_length + void * privkey, size_t privkey_length ); /** Returns the number of bytes needed to store a decryption object. */ @@ -171,6 +177,19 @@ size_t olm_pk_decrypt( 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 } #endif diff --git a/javascript/olm_pk.js b/javascript/olm_pk.js index 25e0fee..2212470 100644 --- a/javascript/olm_pk.js +++ b/javascript/olm_pk.js @@ -118,16 +118,32 @@ PkDecryption.prototype['free'] = function() { free(this.ptr); } +PkDecryption.prototype['init_with_private_key'] = restore_stack(function (private_key) { + var private_key_buffer = stack(private_key.length); + Module['HEAPU8'].set(private_key, private_key_buffer); + + 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, + 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_generate_key_random_length'] + Module['_olm_pk_private_key_length'] )(); var random_buffer = random_stack(random_length); var pubkey_length = pk_encryption_method( Module['_olm_pk_key_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, random_buffer, random_length @@ -135,6 +151,18 @@ PkDecryption.prototype['generate_key'] = restore_stack(function () { 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) { var key_array = array_from_string(key); var pickle_length = pk_decryption_method( diff --git a/javascript/test/pk.spec.js b/javascript/test/pk.spec.js index 007882f..d155cf5 100644 --- a/javascript/test/pk.spec.js +++ b/javascript/test/pk.spec.js @@ -49,6 +49,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 () { var TEST_TEXT='têst1'; var pubkey = decryption.generate_key(); diff --git a/src/error.c b/src/error.c index f541a93..5147b5c 100644 --- a/src/error.c +++ b/src/error.c @@ -31,6 +31,7 @@ static const char * ERRORS[] = { "UNKNOWN_MESSAGE_INDEX", "BAD_LEGACY_ACCOUNT_PICKLE", "BAD_SIGNATURE", + "OLM_INPUT_BUFFER_TOO_SMALL", }; const char * _olm_error_to_string(enum OlmErrorCode error) diff --git a/src/pk.cpp b/src/pk.cpp index e646dc4..20ab991 100644 --- a/src/pk.cpp +++ b/src/pk.cpp @@ -176,7 +176,7 @@ size_t olm_clear_pk_decryption( return sizeof(OlmPkDecryption); } -size_t olm_pk_generate_key_random_length(void) { +size_t olm_pk_private_key_length(void) { return CURVE25519_KEY_LENGTH; } @@ -184,23 +184,23 @@ size_t olm_pk_key_length(void) { return olm::encode_base64_length(CURVE25519_KEY_LENGTH); } -size_t olm_pk_generate_key( +size_t olm_pk_key_from_private( OlmPkDecryption * decryption, void * pubkey, size_t pubkey_length, - void * random, size_t random_length + void * privkey, size_t privkey_length ) { if (pubkey_length < olm_pk_key_length()) { decryption->last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; 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 = - OlmErrorCode::OLM_NOT_ENOUGH_RANDOM; + OlmErrorCode::OLM_INPUT_BUFFER_TOO_SMALL; 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); return 0; } @@ -352,4 +352,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(); +} + } diff --git a/tests/test_pk.cpp b/tests/test_pk.cpp index ee12603..42cc8c9 100644 --- a/tests/test_pk.cpp +++ b/tests/test_pk.cpp @@ -36,7 +36,7 @@ const std::uint8_t *bob_public = (std::uint8_t *) "3p7bfXt9wbTTW2HC7OQ1Nz+DQ8hbe std::uint8_t pubkey[::olm_pk_key_length()]; -olm_pk_generate_key( +olm_pk_key_from_private( decryption, pubkey, sizeof(pubkey), alice_private, sizeof(alice_private) @@ -44,6 +44,11 @@ olm_pk_generate_key( 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()]; 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()]; -olm_pk_generate_key( +olm_pk_key_from_private( decryption, pubkey, sizeof(pubkey), alice_private, sizeof(alice_private) From 8635d68ba8546b333f0c9cdff4f0e6c4b915c159 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Oct 2018 12:09:33 +0100 Subject: [PATCH 14/27] Add other breaking change --- CHANGELOG.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9160ff1..6b450b4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,10 @@ 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 `_ This release includes the following changes since 2.2.2: From e521ee84c5a96f478c6d9b10e90edb47549baf5f Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Oct 2018 19:21:05 +0100 Subject: [PATCH 15/27] Add an export for the length of a private key --- javascript/olm_post.js | 1 + 1 file changed, 1 insertion(+) diff --git a/javascript/olm_post.js b/javascript/olm_post.js index 9e0294a..fffffad 100644 --- a/javascript/olm_post.js +++ b/javascript/olm_post.js @@ -466,6 +466,7 @@ olm_exports["get_library_version"] = restore_stack(function() { Module['onRuntimeInitialized'] = function() { OLM_ERROR = Module['_olm_error'](); + olm_exports["PRIVATE_KEY_LENGTH"] = Module['_olm_pk_private_key_length'](); if (onInitSuccess) onInitSuccess(); }; From b1beadaceeac5556c5e4932155a58c300cf1d39b Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Oct 2018 10:37:24 +0100 Subject: [PATCH 16/27] CircleCI config file --- .circleci/config.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..0ef608b --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,23 @@ +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: JS Tests + working_directory: ~/repo/javascript + command: npm run test From 3e775938e50bffdfcb20241d598fea74ddaaf7e0 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 3 Oct 2018 16:06:15 +0100 Subject: [PATCH 17/27] Replace the impenetrable line of perl with python Mostly because the standard emscripten docker image does not have libjson-perl, but python always comes with json. But also because it was impenetrable. --- Makefile | 2 +- exports.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100755 exports.py diff --git a/Makefile b/Makefile index 154954c..901e78f 100644 --- a/Makefile +++ b/Makefile @@ -164,7 +164,7 @@ fuzzers: $(FUZZER_BINARIES) $(FUZZER_DEBUG_BINARIES) .PHONY: fuzzers $(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 $@ all: test js lib debug doc diff --git a/exports.py b/exports.py new file mode 100755 index 0000000..b37cbbb --- /dev/null +++ b/exports.py @@ -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) From 8161fa51a8d92da400114f20e101fd1774145005 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 3 Oct 2018 16:24:21 +0100 Subject: [PATCH 18/27] run npm install --- .circleci/config.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0ef608b..07fa3fb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -17,6 +17,9 @@ jobs: - run: name: JS Compile command: make js + - run: + name: Install JS Deps + command: npm install - run: name: JS Tests working_directory: ~/repo/javascript From 031eb2dc7591b85937668513d44a08991f4b3d8e Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 3 Oct 2018 16:26:17 +0100 Subject: [PATCH 19/27] ...in the right dir --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 07fa3fb..0a891db 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -19,6 +19,7 @@ jobs: command: make js - run: name: Install JS Deps + working_directory: ~/repo/javascript command: npm install - run: name: JS Tests From 602c00a8d658e8510e37e841dd06c70f276d0f00 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 4 Oct 2018 20:09:54 +0100 Subject: [PATCH 20/27] Dual-build wasm and asm.js olm --- Makefile | 28 +++++++++++++++++++++++----- javascript/olm_post.js | 9 --------- javascript/olm_pre.js | 9 +++++++++ javascript/olm_suffix.js | 10 ++++++---- 4 files changed, 38 insertions(+), 18 deletions(-) diff --git a/Makefile b/Makefile index d99c8fc..f45762d 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,8 @@ AR = ar RELEASE_TARGET := $(BUILD_DIR)/libolm.so.$(VERSION) STATIC_RELEASE_TARGET := $(BUILD_DIR)/libolm.a 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_EXTRA_EXPORTED_RUNTIME_METHODS := ALLOC_STACK @@ -84,8 +85,11 @@ EMCCFLAGS += -s NO_BROWSER=1 # 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). -EMCCFLAGS += -s TOTAL_STACK=65536 -s TOTAL_MEMORY=262144 +# (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.cc = $(EMCC) $(CXXFLAGS) $(CPPFLAGS) -c @@ -121,7 +125,8 @@ $(FUZZER_DEBUG_BINARIES): LDFLAGS += $(DEBUG_OPTIMIZE_FLAGS) $(JS_OBJECTS): CFLAGS += $(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 ifeq ($(shell echo "check_quotes"),"check_quotes") @@ -164,14 +169,27 @@ static: $(STATIC_RELEASE_TARGET) $(STATIC_RELEASE_TARGET): $(RELEASE_OBJECTS) $(AR) rcs $@ $^ -js: $(JS_TARGET) +js: $(JS_WASM_TARGET) $(JS_ASMJS_TARGET) .PHONY: js # Note that the output file we give to emcc determines the name of the # wasm file baked into the js, hence messing around outputting to olm.js # and then renaming it. -$(JS_TARGET): $(JS_OBJECTS) $(JS_PRE) $(JS_POST) $(JS_EXPORTED_FUNCTIONS) $(JS_PREFIX) $(JS_SUFFIX) +$(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_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 + +$(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)" \ diff --git a/javascript/olm_post.js b/javascript/olm_post.js index 9e0294a..21ea890 100644 --- a/javascript/olm_post.js +++ b/javascript/olm_post.js @@ -463,12 +463,3 @@ olm_exports["get_library_version"] = restore_stack(function() { getValue(buf+2, 'i8'), ]; }); - -Module['onRuntimeInitialized'] = function() { - OLM_ERROR = Module['_olm_error'](); - if (onInitSuccess) onInitSuccess(); -}; - -Module['onAbort'] = function(err) { - if (onInitFail) onInitFail(err); -}; diff --git a/javascript/olm_pre.js b/javascript/olm_pre.js index 4feff97..18d836d 100644 --- a/javascript/olm_pre.js +++ b/javascript/olm_pre.js @@ -37,3 +37,12 @@ if (typeof(OLM_OPTIONS) !== 'undefined') { * use UTF8ToString. */ var NULL_BYTE_PADDING_LENGTH = 1; + +Module['onRuntimeInitialized'] = function() { + OLM_ERROR = Module['_olm_error'](); + if (onInitSuccess) onInitSuccess(); +}; + +Module['onAbort'] = function(err) { + if (onInitFail) onInitFail(err); +}; diff --git a/javascript/olm_suffix.js b/javascript/olm_suffix.js index 7f19953..3e2f664 100644 --- a/javascript/olm_suffix.js +++ b/javascript/olm_suffix.js @@ -24,7 +24,9 @@ if (typeof(window) !== 'undefined') { window["Olm"] = olm_exports; } -// 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; +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; +} From 8520168e0b4c8172847a051e532ca4deaec46a95 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Mon, 9 Jul 2018 23:21:55 -0400 Subject: [PATCH 21/27] fix some code style issues and typos --- android/olm-sdk/src/main/jni/olm_pk.cpp | 55 ++++++++++++++++------- include/olm/pk.h | 9 ++-- javascript/olm_pk.js | 8 ++-- src/pk.cpp | 58 ++++++++++++++++++------- 4 files changed, 91 insertions(+), 39 deletions(-) diff --git a/android/olm-sdk/src/main/jni/olm_pk.cpp b/android/olm-sdk/src/main/jni/olm_pk.cpp index 2e936c6..5457419 100644 --- a/android/olm-sdk/src/main/jni/olm_pk.cpp +++ b/android/olm-sdk/src/main/jni/olm_pk.cpp @@ -29,7 +29,10 @@ OlmPkEncryption * initializePkEncryptionMemory() { // init encryption object encryptionPtr = olm_pk_encryption(encryptionPtr); - LOGD("## initializePkEncryptionMemory(): success - OLM encryption size=%lu",static_cast(encryptionSize)); + LOGD( + "## initializePkEncryptionMemory(): success - OLM encryption size=%lu", + static_cast(encryptionSize) + ); } else { @@ -53,7 +56,10 @@ JNIEXPORT jlong OLM_PK_ENCRYPTION_FUNC_DEF(createNewPkEncryptionJni)(JNIEnv *env else { 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) @@ -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; jbyte *keyPtr = NULL; @@ -116,10 +123,13 @@ JNIEXPORT void OLM_PK_ENCRYPTION_FUNC_DEF(setRecipientKeyJni)(JNIEnv *env, jobje } 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); - 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,8 +144,9 @@ 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; const char* errorMessage = NULL; jbyte *plaintextPtr = NULL; @@ -161,8 +172,8 @@ JNIEXPORT jbyteArray OLM_PK_ENCRYPTION_FUNC_DEF(encryptJni)(JNIEnv *env, jobject } else if (!(encryptedMsgJClass = env->GetObjectClass(aEncryptedMsg))) { - LOGE(" ## pkEncryptJni(): failure - unable to get crypted message class"); - errorMessage = "unable to get crypted message class"; + LOGE(" ## pkEncryptJni(): failure - unable to get encrypted message class"); + errorMessage = "unable to get encrypted message class"; } else if (!(macFieldId = env->GetFieldID(encryptedMsgJClass, "mMac", "Ljava/lang/String;"))) { @@ -226,7 +237,9 @@ JNIEXPORT jbyteArray OLM_PK_ENCRYPTION_FUNC_DEF(encryptJni)(JNIEnv *env, jobject else { encryptedMsgRet = env->NewByteArray(ciphertextLength); - env->SetByteArrayRegion(encryptedMsgRet, 0, ciphertextLength, (jbyte*)ciphertextPtr); + env->SetByteArrayRegion( + encryptedMsgRet, 0, ciphertextLength, (jbyte*)ciphertextPtr + ); jstring macStr = env->NewStringUTF((char*)macPtr); env->SetObjectField(aEncryptedMsg, macFieldId, macStr); @@ -276,7 +289,10 @@ OlmPkDecryption * initializePkDecryptionMemory() { // init decryption object decryptionPtr = olm_pk_decryption(decryptionPtr); - LOGD("## initializePkDecryptionMemory(): success - OLM decryption size=%lu",static_cast(decryptionSize)); + LOGD( + "## initializePkDecryptionMemory(): success - OLM decryption size=%lu", + static_cast(decryptionSize) + ); } else { @@ -300,7 +316,10 @@ JNIEXPORT jlong OLM_PK_DECRYPTION_FUNC_DEF(createNewPkDecryptionJni)(JNIEnv *env else { 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) @@ -402,8 +421,9 @@ JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(generateKeyJni)(JNIEnv *env, job 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; OlmPkDecryption *decryptionPtr = getPkDecryptionInstanceId(env, thiz); @@ -528,7 +548,10 @@ JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(decryptJni)(JNIEnv *env, jobject { decryptedMsgRet = env->NewByteArray(plaintextLength); env->SetByteArrayRegion(decryptedMsgRet, 0, plaintextLength, (jbyte*)plaintextPtr); - LOGD("## pkDecryptJni(): success returnedLg=%lu OK", static_cast(plaintextLength)); + LOGD( + "## pkDecryptJni(): success returnedLg=%lu OK", + static_cast(plaintextLength) + ); } } diff --git a/include/olm/pk.h b/include/olm/pk.h index 1f3f9ff..07e6077 100644 --- a/include/olm/pk.h +++ b/include/olm/pk.h @@ -111,9 +111,10 @@ size_t olm_clear_pk_decryption( /** The number of random bytes needed to generate a new key. */ size_t olm_pk_generate_key_random_length(void); -/** Generate a new key to use for decrypting messages. The associated public - * key will be written to the pubkey buffer. Returns olm_error() on failure. If - * the pubkey buffer is too small then olm_pk_decryption_last_error() will be +/** Generate a new key pair to use for decrypting messages. The private key is + * stored in the decryption object, and the associated public key will be + * written to the pubkey buffer. Returns olm_error() on failure. If the pubkey + * buffer is too small then olm_pk_decryption_last_error() will be * "OUTPUT_BUFFER_TOO_SMALL". If there weren't enough random bytes then * olm_pk_decryption_last_error() will be "NOT_ENOUGH_RANDOM". */ size_t olm_pk_generate_key( @@ -164,7 +165,7 @@ size_t olm_pk_max_plaintext_length( * the plaintext buffer is too small then olm_pk_encryption_last_error() will * be "OUTPUT_BUFFER_TOO_SMALL". */ size_t olm_pk_decrypt( - OlmPkDecryption * decrytion, + OlmPkDecryption * decryption, void const * ephemeral_key, size_t ephemeral_key_length, void const * mac, size_t mac_length, void * ciphertext, size_t ciphertext_length, diff --git a/javascript/olm_pk.js b/javascript/olm_pk.js index 25db29a..407eaf1 100644 --- a/javascript/olm_pk.js +++ b/javascript/olm_pk.js @@ -51,7 +51,7 @@ PkEncryption.prototype['encrypt'] = restore_stack(function( )(this.ptr); var mac_buffer = stack(mac_length + NULL_BYTE_PADDING_LENGTH); Module['setValue']( - mac_buffer+mac_length, + mac_buffer + mac_length, 0, "i8" ); var ephemeral_length = pk_encryption_method( @@ -59,7 +59,7 @@ PkEncryption.prototype['encrypt'] = restore_stack(function( )(); var ephemeral_buffer = stack(ephemeral_length + NULL_BYTE_PADDING_LENGTH); Module['setValue']( - ephemeral_buffer+ephemeral_length, + ephemeral_buffer + ephemeral_length, 0, "i8" ); pk_encryption_method(Module['_olm_pk_encrypt'])( @@ -73,7 +73,7 @@ PkEncryption.prototype['encrypt'] = restore_stack(function( // UTF8ToString requires a null-terminated argument, so add the // null terminator. Module['setValue']( - ciphertext_buffer+ciphertext_length, + ciphertext_buffer + ciphertext_length, 0, "i8" ); return { @@ -191,7 +191,7 @@ PkDecryption.prototype['decrypt'] = restore_stack(function ( // UTF8ToString requires a null-terminated argument, so add the // null terminator. Module['setValue']( - plaintext_buffer+plaintext_length, + plaintext_buffer + plaintext_length, 0, "i8" ); return Module['UTF8ToString'](plaintext_buffer); diff --git a/src/pk.cpp b/src/pk.cpp index e646dc4..4c5f50e 100644 --- a/src/pk.cpp +++ b/src/pk.cpp @@ -22,15 +22,15 @@ #include "olm/pickle_encoding.h" #include "olm/pickle.hh" -extern "C" { - 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(""); const struct _olm_cipher *olm_pk_cipher = OLM_CIPHER_BASE(&olm_pk_cipher_aes_sha256); +extern "C" { + struct OlmPkEncryption { OlmErrorCode last_error; _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: 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; } @@ -81,7 +85,9 @@ size_t olm_pk_ciphertext_length( OlmPkEncryption *encryption, 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( @@ -106,9 +112,9 @@ size_t olm_pk_encrypt( ) { if (ciphertext_length < olm_pk_ciphertext_length(encryption, plaintext_length) - || mac_length + || mac_length < _olm_cipher_aes_sha_256_ops.mac_length(olm_pk_cipher) - || ephemeral_key_size + || ephemeral_key_size < olm_pk_key_length()) { encryption->last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; @@ -122,11 +128,16 @@ size_t olm_pk_encrypt( _olm_curve25519_key_pair 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_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 raw_mac[MAC_LENGTH]; size_t result = _olm_cipher_aes_sha_256_ops.encrypt( @@ -201,7 +212,11 @@ size_t olm_pk_generate_key( } _olm_crypto_curve25519_generate_key((uint8_t *) random, &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; } @@ -267,7 +282,10 @@ size_t olm_pickle_pk_decryption( return std::size_t(-1); } pickle(_olm_enc_output_pos(reinterpret_cast(pickled), raw_length), object); - return _olm_enc_output(reinterpret_cast(key), key_length, reinterpret_cast(pickled), raw_length); + return _olm_enc_output( + reinterpret_cast(key), key_length, + reinterpret_cast(pickled), raw_length + ); } size_t olm_unpickle_pk_decryption( @@ -283,7 +301,8 @@ size_t olm_unpickle_pk_decryption( } std::uint8_t * const pos = reinterpret_cast(pickled); std::size_t raw_length = _olm_enc_input( - reinterpret_cast(key), key_length, pos, pickled_length, &object.last_error + reinterpret_cast(key), key_length, + pos, pickled_length, &object.last_error ); if (raw_length == std::size_t(-1)) { return std::size_t(-1); @@ -300,7 +319,11 @@ size_t olm_unpickle_pk_decryption( return std::size_t(-1); } 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; } @@ -309,7 +332,9 @@ size_t olm_pk_max_plaintext_length( OlmPkDecryption * decryption, 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( @@ -327,7 +352,10 @@ size_t olm_pk_decrypt( } 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_crypto_curve25519_shared_secret(&decryption->key_pair, &ephemeral, secret); uint8_t raw_mac[MAC_LENGTH]; From bad14db8dadac46fef2c1c7094c92831b4bed0fb Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Mon, 9 Jul 2018 23:35:40 -0400 Subject: [PATCH 22/27] remove unneeded polyfill --- javascript/test/pk.spec.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/javascript/test/pk.spec.js b/javascript/test/pk.spec.js index aec90ac..0b27470 100644 --- a/javascript/test/pk.spec.js +++ b/javascript/test/pk.spec.js @@ -18,14 +18,6 @@ limitations under the License. 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() { var encryption, decryption; From 173339ae9accddd184bc83f2c23c5ffae3b08d00 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 12 Jul 2018 17:54:03 -0400 Subject: [PATCH 23/27] add more comments describing the pk encrypt/decrypt functions --- include/olm/pk.h | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/include/olm/pk.h b/include/olm/pk.h index 07e6077..8804d1f 100644 --- a/include/olm/pk.h +++ b/include/olm/pk.h @@ -72,11 +72,15 @@ size_t olm_pk_encrypt_random_length( ); /** Encrypt a plaintext for the recipient set using - * olm_pk_encryption_set_recipient_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 - * "NOT_ENOUGH_RANDOM". */ + * olm_pk_encryption_set_recipient_key. Writes to the ciphertext, mac, and + * ephemeral_key buffers, whose values should be sent to the recipient. mac is + * a Message Authentication Code to ensure that the data is received and + * decrypted properly. ephemeral_key is the public part of the ephemeral key + * 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 "NOT_ENOUGH_RANDOM". */ size_t olm_pk_encrypt( OlmPkEncryption *encryption, void const * plaintext, size_t plaintext_length, @@ -160,10 +164,11 @@ size_t olm_pk_max_plaintext_length( size_t ciphertext_length ); -/** Decrypt a ciphertext. The input ciphertext buffer is destroyed. Returns - * the length of the plaintext on success. Returns olm_error() on failure. If - * the plaintext buffer is too small then olm_pk_encryption_last_error() will - * be "OUTPUT_BUFFER_TOO_SMALL". */ +/** Decrypt a ciphertext. The input ciphertext buffer is destroyed. See the + * olm_pk_encrypt function for descriptions of the ephemeral_key and mac + * arguments. Returns the length of the plaintext on success. Returns + * 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( OlmPkDecryption * decryption, void const * ephemeral_key, size_t ephemeral_key_length, From 713e9aeb4d63732f7671f9c7ac3d9c4897449583 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 20 Sep 2018 11:31:09 +0100 Subject: [PATCH 24/27] Build on mac --- Makefile | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 154954c..4dd1f4b 100644 --- a/Makefile +++ b/Makefile @@ -14,9 +14,19 @@ AFL_CC = afl-gcc AFL_CXX = afl-g++ 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 -DEBUG_TARGET := $(BUILD_DIR)/libolm_debug.so.$(VERSION) +DEBUG_TARGET := $(BUILD_DIR)/libolm_debug.$(SO).$(VERSION) JS_TARGET := javascript/olm.js JS_EXPORTED_FUNCTIONS := javascript/exported_functions.json @@ -121,20 +131,18 @@ lib: $(RELEASE_TARGET) $(RELEASE_TARGET): $(RELEASE_OBJECTS) $(CXX) $(LDFLAGS) --shared -fPIC \ - -Wl,-soname,libolm.so.$(MAJOR) \ - -Wl,--version-script,version_script.ver \ + $(OLM_LDFLAGS) \ $(OUTPUT_OPTION) $(RELEASE_OBJECTS) - ln -sf libolm.so.$(VERSION) $(BUILD_DIR)/libolm.so.$(MAJOR) + ln -sf libolm.$(SO).$(VERSION) $(BUILD_DIR)/libolm.$(SO).$(MAJOR) debug: $(DEBUG_TARGET) .PHONY: debug $(DEBUG_TARGET): $(DEBUG_OBJECTS) $(CXX) $(LDFLAGS) --shared -fPIC \ - -Wl,-soname,libolm_debug.so.$(MAJOR) \ - -Wl,--version-script,version_script.ver \ + $(OLM_LDFLAGS) \ $(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) .PHONY: static @@ -177,16 +185,16 @@ install-headers: $(PUBLIC_HEADERS) install-debug: debug install-headers test -d $(DESTDIR)$(PREFIX)/lib || $(call mkdir,$(DESTDIR)$(PREFIX)/lib) - 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 + 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) .PHONY: install-debug install: lib install-headers test -d $(DESTDIR)$(PREFIX)/lib || $(call mkdir,$(DESTDIR)$(PREFIX)/lib) - 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 + 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) .PHONY: install clean:; From 1dbb060c44423048d9782b9a9977e51cc8f43b8d Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 10 Oct 2018 19:40:01 +0100 Subject: [PATCH 25/27] Add note about passing through env var with docker --- README.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.rst b/README.rst index 4f33c00..5a5413e 100644 --- a/README.rst +++ b/README.rst @@ -31,6 +31,9 @@ To build the javascript bindings, install emscripten from http://kripken.github. 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: .. code:: bash From fac1d52dfe25d8bf6119cc41645a84c9111c6f6e Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 11 Oct 2018 18:16:39 +0100 Subject: [PATCH 26/27] Add aliases for deprecated functions --- include/olm/pk.h | 12 ++++++++++++ src/pk.cpp | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/include/olm/pk.h b/include/olm/pk.h index 8748506..4278fca 100644 --- a/include/olm/pk.h +++ b/include/olm/pk.h @@ -116,6 +116,10 @@ size_t olm_clear_pk_decryption( */ size_t olm_pk_private_key_length(); +/** DEPRECATED: Use olm_pk_private_key_length() + */ +size_t olm_pk_generate_key_random_length(void); + /** Initialise the key from the private part of a key as returned by * olm_pk_get_private_key(). The associated public key will be written to the * pubkey buffer. Returns olm_error() on failure. If the pubkey buffer is too @@ -132,6 +136,14 @@ size_t olm_pk_key_from_private( void * privkey, size_t privkey_length ); +/** DEPRECATED: Use olm_pk_key_from_private + */ +size_t olm_pk_generate_key( + OlmPkDecryption * decryption, + void * pubkey, size_t pubkey_length, + void * privkey, size_t privkey_length +); + /** Returns the number of bytes needed to store a decryption object. */ size_t olm_pickle_pk_decryption_length( OlmPkDecryption * decryption diff --git a/src/pk.cpp b/src/pk.cpp index 5ee35d9..5cfcea2 100644 --- a/src/pk.cpp +++ b/src/pk.cpp @@ -191,6 +191,10 @@ size_t olm_pk_private_key_length(void) { 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) { return olm::encode_base64_length(CURVE25519_KEY_LENGTH); } @@ -220,6 +224,14 @@ size_t olm_pk_key_from_private( 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 { static const std::uint32_t PK_DECRYPTION_PICKLE_VERSION = 1; From af86a9a8b899eeb3c1c464cb0c54218acd788fa6 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Wed, 10 Oct 2018 15:06:58 -0400 Subject: [PATCH 27/27] clear out plaintext buffers in Android SDK where possible --- .../java/org/matrix/olm/OlmInboundGroupSession.java | 5 ++++- .../java/org/matrix/olm/OlmOutboundGroupSession.java | 6 +++++- .../main/java/org/matrix/olm/OlmPkDecryption.java | 7 ++++++- .../main/java/org/matrix/olm/OlmPkEncryption.java | 6 +++++- .../src/main/java/org/matrix/olm/OlmSession.java | 11 +++++++++-- .../src/main/jni/olm_outbound_group_session.cpp | 7 ++++++- android/olm-sdk/src/main/jni/olm_pk.cpp | 8 +++++++- android/olm-sdk/src/main/jni/olm_session.cpp | 12 ++++++++++-- 8 files changed, 52 insertions(+), 10 deletions(-) diff --git a/android/olm-sdk/src/main/java/org/matrix/olm/OlmInboundGroupSession.java b/android/olm-sdk/src/main/java/org/matrix/olm/OlmInboundGroupSession.java index 8c2d7b0..b41c67a 100644 --- a/android/olm-sdk/src/main/java/org/matrix/olm/OlmInboundGroupSession.java +++ b/android/olm-sdk/src/main/java/org/matrix/olm/OlmInboundGroupSession.java @@ -25,6 +25,8 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; +import java.util.Arrays; + /** * Class used to create an inbound Megolm session.
* 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. * @param aEncryptedMsg the message to be decrypted * @return the decrypted message information - * @exception OlmException teh failure reason + * @exception OlmException the failure reason */ public DecryptMessageResult decryptMessage(String aEncryptedMsg) throws OlmException { DecryptMessageResult result = new DecryptMessageResult(); @@ -246,6 +248,7 @@ public class OlmInboundGroupSession extends CommonSerializeUtils implements Seri if (null != decryptedMessageBuffer) { result.mDecryptedMessage = new String(decryptedMessageBuffer, "UTF-8"); + Arrays.fill(decryptedMessageBuffer, (byte) 0); } } catch (Exception e) { Log.e(LOG_TAG, "## decryptMessage() failed " + e.getMessage()); diff --git a/android/olm-sdk/src/main/java/org/matrix/olm/OlmOutboundGroupSession.java b/android/olm-sdk/src/main/java/org/matrix/olm/OlmOutboundGroupSession.java index 0481824..e4d4a44 100644 --- a/android/olm-sdk/src/main/java/org/matrix/olm/OlmOutboundGroupSession.java +++ b/android/olm-sdk/src/main/java/org/matrix/olm/OlmOutboundGroupSession.java @@ -26,6 +26,8 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; +import java.util.Arrays; + /** * Class used to create an outbound a 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)) { 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) { retValue = new String(encryptedBuffer , "UTF-8"); diff --git a/android/olm-sdk/src/main/java/org/matrix/olm/OlmPkDecryption.java b/android/olm-sdk/src/main/java/org/matrix/olm/OlmPkDecryption.java index 03d055a..ea838f1 100644 --- a/android/olm-sdk/src/main/java/org/matrix/olm/OlmPkDecryption.java +++ b/android/olm-sdk/src/main/java/org/matrix/olm/OlmPkDecryption.java @@ -18,6 +18,8 @@ package org.matrix.olm; import android.util.Log; +import java.util.Arrays; + public class OlmPkDecryption { private static final String LOG_TAG = "OlmPkDecryption"; @@ -67,7 +69,10 @@ public class OlmPkDecryption { } 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) { Log.e(LOG_TAG, "## pkDecrypt(): failed " + e.getMessage()); throw new OlmException(OlmException.EXCEPTION_CODE_PK_DECRYPTION_DECRYPT, e.getMessage()); diff --git a/android/olm-sdk/src/main/java/org/matrix/olm/OlmPkEncryption.java b/android/olm-sdk/src/main/java/org/matrix/olm/OlmPkEncryption.java index 9bd429d..a2ccf2e 100644 --- a/android/olm-sdk/src/main/java/org/matrix/olm/OlmPkEncryption.java +++ b/android/olm-sdk/src/main/java/org/matrix/olm/OlmPkEncryption.java @@ -18,6 +18,8 @@ package org.matrix.olm; import android.util.Log; +import java.util.Arrays; + public class OlmPkEncryption { private static final String LOG_TAG = "OlmPkEncryption"; @@ -72,7 +74,9 @@ public class OlmPkEncryption { OlmPkMessage encryptedMsgRetValue = new OlmPkMessage(); 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) { encryptedMsgRetValue.mCipherText = new String(ciphertextBuffer, "UTF-8"); diff --git a/android/olm-sdk/src/main/java/org/matrix/olm/OlmSession.java b/android/olm-sdk/src/main/java/org/matrix/olm/OlmSession.java index da2e963..3c5ce49 100644 --- a/android/olm-sdk/src/main/java/org/matrix/olm/OlmSession.java +++ b/android/olm-sdk/src/main/java/org/matrix/olm/OlmSession.java @@ -25,6 +25,8 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; +import java.util.Arrays; + /** * Session class used to create Olm sessions in conjunction with {@link OlmAccount} class.
* Olm session is used to encrypt data between devices, especially to create Olm group sessions (see {@link OlmOutboundGroupSession} and {@link OlmInboundGroupSession}).
@@ -295,7 +297,9 @@ public class OlmSession extends CommonSerializeUtils implements Serializable { OlmMessage encryptedMsgRetValue = new OlmMessage(); 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) { encryptedMsgRetValue.mCipherText = new String(encryptedMessageBuffer, "UTF-8"); @@ -330,7 +334,10 @@ public class OlmSession extends CommonSerializeUtils implements Serializable { } 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) { Log.e(LOG_TAG, "## decryptMessage(): failed " + e.getMessage()); throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_DECRYPT_MESSAGE, e.getMessage()); diff --git a/android/olm-sdk/src/main/jni/olm_outbound_group_session.cpp b/android/olm-sdk/src/main/jni/olm_outbound_group_session.cpp index a821709..b11c474 100644 --- a/android/olm-sdk/src/main/jni/olm_outbound_group_session.cpp +++ b/android/olm-sdk/src/main/jni/olm_outbound_group_session.cpp @@ -297,6 +297,7 @@ JNIEXPORT jbyteArray OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(encryptMessageJni)(JNIE OlmOutboundGroupSession *sessionPtr = NULL; jbyte* clearMsgPtr = NULL; + jboolean clearMsgIsCopied = JNI_FALSE; 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"); 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"); errorMessage = "clear message JNI allocation OOM"; @@ -359,6 +360,10 @@ JNIEXPORT jbyteArray OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(encryptMessageJni)(JNIE // free alloc if (clearMsgPtr) { + if (clearMsgIsCopied) + { + memset(clearMsgPtr, 0, (size_t)env->GetArrayLength(aClearMsgBuffer)); + } env->ReleaseByteArrayElements(aClearMsgBuffer, clearMsgPtr, JNI_ABORT); } diff --git a/android/olm-sdk/src/main/jni/olm_pk.cpp b/android/olm-sdk/src/main/jni/olm_pk.cpp index 5457419..12528de 100644 --- a/android/olm-sdk/src/main/jni/olm_pk.cpp +++ b/android/olm-sdk/src/main/jni/olm_pk.cpp @@ -150,6 +150,7 @@ JNIEXPORT jbyteArray OLM_PK_ENCRYPTION_FUNC_DEF(encryptJni)( jbyteArray encryptedMsgRet = 0; const char* errorMessage = NULL; jbyte *plaintextPtr = NULL; + jboolean plaintextIsCopied = JNI_FALSE; OlmPkEncryption *encryptionPtr = getPkEncryptionInstanceId(env, thiz); jclass encryptedMsgJClass = 0; @@ -165,7 +166,7 @@ JNIEXPORT jbyteArray OLM_PK_ENCRYPTION_FUNC_DEF(encryptJni)( LOGE(" ## pkEncryptJni(): failure - 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"); errorMessage = "plaintext JNI allocation OOM"; @@ -269,6 +270,10 @@ JNIEXPORT jbyteArray OLM_PK_ENCRYPTION_FUNC_DEF(encryptJni)( if (plaintextPtr) { + if (plaintextIsCopied) + { + memset(plaintextPtr, 0, (size_t)env->GetArrayLength(aPlaintextBuffer)); + } env->ReleaseByteArrayElements(aPlaintextBuffer, plaintextPtr, JNI_ABORT); } @@ -561,6 +566,7 @@ JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(decryptJni)( } if (plaintextPtr) { + memset(plaintextPtr, 0, maxPlaintextLength); free(plaintextPtr); } } diff --git a/android/olm-sdk/src/main/jni/olm_session.cpp b/android/olm-sdk/src/main/jni/olm_session.cpp index 5ca49db..b9db286 100644 --- a/android/olm-sdk/src/main/jni/olm_session.cpp +++ b/android/olm-sdk/src/main/jni/olm_session.cpp @@ -472,6 +472,7 @@ JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobjec OlmSession *sessionPtr = getSessionInstanceId(env, thiz); jbyte *clearMsgPtr = NULL; + jboolean clearMsgIsCopied = JNI_FALSE; jclass encryptedMsgJClass = 0; jfieldID typeMsgFieldId; @@ -490,8 +491,9 @@ JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobjec else if (!aEncryptedMsg) { 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"); errorMessage = "clear message JNI allocation OOM"; @@ -580,6 +582,10 @@ JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobjec // free alloc if (clearMsgPtr) { + if (clearMsgIsCopied) + { + memset(clearMsgPtr, 0, (size_t)env->GetArrayLength(aClearMsgBuffer)); + } 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(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; -} \ No newline at end of file +}