From 63cd3b2788c5a0f0b09b82b6128f22e9e5ad667b Mon Sep 17 00:00:00 2001 From: lilia Date: Tue, 24 Nov 2015 16:14:12 -0800 Subject: [PATCH] Use constant time mac comparison In libtextsecure and in libaxolotl. // FREEBIE --- js/libtextsecure.js | 40 +++++++++++++++++++++++++++++-------- libtextsecure/crypto.js | 22 +++++++++++++++----- libtextsecure/libaxolotl.js | 18 ++++++++++++++--- 3 files changed, 64 insertions(+), 16 deletions(-) diff --git a/js/libtextsecure.js b/js/libtextsecure.js index af8cd402f87..4dd595200d0 100644 --- a/js/libtextsecure.js +++ b/js/libtextsecure.js @@ -34490,10 +34490,20 @@ window.axolotl.protocol = function(storage_interface) { var verifyMAC = function(data, key, mac) { return axolotlInternal.crypto.sign(key, data).then(function(calculated_mac) { - if (!isEqual(calculated_mac, mac, true)) + if (calculated_mac.byteLength < mac.byteLength) { + throw new Error("Bad MAC length"); + } + var a = new Uint8Array(calculated_mac); + var b = new Uint8Array(mac); + var result = 0; + for (var i=0; i < mac.byteLength; ++i) { + result = result | (a[i] ^ b[i]); + } + if (result !== 0) { throw new Error("Bad MAC"); + } }); - } + }; /****************************** *** Ratchet implementation *** @@ -34759,7 +34769,9 @@ window.axolotl.protocol = function(storage_interface) { throw new Error("Bad version number on WhisperMessage"); var messageProto = messageBytes.substring(1, messageBytes.length - 8); - var mac = messageBytes.substring(messageBytes.length - 8, messageBytes.length); + var mac = axolotlInternal.utils.convertToArrayBuffer( + messageBytes.substring(messageBytes.length - 8, messageBytes.length) + ); var message = axolotlInternal.protobuf.WhisperMessage.decode(messageProto, 'binary'); var remoteEphemeralKey = axolotlInternal.utils.convertToArrayBuffer(message.ephemeralKey); @@ -35274,12 +35286,24 @@ axolotlInternal.RecipientRecord = function() { }); }; - var verifyMAC = function(data, key, mac) { + var verifyMAC = function(data, key, mac, length) { return calculateMAC(key, data).then(function(calculated_mac) { - if (!isEqual(calculated_mac, mac, true)) + if (mac.byteLength != length || calculated_mac.byteLength < length) { + throw new Error("Bad MAC length"); + } + var a = new Uint8Array(calculated_mac); + var b = new Uint8Array(mac); + + var result = 0; + for (var i=0; i < mac.byteLength; ++i) { + result = result | (a[i] ^ b[i]); + } + + if (result !== 0) { throw new Error("Bad MAC"); + } }); - } + }; window.textsecure = window.textsecure || {}; window.textsecure.crypto = { @@ -35305,7 +35329,7 @@ axolotlInternal.RecipientRecord = function() { var ivAndCiphertext = decodedMessage.slice(0, decodedMessage.byteLength - 10); var mac = decodedMessage.slice(decodedMessage.byteLength - 10, decodedMessage.byteLength); - return verifyMAC(ivAndCiphertext, mac_key, mac).then(function() { + return verifyMAC(ivAndCiphertext, mac_key, mac, 10).then(function() { return decrypt(aes_key, ciphertext, iv); }); }, @@ -35326,7 +35350,7 @@ axolotlInternal.RecipientRecord = function() { var ivAndCiphertext = encryptedBin.slice(0, encryptedBin.byteLength - 32); var mac = encryptedBin.slice(encryptedBin.byteLength - 32, encryptedBin.byteLength); - return verifyMAC(ivAndCiphertext, mac_key, mac).then(function() { + return verifyMAC(ivAndCiphertext, mac_key, mac, 32).then(function() { return decrypt(aes_key, ciphertext, iv); }); }, diff --git a/libtextsecure/crypto.js b/libtextsecure/crypto.js index df1d6b77faf..7c40a73eaed 100644 --- a/libtextsecure/crypto.js +++ b/libtextsecure/crypto.js @@ -25,12 +25,24 @@ }); }; - var verifyMAC = function(data, key, mac) { + var verifyMAC = function(data, key, mac, length) { return calculateMAC(key, data).then(function(calculated_mac) { - if (!isEqual(calculated_mac, mac, true)) + if (mac.byteLength != length || calculated_mac.byteLength < length) { + throw new Error("Bad MAC length"); + } + var a = new Uint8Array(calculated_mac); + var b = new Uint8Array(mac); + + var result = 0; + for (var i=0; i < mac.byteLength; ++i) { + result = result | (a[i] ^ b[i]); + } + + if (result !== 0) { throw new Error("Bad MAC"); + } }); - } + }; window.textsecure = window.textsecure || {}; window.textsecure.crypto = { @@ -56,7 +68,7 @@ var ivAndCiphertext = decodedMessage.slice(0, decodedMessage.byteLength - 10); var mac = decodedMessage.slice(decodedMessage.byteLength - 10, decodedMessage.byteLength); - return verifyMAC(ivAndCiphertext, mac_key, mac).then(function() { + return verifyMAC(ivAndCiphertext, mac_key, mac, 10).then(function() { return decrypt(aes_key, ciphertext, iv); }); }, @@ -77,7 +89,7 @@ var ivAndCiphertext = encryptedBin.slice(0, encryptedBin.byteLength - 32); var mac = encryptedBin.slice(encryptedBin.byteLength - 32, encryptedBin.byteLength); - return verifyMAC(ivAndCiphertext, mac_key, mac).then(function() { + return verifyMAC(ivAndCiphertext, mac_key, mac, 32).then(function() { return decrypt(aes_key, ciphertext, iv); }); }, diff --git a/libtextsecure/libaxolotl.js b/libtextsecure/libaxolotl.js index 993c35f0de3..98d4def66f1 100644 --- a/libtextsecure/libaxolotl.js +++ b/libtextsecure/libaxolotl.js @@ -34392,10 +34392,20 @@ window.axolotl.protocol = function(storage_interface) { var verifyMAC = function(data, key, mac) { return axolotlInternal.crypto.sign(key, data).then(function(calculated_mac) { - if (!isEqual(calculated_mac, mac, true)) + if (calculated_mac.byteLength < mac.byteLength) { + throw new Error("Bad MAC length"); + } + var a = new Uint8Array(calculated_mac); + var b = new Uint8Array(mac); + var result = 0; + for (var i=0; i < mac.byteLength; ++i) { + result = result | (a[i] ^ b[i]); + } + if (result !== 0) { throw new Error("Bad MAC"); + } }); - } + }; /****************************** *** Ratchet implementation *** @@ -34661,7 +34671,9 @@ window.axolotl.protocol = function(storage_interface) { throw new Error("Bad version number on WhisperMessage"); var messageProto = messageBytes.substring(1, messageBytes.length - 8); - var mac = messageBytes.substring(messageBytes.length - 8, messageBytes.length); + var mac = axolotlInternal.utils.convertToArrayBuffer( + messageBytes.substring(messageBytes.length - 8, messageBytes.length) + ); var message = axolotlInternal.protobuf.WhisperMessage.decode(messageProto, 'binary'); var remoteEphemeralKey = axolotlInternal.utils.convertToArrayBuffer(message.ephemeralKey);