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 298663 0e747813be13880fda46dca14981b47cae8688b3
parent 298662 25a5ae2711f38f202ab57b20518d3554072f2f47
child 298664 2ffaaeccf3d96c3345724900bc8c86b32ce50093
push id30284
push usercbook@mozilla.com
push dateWed, 25 May 2016 11:57:54 +0000
treeherdermozilla-central@242c458c4c61 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler
bugs1234305
milestone49.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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());
-}