1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-24 08:09:08 -05:00

feat(ext/crypto): implement pkcs8/spki/jwk exportKey for ECDSA and ECDH (#13104)

This commit is contained in:
Sean Michael Wykes 2022-01-19 00:38:35 -03:00 committed by GitHub
parent b3545dd447
commit 77e58fe7f9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 554 additions and 78 deletions

View file

@ -1176,7 +1176,7 @@ const jwtECKeys = {
type JWK = Record<string, string>;
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);*/
}
});

View file

@ -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;

150
ext/crypto/ec_key.rs Normal file
View file

@ -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<C>,
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<C>,
encoded_point: &'a [u8],
) -> Self {
Self {
private_d,
encoded_point,
algorithm: C::algorithm_identifier(),
}
}
pub fn named_curve_oid(&self) -> Result<ObjectIdentifier, AnyError> {
let parameters = self
.algorithm
.parameters
.ok_or_else(|| data_error("malformed parameters"))?;
Ok(parameters.oid().unwrap())
}
fn internal_to_pkcs8_der(&self) -> der::Result<Vec<u8>> {
// 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<PrivateKeyDocument, AnyError> {
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<ECPrivateKey<C>, 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<PrivateKeyInfo<'a>>
for ECPrivateKey<'a, C>
{
type Error = AnyError;
fn try_from(
pk_info: PrivateKeyInfo<'a>,
) -> Result<ECPrivateKey<'a, C>, 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::<C>::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"))
}
}

View file

@ -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<ExportKeyResult, deno_core::anyhow::Error> {
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 => <p256::NistP256 as p256::elliptic_curve::AlgorithmParameters>::algorithm_identifier(),
EcNamedCurve::P384 => <p384::NistP384 as p384::elliptic_curve::AlgorithmParameters>::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::<p256::NistP256>::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::<p384::NistP384>::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()),
}
}

View file

@ -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;

View file

@ -106,6 +106,35 @@ impl RawKeyData {
_ => Err(type_error("expected secret key")),
}
}
pub fn as_ec_public_key_p256(&self) -> Result<p256::EncodedPoint, AnyError> {
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<p384::EncodedPoint, AnyError> {
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<Cow<'static, str>>) -> AnyError {

View file

@ -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",