--- a/config/external/nss/nss.def
+++ b/config/external/nss/nss.def
@@ -155,16 +155,18 @@ DER_EncodeTimeChoice_Util
DER_Encode_Util
DER_GeneralizedTimeToTime
DER_GeneralizedTimeToTime_Util
DER_GetInteger
DER_GetInteger_Util
DER_Lengths
DER_SetUInteger
DER_UTCTimeToTime_Util
+DSAU_DecodeDerSigToLen
+DSAU_EncodeDerSigWithLen
DTLS_GetHandshakeTimeout
DTLS_ImportFD
HASH_Begin
HASH_Create
HASH_Destroy
HASH_End
HASH_GetHashObject
HASH_GetType
@@ -303,16 +305,17 @@ PK11_DeleteTokenCertAndKey
PK11_DeleteTokenPrivateKey
PK11_DeleteTokenPublicKey
PK11_DEREncodePublicKey
PK11_Derive
PK11_DeriveWithTemplate
PK11_DestroyContext
PK11_DestroyGenericObject
PK11_DestroyMergeLog
+PK11_DestroyObject
PK11_DestroyTokenObject
PK11_DigestBegin
PK11_DigestFinal
PK11_DigestOp
PK11_DoesMechanism
PK11_Encrypt
PK11_ExportDERPrivateKeyInfo
PK11_ExtractKeyValue
@@ -364,16 +367,17 @@ PK11_GetSlotSeries
PK11_GetTokenInfo
PK11_GetTokenName
PK11_HashBuf
PK11_HasRootCerts
PK11_ImportCert
PK11_ImportCertForKey
PK11_ImportCRL
PK11_ImportDERPrivateKeyInfoAndReturnKey
+PK11_ImportPublicKey
PK11_ImportSymKey
PK11_InitPin
PK11_IsDisabled
PK11_IsFIPS
PK11_IsFriendly
PK11_IsHW
PK11_IsInternal
PK11_IsLoggedIn
@@ -667,11 +671,12 @@ SSL_VersionRangeGet
SSL_VersionRangeSet
SSL_VersionRangeSetDefault
UTIL_SetForkState
VFY_Begin
VFY_CreateContext
VFY_DestroyContext
VFY_End
VFY_Update
+VFY_VerifyData
VFY_VerifyDataDirect
VFY_VerifyDataWithAlgorithmID
_SGN_VerifyPKCS1DigestInfo
--- a/dom/crypto/CryptoKey.cpp
+++ b/dom/crypto/CryptoKey.cpp
@@ -390,17 +390,22 @@ CryptoKey::PublicKeyFromSpki(CryptoBuffe
SECStatus rv = SECITEM_CopyItem(spki->arena, &spki->algorithm.algorithm,
&oidData->oid);
if (rv != SECSuccess) {
return nullptr;
}
}
- return SECKEY_ExtractPublicKey(spki.get());
+ ScopedSECKEYPublicKey tmp(SECKEY_ExtractPublicKey(spki.get()));
+ if (!tmp.get() || !PublicKeyValid(tmp.get())) {
+ return nullptr;
+ }
+
+ return SECKEY_CopyPublicKey(tmp);
}
nsresult
CryptoKey::PrivateKeyToPkcs8(SECKEYPrivateKey* aPrivKey,
CryptoBuffer& aRetVal,
const nsNSSShutDownPreventionLock& /*proofOfLock*/)
{
ScopedSECItem pkcs8Item(PK11_ExportDERPrivateKeyInfo(aPrivKey, nullptr));
@@ -838,16 +843,20 @@ CryptoKey::PublicKeyFromJwk(const JsonWe
// Create point.
SECItem* point = CreateECPointForCoordinates(x, y, arena.get());
if (!point) {
return nullptr;
}
key->u.ec.publicValue = *point;
+ if (!PublicKeyValid(key)) {
+ return nullptr;
+ }
+
return SECKEY_CopyPublicKey(key);
}
return nullptr;
}
nsresult
CryptoKey::PublicKeyToJwk(SECKEYPublicKey* aPubKey,
@@ -877,16 +886,36 @@ CryptoKey::PublicKeyToJwk(SECKEYPublicKe
}
return NS_OK;
default:
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
}
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
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return false;
}
// Write in five pieces
--- a/dom/crypto/CryptoKey.h
+++ b/dom/crypto/CryptoKey.h
@@ -166,16 +166,18 @@ public:
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
static SECKEYPublicKey* PublicKeyFromJwk(const JsonWebKey& aKeyData,
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
static nsresult PublicKeyToJwk(SECKEYPublicKey* aPrivKey,
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);
private:
~CryptoKey();
nsRefPtr<nsIGlobalObject> mGlobal;
--- a/dom/crypto/WebCryptoCommon.h
+++ b/dom/crypto/WebCryptoCommon.h
@@ -22,16 +22,17 @@
#define WEBCRYPTO_ALG_SHA256 "SHA-256"
#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_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"
#define WEBCRYPTO_KEY_FORMAT_PKCS8 "pkcs8"
#define WEBCRYPTO_KEY_FORMAT_SPKI "spki"
#define WEBCRYPTO_KEY_FORMAT_JWK "jwk"
// WebCrypto key types
@@ -79,16 +80,19 @@
#define JWK_ALG_RS1 "RS1" // RSASSA-PKCS1
#define JWK_ALG_RS256 "RS256"
#define JWK_ALG_RS384 "RS384"
#define JWK_ALG_RS512 "RS512"
#define JWK_ALG_RSA_OAEP "RSA-OAEP" // RSA-OAEP
#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"
#define JWK_USE_SIG "sig"
// Define an unknown mechanism type
#define UNKNOWN_CK_MECHANISM CKM_VENDOR_DEFINED+1
@@ -220,16 +224,18 @@ NormalizeToken(const nsString& aName, ns
} else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_PBKDF2)) {
aDest.AssignLiteral(WEBCRYPTO_ALG_PBKDF2);
} else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_RSASSA_PKCS1)) {
aDest.AssignLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1);
} else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_RSA_OAEP)) {
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);
} else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_NAMED_CURVE_P384)) {
aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P384);
} else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_NAMED_CURVE_P521)) {
aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P521);
} else {
--- a/dom/crypto/WebCryptoTask.cpp
+++ b/dom/crypto/WebCryptoTask.cpp
@@ -61,17 +61,18 @@ enum TelemetryAlgorithm {
TA_SHA_1 = 14,
TA_SHA_224 = 15,
TA_SHA_256 = 16,
TA_SHA_384 = 17,
TA_SHA_512 = 18,
// Later additions
TA_AES_KW = 19,
TA_ECDH = 20,
- TA_PBKDF2 = 21
+ TA_PBKDF2 = 21,
+ TA_ECDSA = 22,
};
// 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; \
@@ -777,20 +778,19 @@ public:
case CKM_SHA_1:
mMgfMechanism = CKG_MGF1_SHA1; break;
case CKM_SHA256:
mMgfMechanism = CKG_MGF1_SHA256; break;
case CKM_SHA384:
mMgfMechanism = CKG_MGF1_SHA384; break;
case CKM_SHA512:
mMgfMechanism = CKG_MGF1_SHA512; break;
- default: {
+ default:
mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
return;
- }
}
}
private:
CK_MECHANISM_TYPE mHashMechanism;
CK_MECHANISM_TYPE mMgfMechanism;
ScopedSECKEYPrivateKey mPrivKey;
ScopedSECKEYPublicKey mPubKey;
@@ -950,59 +950,107 @@ private:
mSignature.Length());
equal = (cmp == 0);
}
mResultPromise->MaybeResolve(equal);
}
}
};
-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);
-
- 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;
+ nsString algName;
+ mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
+ if (NS_FAILED(mEarlyRv)) {
+ return;
+ }
+
+ // 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
if ((mSign && !mPrivKey) || (!mSign && !mPubKey)) {
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
return;
}
}
@@ -1010,61 +1058,71 @@ public:
private:
SECOidTag mOidTag;
ScopedSECKEYPrivateKey mPrivKey;
ScopedSECKEYPublicKey mPubKey;
CryptoBuffer mSignature;
CryptoBuffer mData;
bool mSign;
bool mVerified;
+ bool mEcdsa;
virtual nsresult DoCrypto() MOZ_OVERRIDE
{
nsresult rv;
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(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;
+ rv = MapSECStatus(SEC_SignData(signature, mData.Elements(),
+ mData.Length(), mPrivKey, mOidTag));
+
+ 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;
+ }
+
+ ATTEMPT_BUFFER_ASSIGN(mSignature, rawSignature);
+ } else {
+ ATTEMPT_BUFFER_ASSIGN(mSignature, signature);
}
- 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;
+ } else {
+ ScopedSECItem signature;
+
+ if (mEcdsa) {
+ // DER-encode the signature
+ ScopedSECItem rawSignature(mSignature.ToSECItem());
+ if (!rawSignature.get()) {
+ return NS_ERROR_DOM_UNKNOWN_ERR;
}
- return NS_ERROR_DOM_OPERATION_ERR;
+
+ 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_Begin(ctx));
- NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
-
- rv = MapSECStatus(VFY_Update(ctx, mData.Elements(), mData.Length()));
- NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
-
- rv = MapSECStatus(VFY_End(ctx));
+ rv = MapSECStatus(VFY_VerifyData(mData.Elements(), mData.Length(),
+ mPubKey, signature, mOidTag, nullptr));
mVerified = NS_SUCCEEDED(rv);
}
return NS_OK;
}
virtual void Resolve() MOZ_OVERRIDE
{
@@ -1656,21 +1714,32 @@ private:
}
}
return NS_OK;
}
virtual nsresult AfterCrypto() MOZ_OVERRIDE
{
+ 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(CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY)) {
- return NS_ERROR_DOM_DATA_ERR;
- }
+ 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)) {
return NS_ERROR_DOM_DATA_ERR;
}
return NS_OK;
@@ -1986,17 +2055,18 @@ public:
// Set up params struct
mRsaParams.keySizeInBits = modulusLength;
bool converted = publicExponent.GetBigIntValue(mRsaParams.pe);
if (!converted) {
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)) {
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
return;
}
if (!NormalizeToken(params.mNamedCurve, mNamedCurve)) {
@@ -2009,17 +2079,18 @@ public:
mKeyPair.mPrivateKey.get()->Algorithm().MakeEc(algName, mNamedCurve);
mMechanism = CKM_EC_KEY_PAIR_GEN;
} else {
mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
return;
}
// 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)) {
privateAllowedUsages = CryptoKey::DECRYPT | CryptoKey::UNWRAPKEY;
publicAllowedUsages = CryptoKey::ENCRYPT | CryptoKey::WRAPKEY;
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
privateAllowedUsages = CryptoKey::DERIVEKEY | CryptoKey::DERIVEBITS;
publicAllowedUsages = 0;
@@ -2171,20 +2242,19 @@ public:
}
// 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: {
+ default:
mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
return;
- }
}
ATTEMPT_BUFFER_INIT(mSalt, params.mSalt)
mLength = aLength >> 3; // bits to bytes
mIterations = params.mIterations;
}
private:
@@ -2539,18 +2609,20 @@ WebCryptoTask::CreateSignVerifyTask(JSCo
nsString algName;
nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
if (NS_FAILED(rv)) {
return new FailureTask(rv);
}
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);
}
WebCryptoTask*
WebCryptoTask::CreateDigestTask(JSContext* aCx,
const ObjectOrString& aAlgorithm,
@@ -2613,17 +2685,18 @@ WebCryptoTask::CreateImportKeyTask(JSCon
algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) ||
algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
return new ImportSymmetricKeyTask(aCx, aFormat, aKeyData, aAlgorithm,
aExtractable, aKeyUsages);
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
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 {
return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
}
}
WebCryptoTask*
@@ -2689,17 +2762,18 @@ WebCryptoTask::CreateGenerateKeyTask(JSC
if (algName.EqualsASCII(WEBCRYPTO_ALG_AES_CBC) ||
algName.EqualsASCII(WEBCRYPTO_ALG_AES_CTR) ||
algName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) ||
algName.EqualsASCII(WEBCRYPTO_ALG_AES_KW) ||
algName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) {
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);
}
}
WebCryptoTask*
WebCryptoTask::CreateDeriveKeyTask(JSContext* aCx,
--- a/dom/crypto/test/test-vectors.js
+++ b/dom/crypto/test/test-vectors.js
@@ -603,9 +603,59 @@ tv = {
// The Y coordinate is missing.
jwk_missing_y: {
kty: "EC",
crv: "P-256",
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"
+ }
+ }
}
new file mode 100644
--- /dev/null
+++ b/dom/crypto/test/test_WebCrypto_ECDSA.html
@@ -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>
--- a/dom/webidl/SubtleCrypto.webidl
+++ b/dom/webidl/SubtleCrypto.webidl
@@ -82,16 +82,19 @@ dictionary AesDerivedKeyParams : Algorit
dictionary HmacDerivedKeyParams : HmacImportParams {
[EnforceRange] unsigned long length;
};
dictionary EcdhKeyDeriveParams : Algorithm {
required CryptoKey public;
};
+dictionary EcdsaParams : Algorithm {
+ required AlgorithmIdentifier hash;
+};
/***** JWK *****/
dictionary RsaOtherPrimesInfo {
// The following fields are defined in Section 6.3.2.7 of JSON Web Algorithms
required DOMString r;
required DOMString d;
required DOMString t;