Skip to content

Commit ad8b0d5

Browse files
committed
x509-cert: rework the profile of builder
This is now providing a trait to be implemented by the consumer. A number of implementation are available, including ones trying to abide by CABF Baseline Requirements. Fixes #1281
1 parent 72eec30 commit ad8b0d5

File tree

9 files changed

+952
-236
lines changed

9 files changed

+952
-236
lines changed

x509-cert/src/builder.rs

Lines changed: 21 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,22 @@ use der::{asn1::BitString, referenced::OwnedToRef, Encode};
77
use signature::{rand_core::CryptoRngCore, Keypair, RandomizedSigner, Signer};
88
use spki::{
99
AlgorithmIdentifier, DynSignatureAlgorithmIdentifier, EncodePublicKey, ObjectIdentifier,
10-
SignatureBitStringEncoding, SubjectPublicKeyInfoOwned, SubjectPublicKeyInfoRef,
10+
SignatureBitStringEncoding, SubjectPublicKeyInfoOwned,
1111
};
1212

1313
use crate::{
1414
certificate::{Certificate, TbsCertificate, Version},
15-
ext::{
16-
pkix::{
17-
AuthorityKeyIdentifier, BasicConstraints, KeyUsage, KeyUsages, SubjectKeyIdentifier,
18-
},
19-
AsExtension, Extension, Extensions,
20-
},
15+
ext::{AsExtension, Extensions},
2116
name::Name,
2217
request::{attributes::AsAttribute, CertReq, CertReqInfo, ExtensionReq},
2318
serial_number::SerialNumber,
2419
time::Validity,
2520
};
2621

22+
pub mod profile;
23+
24+
use self::profile::Profile;
25+
2726
const NULL_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("0.0.0");
2827

2928
/// Error type
@@ -74,156 +73,12 @@ impl From<signature::Error> for Error {
7473
/// Result type
7574
pub type Result<T> = core::result::Result<T, Error>;
7675

77-
/// The type of certificate to build
78-
#[derive(Clone, Debug, Eq, PartialEq)]
79-
pub enum Profile {
80-
/// Build a root CA certificate
81-
Root,
82-
/// Build an intermediate sub CA certificate
83-
SubCA {
84-
/// issuer Name,
85-
/// represents the name signing the certificate
86-
issuer: Name,
87-
/// pathLenConstraint INTEGER (0..MAX) OPTIONAL
88-
/// BasicConstraints as defined in [RFC 5280 Section 4.2.1.9].
89-
path_len_constraint: Option<u8>,
90-
},
91-
/// Build an end certificate
92-
Leaf {
93-
/// issuer Name,
94-
/// represents the name signing the certificate
95-
issuer: Name,
96-
/// should the key agreement flag of KeyUsage be enabled
97-
enable_key_agreement: bool,
98-
/// should the key encipherment flag of KeyUsage be enabled
99-
enable_key_encipherment: bool,
100-
/// should the subject key identifier extension be included
101-
///
102-
/// From [RFC 5280 Section 4.2.1.2]:
103-
/// For end entity certificates, subject key identifiers SHOULD be
104-
/// derived from the public key. Two common methods for generating key
105-
/// identifiers from the public key are identified above.
106-
#[cfg(feature = "hazmat")]
107-
include_subject_key_identifier: bool,
108-
},
109-
#[cfg(feature = "hazmat")]
110-
/// Opt-out of the default extensions
111-
Manual {
112-
/// issuer Name,
113-
/// represents the name signing the certificate
114-
/// A `None` will make it a self-signed certificate
115-
issuer: Option<Name>,
116-
},
117-
}
118-
119-
impl Profile {
120-
fn get_issuer(&self, subject: &Name) -> Name {
121-
match self {
122-
Profile::Root => subject.clone(),
123-
Profile::SubCA { issuer, .. } => issuer.clone(),
124-
Profile::Leaf { issuer, .. } => issuer.clone(),
125-
#[cfg(feature = "hazmat")]
126-
Profile::Manual { issuer, .. } => issuer.as_ref().unwrap_or(subject).clone(),
127-
}
128-
}
129-
130-
fn build_extensions(
131-
&self,
132-
spk: SubjectPublicKeyInfoRef<'_>,
133-
issuer_spk: SubjectPublicKeyInfoRef<'_>,
134-
tbs: &TbsCertificate,
135-
) -> Result<vec::Vec<Extension>> {
136-
#[cfg(feature = "hazmat")]
137-
// User opted out of default extensions set.
138-
if let Profile::Manual { .. } = self {
139-
return Ok(vec::Vec::default());
140-
}
141-
142-
let mut extensions: vec::Vec<Extension> = vec::Vec::new();
143-
144-
match self {
145-
#[cfg(feature = "hazmat")]
146-
Profile::Leaf {
147-
include_subject_key_identifier: false,
148-
..
149-
} => {}
150-
_ => extensions.push(
151-
SubjectKeyIdentifier::try_from(spk)?.to_extension(&tbs.subject, &extensions)?,
152-
),
153-
}
154-
155-
// Build Authority Key Identifier
156-
match self {
157-
Profile::Root => {}
158-
_ => {
159-
extensions.push(
160-
AuthorityKeyIdentifier::try_from(issuer_spk.clone())?
161-
.to_extension(&tbs.subject, &extensions)?,
162-
);
163-
}
164-
}
165-
166-
// Build Basic Contraints extensions
167-
extensions.push(match self {
168-
Profile::Root => BasicConstraints {
169-
ca: true,
170-
path_len_constraint: None,
171-
}
172-
.to_extension(&tbs.subject, &extensions)?,
173-
Profile::SubCA {
174-
path_len_constraint,
175-
..
176-
} => BasicConstraints {
177-
ca: true,
178-
path_len_constraint: *path_len_constraint,
179-
}
180-
.to_extension(&tbs.subject, &extensions)?,
181-
Profile::Leaf { .. } => BasicConstraints {
182-
ca: false,
183-
path_len_constraint: None,
184-
}
185-
.to_extension(&tbs.subject, &extensions)?,
186-
#[cfg(feature = "hazmat")]
187-
Profile::Manual { .. } => unreachable!(),
188-
});
189-
190-
// Build Key Usage extension
191-
match self {
192-
Profile::Root | Profile::SubCA { .. } => {
193-
extensions.push(
194-
KeyUsage(KeyUsages::KeyCertSign | KeyUsages::CRLSign)
195-
.to_extension(&tbs.subject, &extensions)?,
196-
);
197-
}
198-
Profile::Leaf {
199-
enable_key_agreement,
200-
enable_key_encipherment,
201-
..
202-
} => {
203-
let mut key_usage = KeyUsages::DigitalSignature | KeyUsages::NonRepudiation;
204-
if *enable_key_encipherment {
205-
key_usage |= KeyUsages::KeyEncipherment;
206-
}
207-
if *enable_key_agreement {
208-
key_usage |= KeyUsages::KeyAgreement;
209-
}
210-
211-
extensions.push(KeyUsage(key_usage).to_extension(&tbs.subject, &extensions)?);
212-
}
213-
#[cfg(feature = "hazmat")]
214-
Profile::Manual { .. } => unreachable!(),
215-
}
216-
217-
Ok(extensions)
218-
}
219-
}
220-
22176
/// X509 Certificate builder
22277
///
22378
/// ```
22479
/// use der::Decode;
22580
/// use x509_cert::spki::SubjectPublicKeyInfoOwned;
226-
/// use x509_cert::builder::{CertificateBuilder, Profile, Builder};
81+
/// use x509_cert::builder::{CertificateBuilder, Builder, profile};
22782
/// use x509_cert::name::Name;
22883
/// use x509_cert::serial_number::SerialNumber;
22984
/// use x509_cert::time::Validity;
@@ -237,14 +92,14 @@ impl Profile {
23792
/// # use der::referenced::RefToOwned;
23893
/// # fn rsa_signer() -> SigningKey<Sha256> {
23994
/// # let private_key = rsa::RsaPrivateKey::from_pkcs1_der(RSA_2048_PRIV_DER).unwrap();
240-
/// # let signing_key = SigningKey::<Sha256>::new_with_prefix(private_key);
95+
/// # let signing_key = SigningKey::<Sha256>::new(private_key);
24196
/// # signing_key
24297
/// # }
24398
///
24499
/// let serial_number = SerialNumber::from(42u32);
245100
/// let validity = Validity::from_now(Duration::new(5, 0)).unwrap();
246-
/// let profile = Profile::Root;
247101
/// let subject = Name::from_str("CN=World domination corporation,O=World domination Inc,C=US").unwrap();
102+
/// let profile = profile::cabf::Root::new(false,subject).expect("Create root profile");
248103
///
249104
/// let pub_key = SubjectPublicKeyInfoOwned::try_from(RSA_2048_DER).expect("get rsa pub key");
250105
///
@@ -253,33 +108,35 @@ impl Profile {
253108
/// profile,
254109
/// serial_number,
255110
/// validity,
256-
/// subject,
257111
/// pub_key,
258112
/// )
259113
/// .expect("Create certificate builder");
260114
///
261115
/// let cert = builder.build(&signer).expect("Create certificate");
262116
/// ```
263-
pub struct CertificateBuilder {
117+
pub struct CertificateBuilder<P> {
264118
tbs: TbsCertificate,
265119
extensions: Extensions,
266-
profile: Profile,
120+
profile: P,
267121
}
268122

269-
impl CertificateBuilder {
123+
impl<P> CertificateBuilder<P>
124+
where
125+
P: Profile,
126+
{
270127
/// Creates a new certificate builder
271128
pub fn new(
272-
profile: Profile,
129+
profile: P,
273130
serial_number: SerialNumber,
274131
mut validity: Validity,
275-
subject: Name,
276132
subject_public_key_info: SubjectPublicKeyInfoOwned,
277133
) -> Result<Self> {
278134
let signature_alg = AlgorithmIdentifier {
279135
oid: NULL_OID,
280136
parameters: None,
281137
};
282138

139+
let subject = profile.get_subject();
283140
let issuer = profile.get_issuer(&subject);
284141

285142
validity.not_before.rfc5280_adjust_utc_time()?;
@@ -455,7 +312,10 @@ pub trait Builder: Sized {
455312
}
456313
}
457314

458-
impl Builder for CertificateBuilder {
315+
impl<P> Builder for CertificateBuilder<P>
316+
where
317+
P: Profile,
318+
{
459319
type Output = Certificate;
460320

461321
fn finalize<S>(&mut self, cert_signer: &S) -> Result<vec::Vec<u8>>

x509-cert/src/builder/profile.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//! Certificate profiles
2+
//!
3+
//! Profiles need implement by the [`Profile`] trait.
4+
//! They may then be consumed by a [`builder::CertificateBuilder`].
5+
//!
6+
//!
7+
//! Multiple profiles are provided and you may select one depending on your use-case:
8+
//! - [`cabf`] implements the Baseline Requirement from the CA Browser Forum as close as it can be
9+
//! done.
10+
//! - [`piv`] implements the specification for the Personal Identity Verification cards.
11+
//! - [`devid`] implements the specification for IEEE 802.1 AR. Certificates for Secure
12+
//! Device Identity.
13+
//!
14+
//! Please follow each sub-module documentation and select a profile that may suit your needs, or
15+
//! you may implement your own profile, if need be.
16+
17+
#[cfg(doc)]
18+
use crate::builder;
19+
20+
use crate::{builder::Result, certificate::TbsCertificate, ext::Extension, name::Name};
21+
use alloc::vec;
22+
use spki::SubjectPublicKeyInfoRef;
23+
24+
pub mod cabf;
25+
pub mod devid;
26+
pub mod piv;
27+
28+
/// Profile for certificates
29+
pub trait Profile {
30+
/// Issuer to be used for issued certificates
31+
fn get_issuer(&self, subject: &Name) -> Name;
32+
33+
/// Subject for the certificate to be used.
34+
fn get_subject(&self) -> Name;
35+
36+
/// X509v3 extensions to be added in the certificates.
37+
fn build_extensions(
38+
&self,
39+
spk: SubjectPublicKeyInfoRef<'_>,
40+
issuer_spk: SubjectPublicKeyInfoRef<'_>,
41+
tbs: &TbsCertificate,
42+
) -> Result<vec::Vec<Extension>>;
43+
}

0 commit comments

Comments
 (0)