зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1034854 - Add support for ECDSA to WebCrypto API r=ttaubert,dkeeler r=bz
This commit is contained in:
Родитель
1d2292d697
Коммит
f5092e255e
|
@ -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 *****/
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче