Bug 610749: add pure-JS PBKDF2 implementation.

This commit is contained in:
Richard Newman 2010-11-16 11:42:17 -08:00
parent a2b8bef32a
commit 000b5df8db
5 changed files with 194 additions and 8 deletions

View File

@ -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);
}
};

View File

@ -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()) {

View File

@ -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

View 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);
}

View 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();
}