From 128d45cc83b1378422625ea975152e1e3c9d88f6 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Mon, 11 Jun 2018 17:48:45 -0400 Subject: add initial implementation of basic private key encryption functionality --- javascript/olm_pk.js | 179 +++++++++++++++++++++++++++++++++++++++++++++ javascript/olm_post.js | 2 + javascript/test/pk.spec.js | 64 ++++++++++++++++ 3 files changed, 245 insertions(+) create mode 100644 javascript/olm_pk.js create mode 100644 javascript/test/pk.spec.js (limited to 'javascript') diff --git a/javascript/olm_pk.js b/javascript/olm_pk.js new file mode 100644 index 0000000..e63e907 --- /dev/null +++ b/javascript/olm_pk.js @@ -0,0 +1,179 @@ +function PkEncryption() { + var size = Module['_olm_pk_encryption_size'](); + this.buf = malloc(size); + this.ptr = Module['_olm_pk_encryption'](this.buf); +} + +function pk_encryption_method(wrapped) { + return function() { + var result = wrapped.apply(this, arguments); + if (result === OLM_ERROR) { + var message = Pointer_stringify( + Module['_olm_pk_encryption_last_error'](arguments[0]) + ); + throw new Error("OLM." + message); + } + return result; + } +} + +PkEncryption.prototype['free'] = function() { + Module['_olm_clear_pk_encryption'](this.ptr); + free(this.ptr); +} + +PkEncryption.prototype['set_recipient_key'] = restore_stack(function(key) { + var key_array = array_from_string(key); + var key_buffer = stack(key_array); + pk_encryption_method(Module['_olm_pk_encryption_set_recipient_key'])( + this.ptr, key_buffer, key_array.length + ); +}); + +PkEncryption.prototype['encrypt'] = restore_stack(function( + plaintext +) { + var plaintext_buffer, ciphertext_buffer, plaintext_length; + try { + plaintext_length = Module['lengthBytesUTF8'](plaintext) + plaintext_buffer = malloc(plaintext_length + 1); + Module['stringToUTF8'](plaintext, plaintext_buffer, plaintext_length + 1); + var random_length = pk_encryption_method( + Module['_olm_pk_encrypt_random_length'] + )(); + var random = random_stack(random_length); + var ciphertext_length = pk_encryption_method( + Module['_olm_pk_ciphertext_length'] + )(this.ptr, plaintext_length); + ciphertext_buffer = malloc(ciphertext_length + NULL_BYTE_PADDING_LENGTH); + var mac_length = pk_encryption_method( + Module['_olm_pk_mac_length'] + )(this.ptr); + var mac_buffer = stack(mac_length + NULL_BYTE_PADDING_LENGTH); + Module['setValue']( + mac_buffer+mac_length, + 0, "i8" + ); + var ephemeral_length = pk_encryption_method( + Module['_olm_pk_key_length'] + )(); + var ephemeral_buffer = stack(ephemeral_length + NULL_BYTE_PADDING_LENGTH); + Module['setValue']( + ephemeral_buffer+ephemeral_length, + 0, "i8" + ); + pk_encryption_method(Module['_olm_pk_encrypt'])( + this.ptr, + plaintext_buffer, plaintext_length, + ciphertext_buffer, ciphertext_length, + mac_buffer, mac_length, + ephemeral_buffer, ephemeral_length, + random, random_length + ); + // UTF8ToString requires a null-terminated argument, so add the + // null terminator. + Module['setValue']( + ciphertext_buffer+ciphertext_length, + 0, "i8" + ); + return { + "ciphertext": Module['UTF8ToString'](ciphertext_buffer), + "mac": Pointer_stringify(mac_buffer), + "ephemeral": Pointer_stringify(ephemeral_buffer) + }; + } finally { + if (plaintext_buffer !== undefined) { + // don't leave a copy of the plaintext in the heap. + bzero(plaintext_buffer, plaintext_length + 1); + free(plaintext_buffer); + } + if (ciphertext_buffer !== undefined) { + free(ciphertext_buffer); + } + } +}); + + +function PkDecryption() { + var size = Module['_olm_pk_decryption_size'](); + this.buf = malloc(size); + this.ptr = Module['_olm_pk_decryption'](this.buf); +} + +function pk_decryption_method(wrapped) { + return function() { + var result = wrapped.apply(this, arguments); + if (result === OLM_ERROR) { + var message = Pointer_stringify( + Module['_olm_pk_decryption_last_error'](arguments[0]) + ); + throw new Error("OLM." + message); + } + return result; + } +} + +PkDecryption.prototype['free'] = function() { + Module['_olm_clear_pk_decryption'](this.ptr); + free(this.ptr); +} + +PkDecryption.prototype['generate_key'] = restore_stack(function () { + var random_length = pk_decryption_method( + Module['_olm_pk_key_length'] // FIXME: wrong method + )(); + var random_buffer = random_stack(random_length); + var pubkey_length = pk_encryption_method( + Module['_olm_pk_key_length'] + )(); + var pubkey_buffer = stack(pubkey_length); + pk_decryption_method(Module['_olm_pk_generate_key'])( + this.ptr, + pubkey_buffer, pubkey_length, + random_buffer, random_length + ); + return Pointer_stringify(pubkey_buffer); +}); + +PkDecryption.prototype['decrypt'] = restore_stack(function ( + ephemeral_key, mac, ciphertext +) { + var plaintext_buffer, ciphertext_buffer, plaintext_max_length; + try { + ciphertext_length = Module['lengthBytesUTF8'](ciphertext) + ciphertext_buffer = malloc(ciphertext_length + 1); + Module['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); + var mac_buffer = stack(mac_array); + plaintext_max_length = pk_decryption_method(Module['_olm_pk_max_plaintext_length'])( + this.ptr, + ciphertext_length + ); + plaintext_buffer = malloc(plaintext_max_length + NULL_BYTE_PADDING_LENGTH); + var plaintext_length = pk_decryption_method(Module['_olm_pk_decrypt'])( + this.ptr, + ephemeralkey_buffer, ephemeralkey_array.length, + mac_buffer, mac_array.length, + ciphertext_buffer, ciphertext_length, + plaintext_buffer, plaintext_max_length + ); + // UTF8ToString requires a null-terminated argument, so add the + // null terminator. + Module['setValue']( + plaintext_buffer+plaintext_length, + 0, "i8" + ); + return Module['UTF8ToString'](plaintext_buffer); + } finally { + if (plaintext_buffer !== undefined) { + // don't leave a copy of the plaintext in the heap. + bzero(plaintext_buffer, plaintext_length + 1); + free(plaintext_buffer); + } + if (ciphertext_buffer !== undefined) { + free(ciphertext_buffer); + } + } +}) diff --git a/javascript/olm_post.js b/javascript/olm_post.js index 91830fa..7a1d284 100644 --- a/javascript/olm_post.js +++ b/javascript/olm_post.js @@ -461,6 +461,8 @@ Utility.prototype['ed25519_verify'] = restore_stack(function( olm_exports["Account"] = Account; olm_exports["Session"] = Session; olm_exports["Utility"] = Utility; +olm_exports["PkEncryption"] = PkEncryption; +olm_exports["PkDecryption"] = PkDecryption; olm_exports["get_library_version"] = restore_stack(function() { var buf = stack(3); diff --git a/javascript/test/pk.spec.js b/javascript/test/pk.spec.js new file mode 100644 index 0000000..9eec47e --- /dev/null +++ b/javascript/test/pk.spec.js @@ -0,0 +1,64 @@ +/* +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. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +"use strict"; + +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; + + beforeEach(function() { + encryption = new Olm.PkEncryption(); + decryption = new Olm.PkDecryption(); + }); + + afterEach(function () { + if (encryption !== undefined) { + encryption.free(); + encryption = undefined; + } + if (decryption !== undefined) { + decryption.free(); + decryption = undefined; + } + }); + + it('should encrypt and decrypt', function () { + var TEST_TEXT='têst1'; + var pubkey = decryption.generate_key(); + encryption.set_recipient_key(pubkey); + var encrypted = encryption.encrypt(TEST_TEXT); + var decrypted = decryption.decrypt(encrypted.ephemeral, encrypted.mac, encrypted.ciphertext); + console.log(TEST_TEXT, "->", decrypted); + expect(decrypted).toEqual(TEST_TEXT); + + TEST_TEXT='hot beverage: ☕'; + encryption.set_recipient_key(pubkey); + encrypted = encryption.encrypt(TEST_TEXT); + decrypted = decryption.decrypt(encrypted.ephemeral, encrypted.mac, encrypted.ciphertext); + console.log(TEST_TEXT, "->", decrypted); + expect(decrypted).toEqual(TEST_TEXT); + }); +}); -- cgit v1.2.3