deno.land / x / deno@v1.28.2 / ext / crypto / export_key.rs
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400use crate::shared::*;use const_oid::AssociatedOid;use const_oid::ObjectIdentifier;use deno_core::error::custom_error;use deno_core::error::AnyError;use deno_core::op;use deno_core::ZeroCopyBuf;use elliptic_curve::sec1::ToEncodedPoint;use p256::pkcs8::DecodePrivateKey;use rsa::pkcs1::UIntRef;use serde::Deserialize;use serde::Serialize;use spki::der::asn1;use spki::der::Decode;use spki::der::Encode;use spki::AlgorithmIdentifier;
#[derive(Deserialize)]#[serde(rename_all = "camelCase")]pub struct ExportKeyOptions { format: ExportKeyFormat, #[serde(flatten)] algorithm: ExportKeyAlgorithm,}
#[derive(Deserialize)]#[serde(rename_all = "lowercase")]pub enum ExportKeyFormat { Raw, Pkcs8, Spki, JwkPublic, JwkPrivate, JwkSecret,}
#[derive(Deserialize)]#[serde(rename_all = "camelCase", tag = "algorithm")]pub enum ExportKeyAlgorithm { #[serde(rename = "RSASSA-PKCS1-v1_5")] RsassaPkcs1v15 {}, #[serde(rename = "RSA-PSS")] 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")] Hmac {},}
#[derive(Serialize)]#[serde(untagged)]pub enum ExportKeyResult { Raw(ZeroCopyBuf), Pkcs8(ZeroCopyBuf), Spki(ZeroCopyBuf), JwkSecret { k: String, }, JwkPublicRsa { n: String, e: String, }, JwkPrivateRsa { n: String, e: String, d: String, p: String, q: String, dp: String, dq: String, qi: String, }, JwkPublicEc { x: String, y: String, }, JwkPrivateEc { x: String, y: String, d: String, },}
#[op]pub fn op_crypto_export_key( opts: ExportKeyOptions, key_data: RawKeyData,) -> Result<ExportKeyResult, AnyError> { match opts.algorithm { 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) } }}
fn uint_to_b64(bytes: UIntRef) -> String { base64::encode_config(bytes.as_bytes(), base64::URL_SAFE_NO_PAD)}
fn bytes_to_b64(bytes: &[u8]) -> String { base64::encode_config(bytes, base64::URL_SAFE_NO_PAD)}
fn export_key_rsa( format: ExportKeyFormat, key_data: RawKeyData,) -> Result<ExportKeyResult, deno_core::anyhow::Error> { match format { ExportKeyFormat::Spki => { let subject_public_key = &key_data.as_rsa_public_key()?;
// the SPKI structure let key_info = spki::SubjectPublicKeyInfo { algorithm: spki::AlgorithmIdentifier { // rsaEncryption(1) oid: const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1"), // parameters field should not be ommited (None). // It MUST have ASN.1 type NULL. parameters: Some(asn1::AnyRef::from(asn1::Null)), }, subject_public_key, };
// Infallible because we know the public key is valid. let spki_der = key_info.to_vec().unwrap(); Ok(ExportKeyResult::Spki(spki_der.into())) } ExportKeyFormat::Pkcs8 => { let private_key = key_data.as_rsa_private_key()?;
// the PKCS#8 v1 structure // PrivateKeyInfo ::= SEQUENCE { // version Version, // privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, // privateKey PrivateKey, // attributes [0] IMPLICIT Attributes OPTIONAL }
// version is 0 when publickey is None
let pk_info = rsa::pkcs8::PrivateKeyInfo { public_key: None, algorithm: rsa::pkcs8::AlgorithmIdentifier { // rsaEncryption(1) oid: rsa::pkcs8::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1"), // parameters field should not be ommited (None). // It MUST have ASN.1 type NULL as per defined in RFC 3279 Section 2.3.1 parameters: Some(asn1::AnyRef::from(asn1::Null)), }, private_key, };
// Infallible because we know the private key is valid. let pkcs8_der = pk_info.to_vec().unwrap();
Ok(ExportKeyResult::Pkcs8(pkcs8_der.into())) } ExportKeyFormat::JwkPublic => { let public_key = key_data.as_rsa_public_key()?; let public_key = rsa::pkcs1::RsaPublicKey::from_der(&public_key) .map_err(|_| { custom_error( "DOMExceptionOperationError", "failed to decode public key", ) })?;
Ok(ExportKeyResult::JwkPublicRsa { n: uint_to_b64(public_key.modulus), e: uint_to_b64(public_key.public_exponent), }) } ExportKeyFormat::JwkPrivate => { let private_key = key_data.as_rsa_private_key()?; let private_key = rsa::pkcs1::RsaPrivateKey::from_der(private_key) .map_err(|_| { custom_error( "DOMExceptionOperationError", "failed to decode private key", ) })?;
Ok(ExportKeyResult::JwkPrivateRsa { n: uint_to_b64(private_key.modulus), e: uint_to_b64(private_key.public_exponent), d: uint_to_b64(private_key.private_exponent), p: uint_to_b64(private_key.prime1), q: uint_to_b64(private_key.prime2), dp: uint_to_b64(private_key.exponent1), dq: uint_to_b64(private_key.exponent2), qi: uint_to_b64(private_key.coefficient), }) } _ => Err(unsupported_format()), }}
fn export_key_symmetric( format: ExportKeyFormat, key_data: RawKeyData,) -> Result<ExportKeyResult, deno_core::anyhow::Error> { match format { ExportKeyFormat::JwkSecret => { let bytes = key_data.as_secret_key()?;
Ok(ExportKeyResult::JwkSecret { k: bytes_to_b64(bytes), }) } _ => 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::Raw => { 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")) } }; Ok(ExportKeyResult::Raw(subject_public_key.into())) } 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 => AlgorithmIdentifier { oid: elliptic_curve::ALGORITHM_OID, parameters: Some((&p256::NistP256::OID).into()), }, EcNamedCurve::P384 => AlgorithmIdentifier { oid: elliptic_curve::ALGORITHM_OID, parameters: Some((&p384::NistP384::OID).into()), }, EcNamedCurve::P521 => { return Err(data_error("Unsupported named curve")) } };
let alg_id = match algorithm { ExportKeyAlgorithm::Ecdh { .. } => AlgorithmIdentifier { oid: ObjectIdentifier::new_unwrap("1.2.840.10045.2.1"), 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 = p256::SecretKey::from_pkcs8_der(private_key).map_err(|_| { custom_error( "DOMExceptionOperationError", "failed to decode private key", ) })?;
let point = ec_key.public_key().to_encoded_point(false); 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.to_be_bytes()), }) } else { Err(data_error("expected valid public EC key")) } }
EcNamedCurve::P384 => { let ec_key = p384::SecretKey::from_pkcs8_der(private_key).map_err(|_| { custom_error( "DOMExceptionOperationError", "failed to decode private key", ) })?;
let point = ec_key.public_key().to_encoded_point(false); 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.to_be_bytes()), }) } else { Err(data_error("expected valid public EC key")) } } _ => Err(not_supported_error("Unsupported namedCurve")), } } ExportKeyFormat::JwkSecret => Err(unsupported_format()), }}
Version Info