Bug 577383 - Merge fx-sync to mozilla-central [r=mconnor]

This commit is contained in:
Paul O’Shannessy 2010-07-07 17:39:40 -07:00
commit db5200bea6
10 changed files with 191 additions and 26 deletions

View File

@ -51,6 +51,8 @@ function WeaveCrypto() {
}
WeaveCrypto.prototype = {
classDescription: "WeaveCrypto",
contractID: "@labs.mozilla.com/Weave/Crypto;2",
classID: Components.ID("{7fa20841-c90e-4432-a1a1-ba3b20cb6b37}"),
QueryInterface: XPCOMUtils.generateQI([Ci.IWeaveCrypto]),
@ -1123,4 +1125,12 @@ WeaveCrypto.prototype = {
}
};
const NSGetFactory = XPCOMUtils.generateNSGetFactory([WeaveCrypto]);
// Gecko <2.0
let component = typeof Services == "undefined" || typeof ctypes == "undefined" ? [] : [WeaveCrypto];
function NSGetModule (compMgr, fileSpec) {
return XPCOMUtils.generateModule(component);
}
// Gecko >=2.0
if (typeof XPCOMUtils.generateNSGetFactory == "function")
const NSGetFactory = XPCOMUtils.generateNSGetFactory([WeaveCrypto]);

View File

@ -55,5 +55,9 @@ FormNotifier.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFormHistory2]),
};
let components = [FormNotifier];
const NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
// Gecko <2.0
function NSGetModule(compMgr, fileSpec) XPCOMUtils.generateModule([FormNotifier]);
// Gecko >=2.0
if (typeof XPCOMUtils.generateNSGetFactory == "function")
const NSGetFactory = XPCOMUtils.generateNSGetFactory([FormNotifier]);

View File

@ -80,7 +80,7 @@ WeaveService.prototype = {
if (resProt.hasSubstitution("services-sync"))
return;
let uri = ioService.newURI("resource://gre/modules/services-sync",
let uri = ioService.newURI("resource://gre/modules/services-sync/",
null, null);
resProt.setSubstitution("services-sync", uri);
}
@ -138,10 +138,13 @@ AboutWeaveLog1.prototype = {
}
};
let components = [WeaveService, AboutWeaveLog, AboutWeaveLog1];
// Gecko <2.0
function NSGetModule(compMgr, fileSpec) {
return XPCOMUtils.generateModule([
WeaveService,
AboutWeaveLog,
AboutWeaveLog1
]);
return XPCOMUtils.generateModule(components);
}
// Gecko >=2.0
if (typeof XPCOMUtils.generateNSGetFactory == "function")
const NSGetFactory = XPCOMUtils.generateNSGetFactory(components);

View File

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

View File

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

View File

@ -108,10 +108,10 @@ HistoryStore.prototype = {
this._log.trace("Creating SQL statement: _visitStm");
let stm = this._db.createStatement(
"SELECT visit_type type, visit_date date " +
"FROM moz_historyvisits " +
"FROM moz_historyvisits_view " +
"WHERE place_id = (" +
"SELECT id " +
"FROM moz_places " +
"FROM moz_places_view " +
"WHERE url = :url) " +
"ORDER BY date DESC LIMIT 10");
this.__defineGetter__("_visitStm", function() stm);
@ -122,7 +122,7 @@ HistoryStore.prototype = {
this._log.trace("Creating SQL statement: _urlStm");
let stm = this._db.createStatement(
"SELECT url, title, frecency " +
"FROM moz_places " +
"FROM moz_places_view " +
"WHERE id = (" +
"SELECT place_id " +
"FROM moz_annos " +
@ -138,7 +138,7 @@ HistoryStore.prototype = {
this._log.trace("Creating SQL statement: _allUrlStm");
let stm = this._db.createStatement(
"SELECT url " +
"FROM moz_places " +
"FROM moz_places_view " +
"WHERE last_visit_date > :cutoff_date " +
"ORDER BY frecency DESC " +
"LIMIT :max_results");

View File

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

View File

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

View File

@ -742,6 +742,30 @@ let Utils = {
return ret;
},
encodeUTF8: function(str) {
try {
var unicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Ci.nsIScriptableUnicodeConverter);
unicodeConverter.charset = "UTF-8";
str = unicodeConverter.ConvertFromUnicode(str);
return str + unicodeConverter.Finish();
} catch(ex) {
return null;
}
},
decodeUTF8: function(str) {
try {
var unicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Ci.nsIScriptableUnicodeConverter);
unicodeConverter.charset = "UTF-8";
str = unicodeConverter.ConvertToUnicode(str);
return str + unicodeConverter.Finish();
} catch(ex) {
return null;
}
},
/**
* Create an array like the first but without elements of the second
*/

View File

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