From 93f764200ef47cf6ad683216c21d98b438897ead Mon Sep 17 00:00:00 2001
From: Hubert Chathi <hubert@uhoreg.ca>
Date: Tue, 16 Oct 2018 17:50:34 -0400
Subject: zero buffers in the JavaScript bindings

---
 javascript/olm_inbound_group_session.js  |  62 ++++++++---
 javascript/olm_outbound_group_session.js |  34 ++++--
 javascript/olm_pk.js                     |  79 ++++++++++----
 javascript/olm_post.js                   | 175 ++++++++++++++++++++++---------
 4 files changed, 260 insertions(+), 90 deletions(-)

diff --git a/javascript/olm_inbound_group_session.js b/javascript/olm_inbound_group_session.js
index 7d9e401..dd8e493 100644
--- a/javascript/olm_inbound_group_session.js
+++ b/javascript/olm_inbound_group_session.js
@@ -29,9 +29,17 @@ InboundGroupSession.prototype['pickle'] = restore_stack(function(key) {
     )(this.ptr);
     var key_buffer = stack(key_array);
     var pickle_buffer = stack(pickle_length + NULL_BYTE_PADDING_LENGTH);
-    inbound_group_session_method(Module['_olm_pickle_inbound_group_session'])(
-        this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length
-    );
+    try {
+        inbound_group_session_method(Module['_olm_pickle_inbound_group_session'])(
+            this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length
+        );
+    } finally {
+        // clear out copies of the pickle key
+        bzero(key_buffer, key_array.length)
+        for (var i = 0; i < key_array.length; i++) {
+            key_array[i] = 0;
+        }
+    }
     return Pointer_stringify(pickle_buffer);
 });
 
@@ -40,28 +48,52 @@ InboundGroupSession.prototype['unpickle'] = restore_stack(function(key, pickle)
     var key_buffer = stack(key_array);
     var pickle_array = array_from_string(pickle);
     var pickle_buffer = stack(pickle_array);
-    inbound_group_session_method(Module['_olm_unpickle_inbound_group_session'])(
-        this.ptr, key_buffer, key_array.length, pickle_buffer,
-        pickle_array.length
-    );
+    try {
+        inbound_group_session_method(Module['_olm_unpickle_inbound_group_session'])(
+            this.ptr, key_buffer, key_array.length, pickle_buffer,
+            pickle_array.length
+        );
+    } finally {
+        // clear out copies of the pickle key
+        bzero(key_buffer, key_array.length)
+        for (var i = 0; i < key_array.length; i++) {
+            key_array[i] = 0;
+        }
+    }
 });
 
 InboundGroupSession.prototype['create'] = restore_stack(function(session_key) {
     var key_array = array_from_string(session_key);
     var key_buffer = stack(key_array);
 
-    inbound_group_session_method(Module['_olm_init_inbound_group_session'])(
-        this.ptr, key_buffer, key_array.length
-    );
+    try {
+        inbound_group_session_method(Module['_olm_init_inbound_group_session'])(
+            this.ptr, key_buffer, key_array.length
+        );
+    } finally {
+        // clear out copies of the key
+        bzero(key_buffer, key_array.length)
+        for (var i = 0; i < key_array.length; i++) {
+            key_array[i] = 0;
+        }
+    }
 });
 
 InboundGroupSession.prototype['import_session'] = restore_stack(function(session_key) {
     var key_array = array_from_string(session_key);
     var key_buffer = stack(key_array);
 
-    inbound_group_session_method(Module['_olm_import_inbound_group_session'])(
-        this.ptr, key_buffer, key_array.length
-    );
+    try {
+        inbound_group_session_method(Module['_olm_import_inbound_group_session'])(
+            this.ptr, key_buffer, key_array.length
+        );
+    } finally {
+        // clear out copies of the key
+        bzero(key_buffer, key_array.length)
+        for (var i = 0; i < key_array.length; i++) {
+            key_array[i] = 0;
+        }
+    }
 });
 
 InboundGroupSession.prototype['decrypt'] = restore_stack(function(
@@ -140,7 +172,9 @@ InboundGroupSession.prototype['export_session'] = restore_stack(function(message
     outbound_group_session_method(Module['_olm_export_inbound_group_session'])(
         this.ptr, key, key_length, message_index
     );
-    return Pointer_stringify(key);
+    var key_str = Pointer_stringify(key);
+    bzero(key, key_length); // clear out a copy of the key
+    return key_str;
 });
 
 olm_exports['InboundGroupSession'] = InboundGroupSession;
diff --git a/javascript/olm_outbound_group_session.js b/javascript/olm_outbound_group_session.js
index e232883..f1ccd3d 100644
--- a/javascript/olm_outbound_group_session.js
+++ b/javascript/olm_outbound_group_session.js
@@ -29,9 +29,17 @@ OutboundGroupSession.prototype['pickle'] = restore_stack(function(key) {
     )(this.ptr);
     var key_buffer = stack(key_array);
     var pickle_buffer = stack(pickle_length + NULL_BYTE_PADDING_LENGTH);
-    outbound_group_session_method(Module['_olm_pickle_outbound_group_session'])(
-        this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length
-    );
+    try {
+        outbound_group_session_method(Module['_olm_pickle_outbound_group_session'])(
+            this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length
+        );
+    } finally {
+        // clear out copies of the pickle key
+        bzero(key_buffer, key_array.length)
+        for (var i = 0; i < key_array.length; i++) {
+            key_array[i] = 0;
+        }
+    }
     return Pointer_stringify(pickle_buffer);
 });
 
@@ -40,10 +48,18 @@ OutboundGroupSession.prototype['unpickle'] = restore_stack(function(key, pickle)
     var key_buffer = stack(key_array);
     var pickle_array = array_from_string(pickle);
     var pickle_buffer = stack(pickle_array);
-    outbound_group_session_method(Module['_olm_unpickle_outbound_group_session'])(
-        this.ptr, key_buffer, key_array.length, pickle_buffer,
-        pickle_array.length
-    );
+    try {
+        outbound_group_session_method(Module['_olm_unpickle_outbound_group_session'])(
+            this.ptr, key_buffer, key_array.length, pickle_buffer,
+            pickle_array.length
+        );
+    } finally {
+        // clear out copies of the pickle key
+        bzero(key_buffer, key_array.length)
+        for (var i = 0; i < key_array.length; i++) {
+            key_array[i] = 0;
+        }
+    }
 });
 
 OutboundGroupSession.prototype['create'] = restore_stack(function() {
@@ -116,7 +132,9 @@ OutboundGroupSession.prototype['session_key'] = restore_stack(function() {
     outbound_group_session_method(Module['_olm_outbound_group_session_key'])(
         this.ptr, key, key_length
     );
-    return Pointer_stringify(key);
+    var key_str = Pointer_stringify(key);
+    bzero(key, key_length); // clear out our copy of the key
+    return key_str;
 });
 
 OutboundGroupSession.prototype['message_index'] = function() {
diff --git a/javascript/olm_pk.js b/javascript/olm_pk.js
index 4f730dd..81dbad4 100644
--- a/javascript/olm_pk.js
+++ b/javascript/olm_pk.js
@@ -33,15 +33,15 @@ PkEncryption.prototype['set_recipient_key'] = restore_stack(function(key) {
 PkEncryption.prototype['encrypt'] = restore_stack(function(
     plaintext
 ) {
-    var plaintext_buffer, ciphertext_buffer, plaintext_length;
+    var plaintext_buffer, ciphertext_buffer, plaintext_length, random, random_length;
     try {
         plaintext_length = lengthBytesUTF8(plaintext)
         plaintext_buffer = malloc(plaintext_length + 1);
         stringToUTF8(plaintext, plaintext_buffer, plaintext_length + 1);
-        var random_length = pk_encryption_method(
+        random_length = pk_encryption_method(
             Module['_olm_pk_encrypt_random_length']
         )();
-        var random = random_stack(random_length);
+        random = random_stack(random_length);
         var ciphertext_length = pk_encryption_method(
             Module['_olm_pk_ciphertext_length']
         )(this.ptr, plaintext_length);
@@ -82,6 +82,10 @@ PkEncryption.prototype['encrypt'] = restore_stack(function(
             "ephemeral": Pointer_stringify(ephemeral_buffer)
         };
     } finally {
+        if (random !== undefined) {
+            // clear out the random buffer, since it is key data
+            bzero(random, random_length);
+        }
         if (plaintext_buffer !== undefined) {
             // don't leave a copy of the plaintext in the heap.
             bzero(plaintext_buffer, plaintext_length + 1);
@@ -126,11 +130,16 @@ PkDecryption.prototype['init_with_private_key'] = restore_stack(function (privat
         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
-    );
+    try {
+        pk_decryption_method(Module['_olm_pk_key_from_private'])(
+            this.ptr,
+            pubkey_buffer, pubkey_length,
+            private_key_buffer, private_key.length
+        );
+    } finally {
+        // clear out our copy of the private key
+        bzero(private_key_buffer, private_key.length);
+    }
     return Pointer_stringify(pubkey_buffer);
 });
 
@@ -143,11 +152,16 @@ PkDecryption.prototype['generate_key'] = restore_stack(function () {
         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,
-        random_buffer, random_length
-    );
+    try {
+        pk_decryption_method(Module['_olm_pk_key_from_private'])(
+            this.ptr,
+            pubkey_buffer, pubkey_length,
+            random_buffer, random_length
+        );
+    } finally {
+        // clear out the random buffer (= private key)
+        bzero(random_buffer, random_length);
+    }
     return Pointer_stringify(pubkey_buffer);
 });
 
@@ -160,7 +174,14 @@ PkDecryption.prototype['get_private_key'] = restore_stack(function () {
         this.ptr,
         privkey_buffer, privkey_length
     );
-    return new Uint8Array(Module['HEAPU8'].buffer, privkey_buffer, privkey_length);
+    // The inner Uint8Array creates a view of the buffer.  The outer Uint8Array
+    // copies it to a new array to return, since the original buffer will get
+    // deallocated from the stack and could get overwritten.
+    var key_arr = new Uint8Array(
+        new Uint8Array(Module['HEAPU8'].buffer, privkey_buffer, privkey_length)
+    );
+    bzero(privkey_buffer, privkey_length); // clear out our copy of the key
+    return key_arr;
 });
 
 PkDecryption.prototype['pickle'] = restore_stack(function (key) {
@@ -170,9 +191,17 @@ PkDecryption.prototype['pickle'] = restore_stack(function (key) {
     )(this.ptr);
     var key_buffer = stack(key_array);
     var pickle_buffer = stack(pickle_length + NULL_BYTE_PADDING_LENGTH);
-    pk_decryption_method(Module['_olm_pickle_pk_decryption'])(
-        this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length
-    );
+    try {
+        pk_decryption_method(Module['_olm_pickle_pk_decryption'])(
+            this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length
+        );
+    } finally {
+        // clear out copies of the pickle key
+        bzero(key_buffer, key_array.length)
+        for (var i = 0; i < key_array.length; i++) {
+            key_array[i] = 0;
+        }
+    }
     return Pointer_stringify(pickle_buffer);
 });
 
@@ -185,10 +214,18 @@ PkDecryption.prototype['unpickle'] = restore_stack(function (key, pickle) {
         Module["_olm_pk_key_length"]
     )();
     var ephemeral_buffer = stack(ephemeral_length + NULL_BYTE_PADDING_LENGTH);
-    pk_decryption_method(Module['_olm_unpickle_pk_decryption'])(
-        this.ptr, key_buffer, key_array.length, pickle_buffer,
-        pickle_array.length, ephemeral_buffer, ephemeral_length
-    );
+    try {
+        pk_decryption_method(Module['_olm_unpickle_pk_decryption'])(
+            this.ptr, key_buffer, key_array.length, pickle_buffer,
+            pickle_array.length, ephemeral_buffer, ephemeral_length
+        );
+    } finally {
+        // clear out copies of the pickle key
+        bzero(key_buffer, key_array.length)
+        for (var i = 0; i < key_array.length; i++) {
+            key_array[i] = 0;
+        }
+    }
     return Pointer_stringify(ephemeral_buffer);
 });
 
diff --git a/javascript/olm_post.js b/javascript/olm_post.js
index 21ea890..8c06fff 100644
--- a/javascript/olm_post.js
+++ b/javascript/olm_post.js
@@ -91,11 +91,19 @@ Account.prototype['sign'] = restore_stack(function(message) {
     var message_array = array_from_string(message);
     var message_buffer = stack(message_array);
     var signature_buffer = stack(signature_length + NULL_BYTE_PADDING_LENGTH);
-    account_method(Module['_olm_account_sign'])(
-        this.ptr,
-        message_buffer, message_array.length,
-        signature_buffer, signature_length
-    );
+    try {
+        account_method(Module['_olm_account_sign'])(
+            this.ptr,
+            message_buffer, message_array.length,
+            signature_buffer, signature_length
+        );
+    } finally {
+        // clear out copies of the message, which may be plaintext
+        bzero(message_buffer, message_array.length);
+        for (var i = 0; i < message_array.length; i++) {
+            message_array[i] = 0;
+        }
+    }
     return Pointer_stringify(signature_buffer);
 });
 
@@ -145,9 +153,17 @@ Account.prototype['pickle'] = restore_stack(function(key) {
     )(this.ptr);
     var key_buffer = stack(key_array);
     var pickle_buffer = stack(pickle_length + NULL_BYTE_PADDING_LENGTH);
-    account_method(Module['_olm_pickle_account'])(
-        this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length
-    );
+    try {
+        account_method(Module['_olm_pickle_account'])(
+            this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length
+        );
+    } finally {
+        // clear out copies of the pickle key
+        bzero(key_buffer, key_array.length)
+        for (var i = 0; i < key_array.length; i++) {
+            key_array[i] = 0;
+        }
+    }
     return Pointer_stringify(pickle_buffer);
 });
 
@@ -156,10 +172,18 @@ Account.prototype['unpickle'] = restore_stack(function(key, pickle) {
     var key_buffer = stack(key_array);
     var pickle_array = array_from_string(pickle);
     var pickle_buffer = stack(pickle_array);
-    account_method(Module['_olm_unpickle_account'])(
-        this.ptr, key_buffer, key_array.length, pickle_buffer,
-        pickle_array.length
-    );
+    try {
+        account_method(Module['_olm_unpickle_account'])(
+            this.ptr, key_buffer, key_array.length, pickle_buffer,
+            pickle_array.length
+        );
+    } finally {
+        // clear out copies of the pickle key
+        bzero(key_buffer, key_array.length)
+        for (var i = 0; i < key_array.length; i++) {
+            key_array[i] = 0;
+        }
+    }
 });
 
 function Session() {
@@ -193,9 +217,17 @@ Session.prototype['pickle'] = restore_stack(function(key) {
     )(this.ptr);
     var key_buffer = stack(key_array);
     var pickle_buffer = stack(pickle_length + NULL_BYTE_PADDING_LENGTH);
-    session_method(Module['_olm_pickle_session'])(
-        this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length
-    );
+    try {
+        session_method(Module['_olm_pickle_session'])(
+            this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length
+        );
+    } finally {
+        // clear out copies of the pickle key
+        bzero(key_buffer, key_array.length)
+        for (var i = 0; i < key_array.length; i++) {
+            key_array[i] = 0;
+        }
+    }
     return Pointer_stringify(pickle_buffer);
 });
 
@@ -204,10 +236,18 @@ Session.prototype['unpickle'] = restore_stack(function(key, pickle) {
     var key_buffer = stack(key_array);
     var pickle_array = array_from_string(pickle);
     var pickle_buffer = stack(pickle_array);
-    session_method(Module['_olm_unpickle_session'])(
-        this.ptr, key_buffer, key_array.length, pickle_buffer,
-        pickle_array.length
-    );
+    try {
+        session_method(Module['_olm_unpickle_session'])(
+            this.ptr, key_buffer, key_array.length, pickle_buffer,
+            pickle_array.length
+        );
+    } finally {
+        // clear out copies of the pickle key
+        bzero(key_buffer, key_array.length)
+        for (var i = 0; i < key_array.length; i++) {
+            key_array[i] = 0;
+        }
+    }
 });
 
 Session.prototype['create_outbound'] = restore_stack(function(
@@ -221,12 +261,17 @@ Session.prototype['create_outbound'] = restore_stack(function(
     var one_time_key_array = array_from_string(their_one_time_key);
     var identity_key_buffer = stack(identity_key_array);
     var one_time_key_buffer = stack(one_time_key_array);
-    session_method(Module['_olm_create_outbound_session'])(
-        this.ptr, account.ptr,
-        identity_key_buffer, identity_key_array.length,
-        one_time_key_buffer, one_time_key_array.length,
-        random, random_length
-    );
+    try {
+        session_method(Module['_olm_create_outbound_session'])(
+            this.ptr, account.ptr,
+            identity_key_buffer, identity_key_array.length,
+            one_time_key_buffer, one_time_key_array.length,
+            random, random_length
+        );
+    } finally {
+        // clear the random buffer, which is key data
+        bzero(random, random_length);
+    }
 });
 
 Session.prototype['create_inbound'] = restore_stack(function(
@@ -234,9 +279,17 @@ Session.prototype['create_inbound'] = restore_stack(function(
 ) {
     var message_array = array_from_string(one_time_key_message);
     var message_buffer = stack(message_array);
-    session_method(Module['_olm_create_inbound_session'])(
-        this.ptr, account.ptr, message_buffer, message_array.length
-    );
+    try {
+        session_method(Module['_olm_create_inbound_session'])(
+            this.ptr, account.ptr, message_buffer, message_array.length
+        );
+    } finally {
+        // clear out copies of the key
+        bzero(message_buffer, message_array.length);
+        for (var i = 0; i < message_array.length; i++) {
+            message_array[i] = 0;
+        }
+    }
 });
 
 Session.prototype['create_inbound_from'] = restore_stack(function(
@@ -246,11 +299,19 @@ Session.prototype['create_inbound_from'] = restore_stack(function(
     var identity_key_buffer = stack(identity_key_array);
     var message_array = array_from_string(one_time_key_message);
     var message_buffer = stack(message_array);
-    session_method(Module['_olm_create_inbound_session_from'])(
-        this.ptr, account.ptr,
-        identity_key_buffer, identity_key_array.length,
-        message_buffer, message_array.length
-    );
+    try {
+        session_method(Module['_olm_create_inbound_session_from'])(
+            this.ptr, account.ptr,
+            identity_key_buffer, identity_key_array.length,
+            message_buffer, message_array.length
+        );
+    } finally {
+        // clear out copies of the key
+        bzero(message_buffer, message_array.length);
+        for (var i = 0; i < message_array.length; i++) {
+            message_array[i] = 0;
+        }
+    }
 });
 
 Session.prototype['session_id'] = restore_stack(function() {
@@ -296,9 +357,9 @@ Session.prototype['matches_inbound_from'] = restore_stack(function(
 Session.prototype['encrypt'] = restore_stack(function(
     plaintext
 ) {
-    var plaintext_buffer, message_buffer, plaintext_length;
+    var plaintext_buffer, message_buffer, plaintext_length, random, random_length;
     try {
-        var random_length = session_method(
+        random_length = session_method(
             Module['_olm_encrypt_random_length']
         )(this.ptr);
         var message_type = session_method(
@@ -310,7 +371,7 @@ Session.prototype['encrypt'] = restore_stack(function(
             Module['_olm_encrypt_message_length']
         )(this.ptr, plaintext_length);
 
-        var random = random_stack(random_length);
+        random = random_stack(random_length);
 
         // need to allow space for the terminator (which stringToUTF8 always
         // writes), hence + 1.
@@ -338,6 +399,10 @@ Session.prototype['encrypt'] = restore_stack(function(
             "body": UTF8ToString(message_buffer),
         };
     } finally {
+        if (random !== undefined) {
+            // clear out the random buffer, since it is the private key
+            bzero(random, random_length);
+        }
         if (plaintext_buffer !== undefined) {
             // don't leave a copy of the plaintext in the heap.
             bzero(plaintext_buffer, plaintext_length + 1);
@@ -423,11 +488,19 @@ Utility.prototype['sha256'] = restore_stack(function(input) {
     var input_array = array_from_string(input);
     var input_buffer = stack(input_array);
     var output_buffer = stack(output_length + NULL_BYTE_PADDING_LENGTH);
-    utility_method(Module['_olm_sha256'])(
-        this.ptr,
-        input_buffer, input_array.length,
-        output_buffer, output_length
-    );
+    try {
+        utility_method(Module['_olm_sha256'])(
+            this.ptr,
+            input_buffer, input_array.length,
+            output_buffer, output_length
+        );
+    } finally {
+        // clear out copies of the input buffer, which may be plaintext
+        bzero(input_buffer, input_array.length);
+        for (var i = 0; i < input_array.length; i++) {
+            input_array[i] = 0;
+        }
+    }
     return Pointer_stringify(output_buffer);
 });
 
@@ -440,12 +513,20 @@ Utility.prototype['ed25519_verify'] = restore_stack(function(
     var message_buffer = stack(message_array);
     var signature_array = array_from_string(signature);
     var signature_buffer = stack(signature_array);
-    utility_method(Module['_olm_ed25519_verify'])(
-        this.ptr,
-        key_buffer, key_array.length,
-        message_buffer, message_array.length,
-        signature_buffer, signature_array.length
-    );
+    try {
+        utility_method(Module['_olm_ed25519_verify'])(
+            this.ptr,
+            key_buffer, key_array.length,
+            message_buffer, message_array.length,
+            signature_buffer, signature_array.length
+        );
+    } finally {
+        // clear out copies of the input buffer, which may be plaintext
+        bzero(message_buffer, message_array.length);
+        for (var i = 0; i < message_array.length; i++) {
+            message_array[i] = 0;
+        }
+    }
 });
 
 olm_exports["Account"] = Account;
-- 
cgit v1.2.3-70-g09d2