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:
Richard Barnes 2014-07-12 20:06:00 +02:00
parent 4fbd88b3b3
commit d3ecd20c07
10 changed files with 521 additions and 60 deletions

View File

@ -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

View 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

View File

@ -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
}

View File

@ -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

View File

@ -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);
}

View File

@ -8,6 +8,7 @@ TEST_DIRS += ['test']
EXPORTS.mozilla.dom += [
'AesKeyAlgorithm.h',
'BasicSymmetricKeyAlgorithm.h',
'CryptoBuffer.h',
'CryptoKey.h',
'CryptoKeyPair.h',

View File

@ -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"
)
},
}

View File

@ -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 );
}
);*/

View File

@ -67,6 +67,12 @@ dictionary HmacImportParams : Algorithm {
AlgorithmIdentifier hash;
};
dictionary Pbkdf2Params : Algorithm {
CryptoOperationData salt;
[EnforceRange] unsigned long iterations;
AlgorithmIdentifier hash;
};
dictionary RsaHashedImportParams {
AlgorithmIdentifier hash;
};

View File

@ -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