Skip to content
Closed
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
2 changes: 1 addition & 1 deletion rcgen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ x509-parser = { workspace = true, features = ["verify"], optional = true }
zeroize = { version = "1.2", optional = true }

[features]
default = ["crypto", "pem", "ring"]
default = ["crypto", "pem", "ring", "x509-parser"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll want to revert this.

crypto = []
aws_lc_rs = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/aws-lc-sys"]
ring = ["crypto", "dep:ring"]
Expand Down
7 changes: 4 additions & 3 deletions rcgen/src/certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,9 @@ impl CertificateParams {
///
/// The returned [`Certificate`] may be serialized using [`Certificate::der`] and
/// [`Certificate::pem`].
pub fn signed_by(
pub fn signed_by<K: PublicKeyData>(
self,
key_pair: &KeyPair,
key_pair: &K,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I'd prefer to spell this as &impl PublicKeyData.

issuer: &Certificate,
issuer_key: &KeyPair,
) -> Result<Certificate, Error> {
Expand All @@ -160,7 +160,8 @@ impl CertificateParams {
key_pair: issuer_key,
};

let subject_public_key_info = key_pair.public_key_der();
let subject_public_key_info =
yasna::construct_der(|writer| key_pair.serialize_public_key_der(writer));
let der = self.serialize_der_with_signer(key_pair, issuer)?;
Ok(Certificate {
params: self,
Expand Down
5 changes: 5 additions & 0 deletions rcgen/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ pub enum Error {
#[cfg(not(feature = "crypto"))]
/// Missing serial number
MissingSerialNumber,
/// X509 parsing error
#[cfg(feature = "x509-parser")]
X509,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we might want to include a String representation of the nested error here.

}

impl fmt::Display for Error {
Expand Down Expand Up @@ -91,6 +94,8 @@ impl fmt::Display for Error {
)?,
#[cfg(not(feature = "crypto"))]
MissingSerialNumber => write!(f, "A serial number must be specified")?,
#[cfg(feature = "x509-parser")]
X509 => write!(f, "X509 error")?,
};
Ok(())
}
Expand Down
86 changes: 85 additions & 1 deletion rcgen/src/key_pair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,90 @@ impl fmt::Debug for KeyPairKind {
}
}

/// A public key
#[cfg(feature = "x509-parser")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need to guard the type directly.

#[derive(Debug)]
pub struct SubjectPublicKey {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bike-shedding: I think this would be better called SubjectPublicKeyInfo. It's the pairing of an algorithm identifier and the public key.

pub(crate) alg: &'static SignatureAlgorithm,
pub(crate) subject_public_key: Vec<u8>,
}

#[cfg(feature = "x509-parser")]
impl SubjectPublicKey {
/// Create a `SubjectPublicKey` value from a PEM-encoded SubjectPublicKeyInfo string
pub fn from_pem(pem_str: &str) -> Result<Self, Error> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs the feature guards from from_der() and also another one for pem.

let spki_der = pem::parse(pem_str)._err()?.into_contents();
Self::from_der(&spki_der)
}

/// Create a `SubjectPublicKey` value from DER-encoded SubjectPublicKeyInfo bytes
pub fn from_der(spki_der: &[u8]) -> Result<Self, Error> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should guard this constructor.

use x509_parser::{
prelude::FromDer,
x509::{AlgorithmIdentifier, SubjectPublicKeyInfo},
};

let (rem, spki) = SubjectPublicKeyInfo::from_der(spki_der).map_err(|_| Error::X509)?;
if !rem.is_empty() {
return Err(Error::X509);
}
let alg = SignatureAlgorithm::iter()
.find(|alg| {
let bytes = yasna::construct_der(|writer| {
alg.write_oids_sign_alg(writer);
});
let Ok((rest, aid)) = AlgorithmIdentifier::from_der(&bytes) else {
return false;
};
if !rest.is_empty() {
return false;
}
aid == spki.algorithm
})
.ok_or(Error::UnsupportedSignatureAlgorithm)?;
Ok(Self {
alg,
subject_public_key: Vec::from(spki.subject_public_key.as_ref()),
})
}
}

#[cfg(feature = "x509-parser")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't need the guard.

impl PublicKeyData for SubjectPublicKey {
fn alg(&self) -> &SignatureAlgorithm {
self.alg
}
fn raw_bytes(&self) -> &[u8] {
Comment on lines +112 to +113
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: whitespace between fns

&self.subject_public_key
}
}

#[cfg(all(test, feature = "x509-parser", feature = "pem"))]
mod pkd_test {
use super::{KeyPair, PublicKeyData, SubjectPublicKey};
use crate::{PKCS_ECDSA_P256_SHA256, PKCS_ECDSA_P384_SHA384, PKCS_ED25519};

#[test]
fn test_subject_public_key_parsing() {
// NOTE: the other algorithms supported by this crate don't support keygen
for alg in [
&PKCS_ED25519,
&PKCS_ECDSA_P256_SHA256,
&PKCS_ECDSA_P384_SHA384,
] {
let kp = KeyPair::generate_for(alg).expect("keygen");
let pem = kp.public_key_pem();
let der = kp.public_key_der();

let pkd_pem = SubjectPublicKey::from_pem(&pem).expect("from pem");
assert_eq!(kp.raw_bytes(), pkd_pem.raw_bytes());

let pkd_der = SubjectPublicKey::from_der(&der).expect("from der");
assert_eq!(kp.raw_bytes(), pkd_der.raw_bytes());
}
}
}

/// A key pair used to sign certificates and CSRs
///
/// Note that ring, the underlying library to handle RSA keys
Expand Down Expand Up @@ -689,7 +773,7 @@ impl<T> ExternalError<T> for Result<T, pem::PemError> {
}
}

pub(crate) trait PublicKeyData {
pub trait PublicKeyData {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs some tweaks, as discussed here, please put those in a separate commit.

#282 (comment)

Also needs documentation.

fn alg(&self) -> &SignatureAlgorithm;

fn raw_bytes(&self) -> &[u8];
Expand Down
2 changes: 1 addition & 1 deletion rcgen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ pub use error::{Error, InvalidAsn1String};
use key_pair::PublicKeyData;
#[cfg(all(feature = "crypto", feature = "aws_lc_rs"))]
pub use key_pair::RsaKeySize;
pub use key_pair::{KeyPair, RemoteKeyPair};
pub use key_pair::{KeyPair, RemoteKeyPair, SubjectPublicKey};
#[cfg(feature = "crypto")]
use ring_like::digest;
pub use sign_algo::algo::*;
Expand Down
Loading