mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-05 03:54:35 +00:00
Bug 558963, part 2, UTF-8 encode passphrase and rewrap keys if needed, r=mconnor
This commit is contained in:
parent
138373b089
commit
c4e977df64
@ -121,18 +121,22 @@ CryptoMeta.prototype = {
|
||||
__proto__: WBORecord.prototype,
|
||||
_logName: "Record.CryptoMeta",
|
||||
|
||||
getKey: function CryptoMeta_getKey(privkey, passphrase) {
|
||||
getWrappedKey: function _getWrappedKey(privkey) {
|
||||
// get the uri to our public key
|
||||
let pubkeyUri = privkey.publicKeyUri.spec;
|
||||
|
||||
// each hash key is a relative uri, resolve those and match against ours
|
||||
let wrapped_key;
|
||||
for (let relUri in this.keyring) {
|
||||
if (pubkeyUri == this.baseUri.resolve(relUri))
|
||||
wrapped_key = this.keyring[relUri];
|
||||
return this.keyring[relUri];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
getKey: function CryptoMeta_getKey(privkey, passphrase) {
|
||||
let wrapped_key = this.getWrappedKey(privkey);
|
||||
if (!wrapped_key)
|
||||
throw "keyring doesn't contain a key for " + pubkeyUri;
|
||||
throw "keyring doesn't contain a key for " + privkey.publicKeyUri.spec;
|
||||
|
||||
// Make sure the wrapped key hasn't been tampered with
|
||||
let localHMAC = Utils.sha256HMAC(wrapped_key.wrapped, this.hmacKey);
|
||||
@ -144,7 +148,7 @@ CryptoMeta.prototype = {
|
||||
Svc.Crypto.unwrapSymmetricKey(
|
||||
wrapped_key.wrapped,
|
||||
privkey.keyData,
|
||||
passphrase.password,
|
||||
passphrase.passwordUTF8,
|
||||
privkey.salt,
|
||||
privkey.iv
|
||||
)
|
||||
@ -170,7 +174,7 @@ CryptoMeta.prototype = {
|
||||
// each hash key is a relative uri, resolve those and
|
||||
// if we find the one we're about to add, remove it
|
||||
for (let relUri in this.keyring) {
|
||||
if (pubkeyUri == this.uri.resolve(relUri))
|
||||
if (new_pubkey.uri.spec == this.uri.resolve(relUri))
|
||||
delete this.keyring[relUri];
|
||||
}
|
||||
|
||||
@ -183,7 +187,7 @@ CryptoMeta.prototype = {
|
||||
},
|
||||
|
||||
get hmacKey() {
|
||||
let passphrase = ID.get("WeaveCryptoID").password;
|
||||
let passphrase = ID.get("WeaveCryptoID").passwordUTF8;
|
||||
return Svc.KeyFactory.keyFromString(Ci.nsIKeyObject.HMAC, passphrase);
|
||||
}
|
||||
};
|
||||
|
@ -144,7 +144,8 @@ PubKeyManager.prototype = {
|
||||
privkey.iv = Svc.Crypto.generateRandomIV();
|
||||
|
||||
let pub = {}, priv = {};
|
||||
Svc.Crypto.generateKeypair(passphrase.password, privkey.salt, privkey.iv, pub, priv);
|
||||
Svc.Crypto.generateKeypair(passphrase.passwordUTF8, privkey.salt,
|
||||
privkey.iv, pub, priv);
|
||||
[pubkey.keyData, privkey.keyData] = [pub.value, priv.value];
|
||||
|
||||
if (pubkeyUri) {
|
||||
|
@ -79,18 +79,31 @@ function Identity(realm, username, password) {
|
||||
this.realm = realm;
|
||||
this.username = username;
|
||||
this._password = password;
|
||||
if (password)
|
||||
this._passwordUTF8 = Utils.encodeUTF8(password);
|
||||
}
|
||||
Identity.prototype = {
|
||||
get password() {
|
||||
// Look up the password then cache it
|
||||
if (this._password == null)
|
||||
for each (let login in this._logins)
|
||||
if (login.username.toLowerCase() == this.username)
|
||||
if (login.username.toLowerCase() == this.username) {
|
||||
this._password = login.password;
|
||||
this._passwordUTF8 = Utils.encodeUTF8(login.password);
|
||||
}
|
||||
return this._password;
|
||||
},
|
||||
|
||||
set password(value) this._password = value,
|
||||
set password(value) {
|
||||
this._password = value;
|
||||
this._passwordUTF8 = Utils.encodeUTF8(value);
|
||||
},
|
||||
|
||||
get passwordUTF8() {
|
||||
if (!this._passwordUTF8)
|
||||
this.password; // invoke password getter
|
||||
return this._passwordUTF8;
|
||||
},
|
||||
|
||||
persist: function persist() {
|
||||
// Clean up any existing passwords unless it's what we're persisting
|
||||
|
@ -131,6 +131,7 @@ WeaveSvc.prototype = {
|
||||
|
||||
get passphrase() ID.get("WeaveCryptoID").password,
|
||||
set passphrase(value) ID.get("WeaveCryptoID").password = value,
|
||||
get passphraseUTF8() ID.get("WeaveCryptoID").passwordUTF8,
|
||||
|
||||
get serverURL() Svc.Prefs.get("serverURL"),
|
||||
set serverURL(value) {
|
||||
@ -609,10 +610,22 @@ WeaveSvc.prototype = {
|
||||
try {
|
||||
let pubkey = PubKeys.getDefaultKey();
|
||||
let privkey = PrivKeys.get(pubkey.privateKeyUri);
|
||||
return Svc.Crypto.verifyPassphrase(
|
||||
let result = Svc.Crypto.verifyPassphrase(
|
||||
privkey.payload.keyData, this.passphraseUTF8,
|
||||
privkey.payload.salt, privkey.payload.iv
|
||||
);
|
||||
if (result)
|
||||
return true;
|
||||
|
||||
// Passphrase validation failed. Perhaps because the keys are
|
||||
// based on an old low-byte only passphrase?
|
||||
result = Svc.Crypto.verifyPassphrase(
|
||||
privkey.payload.keyData, this.passphrase,
|
||||
privkey.payload.salt, privkey.payload.iv
|
||||
);
|
||||
if (result)
|
||||
this._needUpdatedKeys = true;
|
||||
return result;
|
||||
} catch (e) {
|
||||
// this means no keys are present (or there's a network error)
|
||||
return true;
|
||||
@ -1049,6 +1062,9 @@ WeaveSvc.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._needUpdatedKeys)
|
||||
this._updateKeysToUTF8Passphrase();
|
||||
|
||||
// Only set the wait time to 0 if we need to sync right away
|
||||
let wait;
|
||||
if (this.globalScore > this.syncThreshold) {
|
||||
@ -1058,6 +1074,62 @@ WeaveSvc.prototype = {
|
||||
this._scheduleNextSync(wait);
|
||||
},
|
||||
|
||||
_updateKeysToUTF8Passphrase: function _updateKeysToUTF8Passphrase() {
|
||||
// Rewrap private key in UTF-8 encoded passphrase.
|
||||
let pubkey = PubKeys.getDefaultKey();
|
||||
let privkey = PrivKeys.get(pubkey.privateKeyUri);
|
||||
|
||||
this._log.debug("Rewrapping private key with UTF-8 encoded passphrase.");
|
||||
let oldPrivKeyData = privkey.payload.keyData;
|
||||
privkey.payload.keyData = Svc.Crypto.rewrapPrivateKey(
|
||||
oldPrivKeyData, this.passphrase,
|
||||
privkey.payload.salt, privkey.payload.iv, this.passphraseUTF8
|
||||
);
|
||||
let response = new Resource(privkey.uri).put(privkey);
|
||||
if (!response.success) {
|
||||
this._log("Uploading rewrapped private key failed!");
|
||||
this._needUpdatedKeys = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Recompute HMAC for symmetric bulk keys based on UTF-8 encoded passphrase.
|
||||
let oldHmacKey = Svc.KeyFactory.keyFromString(Ci.nsIKeyObject.HMAC,
|
||||
this.passphrase);
|
||||
let enginesToWipe = [];
|
||||
|
||||
for each (let engine in Engines.getAll()) {
|
||||
let meta = CryptoMetas.get(engine.cryptoMetaURL);
|
||||
if (!meta)
|
||||
continue;
|
||||
|
||||
this._log.debug("Recomputing HMAC for key at " + engine.cryptoMetaURL
|
||||
+ " with UTF-8 encoded passphrase.");
|
||||
for each (key in meta.keyring) {
|
||||
if (key.hmac != Utils.sha256HMAC(key.wrapped, oldHmacKey)) {
|
||||
this._log.debug("Key SHA256 HMAC mismatch! Wiping server.");
|
||||
enginesToWipe.push(engine.name);
|
||||
meta = null;
|
||||
break;
|
||||
}
|
||||
key.hmac = Utils.sha256HMAC(key.wrapped, meta.hmacKey);
|
||||
}
|
||||
|
||||
if (!meta)
|
||||
continue;
|
||||
|
||||
response = new Resource(meta.uri).put(meta);
|
||||
if (!response.success) {
|
||||
this._log.debug("Key upload failed: " + response);
|
||||
}
|
||||
}
|
||||
|
||||
if (enginesToWipe.length) {
|
||||
this._log.debug("Wiping engines " + enginesToWipe.join(", "));
|
||||
this.wipeRemote(enginesToWipe);
|
||||
}
|
||||
this._needUpdatedKeys = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Call sync() on an idle timer
|
||||
*
|
||||
|
@ -4,7 +4,9 @@ try {
|
||||
Cu.import("resource://services-sync/log4moz.js");
|
||||
Cu.import("resource://services-sync/identity.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
} catch (e) { do_throw(e); }
|
||||
} catch (e) {
|
||||
do_throw(e);
|
||||
}
|
||||
|
||||
function pubkey_handler(metadata, response) {
|
||||
let obj = {id: "asdf-1234-asdf-1234",
|
||||
@ -24,7 +26,7 @@ function privkey_handler(metadata, response) {
|
||||
return httpd_basic_auth_handler(JSON.stringify(obj), metadata, response);
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
function test_get() {
|
||||
let server;
|
||||
|
||||
try {
|
||||
@ -56,3 +58,35 @@ function run_test() {
|
||||
catch (e) { do_throw(e); }
|
||||
finally { server.stop(function() {}); }
|
||||
}
|
||||
|
||||
|
||||
function test_createKeypair() {
|
||||
let passphrase = "moneyislike$\u20ac\u00a5\u5143";
|
||||
let id = ID.set('foo', new Identity('foo', 'luser'));
|
||||
id.password = passphrase;
|
||||
|
||||
_("Generate a key pair.");
|
||||
let result = PubKeys.createKeypair(id, "http://pub/key", "http://priv/key");
|
||||
|
||||
_("Check that salt and IV are of correct length.");
|
||||
// 16 bytes = 24 base64 encoded characters
|
||||
do_check_eq(result.privkey.salt.length, 24);
|
||||
do_check_eq(result.privkey.iv.length, 24);
|
||||
|
||||
_("URIs are set.");
|
||||
do_check_eq(result.pubkey.uri.spec, "http://pub/key");
|
||||
do_check_eq(result.pubkey.privateKeyUri.spec, "http://priv/key");
|
||||
do_check_eq(result.privkey.uri.spec, "http://priv/key");
|
||||
do_check_eq(result.privkey.publicKeyUri.spec, "http://pub/key");
|
||||
|
||||
_("UTF8 encoded passphrase was used.");
|
||||
do_check_true(Svc.Crypto.verifyPassphrase(result.privkey.keyData,
|
||||
Utils.encodeUTF8(passphrase),
|
||||
result.privkey.salt,
|
||||
result.privkey.payload.iv));
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
test_get();
|
||||
test_createKeypair();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user