Skip to content

Commit

Permalink
elliptic-curve: impl pkcs8::FromPublicKey for PublicKey<C> (#385)
Browse files Browse the repository at this point in the history
Support for parsing `PublicKey<C>` from DER and PEM encoded documents
containing X.509 SubjectPublicKeyInfo.

These are one of the common formats for public keys (especially in the
OpenSSL ecosystem). Keys in this format begin with:

    -----BEGIN PUBLIC KEY-----
  • Loading branch information
tarcieri authored Dec 5, 2020
1 parent 5d57542 commit 2425c99
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 10 deletions.
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
77 changes: 77 additions & 0 deletions elliptic-curve/src/public_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<C>
where
Expand Down Expand Up @@ -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<C> FromPublicKey for PublicKey<C>
where
Self: TryFrom<EncodedPoint<C>, Error = Error>,
C: Curve + AlgorithmParameters + ProjectiveArithmetic,
FieldBytes<C>: From<Scalar<C>> + for<'r> From<&'r Scalar<C>>,
Scalar<C>: PrimeField<Repr = FieldBytes<C>>,
AffinePoint<C>: Copy + Clone + Debug,
UntaggedPointSize<C>: Add<U1> + ArrayLength<u8>,
UncompressedPointSize<C>: ArrayLength<u8>,
{
fn from_spki(spki: pkcs8::SubjectPublicKeyInfo<'_>) -> pkcs8::Result<Self> {
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<C> FromStr for PublicKey<C>
where
Self: TryFrom<EncodedPoint<C>, Error = Error>,
C: Curve + AlgorithmParameters + ProjectiveArithmetic,
FieldBytes<C>: From<Scalar<C>> + for<'r> From<&'r Scalar<C>>,
Scalar<C>: PrimeField<Repr = FieldBytes<C>>,
AffinePoint<C>: Copy + Clone + Debug,
UntaggedPointSize<C>: Add<U1> + ArrayLength<u8>,
UncompressedPointSize<C>: ArrayLength<u8>,
{
type Err = Error;

fn from_str(s: &str) -> Result<Self, Error> {
Self::from_public_key_pem(s).map_err(|_| Error)
}
}
29 changes: 23 additions & 6 deletions elliptic-curve/src/secret_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<C: Curve + SecretValue> {
/// Secret value (i.e. secret scalar)
Expand Down Expand Up @@ -182,18 +202,15 @@ where

#[cfg(feature = "pkcs8")]
#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
impl<C> FromPkcs8 for SecretKey<C>
impl<C> FromPrivateKey for SecretKey<C>
where
C: Curve + AlgorithmParameters + SecretValue,
C::Secret: Clone + Zeroize,
FieldBytes<C>: From<C::Secret>,
{
fn from_pkcs8_private_key_info(
private_key_info: pkcs8::PrivateKeyInfo<'_>,
) -> pkcs8::Result<Self>
where
C: AlgorithmParameters,
{
) -> pkcs8::Result<Self> {
if private_key_info.algorithm.oid != ALGORITHM_OID
|| private_key_info.algorithm.parameters != Some(C::OID)
{
Expand Down

0 comments on commit 2425c99

Please sign in to comment.