Bug 1034854 - Add support for ECDSA to WebCrypto API r=ttaubert,dkeeler r=bz

This commit is contained in:
Richard Barnes 2014-10-13 23:19:00 +02:00
Родитель 1d2292d697
Коммит f5092e255e
8 изменённых файлов: 403 добавлений и 72 удалений

5
config/external/nss/nss.def поставляемый
Просмотреть файл

@ -160,6 +160,8 @@ DER_GetInteger_Util
DER_Lengths
DER_SetUInteger
DER_UTCTimeToTime_Util
DSAU_DecodeDerSigToLen
DSAU_EncodeDerSigWithLen
DTLS_GetHandshakeTimeout
DTLS_ImportFD
HASH_Begin
@ -308,6 +310,7 @@ PK11_DeriveWithTemplate
PK11_DestroyContext
PK11_DestroyGenericObject
PK11_DestroyMergeLog
PK11_DestroyObject
PK11_DestroyTokenObject
PK11_DigestBegin
PK11_DigestFinal
@ -369,6 +372,7 @@ PK11_ImportCert
PK11_ImportCertForKey
PK11_ImportCRL
PK11_ImportDERPrivateKeyInfoAndReturnKey
PK11_ImportPublicKey
PK11_ImportSymKey
PK11_InitPin
PK11_IsDisabled
@ -672,6 +676,7 @@ VFY_CreateContext
VFY_DestroyContext
VFY_End
VFY_Update
VFY_VerifyData
VFY_VerifyDataDirect
VFY_VerifyDataWithAlgorithmID
_SGN_VerifyPKCS1DigestInfo

Просмотреть файл

@ -395,7 +395,12 @@ CryptoKey::PublicKeyFromSpki(CryptoBuffer& aKeyData,
}
}
return SECKEY_ExtractPublicKey(spki.get());
ScopedSECKEYPublicKey tmp(SECKEY_ExtractPublicKey(spki.get()));
if (!tmp.get() || !PublicKeyValid(tmp.get())) {
return nullptr;
}
return SECKEY_CopyPublicKey(tmp);
}
nsresult
@ -843,6 +848,10 @@ CryptoKey::PublicKeyFromJwk(const JsonWebKey& aJwk,
}
key->u.ec.publicValue = *point;
if (!PublicKeyValid(key)) {
return nullptr;
}
return SECKEY_CopyPublicKey(key);
}
@ -881,6 +890,26 @@ CryptoKey::PublicKeyToJwk(SECKEYPublicKey* aPubKey,
}
}
bool
CryptoKey::PublicKeyValid(SECKEYPublicKey* aPubKey)
{
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
if (!slot.get()) {
return false;
}
// This assumes that NSS checks the validity of a public key when
// it is imported into a PKCS#11 module, and returns CK_INVALID_HANDLE
// if it is invalid.
CK_OBJECT_HANDLE id = PK11_ImportPublicKey(slot, aPubKey, PR_FALSE);
if (id == CK_INVALID_HANDLE) {
return false;
}
SECStatus rv = PK11_DestroyObject(slot, id);
return (rv == SECSuccess);
}
bool
CryptoKey::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const
{

Просмотреть файл

@ -171,6 +171,8 @@ public:
JsonWebKey& aRetVal,
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
static bool PublicKeyValid(SECKEYPublicKey* aPubKey);
// Structured clone methods use these to clone keys
bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const;
bool ReadStructuredClone(JSStructuredCloneReader* aReader);

Просмотреть файл

@ -27,6 +27,7 @@
#define WEBCRYPTO_ALG_RSASSA_PKCS1 "RSASSA-PKCS1-v1_5"
#define WEBCRYPTO_ALG_RSA_OAEP "RSA-OAEP"
#define WEBCRYPTO_ALG_ECDH "ECDH"
#define WEBCRYPTO_ALG_ECDSA "ECDSA"
// WebCrypto key formats
#define WEBCRYPTO_KEY_FORMAT_RAW "raw"
@ -84,6 +85,9 @@
#define JWK_ALG_RSA_OAEP_256 "RSA-OAEP-256"
#define JWK_ALG_RSA_OAEP_384 "RSA-OAEP-384"
#define JWK_ALG_RSA_OAEP_512 "RSA-OAEP-512"
#define JWK_ALG_ECDSA_P_256 "ES256"
#define JWK_ALG_ECDSA_P_384 "ES384"
#define JWK_ALG_ECDSA_P_521 "ES521"
// JWK usages
#define JWK_USE_ENC "enc"
@ -225,6 +229,8 @@ NormalizeToken(const nsString& aName, nsString& aDest)
aDest.AssignLiteral(WEBCRYPTO_ALG_RSA_OAEP);
} else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_ECDH)) {
aDest.AssignLiteral(WEBCRYPTO_ALG_ECDH);
} else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_ECDSA)) {
aDest.AssignLiteral(WEBCRYPTO_ALG_ECDSA);
// Named curve values
} else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_NAMED_CURVE_P256)) {
aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P256);

Просмотреть файл

@ -66,7 +66,8 @@ enum TelemetryAlgorithm {
// Later additions
TA_AES_KW = 19,
TA_ECDH = 20,
TA_PBKDF2 = 21
TA_PBKDF2 = 21,
TA_ECDSA = 22,
};
// Convenience functions for extracting / converting information
@ -782,10 +783,9 @@ public:
mMgfMechanism = CKG_MGF1_SHA384; break;
case CKM_SHA512:
mMgfMechanism = CKG_MGF1_SHA512; break;
default: {
default:
mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
return;
}
}
}
@ -955,49 +955,97 @@ private:
}
};
class RsassaPkcs1Task : public WebCryptoTask
class AsymmetricSignVerifyTask : public WebCryptoTask
{
public:
RsassaPkcs1Task(JSContext* aCx, const ObjectOrString& aAlgorithm,
CryptoKey& aKey,
const CryptoOperationData& aSignature,
const CryptoOperationData& aData,
bool aSign)
AsymmetricSignVerifyTask(JSContext* aCx,
const ObjectOrString& aAlgorithm,
CryptoKey& aKey,
const CryptoOperationData& aSignature,
const CryptoOperationData& aData,
bool aSign)
: mOidTag(SEC_OID_UNKNOWN)
, mPrivKey(aKey.GetPrivateKey())
, mPubKey(aKey.GetPublicKey())
, mSign(aSign)
, mVerified(false)
, mEcdsa(false)
{
Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSASSA_PKCS1);
CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSASSA_PKCS1);
ATTEMPT_BUFFER_INIT(mData, aData);
if (!aSign) {
ATTEMPT_BUFFER_INIT(mSignature, aSignature);
}
// Look up the SECOidTag based on the KeyAlgorithm
// static_cast is safe because we only get here if the algorithm name
// is RSASSA-PKCS1-v1_5, and that only happens if we've constructed
// an RsaHashedKeyAlgorithm
CK_MECHANISM_TYPE mech;
mech = KeyAlgorithmProxy::GetMechanism(aKey.Algorithm().mRsa.mHash);
nsString algName;
mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
if (NS_FAILED(mEarlyRv)) {
return;
}
switch (mech) {
case CKM_SHA_1:
mOidTag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; break;
case CKM_SHA256:
mOidTag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; break;
case CKM_SHA384:
mOidTag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION; break;
case CKM_SHA512:
mOidTag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION; break;
default: {
mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
// Look up the SECOidTag
if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
mEcdsa = false;
Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSASSA_PKCS1);
CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSASSA_PKCS1);
// For RSA, the hash name comes from the key algorithm
nsString hashName = aKey.Algorithm().mRsa.mHash.mName;
switch (MapAlgorithmNameToMechanism(hashName)) {
case CKM_SHA_1:
mOidTag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; break;
case CKM_SHA256:
mOidTag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; break;
case CKM_SHA384:
mOidTag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION; break;
case CKM_SHA512:
mOidTag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION; break;
default:
mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
return;
}
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
mEcdsa = true;
Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_ECDSA);
CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_ECDSA);
// For ECDSA, the hash name comes from the algorithm parameter
RootedDictionary<EcdsaParams> params(aCx);
mEarlyRv = Coerce(aCx, params, aAlgorithm);
if (NS_FAILED(mEarlyRv)) {
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
return;
}
nsString hashName;
mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
if (NS_FAILED(mEarlyRv)) {
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
return;
}
CK_MECHANISM_TYPE hashMechanism = MapAlgorithmNameToMechanism(hashName);
if (hashMechanism == UNKNOWN_CK_MECHANISM) {
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
return;
}
switch (hashMechanism) {
case CKM_SHA_1:
mOidTag = SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE; break;
case CKM_SHA256:
mOidTag = SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE; break;
case CKM_SHA384:
mOidTag = SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE; break;
case CKM_SHA512:
mOidTag = SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE; break;
default:
mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
return;
}
} else {
// This shouldn't happen; CreateSignVerifyTask shouldn't create
// one of these unless it's for the above algorithms.
MOZ_ASSERT(false);
}
// Check that we have the appropriate key
@ -1015,6 +1063,7 @@ private:
CryptoBuffer mData;
bool mSign;
bool mVerified;
bool mEcdsa;
virtual nsresult DoCrypto() MOZ_OVERRIDE
{
@ -1022,44 +1071,53 @@ private:
if (mSign) {
ScopedSECItem signature((SECItem*) PORT_Alloc(sizeof(SECItem)));
ScopedSGNContext ctx(SGN_NewContext(mOidTag, mPrivKey));
if (!ctx) {
if (!signature.get() || !ctx.get()) {
return NS_ERROR_DOM_OPERATION_ERR;
}
rv = MapSECStatus(SGN_Begin(ctx));
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
rv = MapSECStatus(SEC_SignData(signature, mData.Elements(),
mData.Length(), mPrivKey, mOidTag));
rv = MapSECStatus(SGN_Update(ctx, mData.Elements(), mData.Length()));
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
rv = MapSECStatus(SGN_End(ctx, signature));
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
ATTEMPT_BUFFER_ASSIGN(mSignature, signature);
} else {
ScopedSECItem signature(mSignature.ToSECItem());
if (!signature) {
return NS_ERROR_DOM_UNKNOWN_ERR;
}
ScopedVFYContext ctx(VFY_CreateContext(mPubKey, signature,
mOidTag, nullptr));
if (!ctx) {
int err = PORT_GetError();
if (err == SEC_ERROR_BAD_SIGNATURE) {
mVerified = false;
return NS_OK;
if (mEcdsa) {
// DER-decode the signature
int signatureLength = PK11_SignatureLen(mPrivKey);
ScopedSECItem rawSignature(DSAU_DecodeDerSigToLen(signature.get(),
signatureLength));
if (!rawSignature.get()) {
return NS_ERROR_DOM_OPERATION_ERR;
}
return NS_ERROR_DOM_OPERATION_ERR;
ATTEMPT_BUFFER_ASSIGN(mSignature, rawSignature);
} else {
ATTEMPT_BUFFER_ASSIGN(mSignature, signature);
}
rv = MapSECStatus(VFY_Begin(ctx));
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
} else {
ScopedSECItem signature;
rv = MapSECStatus(VFY_Update(ctx, mData.Elements(), mData.Length()));
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
if (mEcdsa) {
// DER-encode the signature
ScopedSECItem rawSignature(mSignature.ToSECItem());
if (!rawSignature.get()) {
return NS_ERROR_DOM_UNKNOWN_ERR;
}
rv = MapSECStatus(VFY_End(ctx));
signature = (SECItem*) PORT_Alloc(sizeof(SECItem));
if (!signature.get()) {
return NS_ERROR_DOM_UNKNOWN_ERR;
}
rv = MapSECStatus(DSAU_EncodeDerSigWithLen(signature, rawSignature,
rawSignature->len));
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
} else {
signature = mSignature.ToSECItem();
if (!signature) {
return NS_ERROR_DOM_UNKNOWN_ERR;
}
}
rv = MapSECStatus(VFY_VerifyData(mData.Elements(), mData.Length(),
mPubKey, signature, mOidTag, nullptr));
mVerified = NS_SUCCEEDED(rv);
}
@ -1661,12 +1719,23 @@ private:
virtual nsresult AfterCrypto() MOZ_OVERRIDE
{
// Check permissions for the requested operation
if (mKey->GetKeyType() == CryptoKey::PRIVATE &&
mKey->HasUsageOtherThan(CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY)) {
return NS_ERROR_DOM_DATA_ERR;
uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0;
if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
privateAllowedUsages = CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY;
publicAllowedUsages = CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY;
} else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
privateAllowedUsages = CryptoKey::SIGN;
publicAllowedUsages = CryptoKey::VERIFY;
}
// Check permissions for the requested operation
if ((mKey->GetKeyType() == CryptoKey::PRIVATE &&
mKey->HasUsageOtherThan(privateAllowedUsages)) ||
(mKey->GetKeyType() == CryptoKey::PUBLIC &&
mKey->HasUsageOtherThan(publicAllowedUsages))) {
return NS_ERROR_DOM_DATA_ERR;
}
mKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
@ -1991,7 +2060,8 @@ public:
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
return;
}
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
RootedDictionary<EcKeyGenParams> params(aCx);
mEarlyRv = Coerce(aCx, params, aAlgorithm);
if (NS_FAILED(mEarlyRv)) {
@ -2014,7 +2084,8 @@ public:
}
// Set key usages.
if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
privateAllowedUsages = CryptoKey::SIGN;
publicAllowedUsages = CryptoKey::VERIFY;
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
@ -2176,10 +2247,9 @@ public:
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: {
default:
mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
return;
}
}
ATTEMPT_BUFFER_INIT(mSalt, params.mSalt)
@ -2544,8 +2614,10 @@ WebCryptoTask::CreateSignVerifyTask(JSContext* aCx,
if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
return new HmacTask(aCx, aAlgorithm, aKey, aSignature, aData, aSign);
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
return new RsassaPkcs1Task(aCx, aAlgorithm, aKey, aSignature, aData, aSign);
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
return new AsymmetricSignVerifyTask(aCx, aAlgorithm, aKey, aSignature,
aData, aSign);
}
return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
@ -2618,7 +2690,8 @@ WebCryptoTask::CreateImportKeyTask(JSContext* aCx,
algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
return new ImportRsaKeyTask(aCx, aFormat, aKeyData, aAlgorithm,
aExtractable, aKeyUsages);
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
return new ImportEcKeyTask(aCx, aFormat, aKeyData, aAlgorithm,
aExtractable, aKeyUsages);
} else {
@ -2694,7 +2767,8 @@ WebCryptoTask::CreateGenerateKeyTask(JSContext* aCx,
return new GenerateSymmetricKeyTask(aCx, aAlgorithm, aExtractable, aKeyUsages);
} else if (algName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
algName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) ||
algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
algName.EqualsASCII(WEBCRYPTO_ALG_ECDH) ||
algName.EqualsASCII(WEBCRYPTO_ALG_ECDSA)) {
return new GenerateAsymmetricKeyTask(aCx, aAlgorithm, aExtractable, aKeyUsages);
} else {
return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);

Просмотреть файл

@ -608,4 +608,54 @@ tv = {
x: "XOe4bjsyZgQD5jcS7wmY3q4QJ_rsPBvp92-TTf61jpg",
}
},
// NIST ECDSA test vectors
// http://csrc.nist.gov/groups/STM/cavp/index.html
ecdsa_verify: {
pub_jwk: {
"kty": "EC",
"crv": "P-521",
// 0061387fd6b95914e885f912edfbb5fb274655027f216c4091ca83e19336740fd8
// 1aedfe047f51b42bdf68161121013e0d55b117a14e4303f926c8debb77a7fdaad1
"x": "AGE4f9a5WRTohfkS7fu1-ydGVQJ_IWxAkcqD4ZM2dA_Y" +
"Gu3-BH9RtCvfaBYRIQE-DVWxF6FOQwP5Jsjeu3en_arR",
// 00e7d0c75c38626e895ca21526b9f9fdf84dcecb93f2b233390550d2b1463b7ee3
// f58df7346435ff0434199583c97c665a97f12f706f2357da4b40288def888e59e6
"y": "AOfQx1w4Ym6JXKIVJrn5_fhNzsuT8rIzOQVQ0rFGO37j" +
"9Y33NGQ1_wQ0GZWDyXxmWpfxL3BvI1faS0Aoje-Ijlnm",
},
"data": util.hex2abv(
"9ecd500c60e701404922e58ab20cc002651fdee7cbc9336adda33e4c1088fab1" +
"964ecb7904dc6856865d6c8e15041ccf2d5ac302e99d346ff2f686531d255216" +
"78d4fd3f76bbf2c893d246cb4d7693792fe18172108146853103a51f824acc62" +
"1cb7311d2463c3361ea707254f2b052bc22cb8012873dcbb95bf1a5cc53ab89f"
),
"sig": util.hex2abv(
"004de826ea704ad10bc0f7538af8a3843f284f55c8b946af9235af5af74f2b76e0" +
"99e4bc72fd79d28a380f8d4b4c919ac290d248c37983ba05aea42e2dd79fdd33e8" +
"0087488c859a96fea266ea13bf6d114c429b163be97a57559086edb64aed4a1859" +
"4b46fb9efc7fd25d8b2de8f09ca0587f54bd287299f47b2ff124aac566e8ee3b43"
),
// Same as "sig", but with the last few octets set to 0
"sig_tampered": util.hex2abv(
"004de826ea704ad10bc0f7538af8a3843f284f55c8b946af9235af5af74f2b76e0" +
"99e4bc72fd79d28a380f8d4b4c919ac290d248c37983ba05aea42e2dd79fdd33e8" +
"0087488c859a96fea266ea13bf6d114c429b163be97a57559086edb64aed4a1859" +
"4b46fb9efc7fd25d8b2de8f09ca0587f54bd287299f47b2ff124aac56600000000"
)
},
ecdsa_bad: {
pub_jwk: {
"kty": "EC",
"crv": "P-521",
"x": "BhOH_WuVkU6IX5Eu37tfsnRlUCfyFsQJHKg-GTNnQP2B" +
"rt_gR_UbQr32gWESEBPg1VsRehTkMD-SbI3rt3p_2q0B",
"y": "AUNouOdGgHsraPNhXNeNdhpGTd15GPyN9R0iWWL98ePc" +
"JD4mUQD/DsEzNZ4zLkTdSa/Y5fOP6GEzVzQy0zwC+goD"
}
}
}

Просмотреть файл

@ -0,0 +1,162 @@
<!DOCTYPE html>
<html>
<head>
<title>WebCrypto Test Suite</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<link rel="stylesheet" href="./test_WebCrypto.css"/>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<!-- Utilities for manipulating ABVs -->
<script src="util.js"></script>
<!-- A simple wrapper around IndexedDB -->
<script src="simpledb.js"></script>
<!-- Test vectors drawn from the literature -->
<script src="./test-vectors.js"></script>
<!-- General testing framework -->
<script src="./test-array.js"></script>
<script>/*<![CDATA[*/
"use strict";
// -----------------------------------------------------------------------------
TestArray.addTest(
"Generate an ECDSA key for named curve P-256",
function() {
var that = this;
var alg = { name: "ECDSA", namedCurve: "P-256" };
crypto.subtle.generateKey(alg, false, ["sign", "verify"]).then(
complete(that, function(x) {
return exists(x.publicKey) &&
(x.publicKey.algorithm.name == alg.name) &&
(x.publicKey.algorithm.namedCurve == alg.namedCurve) &&
(x.publicKey.type == "public") &&
x.publicKey.extractable &&
(x.publicKey.usages.length == 1) &&
(x.publicKey.usages[0] == "verify") &&
exists(x.privateKey) &&
(x.privateKey.algorithm.name == alg.name) &&
(x.privateKey.algorithm.namedCurve == alg.namedCurve) &&
(x.privateKey.type == "private") &&
!x.privateKey.extractable &&
(x.privateKey.usages.length == 1) &&
(x.privateKey.usages[0] == "sign")
}),
error(that)
);
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"ECDSA JWK import and verify a known-good signature",
function() {
var that = this;
var alg = { name: "ECDSA", namedCurve: "P-256", hash: "SHA-256" };
function doVerify(x) {
return crypto.subtle.verify(alg, x, tv.ecdsa_verify.sig, tv.ecdsa_verify.data);
}
crypto.subtle.importKey("jwk", tv.ecdsa_verify.pub_jwk, alg, true, ["verify"])
.then(doVerify)
.then(complete(that), error(that))
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"ECDSA JWK import and reject a known-bad signature",
function() {
var that = this;
var alg = { name: "ECDSA", namedCurve: "P-256", hash: "SHA-256" };
function doVerify(x) {
return crypto.subtle.verify(alg, x, tv.ecdsa_verify.sig_tampered,
tv.ecdsa_verify.data);
}
crypto.subtle.importKey("jwk", tv.ecdsa_verify.pub_jwk, alg, true, ["verify"])
.then(doVerify)
.then(complete(that, x => !x), error(that))
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"ECDSA sign/verify round-trip",
function() {
var that = this;
var alg = { name: "ECDSA", namedCurve: "P-521", hash: "SHA-512" };
var pubKey;
function doSign(keyPair) {
pubKey = keyPair.publicKey;
return crypto.subtle.sign(alg, keyPair.privateKey, tv.ecdsa_verify.data);
}
function doVerify(sig) {
return crypto.subtle.verify(alg, pubKey, sig, tv.ecdsa_verify.data);
}
crypto.subtle.generateKey(alg, true, ["sign", "verify"])
.then(doSign)
.then(doVerify)
.then(complete(that), error(that))
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"Verify that ECDSA import fails with a known-bad public key",
function() {
var that = this;
var alg = { name: "ECDSA", namedCurve: "P-256", hash: "SHA-256" };
function doVerify(x) {
return crypto.subtle.verify(alg, x, tv.ecdsa_verify.sig, tv.ecdsa_verify.data);
}
crypto.subtle.importKey("jwk", tv.ecdsa_bad.pub_jwk, alg, true, ["verify"])
.then(error(that), complete(that))
}
);
/*]]>*/</script>
</head>
<body>
<div id="content">
<div id="head">
<b>Web</b>Crypto<br>
</div>
<div id="start" onclick="start();">RUN ALL</div>
<div id="resultDiv" class="content">
Summary:
<span class="pass"><span id="passN">0</span> passed, </span>
<span class="fail"><span id="failN">0</span> failed, </span>
<span class="pending"><span id="pendingN">0</span> pending.</span>
<br/>
<br/>
<table id="results">
<tr>
<th>Test</th>
<th>Result</th>
<th>Time</th>
</tr>
</table>
</div>
<div id="foot"></div>
</div>
</body>
</html>

Просмотреть файл

@ -87,6 +87,9 @@ dictionary EcdhKeyDeriveParams : Algorithm {
required CryptoKey public;
};
dictionary EcdsaParams : Algorithm {
required AlgorithmIdentifier hash;
};
/***** JWK *****/