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