From 77e58fe7f9fc20dabf77424efbd25ce332f8f59c Mon Sep 17 00:00:00 2001 From: Sean Michael Wykes <8363933+SeanWykes@users.noreply.github.com> Date: Wed, 19 Jan 2022 00:38:35 -0300 Subject: [PATCH] feat(ext/crypto): implement pkcs8/spki/jwk exportKey for ECDSA and ECDH (#13104) --- cli/tests/unit/webcrypto_test.ts | 63 +++++------ ext/crypto/00_crypto.js | 126 ++++++++++++++++++++++ ext/crypto/ec_key.rs | 150 +++++++++++++++++++++++++++ ext/crypto/export_key.rs | 173 +++++++++++++++++++++++++++++++ ext/crypto/lib.rs | 1 + ext/crypto/shared.rs | 29 ++++++ tools/wpt/expectation.json | 90 ++++++++-------- 7 files changed, 554 insertions(+), 78 deletions(-) create mode 100644 ext/crypto/ec_key.rs diff --git a/cli/tests/unit/webcrypto_test.ts b/cli/tests/unit/webcrypto_test.ts index 82ecae4346..a318e730d2 100644 --- a/cli/tests/unit/webcrypto_test.ts +++ b/cli/tests/unit/webcrypto_test.ts @@ -1176,7 +1176,7 @@ const jwtECKeys = { type JWK = Record; -function _equalJwk(expected: JWK, got: JWK): boolean { +function equalJwk(expected: JWK, got: JWK): boolean { const fields = Object.keys(expected); for (let i = 0; i < fields.length; i++) { @@ -1218,11 +1218,11 @@ Deno.test(async function testImportExportEcDsaJwk() { true, ["sign"], ); - /*const expPrivateKeyJWK = await subtle.exportKey( + const expPrivateKeyJWK = await subtle.exportKey( "jwk", privateKeyECDSA, ); - assert(equalJwk(privateJWK, expPrivateKeyJWK as JWK));*/ + assert(equalJwk(privateJWK, expPrivateKeyJWK as JWK)); const publicKeyECDSA = await subtle.importKey( "jwk", @@ -1237,12 +1237,12 @@ Deno.test(async function testImportExportEcDsaJwk() { ["verify"], ); - /*const expPublicKeyJWK = await subtle.exportKey( + const expPublicKeyJWK = await subtle.exportKey( "jwk", publicKeyECDSA, ); - assert(equalJwk(publicJWK, expPublicKeyJWK as JWK));*/ + assert(equalJwk(publicJWK, expPublicKeyJWK as JWK)); const signatureECDSA = await subtle.sign( { name: "ECDSA", hash: "SHA-256" }, @@ -1285,11 +1285,11 @@ Deno.test(async function testImportEcDhJwk() { ["deriveBits"], ); - /* const expPrivateKeyJWK = await subtle.exportKey( + const expPrivateKeyJWK = await subtle.exportKey( "jwk", privateKeyECDH, ); - assert(equalJwk(privateJWK, expPrivateKeyJWK as JWK));*/ + assert(equalJwk(privateJWK, expPrivateKeyJWK as JWK)); const publicKeyECDH = await subtle.importKey( "jwk", @@ -1302,11 +1302,11 @@ Deno.test(async function testImportEcDhJwk() { true, [], ); - /* const expPublicKeyJWK = await subtle.exportKey( + const expPublicKeyJWK = await subtle.exportKey( "jwk", publicKeyECDH, ); - assert(equalJwk(publicJWK, expPublicKeyJWK as JWK));*/ + assert(equalJwk(publicJWK, expPublicKeyJWK as JWK)); const derivedKey = await subtle.deriveBits( { @@ -1357,10 +1357,7 @@ Deno.test(async function testImportEcSpkiPkcs8() { for ( const [_key, keyData] of Object.entries(ecTestKeys) ) { - const { size, namedCurve, spki, pkcs8 } = keyData; - if (size != 256) { - continue; - } + const { namedCurve, spki, pkcs8 } = keyData; const privateKeyECDSA = await subtle.importKey( "pkcs8", @@ -1370,12 +1367,19 @@ Deno.test(async function testImportEcSpkiPkcs8() { ["sign"], ); - /*const expPrivateKeyPKCS8 = await subtle.exportKey( + const expPrivateKeyPKCS8 = await subtle.exportKey( "pkcs8", privateKeyECDSA, ); - assertEquals(new Uint8Array(expPrivateKeyPKCS8), pkcs8);*/ + assertEquals(new Uint8Array(expPrivateKeyPKCS8), pkcs8); + + const expPrivateKeyJWK = await subtle.exportKey( + "jwk", + privateKeyECDSA, + ); + + assertEquals(expPrivateKeyJWK.crv, namedCurve); const publicKeyECDSA = await subtle.importKey( "spki", @@ -1385,8 +1389,22 @@ Deno.test(async function testImportEcSpkiPkcs8() { ["verify"], ); + const expPublicKeySPKI = await subtle.exportKey( + "spki", + publicKeyECDSA, + ); + + assertEquals(new Uint8Array(expPublicKeySPKI), spki); + + const expPublicKeyJWK = await subtle.exportKey( + "jwk", + publicKeyECDSA, + ); + + assertEquals(expPublicKeyJWK.crv, namedCurve); + for ( - const hash of [/*"SHA-1", */ "SHA-256" /*"SHA-384", "SHA-512"*/] + const hash of [/*"SHA-1", */ "SHA-256", "SHA-384" /*"SHA-512"*/] ) { const signatureECDSA = await subtle.sign( { name: "ECDSA", hash }, @@ -1402,19 +1420,6 @@ Deno.test(async function testImportEcSpkiPkcs8() { ); assert(verifyECDSA); } - - /*const expPublicKeySPKI = await subtle.exportKey( - "spki", - publicKeyECDSA, - ); - - assertEquals(new Uint8Array(expPublicKeySPKI), spki); - - /*const expPrivateKeySPKI = await subtle.exportKey( - "spki", - privateKeyECDSA, - ); - assertEquals(new Uint8Array(expPrivateKeySPKI), spki);*/ } }); diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js index 81c475ad76..2596bb0526 100644 --- a/ext/crypto/00_crypto.js +++ b/ext/crypto/00_crypto.js @@ -975,6 +975,10 @@ case "RSA-OAEP": { return exportKeyRSA(format, key, innerKey); } + case "ECDH": + case "ECDSA": { + return exportKeyEC(format, key, innerKey); + } case "AES-CTR": case "AES-CBC": case "AES-GCM": @@ -3377,6 +3381,128 @@ } } + function exportKeyEC(format, key, innerKey) { + switch (format) { + case "pkcs8": { + // 1. + if (key[_type] !== "private") { + throw new DOMException( + "Key is not a private key", + "InvalidAccessError", + ); + } + + // 2. + const data = core.opSync("op_crypto_export_key", { + algorithm: key[_algorithm].name, + namedCurve: key[_algorithm].namedCurve, + format: "pkcs8", + }, innerKey); + + return data.buffer; + } + case "spki": { + // 1. + if (key[_type] !== "public") { + throw new DOMException( + "Key is not a public key", + "InvalidAccessError", + ); + } + + // 2. + const data = core.opSync("op_crypto_export_key", { + algorithm: key[_algorithm].name, + namedCurve: key[_algorithm].namedCurve, + format: "spki", + }, innerKey); + + return data.buffer; + } + case "jwk": { + if (key[_algorithm].name == "ECDSA") { + // 1-2. + const jwk = { + kty: "EC", + }; + + // 3.1 + jwk.crv = key[_algorithm].namedCurve; + + // Missing from spec + let algNamedCurve; + + switch (key[_algorithm].namedCurve) { + case "P-256": { + algNamedCurve = "ES256"; + break; + } + case "P-384": { + algNamedCurve = "ES384"; + break; + } + case "P-521": { + algNamedCurve = "ES512"; + break; + } + default: + throw new DOMException( + "Curve algorithm not supported", + "DataError", + ); + } + + jwk.alg = algNamedCurve; + + // 3.2 - 3.4. + const data = core.opSync("op_crypto_export_key", { + format: key[_type] === "private" ? "jwkprivate" : "jwkpublic", + algorithm: key[_algorithm].name, + namedCurve: key[_algorithm].namedCurve, + }, innerKey); + ObjectAssign(jwk, data); + + // 4. + jwk.key_ops = key.usages; + + // 5. + jwk.ext = key[_extractable]; + + return jwk; + } else { // ECDH + // 1-2. + const jwk = { + kty: "EC", + }; + + // missing step from spec + jwk.alg = "ECDH"; + + // 3.1 + jwk.crv = key[_algorithm].namedCurve; + + // 3.2 - 3.4 + const data = core.opSync("op_crypto_export_key", { + format: key[_type] === "private" ? "jwkprivate" : "jwkpublic", + algorithm: key[_algorithm].name, + namedCurve: key[_algorithm].namedCurve, + }, innerKey); + ObjectAssign(jwk, data); + + // 4. + jwk.key_ops = key.usages; + + // 5. + jwk.ext = key[_extractable]; + + return jwk; + } + } + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } + } + async function generateKeyAES(normalizedAlgorithm, extractable, usages) { const algorithmName = normalizedAlgorithm.name; diff --git a/ext/crypto/ec_key.rs b/ext/crypto/ec_key.rs new file mode 100644 index 0000000000..3509f0aef5 --- /dev/null +++ b/ext/crypto/ec_key.rs @@ -0,0 +1,150 @@ +use deno_core::error::AnyError; + +use elliptic_curve::AlgorithmParameters; + +use elliptic_curve::pkcs8; +use elliptic_curve::pkcs8::der; +use elliptic_curve::pkcs8::der::asn1::*; +use elliptic_curve::pkcs8::der::Decodable as Pkcs8Decodable; +use elliptic_curve::pkcs8::der::Encodable; +use elliptic_curve::pkcs8::der::TagNumber; +use elliptic_curve::pkcs8::AlgorithmIdentifier; +use elliptic_curve::pkcs8::ObjectIdentifier; +use elliptic_curve::pkcs8::PrivateKeyDocument; +use elliptic_curve::pkcs8::PrivateKeyInfo; +use elliptic_curve::zeroize::Zeroizing; + +use crate::shared::*; + +const VERSION: u8 = 1; + +const PUBLIC_KEY_TAG: TagNumber = TagNumber::new(1); + +pub struct ECPrivateKey<'a, C: elliptic_curve::Curve> { + pub algorithm: AlgorithmIdentifier<'a>, + + pub private_d: elliptic_curve::FieldBytes, + + pub encoded_point: &'a [u8], +} + +#[allow(dead_code)] +///todo(@sean) - to be removed in #13154 +impl<'a, C> ECPrivateKey<'a, C> +where + C: elliptic_curve::Curve + AlgorithmParameters, +{ + /// Create a new ECPrivateKey from a serialized private scalar and encoded public key + pub fn from_private_and_public_bytes( + private_d: elliptic_curve::FieldBytes, + encoded_point: &'a [u8], + ) -> Self { + Self { + private_d, + encoded_point, + algorithm: C::algorithm_identifier(), + } + } + + pub fn named_curve_oid(&self) -> Result { + let parameters = self + .algorithm + .parameters + .ok_or_else(|| data_error("malformed parameters"))?; + + Ok(parameters.oid().unwrap()) + } + + fn internal_to_pkcs8_der(&self) -> der::Result> { + // Shamelessly copied from pkcs8 crate and modified so as + // to not require Arithmetic trait currently missing from p384 + let secret_key_field = OctetString::new(&self.private_d)?; + let public_key_bytes = &self.encoded_point; + let public_key_field = ContextSpecific { + tag_number: PUBLIC_KEY_TAG, + value: BitString::new(public_key_bytes)?.into(), + }; + + let der_message_fields: &[&dyn Encodable] = + &[&VERSION, &secret_key_field, &public_key_field]; + + let encoded_len = + der::message::encoded_len(der_message_fields)?.try_into()?; + let mut der_message = Zeroizing::new(vec![0u8; encoded_len]); + let mut encoder = der::Encoder::new(&mut der_message); + encoder.message(der_message_fields)?; + encoder.finish()?; + + Ok(der_message.to_vec()) + } + + pub fn to_pkcs8_der(&self) -> Result { + let pkcs8_der = self + .internal_to_pkcs8_der() + .map_err(|_| data_error("expected valid PKCS#8 data"))?; + + let pki = + pkcs8::PrivateKeyInfo::new(C::algorithm_identifier(), pkcs8_der.as_ref()); + + Ok(pki.to_der()) + } +} + +impl<'a, C: elliptic_curve::Curve> TryFrom<&'a [u8]> for ECPrivateKey<'a, C> { + type Error = AnyError; + + fn try_from(bytes: &'a [u8]) -> Result, AnyError> { + let pk_info = PrivateKeyInfo::from_der(bytes) + .map_err(|_| data_error("expected valid PKCS#8 data"))?; + + Self::try_from(pk_info) + } +} + +impl<'a, C: elliptic_curve::Curve> TryFrom> + for ECPrivateKey<'a, C> +{ + type Error = AnyError; + + fn try_from( + pk_info: PrivateKeyInfo<'a>, + ) -> Result, AnyError> { + let any = der::asn1::Any::from_der(pk_info.private_key).map_err(|_| { + data_error("expected valid PrivateKeyInfo private_key der") + })?; + + if pk_info.algorithm.oid != elliptic_curve::ALGORITHM_OID { + return Err(data_error("unsupported algorithm")); + } + + any + .sequence(|decoder| { + // ver + if decoder.uint8()? != VERSION { + return Err(der::Tag::Integer.value_error()); + } + + // private_key + let priv_key = decoder.octet_string()?.as_bytes(); + let mut private_d = elliptic_curve::FieldBytes::::default(); + if priv_key.len() != private_d.len() { + return Err(der::Tag::Sequence.value_error()); + }; + private_d.copy_from_slice(priv_key); + + let public_key = decoder + .context_specific(PUBLIC_KEY_TAG)? + .ok_or_else(|| { + der::Tag::ContextSpecific(PUBLIC_KEY_TAG).value_error() + })? + .bit_string()?; + + Ok(Self { + private_d, + encoded_point: public_key.as_bytes(), + algorithm: pk_info.algorithm, + }) + }) + .map_err(|_| data_error("expected valid PrivateKeyInfo private_key der")) + } +} diff --git a/ext/crypto/export_key.rs b/ext/crypto/export_key.rs index 25faf1791f..891aea92a8 100644 --- a/ext/crypto/export_key.rs +++ b/ext/crypto/export_key.rs @@ -8,7 +8,10 @@ use serde::Serialize; use spki::der::asn1; use spki::der::Decodable; use spki::der::Encodable; +use spki::AlgorithmIdentifier; +use spki::ObjectIdentifier; +use crate::ec_key::ECPrivateKey; use crate::shared::*; #[derive(Deserialize)] @@ -38,6 +41,10 @@ pub enum ExportKeyAlgorithm { RsaPss {}, #[serde(rename = "RSA-OAEP")] RsaOaep {}, + #[serde(rename = "ECDSA", rename_all = "camelCase")] + Ecdsa { named_curve: EcNamedCurve }, + #[serde(rename = "ECDH", rename_all = "camelCase")] + Ecdh { named_curve: EcNamedCurve }, #[serde(rename = "AES")] Aes {}, #[serde(rename = "HMAC")] @@ -66,6 +73,15 @@ pub enum ExportKeyResult { dq: String, qi: String, }, + JwkPublicEc { + x: String, + y: String, + }, + JwkPrivateEc { + x: String, + y: String, + d: String, + }, } pub fn op_crypto_export_key( @@ -77,6 +93,10 @@ pub fn op_crypto_export_key( ExportKeyAlgorithm::RsassaPkcs1v15 {} | ExportKeyAlgorithm::RsaPss {} | ExportKeyAlgorithm::RsaOaep {} => export_key_rsa(opts.format, key_data), + ExportKeyAlgorithm::Ecdh { named_curve } + | ExportKeyAlgorithm::Ecdsa { named_curve } => { + export_key_ec(opts.format, key_data, opts.algorithm, named_curve) + } ExportKeyAlgorithm::Aes {} | ExportKeyAlgorithm::Hmac {} => { export_key_symmetric(opts.format, key_data) } @@ -200,3 +220,156 @@ fn export_key_symmetric( _ => Err(unsupported_format()), } } + +fn export_key_ec( + format: ExportKeyFormat, + key_data: RawKeyData, + algorithm: ExportKeyAlgorithm, + named_curve: EcNamedCurve, +) -> Result { + match format { + ExportKeyFormat::Spki => { + let subject_public_key = match named_curve { + EcNamedCurve::P256 => { + let point = key_data.as_ec_public_key_p256()?; + + point.as_ref().to_vec() + } + EcNamedCurve::P384 => { + let point = key_data.as_ec_public_key_p384()?; + + point.as_ref().to_vec() + } + EcNamedCurve::P521 => { + return Err(data_error("Unsupported named curve")) + } + }; + + let alg_id = match named_curve { + EcNamedCurve::P256 => ::algorithm_identifier(), + EcNamedCurve::P384 => ::algorithm_identifier(), + EcNamedCurve::P521 => return Err(data_error("Unsupported named curve")) + }; + + let alg_id = match algorithm { + ExportKeyAlgorithm::Ecdh { .. } => AlgorithmIdentifier { + oid: ObjectIdentifier::new("1.3.132.1.12"), + parameters: alg_id.parameters, + }, + _ => alg_id, + }; + + // the SPKI structure + let key_info = spki::SubjectPublicKeyInfo { + algorithm: alg_id, + subject_public_key: &subject_public_key, + }; + + let spki_der = key_info.to_vec().unwrap(); + + Ok(ExportKeyResult::Spki(spki_der.into())) + } + ExportKeyFormat::Pkcs8 => { + // private_key is a PKCS#8 DER-encoded private key + let private_key = key_data.as_ec_private_key()?; + + Ok(ExportKeyResult::Pkcs8(private_key.to_vec().into())) + } + ExportKeyFormat::JwkPublic => match named_curve { + EcNamedCurve::P256 => { + let point = key_data.as_ec_public_key_p256()?; + let coords = point.coordinates(); + + if let p256::elliptic_curve::sec1::Coordinates::Uncompressed { x, y } = + coords + { + Ok(ExportKeyResult::JwkPublicEc { + x: bytes_to_b64(x), + y: bytes_to_b64(y), + }) + } else { + Err(custom_error( + "DOMExceptionOperationError", + "failed to decode public key", + )) + } + } + EcNamedCurve::P384 => { + let point = key_data.as_ec_public_key_p384()?; + let coords = point.coordinates(); + + if let p384::elliptic_curve::sec1::Coordinates::Uncompressed { x, y } = + coords + { + Ok(ExportKeyResult::JwkPublicEc { + x: bytes_to_b64(x), + y: bytes_to_b64(y), + }) + } else { + Err(custom_error( + "DOMExceptionOperationError", + "failed to decode public key", + )) + } + } + EcNamedCurve::P521 => Err(data_error("Unsupported named curve")), + }, + ExportKeyFormat::JwkPrivate => { + let private_key = key_data.as_ec_private_key()?; + + match named_curve { + EcNamedCurve::P256 => { + let ec_key = ECPrivateKey::::try_from(private_key) + .map_err(|_| { + custom_error( + "DOMExceptionOperationError", + "failed to decode private key", + ) + })?; + + let point = p256::EncodedPoint::from_bytes(&ec_key.encoded_point) + .map_err(|_| data_error("expected valid public EC key"))?; + + if let elliptic_curve::sec1::Coordinates::Uncompressed { x, y } = + point.coordinates() + { + Ok(ExportKeyResult::JwkPrivateEc { + x: bytes_to_b64(x), + y: bytes_to_b64(y), + d: bytes_to_b64(&ec_key.private_d), + }) + } else { + Err(data_error("expected valid public EC key")) + } + } + + EcNamedCurve::P384 => { + let ec_key = ECPrivateKey::::try_from(private_key) + .map_err(|_| { + custom_error( + "DOMExceptionOperationError", + "failed to decode private key", + ) + })?; + + let point = p384::EncodedPoint::from_bytes(&ec_key.encoded_point) + .map_err(|_| data_error("expected valid public EC key"))?; + + if let elliptic_curve::sec1::Coordinates::Uncompressed { x, y } = + point.coordinates() + { + Ok(ExportKeyResult::JwkPrivateEc { + x: bytes_to_b64(x), + y: bytes_to_b64(y), + d: bytes_to_b64(&ec_key.private_d), + }) + } else { + Err(data_error("expected valid public EC key")) + } + } + _ => Err(not_supported_error("Unsupported namedCurve")), + } + } + ExportKeyFormat::JwkSecret => Err(unsupported_format()), + } +} diff --git a/ext/crypto/lib.rs b/ext/crypto/lib.rs index 0331d0dc36..f33c25f000 100644 --- a/ext/crypto/lib.rs +++ b/ext/crypto/lib.rs @@ -58,6 +58,7 @@ use std::path::PathBuf; pub use rand; // Re-export rand mod decrypt; +mod ec_key; mod encrypt; mod export_key; mod generate_key; diff --git a/ext/crypto/shared.rs b/ext/crypto/shared.rs index 3b32bb2a2e..de287efb00 100644 --- a/ext/crypto/shared.rs +++ b/ext/crypto/shared.rs @@ -106,6 +106,35 @@ impl RawKeyData { _ => Err(type_error("expected secret key")), } } + + pub fn as_ec_public_key_p256(&self) -> Result { + match self { + RawKeyData::Public(data) => { + // public_key is a serialized EncodedPoint + p256::EncodedPoint::from_bytes(&data) + .map_err(|_| type_error("expected valid private EC key")) + } + _ => Err(type_error("expected private key")), + } + } + + pub fn as_ec_public_key_p384(&self) -> Result { + match self { + RawKeyData::Public(data) => { + // public_key is a serialized EncodedPoint + p384::EncodedPoint::from_bytes(&data) + .map_err(|_| type_error("expected valid private EC key")) + } + _ => Err(type_error("expected private key")), + } + } + + pub fn as_ec_private_key(&self) -> Result<&[u8], AnyError> { + match self { + RawKeyData::Private(data) => Ok(data), + _ => Err(type_error("expected private key")), + } + } } pub fn data_error(msg: impl Into>) -> AnyError { diff --git a/tools/wpt/expectation.json b/tools/wpt/expectation.json index 4251f75920..a5cfd31447 100644 --- a/tools/wpt/expectation.json +++ b/tools/wpt/expectation.json @@ -616,12 +616,6 @@ ], "import_export": { "ec_importKey.https.any.html": [ - "Good parameters: P-256 bits (spki, buffer(91), {name: ECDSA, namedCurve: P-256}, true, [])", - "Good parameters: P-256 bits (jwk, object(kty, crv, x, y), {name: ECDSA, namedCurve: P-256}, true, [])", - "Good parameters: P-256 bits (pkcs8, buffer(138), {name: ECDSA, namedCurve: P-256}, true, [sign])", - "Good parameters: P-256 bits (jwk, object(kty, crv, x, y, d), {name: ECDSA, namedCurve: P-256}, true, [sign])", - "Good parameters: P-384 bits (spki, buffer(120), {name: ECDSA, namedCurve: P-384}, true, [])", - "Good parameters: P-384 bits (jwk, object(kty, crv, x, y), {name: ECDSA, namedCurve: P-384}, true, [])", "Good parameters: P-384 bits (pkcs8, buffer(185), {name: ECDSA, namedCurve: P-384}, true, [sign])", "Good parameters: P-384 bits (jwk, object(kty, crv, x, y, d), {name: ECDSA, namedCurve: P-384}, true, [sign])", "Good parameters: P-384 bits (pkcs8, buffer(185), {name: ECDSA, namedCurve: P-384}, false, [sign])", @@ -635,15 +629,7 @@ "Good parameters: P-521 bits (pkcs8, buffer(241), {name: ECDSA, namedCurve: P-521}, false, [sign])", "Good parameters: P-521 bits (jwk, object(kty, crv, x, y, d), {name: ECDSA, namedCurve: P-521}, false, [sign])", "Good parameters: P-256 bits (spki, buffer(91), {name: ECDH, namedCurve: P-256}, true, [])", - "Good parameters: P-256 bits (jwk, object(kty, crv, x, y), {name: ECDH, namedCurve: P-256}, true, [])", - "Good parameters: P-256 bits (pkcs8, buffer(138), {name: ECDH, namedCurve: P-256}, true, [deriveKey])", - "Good parameters: P-256 bits (jwk, object(kty, crv, x, y, d), {name: ECDH, namedCurve: P-256}, true, [deriveKey])", - "Good parameters: P-256 bits (pkcs8, buffer(138), {name: ECDH, namedCurve: P-256}, true, [deriveBits, deriveKey])", - "Good parameters: P-256 bits (jwk, object(kty, crv, x, y, d), {name: ECDH, namedCurve: P-256}, true, [deriveBits, deriveKey])", - "Good parameters: P-256 bits (pkcs8, buffer(138), {name: ECDH, namedCurve: P-256}, true, [deriveBits])", - "Good parameters: P-256 bits (jwk, object(kty, crv, x, y, d), {name: ECDH, namedCurve: P-256}, true, [deriveBits])", "Good parameters: P-384 bits (spki, buffer(120), {name: ECDH, namedCurve: P-384}, true, [])", - "Good parameters: P-384 bits (jwk, object(kty, crv, x, y), {name: ECDH, namedCurve: P-384}, true, [])", "Good parameters: P-384 bits (pkcs8, buffer(185), {name: ECDH, namedCurve: P-384}, true, [deriveKey])", "Good parameters: P-384 bits (jwk, object(kty, crv, x, y, d), {name: ECDH, namedCurve: P-384}, true, [deriveKey])", "Good parameters: P-384 bits (pkcs8, buffer(185), {name: ECDH, namedCurve: P-384}, true, [deriveBits, deriveKey])", @@ -674,12 +660,6 @@ "Good parameters: P-521 bits (jwk, object(kty, crv, x, y, d), {name: ECDH, namedCurve: P-521}, false, [deriveBits])" ], "ec_importKey.https.any.worker.html": [ - "Good parameters: P-256 bits (spki, buffer(91), {name: ECDSA, namedCurve: P-256}, true, [])", - "Good parameters: P-256 bits (jwk, object(kty, crv, x, y), {name: ECDSA, namedCurve: P-256}, true, [])", - "Good parameters: P-256 bits (pkcs8, buffer(138), {name: ECDSA, namedCurve: P-256}, true, [sign])", - "Good parameters: P-256 bits (jwk, object(kty, crv, x, y, d), {name: ECDSA, namedCurve: P-256}, true, [sign])", - "Good parameters: P-384 bits (spki, buffer(120), {name: ECDSA, namedCurve: P-384}, true, [])", - "Good parameters: P-384 bits (jwk, object(kty, crv, x, y), {name: ECDSA, namedCurve: P-384}, true, [])", "Good parameters: P-384 bits (pkcs8, buffer(185), {name: ECDSA, namedCurve: P-384}, true, [sign])", "Good parameters: P-384 bits (jwk, object(kty, crv, x, y, d), {name: ECDSA, namedCurve: P-384}, true, [sign])", "Good parameters: P-384 bits (pkcs8, buffer(185), {name: ECDSA, namedCurve: P-384}, false, [sign])", @@ -693,15 +673,7 @@ "Good parameters: P-521 bits (pkcs8, buffer(241), {name: ECDSA, namedCurve: P-521}, false, [sign])", "Good parameters: P-521 bits (jwk, object(kty, crv, x, y, d), {name: ECDSA, namedCurve: P-521}, false, [sign])", "Good parameters: P-256 bits (spki, buffer(91), {name: ECDH, namedCurve: P-256}, true, [])", - "Good parameters: P-256 bits (jwk, object(kty, crv, x, y), {name: ECDH, namedCurve: P-256}, true, [])", - "Good parameters: P-256 bits (pkcs8, buffer(138), {name: ECDH, namedCurve: P-256}, true, [deriveKey])", - "Good parameters: P-256 bits (jwk, object(kty, crv, x, y, d), {name: ECDH, namedCurve: P-256}, true, [deriveKey])", - "Good parameters: P-256 bits (pkcs8, buffer(138), {name: ECDH, namedCurve: P-256}, true, [deriveBits, deriveKey])", - "Good parameters: P-256 bits (jwk, object(kty, crv, x, y, d), {name: ECDH, namedCurve: P-256}, true, [deriveBits, deriveKey])", - "Good parameters: P-256 bits (pkcs8, buffer(138), {name: ECDH, namedCurve: P-256}, true, [deriveBits])", - "Good parameters: P-256 bits (jwk, object(kty, crv, x, y, d), {name: ECDH, namedCurve: P-256}, true, [deriveBits])", "Good parameters: P-384 bits (spki, buffer(120), {name: ECDH, namedCurve: P-384}, true, [])", - "Good parameters: P-384 bits (jwk, object(kty, crv, x, y), {name: ECDH, namedCurve: P-384}, true, [])", "Good parameters: P-384 bits (pkcs8, buffer(185), {name: ECDH, namedCurve: P-384}, true, [deriveKey])", "Good parameters: P-384 bits (jwk, object(kty, crv, x, y, d), {name: ECDH, namedCurve: P-384}, true, [deriveKey])", "Good parameters: P-384 bits (pkcs8, buffer(185), {name: ECDH, namedCurve: P-384}, true, [deriveBits, deriveKey])", @@ -1007,6 +979,16 @@ "Can wrap and unwrap AES-GCM keys as non-extractable using raw and AES-CBC", "Can wrap and unwrap AES-GCM keys as non-extractable using jwk and AES-CBC", "Can unwrap AES-GCM non-extractable keys using jwk and AES-CBC", + "Can wrap and unwrap ECDSA private key keys using pkcs8 and AES-GCM", + "Can wrap and unwrap ECDSA private key keys as non-extractable using pkcs8 and AES-GCM", + "Can wrap and unwrap ECDSA private key keys using jwk and AES-GCM", + "Can wrap and unwrap ECDSA private key keys as non-extractable using jwk and AES-GCM", + "Can unwrap ECDSA private key non-extractable keys using jwk and AES-GCM", + "Can wrap and unwrap ECDH private key keys using pkcs8 and AES-GCM", + "Can wrap and unwrap ECDH private key keys as non-extractable using pkcs8 and AES-GCM", + "Can wrap and unwrap ECDH private key keys using jwk and AES-GCM", + "Can wrap and unwrap ECDH private key keys as non-extractable using jwk and AES-GCM", + "Can unwrap ECDH private key non-extractable keys using jwk and AES-GCM", "Can wrap and unwrap HMAC keys using raw and AES-GCM", "Can wrap and unwrap HMAC keys as non-extractable using raw and AES-GCM", "Can wrap and unwrap HMAC keys using jwk and AES-GCM", @@ -1032,20 +1014,6 @@ "Can wrap and unwrap AES-KW keys using jwk and AES-GCM", "Can wrap and unwrap AES-KW keys as non-extractable using jwk and AES-GCM", "Can unwrap AES-KW non-extractable keys using jwk and AES-GCM", - "Can wrap and unwrap RSA-OAEP public key keys using spki and AES-GCM", - "Can wrap and unwrap RSA-OAEP public key keys using jwk and AES-GCM", - "Can wrap and unwrap RSA-OAEP private key keys using pkcs8 and AES-GCM", - "Can wrap and unwrap RSA-OAEP private key keys as non-extractable using pkcs8 and AES-GCM", - "Can wrap and unwrap RSA-OAEP private key keys using jwk and AES-GCM", - "Can wrap and unwrap RSA-OAEP private key keys as non-extractable using jwk and AES-GCM", - "Can unwrap RSA-OAEP private key non-extractable keys using jwk and AES-GCM", - "Can wrap and unwrap RSA-PSS public key keys using spki and AES-GCM", - "Can wrap and unwrap RSA-PSS public key keys using jwk and AES-GCM", - "Can wrap and unwrap RSA-PSS private key keys using pkcs8 and AES-GCM", - "Can wrap and unwrap RSA-PSS private key keys as non-extractable using pkcs8 and AES-GCM", - "Can wrap and unwrap RSA-PSS private key keys using jwk and AES-GCM", - "Can wrap and unwrap RSA-PSS private key keys as non-extractable using jwk and AES-GCM", - "Can unwrap RSA-PSS private key non-extractable keys using jwk and AES-GCM", "Can wrap and unwrap RSASSA-PKCS1-v1_5 public key keys using spki and AES-GCM", "Can wrap and unwrap RSASSA-PKCS1-v1_5 public key keys using jwk and AES-GCM", "Can wrap and unwrap RSASSA-PKCS1-v1_5 private key keys using pkcs8 and AES-GCM", @@ -1053,6 +1021,20 @@ "Can wrap and unwrap RSASSA-PKCS1-v1_5 private key keys using jwk and AES-GCM", "Can wrap and unwrap RSASSA-PKCS1-v1_5 private key keys as non-extractable using jwk and AES-GCM", "Can unwrap RSASSA-PKCS1-v1_5 private key non-extractable keys using jwk and AES-GCM", + "Can wrap and unwrap RSA-PSS public key keys using spki and AES-GCM", + "Can wrap and unwrap RSA-PSS public key keys using jwk and AES-GCM", + "Can wrap and unwrap RSA-PSS private key keys using pkcs8 and AES-GCM", + "Can wrap and unwrap RSA-PSS private key keys as non-extractable using pkcs8 and AES-GCM", + "Can wrap and unwrap RSA-PSS private key keys using jwk and AES-GCM", + "Can wrap and unwrap RSA-PSS private key keys as non-extractable using jwk and AES-GCM", + "Can unwrap RSA-PSS private key non-extractable keys using jwk and AES-GCM", + "Can wrap and unwrap RSA-OAEP public key keys using spki and AES-GCM", + "Can wrap and unwrap RSA-OAEP public key keys using jwk and AES-GCM", + "Can wrap and unwrap RSA-OAEP private key keys using pkcs8 and AES-GCM", + "Can wrap and unwrap RSA-OAEP private key keys as non-extractable using pkcs8 and AES-GCM", + "Can wrap and unwrap RSA-OAEP private key keys using jwk and AES-GCM", + "Can wrap and unwrap RSA-OAEP private key keys as non-extractable using jwk and AES-GCM", + "Can unwrap RSA-OAEP private key non-extractable keys using jwk and AES-GCM", "Can wrap and unwrap AES-GCM keys as non-extractable using raw and AES-KW", "Can wrap and unwrap AES-GCM keys as non-extractable using raw and RSA-OAEP", "Can wrap and unwrap AES-GCM keys as non-extractable using jwk and RSA-OAEP", @@ -1066,6 +1048,16 @@ "Can wrap and unwrap AES-GCM keys as non-extractable using raw and AES-CBC", "Can wrap and unwrap AES-GCM keys as non-extractable using jwk and AES-CBC", "Can unwrap AES-GCM non-extractable keys using jwk and AES-CBC", + "Can wrap and unwrap ECDSA private key keys using pkcs8 and AES-GCM", + "Can wrap and unwrap ECDSA private key keys as non-extractable using pkcs8 and AES-GCM", + "Can wrap and unwrap ECDSA private key keys using jwk and AES-GCM", + "Can wrap and unwrap ECDSA private key keys as non-extractable using jwk and AES-GCM", + "Can unwrap ECDSA private key non-extractable keys using jwk and AES-GCM", + "Can wrap and unwrap ECDH private key keys using pkcs8 and AES-GCM", + "Can wrap and unwrap ECDH private key keys as non-extractable using pkcs8 and AES-GCM", + "Can wrap and unwrap ECDH private key keys using jwk and AES-GCM", + "Can wrap and unwrap ECDH private key keys as non-extractable using jwk and AES-GCM", + "Can unwrap ECDH private key non-extractable keys using jwk and AES-GCM", "Can wrap and unwrap HMAC keys using raw and AES-GCM", "Can wrap and unwrap HMAC keys as non-extractable using raw and AES-GCM", "Can wrap and unwrap HMAC keys using jwk and AES-GCM", @@ -1091,6 +1083,13 @@ "Can wrap and unwrap AES-KW keys using jwk and AES-GCM", "Can wrap and unwrap AES-KW keys as non-extractable using jwk and AES-GCM", "Can unwrap AES-KW non-extractable keys using jwk and AES-GCM", + "Can wrap and unwrap RSA-PSS public key keys using spki and AES-GCM", + "Can wrap and unwrap RSA-PSS public key keys using jwk and AES-GCM", + "Can wrap and unwrap RSA-PSS private key keys using pkcs8 and AES-GCM", + "Can wrap and unwrap RSA-PSS private key keys as non-extractable using pkcs8 and AES-GCM", + "Can wrap and unwrap RSA-PSS private key keys using jwk and AES-GCM", + "Can wrap and unwrap RSA-PSS private key keys as non-extractable using jwk and AES-GCM", + "Can unwrap RSA-PSS private key non-extractable keys using jwk and AES-GCM", "Can wrap and unwrap RSA-OAEP public key keys using spki and AES-GCM", "Can wrap and unwrap RSA-OAEP public key keys using jwk and AES-GCM", "Can wrap and unwrap RSA-OAEP private key keys using pkcs8 and AES-GCM", @@ -1105,13 +1104,6 @@ "Can wrap and unwrap RSASSA-PKCS1-v1_5 private key keys using jwk and AES-GCM", "Can wrap and unwrap RSASSA-PKCS1-v1_5 private key keys as non-extractable using jwk and AES-GCM", "Can unwrap RSASSA-PKCS1-v1_5 private key non-extractable keys using jwk and AES-GCM", - "Can wrap and unwrap RSA-PSS public key keys using spki and AES-GCM", - "Can wrap and unwrap RSA-PSS public key keys using jwk and AES-GCM", - "Can wrap and unwrap RSA-PSS private key keys using pkcs8 and AES-GCM", - "Can wrap and unwrap RSA-PSS private key keys as non-extractable using pkcs8 and AES-GCM", - "Can wrap and unwrap RSA-PSS private key keys using jwk and AES-GCM", - "Can wrap and unwrap RSA-PSS private key keys as non-extractable using jwk and AES-GCM", - "Can unwrap RSA-PSS private key non-extractable keys using jwk and AES-GCM", "Can wrap and unwrap AES-GCM keys as non-extractable using raw and AES-KW", "Can wrap and unwrap AES-GCM keys as non-extractable using raw and RSA-OAEP", "Can wrap and unwrap AES-GCM keys as non-extractable using jwk and RSA-OAEP",