summaryrefslogtreecommitdiffstats
path: root/toolkit/identity/tests/unit/test_jwcrypto.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/identity/tests/unit/test_jwcrypto.js')
-rw-r--r--toolkit/identity/tests/unit/test_jwcrypto.js281
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();
+}