Bug 998802 - Add support for symmetric-key encryption and MAC to WebCrypto API. r=bz,dkeeler

This commit is contained in:
Richard Barnes 2014-05-23 15:29:00 +02:00
parent cc95c8261d
commit 439225237c
5 changed files with 718 additions and 22 deletions

View File

@ -20,24 +20,6 @@ class OwningArrayBufferViewOrArrayBuffer;
class CryptoBuffer : public FallibleTArray<uint8_t>
{
public:
CryptoBuffer()
: FallibleTArray<uint8_t>()
{}
template<class T>
explicit CryptoBuffer(const T& aData)
: FallibleTArray<uint8_t>()
{
Assign(aData);
}
template<class T>
CryptoBuffer& operator=(const T& aData)
{
Assign(aData);
return *this;
}
uint8_t* Assign(const uint8_t* aData, uint32_t aLength);
uint8_t* Assign(const SECItem* aItem);
uint8_t* Assign(const ArrayBuffer& aData);

View File

@ -24,6 +24,20 @@ namespace dom {
// Convenience functions for extracting / converting information
// OOM-safe CryptoBuffer initialization, suitable for constructors
#define ATTEMPT_BUFFER_INIT(dst, src) \
if (!dst.Assign(src)) { \
mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR; \
return; \
}
// OOM-safe CryptoBuffer-to-SECItem copy, suitable for DoCrypto
#define ATTEMPT_BUFFER_TO_SECITEM(dst, src) \
dst = src.ToSECItem(); \
if (!dst) { \
return NS_ERROR_DOM_UNKNOWN_ERR; \
}
class ClearException
{
public:
@ -107,6 +121,264 @@ private:
}
};
class AesTask : public ReturnArrayBufferViewTask
{
public:
AesTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
mozilla::dom::Key& aKey, const CryptoOperationData& aData,
bool aEncrypt)
: mSymKey(aKey.GetSymKey())
, mEncrypt(aEncrypt)
{
ATTEMPT_BUFFER_INIT(mData, aData);
nsString algName;
mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
if (NS_FAILED(mEarlyRv)) {
return;
}
// Check that we got a reasonable key
if ((mSymKey.Length() != 16) &&
(mSymKey.Length() != 24) &&
(mSymKey.Length() != 32))
{
mEarlyRv = NS_ERROR_DOM_DATA_ERR;
return;
}
// Cache parameters depending on the specific algorithm
if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) {
mMechanism = CKM_AES_CBC_PAD;
AesCbcParams params;
nsresult rv = Coerce(aCx, params, aAlgorithm);
if (NS_FAILED(rv) || !params.mIv.WasPassed()) {
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
return;
}
ATTEMPT_BUFFER_INIT(mIv, params.mIv.Value())
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) {
mMechanism = CKM_AES_CTR;
AesCtrParams params;
nsresult rv = Coerce(aCx, params, aAlgorithm);
if (NS_FAILED(rv) || !params.mCounter.WasPassed() ||
!params.mLength.WasPassed()) {
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
return;
}
ATTEMPT_BUFFER_INIT(mIv, params.mCounter.Value())
if (mIv.Length() != 16) {
mEarlyRv = NS_ERROR_DOM_DATA_ERR;
return;
}
mCounterLength = params.mLength.Value();
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
mMechanism = CKM_AES_GCM;
AesGcmParams params;
nsresult rv = Coerce(aCx, params, aAlgorithm);
if (NS_FAILED(rv) || !params.mIv.WasPassed()) {
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
return;
}
ATTEMPT_BUFFER_INIT(mIv, params.mIv.Value())
if (params.mAdditionalData.WasPassed()) {
ATTEMPT_BUFFER_INIT(mAad, params.mAdditionalData.Value())
}
// 32, 64, 96, 104, 112, 120 or 128
mTagLength = 128;
if (params.mTagLength.WasPassed()) {
mTagLength = params.mTagLength.Value();
if ((mTagLength > 128) ||
!(mTagLength == 32 || mTagLength == 64 ||
(mTagLength >= 96 && mTagLength % 8 == 0))) {
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
return;
}
}
} else {
mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
return;
}
}
private:
CK_MECHANISM_TYPE mMechanism;
CryptoBuffer mSymKey;
CryptoBuffer mIv; // Initialization vector
CryptoBuffer mData;
CryptoBuffer mAad; // Additional Authenticated Data
uint8_t mTagLength;
uint8_t mCounterLength;
bool mEncrypt;
virtual nsresult DoCrypto() MOZ_OVERRIDE
{
nsresult rv;
// Construct the parameters object depending on algorithm
SECItem param;
ScopedSECItem cbcParam;
CK_AES_CTR_PARAMS ctrParams;
CK_GCM_PARAMS gcmParams;
switch (mMechanism) {
case CKM_AES_CBC_PAD:
ATTEMPT_BUFFER_TO_SECITEM(cbcParam, mIv);
param = *cbcParam;
break;
case CKM_AES_CTR:
ctrParams.ulCounterBits = mCounterLength;
MOZ_ASSERT(mIv.Length() == 16);
memcpy(&ctrParams.cb, mIv.Elements(), 16);
param.type = siBuffer;
param.data = (unsigned char*) &ctrParams;
param.len = sizeof(ctrParams);
break;
case CKM_AES_GCM:
gcmParams.pIv = mIv.Elements();
gcmParams.ulIvLen = mIv.Length();
gcmParams.pAAD = mAad.Elements();
gcmParams.ulAADLen = mAad.Length();
gcmParams.ulTagBits = mTagLength;
param.type = siBuffer;
param.data = (unsigned char*) &gcmParams;
param.len = sizeof(gcmParams);
break;
default:
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
// Import the key
ScopedSECItem keyItem;
ATTEMPT_BUFFER_TO_SECITEM(keyItem, mSymKey);
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
MOZ_ASSERT(slot.get());
ScopedPK11SymKey symKey(PK11_ImportSymKey(slot, mMechanism, PK11_OriginUnwrap,
CKA_ENCRYPT, keyItem.get(), nullptr));
if (!symKey) {
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
}
// Initialize the output buffer (enough space for padding / a full tag)
uint32_t dataLen = mData.Length();
uint32_t maxLen = dataLen + 16;
if (!mResult.SetLength(maxLen)) {
return NS_ERROR_DOM_UNKNOWN_ERR;
}
uint32_t outLen = 0;
// Perform the encryption/decryption
if (mEncrypt) {
rv = MapSECStatus(PK11_Encrypt(symKey.get(), mMechanism, &param,
mResult.Elements(), &outLen, maxLen,
mData.Elements(), mData.Length()));
} else {
rv = MapSECStatus(PK11_Decrypt(symKey.get(), mMechanism, &param,
mResult.Elements(), &outLen, maxLen,
mData.Elements(), mData.Length()));
}
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
mResult.SetLength(outLen);
return rv;
}
};
class HmacTask : public WebCryptoTask
{
public:
HmacTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
mozilla::dom::Key& aKey,
const CryptoOperationData& aSignature,
const CryptoOperationData& aData,
bool aSign)
: mMechanism(aKey.Algorithm()->Mechanism())
, mSymKey(aKey.GetSymKey())
, mSign(aSign)
{
ATTEMPT_BUFFER_INIT(mData, aData);
if (!aSign) {
ATTEMPT_BUFFER_INIT(mSignature, aSignature);
}
// Check that we got a symmetric key
if (mSymKey.Length() == 0) {
mEarlyRv = NS_ERROR_DOM_DATA_ERR;
return;
}
}
private:
CK_MECHANISM_TYPE mMechanism;
CryptoBuffer mSymKey;
CryptoBuffer mData;
CryptoBuffer mSignature;
CryptoBuffer mResult;
bool mSign;
virtual nsresult DoCrypto() MOZ_OVERRIDE
{
// Initialize the output buffer
if (!mResult.SetLength(HASH_LENGTH_MAX)) {
return NS_ERROR_DOM_UNKNOWN_ERR;
}
uint32_t outLen;
// Import the key
ScopedSECItem keyItem;
ATTEMPT_BUFFER_TO_SECITEM(keyItem, mSymKey);
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
MOZ_ASSERT(slot.get());
ScopedPK11SymKey symKey(PK11_ImportSymKey(slot, mMechanism, PK11_OriginUnwrap,
CKA_SIGN, keyItem.get(), nullptr));
if (!symKey) {
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
}
// Compute the MAC
SECItem param = { siBuffer, nullptr, 0 };
ScopedPK11Context ctx(PK11_CreateContextBySymKey(mMechanism, CKA_SIGN,
symKey.get(), &param));
if (!ctx.get()) {
return NS_ERROR_DOM_OPERATION_ERR;
}
nsresult rv = MapSECStatus(PK11_DigestBegin(ctx.get()));
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
rv = MapSECStatus(PK11_DigestOp(ctx.get(), mData.Elements(), mData.Length()));
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
rv = MapSECStatus(PK11_DigestFinal(ctx.get(), mResult.Elements(),
&outLen, HASH_LENGTH_MAX));
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
mResult.SetLength(outLen);
return rv;
}
// Returns mResult as an ArrayBufferView, or an error
virtual void Resolve() MOZ_OVERRIDE
{
if (mSign) {
// Return the computed MAC
TypedArrayCreator<Uint8Array> ret(mResult);
mResultPromise->MaybeResolve(ret);
} else {
// Compare the MAC to the provided signature
// No truncation allowed
bool equal = (mResult.Length() == mSignature.Length());
int cmp = NSS_SecureMemcmp(mSignature.Elements(),
mResult.Elements(),
mSignature.Length());
equal = equal && (cmp == 0);
mResultPromise->MaybeResolve(equal);
}
}
};
class SimpleDigestTask : public ReturnArrayBufferViewTask
{
public:
@ -114,10 +386,7 @@ public:
const ObjectOrString& aAlgorithm,
const CryptoOperationData& aData)
{
if (!mData.Assign(aData)) {
mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
return;
}
ATTEMPT_BUFFER_INIT(mData, aData);
nsString algName;
mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
@ -513,6 +782,24 @@ WebCryptoTask::EncryptDecryptTask(JSContext* aCx,
const CryptoOperationData& aData,
bool aEncrypt)
{
nsString algName;
nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
if (NS_FAILED(rv)) {
return new FailureTask(rv);
}
// Ensure key is usable for this operation
if ((aEncrypt && !aKey.HasUsage(Key::ENCRYPT)) ||
(!aEncrypt && !aKey.HasUsage(Key::DECRYPT))) {
return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
}
if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
return new AesTask(aCx, aAlgorithm, aKey, aData, aEncrypt);
}
return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
}
@ -524,6 +811,22 @@ WebCryptoTask::SignVerifyTask(JSContext* aCx,
const CryptoOperationData& aData,
bool aSign)
{
nsString algName;
nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
if (NS_FAILED(rv)) {
return new FailureTask(rv);
}
// Ensure key is usable for this operation
if ((aSign && !aKey.HasUsage(Key::SIGN)) ||
(!aSign && !aKey.HasUsage(Key::VERIFY))) {
return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
}
if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
return new HmacTask(aCx, aAlgorithm, aKey, aSignature, aData, aSign);
}
return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
}

View File

@ -121,6 +121,7 @@ public:
const CryptoOperationData& aData)
{
CryptoOperationData dummy;
dummy.SetAsArrayBuffer(aCx);
return SignVerifyTask(aCx, aAlgorithm, aKey, dummy, aData, true);
}

View File

@ -50,4 +50,156 @@ tv = {
result: util.hex2abv("248D6A61D20638B8E5C026930C3E6039A33CE45964F" +
"F2167F6ECEDD419DB06C1"),
},
// Test vector 2 from:
// <https://github.com/geertj/bluepass/blob/master/tests/vectors/aes-cbc-pkcs7.txt>
aes_cbc_enc: {
/*
key: util.hex2abv("893123f2d57b6e2c39e2f10d3ff818d1"),
iv: util.hex2abv("64be1b06ea7453ed2df9a79319d5edc5"),
data: util.hex2abv("44afb9a64ac896c2"),
result: util.hex2abv("7067c4cb6dfc69df949c2f39903c9310"),
*/
key: util.hex2abv("893123f2d57b6e2c39e2f10d3ff818d1"),
iv: util.hex2abv("64be1b06ea7453ed2df9a79319d5edc5"),
data: util.hex2abv("44afb9a64ac896c2"),
result: util.hex2abv("7067c4cb6dfc69df949c2f39903c9310"),
},
// Test vector 11 from:
// <https://github.com/geertj/bluepass/blob/master/tests/vectors/aes-cbc-pkcs7.txt>
aes_cbc_dec: {
key: util.hex2abv("04952c3fcf497a4d449c41e8730c5d9a"),
iv: util.hex2abv("53549bf7d5553b727458c1abaf0ba167"),
data: util.hex2abv("7fa290322ca7a1a04b61a1147ff20fe6" +
"6fde58510a1d0289d11c0ddf6f4decfd"),
result: util.hex2abv("c9a44f6f75e98ddbca7332167f5c45e3"),
},
// Test vector 2 from:
// <http://tools.ietf.org/html/rfc3686#section-6>
aes_ctr_enc: {
key: util.hex2abv("7E24067817FAE0D743D6CE1F32539163"),
iv: util.hex2abv("006CB6DBC0543B59DA48D90B00000001"),
data: util.hex2abv("000102030405060708090A0B0C0D0E0F" +
"101112131415161718191A1B1C1D1E1F"),
result: util.hex2abv("5104A106168A72D9790D41EE8EDAD3" +
"88EB2E1EFC46DA57C8FCE630DF9141BE28"),
},
// Test vector 3 from:
// <http://tools.ietf.org/html/rfc3686#section-6>
aes_ctr_dec: {
key: util.hex2abv("7691BE035E5020A8AC6E618529F9A0DC"),
iv: util.hex2abv("00E0017B27777F3F4A1786F000000001"),
data: util.hex2abv("000102030405060708090A0B0C0D0E0F" +
"101112131415161718191A1B1C1D1E1F20212223"),
result: util.hex2abv("C1CF48A89F2FFDD9CF4652E9EFDB72D7" +
"4540A42BDE6D7836D59A5CEAAEF3105325B2072F"),
},
// Test case #18 from McGrew and Viega
// <http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf>
aes_gcm_enc: {
key: util.hex2abv("feffe9928665731c6d6a8f9467308308" +
"feffe9928665731c6d6a8f9467308308"),
iv: util.hex2abv("9313225df88406e555909c5aff5269aa" +
"6a7a9538534f7da1e4c303d2a318a728" +
"c3c0c95156809539fcf0e2429a6b5254" +
"16aedbf5a0de6a57a637b39b"),
adata: util.hex2abv("feedfacedeadbeeffeedfacedeadbeefabaddad2"),
data: util.hex2abv("d9313225f88406e5a55909c5aff5269a" +
"86a7a9531534f7da2e4c303d8a318a72" +
"1c3c0c95956809532fcf0e2449a6b525" +
"b16aedf5aa0de657ba637b39"),
result: util.hex2abv("5a8def2f0c9e53f1f75d7853659e2a20" +
"eeb2b22aafde6419a058ab4f6f746bf4" +
"0fc0c3b780f244452da3ebf1c5d82cde" +
"a2418997200ef82e44ae7e3f" +
"a44a8266ee1c8eb0c8b5d4cf5ae9f19a"),
},
// Test case #17 from McGrew and Viega
// <http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf>
aes_gcm_dec: {
key: util.hex2abv("feffe9928665731c6d6a8f9467308308" +
"feffe9928665731c6d6a8f9467308308"),
iv: util.hex2abv("cafebabefacedbad"),
adata: util.hex2abv("feedfacedeadbeeffeedfacedeadbeefabaddad2"),
data: util.hex2abv("c3762df1ca787d32ae47c13bf19844cb" +
"af1ae14d0b976afac52ff7d79bba9de0" +
"feb582d33934a4f0954cc2363bc73f78" +
"62ac430e64abe499f47c9b1f" +
"3a337dbf46a792c45e454913fe2ea8f2"),
result: util.hex2abv("d9313225f88406e5a55909c5aff5269a" +
"86a7a9531534f7da2e4c303d8a318a72" +
"1c3c0c95956809532fcf0e2449a6b525" +
"b16aedf5aa0de657ba637b39"),
},
// Test case #17 from McGrew and Viega
// ... but with part of the authentication tag zeroed
// <http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf>
aes_gcm_dec_fail: {
key: util.hex2abv("feffe9928665731c6d6a8f9467308308" +
"feffe9928665731c6d6a8f9467308308"),
iv: util.hex2abv("cafebabefacedbad"),
adata: util.hex2abv("feedfacedeadbeeffeedfacedeadbeefabaddad2"),
data: util.hex2abv("c3762df1ca787d32ae47c13bf19844cb" +
"af1ae14d0b976afac52ff7d79bba9de0" +
"feb582d33934a4f0954cc2363bc73f78" +
"62ac430e64abe499f47c9b1f" +
"00000000000000005e454913fe2ea8f2"),
result: util.hex2abv("d9313225f88406e5a55909c5aff5269a" +
"86a7a9531534f7da2e4c303d8a318a72" +
"1c3c0c95956809532fcf0e2449a6b525" +
"b16aedf5aa0de657ba637b39"),
},
// RFC 4231 <http://tools.ietf.org/html/rfc4231>, Test Case 7
hmac_sign: {
key: util.hex2abv("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
"aaaaaa"),
data: util.hex2abv("54686973206973206120746573742075" +
"73696e672061206c6172676572207468" +
"616e20626c6f636b2d73697a65206b65" +
"7920616e642061206c61726765722074" +
"68616e20626c6f636b2d73697a652064" +
"6174612e20546865206b6579206e6565" +
"647320746f2062652068617368656420" +
"6265666f7265206265696e6720757365" +
"642062792074686520484d414320616c" +
"676f726974686d2e"),
result: util.hex2abv("9b09ffa71b942fcb27635fbcd5b0e944" +
"bfdc63644f0713938a7f51535c3a35e2"),
},
// RFC 4231 <http://tools.ietf.org/html/rfc4231>, Test Case 6
hmac_verify: {
key: util.hex2abv("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
"aaaaaa"),
data: util.hex2abv("54657374205573696e67204c61726765" +
"72205468616e20426c6f636b2d53697a" +
"65204b6579202d2048617368204b6579" +
"204669727374"),
sig: util.hex2abv("60e431591ee0b67f0d8a26aacbf5b77f" +
"8e0bc6213728c5140546040f0ee37f54"),
sig_fail: util.hex2abv("000000001ee0b67f0d8a26aacbf5b77f" +
"8e0bc6213728c5140546040f0ee37f54"),
},
}

View File

@ -332,3 +332,261 @@ TestArray.addTest(
);
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"AES-CBC encrypt",
function () {
var that = this;
function doEncrypt(x) {
console.log(x);
return crypto.subtle.encrypt(
{ name: "AES-CBC", iv: tv.aes_cbc_enc.iv },
x, tv.aes_cbc_enc.data);
}
crypto.subtle.importKey("raw", tv.aes_cbc_enc.key, "AES-CBC", false, ['encrypt'])
.then(doEncrypt)
.then(
memcmp_complete(that, tv.aes_cbc_enc.result),
error(that)
);
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"AES-CBC decrypt",
function () {
var that = this;
function doDecrypt(x) {
return crypto.subtle.decrypt(
{ name: "AES-CBC", iv: tv.aes_cbc_dec.iv },
x, tv.aes_cbc_dec.data);
}
crypto.subtle.importKey("raw", tv.aes_cbc_dec.key, "AES-CBC", false, ['decrypt'])
.then(doDecrypt)
.then(
memcmp_complete(that, tv.aes_cbc_dec.result),
error(that)
);
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"AES-CTR encryption",
function () {
var that = this;
function doEncrypt(x) {
return crypto.subtle.encrypt(
{ name: "AES-CTR", counter: tv.aes_ctr_enc.iv, length: 32 },
x, tv.aes_ctr_enc.data);
}
crypto.subtle.importKey("raw", tv.aes_ctr_enc.key, "AES-CTR", false, ['encrypt'])
.then(doEncrypt)
.then(
memcmp_complete(that, tv.aes_ctr_enc.result),
error(that)
);
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"AES-CTR decryption",
function () {
var that = this;
function doDecrypt(x) {
return crypto.subtle.decrypt(
{ name: "AES-CTR", counter: tv.aes_ctr_dec.iv, length: 32 },
x, tv.aes_ctr_dec.data);
}
crypto.subtle.importKey("raw", tv.aes_ctr_dec.key, "AES-CTR", false, ['decrypt'])
.then(doDecrypt)
.then(
memcmp_complete(that, tv.aes_ctr_dec.result),
error(that)
);
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"AES-GCM encryption",
function () {
var that = this;
function doEncrypt(x) {
return crypto.subtle.encrypt(
{
name: "AES-GCM",
iv: tv.aes_gcm_enc.iv,
additionalData: tv.aes_gcm_enc.adata,
tagLength: 128
},
x, tv.aes_gcm_enc.data);
}
crypto.subtle.importKey("raw", tv.aes_gcm_enc.key, "AES-GCM", false, ['encrypt'])
.then(doEncrypt)
.then(
memcmp_complete(that, tv.aes_gcm_enc.result),
error(that)
);
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"AES-GCM decryption",
function () {
var that = this;
function doDecrypt(x) {
return crypto.subtle.decrypt(
{
name: "AES-GCM",
iv: tv.aes_gcm_dec.iv,
additionalData: tv.aes_gcm_dec.adata,
tagLength: 128
},
x, tv.aes_gcm_dec.data);
}
crypto.subtle.importKey("raw", tv.aes_gcm_dec.key, "AES-GCM", false, ['decrypt'])
.then(doDecrypt)
.then(
memcmp_complete(that, tv.aes_gcm_dec.result),
error(that)
);
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"AES-GCM decryption, failing authentication check",
function () {
var that = this;
function doDecrypt(x) {
return crypto.subtle.decrypt(
{
name: "AES-GCM",
iv: tv.aes_gcm_dec_fail.iv,
additionalData: tv.aes_gcm_dec_fail.adata,
tagLength: 128
},
x, tv.aes_gcm_dec_fail.data);
}
crypto.subtle.importKey("raw", tv.aes_gcm_dec_fail.key, "AES-GCM", false, ['decrypt'])
.then(doDecrypt)
.then(
error(that),
complete(that)
);
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"HMAC SHA-256 sign",
function() {
var that = this;
var alg = {
name: "HMAC",
hash: "SHA-256"
}
function doSign(x) {
return crypto.subtle.sign("HMAC", x, tv.hmac_sign.data);
}
crypto.subtle.importKey("raw", tv.hmac_sign.key, alg, false, ['sign'])
.then(doSign)
.then(
memcmp_complete(that, tv.hmac_sign.result),
error(that)
);
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"HMAC SHA-256 verify",
function() {
var that = this;
var alg = {
name: "HMAC",
hash: "SHA-256"
}
function doVerify(x) {
return crypto.subtle.verify("HMAC", x, tv.hmac_verify.sig, tv.hmac_verify.data);
}
crypto.subtle.importKey("raw", tv.hmac_verify.key, alg, false, ['verify'])
.then(doVerify)
.then(
complete(that, function(x) { return !!x; }),
error(that)
);
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"HMAC SHA-256, failing verification due to bad signature",
function() {
var that = this;
var alg = {
name: "HMAC",
hash: "SHA-256"
}
function doVerify(x) {
return crypto.subtle.verify("HMAC", x, tv.hmac_verify.sig_fail,
tv.hmac_verify.data);
}
crypto.subtle.importKey("raw", tv.hmac_verify.key, alg, false, ['verify'])
.then(doVerify)
.then(
complete(that, function(x) { return !x; }),
error(that)
);
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"HMAC SHA-256, failing verification due to key usage restriction",
function() {
var that = this;
var alg = {
name: "HMAC",
hash: "SHA-256"
}
function doVerify(x) {
return crypto.subtle.verify("HMAC", x, tv.hmac_verify.sig,
tv.hmac_verify.data);
}
crypto.subtle.importKey("raw", tv.hmac_verify.key, alg, false, ['encrypt'])
.then(doVerify)
.then(
error(that),
complete(that, function(x) { return true; })
);
}
);