Skip to content

Commit

Permalink
Bug 943521 - Use onepw prototcol in fxa client. r=ckarlof
Browse files Browse the repository at this point in the history
  • Loading branch information
Jed Parsons committed Feb 5, 2014
1 parent efc6c3e commit ebbd34b
Show file tree
Hide file tree
Showing 11 changed files with 929 additions and 178 deletions.
85 changes: 85 additions & 0 deletions services/common/tests/unit/test_utils_convert_string.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,24 @@

Cu.import("resource://services-common/utils.js");

// A wise line of Greek verse, and the utf-8 byte encoding.
// N.b., Greek begins at utf-8 ce 91
const TEST_STR = "πόλλ' οἶδ' ἀλώπηξ, ἀλλ' ἐχῖνος ἓν μέγα";
const TEST_HEX = h("cf 80 cf 8c ce bb ce bb 27 20 ce bf e1 bc b6 ce"+
"b4 27 20 e1 bc 80 ce bb cf 8e cf 80 ce b7 ce be"+
"2c 20 e1 bc 80 ce bb ce bb 27 20 e1 bc 90 cf 87"+
"e1 bf 96 ce bd ce bf cf 82 20 e1 bc 93 ce bd 20"+
"ce bc ce ad ce b3 ce b1");
// Integer byte values for the above
const TEST_BYTES = [207,128,207,140,206,187,206,187,
39, 32,206,191,225,188,182,206,
180, 39, 32,225,188,128,206,187,
207,142,207,128,206,183,206,190,
44, 32,225,188,128,206,187,206,
187, 39, 32,225,188,144,207,135,
225,191,150,206,189,206,191,207,
130, 32,225,188,147,206,189, 32,
206,188,206,173,206,179,206,177];

function run_test() {
run_next_test();
Expand Down Expand Up @@ -53,3 +71,70 @@ add_test(function test_bad_argument() {

run_next_test();
});

add_task(function test_stringAsHex() {
do_check_eq(TEST_HEX, CommonUtils.stringAsHex(TEST_STR));
run_next_test();
});

add_task(function test_hexAsString() {
do_check_eq(TEST_STR, CommonUtils.hexAsString(TEST_HEX));
run_next_test();
});

add_task(function test_hexToBytes() {
let bytes = CommonUtils.hexToBytes(TEST_HEX);
do_check_eq(TEST_BYTES.length, bytes.length);
// Ensure that the decimal values of each byte are correct
do_check_true(arraysEqual(TEST_BYTES,
CommonUtils.stringToByteArray(bytes)));
run_next_test();
});

add_task(function test_bytesToHex() {
// Create a list of our character bytes from the reference int values
let bytes = CommonUtils.byteArrayToString(TEST_BYTES);
do_check_eq(TEST_HEX, CommonUtils.bytesAsHex(bytes));
run_next_test();
});

add_task(function test_stringToBytes() {
do_check_true(arraysEqual(TEST_BYTES,
CommonUtils.stringToByteArray(CommonUtils.stringToBytes(TEST_STR))));
run_next_test();
});

add_task(function test_stringRoundTrip() {
do_check_eq(TEST_STR,
CommonUtils.hexAsString(CommonUtils.stringAsHex(TEST_STR)));
run_next_test();
});

add_task(function test_hexRoundTrip() {
do_check_eq(TEST_HEX,
CommonUtils.stringAsHex(CommonUtils.hexAsString(TEST_HEX)));
run_next_test();
});

add_task(function test_byteArrayRoundTrip() {
do_check_true(arraysEqual(TEST_BYTES,
CommonUtils.stringToByteArray(CommonUtils.byteArrayToString(TEST_BYTES))));
run_next_test();
});

// turn formatted test vectors into normal hex strings
function h(hexStr) {
return hexStr.replace(/\s+/g, "");
}

function arraysEqual(a1, a2) {
if (a1.length !== a2.length) {
return false;
}
for (let i = 0; i < a1.length; i++) {
if (a1[i] !== a2[i]) {
return false;
}
}
return true;
}
23 changes: 18 additions & 5 deletions services/common/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,12 +196,21 @@ this.CommonUtils = {
return [String.fromCharCode(byte) for each (byte in bytes)].join("");
},

stringToByteArray: function stringToByteArray(bytesString) {
return [String.charCodeAt(byte) for each (byte in bytesString)];
},

bytesAsHex: function bytesAsHex(bytes) {
let hex = "";
for (let i = 0; i < bytes.length; i++) {
hex += ("0" + bytes[i].charCodeAt().toString(16)).slice(-2);
}
return hex;
return [("0" + bytes.charCodeAt(byte).toString(16)).slice(-2)
for (byte in bytes)].join("");
},

stringAsHex: function stringAsHex(str) {
return CommonUtils.bytesAsHex(CommonUtils.encodeUTF8(str));
},

stringToBytes: function stringToBytes(str) {
return CommonUtils.hexToBytes(CommonUtils.stringAsHex(str));
},

hexToBytes: function hexToBytes(str) {
Expand All @@ -212,6 +221,10 @@ this.CommonUtils = {
return String.fromCharCode.apply(String, bytes);
},

hexAsString: function hexAsString(hex) {
return CommonUtils.decodeUTF8(CommonUtils.hexToBytes(hex));
},

/**
* Base32 encode (RFC 4648) a string
*/
Expand Down
24 changes: 14 additions & 10 deletions services/crypto/modules/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,20 +167,24 @@ this.CryptoUtils = {
* c: the number of iterations, a positive integer: e.g., 4096
* dkLen: the length in octets of the destination
* key, a positive integer: e.g., 16
* hmacAlg: The algorithm to use for hmac
* hmacLen: The hmac length
*
* The default value of 20 for hmacLen is appropriate for SHA1. For SHA256,
* hmacLen should be 32.
*
* The output is an octet string of length dkLen, which you
* can encode as you wish.
*/
pbkdf2Generate : function pbkdf2Generate(P, S, c, dkLen) {
pbkdf2Generate : function pbkdf2Generate(P, S, c, dkLen,
hmacAlg=Ci.nsICryptoHMAC.SHA1, hmacLen=20) {

// We don't have a default in the algo itself, as NSS does.
// Use the constant.
if (!dkLen) {
dkLen = SYNC_KEY_DECODED_LENGTH;
}

/* For HMAC-SHA-1 */
const HLEN = 20;

function F(S, c, i, h) {

function XOR(a, b, isA) {
Expand Down Expand Up @@ -216,27 +220,27 @@ this.CryptoUtils = {
}

ret = U[0];
for (j = 1; j < c; j++) {
for (let j = 1; j < c; j++) {
ret = CommonUtils.byteArrayToString(XOR(ret, U[j]));
}

return ret;
}

let l = Math.ceil(dkLen / HLEN);
let r = dkLen - ((l - 1) * HLEN);
let l = Math.ceil(dkLen / hmacLen);
let r = dkLen - ((l - 1) * hmacLen);

// Reuse the key and the hasher. Remaking them 4096 times is 'spensive.
let h = CryptoUtils.makeHMACHasher(Ci.nsICryptoHMAC.SHA1,
let h = CryptoUtils.makeHMACHasher(hmacAlg,
CryptoUtils.makeHMACKey(P));

T = [];
let T = [];
for (let i = 0; i < l;) {
T[i] = F(S, c, ++i, h);
}

let ret = "";
for (i = 0; i < l-1;) {
for (let i = 0; i < l-1;) {
ret += T[i++];
}
ret += T[l - 1].substr(0, r);
Expand Down
155 changes: 153 additions & 2 deletions services/crypto/tests/unit/test_utils_pbkdf2.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,166 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */

// Evil.
let btoa = Cu.import("resource://services-common/utils.js").btoa;
// XXX until bug 937114 is fixed
Cu.importGlobalProperties(['btoa']);
Cu.import("resource://services-crypto/utils.js");
Cu.import("resource://services-common/utils.js");

let {bytesAsHex: b2h} = CommonUtils;

function run_test() {
run_next_test();
}

add_task(function test_pbkdf2() {
let symmKey16 = CryptoUtils.pbkdf2Generate("secret phrase", "DNXPzPpiwn", 4096, 16);
do_check_eq(symmKey16.length, 16);
do_check_eq(btoa(symmKey16), "d2zG0d2cBfXnRwMUGyMwyg==");
do_check_eq(CommonUtils.encodeBase32(symmKey16), "O5WMNUO5TQC7LZ2HAMKBWIZQZI======");
let symmKey32 = CryptoUtils.pbkdf2Generate("passphrase", "salt", 4096, 32);
do_check_eq(symmKey32.length, 32);
});

// http://tools.ietf.org/html/rfc6070
// PBKDF2 HMAC-SHA1 Test Vectors
add_task(function test_pbkdf2_hmac_sha1() {
let pbkdf2 = CryptoUtils.pbkdf2Generate;
let vectors = [
{P: "password", // (8 octets)
S: "salt", // (4 octets)
c: 1,
dkLen: 20,
DK: h("0c 60 c8 0f 96 1f 0e 71"+
"f3 a9 b5 24 af 60 12 06"+
"2f e0 37 a6"), // (20 octets)
},

{P: "password", // (8 octets)
S: "salt", // (4 octets)
c: 2,
dkLen: 20,
DK: h("ea 6c 01 4d c7 2d 6f 8c"+
"cd 1e d9 2a ce 1d 41 f0"+
"d8 de 89 57"), // (20 octets)
},

{P: "password", // (8 octets)
S: "salt", // (4 octets)
c: 4096,
dkLen: 20,
DK: h("4b 00 79 01 b7 65 48 9a"+
"be ad 49 d9 26 f7 21 d0"+
"65 a4 29 c1"), // (20 octets)
},

// XXX Uncomment the following test after Bug 968567 lands
//
// XXX As it stands, I estimate that the CryptoUtils implementation will
// take approximately 16 hours in my 2.3GHz MacBook to perform this many
// rounds.
//
// {P: "password", // (8 octets)
// S: "salt" // (4 octets)
// c: 16777216,
// dkLen = 20,
// DK: h("ee fe 3d 61 cd 4d a4 e4"+
// "e9 94 5b 3d 6b a2 15 8c"+
// "26 34 e9 84"), // (20 octets)
// },

{P: "passwordPASSWORDpassword", // (24 octets)
S: "saltSALTsaltSALTsaltSALTsaltSALTsalt", // (36 octets)
c: 4096,
dkLen: 25,
DK: h("3d 2e ec 4f e4 1c 84 9b"+
"80 c8 d8 36 62 c0 e4 4a"+
"8b 29 1a 96 4c f2 f0 70"+
"38"), // (25 octets)

},

{P: "pass\0word", // (9 octets)
S: "sa\0lt", // (5 octets)
c: 4096,
dkLen: 16,
DK: h("56 fa 6a a7 55 48 09 9d"+
"cc 37 d7 f0 34 25 e0 c3"), // (16 octets)
},
];

for (let v of vectors) {
do_check_eq(v.DK, b2h(pbkdf2(v.P, v.S, v.c, v.dkLen)));
}

run_next_test();
});

// I can't find any normative ietf test vectors for pbkdf2 hmac-sha256.
// The following vectors are derived with the same inputs as above (the sha1
// test). Results verified by users here:
// https://stackoverflow.com/questions/5130513/pbkdf2-hmac-sha2-test-vectors
add_task(function test_pbkdf2_hmac_sha256() {
let pbkdf2 = CryptoUtils.pbkdf2Generate;
let vectors = [
{P: "password", // (8 octets)
S: "salt", // (4 octets)
c: 1,
dkLen: 32,
DK: h("12 0f b6 cf fc f8 b3 2c"+
"43 e7 22 52 56 c4 f8 37"+
"a8 65 48 c9 2c cc 35 48"+
"08 05 98 7c b7 0b e1 7b"), // (32 octets)
},

{P: "password", // (8 octets)
S: "salt", // (4 octets)
c: 2,
dkLen: 32,
DK: h("ae 4d 0c 95 af 6b 46 d3"+
"2d 0a df f9 28 f0 6d d0"+
"2a 30 3f 8e f3 c2 51 df"+
"d6 e2 d8 5a 95 47 4c 43"), // (32 octets)
},

{P: "password", // (8 octets)
S: "salt", // (4 octets)
c: 4096,
dkLen: 32,
DK: h("c5 e4 78 d5 92 88 c8 41"+
"aa 53 0d b6 84 5c 4c 8d"+
"96 28 93 a0 01 ce 4e 11"+
"a4 96 38 73 aa 98 13 4a"), // (32 octets)
},

{P: "passwordPASSWORDpassword", // (24 octets)
S: "saltSALTsaltSALTsaltSALTsaltSALTsalt", // (36 octets)
c: 4096,
dkLen: 40,
DK: h("34 8c 89 db cb d3 2b 2f"+
"32 d8 14 b8 11 6e 84 cf"+
"2b 17 34 7e bc 18 00 18"+
"1c 4e 2a 1f b8 dd 53 e1"+
"c6 35 51 8c 7d ac 47 e9"), // (40 octets)
},

{P: "pass\0word", // (9 octets)
S: "sa\0lt", // (5 octets)
c: 4096,
dkLen: 16,
DK: h("89 b6 9d 05 16 f8 29 89"+
"3c 69 62 26 65 0a 86 87"), // (16 octets)
},
];

for (let v of vectors) {
do_check_eq(v.DK,
b2h(pbkdf2(v.P, v.S, v.c, v.dkLen, Ci.nsICryptoHMAC.SHA256, 32)));
}

run_next_test();
});

// turn formatted test vectors into normal hex strings
function h(hexStr) {
return hexStr.replace(/\s+/g, "");
}
Loading

0 comments on commit ebbd34b

Please sign in to comment.