Such tests. Very mocha. Much chai. Amaze!!!

ERHMAGERRRD testing frameworks are so the best. Removed all our custom
code for ensuring test exclusivity and doneness and isolating callbacks
and everything. mocha does it all for us, and makes it pretty.

Also rather than return a long chain of promises that eventually resolve
to truthiness, we now use chai to make assertions about what is good and
right in the world.

Recommended reading:
  https://visionmedia.github.io/mocha
  http://chaijs.com/api/assert/
This commit is contained in:
lilia 2014-07-25 17:59:46 -10:00
parent 6455da5c9a
commit 2751d0e884
5 changed files with 11295 additions and 449 deletions

View file

@ -14,451 +14,384 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
// Setup dumb test wrapper describe("ArrayBuffer->String conversion", function() {
var testsdiv = $('#tests'); it('works', function() {
var testsOutstanding = []; var b = new ArrayBuffer(3);
var a = new Uint8Array(b);
var exclusiveRunning = -1; a[0] = 0;
var exclusiveTestsWaiting = []; a[1] = 255;
a[2] = 128;
var maxTestId = 0; assert.equal(getString(b), "\x00\xff\x80");
var forceNextTestInverval; });
});
var allTestsDefined = false;
function printTestsDone() { describe("Cryptographic primitives", function() {
if (!allTestsDefined) describe("Encrypt AES-CTR", function() {
return; it('works', function(done) {
for (var i = 0; i < maxTestId; i++) var key = hexToArrayBuffer('2b7e151628aed2a6abf7158809cf4f3c');
if (testsOutstanding[i] !== undefined) var counter = hexToArrayBuffer('f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff');
return; var plaintext = hexToArrayBuffer('6bc1bee22e409f96e93d7e117393172a');
testsdiv.append('<p>All tests done</p>'); var ciphertext = hexToArrayBuffer('874d6191b620e3261bef6864990db6ce');
window.clearInterval(forceNextTestInverval); window.crypto.subtle.encrypt({name: "AES-CTR", counter: counter}, key, plaintext).then(function(result) {
} assert.strictEqual(getString(result), getString(ciphertext));
done();
function startNextExclusiveTest() { });
for (var i = 0; i < maxTestId; i++) { });
if (exclusiveTestsWaiting[i] !== undefined) { });
exclusiveTestsWaiting[i]();
return; describe("Decrypt AES-CTR", function() {
} it('works', function(done) {
} var key = hexToArrayBuffer('2b7e151628aed2a6abf7158809cf4f3c');
} var counter = hexToArrayBuffer('f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff');
var plaintext = hexToArrayBuffer('6bc1bee22e409f96e93d7e117393172a');
function TEST(func, name, exclusive) { var ciphertext = hexToArrayBuffer('874d6191b620e3261bef6864990db6ce');
if (exclusive == undefined) window.crypto.subtle.decrypt({name: "AES-CTR", counter: counter}, key, ciphertext).then(function(result) {
exculsive = false; assert.strictEqual(getString(result), getString(plaintext));
done();
var funcName = name === undefined ? func + "" : name; });
var testIndex = maxTestId; });
});
var exclusiveIndex = -1;
if (exclusive && exclusiveRunning != -1) describe("Decrypt AES-CBC", function() {
exclusiveIndex = maxTestId; it('works', function(done) {
var key = hexToArrayBuffer('603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4');
maxTestId = maxTestId + 1; var iv = hexToArrayBuffer('000102030405060708090a0b0c0d0e0f');
var plaintext = hexToArrayBuffer('6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710');
function resolve(result, error) { var ciphertext = hexToArrayBuffer('f58c4c04d6e5f1ba779eabfb5f7bfbd69cfc4e967edb808d679f777bc6702c7d39f23369a9d9bacfa530e26304231461b2eb05e2c39be9fcda6c19078c6a9d1b3f461796d6b0d6b2e0c2a72b4d80e644');
if (testsOutstanding[testIndex] == undefined) window.crypto.subtle.decrypt({name: "AES-CBC", iv: iv}, key, ciphertext).then(function(result) {
testsdiv.append('<p style="color: red;">' + funcName + ' called back multiple times</p>'); assert.strictEqual(getString(result), getString(plaintext));
else if (error !== undefined) { done();
if (error && error.stack) { });
console.log(error.stack); });
testsdiv.append('<p style="color: red;">' + funcName + ' threw ' + error + '</p>'); });
} else
testsdiv.append('<p style="color: red;">' + funcName + ' threw non-Error: ' + error + '</p>'); describe("HMAC SHA-256", function() {
} else if (result === true) it("works", function(done) {
testsdiv.append('<p style="color: green;">' + funcName + ' passed</p>'); var key = hexToArrayBuffer('6f35628d65813435534b5d67fbdb54cb33403d04e843103e6399f806cb5df95febbdd61236f33245');
else var input = hexToArrayBuffer('752cff52e4b90768558e5369e75d97c69643509a5e5904e0a386cbe4d0970ef73f918f675945a9aefe26daea27587e8dc909dd56fd0468805f834039b345f855cfe19c44b55af241fff3ffcd8045cd5c288e6c4e284c3720570b58e4d47b8feeedc52fd1401f698a209fccfa3b4c0d9a797b046a2759f82a54c41ccd7b5f592b');
testsdiv.append('<p style="color: red;">' + funcName + ' returned ' + result + '</p>'); var mac = getString(hexToArrayBuffer('05d1243e6465ed9620c9aec1c351a186'));
delete testsOutstanding[testIndex]; window.crypto.subtle.sign({name: "HMAC", hash: "SHA-256"}, key, input).then(function(result) {
assert.strictEqual(getString(result).substring(0, mac.length), mac);
if (exclusive) { done();
exclusiveRunning = -1; });
localStorage.clear(); });
if (exclusiveIndex != -1) });
delete exclusiveTestsWaiting[exclusiveIndex];
startNextExclusiveTest();
} describe("HMAC RFC5869 Test vectors", function() {
printTestsDone(); it('works', function(done) {
} var IKM = new Uint8Array(new ArrayBuffer(22));
for (var i = 0; i < 22; i++)
IKM[i] = 11;
var runFunc = function() {
if (exclusive) { var salt = new Uint8Array(new ArrayBuffer(13));
exclusiveRunning = testIndex; for (var i = 0; i < 13; i++)
localStorage.clear(); salt[i] = i;
}
var info = new Uint8Array(new ArrayBuffer(10));
try { for (var i = 0; i < 10; i++)
testsOutstanding[testIndex] = funcName; info[i] = 240 + i;
func().then(resolve).catch(function(e) { resolve(null, e); });
} catch (e) { textsecure.crypto.testing_only.HKDF(IKM.buffer, salt.buffer, info.buffer).then(function(OKM){
resolve(null, e); var T1 = hexToArrayBuffer("3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf");
} var T2 = hexToArrayBuffer("34007208d5b887185865");
} assert.equal(getString(OKM[0]), getString(T1));
assert.equal(getString(OKM[1]).substring(0, 10), getString(T2));
if (!exclusive || exclusiveRunning == -1) done();
runFunc(); });
else });
exclusiveTestsWaiting[exclusiveIndex] = runFunc; });
} });
textsecure.registerOnLoadFunction(function() { describe('Unencrypted PushMessageProto "decrypt"', function() {
TEST(function() { //exclusive
var b = new ArrayBuffer(3); it('works', function(done) {
var a = new Uint8Array(b); var PushMessageProto = dcodeIO.ProtoBuf.loadProtoFile(
a[0] = 0; "protos/IncomingPushMessageSignal.proto"
a[1] = 255; ).build("textsecure.PushMessageContent");
a[2] = 128; var IncomingMessageProto = dcodeIO.ProtoBuf.loadProtoFile(
return Promise.resolve(getString(b) == "\x00\xff\x80"); "protos/IncomingPushMessageSignal.proto"
}, "ArrayBuffer->String conversion"); ).build("textsecure.IncomingPushMessageSignal");
// Basic sanity-checks on the crypto library var text_message = new PushMessageProto();
TEST(function() { text_message.body = "Hi Mom";
var PushMessageProto = dcodeIO.ProtoBuf.loadProtoFile("protos/IncomingPushMessageSignal.proto").build("textsecure.PushMessageContent"); var server_message = {
var IncomingMessageProto = dcodeIO.ProtoBuf.loadProtoFile("protos/IncomingPushMessageSignal.proto").build("textsecure.IncomingPushMessageSignal"); type: 4, // unencrypted
source: "+19999999999",
var text_message = new PushMessageProto(); timestamp: 42,
text_message.body = "Hi Mom"; message: text_message.encode()
var server_message = {type: 4, // unencrypted };
source: "+19999999999", timestamp: 42, message: text_message.encode() };
textsecure.crypto.handleIncomingPushMessageProto(server_message).
return textsecure.crypto.handleIncomingPushMessageProto(server_message).then(function(message) { then(function(message) {
return (message.body == text_message.body && assert.equal(message.body, text_message.body);
message.attachments.length == text_message.attachments.length && assert.equal(message.attachments.length, text_message.attachments.length);
text_message.attachments.length == 0); assert.equal(text_message.attachments.length, 0);
}); done();
}, 'Unencrypted PushMessageProto "decrypt"', true); });
});
TEST(function() { });
return textsecure.crypto.generateKeys().then(function() {
if (textsecure.storage.getEncrypted("25519KeyidentityKey") === undefined) describe("Curve25519", function() {
return false; describe("Implementation", function() {
if (textsecure.storage.getEncrypted("25519KeysignedKey0") === undefined) // this is a just cute little trick to get a nice-looking note about
return false; // which curve25519 impl we're using.
if (window.textsecure.nacl.USE_NACL) {
for (var i = 0; i < 100; i++) it("is NACL");
if (textsecure.storage.getEncrypted("25519KeypreKey" + i) === undefined) } else {
return false; it("is JavaScript");
}
var origIdentityKey = getString(textsecure.storage.getEncrypted("25519KeyidentityKey").privKey); });
return textsecure.crypto.generateKeys().then(function() {
if (textsecure.storage.getEncrypted("25519KeyidentityKey") === undefined || describe("Simple Curve25519 test vectors", function() {
getString(textsecure.storage.getEncrypted("25519KeyidentityKey").privKey) != origIdentityKey) it('works', function(done) {
return false; textsecure.registerOnLoadFunction(function() {
// These are just some random curve25519 test vectors I found online (with a version byte prepended to pubkeys)
if (textsecure.storage.getEncrypted("25519KeysignedKey0") === undefined || var alice_priv = hexToArrayBuffer("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
textsecure.storage.getEncrypted("25519KeysignedKey1") === undefined) var alice_pub = hexToArrayBuffer("058520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a");
return false; var bob_priv = hexToArrayBuffer("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb");
var bob_pub = hexToArrayBuffer("05de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f");
for (var i = 0; i < 200; i++) var shared_sec = hexToArrayBuffer("4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742");
if (textsecure.storage.getEncrypted("25519KeypreKey" + i) === undefined)
return false; textsecure.crypto.testing_only.privToPub(alice_priv, true).then(function(aliceKeyPair) {
var target = new Uint8Array(alice_priv.slice(0));
return textsecure.crypto.generateKeys().then(function() { target[0] &= 248;
if (textsecure.storage.getEncrypted("25519KeyidentityKey") === undefined || target[31] &= 127;
getString(textsecure.storage.getEncrypted("25519KeyidentityKey").privKey) != origIdentityKey) target[31] |= 64;
return false; assert.equal(getString(aliceKeyPair.privKey), getString(target));
if (textsecure.storage.getEncrypted("25519KeysignedKey0") !== undefined || textsecure.crypto.testing_only.privToPub(bob_priv, true).then(function(bobKeyPair) {
textsecure.storage.getEncrypted("25519KeysignedKey1") === undefined || var target = new Uint8Array(bob_priv.slice(0));
textsecure.storage.getEncrypted("25519KeysignedKey2") === undefined) target[0] &= 248;
return false; target[31] &= 127;
target[31] |= 64;
for (var i = 0; i < 300; i++) assert.equal(getString(bobKeyPair.privKey), getString(target));
if (textsecure.storage.getEncrypted("25519KeypreKey" + i) === undefined) assert.equal(getString(aliceKeyPair.pubKey), getString(alice_pub));
return false; assert.equal(getString(bobKeyPair.pubKey), getString(bob_pub));
return true; textsecure.crypto.testing_only.ECDHE(bobKeyPair.pubKey, aliceKeyPair.privKey).then(function(ss) {
}); assert.equal(getString(ss), getString(shared_sec));
});
}); textsecure.crypto.testing_only.ECDHE(aliceKeyPair.pubKey, bobKeyPair.privKey).then(function(ss) {
}, "Test Identity/Pre Key Creation", true); assert.equal(getString(ss), getString(shared_sec));
done();
TEST(function() { });
// These are just some random curve25519 test vectors I found online (with a version byte prepended to pubkeys) });
var alice_priv = hexToArrayBuffer("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a"); });
var alice_pub = hexToArrayBuffer("058520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"); });
var bob_priv = hexToArrayBuffer("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb"); });
var bob_pub = hexToArrayBuffer("05de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f"); });
var shared_sec = hexToArrayBuffer("4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742"); });
return textsecure.crypto.testing_only.privToPub(alice_priv, true).then(function(aliceKeyPair) { describe("Simple Ed25519 tests", function() {
var target = new Uint8Array(alice_priv.slice(0)); it('works', function(done) {
target[0] &= 248; textsecure.registerOnLoadFunction(function() {
target[31] &= 127; // Some self-generated test vectors
target[31] |= 64; var priv = hexToArrayBuffer("48a8892cc4e49124b7b57d94fa15becfce071830d6449004685e387c62409973");
if (getString(aliceKeyPair.privKey) != getString(target)) var pub = hexToArrayBuffer("0555f1bfede27b6a03e0dd389478ffb01462e5c52dbbac32cf870f00af1ed9af3a");
return false; var msg = hexToArrayBuffer("617364666173646661736466");
var sig = hexToArrayBuffer("2bc06c745acb8bae10fbc607ee306084d0c28e2b3bb819133392473431291fd0"+
return textsecure.crypto.testing_only.privToPub(bob_priv, true).then(function(bobKeyPair) { "dfa9c7f11479996cf520730d2901267387e08d85bbf2af941590e3035a545285");
var target = new Uint8Array(bob_priv.slice(0));
target[0] &= 248; textsecure.crypto.testing_only.privToPub(priv, false).then(function(pubCalc) {
target[31] &= 127; //if (getString(pub) != getString(pubCalc))
target[31] |= 64; // return false;
if (getString(bobKeyPair.privKey) != getString(target))
return false; textsecure.crypto.testing_only.Ed25519Sign(priv, msg).then(function(sigCalc) {
assert.equal(getString(sig), getString(sigCalc));
if (getString(aliceKeyPair.pubKey) != getString(alice_pub))
return false; textsecure.crypto.testing_only.Ed25519Verify(pub, msg, sig).then(function() {
done();
if (getString(bobKeyPair.pubKey) != getString(bob_pub)) });
return false; });
});
return textsecure.crypto.testing_only.ECDHE(bobKeyPair.pubKey, aliceKeyPair.privKey).then(function(ss) { });
if (getString(ss) != getString(shared_sec)) });
return false; });
return textsecure.crypto.testing_only.ECDHE(aliceKeyPair.pubKey, bobKeyPair.privKey).then(function(ss) { describe("Identity and Pre Key Creation", function() {
if (getString(ss) != getString(shared_sec)) this.timeout(10000);
return false; before(function() { localStorage.clear(); });
else after(function() { localStorage.clear(); });
return true; it ('works', function(done) {
}); textsecure.registerOnLoadFunction(function() {
}); textsecure.crypto.generateKeys().then(function() {
}); assert.isDefined(textsecure.storage.getEncrypted("25519KeyidentityKey"));
}); assert.isDefined(textsecure.storage.getEncrypted("25519KeysignedKey0"));
}, "Simple Curve25519 test vectors"); for (var i = 0; i < 100; i++) {
assert.isDefined(textsecure.storage.getEncrypted("25519KeypreKey" + i));
TEST(function() { }
// Some self-generated test vectors var origIdentityKey = getString(textsecure.storage.getEncrypted("25519KeyidentityKey").privKey);
var priv = hexToArrayBuffer("48a8892cc4e49124b7b57d94fa15becfce071830d6449004685e387c62409973"); textsecure.crypto.generateKeys().then(function() {
var pub = hexToArrayBuffer("0555f1bfede27b6a03e0dd389478ffb01462e5c52dbbac32cf870f00af1ed9af3a"); assert.isDefined(textsecure.storage.getEncrypted("25519KeyidentityKey"));
var msg = hexToArrayBuffer("617364666173646661736466"); assert.equal(getString(textsecure.storage.getEncrypted("25519KeyidentityKey").privKey), origIdentityKey);
var sig = hexToArrayBuffer("2bc06c745acb8bae10fbc607ee306084d0c28e2b3bb819133392473431291fd0"+
"dfa9c7f11479996cf520730d2901267387e08d85bbf2af941590e3035a545285"); assert.isDefined(textsecure.storage.getEncrypted("25519KeysignedKey0"));
assert.isDefined(textsecure.storage.getEncrypted("25519KeysignedKey1"));
return textsecure.crypto.testing_only.privToPub(priv, false).then(function(pubCalc) {
//if (getString(pub) != getString(pubCalc)) for (var i = 0; i < 200; i++) {
// return false; assert.isDefined(textsecure.storage.getEncrypted("25519KeypreKey" + i));
}
return textsecure.crypto.testing_only.Ed25519Sign(priv, msg).then(function(sigCalc) {
if (getString(sig) != getString(sigCalc)) textsecure.crypto.generateKeys().then(function() {
return false; assert.isDefined(textsecure.storage.getEncrypted("25519KeyidentityKey"));
assert.equal(getString(textsecure.storage.getEncrypted("25519KeyidentityKey").privKey), origIdentityKey);
return textsecure.crypto.testing_only.Ed25519Verify(pub, msg, sig).then(function() {
return true; assert.isUndefined(textsecure.storage.getEncrypted("25519KeysignedKey0"));
}); assert.isDefined(textsecure.storage.getEncrypted("25519KeysignedKey1"));
}); assert.isDefined(textsecure.storage.getEncrypted("25519KeysignedKey2"));
});
}, "Simple Ed25519 tests"); for (var i = 0; i < 300; i++) {
assert.isDefined(textsecure.storage.getEncrypted("25519KeypreKey" + i));
TEST(function() { }
var IKM = new Uint8Array(new ArrayBuffer(22));
for (var i = 0; i < 22; i++) done();
IKM[i] = 11; });
});
var salt = new Uint8Array(new ArrayBuffer(13)); });
for (var i = 0; i < 13; i++) });
salt[i] = i; });
});
var info = new Uint8Array(new ArrayBuffer(10)); });
for (var i = 0; i < 10; i++)
info[i] = 240 + i; describe("Axolotl", function() {
var runAxolotlTest = function(v) {
return textsecure.crypto.testing_only.HKDF(IKM.buffer, salt.buffer, info.buffer).then(function(OKM){ var origCreateNewKeyPair = textsecure.crypto.testing_only.createNewKeyPair;
var T1 = hexToArrayBuffer("3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf"); var doStep;
var T2 = hexToArrayBuffer("34007208d5b887185865"); var stepDone;
return (getString(OKM[0]) == getString(T1) && getString(OKM[1]).substring(0, 10) == getString(T2));
}); stepDone = function(res) {
}, "HMAC RFC5869 Test vectors"); if (!res || privKeyQueue.length != 0 || Object.keys(getKeysForNumberMap).length != 0 || Object.keys(messagesSentMap).length != 0) {
textsecure.crypto.testing_only.createNewKeyPair = origCreateNewKeyPair;
var runAxolotlTest = function(v) { return false;
var origCreateNewKeyPair = textsecure.crypto.testing_only.createNewKeyPair; } else if (step == v.length) {
var doStep; textsecure.crypto.testing_only.createNewKeyPair = origCreateNewKeyPair;
var stepDone; return true;
} else
stepDone = function(res) { return doStep().then(stepDone);
if (!res || privKeyQueue.length != 0 || Object.keys(getKeysForNumberMap).length != 0 || Object.keys(messagesSentMap).length != 0) { }
textsecure.crypto.testing_only.createNewKeyPair = origCreateNewKeyPair;
return false; var privKeyQueue = [];
} else if (step == v.length) { textsecure.crypto.testing_only.createNewKeyPair = function(isIdentity) {
textsecure.crypto.testing_only.createNewKeyPair = origCreateNewKeyPair; if (privKeyQueue.length == 0 || isIdentity)
return true; throw new Error('Out of private keys');
} else else {
return doStep().then(stepDone); var privKey = privKeyQueue.shift();
} return textsecure.crypto.testing_only.privToPub(privKey, false).then(function(keyPair) {
var a = btoa(getString(keyPair.privKey)); var b = btoa(getString(privKey));
var privKeyQueue = []; if (getString(keyPair.privKey) != getString(privKey))
textsecure.crypto.testing_only.createNewKeyPair = function(isIdentity) { throw new Error('Failed to rederive private key!');
if (privKeyQueue.length == 0 || isIdentity) else
throw new Error('Out of private keys'); return keyPair;
else { });
var privKey = privKeyQueue.shift(); }
return textsecure.crypto.testing_only.privToPub(privKey, false).then(function(keyPair) { }
var a = btoa(getString(keyPair.privKey)); var b = btoa(getString(privKey));
if (getString(keyPair.privKey) != getString(privKey)) var step = 0;
throw new Error('Failed to rederive private key!'); var doStep = function() {
else var data = v[step][1];
return keyPair;
}); switch(v[step++][0]) {
} case "receiveMessage":
} var postLocalKeySetup = function() {
if (data.newEphemeralKey !== undefined)
var step = 0; privKeyQueue.push(data.newEphemeralKey);
var doStep = function() {
var data = v[step][1]; var message = new textsecure.protos.IncomingPushMessageProtobuf();
message.type = data.type;
switch(v[step++][0]) { message.source = "SNOWDEN";
case "receiveMessage": message.message = data.message;
var postLocalKeySetup = function() { message.sourceDevice = 1;
if (data.newEphemeralKey !== undefined) try {
privKeyQueue.push(data.newEphemeralKey); return textsecure.crypto.handleIncomingPushMessageProto(textsecure.protos.decodeIncomingPushMessageProtobuf(getString(message.encode()))).then(function(res) {
if (data.expectTerminateSession)
var message = new textsecure.protos.IncomingPushMessageProtobuf(); return res.flags == textsecure.protos.PushMessageContentProtobuf.Flags.END_SESSION;
message.type = data.type; return res.body == data.expectedSmsText;
message.source = "SNOWDEN"; }).catch(function(e) {
message.message = data.message; if (data.expectException)
message.sourceDevice = 1; return true;
try { throw e;
return textsecure.crypto.handleIncomingPushMessageProto(textsecure.protos.decodeIncomingPushMessageProtobuf(getString(message.encode()))).then(function(res) { });
if (data.expectTerminateSession) } catch(e) {
return res.flags == textsecure.protos.PushMessageContentProtobuf.Flags.END_SESSION; if (data.expectException)
return res.body == data.expectedSmsText; return Promise.resolve(true);
}).catch(function(e) { throw e;
if (data.expectException) }
return true; }
throw e;
}); if (data.ourIdentityKey !== undefined)
} catch(e) { return textsecure.crypto.testing_only.privToPub(data.ourIdentityKey, true).then(function(keyPair) {
if (data.expectException) textsecure.storage.putEncrypted("25519KeyidentityKey", keyPair);
return Promise.resolve(true); return textsecure.crypto.testing_only.privToPub(data.ourSignedPreKey, false).then(function(keyPair) {
throw e; textsecure.storage.putEncrypted("25519KeysignedKey" + data.signedPreKeyId, keyPair);
}
} if (data.ourPreKey !== undefined)
return textsecure.crypto.testing_only.privToPub(data.ourPreKey, false).then(function(keyPair) {
if (data.ourIdentityKey !== undefined) textsecure.storage.putEncrypted("25519KeypreKey" + data.preKeyId, keyPair);
return textsecure.crypto.testing_only.privToPub(data.ourIdentityKey, true).then(function(keyPair) { return postLocalKeySetup();
textsecure.storage.putEncrypted("25519KeyidentityKey", keyPair); });
return textsecure.crypto.testing_only.privToPub(data.ourSignedPreKey, false).then(function(keyPair) { else
textsecure.storage.putEncrypted("25519KeysignedKey" + data.signedPreKeyId, keyPair); return postLocalKeySetup();
});
if (data.ourPreKey !== undefined) });
return textsecure.crypto.testing_only.privToPub(data.ourPreKey, false).then(function(keyPair) { else
textsecure.storage.putEncrypted("25519KeypreKey" + data.preKeyId, keyPair); return postLocalKeySetup();
return postLocalKeySetup();
}); case "sendMessage":
else var postLocalKeySetup = function() {
return postLocalKeySetup(); if (data.registrationId !== undefined)
}); textsecure.storage.putUnencrypted("registrationId", data.registrationId);
});
else if (data.getKeys !== undefined)
return postLocalKeySetup(); getKeysForNumberMap["SNOWDEN"] = data.getKeys;
case "sendMessage": var checkMessage = function() {
var postLocalKeySetup = function() { var msg = messagesSentMap["SNOWDEN.1"];
if (data.registrationId !== undefined) delete messagesSentMap["SNOWDEN.1"];
textsecure.storage.putUnencrypted("registrationId", data.registrationId); //XXX: This should be all we do: isEqual(data.expectedCiphertext, msg.body, false);
if (msg.type == 1) {
if (data.getKeys !== undefined) var expected = getString(data.expectedCiphertext);
getKeysForNumberMap["SNOWDEN"] = data.getKeys; var decoded = textsecure.protos.decodeWhisperMessageProtobuf(expected.substring(1, expected.length - 8));
var result = getString(msg.body);
var checkMessage = function() { return getString(decoded.encode()) == result.substring(1, result.length - 8);
var msg = messagesSentMap["SNOWDEN.1"]; } else {
delete messagesSentMap["SNOWDEN.1"]; var decoded = textsecure.protos.decodePreKeyWhisperMessageProtobuf(getString(data.expectedCiphertext).substr(1));
//XXX: This should be all we do: isEqual(data.expectedCiphertext, msg.body, false); var result = getString(msg.body).substring(1);
if (msg.type == 1) { return getString(decoded.encode()) == result;
var expected = getString(data.expectedCiphertext); }
var decoded = textsecure.protos.decodeWhisperMessageProtobuf(expected.substring(1, expected.length - 8)); }
var result = getString(msg.body);
return getString(decoded.encode()) == result.substring(1, result.length - 8); if (data.endSession)
} else { return textsecure.messaging.closeSession("SNOWDEN").then(checkMessage);
var decoded = textsecure.protos.decodePreKeyWhisperMessageProtobuf(getString(data.expectedCiphertext).substr(1)); else
var result = getString(msg.body).substring(1); return textsecure.messaging.sendMessageToNumber("SNOWDEN", data.smsText, []).then(checkMessage);
return getString(decoded.encode()) == result; }
}
} if (data.ourBaseKey !== undefined)
privKeyQueue.push(data.ourBaseKey);
if (data.endSession) if (data.ourEphemeralKey !== undefined)
return textsecure.messaging.closeSession("SNOWDEN").then(checkMessage); privKeyQueue.push(data.ourEphemeralKey);
else
return textsecure.messaging.sendMessageToNumber("SNOWDEN", data.smsText, []).then(checkMessage); if (data.ourIdentityKey !== undefined)
} return textsecure.crypto.testing_only.privToPub(data.ourIdentityKey, true).then(function(keyPair) {
textsecure.storage.putEncrypted("25519KeyidentityKey", keyPair);
if (data.ourBaseKey !== undefined) return postLocalKeySetup();
privKeyQueue.push(data.ourBaseKey); });
if (data.ourEphemeralKey !== undefined) else
privKeyQueue.push(data.ourEphemeralKey); return postLocalKeySetup();
if (data.ourIdentityKey !== undefined) default:
return textsecure.crypto.testing_only.privToPub(data.ourIdentityKey, true).then(function(keyPair) { return Promise.resolve(false);
textsecure.storage.putEncrypted("25519KeyidentityKey", keyPair); }
return postLocalKeySetup(); }
}); return doStep().then(stepDone);
else };
return postLocalKeySetup();
describe("test vectors", function() {
default: _.each(axolotlTestVectors, function(t, i) {
return Promise.resolve(false); it(t.name, function() {
} assert(runAxolotlTest(t.vectors));
} });
return doStep().then(stepDone); });
} });
for (index in axolotlTestVectors) {
var _ = function(i) {
TEST(function() {
return runAxolotlTest(axolotlTestVectors[i].vectors);
}, axolotlTestVectors[i].name, true);
}(index);
}
TEST(function() {
var key = hexToArrayBuffer('6f35628d65813435534b5d67fbdb54cb33403d04e843103e6399f806cb5df95febbdd61236f33245');
var input = hexToArrayBuffer('752cff52e4b90768558e5369e75d97c69643509a5e5904e0a386cbe4d0970ef73f918f675945a9aefe26daea27587e8dc909dd56fd0468805f834039b345f855cfe19c44b55af241fff3ffcd8045cd5c288e6c4e284c3720570b58e4d47b8feeedc52fd1401f698a209fccfa3b4c0d9a797b046a2759f82a54c41ccd7b5f592b');
var mac = getString(hexToArrayBuffer('05d1243e6465ed9620c9aec1c351a186'));
return window.crypto.subtle.sign({name: "HMAC", hash: "SHA-256"}, key, input).then(function(result) {
return getString(result).substring(0, mac.length) === mac;
});
}, "HMAC SHA-256", false);
TEST(function() {
var key = hexToArrayBuffer('2b7e151628aed2a6abf7158809cf4f3c');
var counter = hexToArrayBuffer('f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff');
var plaintext = hexToArrayBuffer('6bc1bee22e409f96e93d7e117393172a');
var ciphertext = hexToArrayBuffer('874d6191b620e3261bef6864990db6ce');
return window.crypto.subtle.encrypt({name: "AES-CTR", counter: counter}, key, plaintext).then(function(result) {
return getString(result) === getString(ciphertext);
});
}, "Encrypt AES-CTR", false);
TEST(function() {
var key = hexToArrayBuffer('2b7e151628aed2a6abf7158809cf4f3c');
var counter = hexToArrayBuffer('f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff');
var plaintext = hexToArrayBuffer('6bc1bee22e409f96e93d7e117393172a');
var ciphertext = hexToArrayBuffer('874d6191b620e3261bef6864990db6ce');
return window.crypto.subtle.decrypt({name: "AES-CTR", counter: counter}, key, ciphertext).then(function(result) {
return getString(result) === getString(plaintext);
});
}, "Decrypt AES-CTR", false);
TEST(function() {
var key = hexToArrayBuffer('603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4');
var iv = hexToArrayBuffer('000102030405060708090a0b0c0d0e0f');
var plaintext = hexToArrayBuffer('6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710');
var ciphertext = hexToArrayBuffer('f58c4c04d6e5f1ba779eabfb5f7bfbd69cfc4e967edb808d679f777bc6702c7d39f23369a9d9bacfa530e26304231461b2eb05e2c39be9fcda6c19078c6a9d1b3f461796d6b0d6b2e0c2a72b4d80e644');
return window.crypto.subtle.decrypt({name: "AES-CBC", iv: iv}, key, ciphertext).then(function(result) {
return getString(result) === getString(plaintext);
});
}, "Decrypt AES-CBC", false);
// Setup test timeouts
forceNextTestInverval = window.setInterval(function() {
for (var i = 0; i < maxTestId; i++) {
if (testsOutstanding[i] !== undefined) {
testsdiv.append('<p style="color: red;">' + testsOutstanding[i] + ' timed out</p>');
if (exclusiveRunning == i) {
testsdiv.append('<p style="color: red;">WARNING: exclusive test left running, further results may be unreliable.</p>');
delete exclusiveTestsWaiting[i];
}
}
delete testsOutstanding[i];
}
printTestsDone();
startNextExclusiveTest();
}, 10000);
allTestsDefined = true;
printTestsDone();
}); });

View file

@ -14,16 +14,28 @@
<html> <html>
<head><title>TextSecure test runner</title></head> <head>
<title>TextSecure test runner</title>
<link rel="stylesheet" href="test/mocha.css" />
</head>
<body data-name="curve25519" data-tools="pnacl" data-configs="Debug Release" data-path="nacl/pnacl/{config}"> <body data-name="curve25519" data-tools="pnacl" data-configs="Debug Release" data-path="nacl/pnacl/{config}">
<div id="listener"></div> <div id="listener"></div>
<div id="log"></div> <div id="log"></div>
<h1>Run this out of the chrome-plugin:// namespace (and expect plugin state to be cleared/corrupted), not file://</h1> <h2>Run this out of the chrome-plugin:// namespace (and expect plugin state to be cleared/corrupted), not file://</h1>
<div id="mocha">
</div>
<div id="tests"> <div id="tests">
</div> </div>
<script type="text/javascript" src="test/mocha.js"></script>
<script type="text/javascript" src="test/chai.js"></script>
<script>
mocha.setup("bdd")
window.assert = chai.assert;
</script>
<script type="text/javascript" src="js-deps/nacl-common.js"></script> <script type="text/javascript" src="js-deps/nacl-common.js"></script>
<script type="text/javascript" src="js-deps/jquery.js"></script> <script type="text/javascript" src="js-deps/jquery.js"></script>
<script type="text/javascript" src="js-deps/CryptoJS.js"></script> <script type="text/javascript" src="js-deps/CryptoJS.js"></script>
@ -48,5 +60,12 @@
<script type="text/javascript" src="js/fake_api.js"></script> <script type="text/javascript" src="js/fake_api.js"></script>
<script type="text/javascript" src="js/testvectors.js"></script> <script type="text/javascript" src="js/testvectors.js"></script>
<script type="text/javascript" src="js/test.js"></script> <script type="text/javascript" src="js/test.js"></script>
<script type="text/javascript">
if (window.mochaPhantomJS)
mochaPhantomJS.run();
else
mocha.run();
</script>
</body> </body>
</html> </html>

4782
test/chai.js Normal file

File diff suppressed because it is too large Load diff

270
test/mocha.css Normal file
View file

@ -0,0 +1,270 @@
@charset "utf-8";
body {
margin:0;
}
#mocha {
font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: 60px 50px;
}
#mocha ul,
#mocha li {
margin: 0;
padding: 0;
}
#mocha ul {
list-style: none;
}
#mocha h1,
#mocha h2 {
margin: 0;
}
#mocha h1 {
margin-top: 15px;
font-size: 1em;
font-weight: 200;
}
#mocha h1 a {
text-decoration: none;
color: inherit;
}
#mocha h1 a:hover {
text-decoration: underline;
}
#mocha .suite .suite h1 {
margin-top: 0;
font-size: .8em;
}
#mocha .hidden {
display: none;
}
#mocha h2 {
font-size: 12px;
font-weight: normal;
cursor: pointer;
}
#mocha .suite {
margin-left: 15px;
}
#mocha .test {
margin-left: 15px;
overflow: hidden;
}
#mocha .test.pending:hover h2::after {
content: '(pending)';
font-family: arial, sans-serif;
}
#mocha .test.pass.medium .duration {
background: #c09853;
}
#mocha .test.pass.slow .duration {
background: #b94a48;
}
#mocha .test.pass::before {
content: '✓';
font-size: 12px;
display: block;
float: left;
margin-right: 5px;
color: #00d6b2;
}
#mocha .test.pass .duration {
font-size: 9px;
margin-left: 5px;
padding: 2px 5px;
color: #fff;
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
-moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
-ms-border-radius: 5px;
-o-border-radius: 5px;
border-radius: 5px;
}
#mocha .test.pass.fast .duration {
display: none;
}
#mocha .test.pending {
color: #0b97c4;
}
#mocha .test.pending::before {
content: '◦';
color: #0b97c4;
}
#mocha .test.fail {
color: #c00;
}
#mocha .test.fail pre {
color: black;
}
#mocha .test.fail::before {
content: '✖';
font-size: 12px;
display: block;
float: left;
margin-right: 5px;
color: #c00;
}
#mocha .test pre.error {
color: #c00;
max-height: 300px;
overflow: auto;
}
/**
* (1): approximate for browsers not supporting calc
* (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border)
* ^^ seriously
*/
#mocha .test pre {
display: block;
float: left;
clear: left;
font: 12px/1.5 monaco, monospace;
margin: 5px;
padding: 15px;
border: 1px solid #eee;
max-width: 85%; /*(1)*/
max-width: calc(100% - 42px); /*(2)*/
word-wrap: break-word;
border-bottom-color: #ddd;
-webkit-border-radius: 3px;
-webkit-box-shadow: 0 1px 3px #eee;
-moz-border-radius: 3px;
-moz-box-shadow: 0 1px 3px #eee;
border-radius: 3px;
}
#mocha .test h2 {
position: relative;
}
#mocha .test a.replay {
position: absolute;
top: 3px;
right: 0;
text-decoration: none;
vertical-align: middle;
display: block;
width: 15px;
height: 15px;
line-height: 15px;
text-align: center;
background: #eee;
font-size: 15px;
-moz-border-radius: 15px;
border-radius: 15px;
-webkit-transition: opacity 200ms;
-moz-transition: opacity 200ms;
transition: opacity 200ms;
opacity: 0.3;
color: #888;
}
#mocha .test:hover a.replay {
opacity: 1;
}
#mocha-report.pass .test.fail {
display: none;
}
#mocha-report.fail .test.pass {
display: none;
}
#mocha-report.pending .test.pass,
#mocha-report.pending .test.fail {
display: none;
}
#mocha-report.pending .test.pass.pending {
display: block;
}
#mocha-error {
color: #c00;
font-size: 1.5em;
font-weight: 100;
letter-spacing: 1px;
}
#mocha-stats {
position: fixed;
top: 15px;
right: 10px;
font-size: 12px;
margin: 0;
color: #888;
z-index: 1;
}
#mocha-stats .progress {
float: right;
padding-top: 0;
}
#mocha-stats em {
color: black;
}
#mocha-stats a {
text-decoration: none;
color: inherit;
}
#mocha-stats a:hover {
border-bottom: 1px solid #eee;
}
#mocha-stats li {
display: inline-block;
margin: 0 5px;
list-style: none;
padding-top: 11px;
}
#mocha-stats canvas {
width: 40px;
height: 40px;
}
#mocha code .comment { color: #ddd; }
#mocha code .init { color: #2f6fad; }
#mocha code .string { color: #5890ad; }
#mocha code .keyword { color: #8a6343; }
#mocha code .number { color: #2f6fad; }
@media screen and (max-device-width: 480px) {
#mocha {
margin: 60px 0px;
}
#mocha #stats {
position: absolute;
}
}

5842
test/mocha.js Normal file

File diff suppressed because it is too large Load diff