services/crypto/modules/WeaveCrypto.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 this.EXPORTED_SYMBOLS = ["WeaveCrypto"];
michael@0 6
michael@0 7 const Cc = Components.classes;
michael@0 8 const Ci = Components.interfaces;
michael@0 9 const Cr = Components.results;
michael@0 10
michael@0 11 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 12 Components.utils.import("resource://gre/modules/Services.jsm");
michael@0 13 Components.utils.import("resource://gre/modules/ctypes.jsm");
michael@0 14
michael@0 15 /**
michael@0 16 * Shortcuts for some algorithm SEC OIDs. Full list available here:
michael@0 17 * http://lxr.mozilla.org/seamonkey/source/security/nss/lib/util/secoidt.h
michael@0 18 */
michael@0 19 const DES_EDE3_CBC = 156;
michael@0 20 const AES_128_CBC = 184;
michael@0 21 const AES_192_CBC = 186;
michael@0 22 const AES_256_CBC = 188;
michael@0 23
michael@0 24 const ALGORITHM = AES_256_CBC;
michael@0 25 const KEYSIZE_AES_256 = 32;
michael@0 26 const KEY_DERIVATION_ITERATIONS = 4096; // PKCS#5 recommends at least 1000.
michael@0 27 const INITIAL_BUFFER_SIZE = 1024;
michael@0 28
michael@0 29 this.WeaveCrypto = function WeaveCrypto() {
michael@0 30 this.init();
michael@0 31 }
michael@0 32
michael@0 33 WeaveCrypto.prototype = {
michael@0 34 prefBranch : null,
michael@0 35 debug : true, // services.sync.log.cryptoDebug
michael@0 36 nss : null,
michael@0 37 nss_t : null,
michael@0 38
michael@0 39 observer : {
michael@0 40 _self : null,
michael@0 41
michael@0 42 QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver,
michael@0 43 Ci.nsISupportsWeakReference]),
michael@0 44
michael@0 45 observe : function (subject, topic, data) {
michael@0 46 let self = this._self;
michael@0 47 self.log("Observed " + topic + " topic.");
michael@0 48 if (topic == "nsPref:changed") {
michael@0 49 self.debug = self.prefBranch.getBoolPref("cryptoDebug");
michael@0 50 }
michael@0 51 }
michael@0 52 },
michael@0 53
michael@0 54 init : function() {
michael@0 55 try {
michael@0 56 // Preferences. Add observer so we get notified of changes.
michael@0 57 this.prefBranch = Services.prefs.getBranch("services.sync.log.");
michael@0 58 this.prefBranch.addObserver("cryptoDebug", this.observer, false);
michael@0 59 this.observer._self = this;
michael@0 60 try {
michael@0 61 this.debug = this.prefBranch.getBoolPref("cryptoDebug");
michael@0 62 } catch (x) {
michael@0 63 this.debug = false;
michael@0 64 }
michael@0 65
michael@0 66 this.initNSS();
michael@0 67 this.initAlgorithmSettings(); // Depends on NSS.
michael@0 68 this.initIVSECItem();
michael@0 69 this.initSharedInts();
michael@0 70 this.initBuffers(INITIAL_BUFFER_SIZE);
michael@0 71 } catch (e) {
michael@0 72 this.log("init failed: " + e);
michael@0 73 throw e;
michael@0 74 }
michael@0 75 },
michael@0 76
michael@0 77 // Avoid allocating new temporary ints on every run of _commonCrypt.
michael@0 78 _commonCryptSignedOutputSize: null,
michael@0 79 _commonCryptSignedOutputSizeAddr: null,
michael@0 80 _commonCryptUnsignedOutputSize: null,
michael@0 81 _commonCryptUnsignedOutputSizeAddr: null,
michael@0 82
michael@0 83 initSharedInts: function initSharedInts() {
michael@0 84 let signed = new ctypes.int();
michael@0 85 let unsigned = new ctypes.unsigned_int();
michael@0 86 this._commonCryptSignedOutputSize = signed;
michael@0 87 this._commonCryptUnsignedOutputSize = unsigned;
michael@0 88 this._commonCryptSignedOutputSizeAddr = signed.address();
michael@0 89 this._commonCryptUnsignedOutputSizeAddr = unsigned.address();
michael@0 90 },
michael@0 91
michael@0 92 /**
michael@0 93 * Set a bunch of NSS values once, at init-time. These are:
michael@0 94 * - .blockSize
michael@0 95 * - .mechanism
michael@0 96 * - .keygenMechanism
michael@0 97 * - .padMechanism
michael@0 98 * - .keySize
michael@0 99 *
michael@0 100 * See also the constant ALGORITHM.
michael@0 101 */
michael@0 102 initAlgorithmSettings: function() {
michael@0 103 this.mechanism = this.nss.PK11_AlgtagToMechanism(ALGORITHM);
michael@0 104 this.blockSize = this.nss.PK11_GetBlockSize(this.mechanism, null);
michael@0 105 this.ivLength = this.nss.PK11_GetIVLength(this.mechanism);
michael@0 106 this.keySize = KEYSIZE_AES_256;
michael@0 107 this.keygenMechanism = this.nss.CKM_AES_KEY_GEN; // Always the same!
michael@0 108
michael@0 109 // Determine which (padded) PKCS#11 mechanism to use.
michael@0 110 // E.g., AES_256_CBC --> CKM_AES_CBC --> CKM_AES_CBC_PAD
michael@0 111 this.padMechanism = this.nss.PK11_GetPadMechanism(this.mechanism);
michael@0 112 if (this.padMechanism == this.nss.CKM_INVALID_MECHANISM)
michael@0 113 throw Components.Exception("invalid algorithm (can't pad)", Cr.NS_ERROR_FAILURE);
michael@0 114 },
michael@0 115
michael@0 116 log : function (message) {
michael@0 117 if (!this.debug)
michael@0 118 return;
michael@0 119 dump("WeaveCrypto: " + message + "\n");
michael@0 120 Services.console.logStringMessage("WeaveCrypto: " + message);
michael@0 121 },
michael@0 122
michael@0 123 initNSS : function() {
michael@0 124 // We use NSS for the crypto ops, which needs to be initialized before
michael@0 125 // use. By convention, PSM is required to be the module that
michael@0 126 // initializes NSS. So, make sure PSM is initialized in order to
michael@0 127 // implicitly initialize NSS.
michael@0 128 Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
michael@0 129
michael@0 130 // Open the NSS library.
michael@0 131 let path = ctypes.libraryName("nss3");
michael@0 132
michael@0 133 // XXX really want to be able to pass specific dlopen flags here.
michael@0 134 var nsslib;
michael@0 135 try {
michael@0 136 this.log("Trying NSS library without path");
michael@0 137 nsslib = ctypes.open(path);
michael@0 138 } catch(e) {
michael@0 139 // In case opening the library without a full path fails,
michael@0 140 // try again with a full path.
michael@0 141 let file = Services.dirsvc.get("GreD", Ci.nsILocalFile);
michael@0 142 file.append(path);
michael@0 143 this.log("Trying again with path " + file.path);
michael@0 144 nsslib = ctypes.open(file.path);
michael@0 145 }
michael@0 146
michael@0 147 this.log("Initializing NSS types and function declarations...");
michael@0 148
michael@0 149 this.nss = {};
michael@0 150 this.nss_t = {};
michael@0 151
michael@0 152 // nsprpub/pr/include/prtypes.h#435
michael@0 153 // typedef PRIntn PRBool; --> int
michael@0 154 this.nss_t.PRBool = ctypes.int;
michael@0 155 // security/nss/lib/util/seccomon.h#91
michael@0 156 // typedef enum
michael@0 157 this.nss_t.SECStatus = ctypes.int;
michael@0 158 // security/nss/lib/softoken/secmodt.h#59
michael@0 159 // typedef struct PK11SlotInfoStr PK11SlotInfo; (defined in secmodti.h)
michael@0 160 this.nss_t.PK11SlotInfo = ctypes.void_t;
michael@0 161 // security/nss/lib/util/pkcs11t.h
michael@0 162 this.nss_t.CK_MECHANISM_TYPE = ctypes.unsigned_long;
michael@0 163 this.nss_t.CK_ATTRIBUTE_TYPE = ctypes.unsigned_long;
michael@0 164 this.nss_t.CK_KEY_TYPE = ctypes.unsigned_long;
michael@0 165 this.nss_t.CK_OBJECT_HANDLE = ctypes.unsigned_long;
michael@0 166 // security/nss/lib/softoken/secmodt.h#359
michael@0 167 // typedef enum PK11Origin
michael@0 168 this.nss_t.PK11Origin = ctypes.int;
michael@0 169 // PK11Origin enum values...
michael@0 170 this.nss.PK11_OriginUnwrap = 4;
michael@0 171 // security/nss/lib/softoken/secmodt.h#61
michael@0 172 // typedef struct PK11SymKeyStr PK11SymKey; (defined in secmodti.h)
michael@0 173 this.nss_t.PK11SymKey = ctypes.void_t;
michael@0 174 // security/nss/lib/util/secoidt.h#454
michael@0 175 // typedef enum
michael@0 176 this.nss_t.SECOidTag = ctypes.int;
michael@0 177 // security/nss/lib/util/seccomon.h#64
michael@0 178 // typedef enum
michael@0 179 this.nss_t.SECItemType = ctypes.int;
michael@0 180 // SECItemType enum values...
michael@0 181 this.nss.SIBUFFER = 0;
michael@0 182 // security/nss/lib/softoken/secmodt.h#62 (defined in secmodti.h)
michael@0 183 // typedef struct PK11ContextStr PK11Context;
michael@0 184 this.nss_t.PK11Context = ctypes.void_t;
michael@0 185 // Needed for SECKEYPrivateKey struct def'n, but I don't think we need to actually access it.
michael@0 186 this.nss_t.PLArenaPool = ctypes.void_t;
michael@0 187 // security/nss/lib/cryptohi/keythi.h#45
michael@0 188 // typedef enum
michael@0 189 this.nss_t.KeyType = ctypes.int;
michael@0 190 // security/nss/lib/softoken/secmodt.h#201
michael@0 191 // typedef PRUint32 PK11AttrFlags;
michael@0 192 this.nss_t.PK11AttrFlags = ctypes.unsigned_int;
michael@0 193 // security/nss/lib/util/seccomon.h#83
michael@0 194 // typedef struct SECItemStr SECItem; --> SECItemStr defined right below it
michael@0 195 this.nss_t.SECItem = ctypes.StructType(
michael@0 196 "SECItem", [{ type: this.nss_t.SECItemType },
michael@0 197 { data: ctypes.unsigned_char.ptr },
michael@0 198 { len : ctypes.int }]);
michael@0 199 // security/nss/lib/util/secoidt.h#52
michael@0 200 // typedef struct SECAlgorithmIDStr --> def'n right below it
michael@0 201 this.nss_t.SECAlgorithmID = ctypes.StructType(
michael@0 202 "SECAlgorithmID", [{ algorithm: this.nss_t.SECItem },
michael@0 203 { parameters: this.nss_t.SECItem }]);
michael@0 204
michael@0 205
michael@0 206 // security/nss/lib/util/pkcs11t.h
michael@0 207 this.nss.CKK_RSA = 0x0;
michael@0 208 this.nss.CKM_RSA_PKCS_KEY_PAIR_GEN = 0x0000;
michael@0 209 this.nss.CKM_AES_KEY_GEN = 0x1080;
michael@0 210 this.nss.CKA_ENCRYPT = 0x104;
michael@0 211 this.nss.CKA_DECRYPT = 0x105;
michael@0 212
michael@0 213 // security/nss/lib/softoken/secmodt.h
michael@0 214 this.nss.PK11_ATTR_SESSION = 0x02;
michael@0 215 this.nss.PK11_ATTR_PUBLIC = 0x08;
michael@0 216 this.nss.PK11_ATTR_SENSITIVE = 0x40;
michael@0 217
michael@0 218 // security/nss/lib/util/secoidt.h
michael@0 219 this.nss.SEC_OID_PKCS5_PBKDF2 = 291;
michael@0 220 this.nss.SEC_OID_HMAC_SHA1 = 294;
michael@0 221 this.nss.SEC_OID_PKCS1_RSA_ENCRYPTION = 16;
michael@0 222
michael@0 223
michael@0 224 // security/nss/lib/pk11wrap/pk11pub.h#286
michael@0 225 // SECStatus PK11_GenerateRandom(unsigned char *data,int len);
michael@0 226 this.nss.PK11_GenerateRandom = nsslib.declare("PK11_GenerateRandom",
michael@0 227 ctypes.default_abi, this.nss_t.SECStatus,
michael@0 228 ctypes.unsigned_char.ptr, ctypes.int);
michael@0 229 // security/nss/lib/pk11wrap/pk11pub.h#74
michael@0 230 // PK11SlotInfo *PK11_GetInternalSlot(void);
michael@0 231 this.nss.PK11_GetInternalSlot = nsslib.declare("PK11_GetInternalSlot",
michael@0 232 ctypes.default_abi, this.nss_t.PK11SlotInfo.ptr);
michael@0 233 // security/nss/lib/pk11wrap/pk11pub.h#73
michael@0 234 // PK11SlotInfo *PK11_GetInternalKeySlot(void);
michael@0 235 this.nss.PK11_GetInternalKeySlot = nsslib.declare("PK11_GetInternalKeySlot",
michael@0 236 ctypes.default_abi, this.nss_t.PK11SlotInfo.ptr);
michael@0 237 // security/nss/lib/pk11wrap/pk11pub.h#328
michael@0 238 // PK11SymKey *PK11_KeyGen(PK11SlotInfo *slot,CK_MECHANISM_TYPE type, SECItem *param, int keySize,void *wincx);
michael@0 239 this.nss.PK11_KeyGen = nsslib.declare("PK11_KeyGen",
michael@0 240 ctypes.default_abi, this.nss_t.PK11SymKey.ptr,
michael@0 241 this.nss_t.PK11SlotInfo.ptr, this.nss_t.CK_MECHANISM_TYPE,
michael@0 242 this.nss_t.SECItem.ptr, ctypes.int, ctypes.voidptr_t);
michael@0 243 // security/nss/lib/pk11wrap/pk11pub.h#477
michael@0 244 // SECStatus PK11_ExtractKeyValue(PK11SymKey *symKey);
michael@0 245 this.nss.PK11_ExtractKeyValue = nsslib.declare("PK11_ExtractKeyValue",
michael@0 246 ctypes.default_abi, this.nss_t.SECStatus,
michael@0 247 this.nss_t.PK11SymKey.ptr);
michael@0 248 // security/nss/lib/pk11wrap/pk11pub.h#478
michael@0 249 // SECItem * PK11_GetKeyData(PK11SymKey *symKey);
michael@0 250 this.nss.PK11_GetKeyData = nsslib.declare("PK11_GetKeyData",
michael@0 251 ctypes.default_abi, this.nss_t.SECItem.ptr,
michael@0 252 this.nss_t.PK11SymKey.ptr);
michael@0 253 // security/nss/lib/pk11wrap/pk11pub.h#278
michael@0 254 // CK_MECHANISM_TYPE PK11_AlgtagToMechanism(SECOidTag algTag);
michael@0 255 this.nss.PK11_AlgtagToMechanism = nsslib.declare("PK11_AlgtagToMechanism",
michael@0 256 ctypes.default_abi, this.nss_t.CK_MECHANISM_TYPE,
michael@0 257 this.nss_t.SECOidTag);
michael@0 258 // security/nss/lib/pk11wrap/pk11pub.h#270
michael@0 259 // int PK11_GetIVLength(CK_MECHANISM_TYPE type);
michael@0 260 this.nss.PK11_GetIVLength = nsslib.declare("PK11_GetIVLength",
michael@0 261 ctypes.default_abi, ctypes.int,
michael@0 262 this.nss_t.CK_MECHANISM_TYPE);
michael@0 263 // security/nss/lib/pk11wrap/pk11pub.h#269
michael@0 264 // int PK11_GetBlockSize(CK_MECHANISM_TYPE type,SECItem *params);
michael@0 265 this.nss.PK11_GetBlockSize = nsslib.declare("PK11_GetBlockSize",
michael@0 266 ctypes.default_abi, ctypes.int,
michael@0 267 this.nss_t.CK_MECHANISM_TYPE, this.nss_t.SECItem.ptr);
michael@0 268 // security/nss/lib/pk11wrap/pk11pub.h#293
michael@0 269 // CK_MECHANISM_TYPE PK11_GetPadMechanism(CK_MECHANISM_TYPE);
michael@0 270 this.nss.PK11_GetPadMechanism = nsslib.declare("PK11_GetPadMechanism",
michael@0 271 ctypes.default_abi, this.nss_t.CK_MECHANISM_TYPE,
michael@0 272 this.nss_t.CK_MECHANISM_TYPE);
michael@0 273 // security/nss/lib/pk11wrap/pk11pub.h#271
michael@0 274 // SECItem *PK11_ParamFromIV(CK_MECHANISM_TYPE type,SECItem *iv);
michael@0 275 this.nss.PK11_ParamFromIV = nsslib.declare("PK11_ParamFromIV",
michael@0 276 ctypes.default_abi, this.nss_t.SECItem.ptr,
michael@0 277 this.nss_t.CK_MECHANISM_TYPE, this.nss_t.SECItem.ptr);
michael@0 278 // security/nss/lib/pk11wrap/pk11pub.h#301
michael@0 279 // PK11SymKey *PK11_ImportSymKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, PK11Origin origin,
michael@0 280 // CK_ATTRIBUTE_TYPE operation, SECItem *key, void *wincx);
michael@0 281 this.nss.PK11_ImportSymKey = nsslib.declare("PK11_ImportSymKey",
michael@0 282 ctypes.default_abi, this.nss_t.PK11SymKey.ptr,
michael@0 283 this.nss_t.PK11SlotInfo.ptr, this.nss_t.CK_MECHANISM_TYPE, this.nss_t.PK11Origin,
michael@0 284 this.nss_t.CK_ATTRIBUTE_TYPE, this.nss_t.SECItem.ptr, ctypes.voidptr_t);
michael@0 285 // security/nss/lib/pk11wrap/pk11pub.h#672
michael@0 286 // PK11Context *PK11_CreateContextBySymKey(CK_MECHANISM_TYPE type, CK_ATTRIBUTE_TYPE operation,
michael@0 287 // PK11SymKey *symKey, SECItem *param);
michael@0 288 this.nss.PK11_CreateContextBySymKey = nsslib.declare("PK11_CreateContextBySymKey",
michael@0 289 ctypes.default_abi, this.nss_t.PK11Context.ptr,
michael@0 290 this.nss_t.CK_MECHANISM_TYPE, this.nss_t.CK_ATTRIBUTE_TYPE,
michael@0 291 this.nss_t.PK11SymKey.ptr, this.nss_t.SECItem.ptr);
michael@0 292 // security/nss/lib/pk11wrap/pk11pub.h#685
michael@0 293 // SECStatus PK11_CipherOp(PK11Context *context, unsigned char *out
michael@0 294 // int *outlen, int maxout, unsigned char *in, int inlen);
michael@0 295 this.nss.PK11_CipherOp = nsslib.declare("PK11_CipherOp",
michael@0 296 ctypes.default_abi, this.nss_t.SECStatus,
michael@0 297 this.nss_t.PK11Context.ptr, ctypes.unsigned_char.ptr,
michael@0 298 ctypes.int.ptr, ctypes.int, ctypes.unsigned_char.ptr, ctypes.int);
michael@0 299 // security/nss/lib/pk11wrap/pk11pub.h#688
michael@0 300 // SECStatus PK11_DigestFinal(PK11Context *context, unsigned char *data,
michael@0 301 // unsigned int *outLen, unsigned int length);
michael@0 302 this.nss.PK11_DigestFinal = nsslib.declare("PK11_DigestFinal",
michael@0 303 ctypes.default_abi, this.nss_t.SECStatus,
michael@0 304 this.nss_t.PK11Context.ptr, ctypes.unsigned_char.ptr,
michael@0 305 ctypes.unsigned_int.ptr, ctypes.unsigned_int);
michael@0 306 // security/nss/lib/pk11wrap/pk11pub.h#731
michael@0 307 // SECAlgorithmID * PK11_CreatePBEV2AlgorithmID(SECOidTag pbeAlgTag, SECOidTag cipherAlgTag,
michael@0 308 // SECOidTag prfAlgTag, int keyLength, int iteration,
michael@0 309 // SECItem *salt);
michael@0 310 this.nss.PK11_CreatePBEV2AlgorithmID = nsslib.declare("PK11_CreatePBEV2AlgorithmID",
michael@0 311 ctypes.default_abi, this.nss_t.SECAlgorithmID.ptr,
michael@0 312 this.nss_t.SECOidTag, this.nss_t.SECOidTag, this.nss_t.SECOidTag,
michael@0 313 ctypes.int, ctypes.int, this.nss_t.SECItem.ptr);
michael@0 314 // security/nss/lib/pk11wrap/pk11pub.h#736
michael@0 315 // PK11SymKey * PK11_PBEKeyGen(PK11SlotInfo *slot, SECAlgorithmID *algid, SECItem *pwitem, PRBool faulty3DES, void *wincx);
michael@0 316 this.nss.PK11_PBEKeyGen = nsslib.declare("PK11_PBEKeyGen",
michael@0 317 ctypes.default_abi, this.nss_t.PK11SymKey.ptr,
michael@0 318 this.nss_t.PK11SlotInfo.ptr, this.nss_t.SECAlgorithmID.ptr,
michael@0 319 this.nss_t.SECItem.ptr, this.nss_t.PRBool, ctypes.voidptr_t);
michael@0 320 // security/nss/lib/pk11wrap/pk11pub.h#675
michael@0 321 // void PK11_DestroyContext(PK11Context *context, PRBool freeit);
michael@0 322 this.nss.PK11_DestroyContext = nsslib.declare("PK11_DestroyContext",
michael@0 323 ctypes.default_abi, ctypes.void_t,
michael@0 324 this.nss_t.PK11Context.ptr, this.nss_t.PRBool);
michael@0 325 // security/nss/lib/pk11wrap/pk11pub.h#299
michael@0 326 // void PK11_FreeSymKey(PK11SymKey *key);
michael@0 327 this.nss.PK11_FreeSymKey = nsslib.declare("PK11_FreeSymKey",
michael@0 328 ctypes.default_abi, ctypes.void_t,
michael@0 329 this.nss_t.PK11SymKey.ptr);
michael@0 330 // security/nss/lib/pk11wrap/pk11pub.h#70
michael@0 331 // void PK11_FreeSlot(PK11SlotInfo *slot);
michael@0 332 this.nss.PK11_FreeSlot = nsslib.declare("PK11_FreeSlot",
michael@0 333 ctypes.default_abi, ctypes.void_t,
michael@0 334 this.nss_t.PK11SlotInfo.ptr);
michael@0 335 // security/nss/lib/util/secitem.h#49
michael@0 336 // extern SECItem *SECITEM_AllocItem(PRArenaPool *arena, SECItem *item, unsigned int len);
michael@0 337 this.nss.SECITEM_AllocItem = nsslib.declare("SECITEM_AllocItem",
michael@0 338 ctypes.default_abi, this.nss_t.SECItem.ptr,
michael@0 339 this.nss_t.PLArenaPool.ptr, // Not used.
michael@0 340 this.nss_t.SECItem.ptr, ctypes.unsigned_int);
michael@0 341 // security/nss/lib/util/secitem.h#274
michael@0 342 // extern void SECITEM_ZfreeItem(SECItem *zap, PRBool freeit);
michael@0 343 this.nss.SECITEM_ZfreeItem = nsslib.declare("SECITEM_ZfreeItem",
michael@0 344 ctypes.default_abi, ctypes.void_t,
michael@0 345 this.nss_t.SECItem.ptr, this.nss_t.PRBool);
michael@0 346 // security/nss/lib/util/secitem.h#114
michael@0 347 // extern void SECITEM_FreeItem(SECItem *zap, PRBool freeit);
michael@0 348 this.nss.SECITEM_FreeItem = nsslib.declare("SECITEM_FreeItem",
michael@0 349 ctypes.default_abi, ctypes.void_t,
michael@0 350 this.nss_t.SECItem.ptr, this.nss_t.PRBool);
michael@0 351 // security/nss/lib/util/secoid.h#103
michael@0 352 // extern void SECOID_DestroyAlgorithmID(SECAlgorithmID *aid, PRBool freeit);
michael@0 353 this.nss.SECOID_DestroyAlgorithmID = nsslib.declare("SECOID_DestroyAlgorithmID",
michael@0 354 ctypes.default_abi, ctypes.void_t,
michael@0 355 this.nss_t.SECAlgorithmID.ptr, this.nss_t.PRBool);
michael@0 356 },
michael@0 357
michael@0 358
michael@0 359 _sharedInputBuffer: null,
michael@0 360 _sharedInputBufferInts: null,
michael@0 361 _sharedInputBufferSize: 0,
michael@0 362 _sharedOutputBuffer: null,
michael@0 363 _sharedOutputBufferSize: 0,
michael@0 364 _randomByteBuffer: null,
michael@0 365 _randomByteBufferAddr: null,
michael@0 366 _randomByteBufferSize: 0,
michael@0 367
michael@0 368 _getInputBuffer: function _getInputBuffer(size) {
michael@0 369 if (size > this._sharedInputBufferSize) {
michael@0 370 let b = new ctypes.ArrayType(ctypes.unsigned_char, size)();
michael@0 371 this._sharedInputBuffer = b;
michael@0 372 this._sharedInputBufferInts = ctypes.cast(b, ctypes.uint8_t.array(size));
michael@0 373 this._sharedInputBufferSize = size;
michael@0 374 }
michael@0 375 return this._sharedInputBuffer;
michael@0 376 },
michael@0 377
michael@0 378 _getOutputBuffer: function _getOutputBuffer(size) {
michael@0 379 if (size > this._sharedOutputBufferSize) {
michael@0 380 let b = new ctypes.ArrayType(ctypes.unsigned_char, size)();
michael@0 381 this._sharedOutputBuffer = b;
michael@0 382 this._sharedOutputBufferSize = size;
michael@0 383 }
michael@0 384 return this._sharedOutputBuffer;
michael@0 385 },
michael@0 386
michael@0 387 _getRandomByteBuffer: function _getRandomByteBuffer(size) {
michael@0 388 if (size > this._randomByteBufferSize) {
michael@0 389 let b = new ctypes.ArrayType(ctypes.unsigned_char, size)();
michael@0 390 this._randomByteBuffer = b;
michael@0 391 this._randomByteBufferAddr = b.address();
michael@0 392 this._randomByteBufferSize = size;
michael@0 393 }
michael@0 394 return this._randomByteBuffer;
michael@0 395 },
michael@0 396
michael@0 397 initBuffers: function initBuffers(initialSize) {
michael@0 398 this._getInputBuffer(initialSize);
michael@0 399 this._getOutputBuffer(initialSize);
michael@0 400
michael@0 401 this._getRandomByteBuffer(this.ivLength);
michael@0 402 },
michael@0 403
michael@0 404 encrypt : function(clearTextUCS2, symmetricKey, iv) {
michael@0 405 this.log("encrypt() called");
michael@0 406
michael@0 407 // js-ctypes autoconverts to a UTF8 buffer, but also includes a null
michael@0 408 // at the end which we don't want. Decrement length to skip it.
michael@0 409 let inputBuffer = new ctypes.ArrayType(ctypes.unsigned_char)(clearTextUCS2);
michael@0 410 let inputBufferSize = inputBuffer.length - 1;
michael@0 411
michael@0 412 // When using CBC padding, the output size is the input size rounded
michael@0 413 // up to the nearest block. If the input size is exactly on a block
michael@0 414 // boundary, the output is 1 extra block long.
michael@0 415 let outputBufferSize = inputBufferSize + this.blockSize;
michael@0 416 let outputBuffer = this._getOutputBuffer(outputBufferSize);
michael@0 417
michael@0 418 outputBuffer = this._commonCrypt(inputBuffer, inputBufferSize,
michael@0 419 outputBuffer, outputBufferSize,
michael@0 420 symmetricKey, iv, this.nss.CKA_ENCRYPT);
michael@0 421
michael@0 422 return this.encodeBase64(outputBuffer.address(), outputBuffer.length);
michael@0 423 },
michael@0 424
michael@0 425
michael@0 426 decrypt : function(cipherText, symmetricKey, iv) {
michael@0 427 this.log("decrypt() called");
michael@0 428
michael@0 429 let inputUCS2 = "";
michael@0 430 if (cipherText.length)
michael@0 431 inputUCS2 = atob(cipherText);
michael@0 432
michael@0 433 // We can't have js-ctypes create the buffer directly from the string
michael@0 434 // (as in encrypt()), because we do _not_ want it to do UTF8
michael@0 435 // conversion... We've got random binary data in the input's low byte.
michael@0 436 //
michael@0 437 // Compress a JS string (2-byte chars) into a normal C string (1-byte chars).
michael@0 438 let len = inputUCS2.length;
michael@0 439 let input = this._getInputBuffer(len);
michael@0 440 this.byteCompressInts(inputUCS2, this._sharedInputBufferInts, len);
michael@0 441
michael@0 442 let outputBuffer = this._commonCrypt(input, len,
michael@0 443 this._getOutputBuffer(len), len,
michael@0 444 symmetricKey, iv, this.nss.CKA_DECRYPT);
michael@0 445
michael@0 446 // outputBuffer contains UTF-8 data, let js-ctypes autoconvert that to a JS string.
michael@0 447 // XXX Bug 573842: wrap the string from ctypes to get a new string, so
michael@0 448 // we don't hit bug 573841.
michael@0 449 return "" + outputBuffer.readString() + "";
michael@0 450 },
michael@0 451
michael@0 452 _commonCrypt : function (input, inputLength, output, outputLength, symmetricKey, iv, operation) {
michael@0 453 this.log("_commonCrypt() called");
michael@0 454 iv = atob(iv);
michael@0 455
michael@0 456 // We never want an IV longer than the block size, which is 16 bytes
michael@0 457 // for AES. Neither do we want one smaller; throw in that case.
michael@0 458 if (iv.length < this.blockSize)
michael@0 459 throw "IV too short; must be " + this.blockSize + " bytes.";
michael@0 460 if (iv.length > this.blockSize)
michael@0 461 iv = iv.slice(0, this.blockSize);
michael@0 462
michael@0 463 // We use a single IV SECItem for the sake of efficiency. Fill it here.
michael@0 464 this.byteCompressInts(iv, this._ivSECItemContents, iv.length);
michael@0 465
michael@0 466 let ctx, symKey, ivParam;
michael@0 467 try {
michael@0 468 ivParam = this.nss.PK11_ParamFromIV(this.padMechanism, this._ivSECItem);
michael@0 469 if (ivParam.isNull())
michael@0 470 throw Components.Exception("can't convert IV to param", Cr.NS_ERROR_FAILURE);
michael@0 471
michael@0 472 symKey = this.importSymKey(symmetricKey, operation);
michael@0 473 ctx = this.nss.PK11_CreateContextBySymKey(this.padMechanism, operation, symKey, ivParam);
michael@0 474 if (ctx.isNull())
michael@0 475 throw Components.Exception("couldn't create context for symkey", Cr.NS_ERROR_FAILURE);
michael@0 476
michael@0 477 let maxOutputSize = outputLength;
michael@0 478 if (this.nss.PK11_CipherOp(ctx, output, this._commonCryptSignedOutputSize.address(), maxOutputSize, input, inputLength))
michael@0 479 throw Components.Exception("cipher operation failed", Cr.NS_ERROR_FAILURE);
michael@0 480
michael@0 481 let actualOutputSize = this._commonCryptSignedOutputSize.value;
michael@0 482 let finalOutput = output.addressOfElement(actualOutputSize);
michael@0 483 maxOutputSize -= actualOutputSize;
michael@0 484
michael@0 485 // PK11_DigestFinal sure sounds like the last step for *hashing*, but it
michael@0 486 // just seems to be an odd name -- NSS uses this to finish the current
michael@0 487 // cipher operation. You'd think it would be called PK11_CipherOpFinal...
michael@0 488 if (this.nss.PK11_DigestFinal(ctx, finalOutput, this._commonCryptUnsignedOutputSizeAddr, maxOutputSize))
michael@0 489 throw Components.Exception("cipher finalize failed", Cr.NS_ERROR_FAILURE);
michael@0 490
michael@0 491 actualOutputSize += this._commonCryptUnsignedOutputSize.value;
michael@0 492 let newOutput = ctypes.cast(output, ctypes.unsigned_char.array(actualOutputSize));
michael@0 493 return newOutput;
michael@0 494 } catch (e) {
michael@0 495 this.log("_commonCrypt: failed: " + e);
michael@0 496 throw e;
michael@0 497 } finally {
michael@0 498 if (ctx && !ctx.isNull())
michael@0 499 this.nss.PK11_DestroyContext(ctx, true);
michael@0 500 if (ivParam && !ivParam.isNull())
michael@0 501 this.nss.SECITEM_FreeItem(ivParam, true);
michael@0 502
michael@0 503 // Note that we do not free the IV SECItem; we reuse it.
michael@0 504 // Neither do we free the symKey, because that's memoized.
michael@0 505 }
michael@0 506 },
michael@0 507
michael@0 508
michael@0 509 generateRandomKey : function() {
michael@0 510 this.log("generateRandomKey() called");
michael@0 511 let slot, randKey, keydata;
michael@0 512 try {
michael@0 513 slot = this.nss.PK11_GetInternalSlot();
michael@0 514 if (slot.isNull())
michael@0 515 throw Components.Exception("couldn't get internal slot", Cr.NS_ERROR_FAILURE);
michael@0 516
michael@0 517 randKey = this.nss.PK11_KeyGen(slot, this.keygenMechanism, null, this.keySize, null);
michael@0 518 if (randKey.isNull())
michael@0 519 throw Components.Exception("PK11_KeyGen failed.", Cr.NS_ERROR_FAILURE);
michael@0 520
michael@0 521 // Slightly odd API, this call just prepares the key value for
michael@0 522 // extraction, we get the actual bits from the call to PK11_GetKeyData().
michael@0 523 if (this.nss.PK11_ExtractKeyValue(randKey))
michael@0 524 throw Components.Exception("PK11_ExtractKeyValue failed.", Cr.NS_ERROR_FAILURE);
michael@0 525
michael@0 526 keydata = this.nss.PK11_GetKeyData(randKey);
michael@0 527 if (keydata.isNull())
michael@0 528 throw Components.Exception("PK11_GetKeyData failed.", Cr.NS_ERROR_FAILURE);
michael@0 529
michael@0 530 return this.encodeBase64(keydata.contents.data, keydata.contents.len);
michael@0 531 } catch (e) {
michael@0 532 this.log("generateRandomKey: failed: " + e);
michael@0 533 throw e;
michael@0 534 } finally {
michael@0 535 if (randKey && !randKey.isNull())
michael@0 536 this.nss.PK11_FreeSymKey(randKey);
michael@0 537 if (slot && !slot.isNull())
michael@0 538 this.nss.PK11_FreeSlot(slot);
michael@0 539 }
michael@0 540 },
michael@0 541
michael@0 542 generateRandomIV : function() this.generateRandomBytes(this.ivLength),
michael@0 543
michael@0 544 generateRandomBytes : function(byteCount) {
michael@0 545 this.log("generateRandomBytes() called");
michael@0 546
michael@0 547 // Temporary buffer to hold the generated data.
michael@0 548 let scratch = this._getRandomByteBuffer(byteCount);
michael@0 549 if (this.nss.PK11_GenerateRandom(scratch, byteCount))
michael@0 550 throw Components.Exception("PK11_GenrateRandom failed", Cr.NS_ERROR_FAILURE);
michael@0 551
michael@0 552 return this.encodeBase64(this._randomByteBufferAddr, byteCount);
michael@0 553 },
michael@0 554
michael@0 555 //
michael@0 556 // PK11SymKey memoization.
michael@0 557 //
michael@0 558
michael@0 559 // Memoize the lookup of symmetric keys. We do this by using the base64
michael@0 560 // string itself as a key -- the overhead of SECItem creation during the
michael@0 561 // initial population is negligible, so that phase is not memoized.
michael@0 562 _encryptionSymKeyMemo: {},
michael@0 563 _decryptionSymKeyMemo: {},
michael@0 564 importSymKey: function importSymKey(encodedKeyString, operation) {
michael@0 565 let memo;
michael@0 566
michael@0 567 // We use two separate memos for thoroughness: operation is an input to
michael@0 568 // key import.
michael@0 569 switch (operation) {
michael@0 570 case this.nss.CKA_ENCRYPT:
michael@0 571 memo = this._encryptionSymKeyMemo;
michael@0 572 break;
michael@0 573 case this.nss.CKA_DECRYPT:
michael@0 574 memo = this._decryptionSymKeyMemo;
michael@0 575 break;
michael@0 576 default:
michael@0 577 throw "Unsupported operation in importSymKey.";
michael@0 578 }
michael@0 579
michael@0 580 if (encodedKeyString in memo)
michael@0 581 return memo[encodedKeyString];
michael@0 582
michael@0 583 let keyItem, slot;
michael@0 584 try {
michael@0 585 keyItem = this.makeSECItem(encodedKeyString, true);
michael@0 586 slot = this.nss.PK11_GetInternalKeySlot();
michael@0 587 if (slot.isNull())
michael@0 588 throw Components.Exception("can't get internal key slot",
michael@0 589 Cr.NS_ERROR_FAILURE);
michael@0 590
michael@0 591 let symKey = this.nss.PK11_ImportSymKey(slot, this.padMechanism,
michael@0 592 this.nss.PK11_OriginUnwrap,
michael@0 593 operation, keyItem, null);
michael@0 594 if (!symKey || symKey.isNull())
michael@0 595 throw Components.Exception("symkey import failed",
michael@0 596 Cr.NS_ERROR_FAILURE);
michael@0 597
michael@0 598 return memo[encodedKeyString] = symKey;
michael@0 599 } finally {
michael@0 600 if (slot && !slot.isNull())
michael@0 601 this.nss.PK11_FreeSlot(slot);
michael@0 602 this.freeSECItem(keyItem);
michael@0 603 }
michael@0 604 },
michael@0 605
michael@0 606
michael@0 607 //
michael@0 608 // Utility functions
michael@0 609 //
michael@0 610
michael@0 611 /**
michael@0 612 * Compress a JS string into a C uint8 array. count is the number of
michael@0 613 * elements in the destination array. If the array is smaller than the
michael@0 614 * string, the string is effectively truncated. If the string is smaller
michael@0 615 * than the array, the array is not 0-padded.
michael@0 616 */
michael@0 617 byteCompressInts : function byteCompressInts (jsString, intArray, count) {
michael@0 618 let len = jsString.length;
michael@0 619 let end = Math.min(len, count);
michael@0 620 for (let i = 0; i < end; i++)
michael@0 621 intArray[i] = jsString.charCodeAt(i) & 0xFF; // convert to bytes.
michael@0 622 },
michael@0 623
michael@0 624 // Expand a normal C string (1-byte chars) into a JS string (2-byte chars)
michael@0 625 // EG, for "ABC", 0x41, 0x42, 0x43 --> 0x0041, 0x0042, 0x0043
michael@0 626 byteExpand : function (charArray) {
michael@0 627 let expanded = "";
michael@0 628 let len = charArray.length;
michael@0 629 let intData = ctypes.cast(charArray, ctypes.uint8_t.array(len));
michael@0 630 for (let i = 0; i < len; i++)
michael@0 631 expanded += String.fromCharCode(intData[i]);
michael@0 632 return expanded;
michael@0 633 },
michael@0 634
michael@0 635 expandData : function expandData(data, len) {
michael@0 636 // Byte-expand the buffer, so we can treat it as a UCS-2 string
michael@0 637 // consisting of u0000 - u00FF.
michael@0 638 let expanded = "";
michael@0 639 let intData = ctypes.cast(data, ctypes.uint8_t.array(len).ptr).contents;
michael@0 640 for (let i = 0; i < len; i++)
michael@0 641 expanded += String.fromCharCode(intData[i]);
michael@0 642 return expanded;
michael@0 643 },
michael@0 644
michael@0 645 encodeBase64 : function (data, len) {
michael@0 646 return btoa(this.expandData(data, len));
michael@0 647 },
michael@0 648
michael@0 649 // Returns a filled SECItem *, as returned by SECITEM_AllocItem.
michael@0 650 //
michael@0 651 // Note that this must be released with freeSECItem, which will also
michael@0 652 // deallocate the internal buffer.
michael@0 653 makeSECItem : function(input, isEncoded) {
michael@0 654 if (isEncoded)
michael@0 655 input = atob(input);
michael@0 656
michael@0 657 let len = input.length;
michael@0 658 let item = this.nss.SECITEM_AllocItem(null, null, len);
michael@0 659 if (item.isNull())
michael@0 660 throw "SECITEM_AllocItem failed.";
michael@0 661
michael@0 662 let ptr = ctypes.cast(item.contents.data,
michael@0 663 ctypes.unsigned_char.array(len).ptr);
michael@0 664 let dest = ctypes.cast(ptr.contents, ctypes.uint8_t.array(len));
michael@0 665 this.byteCompressInts(input, dest, len);
michael@0 666 return item;
michael@0 667 },
michael@0 668
michael@0 669 freeSECItem : function(zap) {
michael@0 670 if (zap && !zap.isNull())
michael@0 671 this.nss.SECITEM_ZfreeItem(zap, true);
michael@0 672 },
michael@0 673
michael@0 674 // We only ever handle one IV at a time, and they're always different.
michael@0 675 // Consequently, we maintain a single SECItem, and a handy pointer into its
michael@0 676 // contents to avoid repetitive and expensive casts.
michael@0 677 _ivSECItem: null,
michael@0 678 _ivSECItemContents: null,
michael@0 679
michael@0 680 initIVSECItem: function initIVSECItem() {
michael@0 681 if (this._ivSECItem) {
michael@0 682 this._ivSECItemContents = null;
michael@0 683 this.freeSECItem(this._ivSECItem);
michael@0 684 }
michael@0 685
michael@0 686 let item = this.nss.SECITEM_AllocItem(null, null, this.blockSize);
michael@0 687 if (item.isNull())
michael@0 688 throw "SECITEM_AllocItem failed.";
michael@0 689
michael@0 690 let ptr = ctypes.cast(item.contents.data,
michael@0 691 ctypes.unsigned_char.array(this.blockSize).ptr);
michael@0 692 let contents = ctypes.cast(ptr.contents,
michael@0 693 ctypes.uint8_t.array(this.blockSize));
michael@0 694 this._ivSECItem = item;
michael@0 695 this._ivSECItemContents = contents;
michael@0 696 },
michael@0 697
michael@0 698 /**
michael@0 699 * Returns the expanded data string for the derived key.
michael@0 700 */
michael@0 701 deriveKeyFromPassphrase : function deriveKeyFromPassphrase(passphrase, salt, keyLength) {
michael@0 702 this.log("deriveKeyFromPassphrase() called.");
michael@0 703 let passItem = this.makeSECItem(passphrase, false);
michael@0 704 let saltItem = this.makeSECItem(salt, true);
michael@0 705
michael@0 706 let pbeAlg = ALGORITHM;
michael@0 707 let cipherAlg = ALGORITHM; // Ignored by callee when pbeAlg != a pkcs5 mech.
michael@0 708
michael@0 709 // Callee picks if SEC_OID_UNKNOWN, but only SHA1 is supported.
michael@0 710 let prfAlg = this.nss.SEC_OID_HMAC_SHA1;
michael@0 711
michael@0 712 let keyLength = keyLength || 0; // 0 = Callee will pick.
michael@0 713 let iterations = KEY_DERIVATION_ITERATIONS;
michael@0 714
michael@0 715 let algid, slot, symKey, keyData;
michael@0 716 try {
michael@0 717 algid = this.nss.PK11_CreatePBEV2AlgorithmID(pbeAlg, cipherAlg, prfAlg,
michael@0 718 keyLength, iterations,
michael@0 719 saltItem);
michael@0 720 if (algid.isNull())
michael@0 721 throw Components.Exception("PK11_CreatePBEV2AlgorithmID failed", Cr.NS_ERROR_FAILURE);
michael@0 722
michael@0 723 slot = this.nss.PK11_GetInternalSlot();
michael@0 724 if (slot.isNull())
michael@0 725 throw Components.Exception("couldn't get internal slot", Cr.NS_ERROR_FAILURE);
michael@0 726
michael@0 727 symKey = this.nss.PK11_PBEKeyGen(slot, algid, passItem, false, null);
michael@0 728 if (symKey.isNull())
michael@0 729 throw Components.Exception("PK11_PBEKeyGen failed", Cr.NS_ERROR_FAILURE);
michael@0 730
michael@0 731 // Take the PK11SymKeyStr, returning the extracted key data.
michael@0 732 if (this.nss.PK11_ExtractKeyValue(symKey)) {
michael@0 733 throw this.makeException("PK11_ExtractKeyValue failed.", Cr.NS_ERROR_FAILURE);
michael@0 734 }
michael@0 735
michael@0 736 keyData = this.nss.PK11_GetKeyData(symKey);
michael@0 737
michael@0 738 if (keyData.isNull())
michael@0 739 throw Components.Exception("PK11_GetKeyData failed", Cr.NS_ERROR_FAILURE);
michael@0 740
michael@0 741 // This copies the key contents into a JS string, so we don't leak.
michael@0 742 // The `finally` block below will clean up.
michael@0 743 return this.expandData(keyData.contents.data, keyData.contents.len);
michael@0 744
michael@0 745 } catch (e) {
michael@0 746 this.log("deriveKeyFromPassphrase: failed: " + e);
michael@0 747 throw e;
michael@0 748 } finally {
michael@0 749 if (algid && !algid.isNull())
michael@0 750 this.nss.SECOID_DestroyAlgorithmID(algid, true);
michael@0 751 if (slot && !slot.isNull())
michael@0 752 this.nss.PK11_FreeSlot(slot);
michael@0 753 if (symKey && !symKey.isNull())
michael@0 754 this.nss.PK11_FreeSymKey(symKey);
michael@0 755
michael@0 756 this.freeSECItem(passItem);
michael@0 757 this.freeSECItem(saltItem);
michael@0 758 }
michael@0 759 },
michael@0 760 };

mercurial