Add closeSession tests as Alice and fix re-requesting prekeys

This commit is contained in:
Matt Corallo 2014-07-24 20:15:27 -04:00
parent 6f3ee151f3
commit c427da04f0
7 changed files with 157 additions and 32 deletions

View File

@ -164,12 +164,15 @@ window.textsecure.api = function() {
});
};
self.getKeysForNumber = function(number) {
self.getKeysForNumber = function(number, deviceId) {
if (deviceId === undefined)
deviceId = "*";
return doAjax({
call : 'keys',
httpType : 'GET',
do_auth : true,
urlParameters : "/" + number + "/*",
urlParameters : "/" + number + "/" + deviceId,
}).then(function(res) {
var promises = [];
res.identityKey = base64DecToArr(res.identityKey);

View File

@ -140,12 +140,16 @@ window.textsecure.crypto = function() {
var device = textsecure.storage.devices.getDeviceObject(encodedNumber);
if (device === undefined)
device = { sessions: {}, encodedNumber: encodedNumber };
if (device.sessions === undefined)
device.sessions = {};
if (registrationId !== undefined)
device.registrationId = registrationId;
crypto_storage.saveSessionAndDevice(device, session);
}
crypto_storage.saveSessionAndDevice = function(device, session) {
if (device.sessions === undefined)
device.sessions = {};
var sessions = device.sessions;
var doDeleteSession = false;
@ -172,6 +176,15 @@ window.textsecure.crypto = function() {
else
sessions[getString(session.indexInfo.baseKey)] = session;
var openSessionRemaining = false;
for (var key in sessions)
if (sessions[key].indexInfo.closed == -1)
openSessionRemaining = true;
if (!openSessionRemaining)
try {
delete device['registrationId'];
} catch(_) {}
textsecure.storage.devices.saveDeviceObject(device);
}
@ -678,7 +691,6 @@ window.textsecure.crypto = function() {
throw new Error('Invalid padding');
}
removeOldChains(session);
delete session['pendingPreKey'];
var finalMessage = textsecure.protos.decodePushMessageContentProtobuf(getString(plaintext));
@ -687,6 +699,8 @@ window.textsecure.crypto = function() {
== textsecure.protos.PushMessageContentProtobuf.Flags.END_SESSION)
closeSession(session);
removeOldChains(session);
crypto_storage.saveSession(encodedNumber, session, registrationId);
return finalMessage;
});
@ -805,7 +819,15 @@ window.textsecure.crypto = function() {
result[0] = (3 << 4) | 3;
result.set(new Uint8Array(encodedMsg), 1);
result.set(new Uint8Array(mac, 0, 8), encodedMsg.byteLength + 1);
crypto_storage.saveSession(deviceObject.encodedNumber, session);
try {
delete deviceObject['signedKey'];
delete deviceObject['signedKeyId'];
delete deviceObject['preKey'];
delete deviceObject['preKeyId'];
} catch(_) {}
crypto_storage.saveSessionAndDevice(deviceObject, session);
return result;
});
});
@ -815,18 +837,18 @@ window.textsecure.crypto = function() {
var preKeyMsg = new textsecure.protos.PreKeyWhisperMessageProtobuf();
preKeyMsg.identityKey = toArrayBuffer(crypto_storage.getIdentityKey().pubKey);
preKeyMsg.preKeyId = deviceObject.preKeyId;
preKeyMsg.signedPreKeyId = deviceObject.signedKeyId;
preKeyMsg.registrationId = textsecure.storage.getUnencrypted("registrationId");
if (session === undefined) {
return createNewKeyPair(false).then(function(baseKey) {
preKeyMsg.preKeyId = deviceObject.preKeyId;
preKeyMsg.signedPreKeyId = deviceObject.signedKeyId;
preKeyMsg.baseKey = toArrayBuffer(baseKey.pubKey);
return initSession(true, baseKey, undefined, deviceObject.encodedNumber,
toArrayBuffer(deviceObject.identityKey), toArrayBuffer(deviceObject.preKey), toArrayBuffer(deviceObject.signedKey))
.then(function(new_session) {
session = new_session;
session.pendingPreKey = baseKey.pubKey;
session.pendingPreKey = { preKeyId: deviceObject.preKeyId, signedKeyId: deviceObject.signedKeyId, baseKey: baseKey.pubKey };
return doEncryptPushMessageContent().then(function(message) {
preKeyMsg.message = message;
var result = String.fromCharCode((3 << 4) | 3) + getString(preKeyMsg.encode());
@ -837,8 +859,11 @@ window.textsecure.crypto = function() {
} else
return doEncryptPushMessageContent().then(function(message) {
if (session.pendingPreKey !== undefined) {
preKeyMsg.baseKey = toArrayBuffer(session.pendingPreKey);
preKeyMsg.baseKey = toArrayBuffer(session.pendingPreKey.baseKey);
preKeyMsg.preKeyId = session.pendingPreKey.preKeyId;
preKeyMsg.signedPreKeyId = session.pendingPreKey.signedKeyId;
preKeyMsg.message = message;
var result = String.fromCharCode((3 << 4) | 3) + getString(preKeyMsg.encode());
return {type: 3, body: result};
} else

View File

@ -15,7 +15,7 @@
*/
var getKeysForNumberMap = {};
textsecure.api.getKeysForNumber = function(number) {
textsecure.api.getKeysForNumber = function(number, deviceId) {
var res = getKeysForNumberMap[number];
if (res !== undefined) {
delete getKeysForNumberMap[number];

View File

@ -340,8 +340,8 @@ window.textsecure.storage = function() {
self.devices = function() {
var self = {};
self.saveDeviceObject = function(deviceObject) {
if (deviceObject.identityKey === undefined || deviceObject.registrationId === undefined || deviceObject.encodedNumber === undefined)
var internalSaveDeviceObject = function(deviceObject, onlyKeys) {
if (deviceObject.identityKey === undefined || deviceObject.encodedNumber === undefined)
throw new Error("Tried to store invalid deviceObject");
var number = textsecure.utils.unencodeNumber(deviceObject.encodedNumber)[0];
@ -355,7 +355,15 @@ window.textsecure.storage = function() {
var updated = false;
for (var i in map.devices) {
if (map.devices[i].encodedNumber == deviceObject.encodedNumber) {
map.devices[i] = deviceObject;
if (!onlyKeys)
map.devices[i] = deviceObject;
else {
map.devices[i].preKey = deviceObject.preKey;
map.devices[i].preKeyId = deviceObject.preKeyId;
map.devices[i].signedKey = deviceObject.signedKey;
map.devices[i].signedKeyId = deviceObject.signedKeyId;
map.devices[i].registrationId = deviceObject.registrationId;
}
updated = true;
}
}
@ -367,6 +375,14 @@ window.textsecure.storage = function() {
textsecure.storage.putEncrypted("devices" + number, map);
}
self.saveDeviceObject = function(deviceObject) {
return internalSaveDeviceObject(deviceObject, false);
}
self.saveKeysToDeviceObject = function(deviceObject) {
return internalSaveDeviceObject(deviceObject, true);
}
self.getDeviceObjectsForNumber = function(number) {
var map = textsecure.storage.getEncrypted("devices" + number);
return map === undefined ? [] : map.devices;

View File

@ -5,10 +5,10 @@ window.textsecure.messaging = function() {
var self = {};
function getKeysForNumber(number, updateDevices) {
return textsecure.api.getKeysForNumber(number).then(function(response) {
var handleResult = function(response) {
for (var i in response.devices) {
if (updateDevices === undefined || updateDevices.indexOf(response.devices[i].deviceId) > -1)
textsecure.storage.devices.saveDeviceObject({
textsecure.storage.devices.saveKeysToDeviceObject({
encodedNumber: number + "." + response.devices[i].deviceId,
identityKey: response.identityKey,
preKey: response.devices[i].preKey.publicKey,
@ -18,7 +18,16 @@ window.textsecure.messaging = function() {
registrationId: response.devices[i].registrationId
});
}
});
};
var promises = [];
if (updateDevices !== undefined)
for (var i in updateDevices)
promises[promises.length] = textsecure.api.getKeysForNumber(number, updateDevices[i]).then(handleResult);
else
return textsecure.api.getKeysForNumber(number).then(handleResult);
return Promise.all(promises);
}
// success_callback(server success/failure map), error_callback(error_msg)
@ -165,14 +174,23 @@ window.textsecure.messaging = function() {
var number = numbers[i];
var devicesForNumber = textsecure.storage.devices.getDeviceObjectsForNumber(number);
if (devicesForNumber.length == 0) {
getKeysForNumber(number)
.then(reloadDevicesAndSend(number, true))
.catch(function(error) {
registerError(number, "Failed to retreive new device keys for number " + number, error);
});
} else
doSendMessage(number, devicesForNumber, true);
var promises = [];
for (var i in devicesForNumber)
if (devicesForNumber[i].registrationId === undefined)
promises[promises.length] = getKeysForNumber(number, [parseInt(textsecure.utils.unencodeNumber(devicesForNumber[i].encodedNumber)[1])]);
Promise.all(promises).then(function() {
devicesForNumber = textsecure.storage.devices.getDeviceObjectsForNumber(number);
if (devicesForNumber.length == 0) {
getKeysForNumber(number)
.then(reloadDevicesAndSend(number, true))
.catch(function(error) {
registerError(number, "Failed to retreive new device keys for number " + number, error);
});
} else
doSendMessage(number, devicesForNumber, true);
});
}
}
@ -229,13 +247,16 @@ window.textsecure.messaging = function() {
}
self.closeSession = function(number) {
var devices = textsecure.storage.devices.getDeviceObjectsForNumber(number);
for (var i in devices)
textsecure.crypto.closeOpenSessionForDevice(devices[i].encodedNumber);
var proto = new textsecure.protos.PushMessageContentProtobuf();
proto.body = "TERMINATE";
proto.flags = textsecure.protos.PushMessageContentProtobuf.Flags.END_SESSION;
return sendIndividualProto(number, proto);
return sendIndividualProto(number, proto).then(function(res) {
var devices = textsecure.storage.devices.getDeviceObjectsForNumber(number);
for (var i in devices)
textsecure.crypto.closeOpenSessionForDevice(devices[i].encodedNumber);
return res;
});
}
self.sendMessageToGroup = function(groupId, messageText, attachments) {

View File

@ -339,7 +339,7 @@ textsecure.registerOnLoadFunction(function() {
if (data.getKeys !== undefined)
getKeysForNumberMap["SNOWDEN"] = data.getKeys;
return textsecure.messaging.sendMessageToNumber("SNOWDEN", data.smsText, []).then(function() {
var checkMessage = function() {
var msg = messagesSentMap["SNOWDEN.1"];
delete messagesSentMap["SNOWDEN.1"];
//XXX: This should be all we do: isEqual(data.expectedCiphertext, msg.body, false);
@ -353,7 +353,12 @@ textsecure.registerOnLoadFunction(function() {
var result = getString(msg.body).substring(1);
return getString(decoded.encode()) == result;
}
});
}
if (data.endSession)
return textsecure.messaging.closeSession("SNOWDEN").then(checkMessage);
else
return textsecure.messaging.sendMessageToNumber("SNOWDEN", data.smsText, []).then(checkMessage);
}
if (data.ourBaseKey !== undefined)
@ -435,6 +440,7 @@ textsecure.registerOnLoadFunction(function() {
}
delete testsOutstanding[i];
}
printTestsDone();
startNextExclusiveTest();
}, 10000);

View File

@ -236,6 +236,8 @@ axolotlTestVectors = function() {
expectedSmsText: "C",
}],
];
// Now shuffle them around and make 5 tests
tests[tests.length] = {name: "Axolotl End Session Test Vectors as Bob", vectors: axolotlEndSessionTestVectorsBob};
var axolotlEndSessionTestVectorsBobCopy = function() {
@ -333,5 +335,57 @@ axolotlTestVectors = function() {
return {name: "Shuffled End Session Axolotl Test Vectors as Bob V", vectors: v};
}();
// Same as above except as Alice
var axolotlEndSessionTestVectorsAlice = [
["sendMessage",
{
smsText: "A",
ourBaseKey: hexToArrayBuffer('b9f458404bb8d9a50b4c58fd373ec109f83dc820ae410d6f933c6f9a72e35e4c'),
ourEphemeralKey: hexToArrayBuffer('9133b17c81c14cdf89b3cd449c7b2ad9c91c223a2e627cc9619e20fbac1b8b6a'),
ourIdentityKey: hexToArrayBuffer('a898043b1b447cfae63e2633e34c49d91cfbad8562c815e300c879e10d4c3456'),
registrationId: 5405,
getKeys: {identityKey: hexToArrayBuffer('056c8e7e99343ae057d3962465a42f9b493e35d06c29140fb95bf01bf8b185852f'),
devices: [{
deviceId: 1,
preKey: {keyId: 6598766, publicKey: hexToArrayBuffer('05cbb234552f2b607fc0b08d76d78dd8ce1f6fc7e2dab8dc5103747cfb398b990d')},
signedPreKey: {keyId: 1564819, publicKey: hexToArrayBuffer('05ac707620d65fe630483f17b43f281d6310d43c3a8d2a27d870300a992f241b5e')},
registrationId: 2966
}]
},
expectedCiphertext: hexToArrayBuffer('3308eee09203122105266863a2585d725c244d440ef03a4ffee9a194a454f92b48500210342cf47e171a2105a028c496fa0850958a9ff1a1dfa528f75fa26a763b168de533f01be99b6b971422d301330a2105cbacb784b46fff7eed25243c96d280306b6336ffb6072b425f3fad2d3f9a1c581000180022a001efa8f1802e2e407754ec82aa7cfc18285733dce8d1bdd9ef934599c816b6d942949391184a74f2f1f156d515a91d9b09352d4116bdb023704c7d5d45b1ed7c9a2555d272fd81d871b9a1c8946ea84d094bb44e184ba03a0fd46c8ac827a05e682c6adb10626cfd98d8e267d6bb7daba7dff77affea1d090592fbe6929736154c16c4648da485b3a5996c8e3536b25844fb2763b2c62fbbcecd21608252e27b34dfd3eb6c618c284b289d2a3093c15f'),
}],
["sendMessage",
{
smsText: "B",
expectedCiphertext: hexToArrayBuffer('3308eee09203122105266863a2585d725c244d440ef03a4ffee9a194a454f92b48500210342cf47e171a2105a028c496fa0850958a9ff1a1dfa528f75fa26a763b168de533f01be99b6b971422d301330a2105cbacb784b46fff7eed25243c96d280306b6336ffb6072b425f3fad2d3f9a1c581001180022a001744576061111ffb4e2df634cf2b155e1cc6d252d3f72cd5d7bad5cb68dc46fc7822176975087abddc65b34d5dc2f644314b4be4deb01e050904ff2c067491324736305c8fc8ce7527d1b6e1c20a08f2d3b3208eacb6e6ce0b8af80f941cc7de1b2d625ae8cdd2e40a2ab27aafe13377bc2a407014492a3a9f21cbf0207997873697d02cd7eea97981860a574333e098c4f55094742c24cfbc73da2640609dc2566e9ed7834240ac5289d2a3093c15f'),
}],
["sendMessage",
{
endSession: true,
expectedCiphertext: hexToArrayBuffer('3308eee09203122105266863a2585d725c244d440ef03a4ffee9a194a454f92b48500210342cf47e171a2105a028c496fa0850958a9ff1a1dfa528f75fa26a763b168de533f01be99b6b971422d301330a2105cbacb784b46fff7eed25243c96d280306b6336ffb6072b425f3fad2d3f9a1c581002180022a001e0b7b0679fa466e677a3e18a28b574c286c59ac48dbf5b5e24e289b7222a2353726ad190aa4ab1cc57d8ac50711adb32ebbed369214bde90a66bcc0b042970224206cb05dd02fab534f12e07e7c909fbbf77e678fb282b81298bc01eae024db13eba6b915651487a06a9b62606c844406496c0c878c6c3422d709d8b08db4d22a7c09a036a3aed6479e0ad07da2f6dfc0b9ee58b11a46d72fe38b662e1c09604a76358b7856dadd7289d2a3093c15f'),
}],
["sendMessage",
{
smsText: "C",
ourBaseKey: hexToArrayBuffer('49a4bb5a4da5ddd29697ff77f787177cd9da36007e456e77bc9107a9f4392b66'),
ourEphemeralKey: hexToArrayBuffer('a189e070781266fbc55e27180a6654e496e98f47e98b0a9e9c4e5e66219dd56e'),
ourIdentityKey: hexToArrayBuffer('a898043b1b447cfae63e2633e34c49d91cfbad8562c815e300c879e10d4c3456'),
registrationId: 5405,
getKeys: {identityKey: hexToArrayBuffer('056c8e7e99343ae057d3962465a42f9b493e35d06c29140fb95bf01bf8b185852f'),
devices: [{
deviceId: 1,
preKey: {keyId: 6598767, publicKey: hexToArrayBuffer('054508c2343459b6a0085f216885096ffa7b8312d073b9bcd1748423b1bfc1ab42')},
signedPreKey: {keyId: 1564819, publicKey: hexToArrayBuffer('05ac707620d65fe630483f17b43f281d6310d43c3a8d2a27d870300a992f241b5e')},
registrationId: 2966
}]
},
expectedCiphertext: hexToArrayBuffer('3308efe092031221054057ff80fb53953c149baf1628fb91b8fcd7df883bf63e94b1bab4037d20966a1a2105a028c496fa0850958a9ff1a1dfa528f75fa26a763b168de533f01be99b6b971422d301330a210591ce9658f1587e42d16b76bfc5035837becde75d630802353c5a215612b385431000180022a001cfd82605cf03277ea76d0c65c9c906a0c4568e312ae9c869ebfcca8c5fe4fe2e80e8eb8d674da589cc45522431903fd0540d4c84bc296332273c165ccbb443859fa697a809a33009a7df03a6f32ac9621807433a456227020e209eec06898af1291e5acf2285ea77aeb04c416464b1e5345a4bf237c3004a0b6f8d334c5783599ea4c1e68d2198872cda7e4e224b24a8fac5e17ce641763f4b14a45a48cc7bcf14e69b2a9272a156289d2a3093c15f'),
}],
];
tests[tests.length] = {name: "Standard End Session Axolotl Test Vectors as Alice", vectors: axolotlEndSessionTestVectorsAlice};
return tests;
}();