mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 22:32:46 +00:00
Bug 1021607 - Add support for PBKDF2 to WebCrypto API. r=bz
0001 - Introduce BasicSymmetricKeyAlgorithm r=rbarnes 0002 - Implement import key operation for PBKDF2 r=bz,rbarnes 0003 - Introduce MapAlgorithmNameToMechanism() r=rbarnes 0004 - Implement derive bits operation for PBKDF2 r=rbarnes,keeler,bz 0005 - Introduce GetKeySizeForAlgorithm() r=rbarnes 0006 - Implement derive keys operation for PBKDF2 r=rbarnes,bz
This commit is contained in:
parent
4fbd88b3b3
commit
d3ecd20c07
@ -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
|
||||
|
38
dom/crypto/BasicSymmetricKeyAlgorithm.h
Normal file
38
dom/crypto/BasicSymmetricKeyAlgorithm.h
Normal file
@ -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
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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<AesKeyGenParams> 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<HmacImportParams> 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<KeyAlgorithm> 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<AesKeyGenParams> 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<KeyAlgorithm> 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<Pbkdf2Params> 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<nsString>& 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<ImportSymmetricKeyTask> 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<nsString>& 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);
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ TEST_DIRS += ['test']
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'AesKeyAlgorithm.h',
|
||||
'BasicSymmetricKeyAlgorithm.h',
|
||||
'CryptoBuffer.h',
|
||||
'CryptoKey.h',
|
||||
'CryptoKeyPair.h',
|
||||
|
@ -310,4 +310,29 @@ tv = {
|
||||
"bcf51540afd0174db4033188556675b1d763360af46feeca5b60f882829ee7b2"
|
||||
),
|
||||
},
|
||||
|
||||
// RFC 6070 <http://tools.ietf.org/html/rfc6070>
|
||||
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"
|
||||
)
|
||||
},
|
||||
}
|
||||
|
@ -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 );
|
||||
}
|
||||
);*/
|
||||
|
@ -67,6 +67,12 @@ dictionary HmacImportParams : Algorithm {
|
||||
AlgorithmIdentifier hash;
|
||||
};
|
||||
|
||||
dictionary Pbkdf2Params : Algorithm {
|
||||
CryptoOperationData salt;
|
||||
[EnforceRange] unsigned long iterations;
|
||||
AlgorithmIdentifier hash;
|
||||
};
|
||||
|
||||
dictionary RsaHashedImportParams {
|
||||
AlgorithmIdentifier hash;
|
||||
};
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user