I lied, there is no verification tag

This commit is contained in:
Matt Corallo 2014-07-24 05:14:38 -04:00
parent 85f33345ab
commit eab0911d3d
2 changed files with 27 additions and 65 deletions

View file

@ -326,11 +326,7 @@ window.textsecure.crypto = function() {
infoArray.set(new Uint8Array(T1)); infoArray.set(new Uint8Array(T1));
infoArray[infoArray.length - 1] = 2; infoArray[infoArray.length - 1] = 2;
return HmacSHA256(PRK, infoBuffer).then(function(T2) { return HmacSHA256(PRK, infoBuffer).then(function(T2) {
infoArray.set(new Uint8Array(T2)); return [ T1, T2 ];
infoArray[infoArray.length - 1] = 3;
return HmacSHA256(PRK, infoBuffer).then(function(T3) {
return [ T1, T2, T3 ];
});
}); });
}); });
}); });
@ -403,16 +399,11 @@ window.textsecure.crypto = function() {
theirSignedPubKey = theirEphemeralPubKey; theirSignedPubKey = theirEphemeralPubKey;
} }
var PUB_KEYS_VERIFICATION_MAGIC = toArrayBuffer("TextSecure Verification Tag"); var sharedSecret;
if (ourEphemeralKey === undefined || theirEphemeralPubKey === undefined)
var sharedSecret; var pubKeysVerification;
if (ourEphemeralKey === undefined || theirEphemeralPubKey === undefined) {
sharedSecret = new Uint8Array(32 * 4); sharedSecret = new Uint8Array(32 * 4);
pubKeysVerification = new Uint8Array(33 * 5 + PUB_KEYS_VERIFICATION_MAGIC.byteLength); else
} else {
sharedSecret = new Uint8Array(32 * 5); sharedSecret = new Uint8Array(32 * 5);
pubKeysVerification = new Uint8Array(33 * 6 + PUB_KEYS_VERIFICATION_MAGIC.byteLength);
}
for (var i = 0; i < 32; i++) for (var i = 0; i < 32; i++)
sharedSecret[i] = 0xff; sharedSecret[i] = 0xff;
@ -423,45 +414,28 @@ window.textsecure.crypto = function() {
sharedSecret.set(new Uint8Array(ecRes), 32 * 3); sharedSecret.set(new Uint8Array(ecRes), 32 * 3);
return HKDF(sharedSecret.buffer, '', "WhisperText").then(function(masterKey) { 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)); // If we're initiating we go ahead and set our first sending ephemeral key now,
var i = PUB_KEYS_VERIFICATION_MAGIC.byteLength - 33; // otherwise we figure it out when we first maybeStepRatchet with the remote's ephemeral key
if (isInitiator) {
pubKeysVerification.set(new Uint8Array(isInitiator ? ourSignedKey.pubKey : theirSignedPubKey), i += 33); return createNewKeyPair(false).then(function(ourSendingEphemeralKey) {
pubKeysVerification.set(new Uint8Array(isInitiator ? ourIdentityKey.pubKey : theirIdentityPubKey), i += 33); session.currentRatchet.ephemeralKeyPair = ourSendingEphemeralKey;
return calculateRatchet(session, theirSignedPubKey, true).then(function() {
pubKeysVerification.set(new Uint8Array(!isInitiator ? ourSignedKey.pubKey : theirSignedPubKey), i += 33); return session;
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];
});
}); });
} else { });
session.currentRatchet.ephemeralKeyPair = ourSignedKey; } else {
return [session, verificationTag]; 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) return initSession(false, preKeyPair, signedPreKeyPair, encodedNumber, toArrayBuffer(message.identityKey), toArrayBuffer(message.baseKey), undefined)
.then(function(res) { .then(function(new_sesion) {
if (!isEqual(res[1] , message.verification, true))
throw new Error('Invalid verification tag');
// Note that the session is not actually saved until the very end of decryptWhisperMessage // 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 // ... 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 [new_session, function() {
return [res[0], function() {
if (open_session !== undefined) if (open_session !== undefined)
crypto_storage.saveSession(encodedNumber, open_session); crypto_storage.saveSession(encodedNumber, open_session);
crypto_storage.removeStoredKeyPair("preKey" + message.preKeyId); crypto_storage.removeStoredKeyPair("preKey" + message.preKeyId);
@ -702,7 +672,6 @@ window.textsecure.crypto = function() {
removeOldChains(session); removeOldChains(session);
delete session['pendingPreKey']; delete session['pendingPreKey'];
delete session['pendingVerification'];
var finalMessage = textsecure.protos.decodePushMessageContentProtobuf(getString(plaintext)); var finalMessage = textsecure.protos.decodePushMessageContentProtobuf(getString(plaintext));
@ -841,12 +810,8 @@ window.textsecure.crypto = function() {
preKeyMsg.baseKey = toArrayBuffer(baseKey.pubKey); preKeyMsg.baseKey = toArrayBuffer(baseKey.pubKey);
return initSession(true, baseKey, undefined, deviceObject.encodedNumber, return initSession(true, baseKey, undefined, deviceObject.encodedNumber,
toArrayBuffer(deviceObject.identityKey), toArrayBuffer(deviceObject.preKey), toArrayBuffer(deviceObject.signedKey)) toArrayBuffer(deviceObject.identityKey), toArrayBuffer(deviceObject.preKey), toArrayBuffer(deviceObject.signedKey))
.then(function(res) { .then(function(session) {
preKeyMsg.verification = res[1].slice(0, 8);
session = res[0];
session.pendingPreKey = baseKey.pubKey; session.pendingPreKey = baseKey.pubKey;
session.pendingVerification = preKeyMsg.verification;
return doEncryptPushMessageContent().then(function(message) { return doEncryptPushMessageContent().then(function(message) {
preKeyMsg.message = message; preKeyMsg.message = message;
var result = String.fromCharCode((3 << 4) | 3) + getString(preKeyMsg.encode()); var result = String.fromCharCode((3 << 4) | 3) + getString(preKeyMsg.encode());
@ -858,7 +823,6 @@ window.textsecure.crypto = function() {
return doEncryptPushMessageContent().then(function(message) { return doEncryptPushMessageContent().then(function(message) {
if (session.pendingPreKey !== undefined) { if (session.pendingPreKey !== undefined) {
preKeyMsg.baseKey = toArrayBuffer(session.pendingPreKey); preKeyMsg.baseKey = toArrayBuffer(session.pendingPreKey);
preKeyMsg.verification = toArrayBuffer(session.pendingVerification);
preKeyMsg.message = message; preKeyMsg.message = message;
var result = String.fromCharCode((3 << 4) | 3) + getString(preKeyMsg.encode()); var result = String.fromCharCode((3 << 4) | 3) + getString(preKeyMsg.encode());
return {type: 3, body: result}; return {type: 3, body: result};

View file

@ -16,7 +16,6 @@ message PreKeyWhisperMessage {
optional uint32 signedPreKeyId = 6; optional uint32 signedPreKeyId = 6;
optional bytes baseKey = 2; optional bytes baseKey = 2;
optional bytes identityKey = 3; optional bytes identityKey = 3;
optional bytes verification = 7;
optional bytes message = 4; // WhisperMessage optional bytes message = 4; // WhisperMessage
} }
@ -26,5 +25,4 @@ message KeyExchangeMessage {
optional bytes ephemeralKey = 3; optional bytes ephemeralKey = 3;
optional bytes identityKey = 4; optional bytes identityKey = 4;
optional bytes baseKeySignature = 5; optional bytes baseKeySignature = 5;
optional bytes verification = 6;
} }