summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDivy Srivastava <dj.srivastava23@gmail.com>2021-09-12 02:19:53 +0530
committerGitHub <noreply@github.com>2021-09-11 16:49:53 -0400
commit40c63d1255642b8d70d7b5ce5b85a50f6af8a00d (patch)
treeabdfe6e1df461b3d8106fc3c75a7c879cd9381a2
parentd236f432b86de55c6006778b0c68fe60b6419069 (diff)
feat(ext/crypto): verify ECDSA signatures (#11739)
-rw-r--r--cli/tests/unit/webcrypto_test.ts52
-rw-r--r--ext/crypto/00_crypto.js20
-rw-r--r--ext/crypto/key.rs10
-rw-r--r--ext/crypto/lib.deno_crypto.d.ts2
-rw-r--r--ext/crypto/lib.rs16
5 files changed, 98 insertions, 2 deletions
diff --git a/cli/tests/unit/webcrypto_test.ts b/cli/tests/unit/webcrypto_test.ts
index 475efde6c..a5de9fa36 100644
--- a/cli/tests/unit/webcrypto_test.ts
+++ b/cli/tests/unit/webcrypto_test.ts
@@ -189,7 +189,7 @@ unitTest(async function testGenerateHMACKey() {
assert(key.usages.includes("sign"));
});
-unitTest(async function testSignECDSA() {
+unitTest(async function testECDSASignVerify() {
const key = await window.crypto.subtle.generateKey(
{
name: "ECDSA",
@@ -208,6 +208,56 @@ unitTest(async function testSignECDSA() {
);
assert(signature);
+ assert(signature instanceof ArrayBuffer);
+
+ const verified = await window.crypto.subtle.verify(
+ { hash: { name: "SHA-384" }, name: "ECDSA" },
+ key.publicKey,
+ signature,
+ encoded,
+ );
+ assert(verified);
+});
+
+// Tests the "bad paths" as a temporary replacement for sign_verify/ecdsa WPT.
+unitTest(async function testECDSASignVerifyFail() {
+ const key = await window.crypto.subtle.generateKey(
+ {
+ name: "ECDSA",
+ namedCurve: "P-384",
+ },
+ true,
+ ["sign", "verify"],
+ );
+
+ const encoded = new Uint8Array([1]);
+ // Signing with a public key (InvalidAccessError)
+ await assertThrowsAsync(async () => {
+ await window.crypto.subtle.sign(
+ { name: "ECDSA", hash: "SHA-384" },
+ key.publicKey,
+ new Uint8Array([1]),
+ );
+ throw new TypeError("unreachable");
+ }, DOMException);
+
+ // Do a valid sign for later verifying.
+ const signature = await window.crypto.subtle.sign(
+ { name: "ECDSA", hash: "SHA-384" },
+ key.privateKey,
+ encoded,
+ );
+
+ // Verifying with a private key (InvalidAccessError)
+ await assertThrowsAsync(async () => {
+ await window.crypto.subtle.verify(
+ { hash: { name: "SHA-384" }, name: "ECDSA" },
+ key.privateKey,
+ signature,
+ encoded,
+ );
+ throw new TypeError("unreachable");
+ }, DOMException);
});
// https://github.com/denoland/deno/issues/11313
diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js
index a95c0be1b..0e5a511e5 100644
--- a/ext/crypto/00_crypto.js
+++ b/ext/crypto/00_crypto.js
@@ -92,6 +92,7 @@
"verify": {
"RSASSA-PKCS1-v1_5": null,
"RSA-PSS": "RsaPssParams",
+ "ECDSA": "EcdsaParams",
"HMAC": null,
},
"importKey": {
@@ -1185,6 +1186,25 @@
signature,
}, data);
}
+ case "ECDSA": {
+ // 1.
+ if (key[_type] !== "public") {
+ throw new DOMException(
+ "Key type not supported",
+ "InvalidAccessError",
+ );
+ }
+ // 2.
+ const hash = normalizedAlgorithm.hash.name;
+ // 3-8.
+ return await core.opAsync("op_crypto_verify_key", {
+ key: keyData,
+ algorithm: "ECDSA",
+ hash,
+ signature,
+ namedCurve: key[_algorithm].namedCurve,
+ }, data);
+ }
}
throw new TypeError("unreachable");
diff --git a/ext/crypto/key.rs b/ext/crypto/key.rs
index d2420bfe9..663217887 100644
--- a/ext/crypto/key.rs
+++ b/ext/crypto/key.rs
@@ -4,6 +4,7 @@ use ring::agreement::Algorithm as RingAlgorithm;
use ring::digest;
use ring::hmac::Algorithm as HmacAlgorithm;
use ring::signature::EcdsaSigningAlgorithm;
+use ring::signature::EcdsaVerificationAlgorithm;
use serde::Deserialize;
use serde::Serialize;
@@ -57,6 +58,15 @@ impl From<CryptoNamedCurve> for &EcdsaSigningAlgorithm {
}
}
+impl From<CryptoNamedCurve> for &EcdsaVerificationAlgorithm {
+ fn from(curve: CryptoNamedCurve) -> &'static EcdsaVerificationAlgorithm {
+ match curve {
+ CryptoNamedCurve::P256 => &ring::signature::ECDSA_P256_SHA256_FIXED,
+ CryptoNamedCurve::P384 => &ring::signature::ECDSA_P384_SHA384_FIXED,
+ }
+ }
+}
+
impl From<CryptoHash> for HmacAlgorithm {
fn from(hash: CryptoHash) -> HmacAlgorithm {
match hash {
diff --git a/ext/crypto/lib.deno_crypto.d.ts b/ext/crypto/lib.deno_crypto.d.ts
index 5169e5c3b..a62e69632 100644
--- a/ext/crypto/lib.deno_crypto.d.ts
+++ b/ext/crypto/lib.deno_crypto.d.ts
@@ -175,7 +175,7 @@ interface SubtleCrypto {
data: BufferSource,
): Promise<ArrayBuffer>;
verify(
- algorithm: AlgorithmIdentifier | RsaPssParams,
+ algorithm: AlgorithmIdentifier | RsaPssParams | EcdsaParams,
key: CryptoKey,
signature: BufferSource,
data: BufferSource,
diff --git a/ext/crypto/lib.rs b/ext/crypto/lib.rs
index 7c4010f53..f2df7ba10 100644
--- a/ext/crypto/lib.rs
+++ b/ext/crypto/lib.rs
@@ -33,6 +33,8 @@ use ring::rand as RingRand;
use ring::rand::SecureRandom;
use ring::signature::EcdsaKeyPair;
use ring::signature::EcdsaSigningAlgorithm;
+use ring::signature::EcdsaVerificationAlgorithm;
+use ring::signature::KeyPair;
use rsa::padding::PaddingScheme;
use rsa::pkcs8::FromPrivateKey;
use rsa::pkcs8::ToPrivateKey;
@@ -407,6 +409,7 @@ pub struct VerifyArg {
salt_length: Option<u32>,
hash: Option<CryptoHash>,
signature: ZeroCopyBuf,
+ named_curve: Option<CryptoNamedCurve>,
}
pub async fn op_crypto_verify_key(
@@ -528,6 +531,19 @@ pub async fn op_crypto_verify_key(
let key = HmacKey::new(hash, &*args.key.data);
ring::hmac::verify(&key, data, &*args.signature).is_ok()
}
+ Algorithm::Ecdsa => {
+ let signing_alg: &EcdsaSigningAlgorithm =
+ args.named_curve.ok_or_else(not_supported)?.try_into()?;
+ let verify_alg: &EcdsaVerificationAlgorithm =
+ args.named_curve.ok_or_else(not_supported)?.try_into()?;
+
+ let private_key = EcdsaKeyPair::from_pkcs8(signing_alg, &*args.key.data)?;
+ let public_key_bytes = private_key.public_key().as_ref();
+ let public_key =
+ ring::signature::UnparsedPublicKey::new(verify_alg, public_key_bytes);
+
+ public_key.verify(data, &*args.signature).is_ok()
+ }
_ => return Err(type_error("Unsupported algorithm".to_string())),
};