Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* Any copyright is dedicated to the Public Domain.
2 * http://creativecommons.org/publicdomain/zero/1.0/ */
4 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
5 Cu.import("resource://services-common/utils.js");
6 Cu.import("resource://services-crypto/utils.js");
8 function run_test() {
9 initTestLogging();
11 run_next_test();
12 }
14 add_test(function test_hawk() {
15 let compute = CryptoUtils.computeHAWK;
17 // vectors copied from the HAWK (node.js) tests
18 let credentials_sha1 = {
19 id: "123456",
20 key: "2983d45yun89q",
21 algorithm: "sha1",
22 };
24 let method = "POST";
25 let ts = 1353809207;
26 let nonce = "Ygvqdz";
27 let result;
29 let uri_http = CommonUtils.makeURI("http://example.net/somewhere/over/the/rainbow");
30 let sha1_opts = { credentials: credentials_sha1,
31 ext: "Bazinga!",
32 ts: ts,
33 nonce: nonce,
34 payload: "something to write about",
35 };
36 result = compute(uri_http, method, sha1_opts);
38 // The HAWK spec uses non-urlsafe base64 (+/) for its output MAC string.
39 do_check_eq(result.field,
40 'Hawk id="123456", ts="1353809207", nonce="Ygvqdz", ' +
41 'hash="bsvY3IfUllw6V5rvk4tStEvpBhE=", ext="Bazinga!", ' +
42 'mac="qbf1ZPG/r/e06F4ht+T77LXi5vw="'
43 );
44 do_check_eq(result.artifacts.ts, ts);
45 do_check_eq(result.artifacts.nonce, nonce);
46 do_check_eq(result.artifacts.method, method);
47 do_check_eq(result.artifacts.resource, "/somewhere/over/the/rainbow");
48 do_check_eq(result.artifacts.host, "example.net");
49 do_check_eq(result.artifacts.port, 80);
50 // artifacts.hash is the *payload* hash, not the overall request MAC.
51 do_check_eq(result.artifacts.hash, "bsvY3IfUllw6V5rvk4tStEvpBhE=");
52 do_check_eq(result.artifacts.ext, "Bazinga!");
54 let credentials_sha256 = {
55 id: "123456",
56 key: "2983d45yun89q",
57 algorithm: "sha256",
58 };
60 let uri_https = CommonUtils.makeURI("https://example.net/somewhere/over/the/rainbow");
61 let sha256_opts = { credentials: credentials_sha256,
62 ext: "Bazinga!",
63 ts: ts,
64 nonce: nonce,
65 payload: "something to write about",
66 contentType: "text/plain",
67 };
69 result = compute(uri_https, method, sha256_opts);
70 do_check_eq(result.field,
71 'Hawk id="123456", ts="1353809207", nonce="Ygvqdz", ' +
72 'hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", ' +
73 'ext="Bazinga!", ' +
74 'mac="q1CwFoSHzPZSkbIvl0oYlD+91rBUEvFk763nMjMndj8="'
75 );
76 do_check_eq(result.artifacts.ts, ts);
77 do_check_eq(result.artifacts.nonce, nonce);
78 do_check_eq(result.artifacts.method, method);
79 do_check_eq(result.artifacts.resource, "/somewhere/over/the/rainbow");
80 do_check_eq(result.artifacts.host, "example.net");
81 do_check_eq(result.artifacts.port, 443);
82 do_check_eq(result.artifacts.hash, "2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=");
83 do_check_eq(result.artifacts.ext, "Bazinga!");
85 let sha256_opts_noext = { credentials: credentials_sha256,
86 ts: ts,
87 nonce: nonce,
88 payload: "something to write about",
89 contentType: "text/plain",
90 };
91 result = compute(uri_https, method, sha256_opts_noext);
92 do_check_eq(result.field,
93 'Hawk id="123456", ts="1353809207", nonce="Ygvqdz", ' +
94 'hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", ' +
95 'mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="'
96 );
97 do_check_eq(result.artifacts.ts, ts);
98 do_check_eq(result.artifacts.nonce, nonce);
99 do_check_eq(result.artifacts.method, method);
100 do_check_eq(result.artifacts.resource, "/somewhere/over/the/rainbow");
101 do_check_eq(result.artifacts.host, "example.net");
102 do_check_eq(result.artifacts.port, 443);
103 do_check_eq(result.artifacts.hash, "2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=");
105 /* Leaving optional fields out should work, although of course then we can't
106 * assert much about the resulting hashes. The resulting header should look
107 * roughly like:
108 * Hawk id="123456", ts="1378764955", nonce="QkynqsrS44M=", mac="/C5NsoAs2fVn+d/I5wMfwe2Gr1MZyAJ6pFyDHG4Gf9U="
109 */
111 result = compute(uri_https, method, { credentials: credentials_sha256 });
112 let fields = result.field.split(" ");
113 do_check_eq(fields[0], "Hawk");
114 do_check_eq(fields[1], 'id="123456",'); // from creds.id
115 do_check_true(fields[2].startsWith('ts="'));
116 /* The HAWK spec calls for seconds-since-epoch, not ms-since-epoch.
117 * Warning: this test will fail in the year 33658, and for time travellers
118 * who journey earlier than 2001. Please plan accordingly. */
119 do_check_true(result.artifacts.ts > 1000*1000*1000);
120 do_check_true(result.artifacts.ts < 1000*1000*1000*1000);
121 do_check_true(fields[3].startsWith('nonce="'));
122 do_check_eq(fields[3].length, ('nonce="12345678901=",').length);
123 do_check_eq(result.artifacts.nonce.length, ("12345678901=").length);
125 let result2 = compute(uri_https, method, { credentials: credentials_sha256 });
126 do_check_neq(result.artifacts.nonce, result2.artifacts.nonce);
128 /* Using an upper-case URI hostname shouldn't affect the hash. */
130 let uri_https_upper = CommonUtils.makeURI("https://EXAMPLE.NET/somewhere/over/the/rainbow");
131 result = compute(uri_https_upper, method, sha256_opts);
132 do_check_eq(result.field,
133 'Hawk id="123456", ts="1353809207", nonce="Ygvqdz", ' +
134 'hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", ' +
135 'ext="Bazinga!", ' +
136 'mac="q1CwFoSHzPZSkbIvl0oYlD+91rBUEvFk763nMjMndj8="'
137 );
139 /* Using a lower-case method name shouldn't affect the hash. */
140 result = compute(uri_https_upper, method.toLowerCase(), sha256_opts);
141 do_check_eq(result.field,
142 'Hawk id="123456", ts="1353809207", nonce="Ygvqdz", ' +
143 'hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", ' +
144 'ext="Bazinga!", ' +
145 'mac="q1CwFoSHzPZSkbIvl0oYlD+91rBUEvFk763nMjMndj8="'
146 );
148 /* The localtimeOffsetMsec field should be honored. HAWK uses this to
149 * compensate for clock skew between client and server: if the request is
150 * rejected with a timestamp out-of-range error, the error includes the
151 * server's time, and the client computes its clock offset and tries again.
152 * Clients can remember this offset for a while.
153 */
155 result = compute(uri_https, method, { credentials: credentials_sha256,
156 now: 1378848968650,
157 });
158 do_check_eq(result.artifacts.ts, 1378848968);
160 result = compute(uri_https, method, { credentials: credentials_sha256,
161 now: 1378848968650,
162 localtimeOffsetMsec: 1000*1000,
163 });
164 do_check_eq(result.artifacts.ts, 1378848968 + 1000);
166 /* Search/query-args in URIs should be included in the hash. */
167 let makeURI = CommonUtils.makeURI;
168 result = compute(makeURI("http://example.net/path"), method, sha256_opts);
169 do_check_eq(result.artifacts.resource, "/path");
170 do_check_eq(result.artifacts.mac, "WyKHJjWaeYt8aJD+H9UeCWc0Y9C+07ooTmrcrOW4MPI=");
172 result = compute(makeURI("http://example.net/path/"), method, sha256_opts);
173 do_check_eq(result.artifacts.resource, "/path/");
174 do_check_eq(result.artifacts.mac, "xAYp2MgZQFvTKJT9u8nsvMjshCRRkuaeYqQbYSFp9Qw=");
176 result = compute(makeURI("http://example.net/path?query=search"), method, sha256_opts);
177 do_check_eq(result.artifacts.resource, "/path?query=search");
178 do_check_eq(result.artifacts.mac, "C06a8pip2rA4QkBiosEmC32WcgFcW/R5SQC6kUWyqho=");
180 /* Test handling of the payload, which is supposed to be a bytestring
181 (String with codepoints from U+0000 to U+00FF, pre-encoded). */
183 result = compute(makeURI("http://example.net/path"), method,
184 { credentials: credentials_sha256,
185 ts: 1353809207,
186 nonce: "Ygvqdz",
187 });
188 do_check_eq(result.artifacts.hash, undefined);
189 do_check_eq(result.artifacts.mac, "S3f8E4hAURAqJxOlsYugkPZxLoRYrClgbSQ/3FmKMbY=");
191 // Empty payload changes nothing.
192 result = compute(makeURI("http://example.net/path"), method,
193 { credentials: credentials_sha256,
194 ts: 1353809207,
195 nonce: "Ygvqdz",
196 payload: null,
197 });
198 do_check_eq(result.artifacts.hash, undefined);
199 do_check_eq(result.artifacts.mac, "S3f8E4hAURAqJxOlsYugkPZxLoRYrClgbSQ/3FmKMbY=");
201 result = compute(makeURI("http://example.net/path"), method,
202 { credentials: credentials_sha256,
203 ts: 1353809207,
204 nonce: "Ygvqdz",
205 payload: "hello",
206 });
207 do_check_eq(result.artifacts.hash, "uZJnFj0XVBA6Rs1hEvdIDf8NraM0qRNXdFbR3NEQbVA=");
208 do_check_eq(result.artifacts.mac, "pLsHHzngIn5CTJhWBtBr+BezUFvdd/IadpTp/FYVIRM=");
210 // update, utf-8 payload
211 result = compute(makeURI("http://example.net/path"), method,
212 { credentials: credentials_sha256,
213 ts: 1353809207,
214 nonce: "Ygvqdz",
215 payload: "andré@example.org", // non-ASCII
216 });
217 do_check_eq(result.artifacts.hash, "66DiyapJ0oGgj09IXWdMv8VCg9xk0PL5RqX7bNnQW2k=");
218 do_check_eq(result.artifacts.mac, "2B++3x5xfHEZbPZGDiK3IwfPZctkV4DUr2ORg1vIHvk=");
220 /* If "hash" is provided, "payload" is ignored. */
221 result = compute(makeURI("http://example.net/path"), method,
222 { credentials: credentials_sha256,
223 ts: 1353809207,
224 nonce: "Ygvqdz",
225 hash: "66DiyapJ0oGgj09IXWdMv8VCg9xk0PL5RqX7bNnQW2k=",
226 payload: "something else",
227 });
228 do_check_eq(result.artifacts.hash, "66DiyapJ0oGgj09IXWdMv8VCg9xk0PL5RqX7bNnQW2k=");
229 do_check_eq(result.artifacts.mac, "2B++3x5xfHEZbPZGDiK3IwfPZctkV4DUr2ORg1vIHvk=");
231 // the payload "hash" is also non-urlsafe base64 (+/)
232 result = compute(makeURI("http://example.net/path"), method,
233 { credentials: credentials_sha256,
234 ts: 1353809207,
235 nonce: "Ygvqdz",
236 payload: "something else",
237 });
238 do_check_eq(result.artifacts.hash, "lERFXr/IKOaAoYw+eBseDUSwmqZTX0uKZpcWLxsdzt8=");
239 do_check_eq(result.artifacts.mac, "jiZuhsac35oD7IdcblhFncBr8tJFHcwWLr8NIYWr9PQ=");
241 /* Test non-ascii hostname. HAWK (via the node.js "url" module) punycodes
242 * "ëxample.net" into "xn--xample-ova.net" before hashing. I still think
243 * punycode was a bad joke that got out of the lab and into a spec.
244 */
246 result = compute(makeURI("http://ëxample.net/path"), method,
247 { credentials: credentials_sha256,
248 ts: 1353809207,
249 nonce: "Ygvqdz",
250 });
251 do_check_eq(result.artifacts.mac, "pILiHl1q8bbNQIdaaLwAFyaFmDU70MGehFuCs3AA5M0=");
252 do_check_eq(result.artifacts.host, "xn--xample-ova.net");
254 result = compute(makeURI("http://example.net/path"), method,
255 { credentials: credentials_sha256,
256 ts: 1353809207,
257 nonce: "Ygvqdz",
258 ext: "backslash=\\ quote=\" EOF",
259 });
260 do_check_eq(result.artifacts.mac, "BEMW76lwaJlPX4E/dajF970T6+GzWvaeyLzUt8eOTOc=");
261 do_check_eq(result.field, 'Hawk id="123456", ts="1353809207", nonce="Ygvqdz", ext="backslash=\\\\ quote=\\\" EOF", mac="BEMW76lwaJlPX4E/dajF970T6+GzWvaeyLzUt8eOTOc="');
263 result = compute(makeURI("http://example.net:1234/path"), method,
264 { credentials: credentials_sha256,
265 ts: 1353809207,
266 nonce: "Ygvqdz",
267 });
268 do_check_eq(result.artifacts.mac, "6D3JSFDtozuq8QvJTNUc1JzeCfy6h5oRvlhmSTPv6LE=");
269 do_check_eq(result.field, 'Hawk id="123456", ts="1353809207", nonce="Ygvqdz", mac="6D3JSFDtozuq8QvJTNUc1JzeCfy6h5oRvlhmSTPv6LE="');
271 /* HAWK (the node.js library) uses a URL parser which stores the "port"
272 * field as a string, but makeURI() gives us an integer. So we'll diverge
273 * on ports with a leading zero. This test vector would fail on the node.js
274 * library (HAWK-1.1.1), where they get a MAC of
275 * "T+GcAsDO8GRHIvZLeepSvXLwDlFJugcZroAy9+uAtcw=". I think HAWK should be
276 * updated to do what we do here, so port="01234" should get the same hash
277 * as port="1234".
278 */
279 result = compute(makeURI("http://example.net:01234/path"), method,
280 { credentials: credentials_sha256,
281 ts: 1353809207,
282 nonce: "Ygvqdz",
283 });
284 do_check_eq(result.artifacts.mac, "6D3JSFDtozuq8QvJTNUc1JzeCfy6h5oRvlhmSTPv6LE=");
285 do_check_eq(result.field, 'Hawk id="123456", ts="1353809207", nonce="Ygvqdz", mac="6D3JSFDtozuq8QvJTNUc1JzeCfy6h5oRvlhmSTPv6LE="');
287 run_next_test();
288 });
291 add_test(function test_strip_header_attributes() {
292 let strip = CryptoUtils.stripHeaderAttributes;
294 do_check_eq(strip(undefined), "");
295 do_check_eq(strip("text/plain"), "text/plain");
296 do_check_eq(strip("TEXT/PLAIN"), "text/plain");
297 do_check_eq(strip(" text/plain "), "text/plain");
298 do_check_eq(strip("text/plain ; charset=utf-8 "), "text/plain");
300 run_next_test();
301 });