Skip to content

Commit b5dc812

Browse files
committed
rework CA builder profiles
1 parent fc19e66 commit b5dc812

File tree

4 files changed

+581
-178
lines changed

4 files changed

+581
-178
lines changed

x509-cert/src/builder.rs

Lines changed: 11 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! X509 Certificate builder
22
33
use alloc::vec;
4-
use core::fmt;
4+
use core::{fmt, marker::PhantomData};
55
use der::{asn1::BitString, referenced::OwnedToRef, Encode};
66
use signature::{rand_core::CryptoRngCore, Keypair, RandomizedSigner, Signer};
77
use spki::{
@@ -23,6 +23,10 @@ use crate::{
2323
time::Validity,
2424
};
2525

26+
pub mod profile;
27+
28+
use self::profile::Profile;
29+
2630
/// Error type
2731
#[derive(Debug)]
2832
#[non_exhaustive]
@@ -70,180 +74,6 @@ impl From<signature::Error> for Error {
7074

7175
type Result<T> = core::result::Result<T, Error>;
7276

73-
/// The type of certificate to build
74-
#[derive(Clone, Debug, Eq, PartialEq)]
75-
pub enum Profile {
76-
/// Build a root CA certificate
77-
Root,
78-
/// Build an intermediate sub CA certificate
79-
SubCA {
80-
/// issuer Name,
81-
/// represents the name signing the certificate
82-
issuer: Name,
83-
/// pathLenConstraint INTEGER (0..MAX) OPTIONAL
84-
/// BasicConstraints as defined in [RFC 5280 Section 4.2.1.9].
85-
path_len_constraint: Option<u8>,
86-
},
87-
/// Build an end certificate
88-
Leaf {
89-
/// issuer Name,
90-
/// represents the name signing the certificate
91-
issuer: Name,
92-
/// Usage of the leaf certificate
93-
usage: Usage,
94-
/// should the subject key identifier extension be included
95-
///
96-
/// From [RFC 5280 Section 4.2.1.2]:
97-
/// For end entity certificates, subject key identifiers SHOULD be
98-
/// derived from the public key. Two common methods for generating key
99-
/// identifiers from the public key are identified above.
100-
#[cfg(feature = "hazmat")]
101-
include_subject_key_identifier: bool,
102-
},
103-
#[cfg(feature = "hazmat")]
104-
/// Opt-out of the default extensions
105-
Manual {
106-
/// issuer Name,
107-
/// represents the name signing the certificate
108-
/// A `None` will make it a self-signed certificate
109-
issuer: Option<Name>,
110-
},
111-
}
112-
113-
impl Profile {
114-
fn get_issuer(&self, subject: &Name) -> Name {
115-
match self {
116-
Profile::Root => subject.clone(),
117-
Profile::SubCA { issuer, .. } => issuer.clone(),
118-
Profile::Leaf { issuer, .. } => issuer.clone(),
119-
#[cfg(feature = "hazmat")]
120-
Profile::Manual { issuer, .. } => issuer.as_ref().unwrap_or(subject).clone(),
121-
}
122-
}
123-
124-
fn build_extensions(
125-
&self,
126-
spk: SubjectPublicKeyInfoRef<'_>,
127-
issuer_spk: SubjectPublicKeyInfoRef<'_>,
128-
tbs: &TbsCertificate,
129-
) -> Result<vec::Vec<Extension>> {
130-
#[cfg(feature = "hazmat")]
131-
// User opted out of default extensions set.
132-
if let Profile::Manual { .. } = self {
133-
return Ok(vec::Vec::default());
134-
}
135-
136-
let mut extensions: vec::Vec<Extension> = vec::Vec::new();
137-
138-
match self {
139-
#[cfg(feature = "hazmat")]
140-
Profile::Leaf {
141-
include_subject_key_identifier: false,
142-
..
143-
} => {}
144-
_ => extensions.push(
145-
SubjectKeyIdentifier::try_from(spk)?.to_extension(&tbs.subject, &extensions)?,
146-
),
147-
}
148-
149-
// Build Authority Key Identifier
150-
match self {
151-
Profile::Root => {}
152-
_ => {
153-
extensions.push(
154-
AuthorityKeyIdentifier::try_from(issuer_spk.clone())?
155-
.to_extension(&tbs.subject, &extensions)?,
156-
);
157-
}
158-
}
159-
160-
// Build Basic Contraints extensions
161-
extensions.push(match self {
162-
Profile::Root => BasicConstraints {
163-
ca: true,
164-
path_len_constraint: None,
165-
}
166-
.to_extension(&tbs.subject, &extensions)?,
167-
Profile::SubCA {
168-
path_len_constraint,
169-
..
170-
} => BasicConstraints {
171-
ca: true,
172-
path_len_constraint: *path_len_constraint,
173-
}
174-
.to_extension(&tbs.subject, &extensions)?,
175-
Profile::Leaf { .. } => BasicConstraints {
176-
ca: false,
177-
path_len_constraint: None,
178-
}
179-
.to_extension(&tbs.subject, &extensions)?,
180-
#[cfg(feature = "hazmat")]
181-
Profile::Manual { .. } => unreachable!(),
182-
});
183-
184-
// Build Key Usage extension
185-
match self {
186-
Profile::Root | Profile::SubCA { .. } => {
187-
extensions.push(
188-
KeyUsage(KeyUsages::KeyCertSign | KeyUsages::CRLSign)
189-
.to_extension(&tbs.subject, &extensions)?,
190-
);
191-
}
192-
Profile::Leaf { usage, .. } => {
193-
let key_usage = usage.key_usage().into();
194-
195-
extensions.push(KeyUsage(key_usage).to_extension(&tbs.subject, &extensions)?);
196-
}
197-
#[cfg(feature = "hazmat")]
198-
Profile::Manual { .. } => unreachable!(),
199-
}
200-
201-
Ok(extensions)
202-
}
203-
}
204-
205-
/// [`Usage`] describes the usage of a Leaf certificate.
206-
///
207-
/// This is designed in accordance with [ETSI EN 319 412-2 § 4.3.2 Key usage].
208-
///
209-
/// The various fields will refer to [RFC 5280 § 4.2.1.3 Key Usage] definitions.
210-
///
211-
/// [RFC 5280 § 4.2.1.3 Key Usage]: https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.3
212-
/// [ETSI EN 319 412-2 § 4.3.2 Key usage]: https://www.etsi.org/deliver/etsi_en/319400_319499/31941202/02.03.01_60/en_31941202v020301p.pdf#page=11
213-
#[derive(Clone, Debug, PartialEq, Eq)]
214-
pub enum Usage {
215-
/// [`Usage::NonRepudiation`] will set the NonRepudiation (also known as
216-
/// contentCommitment) bit of KeyUsage.
217-
NonRepudiation,
218-
/// [`Usage::DigitalSignature`] will set the digitalSignature bit.
219-
///
220-
/// This is meant to be used in an entity authentication service, a data
221-
/// origin authentication service, and/or an integrity service.
222-
DigitalSignature,
223-
/// [`Usage::KeyAgreement`] will set the `keyAgreement` bit.
224-
///
225-
/// This is meant to be used on Certificates when a Diffie-Hellman key is
226-
/// to be used for key management.
227-
KeyAgreement,
228-
/// [`Usage::KeyEncipherment`] will set the `keyEncipherment` bit.
229-
///
230-
/// This is meant to be used on Certificates when an RSA public
231-
/// key is to be used for encrypting a symmetric content-decryption
232-
/// key or an asymmetric private key.
233-
KeyEncipherment,
234-
}
235-
236-
impl Usage {
237-
fn key_usage(&self) -> KeyUsages {
238-
match self {
239-
Self::NonRepudiation => KeyUsages::NonRepudiation,
240-
Self::DigitalSignature => KeyUsages::DigitalSignature,
241-
Self::KeyAgreement => KeyUsages::KeyAgreement,
242-
Self::KeyEncipherment => KeyUsages::KeyEncipherment,
243-
}
244-
}
245-
}
246-
24777
/// X509 Certificate builder
24878
///
24979
/// ```
@@ -297,14 +127,17 @@ where
297127
S::VerifyingKey: EncodePublicKey,
298128
{
299129
/// Creates a new certificate builder
300-
pub fn new(
301-
profile: Profile,
130+
pub fn new<P>(
131+
profile: P,
302132
serial_number: SerialNumber,
303133
mut validity: Validity,
304134
subject: Name,
305135
subject_public_key_info: SubjectPublicKeyInfoOwned,
306136
cert_signer: &'s S,
307-
) -> Result<Self> {
137+
) -> Result<Self>
138+
where
139+
P: Profile,
140+
{
308141
let verifying_key = cert_signer.verifying_key();
309142
let signer_pub = SubjectPublicKeyInfoOwned::from_key(verifying_key)?;
310143

0 commit comments

Comments
 (0)