Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | /* Any copyright is dedicated to the Public Domain. |
michael@0 | 2 | http://creativecommons.org/publicdomain/zero/1.0/ */ |
michael@0 | 3 | |
michael@0 | 4 | "use strict" |
michael@0 | 5 | |
michael@0 | 6 | Cu.import('resource://gre/modules/identity/LogUtils.jsm'); |
michael@0 | 7 | |
michael@0 | 8 | XPCOMUtils.defineLazyModuleGetter(this, "IDService", |
michael@0 | 9 | "resource://gre/modules/identity/Identity.jsm", |
michael@0 | 10 | "IdentityService"); |
michael@0 | 11 | |
michael@0 | 12 | XPCOMUtils.defineLazyModuleGetter(this, "jwcrypto", |
michael@0 | 13 | "resource://gre/modules/identity/jwcrypto.jsm"); |
michael@0 | 14 | |
michael@0 | 15 | XPCOMUtils.defineLazyServiceGetter(this, |
michael@0 | 16 | "CryptoService", |
michael@0 | 17 | "@mozilla.org/identity/crypto-service;1", |
michael@0 | 18 | "nsIIdentityCryptoService"); |
michael@0 | 19 | |
michael@0 | 20 | const RP_ORIGIN = "http://123done.org"; |
michael@0 | 21 | const INTERNAL_ORIGIN = "browserid://"; |
michael@0 | 22 | |
michael@0 | 23 | const SECOND_MS = 1000; |
michael@0 | 24 | const MINUTE_MS = SECOND_MS * 60; |
michael@0 | 25 | const HOUR_MS = MINUTE_MS * 60; |
michael@0 | 26 | |
michael@0 | 27 | function test_sanity() { |
michael@0 | 28 | do_test_pending(); |
michael@0 | 29 | |
michael@0 | 30 | jwcrypto.generateKeyPair("DS160", function(err, kp) { |
michael@0 | 31 | do_check_null(err); |
michael@0 | 32 | |
michael@0 | 33 | do_test_finished(); |
michael@0 | 34 | run_next_test(); |
michael@0 | 35 | }); |
michael@0 | 36 | } |
michael@0 | 37 | |
michael@0 | 38 | function test_generate() { |
michael@0 | 39 | do_test_pending(); |
michael@0 | 40 | jwcrypto.generateKeyPair("DS160", function(err, kp) { |
michael@0 | 41 | do_check_null(err); |
michael@0 | 42 | do_check_neq(kp, null); |
michael@0 | 43 | |
michael@0 | 44 | do_test_finished(); |
michael@0 | 45 | run_next_test(); |
michael@0 | 46 | }); |
michael@0 | 47 | } |
michael@0 | 48 | |
michael@0 | 49 | function test_get_assertion() { |
michael@0 | 50 | do_test_pending(); |
michael@0 | 51 | |
michael@0 | 52 | jwcrypto.generateKeyPair( |
michael@0 | 53 | "DS160", |
michael@0 | 54 | function(err, kp) { |
michael@0 | 55 | jwcrypto.generateAssertion("fake-cert", kp, RP_ORIGIN, (err, backedAssertion) => { |
michael@0 | 56 | do_check_null(err); |
michael@0 | 57 | |
michael@0 | 58 | do_check_eq(backedAssertion.split("~").length, 2); |
michael@0 | 59 | do_check_eq(backedAssertion.split(".").length, 3); |
michael@0 | 60 | |
michael@0 | 61 | do_test_finished(); |
michael@0 | 62 | run_next_test(); |
michael@0 | 63 | }); |
michael@0 | 64 | }); |
michael@0 | 65 | } |
michael@0 | 66 | |
michael@0 | 67 | function test_rsa() { |
michael@0 | 68 | do_test_pending(); |
michael@0 | 69 | function checkRSA(err, kpo) { |
michael@0 | 70 | do_check_neq(kpo, undefined); |
michael@0 | 71 | log(kpo.serializedPublicKey); |
michael@0 | 72 | let pk = JSON.parse(kpo.serializedPublicKey); |
michael@0 | 73 | do_check_eq(pk.algorithm, "RS"); |
michael@0 | 74 | /* TODO |
michael@0 | 75 | do_check_neq(kpo.sign, null); |
michael@0 | 76 | do_check_eq(typeof kpo.sign, "function"); |
michael@0 | 77 | do_check_neq(kpo.userID, null); |
michael@0 | 78 | do_check_neq(kpo.url, null); |
michael@0 | 79 | do_check_eq(kpo.url, INTERNAL_ORIGIN); |
michael@0 | 80 | do_check_neq(kpo.exponent, null); |
michael@0 | 81 | do_check_neq(kpo.modulus, null); |
michael@0 | 82 | |
michael@0 | 83 | // TODO: should sign be async? |
michael@0 | 84 | let sig = kpo.sign("This is a message to sign"); |
michael@0 | 85 | |
michael@0 | 86 | do_check_neq(sig, null); |
michael@0 | 87 | do_check_eq(typeof sig, "string"); |
michael@0 | 88 | do_check_true(sig.length > 1); |
michael@0 | 89 | */ |
michael@0 | 90 | do_test_finished(); |
michael@0 | 91 | run_next_test(); |
michael@0 | 92 | }; |
michael@0 | 93 | |
michael@0 | 94 | jwcrypto.generateKeyPair("RS256", checkRSA); |
michael@0 | 95 | } |
michael@0 | 96 | |
michael@0 | 97 | function test_dsa() { |
michael@0 | 98 | do_test_pending(); |
michael@0 | 99 | function checkDSA(err, kpo) { |
michael@0 | 100 | do_check_neq(kpo, undefined); |
michael@0 | 101 | log(kpo.serializedPublicKey); |
michael@0 | 102 | let pk = JSON.parse(kpo.serializedPublicKey); |
michael@0 | 103 | do_check_eq(pk.algorithm, "DS"); |
michael@0 | 104 | /* TODO |
michael@0 | 105 | do_check_neq(kpo.sign, null); |
michael@0 | 106 | do_check_eq(typeof kpo.sign, "function"); |
michael@0 | 107 | do_check_neq(kpo.userID, null); |
michael@0 | 108 | do_check_neq(kpo.url, null); |
michael@0 | 109 | do_check_eq(kpo.url, INTERNAL_ORIGIN); |
michael@0 | 110 | do_check_neq(kpo.generator, null); |
michael@0 | 111 | do_check_neq(kpo.prime, null); |
michael@0 | 112 | do_check_neq(kpo.subPrime, null); |
michael@0 | 113 | do_check_neq(kpo.publicValue, null); |
michael@0 | 114 | |
michael@0 | 115 | let sig = kpo.sign("This is a message to sign"); |
michael@0 | 116 | |
michael@0 | 117 | do_check_neq(sig, null); |
michael@0 | 118 | do_check_eq(typeof sig, "string"); |
michael@0 | 119 | do_check_true(sig.length > 1); |
michael@0 | 120 | */ |
michael@0 | 121 | do_test_finished(); |
michael@0 | 122 | run_next_test(); |
michael@0 | 123 | }; |
michael@0 | 124 | |
michael@0 | 125 | jwcrypto.generateKeyPair("DS160", checkDSA); |
michael@0 | 126 | } |
michael@0 | 127 | |
michael@0 | 128 | function test_get_assertion_with_offset() { |
michael@0 | 129 | do_test_pending(); |
michael@0 | 130 | |
michael@0 | 131 | |
michael@0 | 132 | // Use an arbitrary date in the past to ensure we don't accidentally pass |
michael@0 | 133 | // this test with current dates, missing offsets, etc. |
michael@0 | 134 | let serverMsec = Date.parse("Tue Oct 31 2000 00:00:00 GMT-0800"); |
michael@0 | 135 | |
michael@0 | 136 | // local clock skew |
michael@0 | 137 | // clock is 12 hours fast; -12 hours offset must be applied |
michael@0 | 138 | let localtimeOffsetMsec = -1 * 12 * HOUR_MS; |
michael@0 | 139 | let localMsec = serverMsec - localtimeOffsetMsec; |
michael@0 | 140 | |
michael@0 | 141 | jwcrypto.generateKeyPair( |
michael@0 | 142 | "DS160", |
michael@0 | 143 | function(err, kp) { |
michael@0 | 144 | jwcrypto.generateAssertion("fake-cert", kp, RP_ORIGIN, |
michael@0 | 145 | { duration: MINUTE_MS, |
michael@0 | 146 | localtimeOffsetMsec: localtimeOffsetMsec, |
michael@0 | 147 | now: localMsec}, |
michael@0 | 148 | function(err, backedAssertion) { |
michael@0 | 149 | do_check_null(err); |
michael@0 | 150 | |
michael@0 | 151 | // properly formed |
michael@0 | 152 | let cert; |
michael@0 | 153 | let assertion; |
michael@0 | 154 | [cert, assertion] = backedAssertion.split("~"); |
michael@0 | 155 | |
michael@0 | 156 | do_check_eq(cert, "fake-cert"); |
michael@0 | 157 | do_check_eq(assertion.split(".").length, 3); |
michael@0 | 158 | |
michael@0 | 159 | let components = extractComponents(assertion); |
michael@0 | 160 | |
michael@0 | 161 | // Expiry is within two minutes, corrected for skew |
michael@0 | 162 | let exp = parseInt(components.payload.exp, 10); |
michael@0 | 163 | do_check_true(exp - serverMsec === MINUTE_MS); |
michael@0 | 164 | |
michael@0 | 165 | do_test_finished(); |
michael@0 | 166 | run_next_test(); |
michael@0 | 167 | } |
michael@0 | 168 | ); |
michael@0 | 169 | } |
michael@0 | 170 | ); |
michael@0 | 171 | } |
michael@0 | 172 | |
michael@0 | 173 | function test_assertion_lifetime() { |
michael@0 | 174 | do_test_pending(); |
michael@0 | 175 | |
michael@0 | 176 | jwcrypto.generateKeyPair( |
michael@0 | 177 | "DS160", |
michael@0 | 178 | function(err, kp) { |
michael@0 | 179 | jwcrypto.generateAssertion("fake-cert", kp, RP_ORIGIN, |
michael@0 | 180 | {duration: MINUTE_MS}, |
michael@0 | 181 | function(err, backedAssertion) { |
michael@0 | 182 | do_check_null(err); |
michael@0 | 183 | |
michael@0 | 184 | // properly formed |
michael@0 | 185 | let cert; |
michael@0 | 186 | let assertion; |
michael@0 | 187 | [cert, assertion] = backedAssertion.split("~"); |
michael@0 | 188 | |
michael@0 | 189 | do_check_eq(cert, "fake-cert"); |
michael@0 | 190 | do_check_eq(assertion.split(".").length, 3); |
michael@0 | 191 | |
michael@0 | 192 | let components = extractComponents(assertion); |
michael@0 | 193 | |
michael@0 | 194 | // Expiry is within one minute, as we specified above |
michael@0 | 195 | let exp = parseInt(components.payload.exp, 10); |
michael@0 | 196 | do_check_true(Math.abs(Date.now() - exp) > 50 * SECOND_MS); |
michael@0 | 197 | do_check_true(Math.abs(Date.now() - exp) <= MINUTE_MS); |
michael@0 | 198 | |
michael@0 | 199 | do_test_finished(); |
michael@0 | 200 | run_next_test(); |
michael@0 | 201 | } |
michael@0 | 202 | ); |
michael@0 | 203 | } |
michael@0 | 204 | ); |
michael@0 | 205 | } |
michael@0 | 206 | |
michael@0 | 207 | function test_audience_encoding_bug972582() { |
michael@0 | 208 | let audience = "i-like-pie.com"; |
michael@0 | 209 | |
michael@0 | 210 | jwcrypto.generateKeyPair( |
michael@0 | 211 | "DS160", |
michael@0 | 212 | function(err, kp) { |
michael@0 | 213 | do_check_null(err); |
michael@0 | 214 | jwcrypto.generateAssertion("fake-cert", kp, audience, |
michael@0 | 215 | function(err, backedAssertion) { |
michael@0 | 216 | do_check_null(err); |
michael@0 | 217 | |
michael@0 | 218 | let [cert, assertion] = backedAssertion.split("~"); |
michael@0 | 219 | let components = extractComponents(assertion); |
michael@0 | 220 | do_check_eq(components.payload.aud, audience); |
michael@0 | 221 | |
michael@0 | 222 | do_test_finished(); |
michael@0 | 223 | run_next_test(); |
michael@0 | 224 | } |
michael@0 | 225 | ); |
michael@0 | 226 | } |
michael@0 | 227 | ); |
michael@0 | 228 | } |
michael@0 | 229 | |
michael@0 | 230 | // End of tests |
michael@0 | 231 | // Helper function follow |
michael@0 | 232 | |
michael@0 | 233 | function extractComponents(signedObject) { |
michael@0 | 234 | if (typeof(signedObject) != 'string') { |
michael@0 | 235 | throw new Error("malformed signature " + typeof(signedObject)); |
michael@0 | 236 | } |
michael@0 | 237 | |
michael@0 | 238 | let parts = signedObject.split("."); |
michael@0 | 239 | if (parts.length != 3) { |
michael@0 | 240 | throw new Error("signed object must have three parts, this one has " + parts.length); |
michael@0 | 241 | } |
michael@0 | 242 | |
michael@0 | 243 | let headerSegment = parts[0]; |
michael@0 | 244 | let payloadSegment = parts[1]; |
michael@0 | 245 | let cryptoSegment = parts[2]; |
michael@0 | 246 | |
michael@0 | 247 | let header = JSON.parse(base64UrlDecode(headerSegment)); |
michael@0 | 248 | let payload = JSON.parse(base64UrlDecode(payloadSegment)); |
michael@0 | 249 | |
michael@0 | 250 | // Ensure well-formed header |
michael@0 | 251 | do_check_eq(Object.keys(header).length, 1); |
michael@0 | 252 | do_check_true(!!header.alg); |
michael@0 | 253 | |
michael@0 | 254 | // Ensure well-formed payload |
michael@0 | 255 | for (let field of ["exp", "aud"]) { |
michael@0 | 256 | do_check_true(!!payload[field]); |
michael@0 | 257 | } |
michael@0 | 258 | |
michael@0 | 259 | return {header: header, |
michael@0 | 260 | payload: payload, |
michael@0 | 261 | headerSegment: headerSegment, |
michael@0 | 262 | payloadSegment: payloadSegment, |
michael@0 | 263 | cryptoSegment: cryptoSegment}; |
michael@0 | 264 | }; |
michael@0 | 265 | |
michael@0 | 266 | let TESTS = [ |
michael@0 | 267 | test_sanity, |
michael@0 | 268 | test_generate, |
michael@0 | 269 | test_get_assertion, |
michael@0 | 270 | test_get_assertion_with_offset, |
michael@0 | 271 | test_assertion_lifetime, |
michael@0 | 272 | test_audience_encoding_bug972582, |
michael@0 | 273 | ]; |
michael@0 | 274 | |
michael@0 | 275 | TESTS = TESTS.concat([test_rsa, test_dsa]); |
michael@0 | 276 | |
michael@0 | 277 | TESTS.forEach(add_test); |
michael@0 | 278 | |
michael@0 | 279 | function run_test() { |
michael@0 | 280 | run_next_test(); |
michael@0 | 281 | } |