1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/identity/tests/unit/test_jwcrypto.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,281 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 + 1.7 +"use strict" 1.8 + 1.9 +Cu.import('resource://gre/modules/identity/LogUtils.jsm'); 1.10 + 1.11 +XPCOMUtils.defineLazyModuleGetter(this, "IDService", 1.12 + "resource://gre/modules/identity/Identity.jsm", 1.13 + "IdentityService"); 1.14 + 1.15 +XPCOMUtils.defineLazyModuleGetter(this, "jwcrypto", 1.16 + "resource://gre/modules/identity/jwcrypto.jsm"); 1.17 + 1.18 +XPCOMUtils.defineLazyServiceGetter(this, 1.19 + "CryptoService", 1.20 + "@mozilla.org/identity/crypto-service;1", 1.21 + "nsIIdentityCryptoService"); 1.22 + 1.23 +const RP_ORIGIN = "http://123done.org"; 1.24 +const INTERNAL_ORIGIN = "browserid://"; 1.25 + 1.26 +const SECOND_MS = 1000; 1.27 +const MINUTE_MS = SECOND_MS * 60; 1.28 +const HOUR_MS = MINUTE_MS * 60; 1.29 + 1.30 +function test_sanity() { 1.31 + do_test_pending(); 1.32 + 1.33 + jwcrypto.generateKeyPair("DS160", function(err, kp) { 1.34 + do_check_null(err); 1.35 + 1.36 + do_test_finished(); 1.37 + run_next_test(); 1.38 + }); 1.39 +} 1.40 + 1.41 +function test_generate() { 1.42 + do_test_pending(); 1.43 + jwcrypto.generateKeyPair("DS160", function(err, kp) { 1.44 + do_check_null(err); 1.45 + do_check_neq(kp, null); 1.46 + 1.47 + do_test_finished(); 1.48 + run_next_test(); 1.49 + }); 1.50 +} 1.51 + 1.52 +function test_get_assertion() { 1.53 + do_test_pending(); 1.54 + 1.55 + jwcrypto.generateKeyPair( 1.56 + "DS160", 1.57 + function(err, kp) { 1.58 + jwcrypto.generateAssertion("fake-cert", kp, RP_ORIGIN, (err, backedAssertion) => { 1.59 + do_check_null(err); 1.60 + 1.61 + do_check_eq(backedAssertion.split("~").length, 2); 1.62 + do_check_eq(backedAssertion.split(".").length, 3); 1.63 + 1.64 + do_test_finished(); 1.65 + run_next_test(); 1.66 + }); 1.67 + }); 1.68 +} 1.69 + 1.70 +function test_rsa() { 1.71 + do_test_pending(); 1.72 + function checkRSA(err, kpo) { 1.73 + do_check_neq(kpo, undefined); 1.74 + log(kpo.serializedPublicKey); 1.75 + let pk = JSON.parse(kpo.serializedPublicKey); 1.76 + do_check_eq(pk.algorithm, "RS"); 1.77 +/* TODO 1.78 + do_check_neq(kpo.sign, null); 1.79 + do_check_eq(typeof kpo.sign, "function"); 1.80 + do_check_neq(kpo.userID, null); 1.81 + do_check_neq(kpo.url, null); 1.82 + do_check_eq(kpo.url, INTERNAL_ORIGIN); 1.83 + do_check_neq(kpo.exponent, null); 1.84 + do_check_neq(kpo.modulus, null); 1.85 + 1.86 + // TODO: should sign be async? 1.87 + let sig = kpo.sign("This is a message to sign"); 1.88 + 1.89 + do_check_neq(sig, null); 1.90 + do_check_eq(typeof sig, "string"); 1.91 + do_check_true(sig.length > 1); 1.92 +*/ 1.93 + do_test_finished(); 1.94 + run_next_test(); 1.95 + }; 1.96 + 1.97 + jwcrypto.generateKeyPair("RS256", checkRSA); 1.98 +} 1.99 + 1.100 +function test_dsa() { 1.101 + do_test_pending(); 1.102 + function checkDSA(err, kpo) { 1.103 + do_check_neq(kpo, undefined); 1.104 + log(kpo.serializedPublicKey); 1.105 + let pk = JSON.parse(kpo.serializedPublicKey); 1.106 + do_check_eq(pk.algorithm, "DS"); 1.107 +/* TODO 1.108 + do_check_neq(kpo.sign, null); 1.109 + do_check_eq(typeof kpo.sign, "function"); 1.110 + do_check_neq(kpo.userID, null); 1.111 + do_check_neq(kpo.url, null); 1.112 + do_check_eq(kpo.url, INTERNAL_ORIGIN); 1.113 + do_check_neq(kpo.generator, null); 1.114 + do_check_neq(kpo.prime, null); 1.115 + do_check_neq(kpo.subPrime, null); 1.116 + do_check_neq(kpo.publicValue, null); 1.117 + 1.118 + let sig = kpo.sign("This is a message to sign"); 1.119 + 1.120 + do_check_neq(sig, null); 1.121 + do_check_eq(typeof sig, "string"); 1.122 + do_check_true(sig.length > 1); 1.123 +*/ 1.124 + do_test_finished(); 1.125 + run_next_test(); 1.126 + }; 1.127 + 1.128 + jwcrypto.generateKeyPair("DS160", checkDSA); 1.129 +} 1.130 + 1.131 +function test_get_assertion_with_offset() { 1.132 + do_test_pending(); 1.133 + 1.134 + 1.135 + // Use an arbitrary date in the past to ensure we don't accidentally pass 1.136 + // this test with current dates, missing offsets, etc. 1.137 + let serverMsec = Date.parse("Tue Oct 31 2000 00:00:00 GMT-0800"); 1.138 + 1.139 + // local clock skew 1.140 + // clock is 12 hours fast; -12 hours offset must be applied 1.141 + let localtimeOffsetMsec = -1 * 12 * HOUR_MS; 1.142 + let localMsec = serverMsec - localtimeOffsetMsec; 1.143 + 1.144 + jwcrypto.generateKeyPair( 1.145 + "DS160", 1.146 + function(err, kp) { 1.147 + jwcrypto.generateAssertion("fake-cert", kp, RP_ORIGIN, 1.148 + { duration: MINUTE_MS, 1.149 + localtimeOffsetMsec: localtimeOffsetMsec, 1.150 + now: localMsec}, 1.151 + function(err, backedAssertion) { 1.152 + do_check_null(err); 1.153 + 1.154 + // properly formed 1.155 + let cert; 1.156 + let assertion; 1.157 + [cert, assertion] = backedAssertion.split("~"); 1.158 + 1.159 + do_check_eq(cert, "fake-cert"); 1.160 + do_check_eq(assertion.split(".").length, 3); 1.161 + 1.162 + let components = extractComponents(assertion); 1.163 + 1.164 + // Expiry is within two minutes, corrected for skew 1.165 + let exp = parseInt(components.payload.exp, 10); 1.166 + do_check_true(exp - serverMsec === MINUTE_MS); 1.167 + 1.168 + do_test_finished(); 1.169 + run_next_test(); 1.170 + } 1.171 + ); 1.172 + } 1.173 + ); 1.174 +} 1.175 + 1.176 +function test_assertion_lifetime() { 1.177 + do_test_pending(); 1.178 + 1.179 + jwcrypto.generateKeyPair( 1.180 + "DS160", 1.181 + function(err, kp) { 1.182 + jwcrypto.generateAssertion("fake-cert", kp, RP_ORIGIN, 1.183 + {duration: MINUTE_MS}, 1.184 + function(err, backedAssertion) { 1.185 + do_check_null(err); 1.186 + 1.187 + // properly formed 1.188 + let cert; 1.189 + let assertion; 1.190 + [cert, assertion] = backedAssertion.split("~"); 1.191 + 1.192 + do_check_eq(cert, "fake-cert"); 1.193 + do_check_eq(assertion.split(".").length, 3); 1.194 + 1.195 + let components = extractComponents(assertion); 1.196 + 1.197 + // Expiry is within one minute, as we specified above 1.198 + let exp = parseInt(components.payload.exp, 10); 1.199 + do_check_true(Math.abs(Date.now() - exp) > 50 * SECOND_MS); 1.200 + do_check_true(Math.abs(Date.now() - exp) <= MINUTE_MS); 1.201 + 1.202 + do_test_finished(); 1.203 + run_next_test(); 1.204 + } 1.205 + ); 1.206 + } 1.207 + ); 1.208 +} 1.209 + 1.210 +function test_audience_encoding_bug972582() { 1.211 + let audience = "i-like-pie.com"; 1.212 + 1.213 + jwcrypto.generateKeyPair( 1.214 + "DS160", 1.215 + function(err, kp) { 1.216 + do_check_null(err); 1.217 + jwcrypto.generateAssertion("fake-cert", kp, audience, 1.218 + function(err, backedAssertion) { 1.219 + do_check_null(err); 1.220 + 1.221 + let [cert, assertion] = backedAssertion.split("~"); 1.222 + let components = extractComponents(assertion); 1.223 + do_check_eq(components.payload.aud, audience); 1.224 + 1.225 + do_test_finished(); 1.226 + run_next_test(); 1.227 + } 1.228 + ); 1.229 + } 1.230 + ); 1.231 +} 1.232 + 1.233 +// End of tests 1.234 +// Helper function follow 1.235 + 1.236 +function extractComponents(signedObject) { 1.237 + if (typeof(signedObject) != 'string') { 1.238 + throw new Error("malformed signature " + typeof(signedObject)); 1.239 + } 1.240 + 1.241 + let parts = signedObject.split("."); 1.242 + if (parts.length != 3) { 1.243 + throw new Error("signed object must have three parts, this one has " + parts.length); 1.244 + } 1.245 + 1.246 + let headerSegment = parts[0]; 1.247 + let payloadSegment = parts[1]; 1.248 + let cryptoSegment = parts[2]; 1.249 + 1.250 + let header = JSON.parse(base64UrlDecode(headerSegment)); 1.251 + let payload = JSON.parse(base64UrlDecode(payloadSegment)); 1.252 + 1.253 + // Ensure well-formed header 1.254 + do_check_eq(Object.keys(header).length, 1); 1.255 + do_check_true(!!header.alg); 1.256 + 1.257 + // Ensure well-formed payload 1.258 + for (let field of ["exp", "aud"]) { 1.259 + do_check_true(!!payload[field]); 1.260 + } 1.261 + 1.262 + return {header: header, 1.263 + payload: payload, 1.264 + headerSegment: headerSegment, 1.265 + payloadSegment: payloadSegment, 1.266 + cryptoSegment: cryptoSegment}; 1.267 +}; 1.268 + 1.269 +let TESTS = [ 1.270 + test_sanity, 1.271 + test_generate, 1.272 + test_get_assertion, 1.273 + test_get_assertion_with_offset, 1.274 + test_assertion_lifetime, 1.275 + test_audience_encoding_bug972582, 1.276 +]; 1.277 + 1.278 +TESTS = TESTS.concat([test_rsa, test_dsa]); 1.279 + 1.280 +TESTS.forEach(add_test); 1.281 + 1.282 +function run_test() { 1.283 + run_next_test(); 1.284 +}