From 167f19153c01994e56d64052a5551c9bf541a334 Mon Sep 17 00:00:00 2001 From: lilia Date: Tue, 14 Jun 2016 19:30:18 -0700 Subject: [PATCH] Update libsignal-protocol v1.1.0 Moves padding operations to the service library. // FREEBIE --- js/libtextsecure.js | 61 ++++++++++++++++++----------- libtextsecure/libsignal-protocol.js | 22 +---------- libtextsecure/message_receiver.js | 20 +++++++++- libtextsecure/outgoing_message.js | 19 ++++++++- 4 files changed, 76 insertions(+), 46 deletions(-) diff --git a/js/libtextsecure.js b/js/libtextsecure.js index aab2efed..3dfd25a3 100644 --- a/js/libtextsecure.js +++ b/js/libtextsecure.js @@ -36150,11 +36150,6 @@ SessionCipher.prototype = { var ourIdentityKey, myRegistrationId, record, session, chain; var msg = new Internal.protobuf.WhisperMessage(); - var paddedPlaintext = new Uint8Array( - this.getPaddedMessageLength(buffer.byteLength + 1) - 1 - ); - paddedPlaintext.set(new Uint8Array(buffer)); - paddedPlaintext[buffer.byteLength] = 0x80; return Promise.all([ this.storage.getIdentityKeyPair(), @@ -36191,7 +36186,7 @@ SessionCipher.prototype = { msg.previousCounter = session.currentRatchet.previousCounter; return Internal.crypto.encrypt( - keys[0], paddedPlaintext.buffer, keys[2].slice(0, 16) + keys[0], buffer, keys[2].slice(0, 16) ).then(function(ciphertext) { msg.ciphertext = ciphertext; var encodedMsg = msg.toArrayBuffer(); @@ -36352,20 +36347,7 @@ SessionCipher.prototype = { }.bind(this)).then(function() { return Internal.crypto.decrypt(keys[0], message.ciphertext.toArrayBuffer(), keys[2].slice(0, 16)); }); - }.bind(this)).then(function(paddedPlaintext) { - paddedPlaintext = new Uint8Array(paddedPlaintext); - var plaintext; - for (var i = paddedPlaintext.length - 1; i >= 0; i--) { - if (paddedPlaintext[i] == 0x80) { - plaintext = new Uint8Array(i); - plaintext.set(paddedPlaintext.subarray(0, i)); - plaintext = plaintext.buffer; - break; - } else if (paddedPlaintext[i] !== 0x00) { - throw new Error('Invalid padding'); - } - } - + }.bind(this)).then(function(plaintext) { delete session.pendingPreKey; return plaintext; }); @@ -38055,13 +38037,29 @@ MessageReceiver.prototype.extend({ ev.proto = envelope; this.dispatchEvent(ev); }, + unpad: function(paddedPlaintext) { + paddedPlaintext = new Uint8Array(paddedPlaintext); + var plaintext; + for (var i = paddedPlaintext.length - 1; i >= 0; i--) { + if (paddedPlaintext[i] == 0x80) { + plaintext = new Uint8Array(i); + plaintext.set(paddedPlaintext.subarray(0, i)); + plaintext = plaintext.buffer; + break; + } else if (paddedPlaintext[i] !== 0x00) { + throw new Error('Invalid padding'); + } + } + + return plaintext; + }, decrypt: function(envelope, ciphertext) { var promise; var address = new libsignal.SignalProtocolAddress(envelope.source, envelope.sourceDevice); var sessionCipher = new libsignal.SessionCipher(textsecure.storage.protocol, address); switch(envelope.type) { case textsecure.protobuf.Envelope.Type.CIPHERTEXT: - promise = sessionCipher.decryptWhisperMessage(ciphertext); + promise = sessionCipher.decryptWhisperMessage(ciphertext).then(this.unpad); break; case textsecure.protobuf.Envelope.Type.PREKEY_BUNDLE: console.log('prekey whisper message'); @@ -38079,7 +38077,7 @@ MessageReceiver.prototype.extend({ }.bind(this)); }, decryptPreKeyWhisperMessage: function(ciphertext, sessionCipher, address) { - return sessionCipher.decryptPreKeyWhisperMessage(ciphertext).catch(function(e) { + return sessionCipher.decryptPreKeyWhisperMessage(ciphertext).then(this.unpad).catch(function(e) { if (e.message === 'Unknown identity key') { // create an error that the UI will pick up and ask the // user if they want to re-negotiate @@ -38506,14 +38504,31 @@ OutgoingMessage.prototype = { }); }, + getPaddedMessageLength: function(messageLength) { + var messageLengthWithTerminator = messageLength + 1; + var messagePartCount = Math.floor(messageLengthWithTerminator / 160); + + if (messageLengthWithTerminator % 160 !== 0) { + messagePartCount++; + } + + return messagePartCount * 160; + }, + doSendMessage: function(number, deviceIds, recurse) { var ciphers = {}; var plaintext = this.message.toArrayBuffer(); + var paddedPlaintext = new Uint8Array( + this.getPaddedMessageLength(plaintext.byteLength + 1) - 1 + ); + paddedPlaintext.set(new Uint8Array(plaintext)); + paddedPlaintext[plaintext.byteLength] = 0x80; + return Promise.all(deviceIds.map(function(deviceId) { var address = new libsignal.SignalProtocolAddress(number, deviceId); var sessionCipher = new libsignal.SessionCipher(textsecure.storage.protocol, address); ciphers[address.getDeviceId()] = sessionCipher; - return this.encryptToDevice(address, plaintext, sessionCipher); + return this.encryptToDevice(address, paddedPlaintext, sessionCipher); }.bind(this))).then(function(jsonData) { return this.transmitMessage(number, jsonData, this.timestamp).then(function() { this.successfulNumbers[this.successfulNumbers.length] = number; diff --git a/libtextsecure/libsignal-protocol.js b/libtextsecure/libsignal-protocol.js index 424d69c5..b967b0c5 100644 --- a/libtextsecure/libsignal-protocol.js +++ b/libtextsecure/libsignal-protocol.js @@ -36026,11 +36026,6 @@ SessionCipher.prototype = { var ourIdentityKey, myRegistrationId, record, session, chain; var msg = new Internal.protobuf.WhisperMessage(); - var paddedPlaintext = new Uint8Array( - this.getPaddedMessageLength(buffer.byteLength + 1) - 1 - ); - paddedPlaintext.set(new Uint8Array(buffer)); - paddedPlaintext[buffer.byteLength] = 0x80; return Promise.all([ this.storage.getIdentityKeyPair(), @@ -36067,7 +36062,7 @@ SessionCipher.prototype = { msg.previousCounter = session.currentRatchet.previousCounter; return Internal.crypto.encrypt( - keys[0], paddedPlaintext.buffer, keys[2].slice(0, 16) + keys[0], buffer, keys[2].slice(0, 16) ).then(function(ciphertext) { msg.ciphertext = ciphertext; var encodedMsg = msg.toArrayBuffer(); @@ -36228,20 +36223,7 @@ SessionCipher.prototype = { }.bind(this)).then(function() { return Internal.crypto.decrypt(keys[0], message.ciphertext.toArrayBuffer(), keys[2].slice(0, 16)); }); - }.bind(this)).then(function(paddedPlaintext) { - paddedPlaintext = new Uint8Array(paddedPlaintext); - var plaintext; - for (var i = paddedPlaintext.length - 1; i >= 0; i--) { - if (paddedPlaintext[i] == 0x80) { - plaintext = new Uint8Array(i); - plaintext.set(paddedPlaintext.subarray(0, i)); - plaintext = plaintext.buffer; - break; - } else if (paddedPlaintext[i] !== 0x00) { - throw new Error('Invalid padding'); - } - } - + }.bind(this)).then(function(plaintext) { delete session.pendingPreKey; return plaintext; }); diff --git a/libtextsecure/message_receiver.js b/libtextsecure/message_receiver.js index 2fcfc9b4..50bdd3cd 100644 --- a/libtextsecure/message_receiver.js +++ b/libtextsecure/message_receiver.js @@ -108,13 +108,29 @@ MessageReceiver.prototype.extend({ ev.proto = envelope; this.dispatchEvent(ev); }, + unpad: function(paddedPlaintext) { + paddedPlaintext = new Uint8Array(paddedPlaintext); + var plaintext; + for (var i = paddedPlaintext.length - 1; i >= 0; i--) { + if (paddedPlaintext[i] == 0x80) { + plaintext = new Uint8Array(i); + plaintext.set(paddedPlaintext.subarray(0, i)); + plaintext = plaintext.buffer; + break; + } else if (paddedPlaintext[i] !== 0x00) { + throw new Error('Invalid padding'); + } + } + + return plaintext; + }, decrypt: function(envelope, ciphertext) { var promise; var address = new libsignal.SignalProtocolAddress(envelope.source, envelope.sourceDevice); var sessionCipher = new libsignal.SessionCipher(textsecure.storage.protocol, address); switch(envelope.type) { case textsecure.protobuf.Envelope.Type.CIPHERTEXT: - promise = sessionCipher.decryptWhisperMessage(ciphertext); + promise = sessionCipher.decryptWhisperMessage(ciphertext).then(this.unpad); break; case textsecure.protobuf.Envelope.Type.PREKEY_BUNDLE: console.log('prekey whisper message'); @@ -132,7 +148,7 @@ MessageReceiver.prototype.extend({ }.bind(this)); }, decryptPreKeyWhisperMessage: function(ciphertext, sessionCipher, address) { - return sessionCipher.decryptPreKeyWhisperMessage(ciphertext).catch(function(e) { + return sessionCipher.decryptPreKeyWhisperMessage(ciphertext).then(this.unpad).catch(function(e) { if (e.message === 'Unknown identity key') { // create an error that the UI will pick up and ask the // user if they want to re-negotiate diff --git a/libtextsecure/outgoing_message.js b/libtextsecure/outgoing_message.js index cd1cf577..a618cf0e 100644 --- a/libtextsecure/outgoing_message.js +++ b/libtextsecure/outgoing_message.js @@ -98,14 +98,31 @@ OutgoingMessage.prototype = { }); }, + getPaddedMessageLength: function(messageLength) { + var messageLengthWithTerminator = messageLength + 1; + var messagePartCount = Math.floor(messageLengthWithTerminator / 160); + + if (messageLengthWithTerminator % 160 !== 0) { + messagePartCount++; + } + + return messagePartCount * 160; + }, + doSendMessage: function(number, deviceIds, recurse) { var ciphers = {}; var plaintext = this.message.toArrayBuffer(); + var paddedPlaintext = new Uint8Array( + this.getPaddedMessageLength(plaintext.byteLength + 1) - 1 + ); + paddedPlaintext.set(new Uint8Array(plaintext)); + paddedPlaintext[plaintext.byteLength] = 0x80; + return Promise.all(deviceIds.map(function(deviceId) { var address = new libsignal.SignalProtocolAddress(number, deviceId); var sessionCipher = new libsignal.SessionCipher(textsecure.storage.protocol, address); ciphers[address.getDeviceId()] = sessionCipher; - return this.encryptToDevice(address, plaintext, sessionCipher); + return this.encryptToDevice(address, paddedPlaintext, sessionCipher); }.bind(this))).then(function(jsonData) { return this.transmitMessage(number, jsonData, this.timestamp).then(function() { this.successfulNumbers[this.successfulNumbers.length] = number;