mirror of
https://github.com/denoland/deno.git
synced 2024-12-23 15:49:44 -05:00
feat(ext/crypto): implement pkcs8/spki/jwk exportKey for ECDSA and ECDH (#13104)
This commit is contained in:
parent
b3545dd447
commit
77e58fe7f9
7 changed files with 554 additions and 78 deletions
|
@ -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);*/
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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
150
ext/crypto/ec_key.rs
Normal 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"))
|
||||
}
|
||||
}
|
|
@ -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()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in a new issue