Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion spki/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ base64 = ["dep:base64ct"]
fingerprint = ["digest", "sha2"]
pem = ["alloc", "der/pem"]

[lints]
workspace = true

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
2 changes: 1 addition & 1 deletion spki/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
[![Project Chat][chat-image]][chat-link]

[X.509] Subject Public Key Info types describing public keys as well as their
associated AlgorithmIdentifiers (i.e. OIDs).
associated `AlgorithmIdentifier`s (i.e. OIDs).

Specified in [RFC 5280 § 4.1].

Expand Down
17 changes: 15 additions & 2 deletions spki/src/algorithm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ pub type AlgorithmIdentifierOwned = AlgorithmIdentifier<Any>;

impl<Params> AlgorithmIdentifier<Params> {
/// Assert the `algorithm` OID is an expected value.
///
/// # Errors
/// Returns [`Error::OidUnknown`] if `self` does not match `expected_oid`.
pub fn assert_algorithm_oid(&self, expected_oid: ObjectIdentifier) -> Result<ObjectIdentifier> {
if self.oid == expected_oid {
Ok(expected_oid)
Expand All @@ -111,6 +114,9 @@ impl<Params> AlgorithmIdentifier<Params> {

impl<'a> AlgorithmIdentifierRef<'a> {
/// Assert `parameters` is an OID and has the expected value.
///
/// # Errors
/// Returns [`Error::OidUnknown`] if `self.parameters_oid()` doesn't match `expected_oid`.
pub fn assert_parameters_oid(
&self,
expected_oid: ObjectIdentifier,
Expand All @@ -125,6 +131,9 @@ impl<'a> AlgorithmIdentifierRef<'a> {
}

/// Assert the values of the `algorithm` and `parameters` OIDs.
///
/// # Errors
/// Returns [`Error::OidUnknown`] if `algorithm` and/or `parameters` aren't the expected values.
pub fn assert_oids(
&self,
algorithm: ObjectIdentifier,
Expand All @@ -137,14 +146,17 @@ impl<'a> AlgorithmIdentifierRef<'a> {

/// Get the `parameters` field as an [`AnyRef`].
///
/// Returns an error if `parameters` are `None`.
/// # Errors
/// Returns [`Error::AlgorithmParametersMissing`] error if `self.parameters` are `None`.
pub fn parameters_any(&self) -> Result<AnyRef<'a>> {
self.parameters.ok_or(Error::AlgorithmParametersMissing)
}

/// Get the `parameters` field as an [`ObjectIdentifier`].
///
/// Returns an error if it is absent or not an OID.
/// # Errors
/// - Returns [`Error::AlgorithmParametersMissing`] error if `self.parameters` are `None`.
/// - Returns [`Error::Asn1`] if `self.parameters` is not an OID.
pub fn parameters_oid(&self) -> Result<ObjectIdentifier> {
Ok(ObjectIdentifier::try_from(self.parameters_any()?)?)
}
Expand All @@ -155,6 +167,7 @@ impl<'a> AlgorithmIdentifierRef<'a> {
/// particular that `NULL` parameters are treated the same as missing
/// parameters.
///
/// # Errors
/// Returns an error if parameters are present but not an OID.
pub fn oids(&self) -> der::Result<(ObjectIdentifier, Option<ObjectIdentifier>)> {
Ok((
Expand Down
1 change: 1 addition & 0 deletions spki/src/digest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use der::{Result, Writer};
use digest::Digest;

/// Adapter object to write to a digest backend
#[derive(Debug)]
pub struct DigestWriter<'d, D>(pub &'d mut D);

impl<D> Writer for DigestWriter<'_, D>
Expand Down
2 changes: 1 addition & 1 deletion spki/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub enum Error {
}

impl core::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Error::Asn1(err) => Some(err),
_ => None,
Expand Down
9 changes: 1 addition & 8 deletions spki/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,7 @@
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
)]
#![forbid(unsafe_code)]
#![warn(
clippy::mod_module_files,
clippy::unwrap_used,
missing_docs,
rust_2018_idioms,
unused_lifetimes,
unused_qualifications
)]

//! # Usage
//! The following example demonstrates how to use an OID as the `parameters`
//! of an [`AlgorithmIdentifier`].
Expand Down
14 changes: 10 additions & 4 deletions spki/src/spki.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ where
///
/// See [RFC7469 § 2.1.1] for more information.
///
/// # Errors
/// Propagates errors that occur during encoding.
///
/// [RFC7469 § 2.1.1]: https://datatracker.ietf.org/doc/html/rfc7469#section-2.1.1
#[cfg(all(feature = "fingerprint", feature = "alloc", feature = "base64"))]
pub fn fingerprint_base64(&self) -> Result<alloc::string::String> {
Expand All @@ -81,6 +84,9 @@ where
///
/// See [RFC7469 § 2.1.1] for more information.
///
/// # Errors
/// Propagates errors that occur during encoding.
///
/// [RFC7469 § 2.1.1]: https://datatracker.ietf.org/doc/html/rfc7469#section-2.1.1
#[cfg(feature = "fingerprint")]
pub fn fingerprint_bytes(&self) -> Result<FingerprintBytes> {
Expand Down Expand Up @@ -215,10 +221,10 @@ mod allocating {
impl SubjectPublicKeyInfoOwned {
/// Create a [`SubjectPublicKeyInfoOwned`] from any object that implements
/// [`EncodePublicKey`].
pub fn from_key<T>(source: &T) -> Result<Self>
where
T: EncodePublicKey,
{
///
/// # Errors
/// Propagates encoding and decoding errors.
pub fn from_key<T: EncodePublicKey>(source: &T) -> Result<Self> {
Ok(source.to_public_key_der()?.decode_msg::<Self>()?)
}
}
Expand Down
49 changes: 41 additions & 8 deletions spki/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ use crate::SubjectPublicKeyInfo;
pub trait DecodePublicKey: Sized {
/// Deserialize object from ASN.1 DER-encoded [`SubjectPublicKeyInfo`]
/// (binary format).
///
/// # Errors
/// Returns decoding errors specific to the concrete type which impls this trait.
fn from_public_key_der(bytes: &[u8]) -> Result<Self>;

/// Deserialize PEM-encoded [`SubjectPublicKeyInfo`].
Expand All @@ -34,6 +37,9 @@ pub trait DecodePublicKey: Sized {
/// ```text
/// -----BEGIN PUBLIC KEY-----
/// ```
///
/// # Errors
/// Returns decoding errors specific to the concrete type which impls this trait.
#[cfg(feature = "pem")]
fn from_public_key_pem(s: &str) -> Result<Self> {
let (label, doc) = Document::from_pem(s)?;
Expand All @@ -43,13 +49,19 @@ pub trait DecodePublicKey: Sized {

/// Load public key object from an ASN.1 DER-encoded file on the local
/// filesystem (binary format).
///
/// # Errors
/// Returns decoding errors specific to the concrete type which impls this trait.
#[cfg(feature = "std")]
fn read_public_key_der_file(path: impl AsRef<Path>) -> Result<Self> {
let doc = Document::read_der_file(path)?;
Self::from_public_key_der(doc.as_bytes())
}

/// Load public key object from a PEM-encoded file on the local filesystem.
///
/// # Errors
/// Returns decoding errors specific to the concrete type which impls this trait.
#[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)?;
Expand All @@ -71,22 +83,34 @@ where
#[cfg(feature = "alloc")]
pub trait EncodePublicKey {
/// Serialize a [`Document`] containing a SPKI-encoded public key.
///
/// # Errors
/// Returns encoding errors specific to the concrete type which impls this trait.
fn to_public_key_der(&self) -> Result<Document>;

/// Serialize this public key as PEM-encoded SPKI with the given [`LineEnding`].
///
/// # Errors
/// Returns encoding errors specific to the concrete type which impls this trait.
#[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(SubjectPublicKeyInfoRef::PEM_LABEL, line_ending)?)
}

/// Write ASN.1 DER-encoded public key to the given path
/// Write ASN.1 DER-encoded public key to the given path.
///
/// # Errors
/// Returns encoding errors specific to the concrete type which impls this trait.
#[cfg(feature = "std")]
fn write_public_key_der_file(&self, path: impl AsRef<Path>) -> Result<()> {
Ok(self.to_public_key_der()?.write_der_file(path)?)
}

/// Write ASN.1 PEM-encoded public key to the given path
/// Write ASN.1 PEM-encoded public key to the given path.
///
/// # Errors
/// Returns encoding errors specific to the concrete type which impls this trait.
#[cfg(all(feature = "pem", feature = "std"))]
fn write_public_key_pem_file(
&self,
Expand Down Expand Up @@ -115,6 +139,9 @@ pub trait AssociatedAlgorithmIdentifier {
#[cfg(feature = "alloc")]
pub trait DynAssociatedAlgorithmIdentifier {
/// `AlgorithmIdentifier` for this structure.
///
/// # Errors
/// Returns errors specific to the concrete type which impls this trait.
fn algorithm_identifier(&self) -> Result<AlgorithmIdentifierOwned>;
}

Expand All @@ -135,9 +162,9 @@ where
}
}

/// Returns `AlgorithmIdentifier` associated with the signature system.
/// Returns [`AlgorithmIdentifier`] associated with the signature system.
///
/// Unlike AssociatedAlgorithmIdentifier this is intended to be implemented for public and/or
/// Unlike [`AssociatedAlgorithmIdentifier`] this is intended to be implemented for public and/or
/// private keys.
pub trait SignatureAlgorithmIdentifier {
/// Algorithm parameters.
Expand All @@ -147,13 +174,16 @@ pub trait SignatureAlgorithmIdentifier {
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params>;
}

/// Returns `AlgorithmIdentifier` associated with the signature system.
/// Returns [`AlgorithmIdentifierOwned`] associated with the signature system.
///
/// Unlike AssociatedAlgorithmIdentifier this is intended to be implemented for public and/or
/// Unlike [`AssociatedAlgorithmIdentifier`] this is intended to be implemented for public and/or
/// private keys.
#[cfg(feature = "alloc")]
pub trait DynSignatureAlgorithmIdentifier {
/// `AlgorithmIdentifier` for the corresponding signature system.
///
/// # Errors
/// Returns errors specific to the concrete type which impls this trait.
fn signature_algorithm_identifier(&self) -> Result<AlgorithmIdentifierOwned>;
}

Expand All @@ -174,11 +204,14 @@ where
}
}

/// Returns the `BitString` encoding of the signature.
/// Returns the [`BitString`] encoding of the signature.
///
/// X.509 and CSR structures require signatures to be BitString encoded.
/// X.509 and CSR structures require signatures to be `BitString` encoded.
#[cfg(feature = "alloc")]
pub trait SignatureBitStringEncoding {
/// `BitString` encoding for this signature.
///
/// # Errors
/// Returns errors specific to the concrete type which impls this trait.
fn to_bitstring(&self) -> der::Result<BitString>;
}
1 change: 1 addition & 0 deletions spki/tests/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const ED25519_DER_EXAMPLE: &[u8] = include_bytes!("examples/ed25519-pub.der");
const ED25519_PEM_EXAMPLE: &str = include_str!("examples/ed25519-pub.pem");

/// Mock key type for testing trait impls against.
#[derive(Debug)]
pub struct MockKey(Vec<u8>);

impl AsRef<[u8]> for MockKey {
Expand Down