Another step forward
This commit is contained in:
parent
d9e7e59c3a
commit
87c19c0c94
2 changed files with 202 additions and 78 deletions
114
js/helpers.js
114
js/helpers.js
|
@ -575,17 +575,19 @@ var crypto_tests = {};
|
||||||
|
|
||||||
var decryptAESCTR = function(ciphertext, key, counter) {
|
var decryptAESCTR = function(ciphertext, key, counter) {
|
||||||
//TODO: Waaayyyy less type conversion here (probably just means replacing CryptoJS)
|
//TODO: Waaayyyy less type conversion here (probably just means replacing CryptoJS)
|
||||||
|
var iv = String.fromCharCode(counter & 0xff000000) + String.fromCharCode(counter & 0xff0000) + String.fromCharCode(counter & 0xff00) + String.fromCharCode(counter & 0xff);
|
||||||
return CryptoJS.AES.decrypt(btoa(getString(ciphertext)),
|
return CryptoJS.AES.decrypt(btoa(getString(ciphertext)),
|
||||||
CryptoJS.enc.Latin1.parse(getString(key)),
|
CryptoJS.enc.Latin1.parse(getString(key)),
|
||||||
{mode: CryptoJS.mode.CTR, iv: CryptoJS.enc.Latin1.parse(""), padding: CryptoJS.pad.NoPadding})
|
{mode: CryptoJS.mode.CTR, iv: CryptoJS.enc.Latin1.parse(iv), padding: CryptoJS.pad.NoPadding})
|
||||||
.toString(CryptoJS.enc.Latin1);
|
.toString(CryptoJS.enc.Latin1);
|
||||||
}
|
}
|
||||||
|
|
||||||
var encryptAESCTR = function(plaintext, key, counter) {
|
var encryptAESCTR = function(plaintext, key, counter) {
|
||||||
//TODO: Waaayyyy less type conversion here (probably just means replacing CryptoJS)
|
//TODO: Waaayyyy less type conversion here (probably just means replacing CryptoJS)
|
||||||
|
var iv = String.fromCharCode(counter & 0xff000000) + String.fromCharCode(counter & 0xff0000) + String.fromCharCode(counter & 0xff00) + String.fromCharCode(counter & 0xff);
|
||||||
return CryptoJS.AES.encrypt(CryptoJS.enc.Latin1.parse(getString(plaintext)),
|
return CryptoJS.AES.encrypt(CryptoJS.enc.Latin1.parse(getString(plaintext)),
|
||||||
CryptoJS.enc.Latin1.parse(getString(key)),
|
CryptoJS.enc.Latin1.parse(getString(key)),
|
||||||
{mode: CryptoJS.mode.CTR, iv: CryptoJS.enc.Latin1.parse(""), padding: CryptoJS.pad.NoPadding})
|
{mode: CryptoJS.mode.CTR, iv: CryptoJS.enc.Latin1.parse(iv), padding: CryptoJS.pad.NoPadding})
|
||||||
.ciphertext.toString(CryptoJS.enc.Latin1);
|
.ciphertext.toString(CryptoJS.enc.Latin1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -610,6 +612,20 @@ var crypto_tests = {};
|
||||||
/******************************
|
/******************************
|
||||||
*** Ratchet implementation ***
|
*** Ratchet implementation ***
|
||||||
******************************/
|
******************************/
|
||||||
|
var calculateRatchet = function(session, remoteKey, sending, callback) {
|
||||||
|
var ratchet = session.currentRatchet;
|
||||||
|
|
||||||
|
ECDHE(remoteKey, ratchet.ephemeralKeyPair.privKey, function(sharedSecret) {
|
||||||
|
var masterKey = HKDF(sharedSecret, ratchet.rootKey, "WhisperRatchet");
|
||||||
|
if (sending)
|
||||||
|
session[getString(ratchet.ephemeralKeyPair.pubKey)] = { messageKeys: {}, chainKey: { counter: -1, key: masterKey[1] } };
|
||||||
|
else
|
||||||
|
session[getString(remoteKey)] = { messageKeys: {}, chainKey: { counter: -1, key: masterKey[1] } };
|
||||||
|
ratchet.rootKey = masterKey[0];
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var initSession = function(isInitiator, ourEphemeralKey, encodedNumber, theirIdentityPubKey, theirEphemeralPubKey, callback) {
|
var initSession = function(isInitiator, ourEphemeralKey, encodedNumber, theirIdentityPubKey, theirEphemeralPubKey, callback) {
|
||||||
var ourIdentityPrivKey = crypto_storage.getIdentityPrivKey();
|
var ourIdentityPrivKey = crypto_storage.getIdentityPrivKey();
|
||||||
|
|
||||||
|
@ -622,16 +638,19 @@ var crypto_tests = {};
|
||||||
sharedSecret += getString(ecRes);
|
sharedSecret += getString(ecRes);
|
||||||
var masterKey = HKDF(sharedSecret, '', "WhisperText");
|
var masterKey = HKDF(sharedSecret, '', "WhisperText");
|
||||||
|
|
||||||
var session = {currentRatchet: { rootKey: masterKey[0], ephemeralKeyPair: ourEphemeralKey,
|
// Create new ephemeral key for sending
|
||||||
lastRemoteEphemeralKey: theirEphemeralPubKey },
|
createNewKeyPair(false, function(ourSendingEphemeralKey) {
|
||||||
oldRatchetList: []
|
var session = {currentRatchet: { rootKey: masterKey[0], ephemeralKeyPair: ourSendingEphemeralKey,
|
||||||
};
|
lastRemoteEphemeralKey: theirEphemeralPubKey },
|
||||||
session[getString(ourEphemeralKey.pubKey)] = { messageKeys: {}, chainKey: { counter: -1, key: masterKey[1] } };
|
oldRatchetList: []
|
||||||
// This isnt an actual ratchet, its just here to make maybeStepRatchet work
|
};
|
||||||
session[getString(theirEphemeralPubKey)] = { messageKeys: {}, chainKey: { counter: 0xffffffff, key: '' } };
|
|
||||||
crypto_storage.saveSession(encodedNumber, session);
|
|
||||||
|
|
||||||
callback();
|
calculateRatchet(session, theirEphemeralPubKey, true, function() {
|
||||||
|
crypto_storage.saveSession(encodedNumber, session);
|
||||||
|
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -662,6 +681,9 @@ var crypto_tests = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
var fillMessageKeys = function(chain, counter) {
|
var fillMessageKeys = function(chain, counter) {
|
||||||
|
if (chain.chainKey.counter + 1000 < counter)
|
||||||
|
return; // Stalker, much?
|
||||||
|
|
||||||
var messageKeys = chain.messageKeys;
|
var messageKeys = chain.messageKeys;
|
||||||
var key = chain.chainKey.key;
|
var key = chain.chainKey.key;
|
||||||
for (var i = chain.chainKey.counter; i < counter; i++) {
|
for (var i = chain.chainKey.counter; i < counter; i++) {
|
||||||
|
@ -673,34 +695,30 @@ var crypto_tests = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
var maybeStepRatchet = function(session, remoteKey, previousCounter, callback) {
|
var maybeStepRatchet = function(session, remoteKey, previousCounter, callback) {
|
||||||
if (session[getString(remoteKey)] !== undefined) { //TODO: null???
|
if (session[getString(remoteKey)] !== undefined) {
|
||||||
callback();//TODO: This is happening in tests as alice (when bob is checking), probably shouldn't?
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ratchet = session.currentRatchet;
|
var ratchet = session.currentRatchet;
|
||||||
|
|
||||||
var previousRatchet = session[getString(ratchet.lastRemoteEphemeralKey)];
|
var previousRatchet = session[getString(ratchet.lastRemoteEphemeralKey)];
|
||||||
fillMessageKeys(previousRatchet, previousCounter);
|
if (previousRatchet !== undefined) {
|
||||||
if (!objectContainsKeys(previousRatchet.messageKeys))
|
fillMessageKeys(previousRatchet, previousCounter);
|
||||||
delete session[getString(ratchet.lastRemoteEphemeralKey)];
|
if (!objectContainsKeys(previousRatchet.messageKeys))
|
||||||
else
|
delete session[getString(ratchet.lastRemoteEphemeralKey)];
|
||||||
session.oldRatchetList[session.oldRatchetList.length] = { added: new Date().getTime(), ephemeralKey: ratchet.lastRemoteEphemeralKey };
|
else
|
||||||
|
session.oldRatchetList[session.oldRatchetList.length] = { added: new Date().getTime(), ephemeralKey: ratchet.lastRemoteEphemeralKey };
|
||||||
|
}
|
||||||
|
|
||||||
delete session[ratchet.ephemeralKeyPair.pubKey];
|
calculateRatchet(session, remoteKey, false, function() {
|
||||||
|
// Now swap the ephemeral key and calculate the new sending chain
|
||||||
ECDHE(remoteKey, ratchet.ephemeralKeyPair.privKey, function(sharedSecret) {
|
ratchet.previousCounter = session[getString(ratchet.ephemeralKeyPair.pubKey)].chainKey.counter;
|
||||||
var masterKey = HKDF(sharedSecret, ratchet.rootKey, "WhisperRatchet");
|
delete session[getString(ratchet.ephemeralKeyPair.pubKey)];
|
||||||
session[getString(remoteKey)] = { messageKeys: {}, chainKey: { counter: -1, key: masterKey[1] } };
|
|
||||||
|
|
||||||
createNewKeyPair(false, function(keyPair) {
|
createNewKeyPair(false, function(keyPair) {
|
||||||
ratchet.ephemeralKeyPair = keyPair;
|
ratchet.ephemeralKeyPair = keyPair;
|
||||||
|
calculateRatchet(session, remoteKey, true, function() {
|
||||||
ECDHE(remoteKey, ratchet.ephemeralKeyPair.privKey, function(sharedSecret) {
|
|
||||||
masterKey = HKDF(sharedSecret, masterKey[0], "WhisperRatchet");
|
|
||||||
ratchet.rootKey = masterKey[0];
|
|
||||||
session[getString(ratchet.ephemeralKeyPair.pubKey)] = { messageKeys: {}, chainKey: { counter: -1, key: masterKey[1] } };
|
|
||||||
|
|
||||||
ratchet.lastRemoteEphemeralKey = remoteKey;
|
ratchet.lastRemoteEphemeralKey = remoteKey;
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
|
@ -733,6 +751,7 @@ var crypto_tests = {};
|
||||||
var plaintext = decryptAESCTR(message.ciphertext, keys[0], message.counter);
|
var plaintext = decryptAESCTR(message.ciphertext, keys[0], message.counter);
|
||||||
|
|
||||||
//TODO: removeOldChains(session);
|
//TODO: removeOldChains(session);
|
||||||
|
delete session['pendingPreKey'];
|
||||||
|
|
||||||
crypto_storage.saveSession(encodedNumber, session);
|
crypto_storage.saveSession(encodedNumber, session);
|
||||||
callback(decodePushMessageContentProtobuf(plaintext));
|
callback(decodePushMessageContentProtobuf(plaintext));
|
||||||
|
@ -796,15 +815,14 @@ var crypto_tests = {};
|
||||||
msg.ephemeralKey = toArrayBuffer(session.currentRatchet.ephemeralKeyPair.pubKey);
|
msg.ephemeralKey = toArrayBuffer(session.currentRatchet.ephemeralKeyPair.pubKey);
|
||||||
var chain = session[getString(msg.ephemeralKey)];
|
var chain = session[getString(msg.ephemeralKey)];
|
||||||
|
|
||||||
fillMessageKeys(chain, chain.counter + 1);
|
fillMessageKeys(chain, chain.chainKey.counter + 1);
|
||||||
var keys = HKDF(chain.messageKeys[chain.counter], '', "WhisperMessageKeys");
|
var keys = HKDF(chain.messageKeys[chain.chainKey.counter], '', "WhisperMessageKeys");
|
||||||
delete chain.messageKeys[chain.counter];
|
|
||||||
msg.counter = chain.counter;
|
|
||||||
|
|
||||||
//TODO
|
delete chain.messageKeys[chain.chainKey.counter];
|
||||||
msg.previousCounter = 1;
|
msg.counter = chain.chainKey.counter;
|
||||||
|
msg.previousCounter = session.currentRatchet.previousCounter;
|
||||||
|
|
||||||
msg.ciphertext = toArrayBuffer(encryptAESCTR(plaintext, keys[0], chain.counter));
|
msg.ciphertext = toArrayBuffer(encryptAESCTR(plaintext, keys[0], chain.chainKey.counter));
|
||||||
var encodedMsg = getString(msg.encode());
|
var encodedMsg = getString(msg.encode());
|
||||||
|
|
||||||
var mac = calculateMACWithVersionByte(encodedMsg, keys[1], (2 << 4) | 2);
|
var mac = calculateMACWithVersionByte(encodedMsg, keys[1], (2 << 4) | 2);
|
||||||
|
@ -814,16 +832,18 @@ var crypto_tests = {};
|
||||||
callback(result);
|
callback(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var preKeyMsg = new PreKeyWhisperMessageProtobuf();
|
||||||
|
preKeyMsg.identityKey = toArrayBuffer(crypto_storage.getStoredPubKey("identityKey"));
|
||||||
|
preKeyMsg.preKeyId = deviceObject.preKeyId;
|
||||||
|
preKeyMsg.registrationId = deviceObject.registrationId;
|
||||||
|
|
||||||
if (session === undefined) {
|
if (session === undefined) {
|
||||||
var preKeyMsg = new PreKeyWhisperMessageProtobuf();
|
|
||||||
preKeyMsg.identityKey = toArrayBuffer(crypto_storage.getStoredPubKey("identityKey"));
|
|
||||||
createNewKeyPair(false, function(baseKey) {
|
createNewKeyPair(false, function(baseKey) {
|
||||||
preKeyMsg.baseKey = toArrayBuffer(baseKey.pubKey);
|
preKeyMsg.baseKey = toArrayBuffer(baseKey.pubKey);
|
||||||
preKeyMsg.preKeyId = deviceObject.preKeyId;
|
|
||||||
initSession(true, baseKey, deviceObject.encodedNumber, deviceObject.identityKey, deviceObject.publicKey, function() {
|
initSession(true, baseKey, deviceObject.encodedNumber, deviceObject.identityKey, deviceObject.publicKey, function() {
|
||||||
//TODO: Delete preKey info now?
|
//TODO: Delete preKey info on first message received back
|
||||||
session = crypto_storage.getSession(deviceObject.encodedNumber);
|
session = crypto_storage.getSession(deviceObject.encodedNumber);
|
||||||
//TODO: We need to step ratchet here, I think
|
session.pendingPreKey = baseKey.pubKey;
|
||||||
doEncryptPushMessageContent(function(message) {
|
doEncryptPushMessageContent(function(message) {
|
||||||
preKeyMsg.message = toArrayBuffer(message);
|
preKeyMsg.message = toArrayBuffer(message);
|
||||||
var result = String.fromCharCode((2 << 4) | 2) + getString(preKeyMsg.encode());
|
var result = String.fromCharCode((2 << 4) | 2) + getString(preKeyMsg.encode());
|
||||||
|
@ -833,7 +853,13 @@ var crypto_tests = {};
|
||||||
});
|
});
|
||||||
} else
|
} else
|
||||||
doEncryptPushMessageContent(function(message) {
|
doEncryptPushMessageContent(function(message) {
|
||||||
callback({type: 1, body: getString(message)});
|
if (session.pendingPreKey !== undefined) {
|
||||||
|
preKeyMsg.baseKey = toArrayBuffer(session.pendingPreKey);
|
||||||
|
preKeyMsg.message = toArrayBuffer(message);
|
||||||
|
var result = String.fromCharCode((2 << 4) | 2) + getString(preKeyMsg.encode());
|
||||||
|
callback({type: 3, body: result});
|
||||||
|
} else
|
||||||
|
callback({type: 1, body: getString(message)});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -956,7 +982,8 @@ function getKeysForNumber(number, success_callback, error_callback) {
|
||||||
encodedNumber: number + "." + response[i].deviceId,
|
encodedNumber: number + "." + response[i].deviceId,
|
||||||
identityKey: response[i].identityKey,
|
identityKey: response[i].identityKey,
|
||||||
publicKey: response[i].publicKey,
|
publicKey: response[i].publicKey,
|
||||||
preKeyId: response[i].keyId
|
preKeyId: response[i].keyId,
|
||||||
|
registrationId: response[i].registrationId
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error_callback(e);
|
error_callback(e);
|
||||||
|
@ -991,6 +1018,7 @@ function sendMessageToDevices(number, deviceObjectList, message, success_callbac
|
||||||
jsonData[i] = {
|
jsonData[i] = {
|
||||||
type: encryptedMsg.type,
|
type: encryptedMsg.type,
|
||||||
destination: deviceObjectList[i].encodedNumber,
|
destination: deviceObjectList[i].encodedNumber,
|
||||||
|
destinationRegistrationId: deviceObjectList[i].registrationId,
|
||||||
body: encryptedMsg.body,
|
body: encryptedMsg.body,
|
||||||
timestamp: new Date().getTime()
|
timestamp: new Date().getTime()
|
||||||
};
|
};
|
||||||
|
|
166
js/test.js
166
js/test.js
|
@ -184,7 +184,7 @@ registerOnLoadFunction(function() {
|
||||||
}, "HMAC RFC5869 Test vectors");*/
|
}, "HMAC RFC5869 Test vectors");*/
|
||||||
|
|
||||||
|
|
||||||
var simpleAxolotlTestVectors = {
|
/*var simpleAxolotlTestVectors = {
|
||||||
aliceIdentityPriv: hexToArrayBuffer("08ebc1e1fdbbc88d1a833a9d8c287328d4f749b7b7eb20afda0957dc05efc258"),
|
aliceIdentityPriv: hexToArrayBuffer("08ebc1e1fdbbc88d1a833a9d8c287328d4f749b7b7eb20afda0957dc05efc258"),
|
||||||
aliceIdentityPub: hexToArrayBuffer("05b9c152cb9fefb0a12df319ae50c728c7909a8a080fcf22d5e1842352186d3870"),
|
aliceIdentityPub: hexToArrayBuffer("05b9c152cb9fefb0a12df319ae50c728c7909a8a080fcf22d5e1842352186d3870"),
|
||||||
bobIdentityPriv: hexToArrayBuffer("08491ea8a9aff03a724cfb44411502f3e974010e62b6db2703b9506a2e18554e"),
|
bobIdentityPriv: hexToArrayBuffer("08491ea8a9aff03a724cfb44411502f3e974010e62b6db2703b9506a2e18554e"),
|
||||||
|
@ -246,42 +246,42 @@ encryptedMessage: hexToArrayBuffer("415a326e6f457937756a6c5355785876342f6b585634
|
||||||
|
|
||||||
TEST(function(callback) {
|
TEST(function(callback) {
|
||||||
simpleAxolotlTestVectorsAsBob(simpleAxolotlTestVectors, callback);
|
simpleAxolotlTestVectorsAsBob(simpleAxolotlTestVectors, callback);
|
||||||
}, "Simple Axolotl test vectors as bob", true);
|
}, "Simple Axolotl test vectors as bob", true);*/
|
||||||
|
|
||||||
|
|
||||||
var axolotlTwoPartyTestVectorsAlice = [
|
var axolotlTwoPartyTestVectorsAlice = [
|
||||||
["sendMessage",
|
["sendMessage",
|
||||||
{
|
{
|
||||||
smsText: "A",
|
smsText: "A ",
|
||||||
ourBaseKey: hexToArrayBuffer('91918ad75986b7fa7546dcf9f36baa4997c2ab4c5f0f0e3a1274907e06dd027d'),
|
ourBaseKey: hexToArrayBuffer('192b4892aa2e4cff1293999dc7c367874456c4d920aae7d9d42e5e62c965546c'),
|
||||||
ourEphemeralKey: hexToArrayBuffer('11cd8695564c84153543ab4b47b7da6f89dca070249f734f08ab6f10062b857f'),
|
ourEphemeralKey: hexToArrayBuffer('f12704787bab04a3cf544ebd9d421b6fe36147519eb5afa7c90e3fb67c141e64'),
|
||||||
ourIdentityKey: hexToArrayBuffer('0806209dfac61e8d4e99a855908f86bf1eb874851223902718eed7a54d672960'),
|
ourIdentityKey: hexToArrayBuffer('a05fd14abb42ff393004eee526e3167441ee51021c6d801b784720c15637747c'),
|
||||||
theirPreKey: hexToArrayBuffer('05522fdfff7ded73e9006096be562eb2d99847fba6fb870300db82ccf9a557a534'),
|
theirPreKey: hexToArrayBuffer('05fee424a5b6ccb717d85ef2207e2057ab1144c40afe89cdc80e9c424dd90c146e'),
|
||||||
theirPreKeyId: 16728523,
|
theirPreKeyId: 13845842,
|
||||||
theirRegistrationId: 9072,//TODO: Figure out wtf this is for
|
theirRegistrationId: 11593,//TODO: Figure out wtf this is for
|
||||||
theirIdentityKey: hexToArrayBuffer('05ae90af12c5cb02daa6bc29ac95082d7e66c96bf60215b33215079f0231a00763'),
|
theirIdentityKey: hexToArrayBuffer('05276e4df34557386f67df38b708eeddb1a8924e0428b9eefdc9213c3e8927cc7d'),
|
||||||
//expectedPlaintext: hexToArrayBuffer('0a0e4120202020202020202020202020'),
|
//expectedPlaintext: hexToArrayBuffer('0a0e4120202020202020202020202020'),
|
||||||
//expectedCounter: 0,
|
//expectedCounter: 0,
|
||||||
expectedCiphertext: hexToArrayBuffer('2208cb83fd071221057f5f3adf3b11525967a6c7d9f4fb34df4c79f40b4827cced8d4cd9c0c85d7e601a210582d1fb7ab4f8f0ecfdea447de66a4e19cd2c3502f2b04b8829dd5bb36bd0d6242242220a21059f20776ec3d0975ebe54f8efaf847fdf227ffb2cc057de845c2e963984e5a878100018002210f5dd335e2d1ec1e7335a11c9e1c2a159a0f432040b064abe28f046'),
|
expectedCiphertext: hexToArrayBuffer('2208d28acd061221059ab4e844771bfeb96382edac5f80e757a1109b5611c770b2ba9f28b363d7c2541a2105bd61aea7fa5304f4dc914892bc3795812cda8bb90b73de9920e22c609cf0ec4e2242220a21058c0c357a3a25e6da46b0186d93fec31d5b86a4ac4973742012d8e9de2346be161000180022104bd27ab87ee151d71cdfe89828050ef4b05bddfb56da491728c95a'),
|
||||||
}],
|
}],
|
||||||
/*["sendMessage",
|
["sendMessage",
|
||||||
{
|
{
|
||||||
smsText: "B",
|
smsText: "B ",
|
||||||
//expectedPlaintext: hexToArrayBuffer('0a0e4220202020202020202020202020'),
|
//expectedPlaintext: hexToArrayBuffer('0a0e4220202020202020202020202020'),
|
||||||
//expectedCounter: 1,
|
//expectedCounter: 1,
|
||||||
expectedCiphertext: hexToArrayBuffer('2208cb83fd071221057f5f3adf3b11525967a6c7d9f4fb34df4c79f40b4827cced8d4cd9c0c85d7e601a210582d1fb7ab4f8f0ecfdea447de66a4e19cd2c3502f2b04b8829dd5bb36bd0d6242242220a21059f20776ec3d0975ebe54f8efaf847fdf227ffb2cc057de845c2e963984e5a878100118002210efa08202abe3ad52828a9a782f3aa5fc18ea1a709fcc6e6828f046'),
|
expectedCiphertext: hexToArrayBuffer('2208d28acd061221059ab4e844771bfeb96382edac5f80e757a1109b5611c770b2ba9f28b363d7c2541a2105bd61aea7fa5304f4dc914892bc3795812cda8bb90b73de9920e22c609cf0ec4e2242220a21058c0c357a3a25e6da46b0186d93fec31d5b86a4ac4973742012d8e9de2346be16100118002210b40da85e4998984b4bac1748045b3661f46657badd576b4128c95a'),
|
||||||
}],
|
}],
|
||||||
["receiveMessage",
|
["receiveMessage",
|
||||||
{
|
{
|
||||||
message: hexToArrayBuffer('0801120c2b313333333333333333333328fec18dd4d3283239220a21051ac83687a0dc5acf4595e12411358fe7efeb068b7deb96f2ae6d25f2a4231f2e100018ffffffff0f220321deef7e214d158ebbd10c3801'),
|
message: hexToArrayBuffer('220a2105edf6feeea8ee95f4b227453d5942d15731cb7a962eff6d04706860a4d577476f100018ffffffff0f22032a53da435b5477336965c6'),
|
||||||
newEphemeralKey: hexToArrayBuffer('21ba1f11c4e9be34da2af78345cb7a04143c67dc084c37b6bd9c3dbc68c86a58'),
|
newEphemeralKey: hexToArrayBuffer('a92a28cf21fb48745ebf68b425a1811476fed69f8623ff5941fd4e547ee4027c'),
|
||||||
//expectedPlaintext: hexToArrayBuffer('220a21051ac83687a0dc5acf4595e12411358fe7efeb068b7deb96f2ae6d25f2a4231f2e100018ffffffff0f220321deef7e214d158ebbd10c'),
|
//expectedPlaintext: hexToArrayBuffer(''),
|
||||||
expectedSmsText: "C",
|
expectedSmsText: "C",
|
||||||
}],
|
}],
|
||||||
["receiveMessage",
|
["receiveMessage",
|
||||||
{
|
{
|
||||||
message: hexToArrayBuffer('0801120c2b313333333333333333333328b3da8dd4d3283239220a21051ac83687a0dc5acf4595e12411358fe7efeb068b7deb96f2ae6d25f2a4231f2e100118ffffffff0f220388e968d2d102a298b2c92a3801'),
|
message: hexToArrayBuffer('220a2105edf6feeea8ee95f4b227453d5942d15731cb7a962eff6d04706860a4d577476f100118ffffffff0f2203a439d199228e124820c28b'),
|
||||||
//expectedPlaintext: hexToArrayBuffer('220a21051ac83687a0dc5acf4595e12411358fe7efeb068b7deb96f2ae6d25f2a4231f2e100118ffffffff0f220388e968d2d102a298b2c92a'),
|
//expectedPlaintext: hexToArrayBuffer(''),
|
||||||
expectedSmsText: "D",
|
expectedSmsText: "D",
|
||||||
}],
|
}],
|
||||||
["sendMessage",
|
["sendMessage",
|
||||||
|
@ -289,24 +289,24 @@ encryptedMessage: hexToArrayBuffer("415a326e6f457937756a6c5355785876342f6b585634
|
||||||
smsText: "E",
|
smsText: "E",
|
||||||
//expectedPlaintext: hexToArrayBuffer('0a0145'),
|
//expectedPlaintext: hexToArrayBuffer('0a0145'),
|
||||||
//expectedCounter: 0,
|
//expectedCounter: 0,
|
||||||
expectedCiphertext: hexToArrayBuffer('220a2105f4465a6be68def783cb36b26fa4d2b6eb80b6fa7678a58181f0492121a760b48100018012203bf89479b777a890a700a36'),
|
expectedCiphertext: hexToArrayBuffer('220a2105f94173eeb7ff19ab9196461d596324385611fadef0ca29592cc182d92eb653281000180122031b67b3e2c43b9a672c9cb0'),
|
||||||
}],*/
|
}],
|
||||||
];
|
];
|
||||||
|
|
||||||
var axolotlTwoPartyTestVectorsBob = [
|
var axolotlTwoPartyTestVectorsBob = [
|
||||||
["receiveMessage",
|
["receiveMessage",
|
||||||
{
|
{
|
||||||
message: hexToArrayBuffer('0803120c2b313232323232323232323228b3e48cd4d3283293012208cb83fd071221057f5f3adf3b11525967a6c7d9f4fb34df4c79f40b4827cced8d4cd9c0c85d7e601a210582d1fb7ab4f8f0ecfdea447de66a4e19cd2c3502f2b04b8829dd5bb36bd0d6242242220a21059f20776ec3d0975ebe54f8efaf847fdf227ffb2cc057de845c2e963984e5a878100018002210f5dd335e2d1ec1e7335a11c9e1c2a159a0f432040b064abe28f0463801'),
|
message: hexToArrayBuffer(''),
|
||||||
ourPreKey: hexToArrayBuffer('71f2d216192d22de45290135bb2cc9b140c864d9b92d8f238577f4fddb2fcb4d'),
|
ourPreKey: hexToArrayBuffer(''),
|
||||||
ourIdentityKey: hexToArrayBuffer('08b66c917e27319f8f9a69791d7571ce47175e7e3db5fc85a51e264b31cdf56b'),
|
ourIdentityKey: hexToArrayBuffer(''),
|
||||||
newEphemeralKey: hexToArrayBuffer('416f63ac4939c0bdd0d9e3b589e1ffde4906f54629742f189e6028c08a5d2666'),
|
newEphemeralKey: hexToArrayBuffer(''),
|
||||||
//expectedPlaintext: hexToArrayBuffer('220a21059f20776ec3d0975ebe54f8efaf847fdf227ffb2cc057de845c2e963984e5a878100018002210f5dd335e2d1ec1e7335a11c9e1c2a159a0f432040b064abe'),
|
//expectedPlaintext: hexToArrayBuffer(''),
|
||||||
expectedSmsText: "A",
|
expectedSmsText: "A",
|
||||||
}],
|
}],
|
||||||
["receiveMessage",
|
["receiveMessage",
|
||||||
{
|
{
|
||||||
message: hexToArrayBuffer('0803120c2b313232323232323232323228e9898dd4d3283293012208cb83fd071221057f5f3adf3b11525967a6c7d9f4fb34df4c79f40b4827cced8d4cd9c0c85d7e601a210582d1fb7ab4f8f0ecfdea447de66a4e19cd2c3502f2b04b8829dd5bb36bd0d6242242220a21059f20776ec3d0975ebe54f8efaf847fdf227ffb2cc057de845c2e963984e5a878100118002210efa08202abe3ad52828a9a782f3aa5fc18ea1a709fcc6e6828f0463801'),
|
message: hexToArrayBuffer(''),
|
||||||
//expectedPlaintext: hexToArrayBuffer('220a21059f20776ec3d0975ebe54f8efaf847fdf227ffb2cc057de845c2e963984e5a878100118002210efa08202abe3ad52828a9a782f3aa5fc18ea1a709fcc6e68'),
|
//expectedPlaintext: hexToArrayBuffer(''),
|
||||||
expectedSmsText: "B",
|
expectedSmsText: "B",
|
||||||
}],
|
}],
|
||||||
["sendMessage",
|
["sendMessage",
|
||||||
|
@ -314,27 +314,123 @@ encryptedMessage: hexToArrayBuffer("415a326e6f457937756a6c5355785876342f6b585634
|
||||||
smsText: "C",
|
smsText: "C",
|
||||||
//expectedPlaintext: hexToArrayBuffer('0a0143'),
|
//expectedPlaintext: hexToArrayBuffer('0a0143'),
|
||||||
//expectedCounter: 0,
|
//expectedCounter: 0,
|
||||||
expectedCiphertext: hexToArrayBuffer('220a21051ac83687a0dc5acf4595e12411358fe7efeb068b7deb96f2ae6d25f2a4231f2e100018ffffffff0f220321deef7e214d158ebbd10c'),
|
expectedCiphertext: hexToArrayBuffer(''),
|
||||||
}],
|
}],
|
||||||
["sendMessage",
|
["sendMessage",
|
||||||
{
|
{
|
||||||
smsText: "D",
|
smsText: "D",
|
||||||
//expectedPlaintext: hexToArrayBuffer('0a0144'),
|
//expectedPlaintext: hexToArrayBuffer('0a0144'),
|
||||||
//expectedCounter: 1,
|
//expectedCounter: 1,
|
||||||
expectedCiphertext: hexToArrayBuffer('220a21051ac83687a0dc5acf4595e12411358fe7efeb068b7deb96f2ae6d25f2a4231f2e100118ffffffff0f220388e968d2d102a298b2c92a'),
|
expectedCiphertext: hexToArrayBuffer(''),
|
||||||
}],
|
}],
|
||||||
["receiveMessage",
|
["receiveMessage",
|
||||||
{
|
{
|
||||||
message: hexToArrayBuffer('0801120c2b313232323232323232323228e1ad8ed4d3283235220a2105f4465a6be68def783cb36b26fa4d2b6eb80b6fa7678a58181f0492121a760b48100018012203bf89479b777a890a700a363801'),
|
message: hexToArrayBuffer(''),
|
||||||
newEphemeralKey: hexToArrayBuffer('31a5281df2a88cf8ddea3f34eb4ac2c7c55eb91cdf052c5f87d788b4649bae47'),
|
newEphemeralKey: hexToArrayBuffer(''),
|
||||||
//expectedPlaintext: hexToArrayBuffer('220a2105f4465a6be68def783cb36b26fa4d2b6eb80b6fa7678a58181f0492121a760b48100018012203bf89479b777a890a700a36'),
|
//expectedPlaintext: hexToArrayBuffer(''),
|
||||||
expectedSmsText: "E",
|
expectedSmsText: "E",
|
||||||
}],
|
}],
|
||||||
];
|
];
|
||||||
|
|
||||||
TEST(function(callback) {
|
TEST(function(callback) {
|
||||||
axolotlTestVectorsAsBob(axolotlTestVectors, callback);
|
var v = axolotlTwoPartyTestVectorsAlice;
|
||||||
}, "Axolotl test vectors as bob", true);
|
var origCreateNewKeyPair = crypto_tests.createNewKeyPair;
|
||||||
|
var remoteDevice = { encodedNumber: "BOB" };
|
||||||
|
|
||||||
|
var doStep;
|
||||||
|
|
||||||
|
var stepDone = function(res) {
|
||||||
|
if (!res || privKeyQueue.length != 0) {
|
||||||
|
crypto_tests.createNewKeyPair = origCreateNewKeyPair;
|
||||||
|
callback(false);
|
||||||
|
} else if (step == v.length) {
|
||||||
|
crypto_tests.createNewKeyPair = origCreateNewKeyPair;
|
||||||
|
callback(true);
|
||||||
|
} else
|
||||||
|
doStep();
|
||||||
|
}
|
||||||
|
|
||||||
|
var privKeyQueue = [];
|
||||||
|
crypto_tests.createNewKeyPair = function(isIdentity, callback) {
|
||||||
|
if (privKeyQueue.length == 0 || isIdentity)
|
||||||
|
stepDone(false);
|
||||||
|
else {
|
||||||
|
var privKey = privKeyQueue.shift();
|
||||||
|
crypto_tests.privToPub(privKey, false, function(keyPair) {
|
||||||
|
var a = btoa(getString(keyPair.privKey)); var b = btoa(getString(privKey));
|
||||||
|
if (getString(keyPair.privKey) != getString(privKey))
|
||||||
|
stepDone(false);
|
||||||
|
else
|
||||||
|
callback(keyPair);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var step = 0;
|
||||||
|
var doStep = function() {
|
||||||
|
var data = v[step][1];
|
||||||
|
|
||||||
|
switch(v[step++][0]) {
|
||||||
|
case "receiveMessage":
|
||||||
|
if (data.newEphemeralKey !== undefined)
|
||||||
|
privKeyQueue.push(data.newEphemeralKey);
|
||||||
|
|
||||||
|
var message = new IncomingPushMessageProtobuf();
|
||||||
|
message.type = 1;
|
||||||
|
message.source = "BOB";
|
||||||
|
message.message = data.message;
|
||||||
|
crypto.handleIncomingPushMessageProto(decodeIncomingPushMessageProtobuf(getString(message.encode())), function(res) {
|
||||||
|
stepDone(res.message.body == data.expectedSmsText);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "sendMessage":
|
||||||
|
var postLocalKeySetup = function() {
|
||||||
|
if (data.theirIdentityKey !== undefined)
|
||||||
|
remoteDevice.identityKey = data.theirIdentityKey;
|
||||||
|
if (data.theirPreKey !== undefined) {
|
||||||
|
remoteDevice.publicKey = data.theirPreKey;
|
||||||
|
remoteDevice.preKeyId = data.theirPreKeyId;
|
||||||
|
remoteDevice.registrationId = data.theirRegistrationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
var message = new PushMessageContentProtobuf();
|
||||||
|
message.body = data.smsText;
|
||||||
|
|
||||||
|
crypto.encryptMessageFor(remoteDevice, message, function(res) {
|
||||||
|
if (res.type == 1) { //XXX: This should be used for everything...
|
||||||
|
stepDone(getString(data.expectedCiphertext) == getString(res.body));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Reencode expectedCiphertext...?
|
||||||
|
var decoded = decodePreKeyWhisperMessageProtobuf(getString(data.expectedCiphertext).substr(1));
|
||||||
|
var reencoded = String.fromCharCode((2<<4) | 2) + getString(decoded.encode());
|
||||||
|
|
||||||
|
stepDone(getString(data.expectedCiphertext)[0] == String.fromCharCode((2 << 4) | 2) &&
|
||||||
|
reencoded == res.body);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.ourBaseKey !== undefined)
|
||||||
|
privKeyQueue.push(data.ourBaseKey);
|
||||||
|
if (data.ourEphemeralKey !== undefined)
|
||||||
|
privKeyQueue.push(data.ourEphemeralKey);
|
||||||
|
|
||||||
|
if (data.ourIdentityKey !== undefined)
|
||||||
|
crypto_tests.privToPub(data.ourIdentityKey, true, function(keyPair) {
|
||||||
|
storage.putEncrypted("25519KeyidentityKey", keyPair);
|
||||||
|
postLocalKeySetup();
|
||||||
|
});
|
||||||
|
else
|
||||||
|
postLocalKeySetup();
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
stepDone(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
doStep();
|
||||||
|
}, "Standard Axolotl Test Vectors as Alice", true);
|
||||||
|
|
||||||
// Setup test timeouts (note that this will only work if things are actually
|
// Setup test timeouts (note that this will only work if things are actually
|
||||||
// being run async, ie in the case of NaCL)
|
// being run async, ie in the case of NaCL)
|
||||||
|
|
Loading…
Reference in a new issue