mirror of
https://github.com/denoland/deno.git
synced 2024-12-31 11:34:15 -05:00
feat(ext/crypto): verify ECDSA signatures (#11739)
This commit is contained in:
parent
d236f432b8
commit
40c63d1255
5 changed files with 98 additions and 2 deletions
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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 {
|
||||
|
|
2
ext/crypto/lib.deno_crypto.d.ts
vendored
2
ext/crypto/lib.deno_crypto.d.ts
vendored
|
@ -175,7 +175,7 @@ interface SubtleCrypto {
|
|||
data: BufferSource,
|
||||
): Promise<ArrayBuffer>;
|
||||
verify(
|
||||
algorithm: AlgorithmIdentifier | RsaPssParams,
|
||||
algorithm: AlgorithmIdentifier | RsaPssParams | EcdsaParams,
|
||||
key: CryptoKey,
|
||||
signature: BufferSource,
|
||||
data: BufferSource,
|
||||
|
|
|
@ -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())),
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue