mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-04-02 12:32:55 +00:00
Bug 577383 - Merge fx-sync to mozilla-central [r=mconnor]
This commit is contained in:
commit
db5200bea6
@ -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]);
|
||||
|
@ -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]);
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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…
x
Reference in New Issue
Block a user