2012-03-23 02:49:50 +04:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
2012-10-31 20:13:28 +04:00
|
|
|
this.EXPORTED_SYMBOLS = [
|
2012-03-23 02:49:50 +04:00
|
|
|
"BulkKeyBundle",
|
|
|
|
"SyncKeyBundle"
|
|
|
|
];
|
|
|
|
|
|
|
|
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
|
|
|
|
|
|
|
Cu.import("resource://services-sync/constants.js");
|
2012-04-06 10:26:06 +04:00
|
|
|
Cu.import("resource://services-common/log4moz.js");
|
2012-03-23 02:49:50 +04:00
|
|
|
Cu.import("resource://services-sync/util.js");
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Represents a pair of keys.
|
|
|
|
*
|
|
|
|
* Each key stored in a key bundle is 256 bits. One key is used for symmetric
|
|
|
|
* encryption. The other is used for HMAC.
|
|
|
|
*
|
|
|
|
* A KeyBundle by itself is just an anonymous pair of keys. Other types
|
|
|
|
* deriving from this one add semantics, such as associated collections or
|
|
|
|
* generating a key bundle via HKDF from another key.
|
|
|
|
*/
|
|
|
|
function KeyBundle() {
|
|
|
|
this._encrypt = null;
|
|
|
|
this._encryptB64 = null;
|
|
|
|
this._hmac = null;
|
|
|
|
this._hmacB64 = null;
|
|
|
|
this._hmacObj = null;
|
|
|
|
this._sha256HMACHasher = null;
|
|
|
|
}
|
|
|
|
KeyBundle.prototype = {
|
|
|
|
_encrypt: null,
|
|
|
|
_encryptB64: null,
|
|
|
|
_hmac: null,
|
|
|
|
_hmacB64: null,
|
|
|
|
_hmacObj: null,
|
|
|
|
_sha256HMACHasher: null,
|
|
|
|
|
|
|
|
equals: function equals(bundle) {
|
|
|
|
return bundle &&
|
|
|
|
(bundle.hmacKey == this.hmacKey) &&
|
|
|
|
(bundle.encryptionKey == this.encryptionKey);
|
|
|
|
},
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Accessors for the two keys.
|
|
|
|
*/
|
|
|
|
get encryptionKey() {
|
|
|
|
return this._encrypt;
|
|
|
|
},
|
|
|
|
|
|
|
|
set encryptionKey(value) {
|
|
|
|
if (!value || typeof value != "string") {
|
|
|
|
throw new Error("Encryption key can only be set to string values.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (value.length < 16) {
|
|
|
|
throw new Error("Encryption key must be at least 128 bits long.");
|
|
|
|
}
|
|
|
|
|
|
|
|
this._encrypt = value;
|
|
|
|
this._encryptB64 = btoa(value);
|
|
|
|
},
|
|
|
|
|
|
|
|
get encryptionKeyB64() {
|
|
|
|
return this._encryptB64;
|
|
|
|
},
|
|
|
|
|
|
|
|
get hmacKey() {
|
|
|
|
return this._hmac;
|
|
|
|
},
|
|
|
|
|
|
|
|
set hmacKey(value) {
|
|
|
|
if (!value || typeof value != "string") {
|
|
|
|
throw new Error("HMAC key can only be set to string values.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (value.length < 16) {
|
|
|
|
throw new Error("HMAC key must be at least 128 bits long.");
|
|
|
|
}
|
|
|
|
|
|
|
|
this._hmac = value;
|
|
|
|
this._hmacB64 = btoa(value);
|
|
|
|
this._hmacObj = value ? Utils.makeHMACKey(value) : null;
|
|
|
|
this._sha256HMACHasher = value ? Utils.makeHMACHasher(
|
|
|
|
Ci.nsICryptoHMAC.SHA256, this._hmacObj) : null;
|
|
|
|
},
|
|
|
|
|
|
|
|
get hmacKeyB64() {
|
|
|
|
return this._hmacB64;
|
|
|
|
},
|
|
|
|
|
|
|
|
get hmacKeyObject() {
|
|
|
|
return this._hmacObj;
|
|
|
|
},
|
|
|
|
|
|
|
|
get sha256HMACHasher() {
|
|
|
|
return this._sha256HMACHasher;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Populate this key pair with 2 new, randomly generated keys.
|
|
|
|
*/
|
|
|
|
generateRandom: function generateRandom() {
|
|
|
|
let generatedHMAC = Svc.Crypto.generateRandomKey();
|
|
|
|
let generatedEncr = Svc.Crypto.generateRandomKey();
|
|
|
|
this.keyPairB64 = [generatedEncr, generatedHMAC];
|
|
|
|
},
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Represents a KeyBundle associated with a collection.
|
|
|
|
*
|
|
|
|
* This is just a KeyBundle with a collection attached.
|
|
|
|
*/
|
2012-10-31 20:13:28 +04:00
|
|
|
this.BulkKeyBundle = function BulkKeyBundle(collection) {
|
2012-03-23 02:49:50 +04:00
|
|
|
let log = Log4Moz.repository.getLogger("Sync.BulkKeyBundle");
|
|
|
|
log.info("BulkKeyBundle being created for " + collection);
|
|
|
|
KeyBundle.call(this);
|
|
|
|
|
|
|
|
this._collection = collection;
|
|
|
|
}
|
|
|
|
|
|
|
|
BulkKeyBundle.prototype = {
|
|
|
|
__proto__: KeyBundle.prototype,
|
|
|
|
|
|
|
|
get collection() {
|
|
|
|
return this._collection;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Obtain the key pair in this key bundle.
|
|
|
|
*
|
|
|
|
* The returned keys are represented as raw byte strings.
|
|
|
|
*/
|
|
|
|
get keyPair() {
|
|
|
|
return [this.encryptionKey, this.hmacKey];
|
|
|
|
},
|
|
|
|
|
|
|
|
set keyPair(value) {
|
|
|
|
if (!Array.isArray(value) || value.length != 2) {
|
|
|
|
throw new Error("BulkKeyBundle.keyPair value must be array of 2 keys.");
|
|
|
|
}
|
|
|
|
|
|
|
|
this.encryptionKey = value[0];
|
|
|
|
this.hmacKey = value[1];
|
|
|
|
},
|
|
|
|
|
|
|
|
get keyPairB64() {
|
|
|
|
return [this.encryptionKeyB64, this.hmacKeyB64];
|
|
|
|
},
|
|
|
|
|
|
|
|
set keyPairB64(value) {
|
|
|
|
if (!Array.isArray(value) || value.length != 2) {
|
|
|
|
throw new Error("BulkKeyBundle.keyPairB64 value must be an array of 2 " +
|
|
|
|
"keys.");
|
|
|
|
}
|
|
|
|
|
|
|
|
this.encryptionKey = Utils.safeAtoB(value[0]);
|
|
|
|
this.hmacKey = Utils.safeAtoB(value[1]);
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Represents a key pair derived from a Sync Key via HKDF.
|
|
|
|
*
|
|
|
|
* Instances of this type should be considered immutable. You create an
|
|
|
|
* instance by specifying the username and 26 character "friendly" Base32
|
|
|
|
* encoded Sync Key. The Sync Key is derived at instance creation time.
|
|
|
|
*
|
|
|
|
* If the username or Sync Key is invalid, an Error will be thrown.
|
|
|
|
*/
|
2012-10-31 20:13:28 +04:00
|
|
|
this.SyncKeyBundle = function SyncKeyBundle(username, syncKey) {
|
2012-03-23 02:49:50 +04:00
|
|
|
let log = Log4Moz.repository.getLogger("Sync.SyncKeyBundle");
|
|
|
|
log.info("SyncKeyBundle being created.");
|
|
|
|
KeyBundle.call(this);
|
|
|
|
|
|
|
|
this.generateFromKey(username, syncKey);
|
|
|
|
}
|
|
|
|
SyncKeyBundle.prototype = {
|
|
|
|
__proto__: KeyBundle.prototype,
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we've got a string, hash it into keys and store them.
|
|
|
|
*/
|
|
|
|
generateFromKey: function generateFromKey(username, syncKey) {
|
|
|
|
if (!username || (typeof username != "string")) {
|
|
|
|
throw new Error("Sync Key cannot be generated from non-string username.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!syncKey || (typeof syncKey != "string")) {
|
|
|
|
throw new Error("Sync Key cannot be generated from non-string key.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Utils.isPassphrase(syncKey)) {
|
|
|
|
throw new Error("Provided key is not a passphrase, cannot derive Sync " +
|
|
|
|
"Key Bundle.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Expand the base32 Sync Key to an AES 256 and 256 bit HMAC key.
|
|
|
|
let prk = Utils.decodeKeyBase32(syncKey);
|
|
|
|
let info = HMAC_INPUT + username;
|
|
|
|
let okm = Utils.hkdfExpand(prk, info, 32 * 2);
|
|
|
|
this.encryptionKey = okm.slice(0, 32);
|
|
|
|
this.hmacKey = okm.slice(32, 64);
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|