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/ec_key.rs | |
parent | b3545dd447dcbab6629827dbe8d127ef82f8da69 (diff) |
feat(ext/crypto): implement pkcs8/spki/jwk exportKey for ECDSA and ECDH (#13104)
Diffstat (limited to 'ext/crypto/ec_key.rs')
-rw-r--r-- | ext/crypto/ec_key.rs | 150 |
1 files changed, 150 insertions, 0 deletions
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")) + } +} |