diff --git a/js/helpers.js b/js/helpers.js index e05d02c7..ae9a088b 100644 --- a/js/helpers.js +++ b/js/helpers.js @@ -77,6 +77,7 @@ function base64EncArr (aBytes) { *** Type conversion utilities *** *********************************/ // Strings/arrays +//TODO: Throw all this shit in favor of consistent types var StaticByteBufferProto = new dcodeIO.ByteBuffer().__proto__; var StaticArrayBufferProto = new ArrayBuffer().__proto__; var StaticUint8ArrayProto = new Uint8Array().__proto__; @@ -108,6 +109,8 @@ function toArrayBuffer(thing) { //TODO: Optimize this for specific cases if (thing === undefined) return undefined; + if (thing === Object(thing) && thing.__proto__ == StaticArrayBufferProto) + return thing; if (!getStringable(thing)) throw "Tried to convert a non-stringable thing of type " + typeof thing + " to an array buffer"; var str = getString(thing); @@ -396,6 +399,8 @@ function getRandomBytes(size) { } } +var crypto_tests = {}; + (function(crypto, $, undefined) { var createNewKeyPair = function(callback) { var privKey = getRandomBytes(32); @@ -467,31 +472,39 @@ function getRandomBytes(size) { } var HMACSHA256 = function(input, key) { - //TODO: return type + //TODO: Waaayyyy less type conversion here (probably just means replacing CryptoJS) return CryptoJS.HmacSHA256( CryptoJS.lib.WordArray.create(toArrayBuffer(input)), CryptoJS.enc.Latin1.parse(getString(key))) .toString(CryptoJS.enc.Latin1); } + crypto_tests.HKDF = function(input, salt, info) { + // Specific implementation of RFC 5869 that only returns exactly 64 bytes + var PRK = HMACSHA256(input, salt); + + var infoString = getString(info); + var T1 = HMACSHA256(infoString + String.fromCharCode(1), PRK); + var T2 = HMACSHA256(getString(T1) + infoString + String.fromCharCode(2), PRK); + + return [ T1, T2 ]; + } + var HKDF = function(input, salt, info) { - var key; + // HKDF for TextSecure has a bit of additional handling - salts always end up being 32 bytes if (salt == '') { - var key = new ArrayBuffer(32); - var uintKey = new Uint8Array(key); + salt = new ArrayBuffer(32); + var uintKey = new Uint8Array(salt); for (var i = 0; i < 32; i++) uintKey[i] = 0; - } else - key = toArrayBuffer(salt); + } - if (key.byteLength != 32) + salt = toArrayBuffer(salt); + + if (salt.byteLength != 32) throw "Got salt of incorrect length"; - var PRK = HMACSHA256(input, salt); - - HMACSHA256(salt, input); - var hkdf = "HKDF(" + input + ", " + salt + ", " + info + ")"; //TODO - return [ hkdf.substring(0, 32), hkdf.substring(32, 64) ]; + return crypto_tests.HKDF(input, salt, info); } var decryptPaddedAES = function(ciphertext, key, iv) { @@ -518,11 +531,11 @@ function getRandomBytes(size) { var sharedSecret; ECDHE(theirEphemeralPubKey, ourIdentityPrivKey, function(ecRes) { - sharedSecret = ecRes; + sharedSecret = getString(ecRes); function finishInit() { ECDHE(theirEphemeralPubKey, ourEphemeralPrivKey, function(ecRes) { - sharedSecret += ecRes; + sharedSecret += getString(ecRes); var masterKey = HKDF(sharedSecret, '', "WhisperText"); callback({ rootKey: masterKey[0], chainKey: masterKey[1] }); @@ -531,12 +544,12 @@ function getRandomBytes(size) { if (isInitiator) { ECDHE(theirIdentityPubKey, ourEphemeralPrivKey, function(ecRes) { - sharedSecret = sharedSecret + ecRes; + sharedSecret = sharedSecret + getString(ecRes); finishInit(); }); } else { ECDHE(theirIdentityPubKey, ourEphemeralPrivKey, function(ecRes) { - sharedSecret = ecRes + sharedSecret; + sharedSecret = getString(ecRes) + sharedSecret; finishInit(); }); } @@ -631,7 +644,7 @@ function getRandomBytes(size) { var message = decodeWhisperMessageProtobuf(messageProto); - maybeStepRatchet(session, getString(message.ephemeralKey), message.previousCounter, function() { + maybeStepRatchet(session, message.ephemeralKey, message.previousCounter, function() { var chain = session[getString(message.ephemeralKey)]; fillMessageKeys(chain, message.counter); diff --git a/js/test.js b/js/test.js index 6f4ed6af..efbd5a8f 100644 --- a/js/test.js +++ b/js/test.js @@ -114,7 +114,7 @@ registerOnLoadFunction(function() { crypto.generateKeys(function() { callback(true); }); - }, "Test simple create key"); + }, "Test simple create key", true); TEST(function(callback) { // These are just some random curve25519 test vectors I found online @@ -167,21 +167,40 @@ registerOnLoadFunction(function() { }); }, "Simple Curve25519 test vector"); + TEST(function(callback) { + var IKM = new Uint8Array(new ArrayBuffer(22)); + for (var i = 0; i < 22; i++) + 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; + + var OKM = crypto_tests.HKDF(IKM, salt, info); + var T1 = hexToArrayBuffer("3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf"); + var T2 = hexToArrayBuffer("34007208d5b887185865"); + callback(getString(OKM[0]) == getString(T1) && getString(OKM[1]).substring(0, 10) == getString(T2)); + }, "HMAC RFC5869 Test vectors"); + var axolotlTestVectors = { -aliceIdentityPriv: hexToArrayBuffer("38115e981295947fb6130c3a9521760e9692476143810c64bb502d2a7757f953"), -aliceIdentityPub: hexToArrayBuffer("057f6f0cf5a353e3c2fa73774d36b07d491c2ba5285388a4dc7be3b08de4528933"), -bobIdentityPriv: hexToArrayBuffer("9069fadc08faaf6265a90a41fd1a9214bc70eb44ab30aa92ea5ad5a6b8609557"), -bobIdentityPub: hexToArrayBuffer("05889d0f94f4049fac36cc939c50dabec18c2af8e344f2cdf72cb5b0b02323337c"), -aliceLastResort: hexToArrayBuffer("10cd8adcbbf5a0157ed45e3be2e0bde5e91c3cdf77954530ac75c6ae9b8c2458"), -bobLastResort: hexToArrayBuffer("9855c7ce178404bb02c6c026103cc7d74d68a12f77d35d03df40a5f2dff6784d"), -alicePre0: hexToArrayBuffer("981bea233e094a34f7d564c63515af4691d3059268371c3b26f37a141e4fb553"), -alicePre1: hexToArrayBuffer("18b27cac90879c8ba5b1d6eb29d08ee380bac2e1022ead9a7dbe5ddfa0009852"), -bobPre0: hexToArrayBuffer("780fc02d2b8ffce8cd6f92233ce28b46a487dafeb97597461b1ed964fb118e5c"), -bobPre1: hexToArrayBuffer("c8e218839b33ae6c54d73ef56ad77551b6575e64c5b4acc0f0710b93799c3b56"), -aliceToBob: hexToArrayBuffer("08031205414c4943452203424f4228bff1f9c3c92832860122080012210541ef7f7215ffb6260413ab2c95bbd39d5d8d55241de87a6a7fa50ae5d99cbe741a21057f6f0cf5a353e3c2fa73774d36b07d491c2ba5285388a4dc7be3b08de4528933223b220a2105c851726034e7181614e21c6349d33f9bcff4f4becfc08c4422c6606cdc976d2c100018012209bb4876c684c88dd05207274341f4e3aa91"), +aliceIdentityPriv: hexToArrayBuffer("d8dab419978a2693f2842691931e4c8aaa09a11e8a94b5817cc4f81a1c474a62"), +aliceIdentityPub: hexToArrayBuffer("0502e9e6c0528ea9e44d8cb759aeaa02ae3eccbe3107de4f0815240b414744fd22"), +bobIdentityPriv: hexToArrayBuffer("18bd2244222329293a303343759e8e9feb4a4e2771273111528ef97abb6a567e"), +bobIdentityPub: hexToArrayBuffer("05b1cb0b5f1e5c93d20a3db2af0ab2512d39d91e6b671ee8e462a62658ba064a5b"), +aliceLastResort: hexToArrayBuffer("8825a933b97d40f65ea37701800900f10d8ba6dff6979a5634e0dc6de8d9b24f"), +bobLastResort: hexToArrayBuffer("c054dbefd8eee42ff3c1bb873def01f05411304ee07014ff86088c11f57b8e76"), +alicePre0: hexToArrayBuffer("384db59677ea9545f1da2c10426b463fb3180bd30d294ad69cd91b44e3a3ad43"), +alicePre1: hexToArrayBuffer("a841ece2aab82a0542c59c0daca5bd0ae36bc81b3f375ba0ffcc73ab2feacf6f"), +bobPre0: hexToArrayBuffer("10b6b52b867f79e330a5fa3b46fd6a542a1dc21d1103f4d2e8f741c0dd989474"), +bobPre1: hexToArrayBuffer("70be59ec978e3aaa95b64635b09ad1a614c2eda2142ac577b1eb6b1ec8e7f270"), +aliceToBob: hexToArrayBuffer("08031205414c4943452203424f4228c3dfc6cac92832860122080012210518761fa74e002dbbbb140ca7950fb83a3aab3dd4fb7b75ec87eea17f0cde7e0a1a210502e9e6c0528ea9e44d8cb759aeaa02ae3eccbe3107de4f0815240b414744fd22223b220a2105fbad228c11ab8098c3fcb9c16ff1f9df705dce81ea4b6e3df988148e254270751000180122093cfc98c3bc5557b31ffe8deba6900bcfe6"), plain: hexToArrayBuffer("0a07486920426f6221"), -sessionKey: hexToArrayBuffer("03a3a58503e91c9e5ae179fa87bbb755f7afe35a2bffce4932cec2c2fcaa28832389e20000c7e1e7c7365959e4146d98f3620df9"), -encryptedMessage: hexToArrayBuffer("41576d4e6f7431327748385470366f724c7735416a62425446767139314c376857757a663854394e54775256326955384f76376265494463587030396634356441506159515a456a6e71704176364852574f3539584e35796d73507548423342716f78594e7136396346424c6e4145766439495a2f6674593835555a586e5668493574704e673946354c56627a4e70474563436672762f6a69393661675837755936335867746342683177412b5967364a472b37626a4a3044542b526e67383579325852705473644d3931794144776b5a744478616d7976412f714746556b37447a7353367061704a364459532b6e35673841457230764f37413d3d"), +sessionKey: hexToArrayBuffer("34400f1fde8f3b96beb435c280a6e93b829679b7e948a85c2f7250c6bfd419dd411a0e9cb5f62cd8b39e2ba23e013763169eb40a"), +encryptedMessage: hexToArrayBuffer("415733486e6d3165754275487778594d2f4b744a556e63364f67386e45754d6868663054704f486b45494b786c33616a79544552665531354c7539687a426736572f38546b7255583957653542724572724b3867367544554348425257555646774c4662614c67497446722f3242492f56434c31307a49666e2b765457524d324474475a46345a36553869717633566179443273354330417274386d6f48517342524970535a59387841566e584464572f466c4e7632706f6c5a6a2f464e597635552b56335933324552417572327464344a44654e6c544d4c6d4b7456387252384f354d7546626e735074336846456d496d374d7633573039673d3d") }; // Axolotl test vectors