From eab0911d3dc76e1ba54f8a343c87a73945ec579a Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Thu, 24 Jul 2014 05:14:38 -0400 Subject: [PATCH] I lied, there is no verification tag --- js/crypto.js | 90 ++++++++++---------------------- protos/WhisperTextProtocol.proto | 2 - 2 files changed, 27 insertions(+), 65 deletions(-) diff --git a/js/crypto.js b/js/crypto.js index ac3b2fe1556..ff3f1cf7bd8 100644 --- a/js/crypto.js +++ b/js/crypto.js @@ -326,11 +326,7 @@ window.textsecure.crypto = function() { infoArray.set(new Uint8Array(T1)); infoArray[infoArray.length - 1] = 2; return HmacSHA256(PRK, infoBuffer).then(function(T2) { - infoArray.set(new Uint8Array(T2)); - infoArray[infoArray.length - 1] = 3; - return HmacSHA256(PRK, infoBuffer).then(function(T3) { - return [ T1, T2, T3 ]; - }); + return [ T1, T2 ]; }); }); }); @@ -403,16 +399,11 @@ window.textsecure.crypto = function() { theirSignedPubKey = theirEphemeralPubKey; } - var PUB_KEYS_VERIFICATION_MAGIC = toArrayBuffer("TextSecure Verification Tag"); - - var sharedSecret; var pubKeysVerification; - if (ourEphemeralKey === undefined || theirEphemeralPubKey === undefined) { + var sharedSecret; + if (ourEphemeralKey === undefined || theirEphemeralPubKey === undefined) sharedSecret = new Uint8Array(32 * 4); - pubKeysVerification = new Uint8Array(33 * 5 + PUB_KEYS_VERIFICATION_MAGIC.byteLength); - } else { + else sharedSecret = new Uint8Array(32 * 5); - pubKeysVerification = new Uint8Array(33 * 6 + PUB_KEYS_VERIFICATION_MAGIC.byteLength); - } for (var i = 0; i < 32; i++) sharedSecret[i] = 0xff; @@ -423,45 +414,28 @@ window.textsecure.crypto = function() { sharedSecret.set(new Uint8Array(ecRes), 32 * 3); return HKDF(sharedSecret.buffer, '', "WhisperText").then(function(masterKey) { + var session = {currentRatchet: { rootKey: masterKey[0], lastRemoteEphemeralKey: theirSignedPubKey }, + indexInfo: { remoteIdentityKey: theirIdentityPubKey, closed: -1 }, + oldRatchetList: [] + }; + if (!isInitiator) + session.indexInfo.baseKey = theirEphemeralPubKey; + else + session.indexInfo.baseKey = ourEphemeralKey.pubKey; - pubKeysVerification.set(new Uint8Array(PUB_KEYS_VERIFICATION_MAGIC)); - var i = PUB_KEYS_VERIFICATION_MAGIC.byteLength - 33; - - pubKeysVerification.set(new Uint8Array(isInitiator ? ourSignedKey.pubKey : theirSignedPubKey), i += 33); - pubKeysVerification.set(new Uint8Array(isInitiator ? ourIdentityKey.pubKey : theirIdentityPubKey), i += 33); - - pubKeysVerification.set(new Uint8Array(!isInitiator ? ourSignedKey.pubKey : theirSignedPubKey), i += 33); - pubKeysVerification.set(new Uint8Array(!isInitiator ? ourIdentityKey.pubKey : theirIdentityPubKey), i += 33); - - if (ourEphemeralKey !== undefined && theirEphemeralPubKey !== undefined) { - pubKeysVerification.set(new Uint8Array(isInitiator ? ourEphemeralKey.pubKey : theirEphemeralPubKey), i += 33); - pubKeysVerification.set(new Uint8Array(!isInitiator ? ourEphemeralKey.pubKey : theirEphemeralPubKey), i += 33); - } - - return HmacSHA256(masterKey[2], pubKeysVerification.buffer).then(function(verificationTag) { - var session = {currentRatchet: { rootKey: masterKey[0], lastRemoteEphemeralKey: theirSignedPubKey }, - indexInfo: { remoteIdentityKey: theirIdentityPubKey, closed: -1 }, - oldRatchetList: [] - }; - if (!isInitiator) - session.indexInfo.baseKey = theirEphemeralPubKey; - else - session.indexInfo.baseKey = ourEphemeralKey.pubKey; - - // If we're initiating we go ahead and set our first sending ephemeral key now, - // otherwise we figure it out when we first maybeStepRatchet with the remote's ephemeral key - if (isInitiator) { - return createNewKeyPair(false).then(function(ourSendingEphemeralKey) { - session.currentRatchet.ephemeralKeyPair = ourSendingEphemeralKey; - return calculateRatchet(session, theirSignedPubKey, true).then(function() { - return [session, verificationTag]; - }); + // If we're initiating we go ahead and set our first sending ephemeral key now, + // otherwise we figure it out when we first maybeStepRatchet with the remote's ephemeral key + if (isInitiator) { + return createNewKeyPair(false).then(function(ourSendingEphemeralKey) { + session.currentRatchet.ephemeralKeyPair = ourSendingEphemeralKey; + return calculateRatchet(session, theirSignedPubKey, true).then(function() { + return session; }); - } else { - session.currentRatchet.ephemeralKeyPair = ourSignedKey; - return [session, verificationTag]; - } - }); + }); + } else { + session.currentRatchet.ephemeralKeyPair = ourSignedKey; + return session; + } }); }); } @@ -562,14 +536,10 @@ window.textsecure.crypto = function() { } } return initSession(false, preKeyPair, signedPreKeyPair, encodedNumber, toArrayBuffer(message.identityKey), toArrayBuffer(message.baseKey), undefined) - .then(function(res) { - if (!isEqual(res[1] , message.verification, true)) - throw new Error('Invalid verification tag'); - + .then(function(new_sesion) { // Note that the session is not actually saved until the very end of decryptWhisperMessage // ... to ensure that the sender actually holds the private keys for all reported pubkeys - // (though this is actually not needed any more thanks to the verification tag) - return [res[0], function() { + return [new_session, function() { if (open_session !== undefined) crypto_storage.saveSession(encodedNumber, open_session); crypto_storage.removeStoredKeyPair("preKey" + message.preKeyId); @@ -702,7 +672,6 @@ window.textsecure.crypto = function() { removeOldChains(session); delete session['pendingPreKey']; - delete session['pendingVerification']; var finalMessage = textsecure.protos.decodePushMessageContentProtobuf(getString(plaintext)); @@ -841,12 +810,8 @@ window.textsecure.crypto = function() { preKeyMsg.baseKey = toArrayBuffer(baseKey.pubKey); return initSession(true, baseKey, undefined, deviceObject.encodedNumber, toArrayBuffer(deviceObject.identityKey), toArrayBuffer(deviceObject.preKey), toArrayBuffer(deviceObject.signedKey)) - .then(function(res) { - preKeyMsg.verification = res[1].slice(0, 8); - - session = res[0]; + .then(function(session) { session.pendingPreKey = baseKey.pubKey; - session.pendingVerification = preKeyMsg.verification; return doEncryptPushMessageContent().then(function(message) { preKeyMsg.message = message; var result = String.fromCharCode((3 << 4) | 3) + getString(preKeyMsg.encode()); @@ -858,7 +823,6 @@ window.textsecure.crypto = function() { return doEncryptPushMessageContent().then(function(message) { if (session.pendingPreKey !== undefined) { preKeyMsg.baseKey = toArrayBuffer(session.pendingPreKey); - preKeyMsg.verification = toArrayBuffer(session.pendingVerification); preKeyMsg.message = message; var result = String.fromCharCode((3 << 4) | 3) + getString(preKeyMsg.encode()); return {type: 3, body: result}; diff --git a/protos/WhisperTextProtocol.proto b/protos/WhisperTextProtocol.proto index 387d6072145..5aee7525ca3 100644 --- a/protos/WhisperTextProtocol.proto +++ b/protos/WhisperTextProtocol.proto @@ -16,7 +16,6 @@ message PreKeyWhisperMessage { optional uint32 signedPreKeyId = 6; optional bytes baseKey = 2; optional bytes identityKey = 3; - optional bytes verification = 7; optional bytes message = 4; // WhisperMessage } @@ -26,5 +25,4 @@ message KeyExchangeMessage { optional bytes ephemeralKey = 3; optional bytes identityKey = 4; optional bytes baseKeySignature = 5; - optional bytes verification = 6; }