|
1 /* Any copyright is dedicated to the Public Domain. |
|
2 * http://creativecommons.org/publicdomain/zero/1.0/ */ |
|
3 |
|
4 Cu.import("resource://gre/modules/Credentials.jsm"); |
|
5 Cu.import("resource://gre/modules/Promise.jsm"); |
|
6 Cu.import("resource://services-common/utils.js"); |
|
7 Cu.import("resource://services-crypto/utils.js"); |
|
8 |
|
9 let {hexToBytes: h2b, |
|
10 hexAsString: h2s, |
|
11 stringAsHex: s2h, |
|
12 bytesAsHex: b2h} = CommonUtils; |
|
13 |
|
14 // Test vectors for the "onepw" protocol: |
|
15 // https://github.com/mozilla/fxa-auth-server/wiki/onepw-protocol#wiki-test-vectors |
|
16 let vectors = { |
|
17 "client stretch-KDF": { |
|
18 email: |
|
19 h("616e6472c3a94065 78616d706c652e6f 7267"), |
|
20 password: |
|
21 h("70c3a4737377c3b6 7264"), |
|
22 quickStretchedPW: |
|
23 h("e4e8889bd8bd61ad 6de6b95c059d56e7 b50dacdaf62bd846 44af7e2add84345d"), |
|
24 authPW: |
|
25 h("247b675ffb4c4631 0bc87e26d712153a be5e1c90ef00a478 4594f97ef54f2375"), |
|
26 authSalt: |
|
27 h("00f0000000000000 0000000000000000 0000000000000000 0000000000000000"), |
|
28 }, |
|
29 }; |
|
30 |
|
31 // A simple test suite with no utf8 encoding madness. |
|
32 add_task(function test_onepw_setup_credentials() { |
|
33 let email = "francine@example.org"; |
|
34 let password = CommonUtils.encodeUTF8("i like pie"); |
|
35 |
|
36 let pbkdf2 = CryptoUtils.pbkdf2Generate; |
|
37 let hkdf = CryptoUtils.hkdf; |
|
38 |
|
39 // quickStretch the email |
|
40 let saltyEmail = Credentials.keyWordExtended("quickStretch", email); |
|
41 |
|
42 do_check_eq(b2h(saltyEmail), "6964656e746974792e6d6f7a696c6c612e636f6d2f7069636c2f76312f717569636b537472657463683a6672616e63696e65406578616d706c652e6f7267"); |
|
43 |
|
44 let pbkdf2Rounds = 1000; |
|
45 let pbkdf2Len = 32; |
|
46 |
|
47 let quickStretchedPW = pbkdf2(password, saltyEmail, pbkdf2Rounds, pbkdf2Len, Ci.nsICryptoHMAC.SHA256, 32); |
|
48 let quickStretchedActual = "6b88094c1c73bbf133223f300d101ed70837af48d9d2c1b6e7d38804b20cdde4"; |
|
49 do_check_eq(b2h(quickStretchedPW), quickStretchedActual); |
|
50 |
|
51 // obtain hkdf info |
|
52 let authKeyInfo = Credentials.keyWord('authPW'); |
|
53 do_check_eq(b2h(authKeyInfo), "6964656e746974792e6d6f7a696c6c612e636f6d2f7069636c2f76312f617574685057"); |
|
54 |
|
55 // derive auth password |
|
56 let hkdfSalt = h2b("00"); |
|
57 let hkdfLen = 32; |
|
58 let authPW = hkdf(quickStretchedPW, hkdfSalt, authKeyInfo, hkdfLen); |
|
59 |
|
60 do_check_eq(b2h(authPW), "4b8dec7f48e7852658163601ff766124c312f9392af6c3d4e1a247eb439be342"); |
|
61 |
|
62 // derive unwrap key |
|
63 let unwrapKeyInfo = Credentials.keyWord('unwrapBkey'); |
|
64 let unwrapKey = hkdf(quickStretchedPW, hkdfSalt, unwrapKeyInfo, hkdfLen); |
|
65 |
|
66 do_check_eq(b2h(unwrapKey), "8ff58975be391338e4ec5d7138b5ed7b65c7d1bfd1f3a4f93e05aa47d5b72be9"); |
|
67 }); |
|
68 |
|
69 add_task(function test_client_stretch_kdf() { |
|
70 let pbkdf2 = CryptoUtils.pbkdf2Generate; |
|
71 let hkdf = CryptoUtils.hkdf; |
|
72 let expected = vectors["client stretch-KDF"]; |
|
73 |
|
74 let emailUTF8 = h2s(expected.email); |
|
75 let passwordUTF8 = h2s(expected.password); |
|
76 |
|
77 // Intermediate value from sjcl implementation in fxa-js-client |
|
78 // The key thing is the c3a9 sequence in "andré" |
|
79 let salt = Credentials.keyWordExtended("quickStretch", emailUTF8); |
|
80 do_check_eq(b2h(salt), "6964656e746974792e6d6f7a696c6c612e636f6d2f7069636c2f76312f717569636b537472657463683a616e6472c3a9406578616d706c652e6f7267"); |
|
81 |
|
82 let options = { |
|
83 stretchedPassLength: 32, |
|
84 pbkdf2Rounds: 1000, |
|
85 hmacAlgorithm: Ci.nsICryptoHMAC.SHA256, |
|
86 hmacLength: 32, |
|
87 hkdfSalt: h2b("00"), |
|
88 hkdfLength: 32, |
|
89 }; |
|
90 |
|
91 let results = yield Credentials.setup(emailUTF8, passwordUTF8, options); |
|
92 |
|
93 do_check_eq(emailUTF8, results.emailUTF8, |
|
94 "emailUTF8 is wrong"); |
|
95 |
|
96 do_check_eq(passwordUTF8, results.passwordUTF8, |
|
97 "passwordUTF8 is wrong"); |
|
98 |
|
99 do_check_eq(expected.quickStretchedPW, b2h(results.quickStretchedPW), |
|
100 "quickStretchedPW is wrong"); |
|
101 |
|
102 do_check_eq(expected.authPW, b2h(results.authPW), |
|
103 "authPW is wrong"); |
|
104 }); |
|
105 |
|
106 // End of tests |
|
107 // Utility functions follow |
|
108 |
|
109 function run_test() { |
|
110 run_next_test(); |
|
111 } |
|
112 |
|
113 // turn formatted test vectors into normal hex strings |
|
114 function h(hexStr) { |
|
115 return hexStr.replace(/\s+/g, ""); |
|
116 } |