diff options
Diffstat (limited to 'javascript')
-rw-r--r-- | javascript/.gitignore | 1 | ||||
-rw-r--r-- | javascript/externs.js | 4 | ||||
-rw-r--r-- | javascript/olm_inbound_group_session.js | 14 | ||||
-rw-r--r-- | javascript/olm_outbound_group_session.js | 15 | ||||
-rw-r--r-- | javascript/olm_pk.js | 52 | ||||
-rw-r--r-- | javascript/olm_post.js | 59 | ||||
-rw-r--r-- | javascript/olm_pre.js | 34 | ||||
-rw-r--r-- | javascript/olm_prefix.js | 3 | ||||
-rw-r--r-- | javascript/olm_suffix.js | 30 | ||||
-rw-r--r-- | javascript/package.json | 1 | ||||
-rw-r--r-- | javascript/test/megolm.spec.js | 11 | ||||
-rw-r--r-- | javascript/test/olm.spec.js | 18 | ||||
-rw-r--r-- | javascript/test/pk.spec.js | 24 |
13 files changed, 169 insertions, 97 deletions
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/externs.js b/javascript/externs.js new file mode 100644 index 0000000..752e937 --- /dev/null +++ b/javascript/externs.js @@ -0,0 +1,4 @@ +var OLM_OPTIONS; +var olm_exports; +var onInitSuccess; +var onInitFail; 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 407eaf1..4f730dd 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) }; @@ -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_decryption_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( @@ -169,9 +197,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 +218,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..fffffad 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,21 +464,12 @@ 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 - module.exports = olm_exports; -} +Module['onRuntimeInitialized'] = function() { + OLM_ERROR = Module['_olm_error'](); + olm_exports["PRIVATE_KEY_LENGTH"] = Module['_olm_pk_private_key_length'](); + if (onInitSuccess) onInitSuccess(); +}; -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['onAbort'] = function(err) { + if (onInitFail) onInitFail(err); +}; diff --git a/javascript/olm_pre.js b/javascript/olm_pre.js index ae7aba5..4feff97 100644 --- a/javascript/olm_pre.js +++ b/javascript/olm_pre.js @@ -1,10 +1,7 @@ -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 +9,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 +19,21 @@ 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 + */ +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/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..7f19953 --- /dev/null +++ b/javascript/olm_suffix.js @@ -0,0 +1,30 @@ +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; +} + +// 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/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": { diff --git a/javascript/test/megolm.spec.js b/javascript/test/megolm.spec.js index 8f9d24a..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. @@ -21,9 +22,13 @@ var Olm = require('../olm'); describe("megolm", function() { var aliceSession, bobSession; - beforeEach(function() { - aliceSession = new Olm.OutboundGroupSession(); - bobSession = new Olm.InboundGroupSession(); + beforeEach(function(done) { + Olm.init().then(function() { + aliceSession = new Olm.OutboundGroupSession(); + bobSession = new Olm.InboundGroupSession(); + + done(); + }); }); afterEach(function() { diff --git a/javascript/test/olm.spec.js b/javascript/test/olm.spec.js index b7cc3ae..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. @@ -30,11 +31,18 @@ describe("olm", function() { var aliceAccount, bobAccount; var aliceSession, bobSession; - beforeEach(function() { - aliceAccount = new Olm.Account(); - bobAccount = new Olm.Account(); - aliceSession = new Olm.Session(); - bobSession = new Olm.Session(); + beforeEach(function(done) { + // This should really be in a beforeAll, but jasmine-node + // doesn't support that + debugger; + Olm.init().then(function() { + aliceAccount = new Olm.Account(); + bobAccount = new Olm.Account(); + aliceSession = new Olm.Session(); + bobSession = new Olm.Session(); + + done(); + }); }); afterEach(function() { diff --git a/javascript/test/pk.spec.js b/javascript/test/pk.spec.js index 0b27470..b4b119e 100644 --- a/javascript/test/pk.spec.js +++ b/javascript/test/pk.spec.js @@ -21,9 +21,13 @@ var Olm = require('../olm'); describe("pk", function() { var encryption, decryption; - beforeEach(function() { - encryption = new Olm.PkEncryption(); - decryption = new Olm.PkDecryption(); + beforeEach(function(done) { + Olm.init().then(function() { + encryption = new Olm.PkEncryption(); + decryption = new Olm.PkDecryption(); + + done(); + }); }); afterEach(function () { @@ -37,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 () { var TEST_TEXT='têst1'; var pubkey = decryption.generate_key(); |