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