diff options
Diffstat (limited to 'toolkit/identity/tests/unit/test_jwcrypto.js')
-rw-r--r-- | toolkit/identity/tests/unit/test_jwcrypto.js | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/toolkit/identity/tests/unit/test_jwcrypto.js b/toolkit/identity/tests/unit/test_jwcrypto.js new file mode 100644 index 000000000..f8fe82453 --- /dev/null +++ b/toolkit/identity/tests/unit/test_jwcrypto.js @@ -0,0 +1,281 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict" + +Cu.import('resource://gre/modules/identity/LogUtils.jsm'); + +XPCOMUtils.defineLazyModuleGetter(this, "IDService", + "resource://gre/modules/identity/Identity.jsm", + "IdentityService"); + +XPCOMUtils.defineLazyModuleGetter(this, "jwcrypto", + "resource://gre/modules/identity/jwcrypto.jsm"); + +XPCOMUtils.defineLazyServiceGetter(this, + "CryptoService", + "@mozilla.org/identity/crypto-service;1", + "nsIIdentityCryptoService"); + +const RP_ORIGIN = "http://123done.org"; +const INTERNAL_ORIGIN = "browserid://"; + +const SECOND_MS = 1000; +const MINUTE_MS = SECOND_MS * 60; +const HOUR_MS = MINUTE_MS * 60; + +function test_sanity() { + do_test_pending(); + + jwcrypto.generateKeyPair("DS160", function(err, kp) { + do_check_null(err); + + do_test_finished(); + run_next_test(); + }); +} + +function test_generate() { + do_test_pending(); + jwcrypto.generateKeyPair("DS160", function(err, kp) { + do_check_null(err); + do_check_neq(kp, null); + + do_test_finished(); + run_next_test(); + }); +} + +function test_get_assertion() { + do_test_pending(); + + jwcrypto.generateKeyPair( + "DS160", + function(err, kp) { + jwcrypto.generateAssertion("fake-cert", kp, RP_ORIGIN, (err2, backedAssertion) => { + do_check_null(err2); + + do_check_eq(backedAssertion.split("~").length, 2); + do_check_eq(backedAssertion.split(".").length, 3); + + do_test_finished(); + run_next_test(); + }); + }); +} + +function test_rsa() { + do_test_pending(); + function checkRSA(err, kpo) { + do_check_neq(kpo, undefined); + log(kpo.serializedPublicKey); + let pk = JSON.parse(kpo.serializedPublicKey); + do_check_eq(pk.algorithm, "RS"); +/* TODO + do_check_neq(kpo.sign, null); + do_check_eq(typeof kpo.sign, "function"); + do_check_neq(kpo.userID, null); + do_check_neq(kpo.url, null); + do_check_eq(kpo.url, INTERNAL_ORIGIN); + do_check_neq(kpo.exponent, null); + do_check_neq(kpo.modulus, null); + + // TODO: should sign be async? + let sig = kpo.sign("This is a message to sign"); + + do_check_neq(sig, null); + do_check_eq(typeof sig, "string"); + do_check_true(sig.length > 1); +*/ + do_test_finished(); + run_next_test(); + } + + jwcrypto.generateKeyPair("RS256", checkRSA); +} + +function test_dsa() { + do_test_pending(); + function checkDSA(err, kpo) { + do_check_neq(kpo, undefined); + log(kpo.serializedPublicKey); + let pk = JSON.parse(kpo.serializedPublicKey); + do_check_eq(pk.algorithm, "DS"); +/* TODO + do_check_neq(kpo.sign, null); + do_check_eq(typeof kpo.sign, "function"); + do_check_neq(kpo.userID, null); + do_check_neq(kpo.url, null); + do_check_eq(kpo.url, INTERNAL_ORIGIN); + do_check_neq(kpo.generator, null); + do_check_neq(kpo.prime, null); + do_check_neq(kpo.subPrime, null); + do_check_neq(kpo.publicValue, null); + + let sig = kpo.sign("This is a message to sign"); + + do_check_neq(sig, null); + do_check_eq(typeof sig, "string"); + do_check_true(sig.length > 1); +*/ + do_test_finished(); + run_next_test(); + } + + jwcrypto.generateKeyPair("DS160", checkDSA); +} + +function test_get_assertion_with_offset() { + do_test_pending(); + + + // Use an arbitrary date in the past to ensure we don't accidentally pass + // this test with current dates, missing offsets, etc. + let serverMsec = Date.parse("Tue Oct 31 2000 00:00:00 GMT-0800"); + + // local clock skew + // clock is 12 hours fast; -12 hours offset must be applied + let localtimeOffsetMsec = -1 * 12 * HOUR_MS; + let localMsec = serverMsec - localtimeOffsetMsec; + + jwcrypto.generateKeyPair( + "DS160", + function(err, kp) { + jwcrypto.generateAssertion("fake-cert", kp, RP_ORIGIN, + { duration: MINUTE_MS, + localtimeOffsetMsec: localtimeOffsetMsec, + now: localMsec}, + function(err2, backedAssertion) { + do_check_null(err2); + + // properly formed + let cert; + let assertion; + [cert, assertion] = backedAssertion.split("~"); + + do_check_eq(cert, "fake-cert"); + do_check_eq(assertion.split(".").length, 3); + + let components = extractComponents(assertion); + + // Expiry is within two minutes, corrected for skew + let exp = parseInt(components.payload.exp, 10); + do_check_true(exp - serverMsec === MINUTE_MS); + + do_test_finished(); + run_next_test(); + } + ); + } + ); +} + +function test_assertion_lifetime() { + do_test_pending(); + + jwcrypto.generateKeyPair( + "DS160", + function(err, kp) { + jwcrypto.generateAssertion("fake-cert", kp, RP_ORIGIN, + {duration: MINUTE_MS}, + function(err2, backedAssertion) { + do_check_null(err2); + + // properly formed + let cert; + let assertion; + [cert, assertion] = backedAssertion.split("~"); + + do_check_eq(cert, "fake-cert"); + do_check_eq(assertion.split(".").length, 3); + + let components = extractComponents(assertion); + + // Expiry is within one minute, as we specified above + let exp = parseInt(components.payload.exp, 10); + do_check_true(Math.abs(Date.now() - exp) > 50 * SECOND_MS); + do_check_true(Math.abs(Date.now() - exp) <= MINUTE_MS); + + do_test_finished(); + run_next_test(); + } + ); + } + ); +} + +function test_audience_encoding_bug972582() { + let audience = "i-like-pie.com"; + + jwcrypto.generateKeyPair( + "DS160", + function(err, kp) { + do_check_null(err); + jwcrypto.generateAssertion("fake-cert", kp, audience, + function(err2, backedAssertion) { + do_check_null(err2); + + let [cert, assertion] = backedAssertion.split("~"); + let components = extractComponents(assertion); + do_check_eq(components.payload.aud, audience); + + do_test_finished(); + run_next_test(); + } + ); + } + ); +} + +// End of tests +// Helper function follow + +function extractComponents(signedObject) { + if (typeof(signedObject) != 'string') { + throw new Error("malformed signature " + typeof(signedObject)); + } + + let parts = signedObject.split("."); + if (parts.length != 3) { + throw new Error("signed object must have three parts, this one has " + parts.length); + } + + let headerSegment = parts[0]; + let payloadSegment = parts[1]; + let cryptoSegment = parts[2]; + + let header = JSON.parse(base64UrlDecode(headerSegment)); + let payload = JSON.parse(base64UrlDecode(payloadSegment)); + + // Ensure well-formed header + do_check_eq(Object.keys(header).length, 1); + do_check_true(!!header.alg); + + // Ensure well-formed payload + for (let field of ["exp", "aud"]) { + do_check_true(!!payload[field]); + } + + return {header: header, + payload: payload, + headerSegment: headerSegment, + payloadSegment: payloadSegment, + cryptoSegment: cryptoSegment}; +} + +var TESTS = [ + test_sanity, + test_generate, + test_get_assertion, + test_get_assertion_with_offset, + test_assertion_lifetime, + test_audience_encoding_bug972582, +]; + +TESTS = TESTS.concat([test_rsa, test_dsa]); + +TESTS.forEach(add_test); + +function run_test() { + run_next_test(); +} |