diff --git a/Cargo.lock b/Cargo.lock index 8356458bc..c60c0d1c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,8 +93,8 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.3.0-pre" -source = "git+https://github.com/RustCrypto/utils.git#4183dbfdab9797d2b90e11e19b8750b426d2e179" +version = "0.3.1" +source = "git+https://github.com/RustCrypto/utils.git#aa28abd645c444ed832d4a0adf9bd7189501d076" [[package]] name = "cpuid-bool" @@ -263,7 +263,7 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "pkcs8" version = "0.0.0" -source = "git+https://github.com/RustCrypto/utils.git#4183dbfdab9797d2b90e11e19b8750b426d2e179" +source = "git+https://github.com/RustCrypto/utils.git#aa28abd645c444ed832d4a0adf9bd7189501d076" dependencies = [ "const-oid", "subtle-encoding", diff --git a/Cargo.toml b/Cargo.toml index 31f01b7fe..c82783df0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,5 +12,4 @@ members = [ ] [patch.crates-io] -const-oid = { git = "https://github.com/RustCrypto/utils.git" } pkcs8 = { git = "https://github.com/RustCrypto/utils.git" } diff --git a/elliptic-curve/src/public_key.rs b/elliptic-curve/src/public_key.rs index c48ea70df..d83ad5cf3 100644 --- a/elliptic-curve/src/public_key.rs +++ b/elliptic-curve/src/public_key.rs @@ -16,10 +16,39 @@ use core::{ use ff::PrimeField; use generic_array::ArrayLength; +#[cfg(feature = "pkcs8")] +use crate::{AlgorithmParameters, ALGORITHM_OID}; +#[cfg(feature = "pkcs8")] +use pkcs8::FromPublicKey; + +#[cfg(feature = "pem")] +use core::str::FromStr; + /// Elliptic curve public keys. /// /// These are a thin wrapper around [`AffinePoint`] which simplifies /// encoding/decoding. +/// +/// # Parsing "SPKI" Keys +/// +/// X.509 `SubjectPublicKeyInfo` (SPKI) is a commonly used format for encoding +/// public keys, notably public keys corresponding to PKCS#8 private keys. +/// (especially ones generated by OpenSSL). +/// +/// Keys in SPKI format are either binary (ASN.1 BER/DER), or PEM encoded +/// (ASCII) and begin with the following: +/// +/// ```text +/// -----BEGIN PUBLIC KEY----- +/// ``` +/// +/// To decode an elliptic curve public key from SPKI, enable the `pkcs8` +/// feature of this crate (or the `pkcs8` feature of a specific RustCrypto +/// elliptic curve crate) and use the [`pkcs8::FromPublicKey`] trait to +/// parse it. +/// +/// When the `pem` feature of this crate (or a specific RustCrypto elliptic +/// curve crate) is enabled, a [`FromStr`] impl is also available. #[derive(Clone, Debug)] pub struct PublicKey where @@ -222,3 +251,51 @@ where self.to_encoded_point(false) == other.to_encoded_point(false) } } + +#[cfg(feature = "pkcs8")] +#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))] +impl FromPublicKey for PublicKey +where + Self: TryFrom, Error = Error>, + C: Curve + AlgorithmParameters + ProjectiveArithmetic, + FieldBytes: From> + for<'r> From<&'r Scalar>, + Scalar: PrimeField>, + AffinePoint: Copy + Clone + Debug, + UntaggedPointSize: Add + ArrayLength, + UncompressedPointSize: ArrayLength, +{ + fn from_spki(spki: pkcs8::SubjectPublicKeyInfo<'_>) -> pkcs8::Result { + if spki.algorithm.oid != ALGORITHM_OID || spki.algorithm.parameters != Some(C::OID) { + return Err(pkcs8::Error); + } + + // Strip leading `0` byte if it exists + // TODO(tarcieri): determine if there's actually any case where this byte doesn't exist + let bytes = match spki.subject_public_key.get(0) { + Some(0) => &spki.subject_public_key[1..], + Some(_) => spki.subject_public_key, + None => return Err(pkcs8::Error), + }; + + Self::from_sec1_bytes(bytes).map_err(|_| pkcs8::Error) + } +} + +#[cfg(feature = "pem")] +#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] +impl FromStr for PublicKey +where + Self: TryFrom, Error = Error>, + C: Curve + AlgorithmParameters + ProjectiveArithmetic, + FieldBytes: From> + for<'r> From<&'r Scalar>, + Scalar: PrimeField>, + AffinePoint: Copy + Clone + Debug, + UntaggedPointSize: Add + ArrayLength, + UncompressedPointSize: ArrayLength, +{ + type Err = Error; + + fn from_str(s: &str) -> Result { + Self::from_public_key_pem(s).map_err(|_| Error) + } +} diff --git a/elliptic-curve/src/secret_key.rs b/elliptic-curve/src/secret_key.rs index e8d9f62b2..86f91109f 100644 --- a/elliptic-curve/src/secret_key.rs +++ b/elliptic-curve/src/secret_key.rs @@ -28,7 +28,7 @@ use crate::{ #[cfg(feature = "pkcs8")] use crate::{generic_array::typenum::Unsigned, AlgorithmParameters, ALGORITHM_OID}; #[cfg(feature = "pkcs8")] -use pkcs8::FromPkcs8; +use pkcs8::FromPrivateKey; #[cfg(feature = "pem")] use core::str::FromStr; @@ -77,6 +77,26 @@ where /// This type wraps a secret scalar value, helping to prevent accidental /// exposure and securely erasing the value from memory when dropped /// (when the `zeroize` feature of this crate is enabled). +/// +/// # Parsing PKCS#8 Keys +/// +/// PKCS#8 is a commonly used format for encoding secret keys (especially ones +/// generated by OpenSSL). +/// +/// Keys in PKCS#8 format are either binary (ASN.1 BER/DER), or PEM encoded +/// (ASCII) and begin with the following: +/// +/// ```text +/// -----BEGIN PRIVATE KEY----- +/// ``` +/// +/// To decode an elliptic curve private key from PKCS#8, enable the `pkcs8` +/// feature of this crate (or the `pkcs8` feature of a specific RustCrypto +/// elliptic curve crate) and use the [`pkcs8::FromPrivateKey`] trait to +/// parse it. +/// +/// When the `pem` feature of this crate (or a specific RustCrypto elliptic +/// curve crate) is enabled, a [`FromStr`] impl is also available. #[derive(Clone)] pub struct SecretKey { /// Secret value (i.e. secret scalar) @@ -182,7 +202,7 @@ where #[cfg(feature = "pkcs8")] #[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))] -impl FromPkcs8 for SecretKey +impl FromPrivateKey for SecretKey where C: Curve + AlgorithmParameters + SecretValue, C::Secret: Clone + Zeroize, @@ -190,10 +210,7 @@ where { fn from_pkcs8_private_key_info( private_key_info: pkcs8::PrivateKeyInfo<'_>, - ) -> pkcs8::Result - where - C: AlgorithmParameters, - { + ) -> pkcs8::Result { if private_key_info.algorithm.oid != ALGORITHM_OID || private_key_info.algorithm.parameters != Some(C::OID) {