Bug 1034855 - Implement generateKey() for ECDH r=rbarnes,keeler

This commit is contained in:
Tim Taubert 2014-07-26 08:01:14 +02:00
parent d2132b58df
commit 48844df532
3 changed files with 158 additions and 3 deletions

View File

@ -49,6 +49,11 @@
#define WEBCRYPTO_KEY_USAGE_WRAPKEY "wrapKey"
#define WEBCRYPTO_KEY_USAGE_UNWRAPKEY "unwrapKey"
// WebCrypto named curves
#define WEBCRYPTO_NAMED_CURVE_P256 "P-256"
#define WEBCRYPTO_NAMED_CURVE_P384 "P-384"
#define WEBCRYPTO_NAMED_CURVE_P521 "P-521"
// JWK key types
#define JWK_TYPE_SYMMETRIC "oct"
#define JWK_TYPE_RSA "RSA"
@ -184,6 +189,88 @@ MapAlgorithmNameToMechanism(const nsString& aName)
return mechanism;
}
inline bool
NormalizeNamedCurveValue(const nsString& aNamedCurve, nsString& aDest)
{
if (aNamedCurve.EqualsIgnoreCase(WEBCRYPTO_NAMED_CURVE_P256)) {
aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P256);
} else if (aNamedCurve.EqualsIgnoreCase(WEBCRYPTO_NAMED_CURVE_P384)) {
aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P384);
} else if (aNamedCurve.EqualsIgnoreCase(WEBCRYPTO_NAMED_CURVE_P521)) {
aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P521);
} else {
return false;
}
return true;
}
inline bool
CheckEncodedECParameters(const SECItem* aEcParams)
{
// Need at least two bytes for a valid ASN.1 encoding.
if (aEcParams->len < 2) {
return false;
}
// Check the ASN.1 tag.
if (aEcParams->data[0] != SEC_ASN1_OBJECT_ID) {
return false;
}
// OID tags are short, we never need more than one length byte.
if (aEcParams->data[1] >= 128) {
return false;
}
// Check that the SECItem's length is correct.
if (aEcParams->len != (unsigned)aEcParams->data[1] + 2) {
return false;
}
return true;
}
inline SECItem*
CreateECParamsForCurve(const nsString& aNamedCurve, PLArenaPool* aArena)
{
SECOidTag curveOIDTag;
if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P256)) {
curveOIDTag = SEC_OID_SECG_EC_SECP256R1;
} else if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P384)) {
curveOIDTag = SEC_OID_SECG_EC_SECP384R1;
} else if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P521)) {
curveOIDTag = SEC_OID_SECG_EC_SECP521R1;
} else {
return nullptr;
}
// Retrieve curve data by OID tag.
SECOidData* oidData = SECOID_FindOIDByTag(curveOIDTag);
if (!oidData) {
return nullptr;
}
// Create parameters.
SECItem* params = ::SECITEM_AllocItem(aArena, nullptr, 2 + oidData->oid.len);
if (!params) {
return nullptr;
}
// Set parameters.
params->data[0] = SEC_ASN1_OBJECT_ID;
params->data[1] = oidData->oid.len;
memcpy(params->data + 2, oidData->oid.data, oidData->oid.len);
// Sanity check the params we just created.
if (!CheckEncodedECParameters(params)) {
return nullptr;
}
return params;
}
} // namespace dom
} // namespace mozilla

View File

@ -2000,6 +2000,24 @@ public:
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
return;
}
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
RootedDictionary<EcKeyGenParams> params(aCx);
mEarlyRv = Coerce(aCx, params, aAlgorithm);
if (NS_FAILED(mEarlyRv) || !params.mNamedCurve.WasPassed()) {
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
return;
}
if (!NormalizeNamedCurveValue(params.mNamedCurve.Value(), mNamedCurve)) {
mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
return;
}
// Create algorithm.
algorithm = new EcKeyAlgorithm(global, algName, mNamedCurve);
mKeyPair->PublicKey()->SetAlgorithm(algorithm);
mKeyPair->PrivateKey()->SetAlgorithm(algorithm);
mMechanism = CKM_EC_KEY_PAIR_GEN;
} else {
mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
return;
@ -2013,6 +2031,9 @@ public:
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;
}
mKeyPair->PrivateKey()->SetExtractable(aExtractable);
@ -2044,6 +2065,7 @@ private:
PK11RSAGenParams mRsaParams;
ScopedSECKEYPublicKey mPublicKey;
ScopedSECKEYPrivateKey mPrivateKey;
nsString mNamedCurve;
virtual void ReleaseNSSResources() MOZ_OVERRIDE
{
@ -2057,9 +2079,26 @@ private:
MOZ_ASSERT(slot.get());
void* param;
ScopedPLArenaPool arena;
switch (mMechanism) {
case CKM_RSA_PKCS_KEY_PAIR_GEN: param = &mRsaParams; break;
default: return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
case CKM_RSA_PKCS_KEY_PAIR_GEN:
param = &mRsaParams;
break;
case CKM_EC_KEY_PAIR_GEN: {
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (!arena) {
return NS_ERROR_DOM_UNKNOWN_ERR;
}
param = CreateECParamsForCurve(mNamedCurve, arena.get());
if (!param) {
return NS_ERROR_DOM_UNKNOWN_ERR;
}
break;
}
default:
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
SECKEYPublicKey* pubKey = nullptr;
@ -2477,7 +2516,8 @@ WebCryptoTask::CreateGenerateKeyTask(JSContext* aCx,
return new GenerateSymmetricKeyTask(aCx, aAlgorithm, aExtractable, aKeyUsages);
} else if (algName.EqualsASCII(WEBCRYPTO_ALG_RSAES_PKCS1) ||
algName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
algName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP)) {
algName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) ||
algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
return new GenerateAsymmetricKeyTask(aCx, aAlgorithm, aExtractable, aKeyUsages);
} else {
return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);

View File

@ -1867,3 +1867,31 @@ TestArray.addTest(
.then(complete(that), error(that));
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"Generate an ECDH key for named curve P-256",
function() {
var that = this;
var alg = { name: "ECDH", namedCurve: "P-256" };
crypto.subtle.generateKey(alg, false, ["deriveKey", "deriveBits"]).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 == 0) &&
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 == 2) &&
(x.privateKey.usages[0] == "deriveKey") &&
(x.privateKey.usages[1] == "deriveBits");
}),
error(that)
);
}
);