keys.sys.mjs (4198B)
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/. */ 4 5 import { CommonUtils } from "resource://services-common/utils.sys.mjs"; 6 7 import { Log } from "resource://gre/modules/Log.sys.mjs"; 8 9 import { Weave } from "resource://services-sync/main.sys.mjs"; 10 11 /** 12 * Represents a pair of keys. 13 * 14 * Each key stored in a key bundle is 256 bits. One key is used for symmetric 15 * encryption. The other is used for HMAC. 16 * 17 * A KeyBundle by itself is just an anonymous pair of keys. Other types 18 * deriving from this one add semantics, such as associated collections or 19 * generating a key bundle via HKDF from another key. 20 */ 21 function KeyBundle() { 22 this._encrypt = null; 23 this._encryptB64 = null; 24 this._hmac = null; 25 this._hmacB64 = null; 26 } 27 KeyBundle.prototype = { 28 _encrypt: null, 29 _encryptB64: null, 30 _hmac: null, 31 _hmacB64: null, 32 33 equals: function equals(bundle) { 34 return ( 35 bundle && 36 bundle.hmacKey == this.hmacKey && 37 bundle.encryptionKey == this.encryptionKey 38 ); 39 }, 40 41 /* 42 * Accessors for the two keys. 43 */ 44 get encryptionKey() { 45 return this._encrypt; 46 }, 47 48 set encryptionKey(value) { 49 if (!value || typeof value != "string") { 50 throw new Error("Encryption key can only be set to string values."); 51 } 52 53 if (value.length < 16) { 54 throw new Error("Encryption key must be at least 128 bits long."); 55 } 56 57 this._encrypt = value; 58 this._encryptB64 = btoa(value); 59 }, 60 61 get encryptionKeyB64() { 62 return this._encryptB64; 63 }, 64 65 get hmacKey() { 66 return this._hmac; 67 }, 68 69 set hmacKey(value) { 70 if (!value || typeof value != "string") { 71 throw new Error("HMAC key can only be set to string values."); 72 } 73 74 if (value.length < 16) { 75 throw new Error("HMAC key must be at least 128 bits long."); 76 } 77 78 this._hmac = value; 79 this._hmacB64 = btoa(value); 80 }, 81 82 get hmacKeyB64() { 83 return this._hmacB64; 84 }, 85 86 /** 87 * Populate this key pair with 2 new, randomly generated keys. 88 */ 89 async generateRandom() { 90 // Compute both at that same time 91 let [generatedHMAC, generatedEncr] = await Promise.all([ 92 Weave.Crypto.generateRandomKey(), 93 Weave.Crypto.generateRandomKey(), 94 ]); 95 this.keyPairB64 = [generatedEncr, generatedHMAC]; 96 }, 97 }; 98 99 /** 100 * Represents a KeyBundle associated with a collection. 101 * 102 * This is just a KeyBundle with a collection attached. 103 */ 104 export function BulkKeyBundle(collection) { 105 let log = Log.repository.getLogger("Sync.BulkKeyBundle"); 106 log.info("BulkKeyBundle being created for " + collection); 107 KeyBundle.call(this); 108 109 this._collection = collection; 110 } 111 112 BulkKeyBundle.fromHexKey = function (hexKey) { 113 let key = CommonUtils.hexToBytes(hexKey); 114 let bundle = new BulkKeyBundle(); 115 // [encryptionKey, hmacKey] 116 bundle.keyPair = [key.slice(0, 32), key.slice(32, 64)]; 117 return bundle; 118 }; 119 120 BulkKeyBundle.fromJWK = function (jwk) { 121 if (!jwk || !jwk.k || jwk.kty !== "oct") { 122 throw new Error("Invalid JWK provided to BulkKeyBundle.fromJWK"); 123 } 124 return BulkKeyBundle.fromHexKey(CommonUtils.base64urlToHex(jwk.k)); 125 }; 126 127 BulkKeyBundle.prototype = { 128 get collection() { 129 return this._collection; 130 }, 131 132 /** 133 * Obtain the key pair in this key bundle. 134 * 135 * The returned keys are represented as raw byte strings. 136 */ 137 get keyPair() { 138 return [this.encryptionKey, this.hmacKey]; 139 }, 140 141 set keyPair(value) { 142 if (!Array.isArray(value) || value.length != 2) { 143 throw new Error("BulkKeyBundle.keyPair value must be array of 2 keys."); 144 } 145 146 this.encryptionKey = value[0]; 147 this.hmacKey = value[1]; 148 }, 149 150 get keyPairB64() { 151 return [this.encryptionKeyB64, this.hmacKeyB64]; 152 }, 153 154 set keyPairB64(value) { 155 if (!Array.isArray(value) || value.length != 2) { 156 throw new Error( 157 "BulkKeyBundle.keyPairB64 value must be an array of 2 keys." 158 ); 159 } 160 161 this.encryptionKey = CommonUtils.safeAtoB(value[0]); 162 this.hmacKey = CommonUtils.safeAtoB(value[1]); 163 }, 164 }; 165 166 Object.setPrototypeOf(BulkKeyBundle.prototype, KeyBundle.prototype);