Skip to content

Commit

Permalink
spki: make SubjectPublicKeyInfo own the public key (#790)
Browse files Browse the repository at this point in the history
Co-authored-by: Tony Arcieri <bascule@gmail.com>
  • Loading branch information
baloo and tarcieri authored Dec 13, 2022
1 parent ab9a848 commit 01dd38c
Show file tree
Hide file tree
Showing 11 changed files with 114 additions and 70 deletions.
15 changes: 9 additions & 6 deletions pkcs1/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ use {
};

#[cfg(feature = "pkcs8")]
use crate::{ALGORITHM_ID, ALGORITHM_OID};
use {
crate::{ALGORITHM_ID, ALGORITHM_OID},
der::asn1::BitStringRef,
};

#[cfg(feature = "std")]
use std::path::Path;
Expand Down Expand Up @@ -186,12 +189,12 @@ where
#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
impl<T: pkcs8::DecodePublicKey> DecodeRsaPublicKey for T
where
T: for<'a> TryFrom<pkcs8::SubjectPublicKeyInfo<'a>, Error = pkcs8::Error>,
T: for<'a> TryFrom<pkcs8::SubjectPublicKeyInfoRef<'a>, Error = pkcs8::Error>,
{
fn from_pkcs1_der(public_key: &[u8]) -> Result<Self> {
Ok(Self::try_from(pkcs8::SubjectPublicKeyInfo {
Ok(Self::try_from(pkcs8::SubjectPublicKeyInfoRef {
algorithm: ALGORITHM_ID,
subject_public_key: public_key,
subject_public_key: BitStringRef::from_bytes(public_key)?,
})?)
}
}
Expand All @@ -212,8 +215,8 @@ impl<T: pkcs8::EncodePrivateKey> EncodeRsaPrivateKey for T {
impl<T: pkcs8::EncodePublicKey> EncodeRsaPublicKey for T {
fn to_pkcs1_der(&self) -> Result<Document> {
let doc = self.to_public_key_der()?;
let spki = pkcs8::SubjectPublicKeyInfo::from_der(doc.as_bytes())?;
let spki = pkcs8::SubjectPublicKeyInfoRef::from_der(doc.as_bytes())?;
spki.algorithm.assert_algorithm_oid(ALGORITHM_OID)?;
RsaPublicKey::from_der(spki.subject_public_key)?.try_into()
RsaPublicKey::from_der(spki.subject_public_key.raw_bytes())?.try_into()
}
}
4 changes: 3 additions & 1 deletion pkcs8/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ pub use crate::{
version::Version,
};
pub use der::{self, asn1::ObjectIdentifier, oid::AssociatedOid};
pub use spki::{self, AlgorithmIdentifierRef, DecodePublicKey, SubjectPublicKeyInfo};
pub use spki::{
self, AlgorithmIdentifierRef, DecodePublicKey, SubjectPublicKeyInfo, SubjectPublicKeyInfoRef,
};

#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
Expand Down
7 changes: 5 additions & 2 deletions spki/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,16 @@ mod fingerprint;
pub use crate::{
algorithm::{AlgorithmIdentifier, AlgorithmIdentifierRef},
error::{Error, Result},
spki::SubjectPublicKeyInfo,
spki::{SubjectPublicKeyInfo, SubjectPublicKeyInfoRef},
traits::DecodePublicKey,
};
pub use der::{self, asn1::ObjectIdentifier};

#[cfg(feature = "alloc")]
pub use {crate::traits::EncodePublicKey, der::Document};
pub use {
crate::{spki::SubjectPublicKeyInfoOwned, traits::EncodePublicKey},
der::Document,
};

#[cfg(feature = "fingerprint")]
pub use crate::fingerprint::FingerprintBytes;
91 changes: 62 additions & 29 deletions spki/src/spki.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
//! X.509 `SubjectPublicKeyInfo`

use crate::{AlgorithmIdentifierRef, Error, Result};
use crate::{AlgorithmIdentifier, Error, Result};
use core::cmp::Ordering;
use der::{
asn1::BitStringRef, Decode, DecodeValue, DerOrd, Encode, Header, Reader, Sequence, ValueOrd,
asn1::{AnyRef, BitStringRef},
Choice, Decode, DecodeValue, DerOrd, Encode, FixedTag, Header, Reader, Sequence, ValueOrd,
};

#[cfg(feature = "alloc")]
use der::Document;
use der::{
asn1::{Any, BitString},
Document,
};

#[cfg(feature = "fingerprint")]
use crate::{fingerprint, FingerprintBytes};

#[cfg(feature = "pem")]
use der::pem::PemLabel;

#[cfg(doc)]
use crate::AlgorithmIdentifier;
/// [`SubjectPublicKeyInfo`] with [`AnyRef`] algorithm parameters, and [`BitStringRef`] params.
pub type SubjectPublicKeyInfoRef<'a> = SubjectPublicKeyInfo<AnyRef<'a>, BitStringRef<'a>>;

/// [`SubjectPublicKeyInfo`] with [`Any`] algorithm parameters, and [`BitString`] params.
#[cfg(feature = "alloc")]
pub type SubjectPublicKeyInfoOwned = SubjectPublicKeyInfo<Any, BitString>;

/// X.509 `SubjectPublicKeyInfo` (SPKI) as defined in [RFC 5280 § 4.1.2.7].
///
Expand All @@ -30,16 +38,22 @@ use crate::AlgorithmIdentifier;
/// ```
///
/// [RFC 5280 § 4.1.2.7]: https://tools.ietf.org/html/rfc5280#section-4.1.2.7
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct SubjectPublicKeyInfo<'a> {
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SubjectPublicKeyInfo<Params, Key> {
/// X.509 [`AlgorithmIdentifier`] for the public key type
pub algorithm: AlgorithmIdentifierRef<'a>,
pub algorithm: AlgorithmIdentifier<Params>,

/// Public key data
pub subject_public_key: &'a [u8],
pub subject_public_key: Key,
}

impl<'a> SubjectPublicKeyInfo<'a> {
impl<'a, Params, Key> SubjectPublicKeyInfo<Params, Key>
where
Params: Choice<'a> + Encode,
// TODO: replace FixedTag with FixedTag<TAG = { Tag::BitString }> once
// https://github.com/rust-lang/rust/issues/92827 is fixed
Key: Decode<'a> + Encode + FixedTag,
{
/// Calculate the SHA-256 fingerprint of this [`SubjectPublicKeyInfo`] and
/// encode it as a Base64 string.
///
Expand Down Expand Up @@ -69,74 +83,93 @@ impl<'a> SubjectPublicKeyInfo<'a> {
self.encode(&mut builder)?;
Ok(builder.finish())
}

/// Get a [`BitString`] representing the `subject_public_key`
fn bitstring(&self) -> der::Result<BitStringRef<'a>> {
BitStringRef::from_bytes(self.subject_public_key)
}
}

impl<'a> DecodeValue<'a> for SubjectPublicKeyInfo<'a> {
impl<'a: 'k, 'k, Params, Key: 'k> DecodeValue<'a> for SubjectPublicKeyInfo<Params, Key>
where
Params: Choice<'a> + Encode,
Key: Decode<'a>,
{
fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> der::Result<Self> {
reader.read_nested(header.length, |reader| {
Ok(Self {
algorithm: reader.decode()?,
subject_public_key: BitStringRef::decode(reader)?
.as_bytes()
.ok_or_else(|| der::Tag::BitString.value_error())?,
subject_public_key: Key::decode(reader)?,
})
})
}
}

impl<'a> Sequence<'a> for SubjectPublicKeyInfo<'a> {
impl<'a, Params, Key> Sequence<'a> for SubjectPublicKeyInfo<Params, Key>
where
Params: Choice<'a> + Encode,
Key: Decode<'a> + Encode + FixedTag,
{
fn fields<F, T>(&self, f: F) -> der::Result<T>
where
F: FnOnce(&[&dyn Encode]) -> der::Result<T>,
{
f(&[&self.algorithm, &self.bitstring()?])
f(&[&self.algorithm, &self.subject_public_key])
}
}

impl<'a> TryFrom<&'a [u8]> for SubjectPublicKeyInfo<'a> {
impl<'a, Params, Key> TryFrom<&'a [u8]> for SubjectPublicKeyInfo<Params, Key>
where
Params: Choice<'a> + Encode,
Key: Decode<'a> + Encode + FixedTag,
{
type Error = Error;

fn try_from(bytes: &'a [u8]) -> Result<Self> {
Ok(Self::from_der(bytes)?)
}
}

impl ValueOrd for SubjectPublicKeyInfo<'_> {
impl<'a, Params, Key> ValueOrd for SubjectPublicKeyInfo<Params, Key>
where
Params: Choice<'a> + DerOrd + Encode,
Key: ValueOrd,
{
fn value_cmp(&self, other: &Self) -> der::Result<Ordering> {
match self.algorithm.der_cmp(&other.algorithm)? {
Ordering::Equal => self.bitstring()?.der_cmp(&other.bitstring()?),
Ordering::Equal => self.subject_public_key.value_cmp(&other.subject_public_key),
other => Ok(other),
}
}
}

#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
impl TryFrom<SubjectPublicKeyInfo<'_>> for Document {
impl<'a: 'k, 'k, Params, Key: 'k> TryFrom<SubjectPublicKeyInfo<Params, Key>> for Document
where
Params: Choice<'a> + Encode,
Key: Decode<'a> + Encode + FixedTag,
BitStringRef<'a>: From<&'k Key>,
{
type Error = Error;

fn try_from(spki: SubjectPublicKeyInfo<'_>) -> Result<Document> {
fn try_from(spki: SubjectPublicKeyInfo<Params, Key>) -> Result<Document> {
Self::try_from(&spki)
}
}

#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
impl TryFrom<&SubjectPublicKeyInfo<'_>> for Document {
impl<'a: 'k, 'k, Params, Key: 'k> TryFrom<&SubjectPublicKeyInfo<Params, Key>> for Document
where
Params: Choice<'a> + Encode,
Key: Decode<'a> + Encode + FixedTag,
BitStringRef<'a>: From<&'k Key>,
{
type Error = Error;

fn try_from(spki: &SubjectPublicKeyInfo<'_>) -> Result<Document> {
fn try_from(spki: &SubjectPublicKeyInfo<Params, Key>) -> Result<Document> {
Ok(Self::encode_msg(spki)?)
}
}

#[cfg(feature = "pem")]
#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
impl PemLabel for SubjectPublicKeyInfo<'_> {
impl<Params, Key> PemLabel for SubjectPublicKeyInfo<Params, Key> {
const PEM_LABEL: &'static str = "PUBLIC KEY";
}
17 changes: 10 additions & 7 deletions spki/src/traits.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Traits for encoding/decoding SPKI public keys.

use crate::{Error, Result, SubjectPublicKeyInfo};
use crate::{Error, Result, SubjectPublicKeyInfoRef};

#[cfg(feature = "alloc")]
use der::Document;
Expand All @@ -14,6 +14,9 @@ use {
#[cfg(feature = "std")]
use std::path::Path;

#[cfg(doc)]
use crate::SubjectPublicKeyInfo;

/// Parse a public key object from an encoded SPKI document.
pub trait DecodePublicKey: Sized {
/// Deserialize object from ASN.1 DER-encoded [`SubjectPublicKeyInfo`]
Expand All @@ -31,7 +34,7 @@ pub trait DecodePublicKey: Sized {
#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
fn from_public_key_pem(s: &str) -> Result<Self> {
let (label, doc) = Document::from_pem(s)?;
SubjectPublicKeyInfo::validate_pem_label(label)?;
SubjectPublicKeyInfoRef::validate_pem_label(label)?;
Self::from_public_key_der(doc.as_bytes())
}

Expand All @@ -49,17 +52,17 @@ pub trait DecodePublicKey: Sized {
#[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "std"))))]
fn read_public_key_pem_file(path: impl AsRef<Path>) -> Result<Self> {
let (label, doc) = Document::read_pem_file(path)?;
SubjectPublicKeyInfo::validate_pem_label(&label)?;
SubjectPublicKeyInfoRef::validate_pem_label(&label)?;
Self::from_public_key_der(doc.as_bytes())
}
}

impl<T> DecodePublicKey for T
where
T: for<'a> TryFrom<SubjectPublicKeyInfo<'a>, Error = Error>,
T: for<'a> TryFrom<SubjectPublicKeyInfoRef<'a>, Error = Error>,
{
fn from_public_key_der(bytes: &[u8]) -> Result<Self> {
Self::try_from(SubjectPublicKeyInfo::try_from(bytes)?)
Self::try_from(SubjectPublicKeyInfoRef::try_from(bytes)?)
}
}

Expand All @@ -75,7 +78,7 @@ pub trait EncodePublicKey {
#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
fn to_public_key_pem(&self, line_ending: LineEnding) -> Result<String> {
let doc = self.to_public_key_der()?;
Ok(doc.to_pem(SubjectPublicKeyInfo::PEM_LABEL, line_ending)?)
Ok(doc.to_pem(SubjectPublicKeyInfoRef::PEM_LABEL, line_ending)?)
}

/// Write ASN.1 DER-encoded public key to the given path
Expand All @@ -94,6 +97,6 @@ pub trait EncodePublicKey {
line_ending: LineEnding,
) -> Result<()> {
let doc = self.to_public_key_der()?;
Ok(doc.write_pem_file(path, SubjectPublicKeyInfo::PEM_LABEL, line_ending)?)
Ok(doc.write_pem_file(path, SubjectPublicKeyInfoRef::PEM_LABEL, line_ending)?)
}
}
Loading

0 comments on commit 01dd38c

Please sign in to comment.