From 298c8624b2fc9d9a9eeab9592547386195368e17 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Thu, 15 Jan 2015 23:38:29 -1000 Subject: [PATCH] Move test vectors to libaxolotl --- libaxolotl/test/fake_api.js | 43 ++++ libaxolotl/test/index.html | 12 +- libaxolotl/test/protocol_test.js | 236 ++++++++++++++++++ libaxolotl/test/protos | 1 + libaxolotl/test/temp_helpers.js | 85 +++++++ .../test/testvectors.js | 53 ++-- libtextsecure/test/index.html | 1 - libtextsecure/test/protocol_test.js | 190 +------------- 8 files changed, 404 insertions(+), 217 deletions(-) create mode 100644 libaxolotl/test/fake_api.js create mode 100644 libaxolotl/test/protocol_test.js create mode 120000 libaxolotl/test/protos create mode 100644 libaxolotl/test/temp_helpers.js rename {libtextsecure => libaxolotl}/test/testvectors.js (79%) diff --git a/libaxolotl/test/fake_api.js b/libaxolotl/test/fake_api.js new file mode 100644 index 000000000000..722a33abc986 --- /dev/null +++ b/libaxolotl/test/fake_api.js @@ -0,0 +1,43 @@ +'use strict'; + +var testSessionMap = {}; + +;(function() { + window.axolotl = window.axolotl || {}; + window.axolotl.api = { + getMyRegistrationId: function() { + return window.myRegistrationId; + }, + storage: { + put: function(key, value) { + if (value === undefined) + throw new Error("Tried to store undefined"); + localStorage.setItem(key, textsecure.utils.jsonThing(value)); + }, + get: function(key, defaultValue) { + var value = localStorage.getItem(key); + if (value === null) + return defaultValue; + return JSON.parse(value); + }, + remove: function(key) { + localStorage.removeItem(key); + }, + + sessions: { + get: function(identifier) { + return testSessionMap[identifier]; + }, + put: function(identifier, record) { + testSessionMap[identifier] = record; + } + } + }, + updateKeys: function(keys) { + return textsecure.api.registerKeys(keys).catch(function(e) { + //TODO: Notify the user somehow? + console.error(e); + }); + }, + }; +})(); diff --git a/libaxolotl/test/index.html b/libaxolotl/test/index.html index ec5143053157..416814305142 100644 --- a/libaxolotl/test/index.html +++ b/libaxolotl/test/index.html @@ -29,12 +29,22 @@ - + + + + + + + + + + + diff --git a/libaxolotl/test/protocol_test.js b/libaxolotl/test/protocol_test.js new file mode 100644 index 000000000000..a554eaf5be6a --- /dev/null +++ b/libaxolotl/test/protocol_test.js @@ -0,0 +1,236 @@ +/* vim: ts=4:sw=4 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +'use strict'; +describe('Protocol', function() { + + describe("Identity and Pre Key Creation", function() { + this.timeout(200000); + before(function() { localStorage.clear(); }); + after(function() { localStorage.clear(); }); + it ('works', function(done) { + localStorage.clear(); + return axolotl.protocol.generateKeys().then(function() { + assert.isDefined(axolotl.api.storage.get("25519KeyidentityKey")); + assert.isDefined(axolotl.api.storage.get("25519KeysignedKey0")); + for (var i = 0; i < 100; i++) { + assert.isDefined(axolotl.api.storage.get("25519KeypreKey" + i)); + } + var origIdentityKey = getString(axolotl.api.storage.get("25519KeyidentityKey").privKey); + return axolotl.protocol.generateKeys().then(function() { + assert.isDefined(axolotl.api.storage.get("25519KeyidentityKey")); + assert.equal(getString(axolotl.api.storage.get("25519KeyidentityKey").privKey), origIdentityKey); + + assert.isDefined(axolotl.api.storage.get("25519KeysignedKey0")); + assert.isDefined(axolotl.api.storage.get("25519KeysignedKey1")); + + for (var i = 0; i < 200; i++) { + assert.isDefined(axolotl.api.storage.get("25519KeypreKey" + i)); + } + + return axolotl.protocol.generateKeys().then(function() { + assert.isDefined(axolotl.api.storage.get("25519KeyidentityKey")); + assert.equal(getString(axolotl.api.storage.get("25519KeyidentityKey").privKey), origIdentityKey); + + assert.isUndefined(axolotl.api.storage.get("25519KeysignedKey0")); + assert.isDefined(axolotl.api.storage.get("25519KeysignedKey1")); + assert.isDefined(axolotl.api.storage.get("25519KeysignedKey2")); + + for (var i = 0; i < 300; i++) { + assert.isDefined(axolotl.api.storage.get("25519KeypreKey" + i)); + } + }); + }); + }).then(done).catch(done); + }); + }); + + describe("Axolotl", function() { + var runAxolotlTest = function(v) { + var origCreateKeyPair = axolotl.crypto.createKeyPair; + var doStep; + var stepDone; + + stepDone = function(res) { + if (!res || privKeyQueue.length != 0) { + axolotl.crypto.createKeyPair = origCreateKeyPair; + return false; + } else if (step == v.length) { + axolotl.crypto.createKeyPair = origCreateKeyPair; + return true; + } else + return doStep().then(stepDone); + } + + var privKeyQueue = []; + axolotl.crypto.createKeyPair = function(privKey) { + if (privKey !== undefined) + return origCreateKeyPair(privKey); + if (privKeyQueue.length == 0) + throw new Error('Out of private keys'); + else { + var privKey = privKeyQueue.shift(); + return axolotl.crypto.createKeyPair(privKey).then(function(keyPair) { + var a = btoa(getString(keyPair.privKey)); var b = btoa(getString(privKey)); + if (getString(keyPair.privKey) != getString(privKey)) + throw new Error('Failed to rederive private key!'); + else + return keyPair; + }); + } + } + + var deviceObject = {encodedNumber: "SNOWDEN.1"}; + + var step = 0; + var doStep = function() { + var data = v[step][1]; + + switch(v[step++][0]) { + case "receiveMessage": + var postLocalKeySetup = function() { + if (data.newEphemeralKey !== undefined) + privKeyQueue.push(data.newEphemeralKey); + + try { + var checkResult = function(res) { + res = textsecure.protobuf.PushMessageContent.decode(res[0]); + //TODO: Handle END_SESSION here (just like libtextsecure.protocol_wrapper + if (data.expectTerminateSession) + return res.flags == textsecure.protobuf.PushMessageContent.Flags.END_SESSION; + return res.body == data.expectedSmsText; + } + var checkException = function(e) { + if (data.expectException) + return true; + throw e; + } + + if (data.type == textsecure.protobuf.IncomingPushMessageSignal.Type.CIPHERTEXT) + return axolotl.protocol.decryptWhisperMessage("SNOWDEN.1", getString(data.message)).then(checkResult).catch(checkException); + else if (data.type == textsecure.protobuf.IncomingPushMessageSignal.Type.PREKEY_BUNDLE) { + if (getString(data.message).charCodeAt(0) != ((3 << 4) | 3)) + throw new Error("Bad version byte"); + return axolotl.protocol.handlePreKeyWhisperMessage("SNOWDEN.1", getString(data.message).substring(1)).then(checkResult).catch(checkException); + } else + return Promise.reject(new Error("Unknown data type in test vector")); + } catch(e) { + if (data.expectException) + return Promise.resolve(true); + throw e; + } + } + + if (data.ourIdentityKey !== undefined) + return axolotl.crypto.createKeyPair(data.ourIdentityKey).then(function(keyPair) { + axolotl.api.storage.put("25519KeyidentityKey", keyPair); + return axolotl.crypto.createKeyPair(data.ourSignedPreKey).then(function(keyPair) { + axolotl.api.storage.put("25519KeysignedKey" + data.signedPreKeyId, keyPair); + + if (data.ourPreKey !== undefined) + return axolotl.crypto.createKeyPair(data.ourPreKey).then(function(keyPair) { + axolotl.api.storage.put("25519KeypreKey" + data.preKeyId, keyPair); + return postLocalKeySetup(); + }); + else + return postLocalKeySetup(); + }); + }); + else + return postLocalKeySetup(); + + case "sendMessage": + var postLocalKeySetup = function() { + if (data.registrationId !== undefined) + window.myRegistrationId = data.registrationId; + + if (data.getKeys !== undefined) { + deviceObject = {encodedNumber: "SNOWDEN.1", + identityKey: data.getKeys.identityKey, + preKey: data.getKeys.devices[0].preKey.publicKey, + preKeyId: data.getKeys.devices[0].preKey.keyId, + signedKey: data.getKeys.devices[0].signedPreKey.publicKey, + signedKeyId: data.getKeys.devices[0].signedPreKey.keyId, + signedKeySignature: data.getKeys.devices[0].signedPreKey.signature, + registrationId: data.getKeys.devices[0].signedPreKey.keyId + }; + } + + var checkMessage = function(msg) { + //XXX: This should be all we do: isEqual(data.expectedCiphertext, encryptedMsg, false); + var encryptedMsg = msg.body; + if (msg.type == 1) { + var expected = getString(data.expectedCiphertext); + var decoded = axolotl.protobuf.WhisperMessage.decode(expected.substring(1, expected.length - 8), 'binary'); + var result = getString(encryptedMsg); + return getString(decoded.encode()) == result.substring(1, result.length - 8); + } else { + var decoded = axolotl.protobuf.PreKeyWhisperMessage.decode(getString(data.expectedCiphertext).substr(1), 'binary'); + var result = getString(encryptedMsg).substring(1); + return getString(decoded.encode()) == result; + } + } + + var proto = new textsecure.protobuf.PushMessageContent(); + if (data.endSession) { + proto.flags = textsecure.protobuf.PushMessageContent.Flags.END_SESSION; + proto.body = "TERMINATE"; + } else + proto.body = data.smsText; + + return axolotl.protocol.encryptMessageFor(deviceObject, proto).then(checkMessage) + .then(function(res) { + if (data.endSession) + axolotl.protocol.closeOpenSessionForDevice("SNOWDEN.1"); + return res; + }); + } + + if (data.ourBaseKey !== undefined) + privKeyQueue.push(data.ourBaseKey); + if (data.ourEphemeralKey !== undefined) + privKeyQueue.push(data.ourEphemeralKey); + + if (data.ourIdentityKey !== undefined) + return axolotl.crypto.createKeyPair(data.ourIdentityKey).then(function(keyPair) { + axolotl.api.storage.put("25519KeyidentityKey", keyPair); + return postLocalKeySetup(); + }); + else + return postLocalKeySetup(); + + default: + return Promise.resolve(false); + } + } + return doStep().then(stepDone); + }; + + describe("test vectors", function() { + function defineTest(i) { + it(axolotlTestVectors[i].name, function(done) { + localStorage.clear(); + testSessionMap = {}; + return runAxolotlTest(axolotlTestVectors[i].vectors).then(function(res) { + assert(res); + }).then(done).catch(done); + }); + } + for (var i in axolotlTestVectors) + defineTest(i); + }); + }); +}); diff --git a/libaxolotl/test/protos b/libaxolotl/test/protos new file mode 120000 index 000000000000..3d021e59764c --- /dev/null +++ b/libaxolotl/test/protos @@ -0,0 +1 @@ +../../protos/ \ No newline at end of file diff --git a/libaxolotl/test/temp_helpers.js b/libaxolotl/test/temp_helpers.js new file mode 100644 index 000000000000..92e3669fd0e3 --- /dev/null +++ b/libaxolotl/test/temp_helpers.js @@ -0,0 +1,85 @@ +var StaticByteBufferProto = new dcodeIO.ByteBuffer().__proto__; +var StaticArrayBufferProto = new ArrayBuffer().__proto__; +var StaticUint8ArrayProto = new Uint8Array().__proto__; +function getString(thing) { + if (thing === Object(thing)) { + if (thing.__proto__ == StaticUint8ArrayProto) + return String.fromCharCode.apply(null, thing); + if (thing.__proto__ == StaticArrayBufferProto) + return getString(new Uint8Array(thing)); + if (thing.__proto__ == StaticByteBufferProto) + return thing.toString("binary"); + } + return thing; +} + +function getStringable(thing) { + return (typeof thing == "string" || typeof thing == "number" || typeof thing == "boolean" || + (thing === Object(thing) && + (thing.__proto__ == StaticArrayBufferProto || + thing.__proto__ == StaticUint8ArrayProto || + thing.__proto__ == StaticByteBufferProto))); +} + +function isEqual(a, b, mayBeShort) { + // TODO: Special-case arraybuffers, etc + if (a === undefined || b === undefined) + return false; + a = getString(a); + b = getString(b); + var maxLength = mayBeShort ? Math.min(a.length, b.length) : Math.max(a.length, b.length); + if (maxLength < 5) + throw new Error("a/b compare too short"); + return a.substring(0, Math.min(maxLength, a.length)) == b.substring(0, Math.min(maxLength, b.length)); +} + +function toArrayBuffer(thing) { + //TODO: Optimize this for specific cases + if (thing === undefined) + return undefined; + if (thing === Object(thing) && thing.__proto__ == StaticArrayBufferProto) + return thing; + + if (thing instanceof Array) { + // Assuming Uint16Array from curve25519 + var res = new ArrayBuffer(thing.length * 2); + var uint = new Uint16Array(res); + for (var i = 0; i < thing.length; i++) + uint[i] = thing[i]; + return res; + } + + if (!getStringable(thing)) + throw new Error("Tried to convert a non-stringable thing of type " + typeof thing + " to an array buffer"); + var str = getString(thing); + var res = new ArrayBuffer(str.length); + var uint = new Uint8Array(res); + for (var i = 0; i < str.length; i++) + uint[i] = str.charCodeAt(i); + return res; +} + +function ensureStringed(thing) { + if (getStringable(thing)) + return getString(thing); + else if (thing instanceof Array) { + var res = []; + for (var i = 0; i < thing.length; i++) + res[i] = ensureStringed(thing[i]); + return res; + } else if (thing === Object(thing)) { + var res = {}; + for (var key in thing) + res[key] = ensureStringed(thing[key]); + return res; + } + throw new Error("unsure of how to jsonify object of type " + typeof thing); +} + +window.textsecure = { + utils: { + jsonThing: function(thing) { + return JSON.stringify(ensureStringed(thing)); + } + } +}; diff --git a/libtextsecure/test/testvectors.js b/libaxolotl/test/testvectors.js similarity index 79% rename from libtextsecure/test/testvectors.js rename to libaxolotl/test/testvectors.js index aa7f93467df1..a14817900ec6 100644 --- a/libtextsecure/test/testvectors.js +++ b/libaxolotl/test/testvectors.js @@ -11,42 +11,42 @@ axolotlTestVectors = function() { ["sendMessage", { smsText: "A", - ourBaseKey: hexToArrayBuffer('08474c43256fb3bde899d899c3e997b24b94839c9f55a1f55ea671e7cdc55f5c'), - ourEphemeralKey: hexToArrayBuffer('e0a18f35a04b7cfd246f72f3c7fe0fc3b000f5d11fe548e818775b7e9d93a94c'), - ourIdentityKey: hexToArrayBuffer('a0f1dcf3d76ce4b538684b3360039bfde3ce59bca74449a01276e3ec395f4060'), - registrationId: 9784, - getKeys: {identityKey: hexToArrayBuffer('05c7214f5c39c83ddf81c4d56e88a33285c95db5eb87eac67536003530f5d62628'), + ourBaseKey: hexToArrayBuffer('2060fe31b041d28127ac35cbfe790e2a25f92d2e21eb2251690ae75e732f5c4d'), + ourEphemeralKey: hexToArrayBuffer('082e6391deb7154bd0375df3fc07f87020a3b0fd7a8c6c90e73f0e054bc2bf5d'), + ourIdentityKey: hexToArrayBuffer('d83d8141aad5f1d62d78a1af09ffbe61f2d3458eeb887a047a58a07565d24463'), + registrationId: 10290, + getKeys: {identityKey: hexToArrayBuffer('059c2197be51bae703ae2edd26b6ff2b03d589ef4851be33a3f8d923ad86a6b439'), devices: [{ deviceId: 1, - preKey: {keyId: 2803392, publicKey: hexToArrayBuffer('05a189dc5be02a35e32fafa5e8e4296ef74132e29d0033de26685f471301c80738')}, - signedPreKey: {keyId: 15452175, publicKey: hexToArrayBuffer('05e59ff855c698d58a3ff0dd04a925ebe1deffe276df623ddd873fb934b7314413')}, - registrationId: 42 + preKey: {keyId: 4611143, publicKey: hexToArrayBuffer('052cd5004a4c31dd7b89b7fc80cc3e62abcf9cf1af014c93ec4589f7ca3e79e65c')}, + signedPreKey: {keyId: 14983230, publicKey: hexToArrayBuffer('05a9ecf666ec55fc27988ecc417db0d62dd5e1fa751da1f7a2dd2eca0d14c8bd46'), signature: hexToArrayBuffer('0b46fdb238f1e2df7b28a94ba575e58b0aa1d377bb843602cc8c2a7cd33770fdd741f65a240f7c3086f00f31dc4f3b8ceeab498356f8d5e4bfe6f2dd3eeca98f')}, + registrationId: 0xd00d }] }, - expectedCiphertext: hexToArrayBuffer('3308c08dab01122105d51a5f0d744180bf49d1b71b5d77a66db9664a4806d8470b99bed6311b7ed1681a210596857fd3566a584bb7ff0664df4d11cfdba8e1a445e0930a527b7b139c6a897322d301330a210577874cb60271862f343d52984224e2558f828f336d68bd14fa4aaf5a1692be7d1000180022a001fa2af5c8688440c0bc45121ae2f6b9c6d5b8cf00acfee0d1a45f9e7b245636e1fa63b3062e2a6a9cd8be9096c375b413f71c2bb18054738c8440d1aa52997899d8bcedee6a93f6a295bdaf488e81188a9c88c4e841eb772b3194576c52bd8ef8fcbc5c29545fa38d2213c84d088f9843307f45c3254927f97c6f0afca9737a00d09146284f8948bbe69c755e47e6e571887bf57959b228ba9c2418f7bfc780b7bf1adbffd96276f328b84c308f90af07'), + expectedCiphertext: hexToArrayBuffer('3308c7b899021221058a49fa8a94224aaa8f5873404e01710ff9ef02169a75f90af4fbbc600796e0521a21050a6cf5e075c9970f14862db8a703a6c761f50b5182d17874908940556a22372222d301330a2105883ab58b3eb6db93b32bf91899a5b5175e7b21e96fff2cec02c83dff16ba1b271000180022a0013c5d070d1b75c418cef769bd7378a58969537a00e0ff60cbb99defb486fcfb43384264da4ea9821c1336f02d988da38944453331c4b30181704cbcec5a792ab87c5ccff256e0b4d61ba6a30a6964783875018882e66bfbd9445ac44fee9dc67edc2ad9de78adbe0eb7e9cb990272183ce5fac682ee5106f67d732cd16dfb731239590ba67dc827e849c49a9fb5ed8eed41d85d5e6de3294e74f3524c6489c2f25482ff52f9ea29c928b25030bec09207'), }], ["sendMessage", { smsText: "B", - expectedCiphertext: hexToArrayBuffer('3308c08dab01122105d51a5f0d744180bf49d1b71b5d77a66db9664a4806d8470b99bed6311b7ed1681a210596857fd3566a584bb7ff0664df4d11cfdba8e1a445e0930a527b7b139c6a897322d301330a210577874cb60271862f343d52984224e2558f828f336d68bd14fa4aaf5a1692be7d1001180022a00138b6a0e9b02bd7347e7428cc443df228b7a58a28456812d0f100985937ac08cf729f8be6a38596d3611c035352845f9732901a42e3dd4761be43d231edba5a56bcd60f3534e8872816a1c9eed4383ec0cb958ce2e66f00f8e5c90c13ebe44af5516df5cb41dfb74818795881633e19fefe562d3e89013aafba0aa672dab3e418b02f3a5c4c28d1be1a47bf3a79a95cccf071debe38a093dab22027296bfb0caa892317d15c715b8c28b84c308f90af07'), + expectedCiphertext: hexToArrayBuffer('3308c7b899021221058a49fa8a94224aaa8f5873404e01710ff9ef02169a75f90af4fbbc600796e0521a21050a6cf5e075c9970f14862db8a703a6c761f50b5182d17874908940556a22372222d301330a2105883ab58b3eb6db93b32bf91899a5b5175e7b21e96fff2cec02c83dff16ba1b271001180022a001256aae85babf8c0808f75e08bf10a63f7f3aea97324c2583d777f609df493d7d45232c8883c3e1118fbc29b6318a3091ae57fed4f1c54458c6bb832fbb35f24933cb79765d00f4a161e2877a5a21a26592cdb0aa8a2f70f5fbe8c601ecdff0bef1b733d7fd0cb7b7d8fc1e45f79c016c8f90449239ca1a04b374538f2760eef43127ddc9a6439c6ceca5faf5962fb26d7248257d4d5ee3fe4cf8795acc555718558e5317f618828328b25030bec09207'), }], ["receiveMessage", { - message: hexToArrayBuffer('330a210519549798b47a051f48291f03140179329c03de0243459c5283d7b31d65b3ec661000180022a001dce182f136cc10858ecb48cd3299882de15006b179626f13d85f9353f64782df6afafefee527bab4ad7aedeb5863fdd0641d3db8bed305b4b64df6d1d39fa262058066430ea1fdd019b76cfcef4ae412214dd37f5a3e4270d48e278455fe94e2bd99940ec562703e45bbeab9a0a147e6e66af6b8188cb8bf7eaf18d601eb7637055e5528afce2e4a48ac49915a15aa23750f24644777a4c609f0f2fb4cbe93934dc8144f2ea3c888'), + message: hexToArrayBuffer('330a2105bc81f1348a1d065b2bd2776edb9f29bc4150399db35c1d87dc258b94894bc57a1000180022a001c93af1107634d9eaa1516a4f8e95c6a454c27313b38830709eb863608f08f2f3a598ff8f558645427f7b6ea8e182e40f7b4a92ce0325f2e22f76f36f6954f6f391dd21d2cad12e5b620e75b991e69df8821ab0e826e3cb2ae1c7a1fb8ed72213e36fc508ca1f0a92ebe2089535b5d5e1b34eae5f91497bd072de47de3291ba78a6fd67d3f8f3f20d04ab3a1159df8f36ef7e4696847e32ce6be07edb93763a2226c87feff8cc4827'), type: 1, - newEphemeralKey: hexToArrayBuffer('d872c0de95e128656ddb3a2ac422ab9c5273415f6e55996a5dd395f915472052'), + newEphemeralKey: hexToArrayBuffer('d04f334799ea1272eff64c5267e28274f54b91b3b11372879303eb7a8cd52763'), expectedSmsText: "C", }], ["receiveMessage", { - message: hexToArrayBuffer('330a210519549798b47a051f48291f03140179329c03de0243459c5283d7b31d65b3ec661001180022a0014428b3743a7be1682237400d3997a6ccccc7aecbed3192d819e05b5dad8bd91d3ca1fcb39a6187a8c9fe1e0216f37aecb0178cf0f2998a1177e611f0f0ab18a1d528541eda02137a45b388377a13387e65c4bbdd468fa2da77e4de567828914225de8d27ed62e05d2da32b9ae19ac37cf903990973eef8616d58ab2b67848526244980f3827aee4e56d715500adac8f6d93fd8dd24d00a3a3e001a1f4b86049195f2d93815efd3f3'), + message: hexToArrayBuffer('330a2105bc81f1348a1d065b2bd2776edb9f29bc4150399db35c1d87dc258b94894bc57a1001180022a001eb52c72c7bb6b8878c96398cc05810382d29fc17644f88bdc8d57509e8a734626620ae243cb740466806ee3c64bbf12957d5ac0452a17aba6c0e10e2a82626a986df0c4e5cadebb9ce824f1af4fac85cf7d1b9b7cf37f5df06d77b901d0e2aaa772b49f838ec92a67d13b4d7908cf91f7e0a54ad031b2aa4a954180b652f0696350e4f286592e24cc83091b196f2d48397241e33acaf6f65be27af12f1a8af91fd1daf2c01bdfaaa'), type: 1, expectedSmsText: "D", }], ["sendMessage", { smsText: "E", - expectedCiphertext: hexToArrayBuffer('330a21052f7f7ab6e0d172b8fcab6f7e4b7adef1dc25205d3642e665f11f5681798d99451000180122a001e492d8b25eaec22f39ff1c8cac2e8ac164d0ebb5894c20330e20ebce6dc58ea0469b3e0075657eebc6e04c1da3aed6d8ab7631412a017cc20bf18b433d2ead4fd40320e9093b8be5565d581fd679a3c296edeb463638353422ead082239f7047537c79a749d4949e3a21d98ec873506d27c5f1abea28d2faddd1f5d4e26c1633eaa213af5372daf8d76308097d54a97cee1164f2302784f21f6808d031a5650f8598069de06473b1'), + expectedCiphertext: hexToArrayBuffer('330a2105576f3c29717db75ffd19a37154d4d6beba8d796a26c4244793132f7e6cb180491000180122a001bd139a95021d34d9df74d99aa897981aa6718fd6b72d8567891afff92c6e3534ded0de80be7e7c58730a001f2acc1f1e6447f9ca0a99681f3f65d9a4072f3a1fb978740918d3db5c346170edb3bf8fec2b52362edf7138f93cb23a3f17b0f40bf9769e01273955b14c20b6212cbb1f665d1a7e5e770437a53b1727c13bcd639bf5beba71893b8de435244acddc42c3ba592b7debdacdc4dea12dc7e4e670753419be0455e0043f91'), }], ]; // Now change the order and make 2 tests out of them: @@ -73,37 +73,37 @@ axolotlTestVectors = function() { var axolotlTwoPartyTestVectorsBob = [ ["receiveMessage", { - message: hexToArrayBuffer('3308c08dab01122105d51a5f0d744180bf49d1b71b5d77a66db9664a4806d8470b99bed6311b7ed1681a210596857fd3566a584bb7ff0664df4d11cfdba8e1a445e0930a527b7b139c6a897322d301330a210577874cb60271862f343d52984224e2558f828f336d68bd14fa4aaf5a1692be7d1000180022a001fa2af5c8688440c0bc45121ae2f6b9c6d5b8cf00acfee0d1a45f9e7b245636e1fa63b3062e2a6a9cd8be9096c375b413f71c2bb18054738c8440d1aa52997899d8bcedee6a93f6a295bdaf488e81188a9c88c4e841eb772b3194576c52bd8ef8fcbc5c29545fa38d2213c84d088f9843307f45c3254927f97c6f0afca9737a00d09146284f8948bbe69c755e47e6e571887bf57959b228ba9c2418f7bfc780b7bf1adbffd96276f328b84c308f90af07'), + message: hexToArrayBuffer('3308c7b899021221058a49fa8a94224aaa8f5873404e01710ff9ef02169a75f90af4fbbc600796e0521a21050a6cf5e075c9970f14862db8a703a6c761f50b5182d17874908940556a22372222d301330a2105883ab58b3eb6db93b32bf91899a5b5175e7b21e96fff2cec02c83dff16ba1b271000180022a0013c5d070d1b75c418cef769bd7378a58969537a00e0ff60cbb99defb486fcfb43384264da4ea9821c1336f02d988da38944453331c4b30181704cbcec5a792ab87c5ccff256e0b4d61ba6a30a6964783875018882e66bfbd9445ac44fee9dc67edc2ad9de78adbe0eb7e9cb990272183ce5fac682ee5106f67d732cd16dfb731239590ba67dc827e849c49a9fb5ed8eed41d85d5e6de3294e74f3524c6489c2f25482ff52f9ea29c928b25030bec09207'), type: 3, - ourPreKey: hexToArrayBuffer('40a5ce4c6e21c2e686bed765c854b010117ea26fb08438994d84ffa516c7d84e'), - preKeyId: 2803392, - ourSignedPreKey: hexToArrayBuffer('280d9e0067b68c877c48120d400f5a16c0b3e74374149751777d1f3c49b2b355'), - signedPreKeyId: 15452175, - ourIdentityKey: hexToArrayBuffer('b8ee332fd87c5544ccd52afa8ce55140b25fd4ca4441a232799fe474ff4cae79'), - newEphemeralKey: hexToArrayBuffer('400146d280077821aacaed96ad75b99ab665d2530ef5c061fad0e24bac285640'), + ourPreKey: hexToArrayBuffer('88d9a12e7b03afdac42e49ec9d4e5488e1b1e6d48c6eef6029e45dec09a9d562'), + preKeyId: 4611143, + ourSignedPreKey: hexToArrayBuffer('888b3f14aff80e36bb2d2cc26a72da2e1a99330962f5066c7c1dded1262ca665'), + signedPreKeyId: 14983230, + ourIdentityKey: hexToArrayBuffer('58c9fb2ec2c6b13e279e7db57ce837c02aac1531504f71130d167cc8fb25a857'), + newEphemeralKey: hexToArrayBuffer('f0b66ac79b6f4ae997636bc8ed622a184dbe00603b2c657ac18800122523d142'), expectedSmsText: "A", }], ["receiveMessage", { - message: hexToArrayBuffer('3308c08dab01122105d51a5f0d744180bf49d1b71b5d77a66db9664a4806d8470b99bed6311b7ed1681a210596857fd3566a584bb7ff0664df4d11cfdba8e1a445e0930a527b7b139c6a897322d301330a210577874cb60271862f343d52984224e2558f828f336d68bd14fa4aaf5a1692be7d1001180022a00138b6a0e9b02bd7347e7428cc443df228b7a58a28456812d0f100985937ac08cf729f8be6a38596d3611c035352845f9732901a42e3dd4761be43d231edba5a56bcd60f3534e8872816a1c9eed4383ec0cb958ce2e66f00f8e5c90c13ebe44af5516df5cb41dfb74818795881633e19fefe562d3e89013aafba0aa672dab3e418b02f3a5c4c28d1be1a47bf3a79a95cccf071debe38a093dab22027296bfb0caa892317d15c715b8c28b84c308f90af07'), + message: hexToArrayBuffer('3308c7b899021221058a49fa8a94224aaa8f5873404e01710ff9ef02169a75f90af4fbbc600796e0521a21050a6cf5e075c9970f14862db8a703a6c761f50b5182d17874908940556a22372222d301330a2105883ab58b3eb6db93b32bf91899a5b5175e7b21e96fff2cec02c83dff16ba1b271001180022a001256aae85babf8c0808f75e08bf10a63f7f3aea97324c2583d777f609df493d7d45232c8883c3e1118fbc29b6318a3091ae57fed4f1c54458c6bb832fbb35f24933cb79765d00f4a161e2877a5a21a26592cdb0aa8a2f70f5fbe8c601ecdff0bef1b733d7fd0cb7b7d8fc1e45f79c016c8f90449239ca1a04b374538f2760eef43127ddc9a6439c6ceca5faf5962fb26d7248257d4d5ee3fe4cf8795acc555718558e5317f618828328b25030bec09207'), type: 3, expectedSmsText: "B", }], ["sendMessage", { smsText: "C", - expectedCiphertext: hexToArrayBuffer('330a210519549798b47a051f48291f03140179329c03de0243459c5283d7b31d65b3ec661000180022a001dce182f136cc10858ecb48cd3299882de15006b179626f13d85f9353f64782df6afafefee527bab4ad7aedeb5863fdd0641d3db8bed305b4b64df6d1d39fa262058066430ea1fdd019b76cfcef4ae412214dd37f5a3e4270d48e278455fe94e2bd99940ec562703e45bbeab9a0a147e6e66af6b8188cb8bf7eaf18d601eb7637055e5528afce2e4a48ac49915a15aa23750f24644777a4c609f0f2fb4cbe93934dc8144f2ea3c888'), + expectedCiphertext: hexToArrayBuffer('330a2105bc81f1348a1d065b2bd2776edb9f29bc4150399db35c1d87dc258b94894bc57a1000180022a001c93af1107634d9eaa1516a4f8e95c6a454c27313b38830709eb863608f08f2f3a598ff8f558645427f7b6ea8e182e40f7b4a92ce0325f2e22f76f36f6954f6f391dd21d2cad12e5b620e75b991e69df8821ab0e826e3cb2ae1c7a1fb8ed72213e36fc508ca1f0a92ebe2089535b5d5e1b34eae5f91497bd072de47de3291ba78a6fd67d3f8f3f20d04ab3a1159df8f36ef7e4696847e32ce6be07edb93763a2226c87feff8cc4827'), }], ["sendMessage", { smsText: "D", - expectedCiphertext: hexToArrayBuffer('330a210519549798b47a051f48291f03140179329c03de0243459c5283d7b31d65b3ec661001180022a0014428b3743a7be1682237400d3997a6ccccc7aecbed3192d819e05b5dad8bd91d3ca1fcb39a6187a8c9fe1e0216f37aecb0178cf0f2998a1177e611f0f0ab18a1d528541eda02137a45b388377a13387e65c4bbdd468fa2da77e4de567828914225de8d27ed62e05d2da32b9ae19ac37cf903990973eef8616d58ab2b67848526244980f3827aee4e56d715500adac8f6d93fd8dd24d00a3a3e001a1f4b86049195f2d93815efd3f3'), + expectedCiphertext: hexToArrayBuffer('330a2105bc81f1348a1d065b2bd2776edb9f29bc4150399db35c1d87dc258b94894bc57a1001180022a001eb52c72c7bb6b8878c96398cc05810382d29fc17644f88bdc8d57509e8a734626620ae243cb740466806ee3c64bbf12957d5ac0452a17aba6c0e10e2a82626a986df0c4e5cadebb9ce824f1af4fac85cf7d1b9b7cf37f5df06d77b901d0e2aaa772b49f838ec92a67d13b4d7908cf91f7e0a54ad031b2aa4a954180b652f0696350e4f286592e24cc83091b196f2d48397241e33acaf6f65be27af12f1a8af91fd1daf2c01bdfaaa'), }], ["receiveMessage", { - message: hexToArrayBuffer('330a21052f7f7ab6e0d172b8fcab6f7e4b7adef1dc25205d3642e665f11f5681798d99451000180122a001e492d8b25eaec22f39ff1c8cac2e8ac164d0ebb5894c20330e20ebce6dc58ea0469b3e0075657eebc6e04c1da3aed6d8ab7631412a017cc20bf18b433d2ead4fd40320e9093b8be5565d581fd679a3c296edeb463638353422ead082239f7047537c79a749d4949e3a21d98ec873506d27c5f1abea28d2faddd1f5d4e26c1633eaa213af5372daf8d76308097d54a97cee1164f2302784f21f6808d031a5650f8598069de06473b1'), + message: hexToArrayBuffer('330a2105576f3c29717db75ffd19a37154d4d6beba8d796a26c4244793132f7e6cb180491000180122a001bd139a95021d34d9df74d99aa897981aa6718fd6b72d8567891afff92c6e3534ded0de80be7e7c58730a001f2acc1f1e6447f9ca0a99681f3f65d9a4072f3a1fb978740918d3db5c346170edb3bf8fec2b52362edf7138f93cb23a3f17b0f40bf9769e01273955b14c20b6212cbb1f665d1a7e5e770437a53b1727c13bcd639bf5beba71893b8de435244acddc42c3ba592b7debdacdc4dea12dc7e4e670753419be0455e0043f91'), type: 1, - newEphemeralKey: hexToArrayBuffer('b89a87781558e4e9d19ebefa103c1ccdb20b573678f0753500376decfa975e71'), + newEphemeralKey: hexToArrayBuffer('98bee5f861b528816888d45c2ca40125b111d2c03e483e57e6886c82dd758467'), expectedSmsText: "E", }], ]; @@ -466,6 +466,7 @@ axolotlTestVectors = function() { */ //TODO: GROUPS + //TODO: Sender changes identity key? return tests; }(); diff --git a/libtextsecure/test/index.html b/libtextsecure/test/index.html index 9d35de18014e..0fa4ddd3785a 100644 --- a/libtextsecure/test/index.html +++ b/libtextsecure/test/index.html @@ -46,7 +46,6 @@ - diff --git a/libtextsecure/test/protocol_test.js b/libtextsecure/test/protocol_test.js index 36ae16f51edb..94b8b07444d6 100644 --- a/libtextsecure/test/protocol_test.js +++ b/libtextsecure/test/protocol_test.js @@ -40,193 +40,5 @@ describe('Protocol', function() { }); }); - - describe("Identity and Pre Key Creation", function() { - this.timeout(200000); - before(function() { localStorage.clear(); }); - after(function() { localStorage.clear(); }); - it ('works', function(done) { - localStorage.clear(); - return axolotl.protocol.generateKeys().then(function() { - assert.isDefined(textsecure.storage.getEncrypted("25519KeyidentityKey")); - assert.isDefined(textsecure.storage.getEncrypted("25519KeysignedKey0")); - for (var i = 0; i < 100; i++) { - assert.isDefined(textsecure.storage.getEncrypted("25519KeypreKey" + i)); - } - var origIdentityKey = getString(textsecure.storage.getEncrypted("25519KeyidentityKey").privKey); - return axolotl.protocol.generateKeys().then(function() { - assert.isDefined(textsecure.storage.getEncrypted("25519KeyidentityKey")); - assert.equal(getString(textsecure.storage.getEncrypted("25519KeyidentityKey").privKey), origIdentityKey); - - assert.isDefined(textsecure.storage.getEncrypted("25519KeysignedKey0")); - assert.isDefined(textsecure.storage.getEncrypted("25519KeysignedKey1")); - - for (var i = 0; i < 200; i++) { - assert.isDefined(textsecure.storage.getEncrypted("25519KeypreKey" + i)); - } - - return axolotl.protocol.generateKeys().then(function() { - assert.isDefined(textsecure.storage.getEncrypted("25519KeyidentityKey")); - assert.equal(getString(textsecure.storage.getEncrypted("25519KeyidentityKey").privKey), origIdentityKey); - - assert.isUndefined(textsecure.storage.getEncrypted("25519KeysignedKey0")); - assert.isDefined(textsecure.storage.getEncrypted("25519KeysignedKey1")); - assert.isDefined(textsecure.storage.getEncrypted("25519KeysignedKey2")); - - for (var i = 0; i < 300; i++) { - assert.isDefined(textsecure.storage.getEncrypted("25519KeypreKey" + i)); - } - }); - }); - }).then(done).catch(done); - }); - }); - - describe("Axolotl", function() { - var runAxolotlTest = function(v) { - var origCreateKeyPair = axolotl.crypto.createKeyPair; - var doStep; - var stepDone; - - stepDone = function(res) { - if (!res || privKeyQueue.length != 0 || Object.keys(getKeysForNumberMap).length != 0 || Object.keys(messagesSentMap).length != 0) { - axolotl.crypto.createKeyPair = origCreateKeyPair; - return false; - } else if (step == v.length) { - axolotl.crypto.createKeyPair = origCreateKeyPair; - return true; - } else - return doStep().then(stepDone); - } - - var privKeyQueue = []; - axolotl.crypto.createKeyPair = function(privKey) { - if (privKey !== undefined) - return origCreateKeyPair(privKey); - if (privKeyQueue.length == 0) - throw new Error('Out of private keys'); - else { - var privKey = privKeyQueue.shift(); - return axolotl.crypto.createKeyPair(privKey).then(function(keyPair) { - var a = btoa(getString(keyPair.privKey)); var b = btoa(getString(privKey)); - if (getString(keyPair.privKey) != getString(privKey)) - throw new Error('Failed to rederive private key!'); - else - return keyPair; - }); - } - } - - var step = 0; - var doStep = function() { - var data = v[step][1]; - - switch(v[step++][0]) { - case "receiveMessage": - var postLocalKeySetup = function() { - if (data.newEphemeralKey !== undefined) - privKeyQueue.push(data.newEphemeralKey); - - var message = new textsecure.protobuf.IncomingPushMessageSignal(); - message.type = data.type; - message.source = "SNOWDEN"; - message.message = data.message; - message.sourceDevice = 1; - try { - var proto = textsecure.protobuf.IncomingPushMessageSignal.decode(message.encode()); - return textsecure.protocol_wrapper.handleIncomingPushMessageProto(proto).then(function(res) { - if (data.expectTerminateSession) - return res.flags == textsecure.protobuf.PushMessageContent.Flags.END_SESSION; - return res.body == data.expectedSmsText; - }).catch(function(e) { - if (data.expectException) - return true; - throw e; - }); - } catch(e) { - if (data.expectException) - return Promise.resolve(true); - throw e; - } - } - - if (data.ourIdentityKey !== undefined) - return axolotl.crypto.createKeyPair(data.ourIdentityKey).then(function(keyPair) { - textsecure.storage.putEncrypted("25519KeyidentityKey", keyPair); - return axolotl.crypto.createKeyPair(data.ourSignedPreKey).then(function(keyPair) { - textsecure.storage.putEncrypted("25519KeysignedKey" + data.signedPreKeyId, keyPair); - - if (data.ourPreKey !== undefined) - return axolotl.crypto.createKeyPair(data.ourPreKey).then(function(keyPair) { - textsecure.storage.putEncrypted("25519KeypreKey" + data.preKeyId, keyPair); - return postLocalKeySetup(); - }); - else - return postLocalKeySetup(); - }); - }); - else - return postLocalKeySetup(); - - case "sendMessage": - var postLocalKeySetup = function() { - if (data.registrationId !== undefined) - textsecure.storage.putUnencrypted("registrationId", data.registrationId); - - if (data.getKeys !== undefined) - getKeysForNumberMap["SNOWDEN"] = data.getKeys; - - 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); - if (msg.type == 1) { - var expected = getString(data.expectedCiphertext); - var decoded = axolotl.protobuf.WhisperMessage.decode(expected.substring(1, expected.length - 8), 'binary'); - var result = getString(msg.body); - return getString(decoded.encode()) == result.substring(1, result.length - 8); - } else { - var decoded = axolotl.protobuf.PreKeyWhisperMessage.decode(getString(data.expectedCiphertext).substr(1), 'binary'); - 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, [], Date.now()).then(checkMessage); - } - - if (data.ourBaseKey !== undefined) - privKeyQueue.push(data.ourBaseKey); - if (data.ourEphemeralKey !== undefined) - privKeyQueue.push(data.ourEphemeralKey); - - if (data.ourIdentityKey !== undefined) - return axolotl.crypto.createKeyPair(data.ourIdentityKey).then(function(keyPair) { - textsecure.storage.putEncrypted("25519KeyidentityKey", keyPair); - return postLocalKeySetup(); - }); - else - return postLocalKeySetup(); - - default: - return Promise.resolve(false); - } - } - return doStep().then(stepDone); - }; - - describe("test vectors", function() { - for (var i in axolotlTestVectors) { - it(axolotlTestVectors[i].name, function(done) { - localStorage.clear(); - return runAxolotlTest(axolotlTestVectors[i].vectors).then(function(res) { - assert(res); - }).then(done).catch(done); - }); - } - }); - }); + // TODO: Use fake_api's hiding of api.sendMessage to test sendmessage.js' maze });