From 93f764200ef47cf6ad683216c21d98b438897ead Mon Sep 17 00:00:00 2001 From: Hubert Chathi 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