services/sync/modules/keys.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 "use strict";
     7 this.EXPORTED_SYMBOLS = [
     8   "BulkKeyBundle",
     9   "SyncKeyBundle"
    10 ];
    12 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
    14 Cu.import("resource://services-sync/constants.js");
    15 Cu.import("resource://gre/modules/Log.jsm");
    16 Cu.import("resource://services-sync/util.js");
    18 /**
    19  * Represents a pair of keys.
    20  *
    21  * Each key stored in a key bundle is 256 bits. One key is used for symmetric
    22  * encryption. The other is used for HMAC.
    23  *
    24  * A KeyBundle by itself is just an anonymous pair of keys. Other types
    25  * deriving from this one add semantics, such as associated collections or
    26  * generating a key bundle via HKDF from another key.
    27  */
    28 function KeyBundle() {
    29   this._encrypt = null;
    30   this._encryptB64 = null;
    31   this._hmac = null;
    32   this._hmacB64 = null;
    33   this._hmacObj = null;
    34   this._sha256HMACHasher = null;
    35 }
    36 KeyBundle.prototype = {
    37   _encrypt: null,
    38   _encryptB64: null,
    39   _hmac: null,
    40   _hmacB64: null,
    41   _hmacObj: null,
    42   _sha256HMACHasher: null,
    44   equals: function equals(bundle) {
    45     return bundle &&
    46            (bundle.hmacKey == this.hmacKey) &&
    47            (bundle.encryptionKey == this.encryptionKey);
    48   },
    50   /*
    51    * Accessors for the two keys.
    52    */
    53   get encryptionKey() {
    54     return this._encrypt;
    55   },
    57   set encryptionKey(value) {
    58     if (!value || typeof value != "string") {
    59       throw new Error("Encryption key can only be set to string values.");
    60     }
    62     if (value.length < 16) {
    63       throw new Error("Encryption key must be at least 128 bits long.");
    64     }
    66     this._encrypt = value;
    67     this._encryptB64 = btoa(value);
    68   },
    70   get encryptionKeyB64() {
    71     return this._encryptB64;
    72   },
    74   get hmacKey() {
    75     return this._hmac;
    76   },
    78   set hmacKey(value) {
    79     if (!value || typeof value != "string") {
    80       throw new Error("HMAC key can only be set to string values.");
    81     }
    83     if (value.length < 16) {
    84       throw new Error("HMAC key must be at least 128 bits long.");
    85     }
    87     this._hmac = value;
    88     this._hmacB64 = btoa(value);
    89     this._hmacObj = value ? Utils.makeHMACKey(value) : null;
    90     this._sha256HMACHasher = value ? Utils.makeHMACHasher(
    91       Ci.nsICryptoHMAC.SHA256, this._hmacObj) : null;
    92   },
    94   get hmacKeyB64() {
    95     return this._hmacB64;
    96   },
    98   get hmacKeyObject() {
    99     return this._hmacObj;
   100   },
   102   get sha256HMACHasher() {
   103     return this._sha256HMACHasher;
   104   },
   106   /**
   107    * Populate this key pair with 2 new, randomly generated keys.
   108    */
   109   generateRandom: function generateRandom() {
   110     let generatedHMAC = Svc.Crypto.generateRandomKey();
   111     let generatedEncr = Svc.Crypto.generateRandomKey();
   112     this.keyPairB64 = [generatedEncr, generatedHMAC];
   113   },
   115 };
   117 /**
   118  * Represents a KeyBundle associated with a collection.
   119  *
   120  * This is just a KeyBundle with a collection attached.
   121  */
   122 this.BulkKeyBundle = function BulkKeyBundle(collection) {
   123   let log = Log.repository.getLogger("Sync.BulkKeyBundle");
   124   log.info("BulkKeyBundle being created for " + collection);
   125   KeyBundle.call(this);
   127   this._collection = collection;
   128 }
   130 BulkKeyBundle.prototype = {
   131   __proto__: KeyBundle.prototype,
   133   get collection() {
   134     return this._collection;
   135   },
   137   /**
   138    * Obtain the key pair in this key bundle.
   139    *
   140    * The returned keys are represented as raw byte strings.
   141    */
   142   get keyPair() {
   143     return [this.encryptionKey, this.hmacKey];
   144   },
   146   set keyPair(value) {
   147     if (!Array.isArray(value) || value.length != 2) {
   148       throw new Error("BulkKeyBundle.keyPair value must be array of 2 keys.");
   149     }
   151     this.encryptionKey = value[0];
   152     this.hmacKey       = value[1];
   153   },
   155   get keyPairB64() {
   156     return [this.encryptionKeyB64, this.hmacKeyB64];
   157   },
   159   set keyPairB64(value) {
   160     if (!Array.isArray(value) || value.length != 2) {
   161       throw new Error("BulkKeyBundle.keyPairB64 value must be an array of 2 " +
   162                       "keys.");
   163     }
   165     this.encryptionKey  = Utils.safeAtoB(value[0]);
   166     this.hmacKey        = Utils.safeAtoB(value[1]);
   167   },
   168 };
   170 /**
   171  * Represents a key pair derived from a Sync Key via HKDF.
   172  *
   173  * Instances of this type should be considered immutable. You create an
   174  * instance by specifying the username and 26 character "friendly" Base32
   175  * encoded Sync Key. The Sync Key is derived at instance creation time.
   176  *
   177  * If the username or Sync Key is invalid, an Error will be thrown.
   178  */
   179 this.SyncKeyBundle = function SyncKeyBundle(username, syncKey) {
   180   let log = Log.repository.getLogger("Sync.SyncKeyBundle");
   181   log.info("SyncKeyBundle being created.");
   182   KeyBundle.call(this);
   184   this.generateFromKey(username, syncKey);
   185 }
   186 SyncKeyBundle.prototype = {
   187   __proto__: KeyBundle.prototype,
   189   /*
   190    * If we've got a string, hash it into keys and store them.
   191    */
   192   generateFromKey: function generateFromKey(username, syncKey) {
   193     if (!username || (typeof username != "string")) {
   194       throw new Error("Sync Key cannot be generated from non-string username.");
   195     }
   197     if (!syncKey || (typeof syncKey != "string")) {
   198       throw new Error("Sync Key cannot be generated from non-string key.");
   199     }
   201     if (!Utils.isPassphrase(syncKey)) {
   202       throw new Error("Provided key is not a passphrase, cannot derive Sync " +
   203                       "Key Bundle.");
   204     }
   206     // Expand the base32 Sync Key to an AES 256 and 256 bit HMAC key.
   207     let prk = Utils.decodeKeyBase32(syncKey);
   208     let info = HMAC_INPUT + username;
   209     let okm = Utils.hkdfExpand(prk, info, 32 * 2);
   210     this.encryptionKey = okm.slice(0, 32);
   211     this.hmacKey = okm.slice(32, 64);
   212   },
   213 };

mercurial