diff --git a/js/crypto.js b/js/crypto.js index be06af99..477eba0f 100644 --- a/js/crypto.js +++ b/js/crypto.js @@ -22,9 +22,9 @@ * for all low-level crypto operations, */ - // TODO Select an ecc implementation - //var ecc = textsecure.NATIVE_CLIENT ? textsecure.nativeclient : textsecure.tweetnacl || asmjs; - var ecc = textsecure.nativeclient; + // TODO Select an curve25519 implementation + //var curve25519 = textsecure.NATIVE_CLIENT ? textsecure.nativeclient : textsecure.tweetnacl || asmjs; + var curve25519 = textsecure.nativeclient; window.textsecure.crypto = { getRandomBytes: function(size) { @@ -82,7 +82,7 @@ throw new Error("Invalid private key"); } - return ecc.privToPub(privKey).then(function(raw_keys) { + return curve25519.privToPub(privKey).then(function(raw_keys) { // prepend version byte var origPub = new Uint8Array(raw_keys.pubKey); var pub = new Uint8Array(33); @@ -100,7 +100,7 @@ if (pubKey === undefined || pubKey.byteLength != 32) throw new Error("Invalid public key"); - return ecc.ECDHE(pubKey, privKey); + return curve25519.ECDHE(pubKey, privKey); }, Ed25519Sign: function(privKey, message) { if (privKey === undefined || privKey.byteLength != 32) @@ -109,7 +109,7 @@ if (message === undefined) throw new Error("Invalid message"); - return ecc.Ed25519Sign(privKey, message); + return curve25519.Ed25519Sign(privKey, message); }, Ed25519Verify: function(pubKey, msg, sig) { pubKey = validatePubKeyFormat(pubKey); @@ -123,7 +123,7 @@ if (sig === undefined || sig.byteLength != 64) throw new Error("Invalid signature"); - return ecc.Ed25519Verify(pubKey, msg, sig); + return curve25519.Ed25519Verify(pubKey, msg, sig); } }; diff --git a/test/curve25519_test.js b/test/curve25519_test.js new file mode 100644 index 00000000..4e54cd8d --- /dev/null +++ b/test/curve25519_test.js @@ -0,0 +1,111 @@ +/* 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'; + +/* + * We don't run any tests here, just define an abstract test function + * that excercises our requirements for curve25519 interface, which are + * + * privTopub(privateKey) + * takes a 32-byte private key array buffer and outputs the corresponding + * public key as an array buffer + * + * ECDHE(publicKey, privateKey) + * computes a shared secret from two curve25519 keys using the given keys + * + * Ed25519Sign(privateKey, message) + * computes a signature for the given message using a private key + * + * Ed25519Verify(publicKey, message, signature) + * verifies a signature for the given message using a public key + * + */ + +var test_curve25519_implementation = function(implementation) { + describe("Curve25519", function() { + var alice_bytes = hexToArrayBuffer("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a"); + var alice_priv = hexToArrayBuffer("70076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c6a"); + var alice_pub = hexToArrayBuffer("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"); + var bob_bytes = hexToArrayBuffer("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb"); + var bob_priv = hexToArrayBuffer("58ab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e06b"); + var bob_pub = hexToArrayBuffer("de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f"); + var shared_sec = hexToArrayBuffer("4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742"); + + describe("privToPub", function() { + it ('converts alice private keys to a keypair', function(done) { + implementation.privToPub(alice_bytes).then(function(keypair) { + assertEqualArrayBuffers(keypair.privKey, alice_priv); + assertEqualArrayBuffers(keypair.pubKey, alice_pub); + done(); + }).catch(done); + }); + it ('converts bob private keys to a keypair', function(done) { + implementation.privToPub(bob_bytes).then(function(keypair) { + assertEqualArrayBuffers(keypair.privKey, bob_priv); + assertEqualArrayBuffers(keypair.pubKey, bob_pub); + done(); + }).catch(done); + }); + }); + + describe("ECDHE", function() { + it("computes the shared secret for alice", function(done) { + implementation.ECDHE(bob_pub, alice_priv).then(function(secret) { + assertEqualArrayBuffers(shared_sec, secret); + done(); + }).catch(done); + }); + it("computes the shared secret for bob", function(done) { + implementation.ECDHE(alice_pub, bob_priv).then(function(secret) { + assertEqualArrayBuffers(shared_sec, secret); + done(); + }).catch(done); + }); + }); + + var priv = hexToArrayBuffer("48a8892cc4e49124b7b57d94fa15becfce071830d6449004685e387c62409973"); + var pub = hexToArrayBuffer("55f1bfede27b6a03e0dd389478ffb01462e5c52dbbac32cf870f00af1ed9af3a"); + var msg = hexToArrayBuffer("617364666173646661736466"); + var sig = hexToArrayBuffer("2bc06c745acb8bae10fbc607ee306084d0c28e2b3bb819133392473431291fd0dfa9c7f11479996cf520730d2901267387e08d85bbf2af941590e3035a545285"); + describe("Ed25519Sign", function() { + it("computes the signature", function(done) { + implementation.Ed25519Sign(priv, msg).then(function(signature) { + assertEqualArrayBuffers(sig, signature); + done(); + }).catch(done); + }); + }); + + describe("Ed25519Verify", function() { + it("throws on bad signature", function(done) { + var badsig = sig.slice(0); + new Uint8Array(badsig).set([0], 0); + + implementation.Ed25519Verify(pub, msg, badsig).catch(function(e) { + if (e.message === 'Invalid signature') { + done(); + } else { throw e; } + }).catch(done); + }); + + it("does not throw on good signature", function(done) { + implementation.Ed25519Verify(pub, msg, sig).then(done).catch(done); + }); + }); + }); +}; + diff --git a/test/index.html b/test/index.html index b23555e3..977e9804 100644 --- a/test/index.html +++ b/test/index.html @@ -156,6 +156,7 @@ + diff --git a/test/nativeclient_test.js b/test/nativeclient_test.js index 3f6a9b8b..582bc990 100644 --- a/test/nativeclient_test.js +++ b/test/nativeclient_test.js @@ -16,81 +16,12 @@ 'use strict'; -describe("textsecure.nativeclient", function() { - var alice_bytes = hexToArrayBuffer("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a"); - var alice_priv = hexToArrayBuffer("70076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c6a"); - var alice_pub = hexToArrayBuffer("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"); - var bob_bytes = hexToArrayBuffer("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb"); - var bob_priv = hexToArrayBuffer("58ab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e06b"); - var bob_pub = hexToArrayBuffer("de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f"); - var shared_sec = hexToArrayBuffer("4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742"); - - describe("privToPub", function() { - it ('converts alice private keys to a keypair', function(done) { - textsecure.nativeclient.privToPub(alice_bytes).then(function(keypair) { - assertEqualArrayBuffers(keypair.privKey, alice_priv); - assertEqualArrayBuffers(keypair.pubKey, alice_pub); - done(); - }).catch(done); - }); - it ('converts bob private keys to a keypair', function(done) { - textsecure.nativeclient.privToPub(bob_bytes).then(function(keypair) { - assertEqualArrayBuffers(keypair.privKey, bob_priv); - assertEqualArrayBuffers(keypair.pubKey, bob_pub); - done(); - }).catch(done); - }); - }); - - describe("ECDHE", function() { - it("computes the shared secret for alice", function(done) { - textsecure.nativeclient.ECDHE(bob_pub, alice_priv).then(function(secret) { - assertEqualArrayBuffers(shared_sec, secret); - done(); - }).catch(done); - }); - it("computes the shared secret for bob", function(done) { - textsecure.nativeclient.ECDHE(alice_pub, bob_priv).then(function(secret) { - assertEqualArrayBuffers(shared_sec, secret); - done(); - }).catch(done); - }); - }); - - var priv = hexToArrayBuffer("48a8892cc4e49124b7b57d94fa15becfce071830d6449004685e387c62409973"); - var pub = hexToArrayBuffer("55f1bfede27b6a03e0dd389478ffb01462e5c52dbbac32cf870f00af1ed9af3a"); - var msg = hexToArrayBuffer("617364666173646661736466"); - var sig = hexToArrayBuffer("2bc06c745acb8bae10fbc607ee306084d0c28e2b3bb819133392473431291fd0dfa9c7f11479996cf520730d2901267387e08d85bbf2af941590e3035a545285"); - describe("Ed25519Sign", function() { - it("computes the signature", function(done) { - textsecure.nativeclient.Ed25519Sign(priv, msg).then(function(signature) { - assertEqualArrayBuffers(sig, signature); - done(); - }).catch(done); - }); - }); - - describe("Ed25519Verify", function() { - it("throws on bad signature", function(done) { - var badsig = sig.slice(0); - new Uint8Array(badsig).set([0], 0); - - textsecure.nativeclient.Ed25519Verify(pub, msg, badsig).catch(function(e) { - if (e.message === 'Invalid signature') { - done(); - } else { throw e; } - }).catch(done); - }); - - it("does not throw on good signature", function(done) { - textsecure.nativeclient.Ed25519Verify(pub, msg, sig).then(done).catch(done); - }); - }); +describe("Native Client", function() { + test_curve25519_implementation(textsecure.nativeclient); describe("registerOnLoadFunction", function() { it('queues a callback til native client is loaded', function(done) { textsecure.nativeclient.registerOnLoadFunction(done); }); }); - });