Bug 1234305 - Replace WeaveCrypto NSS implementation with Web Crypto. r=keeler
authorEdouard Oger <eoger@fastmail.com>
Wed, 11 May 2016 13:46:04 -0700
changeset 298270 0e747813be13880fda46dca14981b47cae8688b3
parent 298269 25a5ae2711f38f202ab57b20518d3554072f2f47
child 298271 2ffaaeccf3d96c3345724900bc8c86b32ce50093
push id19348
push usercbook@mozilla.com
push dateTue, 24 May 2016 09:38:55 +0000
treeherderfx-team@86f8ed931685 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler
bugs1234305
milestone49.0a1
Bug 1234305 - Replace WeaveCrypto NSS implementation with Web Crypto. r=keeler MozReview-Commit-ID: BBOY9zSLzea
services/crypto/modules/WeaveCrypto.js
services/crypto/tests/unit/test_crypto_crypt.js
services/crypto/tests/unit/test_crypto_deriveKey.js
--- a/services/crypto/modules/WeaveCrypto.js
+++ b/services/crypto/modules/WeaveCrypto.js
@@ -3,758 +3,264 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 this.EXPORTED_SYMBOLS = ["WeaveCrypto"];
 
 var {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/ctypes.jsm");
-Cu.import('resource://gre/modules/AppConstants.jsm');
+Cu.import("resource://services-common/async.js");
+
+Cu.importGlobalProperties(['crypto']);
 
-/**
- * Shortcuts for some algorithm SEC OIDs.  Full list available here:
- * http://lxr.mozilla.org/seamonkey/source/security/nss/lib/util/secoidt.h
- */
-const DES_EDE3_CBC = 156;
-const AES_128_CBC  = 184;
-const AES_192_CBC  = 186;
-const AES_256_CBC  = 188;
+const CRYPT_ALGO        = "AES-CBC";
+const CRYPT_ALGO_LENGTH = 256;
+const AES_CBC_IV_SIZE   = 16;
+const OPERATIONS        = { ENCRYPT: 0, DECRYPT: 1 };
+const UTF_LABEL          = "utf-8";
 
-const ALGORITHM                 = AES_256_CBC;
-const KEYSIZE_AES_256           = 32;
-const KEY_DERIVATION_ITERATIONS = 4096;   // PKCS#5 recommends at least 1000.
-const INITIAL_BUFFER_SIZE       = 1024;
+const KEY_DERIVATION_ALGO         = "PBKDF2";
+const KEY_DERIVATION_HASHING_ALGO = "SHA-1";
+const KEY_DERIVATION_ITERATIONS   = 4096; // PKCS#5 recommends at least 1000.
+const DERIVED_KEY_ALGO            = CRYPT_ALGO;
 
 this.WeaveCrypto = function WeaveCrypto() {
     this.init();
-}
+};
 
 WeaveCrypto.prototype = {
     prefBranch : null,
     debug      : true,  // services.sync.log.cryptoDebug
-    nss        : null,
-    nss_t      : null,
 
     observer : {
         _self : null,
 
         QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver,
                                                 Ci.nsISupportsWeakReference]),
 
-        observe : function (subject, topic, data) {
+        observe(subject, topic, data) {
             let self = this._self;
             self.log("Observed " + topic + " topic.");
             if (topic == "nsPref:changed") {
                 self.debug = self.prefBranch.getBoolPref("cryptoDebug");
             }
         }
     },
 
-    init : function() {
+    init() {
+        // Preferences. Add observer so we get notified of changes.
+        this.prefBranch = Services.prefs.getBranch("services.sync.log.");
+        this.prefBranch.addObserver("cryptoDebug", this.observer, false);
+        this.observer._self = this;
         try {
-            // Preferences. Add observer so we get notified of changes.
-            this.prefBranch = Services.prefs.getBranch("services.sync.log.");
-            this.prefBranch.addObserver("cryptoDebug", this.observer, false);
-            this.observer._self = this;
-            try {
-              this.debug = this.prefBranch.getBoolPref("cryptoDebug");
-            } catch (x) {
-              this.debug = false;
-            }
-
-            this.initNSS();
-            this.initAlgorithmSettings();   // Depends on NSS.
-            this.initIVSECItem();
-            this.initSharedInts();
-            this.initBuffers(INITIAL_BUFFER_SIZE);
-        } catch (e) {
-            this.log("init failed: " + e);
-            throw e;
+          this.debug = this.prefBranch.getBoolPref("cryptoDebug");
+        } catch (x) {
+          this.debug = false;
         }
+        XPCOMUtils.defineLazyGetter(this, 'encoder', () => new TextEncoder(UTF_LABEL));
+        XPCOMUtils.defineLazyGetter(this, 'decoder', () => new TextDecoder(UTF_LABEL, { fatal: true }));
     },
 
-    // Avoid allocating new temporary ints on every run of _commonCrypt.
-    _commonCryptSignedOutputSize:       null,
-    _commonCryptSignedOutputSizeAddr:   null,
-    _commonCryptUnsignedOutputSize:     null,
-    _commonCryptUnsignedOutputSizeAddr: null,
-
-    initSharedInts: function initSharedInts() {
-        let signed   = new ctypes.int();
-        let unsigned = new ctypes.unsigned_int();
-        this._commonCryptSignedOutputSize       = signed;
-        this._commonCryptUnsignedOutputSize     = unsigned;
-        this._commonCryptSignedOutputSizeAddr   = signed.address();
-        this._commonCryptUnsignedOutputSizeAddr = unsigned.address();
-    },
-
-    /**
-     * Set a bunch of NSS values once, at init-time. These are:
-     *   - .blockSize
-     *   - .mechanism
-     *   - .keygenMechanism
-     *   - .padMechanism
-     *   - .keySize
-     *
-     * See also the constant ALGORITHM.
-     */
-    initAlgorithmSettings: function() {
-        this.mechanism = this.nss.PK11_AlgtagToMechanism(ALGORITHM);
-        this.blockSize = this.nss.PK11_GetBlockSize(this.mechanism, null);
-        this.ivLength  = this.nss.PK11_GetIVLength(this.mechanism);
-        this.keySize   = KEYSIZE_AES_256;
-        this.keygenMechanism = this.nss.CKM_AES_KEY_GEN;  // Always the same!
-
-        // Determine which (padded) PKCS#11 mechanism to use.
-        // E.g., AES_256_CBC --> CKM_AES_CBC --> CKM_AES_CBC_PAD
-        this.padMechanism = this.nss.PK11_GetPadMechanism(this.mechanism);
-        if (this.padMechanism == this.nss.CKM_INVALID_MECHANISM)
-            throw Components.Exception("invalid algorithm (can't pad)", Cr.NS_ERROR_FAILURE);
-    },
-
-    log : function (message) {
-        if (!this.debug)
+    log(message) {
+        if (!this.debug) {
             return;
+        }
         dump("WeaveCrypto: " + message + "\n");
         Services.console.logStringMessage("WeaveCrypto: " + message);
     },
 
-    initNSS : function() {
-        // We use NSS for the crypto ops, which needs to be initialized before
-        // use. By convention, PSM is required to be the module that
-        // initializes NSS. So, make sure PSM is initialized in order to
-        // implicitly initialize NSS.
-        Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
+    // /!\ Only use this for tests! /!\
+    _getCrypto() {
+        return crypto;
+    },
 
-        // Open the NSS library.
-        let path = ctypes.libraryName("nss3");
+    encrypt(clearTextUCS2, symmetricKey, iv) {
+        this.log("encrypt() called");
+        let clearTextBuffer = this.encoder.encode(clearTextUCS2).buffer;
+        let encrypted = this._commonCrypt(clearTextBuffer, symmetricKey, iv, OPERATIONS.ENCRYPT);
+        return this.encodeBase64(encrypted);
+    },
+
+    decrypt(cipherText, symmetricKey, iv) {
+        this.log("decrypt() called");
+        if (cipherText.length) {
+            cipherText = atob(cipherText);
+        }
+        let cipherTextBuffer = this.byteCompressInts(cipherText);
+        let decrypted = this._commonCrypt(cipherTextBuffer, symmetricKey, iv, OPERATIONS.DECRYPT);
+        return this.decoder.decode(decrypted);
+    },
 
-        // XXX really want to be able to pass specific dlopen flags here.
-        var nsslib;
-        if (AppConstants.MOZ_SYSTEM_NSS) {
-            // Search platform-dependent library paths for system NSS.
-            this.log("Trying NSS library without path");
-            nsslib = ctypes.open(path);
-        } else {
-            let file = Services.dirsvc.get("GreBinD", Ci.nsILocalFile);
-            file.append(path);
-            this.log("Trying NSS library with path " + file.path);
-            nsslib = ctypes.open(file.path);
+    /**
+     * _commonCrypt
+     *
+     * @args
+     * data: data to encrypt/decrypt (ArrayBuffer)
+     * symKeyStr: symmetric key (Base64 String)
+     * ivStr: initialization vector (Base64 String)
+     * operation: operation to apply (either OPERATIONS.ENCRYPT or OPERATIONS.DECRYPT)
+     * @returns
+     * the encrypted/decrypted data (ArrayBuffer)
+    */
+    _commonCrypt(data, symKeyStr, ivStr, operation) {
+        this.log("_commonCrypt() called");
+        ivStr = atob(ivStr);
+
+        if (operation !== OPERATIONS.ENCRYPT && operation !== OPERATIONS.DECRYPT) {
+            throw new Error("Unsupported operation in _commonCrypt.");
+        }
+        // We never want an IV longer than the block size, which is 16 bytes
+        // for AES, neither do we want one smaller; throw in both cases.
+        if (ivStr.length !== AES_CBC_IV_SIZE) {
+            throw "Invalid IV size; must be " + AES_CBC_IV_SIZE + " bytes.";
         }
 
-        this.log("Initializing NSS types and function declarations...");
-
-        this.nss = {};
-        this.nss_t = {};
-
-        // nsprpub/pr/include/prtypes.h#435
-        // typedef PRIntn PRBool; --> int
-        this.nss_t.PRBool = ctypes.int;
-        // security/nss/lib/util/seccomon.h#91
-        // typedef enum
-        this.nss_t.SECStatus = ctypes.int;
-        // security/nss/lib/softoken/secmodt.h#59
-        // typedef struct PK11SlotInfoStr PK11SlotInfo; (defined in secmodti.h) 
-        this.nss_t.PK11SlotInfo = ctypes.void_t;
-        // security/nss/lib/util/pkcs11t.h
-        this.nss_t.CK_MECHANISM_TYPE = ctypes.unsigned_long;
-        this.nss_t.CK_ATTRIBUTE_TYPE = ctypes.unsigned_long;
-        this.nss_t.CK_KEY_TYPE       = ctypes.unsigned_long;
-        this.nss_t.CK_OBJECT_HANDLE  = ctypes.unsigned_long;
-        // security/nss/lib/softoken/secmodt.h#359
-        // typedef enum PK11Origin
-        this.nss_t.PK11Origin = ctypes.int;
-        // PK11Origin enum values...
-        this.nss.PK11_OriginUnwrap = 4;
-        // security/nss/lib/softoken/secmodt.h#61
-        // typedef struct PK11SymKeyStr PK11SymKey; (defined in secmodti.h)
-        this.nss_t.PK11SymKey = ctypes.void_t;
-        // security/nss/lib/util/secoidt.h#454
-        // typedef enum
-        this.nss_t.SECOidTag = ctypes.int;
-        // security/nss/lib/util/seccomon.h#64
-        // typedef enum
-        this.nss_t.SECItemType = ctypes.int;
-        // SECItemType enum values...
-        this.nss.SIBUFFER = 0;
-        // security/nss/lib/softoken/secmodt.h#62 (defined in secmodti.h)
-        // typedef struct PK11ContextStr PK11Context;
-        this.nss_t.PK11Context = ctypes.void_t;
-        // Needed for SECKEYPrivateKey struct def'n, but I don't think we need to actually access it.
-        this.nss_t.PLArenaPool = ctypes.void_t;
-        // security/nss/lib/cryptohi/keythi.h#45
-        // typedef enum
-        this.nss_t.KeyType = ctypes.int;
-        // security/nss/lib/softoken/secmodt.h#201
-        // typedef PRUint32 PK11AttrFlags;
-        this.nss_t.PK11AttrFlags = ctypes.unsigned_int;
-        // security/nss/lib/util/seccomon.h#83
-        // typedef struct SECItemStr SECItem; --> SECItemStr defined right below it
-        this.nss_t.SECItem = ctypes.StructType(
-            "SECItem", [{ type: this.nss_t.SECItemType },
-                        { data: ctypes.unsigned_char.ptr },
-                        { len : ctypes.int }]);
-        // security/nss/lib/util/secoidt.h#52
-        // typedef struct SECAlgorithmIDStr --> def'n right below it
-        this.nss_t.SECAlgorithmID = ctypes.StructType(
-            "SECAlgorithmID", [{ algorithm:  this.nss_t.SECItem },
-                               { parameters: this.nss_t.SECItem }]);
-
-
-        // security/nss/lib/util/pkcs11t.h
-        this.nss.CKK_RSA = 0x0;
-        this.nss.CKM_RSA_PKCS_KEY_PAIR_GEN = 0x0000;
-        this.nss.CKM_AES_KEY_GEN           = 0x1080; 
-        this.nss.CKA_ENCRYPT = 0x104;
-        this.nss.CKA_DECRYPT = 0x105;
-
-        // security/nss/lib/softoken/secmodt.h
-        this.nss.PK11_ATTR_SESSION   = 0x02;
-        this.nss.PK11_ATTR_PUBLIC    = 0x08;
-        this.nss.PK11_ATTR_SENSITIVE = 0x40;
-
-        // security/nss/lib/util/secoidt.h
-        this.nss.SEC_OID_PKCS5_PBKDF2         = 291;
-        this.nss.SEC_OID_HMAC_SHA1            = 294;
-        this.nss.SEC_OID_PKCS1_RSA_ENCRYPTION = 16;
+        let iv = this.byteCompressInts(ivStr);
+        let symKey = this.importSymKey(symKeyStr, operation);
+        let cryptMethod = (operation === OPERATIONS.ENCRYPT
+                           ? crypto.subtle.encrypt
+                           : crypto.subtle.decrypt)
+                          .bind(crypto.subtle);
+        let algo = { name: CRYPT_ALGO, iv: iv };
 
 
-        // security/nss/lib/pk11wrap/pk11pub.h#286
-        // SECStatus PK11_GenerateRandom(unsigned char *data,int len);
-        this.nss.PK11_GenerateRandom = nsslib.declare("PK11_GenerateRandom",
-                                                      ctypes.default_abi, this.nss_t.SECStatus,
-                                                      ctypes.unsigned_char.ptr, ctypes.int);
-        // security/nss/lib/pk11wrap/pk11pub.h#74
-        // PK11SlotInfo *PK11_GetInternalSlot(void);
-        this.nss.PK11_GetInternalSlot = nsslib.declare("PK11_GetInternalSlot",
-                                                       ctypes.default_abi, this.nss_t.PK11SlotInfo.ptr);
-        // security/nss/lib/pk11wrap/pk11pub.h#73
-        // PK11SlotInfo *PK11_GetInternalKeySlot(void);
-        this.nss.PK11_GetInternalKeySlot = nsslib.declare("PK11_GetInternalKeySlot",
-                                                          ctypes.default_abi, this.nss_t.PK11SlotInfo.ptr);
-        // security/nss/lib/pk11wrap/pk11pub.h#328
-        // PK11SymKey *PK11_KeyGen(PK11SlotInfo *slot,CK_MECHANISM_TYPE type, SECItem *param, int keySize,void *wincx);
-        this.nss.PK11_KeyGen = nsslib.declare("PK11_KeyGen",
-                                              ctypes.default_abi, this.nss_t.PK11SymKey.ptr,
-                                              this.nss_t.PK11SlotInfo.ptr, this.nss_t.CK_MECHANISM_TYPE,
-                                              this.nss_t.SECItem.ptr, ctypes.int, ctypes.voidptr_t);
-        // security/nss/lib/pk11wrap/pk11pub.h#477
-        // SECStatus PK11_ExtractKeyValue(PK11SymKey *symKey);
-        this.nss.PK11_ExtractKeyValue = nsslib.declare("PK11_ExtractKeyValue",
-                                                       ctypes.default_abi, this.nss_t.SECStatus,
-                                                       this.nss_t.PK11SymKey.ptr);
-        // security/nss/lib/pk11wrap/pk11pub.h#478
-        // SECItem * PK11_GetKeyData(PK11SymKey *symKey);
-        this.nss.PK11_GetKeyData = nsslib.declare("PK11_GetKeyData",
-                                                  ctypes.default_abi, this.nss_t.SECItem.ptr,
-                                                  this.nss_t.PK11SymKey.ptr);
-        // security/nss/lib/pk11wrap/pk11pub.h#278
-        // CK_MECHANISM_TYPE PK11_AlgtagToMechanism(SECOidTag algTag);
-        this.nss.PK11_AlgtagToMechanism = nsslib.declare("PK11_AlgtagToMechanism",
-                                                         ctypes.default_abi, this.nss_t.CK_MECHANISM_TYPE,
-                                                         this.nss_t.SECOidTag);
-        // security/nss/lib/pk11wrap/pk11pub.h#270
-        // int PK11_GetIVLength(CK_MECHANISM_TYPE type);
-        this.nss.PK11_GetIVLength = nsslib.declare("PK11_GetIVLength",
-                                                   ctypes.default_abi, ctypes.int,
-                                                   this.nss_t.CK_MECHANISM_TYPE);
-        // security/nss/lib/pk11wrap/pk11pub.h#269
-        // int PK11_GetBlockSize(CK_MECHANISM_TYPE type,SECItem *params);
-        this.nss.PK11_GetBlockSize = nsslib.declare("PK11_GetBlockSize",
-                                                    ctypes.default_abi, ctypes.int,
-                                                    this.nss_t.CK_MECHANISM_TYPE, this.nss_t.SECItem.ptr);
-        // security/nss/lib/pk11wrap/pk11pub.h#293
-        // CK_MECHANISM_TYPE PK11_GetPadMechanism(CK_MECHANISM_TYPE);
-        this.nss.PK11_GetPadMechanism = nsslib.declare("PK11_GetPadMechanism",
-                                                       ctypes.default_abi, this.nss_t.CK_MECHANISM_TYPE,
-                                                       this.nss_t.CK_MECHANISM_TYPE);
-        // security/nss/lib/pk11wrap/pk11pub.h#271
-        // SECItem *PK11_ParamFromIV(CK_MECHANISM_TYPE type,SECItem *iv);
-        this.nss.PK11_ParamFromIV = nsslib.declare("PK11_ParamFromIV",
-                                                   ctypes.default_abi, this.nss_t.SECItem.ptr,
-                                                   this.nss_t.CK_MECHANISM_TYPE, this.nss_t.SECItem.ptr);
-        // security/nss/lib/pk11wrap/pk11pub.h#301
-        // PK11SymKey *PK11_ImportSymKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, PK11Origin origin,
-        //                               CK_ATTRIBUTE_TYPE operation, SECItem *key, void *wincx);
-        this.nss.PK11_ImportSymKey = nsslib.declare("PK11_ImportSymKey",
-                                                    ctypes.default_abi, this.nss_t.PK11SymKey.ptr,
-                                                    this.nss_t.PK11SlotInfo.ptr, this.nss_t.CK_MECHANISM_TYPE, this.nss_t.PK11Origin,
-                                                    this.nss_t.CK_ATTRIBUTE_TYPE, this.nss_t.SECItem.ptr, ctypes.voidptr_t);
-        // security/nss/lib/pk11wrap/pk11pub.h#672
-        // PK11Context *PK11_CreateContextBySymKey(CK_MECHANISM_TYPE type, CK_ATTRIBUTE_TYPE operation,
-        //                                         PK11SymKey *symKey, SECItem *param);
-        this.nss.PK11_CreateContextBySymKey = nsslib.declare("PK11_CreateContextBySymKey",
-                                                             ctypes.default_abi, this.nss_t.PK11Context.ptr,
-                                                             this.nss_t.CK_MECHANISM_TYPE, this.nss_t.CK_ATTRIBUTE_TYPE,
-                                                             this.nss_t.PK11SymKey.ptr, this.nss_t.SECItem.ptr);
-        // security/nss/lib/pk11wrap/pk11pub.h#685
-        // SECStatus PK11_CipherOp(PK11Context *context, unsigned char *out
-        //                         int *outlen, int maxout, unsigned char *in, int inlen);
-        this.nss.PK11_CipherOp = nsslib.declare("PK11_CipherOp",
-                                                ctypes.default_abi, this.nss_t.SECStatus,
-                                                this.nss_t.PK11Context.ptr, ctypes.unsigned_char.ptr,
-                                                ctypes.int.ptr, ctypes.int, ctypes.unsigned_char.ptr, ctypes.int);
-        // security/nss/lib/pk11wrap/pk11pub.h#688
-        // SECStatus PK11_DigestFinal(PK11Context *context, unsigned char *data,
-        //                            unsigned int *outLen, unsigned int length);
-        this.nss.PK11_DigestFinal = nsslib.declare("PK11_DigestFinal",
-                                                   ctypes.default_abi, this.nss_t.SECStatus,
-                                                   this.nss_t.PK11Context.ptr, ctypes.unsigned_char.ptr,
-                                                   ctypes.unsigned_int.ptr, ctypes.unsigned_int);
-        // security/nss/lib/pk11wrap/pk11pub.h#731
-        // SECAlgorithmID * PK11_CreatePBEV2AlgorithmID(SECOidTag pbeAlgTag, SECOidTag cipherAlgTag,
-        //                                              SECOidTag prfAlgTag, int keyLength, int iteration,
-        //                                              SECItem *salt);
-        this.nss.PK11_CreatePBEV2AlgorithmID = nsslib.declare("PK11_CreatePBEV2AlgorithmID",
-                                                              ctypes.default_abi, this.nss_t.SECAlgorithmID.ptr,
-                                                              this.nss_t.SECOidTag, this.nss_t.SECOidTag, this.nss_t.SECOidTag, 
-                                                              ctypes.int, ctypes.int, this.nss_t.SECItem.ptr);
-        // security/nss/lib/pk11wrap/pk11pub.h#736
-        // PK11SymKey * PK11_PBEKeyGen(PK11SlotInfo *slot, SECAlgorithmID *algid,  SECItem *pwitem, PRBool faulty3DES, void *wincx);
-        this.nss.PK11_PBEKeyGen = nsslib.declare("PK11_PBEKeyGen",
-                                                 ctypes.default_abi, this.nss_t.PK11SymKey.ptr,
-                                                 this.nss_t.PK11SlotInfo.ptr, this.nss_t.SECAlgorithmID.ptr,
-                                                 this.nss_t.SECItem.ptr, this.nss_t.PRBool, ctypes.voidptr_t);
-        // security/nss/lib/pk11wrap/pk11pub.h#675
-        // void PK11_DestroyContext(PK11Context *context, PRBool freeit);
-        this.nss.PK11_DestroyContext = nsslib.declare("PK11_DestroyContext",
-                                                       ctypes.default_abi, ctypes.void_t,
-                                                       this.nss_t.PK11Context.ptr, this.nss_t.PRBool);
-        // security/nss/lib/pk11wrap/pk11pub.h#299
-        // void PK11_FreeSymKey(PK11SymKey *key);
-        this.nss.PK11_FreeSymKey = nsslib.declare("PK11_FreeSymKey",
-                                                  ctypes.default_abi, ctypes.void_t,
-                                                  this.nss_t.PK11SymKey.ptr);
-        // security/nss/lib/pk11wrap/pk11pub.h#70
-        // void PK11_FreeSlot(PK11SlotInfo *slot);
-        this.nss.PK11_FreeSlot = nsslib.declare("PK11_FreeSlot",
-                                                ctypes.default_abi, ctypes.void_t,
-                                                this.nss_t.PK11SlotInfo.ptr);
-        // security/nss/lib/util/secitem.h#49
-        // extern SECItem *SECITEM_AllocItem(PRArenaPool *arena, SECItem *item, unsigned int len);
-        this.nss.SECITEM_AllocItem = nsslib.declare("SECITEM_AllocItem",
-                                                    ctypes.default_abi, this.nss_t.SECItem.ptr,
-                                                    this.nss_t.PLArenaPool.ptr,     // Not used.
-                                                    this.nss_t.SECItem.ptr, ctypes.unsigned_int);
-        // security/nss/lib/util/secitem.h#274
-        // extern void SECITEM_ZfreeItem(SECItem *zap, PRBool freeit);
-        this.nss.SECITEM_ZfreeItem = nsslib.declare("SECITEM_ZfreeItem",
-                                                    ctypes.default_abi, ctypes.void_t,
-                                                    this.nss_t.SECItem.ptr, this.nss_t.PRBool);
-        // security/nss/lib/util/secitem.h#114
-        // extern void SECITEM_FreeItem(SECItem *zap, PRBool freeit);
-        this.nss.SECITEM_FreeItem = nsslib.declare("SECITEM_FreeItem",
-                                                   ctypes.default_abi, ctypes.void_t,
-                                                   this.nss_t.SECItem.ptr, this.nss_t.PRBool);
-        // security/nss/lib/util/secoid.h#103
-        // extern void SECOID_DestroyAlgorithmID(SECAlgorithmID *aid, PRBool freeit);
-        this.nss.SECOID_DestroyAlgorithmID = nsslib.declare("SECOID_DestroyAlgorithmID",
-                                                            ctypes.default_abi, ctypes.void_t,
-                                                            this.nss_t.SECAlgorithmID.ptr, this.nss_t.PRBool);
+        return Async.promiseSpinningly(
+            cryptMethod(algo, symKey, data)
+            .then(keyBytes => new Uint8Array(keyBytes))
+        );
     },
 
 
-    _sharedInputBuffer:      null,
-    _sharedInputBufferInts:  null,
-    _sharedInputBufferSize:  0,
-    _sharedOutputBuffer:     null,
-    _sharedOutputBufferSize: 0,
-    _randomByteBuffer:       null,
-    _randomByteBufferAddr:   null,
-    _randomByteBufferSize:   0,
-
-    _getInputBuffer: function _getInputBuffer(size) {
-      if (size > this._sharedInputBufferSize) {
-        let b = new ctypes.ArrayType(ctypes.unsigned_char, size)();
-        this._sharedInputBuffer     = b;
-        this._sharedInputBufferInts = ctypes.cast(b, ctypes.uint8_t.array(size));
-        this._sharedInputBufferSize = size;
-      }
-      return this._sharedInputBuffer;
-    },
-
-    _getOutputBuffer: function _getOutputBuffer(size) {
-      if (size > this._sharedOutputBufferSize) {
-        let b = new ctypes.ArrayType(ctypes.unsigned_char, size)();
-        this._sharedOutputBuffer     = b;
-        this._sharedOutputBufferSize = size;
-      }
-      return this._sharedOutputBuffer;
-    },
-
-    _getRandomByteBuffer: function _getRandomByteBuffer(size) {
-        if (size > this._randomByteBufferSize) {
-          let b = new ctypes.ArrayType(ctypes.unsigned_char, size)();
-          this._randomByteBuffer     = b;
-          this._randomByteBufferAddr = b.address();
-          this._randomByteBufferSize = size;
-        }
-        return this._randomByteBuffer;
-    },
-
-    initBuffers: function initBuffers(initialSize) {
-        this._getInputBuffer(initialSize);
-        this._getOutputBuffer(initialSize);
-
-        this._getRandomByteBuffer(this.ivLength);
-    },
-
-    encrypt : function(clearTextUCS2, symmetricKey, iv) {
-        this.log("encrypt() called");
-
-        // js-ctypes autoconverts to a UTF8 buffer, but also includes a null
-        // at the end which we don't want. Decrement length to skip it.
-        let inputBuffer = new ctypes.ArrayType(ctypes.unsigned_char)(clearTextUCS2);
-        let inputBufferSize = inputBuffer.length - 1;
-
-        // When using CBC padding, the output size is the input size rounded
-        // up to the nearest block. If the input size is exactly on a block
-        // boundary, the output is 1 extra block long.
-        let outputBufferSize = inputBufferSize + this.blockSize;
-        let outputBuffer = this._getOutputBuffer(outputBufferSize);
-
-        outputBuffer = this._commonCrypt(inputBuffer, inputBufferSize,
-                                         outputBuffer, outputBufferSize,
-                                         symmetricKey, iv, this.nss.CKA_ENCRYPT);
-
-        return this.encodeBase64(outputBuffer.address(), outputBuffer.length);
-    },
-
-
-    decrypt : function(cipherText, symmetricKey, iv) {
-        this.log("decrypt() called");
-
-        let inputUCS2 = "";
-        if (cipherText.length)
-            inputUCS2 = atob(cipherText);
-
-        // We can't have js-ctypes create the buffer directly from the string
-        // (as in encrypt()), because we do _not_ want it to do UTF8
-        // conversion... We've got random binary data in the input's low byte.
-        //
-        // Compress a JS string (2-byte chars) into a normal C string (1-byte chars).
-        let len   = inputUCS2.length;
-        let input = this._getInputBuffer(len);
-        this.byteCompressInts(inputUCS2, this._sharedInputBufferInts, len);
-
-        let outputBuffer = this._commonCrypt(input, len,
-                                             this._getOutputBuffer(len), len,
-                                             symmetricKey, iv, this.nss.CKA_DECRYPT);
-
-        // outputBuffer contains UTF-8 data, let js-ctypes autoconvert that to a JS string.
-        // XXX Bug 573842: wrap the string from ctypes to get a new string, so
-        // we don't hit bug 573841.
-        return "" + outputBuffer.readString() + "";
+    generateRandomKey() {
+        this.log("generateRandomKey() called");
+        let algo = {
+            name: CRYPT_ALGO,
+            length: CRYPT_ALGO_LENGTH
+        };
+        return Async.promiseSpinningly(
+            crypto.subtle.generateKey(algo, true, [])
+            .then(key => crypto.subtle.exportKey("raw", key))
+            .then(keyBytes => {
+                keyBytes = new Uint8Array(keyBytes);
+                return this.encodeBase64(keyBytes);
+            })
+        );
     },
 
-    _commonCrypt : function (input, inputLength, output, outputLength, symmetricKey, iv, operation) {
-        this.log("_commonCrypt() called");
-        iv = atob(iv);
-
-        // We never want an IV longer than the block size, which is 16 bytes
-        // for AES. Neither do we want one smaller; throw in that case.
-        if (iv.length < this.blockSize)
-            throw "IV too short; must be " + this.blockSize + " bytes.";
-        if (iv.length > this.blockSize)
-            iv = iv.slice(0, this.blockSize);
-
-        // We use a single IV SECItem for the sake of efficiency. Fill it here.
-        this.byteCompressInts(iv, this._ivSECItemContents, iv.length);
-
-        let ctx, symKey, ivParam;
-        try {
-            ivParam = this.nss.PK11_ParamFromIV(this.padMechanism, this._ivSECItem);
-            if (ivParam.isNull())
-                throw Components.Exception("can't convert IV to param", Cr.NS_ERROR_FAILURE);
-
-            symKey = this.importSymKey(symmetricKey, operation);
-            ctx = this.nss.PK11_CreateContextBySymKey(this.padMechanism, operation, symKey, ivParam);
-            if (ctx.isNull())
-                throw Components.Exception("couldn't create context for symkey", Cr.NS_ERROR_FAILURE);
-
-            let maxOutputSize = outputLength;
-            if (this.nss.PK11_CipherOp(ctx, output, this._commonCryptSignedOutputSize.address(), maxOutputSize, input, inputLength))
-                throw Components.Exception("cipher operation failed", Cr.NS_ERROR_FAILURE);
-
-            let actualOutputSize = this._commonCryptSignedOutputSize.value;
-            let finalOutput = output.addressOfElement(actualOutputSize);
-            maxOutputSize -= actualOutputSize;
-
-            // PK11_DigestFinal sure sounds like the last step for *hashing*, but it
-            // just seems to be an odd name -- NSS uses this to finish the current
-            // cipher operation. You'd think it would be called PK11_CipherOpFinal...
-            if (this.nss.PK11_DigestFinal(ctx, finalOutput, this._commonCryptUnsignedOutputSizeAddr, maxOutputSize))
-                throw Components.Exception("cipher finalize failed", Cr.NS_ERROR_FAILURE);
-
-            actualOutputSize += this._commonCryptUnsignedOutputSize.value;
-            let newOutput = ctypes.cast(output, ctypes.unsigned_char.array(actualOutputSize));
-            return newOutput;
-        } catch (e) {
-            this.log("_commonCrypt: failed: " + e);
-            throw e;
-        } finally {
-            if (ctx && !ctx.isNull())
-                this.nss.PK11_DestroyContext(ctx, true);
-            if (ivParam && !ivParam.isNull())
-                this.nss.SECITEM_FreeItem(ivParam, true);
-
-            // Note that we do not free the IV SECItem; we reuse it.
-            // Neither do we free the symKey, because that's memoized.
-        }
+    generateRandomIV() {
+      return this.generateRandomBytes(AES_CBC_IV_SIZE);
     },
 
-
-    generateRandomKey : function() {
-        this.log("generateRandomKey() called");
-        let slot, randKey, keydata;
-        try {
-            slot = this.nss.PK11_GetInternalSlot();
-            if (slot.isNull())
-                throw Components.Exception("couldn't get internal slot", Cr.NS_ERROR_FAILURE);
-
-            randKey = this.nss.PK11_KeyGen(slot, this.keygenMechanism, null, this.keySize, null);
-            if (randKey.isNull())
-                throw Components.Exception("PK11_KeyGen failed.", Cr.NS_ERROR_FAILURE);
-
-            // Slightly odd API, this call just prepares the key value for
-            // extraction, we get the actual bits from the call to PK11_GetKeyData().
-            if (this.nss.PK11_ExtractKeyValue(randKey))
-                throw Components.Exception("PK11_ExtractKeyValue failed.", Cr.NS_ERROR_FAILURE);
-
-            keydata = this.nss.PK11_GetKeyData(randKey);
-            if (keydata.isNull())
-                throw Components.Exception("PK11_GetKeyData failed.", Cr.NS_ERROR_FAILURE);
-
-            return this.encodeBase64(keydata.contents.data, keydata.contents.len);
-        } catch (e) {
-            this.log("generateRandomKey: failed: " + e);
-            throw e;
-        } finally {
-            if (randKey && !randKey.isNull())
-                this.nss.PK11_FreeSymKey(randKey);
-            if (slot && !slot.isNull())
-                this.nss.PK11_FreeSlot(slot);
-        }
-    },
-
-    generateRandomIV : function() {
-      return this.generateRandomBytes(this.ivLength);
-    },
-
-    generateRandomBytes : function(byteCount) {
+    generateRandomBytes(byteCount) {
         this.log("generateRandomBytes() called");
 
-        // Temporary buffer to hold the generated data.
-        let scratch = this._getRandomByteBuffer(byteCount);
-        if (this.nss.PK11_GenerateRandom(scratch, byteCount))
-            throw Components.Exception("PK11_GenrateRandom failed", Cr.NS_ERROR_FAILURE);
+        let randBytes = new Uint8Array(byteCount);
+        crypto.getRandomValues(randBytes);
 
-        return this.encodeBase64(this._randomByteBufferAddr, byteCount);
+        return this.encodeBase64(randBytes);
     },
 
     //
-    // PK11SymKey memoization.
+    // SymKey CryptoKey memoization.
     //
 
-    // Memoize the lookup of symmetric keys. We do this by using the base64
-    // string itself as a key -- the overhead of SECItem creation during the
-    // initial population is negligible, so that phase is not memoized.
+    // Memoize the import of symmetric keys. We do this by using the base64
+    // string itself as a key.
     _encryptionSymKeyMemo: {},
     _decryptionSymKeyMemo: {},
-    importSymKey: function importSymKey(encodedKeyString, operation) {
+    importSymKey(encodedKeyString, operation) {
         let memo;
 
         // We use two separate memos for thoroughness: operation is an input to
         // key import.
         switch (operation) {
-            case this.nss.CKA_ENCRYPT:
+            case OPERATIONS.ENCRYPT:
                 memo = this._encryptionSymKeyMemo;
                 break;
-            case this.nss.CKA_DECRYPT:
+            case OPERATIONS.DECRYPT:
                 memo = this._decryptionSymKeyMemo;
                 break;
             default:
                 throw "Unsupported operation in importSymKey.";
         }
 
         if (encodedKeyString in memo)
             return memo[encodedKeyString];
 
-        let keyItem, slot;
-        try {
-            keyItem = this.makeSECItem(encodedKeyString, true);
-            slot    = this.nss.PK11_GetInternalKeySlot();
-            if (slot.isNull())
-                throw Components.Exception("can't get internal key slot",
-                                           Cr.NS_ERROR_FAILURE);
+        let symmetricKeyBuffer = this.makeUint8Array(encodedKeyString, true);
+        let algo = { name: CRYPT_ALGO };
+        let usages = [operation === OPERATIONS.ENCRYPT ? "encrypt" : "decrypt"];
 
-            let symKey = this.nss.PK11_ImportSymKey(slot, this.padMechanism,
-                                                    this.nss.PK11_OriginUnwrap,
-                                                    operation, keyItem, null);
-            if (!symKey || symKey.isNull())
-                throw Components.Exception("symkey import failed",
-                                           Cr.NS_ERROR_FAILURE);
-
-            return memo[encodedKeyString] = symKey;
-        } finally {
-            if (slot && !slot.isNull())
-                this.nss.PK11_FreeSlot(slot);
-            this.freeSECItem(keyItem);
-        }
+        return Async.promiseSpinningly(
+            crypto.subtle.importKey("raw", symmetricKeyBuffer, algo, false, usages)
+            .then(symKey => {
+                memo[encodedKeyString] = symKey;
+                return symKey;
+            })
+        );
     },
 
 
     //
     // Utility functions
     //
 
     /**
-     * Compress a JS string into a C uint8 array. count is the number of
-     * elements in the destination array. If the array is smaller than the
-     * string, the string is effectively truncated. If the string is smaller
-     * than the array, the array is not 0-padded.
+     * Returns an Uint8Array filled with a JS string,
+     * which means we only keep utf-16 characters from 0x00 to 0xFF.
      */
-    byteCompressInts : function byteCompressInts (jsString, intArray, count) {
-        let len = jsString.length;
-        let end = Math.min(len, count);
-        for (let i = 0; i < end; i++)
-            intArray[i] = jsString.charCodeAt(i) & 0xFF;  // convert to bytes.
+    byteCompressInts(str) {
+        let arrayBuffer = new Uint8Array(str.length);
+        for (let i = 0; i < str.length; i++) {
+            arrayBuffer[i] = str.charCodeAt(i) & 0xFF;
+        }
+        return arrayBuffer;
     },
 
-    // Expand a normal C string (1-byte chars) into a JS string (2-byte chars)
-    // EG, for "ABC",  0x41, 0x42, 0x43 --> 0x0041, 0x0042, 0x0043
-    byteExpand : function (charArray) {
+    expandData(data) {
         let expanded = "";
-        let len = charArray.length;
-        let intData = ctypes.cast(charArray, ctypes.uint8_t.array(len));
-        for (let i = 0; i < len; i++)
-            expanded += String.fromCharCode(intData[i]);
+        for (let i = 0; i < data.length; i++) {
+            expanded += String.fromCharCode(data[i]);
+        }
         return expanded;
     },
 
-    expandData : function expandData(data, len) {
-        // Byte-expand the buffer, so we can treat it as a UCS-2 string
-        // consisting of u0000 - u00FF.
-        let expanded = "";
-        let intData = ctypes.cast(data, ctypes.uint8_t.array(len).ptr).contents;
-        for (let i = 0; i < len; i++)
-            expanded += String.fromCharCode(intData[i]);
-      return expanded;
-    },
-
-    encodeBase64 : function (data, len) {
-        return btoa(this.expandData(data, len));
+    encodeBase64(data) {
+        return btoa(this.expandData(data));
     },
 
-    // Returns a filled SECItem *, as returned by SECITEM_AllocItem.
-    //
-    // Note that this must be released with freeSECItem, which will also
-    // deallocate the internal buffer.
-    makeSECItem : function(input, isEncoded) {
-        if (isEncoded)
+    makeUint8Array(input, isEncoded) {
+        if (isEncoded) {
             input = atob(input);
-
-        let len = input.length;
-        let item = this.nss.SECITEM_AllocItem(null, null, len);
-        if (item.isNull())
-            throw "SECITEM_AllocItem failed.";
-
-        let ptr  = ctypes.cast(item.contents.data,
-                               ctypes.unsigned_char.array(len).ptr);
-        let dest = ctypes.cast(ptr.contents, ctypes.uint8_t.array(len));
-        this.byteCompressInts(input, dest, len);
-        return item;
-    },
-
-    freeSECItem : function(zap) {
-        if (zap && !zap.isNull())
-            this.nss.SECITEM_ZfreeItem(zap, true);
-    },
-
-    // We only ever handle one IV at a time, and they're always different.
-    // Consequently, we maintain a single SECItem, and a handy pointer into its
-    // contents to avoid repetitive and expensive casts.
-    _ivSECItem: null,
-    _ivSECItemContents: null,
-
-    initIVSECItem: function initIVSECItem() {
-        if (this._ivSECItem) {
-            this._ivSECItemContents = null;
-            this.freeSECItem(this._ivSECItem);
         }
-
-        let item = this.nss.SECITEM_AllocItem(null, null, this.blockSize);
-        if (item.isNull())
-            throw "SECITEM_AllocItem failed.";
-
-        let ptr = ctypes.cast(item.contents.data,
-                              ctypes.unsigned_char.array(this.blockSize).ptr);
-        let contents = ctypes.cast(ptr.contents,
-                                   ctypes.uint8_t.array(this.blockSize));
-        this._ivSECItem = item;
-        this._ivSECItemContents = contents;
+        return this.byteCompressInts(input);
     },
 
     /**
      * Returns the expanded data string for the derived key.
      */
-    deriveKeyFromPassphrase : function deriveKeyFromPassphrase(passphrase, salt, keyLength) {
+    deriveKeyFromPassphrase(passphrase, saltStr, keyLength = 32) {
         this.log("deriveKeyFromPassphrase() called.");
-        let passItem = this.makeSECItem(passphrase, false);
-        let saltItem = this.makeSECItem(salt, true);
-
-        let pbeAlg    = ALGORITHM;
-        let cipherAlg = ALGORITHM;   // Ignored by callee when pbeAlg != a pkcs5 mech.
-
-        // Callee picks if SEC_OID_UNKNOWN, but only SHA1 is supported.
-        let prfAlg    = this.nss.SEC_OID_HMAC_SHA1;
-
-        keyLength  = keyLength || 0;    // 0 = Callee will pick.
-        let iterations = KEY_DERIVATION_ITERATIONS;
-
-        let algid, slot, symKey, keyData;
-        try {
-            algid = this.nss.PK11_CreatePBEV2AlgorithmID(pbeAlg, cipherAlg, prfAlg,
-                                                         keyLength, iterations,
-                                                         saltItem);
-            if (algid.isNull())
-                throw Components.Exception("PK11_CreatePBEV2AlgorithmID failed", Cr.NS_ERROR_FAILURE);
-
-            slot = this.nss.PK11_GetInternalSlot();
-            if (slot.isNull())
-                throw Components.Exception("couldn't get internal slot", Cr.NS_ERROR_FAILURE);
-
-            symKey = this.nss.PK11_PBEKeyGen(slot, algid, passItem, false, null);
-            if (symKey.isNull())
-                throw Components.Exception("PK11_PBEKeyGen failed", Cr.NS_ERROR_FAILURE);
-
-            // Take the PK11SymKeyStr, returning the extracted key data.
-            if (this.nss.PK11_ExtractKeyValue(symKey)) {
-                throw this.makeException("PK11_ExtractKeyValue failed.", Cr.NS_ERROR_FAILURE);
-            }
-
-            keyData = this.nss.PK11_GetKeyData(symKey);
-
-            if (keyData.isNull())
-                throw Components.Exception("PK11_GetKeyData failed", Cr.NS_ERROR_FAILURE);
-
-            // This copies the key contents into a JS string, so we don't leak.
-            // The `finally` block below will clean up.
-            return this.expandData(keyData.contents.data, keyData.contents.len);
-
-        } catch (e) {
-            this.log("deriveKeyFromPassphrase: failed: " + e);
-            throw e;
-        } finally {
-            if (algid && !algid.isNull())
-                this.nss.SECOID_DestroyAlgorithmID(algid, true);
-            if (slot && !slot.isNull())
-                this.nss.PK11_FreeSlot(slot);
-            if (symKey && !symKey.isNull())
-                this.nss.PK11_FreeSymKey(symKey);
-
-            this.freeSECItem(passItem);
-            this.freeSECItem(saltItem);
-        }
+        let keyData = this.makeUint8Array(passphrase, false);
+        let salt = this.makeUint8Array(saltStr, true);
+        let importAlgo = { name: KEY_DERIVATION_ALGO };
+        let deriveAlgo = {
+            name: KEY_DERIVATION_ALGO,
+            salt: salt,
+            iterations: KEY_DERIVATION_ITERATIONS,
+            hash: { name: KEY_DERIVATION_HASHING_ALGO },
+        };
+        let derivedKeyType = {
+            name: DERIVED_KEY_ALGO,
+            length: keyLength * 8,
+        };
+        return Async.promiseSpinningly(
+            crypto.subtle.importKey("raw", keyData, importAlgo, false, ["deriveKey"])
+            .then(key => crypto.subtle.deriveKey(deriveAlgo, key, derivedKeyType, true, []))
+            .then(derivedKey => crypto.subtle.exportKey("raw", derivedKey))
+            .then(keyBytes => {
+                keyBytes = new Uint8Array(keyBytes);
+                return this.expandData(keyBytes);
+            })
+        );
     },
 };
--- a/services/crypto/tests/unit/test_crypto_crypt.js
+++ b/services/crypto/tests/unit/test_crypto_crypt.js
@@ -1,59 +1,60 @@
 Cu.import("resource://services-crypto/WeaveCrypto.js");
+Cu.importGlobalProperties(['crypto']);
 
 var cryptoSvc = new WeaveCrypto();
 
 function run_test() {
-  
+
   if ("makeSECItem" in cryptoSvc)   // Only for js-ctypes WeaveCrypto.
     test_makeSECItem();
-  
+
   if (this.gczeal) {
     _("Running crypto tests with gczeal(2).");
     gczeal(2);
   }
   test_bug_617650();
   test_encrypt_decrypt();
-  test_SECItem_byteCompressInts();
   test_key_memoization();
   if (this.gczeal)
     gczeal(0);
 }
 
 function test_key_memoization() {
-  let oldImport = cryptoSvc.nss && cryptoSvc.nss.PK11_ImportSymKey;
+  let cryptoGlobal = cryptoSvc._getCrypto();
+  let oldImport = cryptoGlobal.subtle.importKey;
   if (!oldImport) {
-    _("Couldn't swizzle PK11_ImportSymKey; returning.");
+    _("Couldn't swizzle crypto.subtle.importKey; returning.");
     return;
   }
 
   let iv  = cryptoSvc.generateRandomIV();
   let key = cryptoSvc.generateRandomKey();
   let c   = 0;
-  cryptoSvc.nss.PK11_ImportSymKey = function(slot, type, origin, operation, key, wincx) {
+  cryptoGlobal.subtle.importKey = function(format, keyData, algo, extractable, usages) {
     c++;
-    return oldImport(slot, type, origin, operation, key, wincx);
+    return oldImport.call(cryptoGlobal.subtle, format, keyData, algo, extractable, usages);
   }
 
   // Encryption should cause a single counter increment.
   do_check_eq(c, 0);
   let cipherText = cryptoSvc.encrypt("Hello, world.", key, iv);
   do_check_eq(c, 1);
   cipherText = cryptoSvc.encrypt("Hello, world.", key, iv);
   do_check_eq(c, 1);
 
   // ... as should decryption.
   cryptoSvc.decrypt(cipherText, key, iv);
   cryptoSvc.decrypt(cipherText, key, iv);
   cryptoSvc.decrypt(cipherText, key, iv);
   do_check_eq(c, 2);
 
   // Un-swizzle.
-  cryptoSvc.nss.PK11_ImportSymKey = oldImport;
+  cryptoGlobal.subtle.importKey = oldImport;
 }
 
 function multiple_decrypts(iterations) {
   let iv = cryptoSvc.generateRandomIV();
   let key = cryptoSvc.generateRandomKey();
   let cipherText = cryptoSvc.encrypt("Hello, world.", key, iv);
 
   for (let i = 0; i < iterations; ++i) {
@@ -73,42 +74,23 @@ function test_bug_617650() {
   } else {
     // We can't use gczeal on non-debug builds, so try lots of reps instead.
     _("No gczeal (non-debug build?); attempting 10,000 iterations of multiple_decrypts.");
     multiple_decrypts(10000);
   }
 }
 
 // Just verify that it gets populated with the correct bytes.
-function test_makeSECItem() {
-  Components.utils.import("resource://gre/modules/ctypes.jsm");
-
-  let item1 = cryptoSvc.makeSECItem("abcdefghi", false);
-  do_check_true(!item1.isNull());
-  let intData = ctypes.cast(item1.contents.data, ctypes.uint8_t.array(8).ptr).contents;
-  for (let i = 0; i < 8; ++i)
-    do_check_eq(intData[i], "abcdefghi".charCodeAt(i));
-}
-
-function test_SECItem_byteCompressInts() {
+function test_makeUint8Array() {
   Components.utils.import("resource://gre/modules/ctypes.jsm");
 
-  let item1 = cryptoSvc.makeSECItem("abcdefghi", false);
-  do_check_true(!item1.isNull());
-  let intData = ctypes.cast(item1.contents.data, ctypes.uint8_t.array(8).ptr).contents;
-
-  // Fill it too short.
-  cryptoSvc.byteCompressInts("MMM", intData, 8);
-  for (let i = 0; i < 3; ++i)
-    do_check_eq(intData[i], [77, 77, 77][i]);
-
-  // Fill it too much. Doesn't buffer overrun.
-  cryptoSvc.byteCompressInts("NNNNNNNNNNNNNNNN", intData, 8);
+  let item1 = cryptoSvc.makeUint8Array("abcdefghi", false);
+  do_check_true(item1);
   for (let i = 0; i < 8; ++i)
-    do_check_eq(intData[i], "NNNNNNNNNNNNNNNN".charCodeAt(i));
+    do_check_eq(item1[i], "abcdefghi".charCodeAt(i));
 }
 
 function test_encrypt_decrypt() {
 
   // First, do a normal run with expected usage... Generate a random key and
   // iv, encrypt and decrypt a string.
   var iv = cryptoSvc.generateRandomIV();
   do_check_eq(iv.length, 24);
@@ -117,37 +99,46 @@ function test_encrypt_decrypt() {
   do_check_eq(key.length, 44);
 
   var mySecret = "bacon is a vegetable";
   var cipherText = cryptoSvc.encrypt(mySecret, key, iv);
   do_check_eq(cipherText.length, 44);
 
   var clearText = cryptoSvc.decrypt(cipherText, key, iv);
   do_check_eq(clearText.length, 20);
-  
+
   // Did the text survive the encryption round-trip?
   do_check_eq(clearText, mySecret);
   do_check_neq(cipherText, mySecret); // just to be explicit
 
 
   // Do some more tests with a fixed key/iv, to check for reproducable results.
   key = "St1tFCor7vQEJNug/465dQ==";
   iv  = "oLjkfrLIOnK2bDRvW4kXYA==";
 
   _("Testing small IV.");
   mySecret = "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=";
-  shortiv  = "YWJj";           // "abc": Less than 16.
+  let shortiv  = "YWJj";
   let err;
   try {
     cryptoSvc.encrypt(mySecret, key, shortiv);
   } catch (ex) {
     err = ex;
   }
   do_check_true(!!err);
 
+  _("Testing long IV.");
+  let longiv  = "gsgLRDaxWvIfKt75RjuvFWERt83FFsY2A0TW+0b2iVk=";
+  try {
+    cryptoSvc.encrypt(mySecret, key, longiv);
+  } catch (ex) {
+    err = ex;
+  }
+  do_check_true(!!err);
+
   // Test small input sizes
   mySecret = "";
   cipherText = cryptoSvc.encrypt(mySecret, key, iv);
   clearText = cryptoSvc.decrypt(cipherText, key, iv);
   do_check_eq(cipherText, "OGQjp6mK1a3fs9k9Ml4L3w==");
   do_check_eq(clearText, mySecret);
 
   mySecret = "x";
@@ -198,26 +189,26 @@ function test_encrypt_decrypt() {
   mySecret = "12345678901234567";
   cipherText = cryptoSvc.encrypt(mySecret, key, iv);
   clearText = cryptoSvc.decrypt(cipherText, key, iv);
   do_check_eq(cipherText, "V6aaOZw8pWlYkoIHNkhsP5GvxWJ9+GIAS6lXw+5fHTI=");
   do_check_eq(clearText, mySecret);
 
 
   key = "iz35tuIMq4/H+IYw2KTgow==";
-  iv  = "TJYrvva2KxvkM8hvOIvWp3xgjTXgq5Ss";
+  iv  = "TJYrvva2KxvkM8hvOIvWp3==";
   mySecret = "i like pie";
 
   cipherText = cryptoSvc.encrypt(mySecret, key, iv);
   clearText = cryptoSvc.decrypt(cipherText, key, iv);
   do_check_eq(cipherText, "DLGx8BWqSCLGG7i/xwvvxg==");
   do_check_eq(clearText, mySecret);
 
   key = "c5hG3YG+NC61FFy8NOHQak1ZhMEWO79bwiAfar2euzI=";
-  iv  = "gsgLRDaxWvIfKt75RjuvFWERt83FFsY2A0TW+0b2iVk=";
+  iv  = "gsgLRDaxWvIfKt75RjuvFW==";
   mySecret = "i like pie";
 
   cipherText = cryptoSvc.encrypt(mySecret, key, iv);
   clearText = cryptoSvc.decrypt(cipherText, key, iv);
   do_check_eq(cipherText, "o+ADtdMd8ubzNWurS6jt0Q==");
   do_check_eq(clearText, mySecret);
 
   key = "St1tFCor7vQEJNug/465dQ==";
--- a/services/crypto/tests/unit/test_crypto_deriveKey.js
+++ b/services/crypto/tests/unit/test_crypto_deriveKey.js
@@ -1,38 +1,28 @@
-var btoa;
+Components.utils.import("resource://services-crypto/WeaveCrypto.js");
 
-function test_derive(cryptoSvc) {
+function run_test() {
+  let cryptoSvc = new WeaveCrypto();
   // Extracted from test_utils_deriveKey.
   let pp = "secret phrase";
   let salt = "RE5YUHpQcGl3bg==";   // btoa("DNXPzPpiwn")
-  
+
   // 16-byte, extract key data.
   let k = cryptoSvc.deriveKeyFromPassphrase(pp, salt, 16);
   do_check_eq(16, k.length);
   do_check_eq(btoa(k), "d2zG0d2cBfXnRwMUGyMwyg==");
-  
+
   // Test different key lengths.
   k = cryptoSvc.deriveKeyFromPassphrase(pp, salt, 32);
   do_check_eq(32, k.length);
+  do_check_eq(btoa(k), "d2zG0d2cBfXnRwMUGyMwyroRXtnrSIeLwSDvReSfcyA=");
   let encKey = btoa(k);
-  
+
   // Test via encryption.
   let iv = cryptoSvc.generateRandomIV();
   do_check_eq(cryptoSvc.decrypt(cryptoSvc.encrypt("bacon", encKey, iv), encKey, iv), "bacon");
-  
+
   // Test default length (32).
-  k = cryptoSvc.deriveKeyFromPassphrase(pp, salt, null);
+  k = cryptoSvc.deriveKeyFromPassphrase(pp, salt);
   do_check_eq(32, k.length);
   do_check_eq(encKey, btoa(k));
 }
-
-function run_test() {
-  let cryptoSvc;
-  try {
-    let backstagePass = Components.utils.import("resource://services-crypto/WeaveCrypto.js");
-    btoa = backstagePass.btoa;
-  } catch (ex) {
-    _("Aborting test: no WeaveCrypto.js.");
-    return;
-  }
-  test_derive(new WeaveCrypto());
-}