summaryrefslogtreecommitdiff
path: root/ext/crypto/export_key.rs
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 /ext/crypto/export_key.rs
parentb3545dd447dcbab6629827dbe8d127ef82f8da69 (diff)
feat(ext/crypto): implement pkcs8/spki/jwk exportKey for ECDSA and ECDH (#13104)
Diffstat (limited to 'ext/crypto/export_key.rs')
-rw-r--r--ext/crypto/export_key.rs173
1 files changed, 173 insertions, 0 deletions
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()),
+ }
+}