diff options
author | Sean Michael Wykes <8363933+SeanWykes@users.noreply.github.com> | 2022-01-19 00:38:35 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-19 09:08:35 +0530 |
commit | 77e58fe7f9fc20dabf77424efbd25ce332f8f59c (patch) | |
tree | e8cdf3b06661b209ea685b5d762f670e662923b7 /ext/crypto/export_key.rs | |
parent | b3545dd447dcbab6629827dbe8d127ef82f8da69 (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.rs | 173 |
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()), + } +} |