summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Michael Wykes <8363933+SeanWykes@users.noreply.github.com>2022-01-19 00:38:35 -0300
committerGitHub <noreply@github.com>2022-01-19 09:08:35 +0530
commit77e58fe7f9fc20dabf77424efbd25ce332f8f59c (patch)
treee8cdf3b06661b209ea685b5d762f670e662923b7
parentb3545dd447dcbab6629827dbe8d127ef82f8da69 (diff)
feat(ext/crypto): implement pkcs8/spki/jwk exportKey for ECDSA and ECDH (#13104)
-rw-r--r--cli/tests/unit/webcrypto_test.ts63
-rw-r--r--ext/crypto/00_crypto.js126
-rw-r--r--ext/crypto/ec_key.rs150
-rw-r--r--ext/crypto/export_key.rs173
-rw-r--r--ext/crypto/lib.rs1
-rw-r--r--ext/crypto/shared.rs29
-rw-r--r--tools/wpt/expectation.json90
7 files changed, 554 insertions, 78 deletions
diff --git a/cli/tests/unit/webcrypto_test.ts b/cli/tests/unit/webcrypto_test.ts
index 82ecae434..a318e730d 100644
--- a/cli/tests/unit/webcrypto_test.ts
+++ b/cli/tests/unit/webcrypto_test.ts
@@ -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);*/
}
});
diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js
index 81c475ad7..2596bb052 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 000000000..3509f0aef
--- /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<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"))
+ }
+}
diff --git a/ext/crypto/export_key.rs b/ext/crypto/export_key.rs
index 25faf1791..891aea92a 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<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()),
+ }
+}
diff --git a/ext/crypto/lib.rs b/ext/crypto/lib.rs
index 0331d0dc3..f33c25f00 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 3b32bb2a2..de287efb0 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<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 {
diff --git a/tools/wpt/expectation.json b/tools/wpt/expectation.json
index 4251f7592..a5cfd3144 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",