diff --git a/dom/crypto/AesKeyAlgorithm.h b/dom/crypto/AesKeyAlgorithm.h index b0b7ef3790c7..229510f209ea 100644 --- a/dom/crypto/AesKeyAlgorithm.h +++ b/dom/crypto/AesKeyAlgorithm.h @@ -7,18 +7,17 @@ #ifndef mozilla_dom_AesKeyAlgorithm_h #define mozilla_dom_AesKeyAlgorithm_h -#include "mozilla/dom/KeyAlgorithm.h" +#include "mozilla/dom/BasicSymmetricKeyAlgorithm.h" #include "js/TypeDecls.h" namespace mozilla { namespace dom { -class AesKeyAlgorithm MOZ_FINAL : public KeyAlgorithm +class AesKeyAlgorithm MOZ_FINAL : public BasicSymmetricKeyAlgorithm { public: AesKeyAlgorithm(nsIGlobalObject* aGlobal, const nsString& aName, uint16_t aLength) - : KeyAlgorithm(aGlobal, aName) - , mLength(aLength) + : BasicSymmetricKeyAlgorithm(aGlobal, aName, aLength) {} ~AesKeyAlgorithm() @@ -26,17 +25,9 @@ public: virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; - uint16_t Length() const - { - return mLength; - } - virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE; static KeyAlgorithm* Create(nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader); - -protected: - uint16_t mLength; }; } // namespace dom diff --git a/dom/crypto/BasicSymmetricKeyAlgorithm.h b/dom/crypto/BasicSymmetricKeyAlgorithm.h new file mode 100644 index 000000000000..afcdc5f20346 --- /dev/null +++ b/dom/crypto/BasicSymmetricKeyAlgorithm.h @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_BasicSymmetricKeyAlgorithm_h +#define mozilla_dom_BasicSymmetricKeyAlgorithm_h + +#include "mozilla/dom/KeyAlgorithm.h" + +namespace mozilla { +namespace dom { + +class BasicSymmetricKeyAlgorithm : public KeyAlgorithm +{ +public: + BasicSymmetricKeyAlgorithm(nsIGlobalObject* aGlobal, const nsString& aName, uint16_t aLength) + : KeyAlgorithm(aGlobal, aName) + , mLength(aLength) + {} + + ~BasicSymmetricKeyAlgorithm() + {} + + uint16_t Length() const + { + return mLength; + } + +protected: + uint16_t mLength; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_BasicSymmetricKeyAlgorithm_h diff --git a/dom/crypto/KeyAlgorithm.cpp b/dom/crypto/KeyAlgorithm.cpp index 72e4b5c5bcfd..ae03d8524f56 100644 --- a/dom/crypto/KeyAlgorithm.cpp +++ b/dom/crypto/KeyAlgorithm.cpp @@ -32,27 +32,8 @@ KeyAlgorithm::KeyAlgorithm(nsIGlobalObject* aGlobal, const nsString& aName) SetIsDOMBinding(); // Set mechanism based on algorithm name - if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) { - mMechanism = CKM_AES_CBC_PAD; - } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) { - mMechanism = CKM_AES_CTR; - } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) { - mMechanism = CKM_AES_GCM; - } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) { - mMechanism = CKM_SHA_1; - } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) { - mMechanism = CKM_SHA256; - } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) { - mMechanism = CKM_SHA384; - } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) { - mMechanism = CKM_SHA512; - } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) { - mMechanism = CKM_RSA_PKCS; - } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) { - mMechanism = CKM_RSA_PKCS; - } else { - mMechanism = UNKNOWN_CK_MECHANISM; - } + mMechanism = MapAlgorithmNameToMechanism(aName); + // HMAC not handled here, since it requires extra info } diff --git a/dom/crypto/WebCryptoCommon.h b/dom/crypto/WebCryptoCommon.h index 5b1ce22a8744..f25a8dce5de9 100644 --- a/dom/crypto/WebCryptoCommon.h +++ b/dom/crypto/WebCryptoCommon.h @@ -21,6 +21,7 @@ #define WEBCRYPTO_ALG_SHA384 "SHA-384" #define WEBCRYPTO_ALG_SHA512 "SHA-512" #define WEBCRYPTO_ALG_HMAC "HMAC" +#define WEBCRYPTO_ALG_PBKDF2 "PBKDF2" #define WEBCRYPTO_ALG_RSAES_PKCS1 "RSAES-PKCS1-v1_5" #define WEBCRYPTO_ALG_RSASSA_PKCS1 "RSASSA-PKCS1-v1_5" @@ -119,6 +120,37 @@ WriteBuffer(JSStructuredCloneWriter* aWriter, const CryptoBuffer& aBuffer) return ret; } +inline CK_MECHANISM_TYPE +MapAlgorithmNameToMechanism(const nsString& aName) +{ + CK_MECHANISM_TYPE mechanism(UNKNOWN_CK_MECHANISM); + + // Set mechanism based on algorithm name + if (aName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) { + mechanism = CKM_AES_CBC_PAD; + } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) { + mechanism = CKM_AES_CTR; + } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) { + mechanism = CKM_AES_GCM; + } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) { + mechanism = CKM_SHA_1; + } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) { + mechanism = CKM_SHA256; + } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) { + mechanism = CKM_SHA384; + } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) { + mechanism = CKM_SHA512; + } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) { + mechanism = CKM_PKCS5_PBKD2; + } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) { + mechanism = CKM_RSA_PKCS; + } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) { + mechanism = CKM_RSA_PKCS; + } + + return mechanism; +} + } // namespace dom } // namespace mozilla diff --git a/dom/crypto/WebCryptoTask.cpp b/dom/crypto/WebCryptoTask.cpp index a99434348fb7..2b50693aa986 100644 --- a/dom/crypto/WebCryptoTask.cpp +++ b/dom/crypto/WebCryptoTask.cpp @@ -148,6 +148,78 @@ Coerce(JSContext* aCx, T& aTarget, const OOS& aAlgorithm) return NS_OK; } +inline size_t +MapHashAlgorithmNameToBlockSize(const nsString& aName) +{ + if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1) || + aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) { + return 512; + } + + if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384) || + aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) { + return 1024; + } + + return 0; +} + +inline nsresult +GetKeySizeForAlgorithm(JSContext* aCx, const ObjectOrString& aAlgorithm, + size_t& aLength) +{ + aLength = 0; + + // Extract algorithm name + nsString algName; + if (NS_FAILED(GetAlgorithmName(aCx, aAlgorithm, algName))) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + + // Read AES key length from given algorithm object. + if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) || + algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) || + algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) { + RootedDictionary params(aCx); + if (NS_FAILED(Coerce(aCx, params, aAlgorithm)) || + !params.mLength.WasPassed()) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + + size_t length = params.mLength.Value(); + if (length != 128 && length != 192 && length != 256) { + return NS_ERROR_DOM_DATA_ERR; + } + + aLength = length; + return NS_OK; + } + + // Determine HMAC key length as the block size of the given hash. + if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) { + RootedDictionary params(aCx); + if (NS_FAILED(Coerce(aCx, params, aAlgorithm)) || + !params.mHash.WasPassed()) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + + nsString hashName; + if (NS_FAILED(GetAlgorithmName(aCx, params.mHash.Value(), hashName))) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + + size_t length = MapHashAlgorithmNameToBlockSize(hashName); + if (length == 0) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + + aLength = length; + return NS_OK; + } + + return NS_ERROR_DOM_NOT_SUPPORTED_ERR; +} + // Implementation of WebCryptoTask methods void @@ -828,10 +900,12 @@ public: mKeyData.Assign(aKeyData.GetAsArrayBufferView()); } else if (aKeyData.IsArrayBuffer()) { mKeyData.Assign(aKeyData.GetAsArrayBuffer()); - } else { - mEarlyRv = NS_ERROR_DOM_DATA_ERR; - return; } + // We would normally fail here if the key data is not an ArrayBuffer or + // an ArrayBufferView but let's wait for BeforeCrypto() to be called in + // case PBKDF2's deriveKey() operation passed dummy key data. When that + // happens DerivePbkdfKeyTask is responsible for calling SetKeyData() + // itself before this task is actually run. } else if (aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) { mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR; return; @@ -859,6 +933,11 @@ public: virtual nsresult BeforeCrypto() MOZ_OVERRIDE { + // Check that we have valid key data. + if (mKeyData.Length() == 0) { + return NS_ERROR_DOM_DATA_ERR; + } + // Construct an appropriate KeyAlorithm, // and verify that usages are appropriate nsRefPtr algorithm; @@ -875,6 +954,11 @@ public: return NS_ERROR_DOM_DATA_ERR; } algorithm = new AesKeyAlgorithm(global, mAlgName, length); + } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) { + if (mKey->HasUsageOtherThan(CryptoKey::DERIVEKEY)) { + return NS_ERROR_DOM_DATA_ERR; + } + algorithm = new BasicSymmetricKeyAlgorithm(global, mAlgName, length); } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) { if (mKey->HasUsageOtherThan(CryptoKey::SIGN | CryptoKey::VERIFY)) { return NS_ERROR_DOM_DATA_ERR; @@ -895,6 +979,12 @@ public: return NS_OK; } + void SetKeyData(const CryptoBuffer& aKeyData) + { + // An OOM will just result in an error in BeforeCrypto + mKeyData = aKeyData; + } + private: CryptoBuffer mKeyData; nsString mHashName; @@ -1129,16 +1219,8 @@ public: if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) || algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) || algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) { - RootedDictionary params(aCx); - mEarlyRv = Coerce(aCx, params, aAlgorithm); - if (NS_FAILED(mEarlyRv) || !params.mLength.WasPassed()) { - mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; - return; - } - - mLength = params.mLength.Value(); - if (mLength != 128 && mLength != 192 && mLength != 256) { - mEarlyRv = NS_ERROR_DOM_DATA_ERR; + mEarlyRv = GetKeySizeForAlgorithm(aCx, aAlgorithm, mLength); + if (NS_FAILED(mEarlyRv)) { return; } algorithm = new AesKeyAlgorithm(global, algName, mLength); @@ -1167,20 +1249,7 @@ public: if (params.mLength.WasPassed()) { mLength = params.mLength.Value(); } else { - nsRefPtr hashAlg = new KeyAlgorithm(global, hashName); - switch (hashAlg->Mechanism()) { - case CKM_SHA_1: - case CKM_SHA256: - mLength = 512; - break; - case CKM_SHA384: - case CKM_SHA512: - mLength = 1024; - break; - default: - mLength = 0; - break; - } + mLength = MapHashAlgorithmNameToBlockSize(hashName); } if (mLength == 0) { @@ -1421,6 +1490,160 @@ private: } }; +class DerivePbkdfBitsTask : public ReturnArrayBufferViewTask +{ +public: + DerivePbkdfBitsTask(JSContext* aCx, + const ObjectOrString& aAlgorithm, CryptoKey& aKey, uint32_t aLength) + : mSymKey(aKey.GetSymKey()) + { + Init(aCx, aAlgorithm, aKey, aLength); + } + + DerivePbkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm, + CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm) + : mSymKey(aKey.GetSymKey()) + { + size_t length; + mEarlyRv = GetKeySizeForAlgorithm(aCx, aTargetAlgorithm, length); + + if (NS_SUCCEEDED(mEarlyRv)) { + Init(aCx, aAlgorithm, aKey, length); + } + } + + void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey, + uint32_t aLength) + { + // Check that we got a symmetric key + if (mSymKey.Length() == 0) { + mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR; + return; + } + + RootedDictionary params(aCx); + mEarlyRv = Coerce(aCx, params, aAlgorithm); + if (NS_FAILED(mEarlyRv) || !params.mHash.WasPassed() || + !params.mIterations.WasPassed() || !params.mSalt.WasPassed()) { + mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; + return; + } + + // length must be a multiple of 8 bigger than zero. + if (aLength == 0 || aLength % 8) { + mEarlyRv = NS_ERROR_DOM_DATA_ERR; + return; + } + + // Extract the hash algorithm. + nsString hashName; + mEarlyRv = GetAlgorithmName(aCx, params.mHash.Value(), hashName); + if (NS_FAILED(mEarlyRv)) { + return; + } + + // Check the given hash algorithm. + switch (MapAlgorithmNameToMechanism(hashName)) { + case CKM_SHA_1: mHashOidTag = SEC_OID_HMAC_SHA1; break; + case CKM_SHA256: mHashOidTag = SEC_OID_HMAC_SHA256; break; + case CKM_SHA384: mHashOidTag = SEC_OID_HMAC_SHA384; break; + case CKM_SHA512: mHashOidTag = SEC_OID_HMAC_SHA512; break; + default: { + mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR; + return; + } + } + + ATTEMPT_BUFFER_INIT(mSalt, params.mSalt.Value()) + mLength = aLength >> 3; // bits to bytes + mIterations = params.mIterations.Value(); + } + +private: + size_t mLength; + size_t mIterations; + CryptoBuffer mSalt; + CryptoBuffer mSymKey; + SECOidTag mHashOidTag; + + virtual nsresult DoCrypto() MOZ_OVERRIDE + { + ScopedSECItem salt; + ATTEMPT_BUFFER_TO_SECITEM(salt, mSalt); + + // Always pass in cipherAlg=SEC_OID_HMAC_SHA1 (i.e. PBMAC1) as this + // parameter is unused for key generation. It is currently only used + // for PBKDF2 authentication or key (un)wrapping when specifying an + // encryption algorithm (PBES2). + ScopedSECAlgorithmID alg_id(PK11_CreatePBEV2AlgorithmID( + SEC_OID_PKCS5_PBKDF2, SEC_OID_HMAC_SHA1, mHashOidTag, + mLength, mIterations, salt)); + + if (!alg_id.get()) { + return NS_ERROR_DOM_OPERATION_ERR; + } + + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + if (!slot.get()) { + return NS_ERROR_DOM_OPERATION_ERR; + } + + ScopedSECItem keyItem; + ATTEMPT_BUFFER_TO_SECITEM(keyItem, mSymKey); + + ScopedPK11SymKey symKey(PK11_PBEKeyGen(slot, alg_id, keyItem, false, nullptr)); + if (!symKey.get()) { + return NS_ERROR_DOM_OPERATION_ERR; + } + + nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey)); + if (NS_FAILED(rv)) { + return NS_ERROR_DOM_OPERATION_ERR; + } + + // This doesn't leak, because the SECItem* returned by PK11_GetKeyData + // just refers to a buffer managed by symKey. The assignment copies the + // data, so mResult manages one copy, while symKey manages another. + ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey)); + return NS_OK; + } +}; + +class DerivePbkdfKeyTask : public DerivePbkdfBitsTask +{ +public: + DerivePbkdfKeyTask(JSContext* aCx, + const ObjectOrString& aAlgorithm, CryptoKey& aBaseKey, + const ObjectOrString& aDerivedKeyType, bool aExtractable, + const Sequence& aKeyUsages) + : DerivePbkdfBitsTask(aCx, aAlgorithm, aBaseKey, aDerivedKeyType) + { + if (NS_FAILED(mEarlyRv)) { + return; + } + + CryptoOperationData dummy; + NS_NAMED_LITERAL_STRING(format, WEBCRYPTO_KEY_FORMAT_RAW); + + mTask = new ImportSymmetricKeyTask(aCx, format, dummy, aDerivedKeyType, + aExtractable, aKeyUsages); + } + +protected: + nsRefPtr mTask; + +private: + virtual void Resolve() MOZ_OVERRIDE { + mTask->SetKeyData(mResult); + mTask->DispatchWithPromise(mResultPromise); + } + + virtual void Cleanup() MOZ_OVERRIDE + { + mTask = nullptr; + } +}; + // Task creation methods for WebCryptoTask @@ -1520,6 +1743,7 @@ WebCryptoTask::ImportKeyTask(JSContext* aCx, if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) || algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) || algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) || + algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) || algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) { return new ImportSymmetricKeyTask(aCx, aFormat, aKeyData, aAlgorithm, aExtractable, aKeyUsages); @@ -1582,6 +1806,18 @@ WebCryptoTask::DeriveKeyTask(JSContext* aCx, const Sequence& aKeyUsages) { Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEKEY); + + nsString algName; + nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName); + if (NS_FAILED(rv)) { + return new FailureTask(rv); + } + + if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) { + return new DerivePbkdfKeyTask(aCx, aAlgorithm, aBaseKey, aDerivedKeyType, + aExtractable, aKeyUsages); + } + return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR); } @@ -1592,6 +1828,17 @@ WebCryptoTask::DeriveBitsTask(JSContext* aCx, uint32_t aLength) { Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEBITS); + + nsString algName; + nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName); + if (NS_FAILED(rv)) { + return new FailureTask(rv); + } + + if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) { + return new DerivePbkdfBitsTask(aCx, aAlgorithm, aKey, aLength); + } + return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR); } diff --git a/dom/crypto/moz.build b/dom/crypto/moz.build index a603aabe21f1..4c7e60de983b 100644 --- a/dom/crypto/moz.build +++ b/dom/crypto/moz.build @@ -8,6 +8,7 @@ TEST_DIRS += ['test'] EXPORTS.mozilla.dom += [ 'AesKeyAlgorithm.h', + 'BasicSymmetricKeyAlgorithm.h', 'CryptoBuffer.h', 'CryptoKey.h', 'CryptoKeyPair.h', diff --git a/dom/crypto/test/test-vectors.js b/dom/crypto/test/test-vectors.js index 9d0916fc1c23..5d47e51c9ac6 100644 --- a/dom/crypto/test/test-vectors.js +++ b/dom/crypto/test/test-vectors.js @@ -310,4 +310,29 @@ tv = { "bcf51540afd0174db4033188556675b1d763360af46feeca5b60f882829ee7b2" ), }, + + // RFC 6070 + pbkdf2_sha1: { + password: new TextEncoder("utf-8").encode("passwordPASSWORDpassword"), + salt: new TextEncoder("utf-8").encode("saltSALTsaltSALTsaltSALTsaltSALTsalt"), + iterations: 4096, + length: 25 * 8, + + derived: util.hex2abv( + "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038" + ) + }, + + // https://stackoverflow.com/questions/5130513/pbkdf2-hmac-sha2-test-vectors + pbkdf2_sha256: { + password: new TextEncoder("utf-8").encode("passwordPASSWORDpassword"), + salt: new TextEncoder("utf-8").encode("saltSALTsaltSALTsaltSALTsaltSALTsalt"), + iterations: 4096, + length: 40 * 8, + + derived: util.hex2abv( + "348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c4e2a1fb8dd53e1" + + "c635518c7dac47e9" + ) + }, } diff --git a/dom/crypto/test/tests.js b/dom/crypto/test/tests.js index 6f121c9d831f..d1de549b5c41 100644 --- a/dom/crypto/test/tests.js +++ b/dom/crypto/test/tests.js @@ -1018,4 +1018,135 @@ TestArray.addTest( } ); +// ----------------------------------------------------------------------------- +TestArray.addTest( + "Import raw PBKDF2 key", + function() { + var that = this; + var alg = "PBKDF2"; + var key = new TextEncoder("utf-8").encode("password"); + crypto.subtle.importKey("raw", key, alg, false, ["deriveKey"]).then( + complete(that, hasKeyFields), + error(that) + ); + } +); + +// ----------------------------------------------------------------------------- +TestArray.addTest( + "Import raw PBKDF2 key and derive bits using HMAC-SHA-1", + function() { + var that = this; + var alg = "PBKDF2"; + var key = tv.pbkdf2_sha1.password; + + function doDerive(x) { + console.log("deriving"); + if (!hasKeyFields(x)) { + throw "Invalid key; missing field(s)"; + } + + var alg = { + name: "PBKDF2", + hash: "SHA-1", + salt: tv.pbkdf2_sha1.salt, + iterations: tv.pbkdf2_sha1.iterations + }; + return crypto.subtle.deriveBits(alg, x, tv.pbkdf2_sha1.length); + } + function fail(x) { console.log("failing"); error(that)(x); } + + crypto.subtle.importKey("raw", key, alg, false, ["deriveKey"]) + .then( doDerive, fail ) + .then( memcmp_complete(that, tv.pbkdf2_sha1.derived), fail ); + } +); + +// ----------------------------------------------------------------------------- +TestArray.addTest( + "Import raw PBKDF2 key and derive a new key using HMAC-SHA-1", + function() { + var that = this; + var alg = "PBKDF2"; + var key = tv.pbkdf2_sha1.password; + + function doDerive(x) { + console.log("deriving"); + if (!hasKeyFields(x)) { + throw "Invalid key; missing field(s)"; + } + + var alg = { + name: "PBKDF2", + hash: "SHA-1", + salt: tv.pbkdf2_sha1.salt, + iterations: tv.pbkdf2_sha1.iterations + }; + + var algDerived = { + name: "HMAC", + hash: {name: "SHA-1"} + }; + + return crypto.subtle.deriveKey(alg, x, algDerived, false, ["sign", "verify"]) + .then(function (x) { + if (!hasKeyFields(x)) { + throw "Invalid key; missing field(s)"; + } + + if (x.algorithm.length != 512) { + throw "Invalid key; incorrect length"; + } + + return x; + }); + } + + function doSignAndVerify(x) { + var data = new Uint8Array(1024); + + return crypto.subtle.sign("HMAC", x, data) + .then(function (sig) { + return crypto.subtle.verify("HMAC", x, sig, data); + }); + } + + function fail(x) { console.log("failing"); error(that)(x); } + + crypto.subtle.importKey("raw", key, alg, false, ["deriveKey"]) + .then( doDerive, fail ) + .then( doSignAndVerify, fail ) + .then( complete(that), fail ); + } +); + +// ----------------------------------------------------------------------------- +/*TestArray.addTest( + "Import raw PBKDF2 key and derive bits using HMAC-SHA-256", + function() { + var that = this; + var alg = "PBKDF2"; + var key = tv.pbkdf2_sha256.password; + + function doDerive(x) { + console.log("deriving"); + if (!hasKeyFields(x)) { + throw "Invalid key; missing field(s)"; + } + + var alg = { + name: "PBKDF2", + hash: "SHA-256", + salt: tv.pbkdf2_sha256.salt, + iterations: tv.pbkdf2_sha256.iterations + }; + return crypto.subtle.deriveBits(alg, x, tv.pbkdf2_sha256.length); + } + function fail(x) { console.log("failing"); error(that)(x); } + + crypto.subtle.importKey("raw", key, alg, false, ["deriveKey"]) + .then( doDerive, fail ) + .then( memcmp_complete(that, tv.pbkdf2_sha256.derived), fail ); + } +);*/ diff --git a/dom/webidl/SubtleCrypto.webidl b/dom/webidl/SubtleCrypto.webidl index 865280f4d534..4fc9999762fb 100644 --- a/dom/webidl/SubtleCrypto.webidl +++ b/dom/webidl/SubtleCrypto.webidl @@ -67,6 +67,12 @@ dictionary HmacImportParams : Algorithm { AlgorithmIdentifier hash; }; +dictionary Pbkdf2Params : Algorithm { + CryptoOperationData salt; + [EnforceRange] unsigned long iterations; + AlgorithmIdentifier hash; +}; + dictionary RsaHashedImportParams { AlgorithmIdentifier hash; }; diff --git a/security/manager/ssl/src/ScopedNSSTypes.h b/security/manager/ssl/src/ScopedNSSTypes.h index 4934e76957bf..f287eb79c92f 100644 --- a/security/manager/ssl/src/ScopedNSSTypes.h +++ b/security/manager/ssl/src/ScopedNSSTypes.h @@ -291,6 +291,11 @@ inline void SECITEM_FreeItem_true(SECItem * s) return SECITEM_FreeItem(s, true); } +inline void SECOID_DestroyAlgorithmID_true(SECAlgorithmID * a) +{ + return SECOID_DestroyAlgorithmID(a, true); +} + } // namespace internal MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSECItem, @@ -303,6 +308,10 @@ MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSECKEYPrivateKey, MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSECKEYPublicKey, SECKEYPublicKey, SECKEY_DestroyPublicKey) +MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSECAlgorithmID, + SECAlgorithmID, + internal::SECOID_DestroyAlgorithmID_true) + } // namespace mozilla