mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-11 20:35:50 +00:00
Bug 610749: add pure-JS PBKDF2 implementation.
This commit is contained in:
parent
a2b8bef32a
commit
000b5df8db
@ -159,8 +159,7 @@ CryptoMeta.prototype = {
|
||||
)
|
||||
);
|
||||
|
||||
unwrappedKey.hmacKey = Svc.KeyFactory.keyFromString(Ci.nsIKeyObject.HMAC,
|
||||
unwrappedKey);
|
||||
unwrappedKey.hmacKey = Utils.makeHMACKey(unwrappedKey);
|
||||
|
||||
// Cache the result after the first get and just return it
|
||||
return (this.getKey = function() unwrappedKey)();
|
||||
@ -193,7 +192,7 @@ CryptoMeta.prototype = {
|
||||
|
||||
get hmacKey() {
|
||||
let passphrase = ID.get("WeaveCryptoID").passwordUTF8;
|
||||
return Svc.KeyFactory.keyFromString(Ci.nsIKeyObject.HMAC, passphrase);
|
||||
return Utils.makeHMACKey(passphrase);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1151,8 +1151,7 @@ WeaveSvc.prototype = {
|
||||
}
|
||||
|
||||
// Recompute HMAC for symmetric bulk keys based on UTF-8 encoded passphrase.
|
||||
let oldHmacKey = Svc.KeyFactory.keyFromString(Ci.nsIKeyObject.HMAC,
|
||||
this.passphrase);
|
||||
let oldHmacKey = Utils.makeHMACKey(this.passphrase);
|
||||
let enginesToWipe = [];
|
||||
|
||||
for each (let engine in Engines.getAll()) {
|
||||
|
@ -481,7 +481,7 @@ let Utils = {
|
||||
exceptionStr: function Weave_exceptionStr(e) {
|
||||
let message = e.message ? e.message : e;
|
||||
return message + " " + Utils.stackTrace(e);
|
||||
},
|
||||
},
|
||||
|
||||
stackTraceFromFrame: function Weave_stackTraceFromFrame(frame) {
|
||||
let output = [];
|
||||
@ -563,16 +563,127 @@ let Utils = {
|
||||
sha1Base32: function sha1Base32(message) {
|
||||
return Utils.encodeBase32(Utils._sha1(message));
|
||||
},
|
||||
|
||||
/**
|
||||
* Produce an HMAC key object from a key string.
|
||||
*/
|
||||
makeHMACKey: function makeHMACKey(str) {
|
||||
return Svc.KeyFactory.keyFromString(Ci.nsIKeyObject.HMAC, str);
|
||||
},
|
||||
|
||||
/**
|
||||
* Produce an HMAC hasher.
|
||||
*/
|
||||
makeHMACHasher: function makeHMACHasher() {
|
||||
return Cc["@mozilla.org/security/hmac;1"]
|
||||
.createInstance(Ci.nsICryptoHMAC);
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate a sha1 HMAC for a message, not UTF-8 encoded,
|
||||
* and a given nsIKeyObject.
|
||||
* Optionally provide an existing hasher, which will be
|
||||
* initialized and reused.
|
||||
*/
|
||||
sha1HMACBytes: function sha1HMACBytes(message, key, hasher) {
|
||||
let h = hasher || this.makeHMACHasher();
|
||||
h.init(h.SHA1, key);
|
||||
|
||||
// No UTF-8 encoding for you, sunshine.
|
||||
let bytes = [b.charCodeAt() for each (b in message)];
|
||||
h.update(bytes, bytes.length);
|
||||
return h.finish(false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate a sha256 HMAC for a string message and a given nsIKeyObject
|
||||
*/
|
||||
sha256HMAC: function sha256HMAC(message, key) {
|
||||
let hasher = Cc["@mozilla.org/security/hmac;1"].
|
||||
createInstance(Ci.nsICryptoHMAC);
|
||||
let hasher = this.makeHMACHasher();
|
||||
hasher.init(hasher.SHA256, key);
|
||||
return Utils.bytesAsHex(Utils.digest(message, hasher));
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* PBKDF2 implementation in Javascript.
|
||||
*/
|
||||
/* For HMAC-SHA-1 */
|
||||
_hLen : 20,
|
||||
|
||||
_arrayToString : function _arrayToString(arr) {
|
||||
let ret = '';
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
ret += String.fromCharCode(arr[i]);
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
|
||||
_XOR : function _XOR(a, b, isA) {
|
||||
if (a.length != b.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let val = [];
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
if (isA) {
|
||||
val[i] = a[i] ^ b[i];
|
||||
} else {
|
||||
val[i] = a.charCodeAt(i) ^ b.charCodeAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
},
|
||||
|
||||
_F : function _F(PK, S, c, i, h) {
|
||||
let ret;
|
||||
let U = [];
|
||||
|
||||
/* Encode i into 4 octets: _INT */
|
||||
let I = [];
|
||||
I[0] = String.fromCharCode((i >> 24) & 0xff);
|
||||
I[1] = String.fromCharCode((i >> 16) & 0xff);
|
||||
I[2] = String.fromCharCode((i >> 8) & 0xff);
|
||||
I[3] = String.fromCharCode(i & 0xff);
|
||||
|
||||
U[0] = this.sha1HMACBytes(S + I.join(''), PK, h);
|
||||
for (let j = 1; j < c; j++) {
|
||||
U[j] = this.sha1HMACBytes(U[j - 1], PK, h);
|
||||
}
|
||||
|
||||
ret = U[0];
|
||||
for (j = 1; j < c; j++) {
|
||||
ret = this._arrayToString(this._XOR(ret, U[j]));
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
/* PKCS #5, v2.0 pp. 9-10 */
|
||||
pbkdf2Generate : function pbkdf2Generate(P, S, c, dkLen) {
|
||||
let l = Math.ceil(dkLen / this._hLen);
|
||||
let r = dkLen - ((l - 1) * this._hLen);
|
||||
|
||||
// Reuse the key and the hasher. Remaking them 4096 times is 'spensive.
|
||||
let PK = this.makeHMACKey(P);
|
||||
let h = this.makeHMACHasher();
|
||||
|
||||
T = [];
|
||||
for (let i = 0; i < l;) {
|
||||
T[i] = this._F(PK, S, c, ++i, h);
|
||||
}
|
||||
|
||||
let ret = '';
|
||||
for (i = 0; i < l-1;) {
|
||||
ret += T[i++];
|
||||
}
|
||||
ret += T[l - 1].substr(0, r);
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Base32 encode (RFC 4648) a string
|
||||
|
11
services/sync/tests/unit/test_utils_pbkdf2.js
Normal file
11
services/sync/tests/unit/test_utils_pbkdf2.js
Normal file
@ -0,0 +1,11 @@
|
||||
// Evil.
|
||||
let btoa = Cu.import("resource://services-sync/util.js").btoa;
|
||||
|
||||
function run_test() {
|
||||
let symmKey16 = Utils.pbkdf2Generate("secret phrase", "DNXPzPpiwn", 4096, 16);
|
||||
do_check_eq(symmKey16.length, 16);
|
||||
do_check_eq(btoa(symmKey16), "d2zG0d2cBfXnRwMUGyMwyg==");
|
||||
do_check_eq(Utils.encodeBase32(symmKey16), "O5WMNUO5TQC7LZ2HAMKBWIZQZI======");
|
||||
let symmKey32 = Utils.pbkdf2Generate("passphrase", "salt", 4096, 32);
|
||||
do_check_eq(symmKey32.length, 32);
|
||||
}
|
66
services/sync/tests/unit/test_utils_sha1hmac.js
Normal file
66
services/sync/tests/unit/test_utils_sha1hmac.js
Normal file
@ -0,0 +1,66 @@
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
function _hexToString(hex) {
|
||||
var ret = '';
|
||||
if (hex.length % 2 != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < hex.length; i += 2) {
|
||||
var cur = hex[i] + hex[i + 1];
|
||||
ret += String.fromCharCode(parseInt(cur, 16));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function test_sha1_hmac() {
|
||||
let test_data =
|
||||
[{test_case: 1,
|
||||
key: _hexToString("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"),
|
||||
key_len: 20,
|
||||
data: "Hi There",
|
||||
data_len: 8,
|
||||
digest: "b617318655057264e28bc0b6fb378c8ef146be00"},
|
||||
|
||||
{test_case: 2,
|
||||
key: "Jefe",
|
||||
key_len: 4,
|
||||
data: "what do ya want for nothing?",
|
||||
data_len: 28,
|
||||
digest: "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79"}];
|
||||
|
||||
let d;
|
||||
// Testing repeated hashing.
|
||||
d = test_data[0];
|
||||
do_check_eq(Utils.bytesAsHex(Utils.sha1HMACBytes(d.data, Utils.makeHMACKey(d.key))), d.digest);
|
||||
d = test_data[1];
|
||||
do_check_eq(Utils.bytesAsHex(Utils.sha1HMACBytes(d.data, Utils.makeHMACKey(d.key))), d.digest);
|
||||
d = test_data[0];
|
||||
do_check_eq(Utils.bytesAsHex(Utils.sha1HMACBytes(d.data, Utils.makeHMACKey(d.key))), d.digest);
|
||||
d = test_data[1];
|
||||
do_check_eq(Utils.bytesAsHex(Utils.sha1HMACBytes(d.data, Utils.makeHMACKey(d.key))), d.digest);
|
||||
let kk = Utils.makeHMACKey(d.key);
|
||||
do_check_eq(
|
||||
Utils.bytesAsHex(
|
||||
Utils.sha1HMACBytes(
|
||||
Utils.sha1HMACBytes(
|
||||
Utils.sha1HMACBytes(d.data, kk),
|
||||
kk),
|
||||
kk)),
|
||||
Utils.bytesAsHex(
|
||||
Utils.sha1HMACBytes(
|
||||
Utils.sha1HMACBytes(
|
||||
Utils.sha1HMACBytes(d.data, kk),
|
||||
kk),
|
||||
kk)));
|
||||
|
||||
d = test_data[0];
|
||||
kk = Utils.makeHMACKey(d.key);
|
||||
do_check_eq(Utils.bytesAsHex(Utils.sha1HMACBytes(d.data, kk)), d.digest);
|
||||
do_check_eq(Utils.bytesAsHex(Utils.sha1HMACBytes(d.data, kk)), d.digest);
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
test_sha1_hmac();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user