Skip to content

Commit 1e4b92e

Browse files
committed
rework AsExtension trait?
1 parent 132a7ab commit 1e4b92e

File tree

9 files changed

+112
-65
lines changed

9 files changed

+112
-65
lines changed

x509-cert/src/builder.rs

+47-61
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! X509 Certificate builder
22
3-
use alloc::vec::Vec;
3+
use alloc::vec;
44
use der::asn1::{BitString, OctetString};
55
use sha1::{Digest, Sha1};
66
use spki::{AlgorithmIdentifierOwned, SubjectPublicKeyInfoOwned};
@@ -81,12 +81,13 @@ impl Profile {
8181
&self,
8282
spk: &SubjectPublicKeyInfoOwned,
8383
issuer_spk: &SubjectPublicKeyInfoOwned,
84-
) -> Result<Vec<Extension>> {
85-
let mut extensions: Vec<Extension> = Vec::new();
84+
tbs: &TbsCertificate,
85+
) -> Result<vec::Vec<Extension>> {
86+
let mut extensions: vec::Vec<Extension> = vec::Vec::new();
8687

8788
extensions.push({
8889
let result = Sha1::digest(spk.subject_public_key.raw_bytes());
89-
SubjectKeyIdentifier(OctetString::new(result.to_vec())?).to_extension()?
90+
SubjectKeyIdentifier(OctetString::new(result.to_vec())?).to_extension(tbs)?
9091
});
9192

9293
// Build Authority Key Identifier
@@ -103,7 +104,7 @@ impl Profile {
103104
authority_cert_issuer: None,
104105
authority_cert_serial_number: None,
105106
}
106-
.to_extension()?,
107+
.to_extension(tbs)?,
107108
);
108109
}
109110
}
@@ -114,20 +115,20 @@ impl Profile {
114115
ca: true,
115116
path_len_constraint: None,
116117
}
117-
.to_extension()?,
118+
.to_extension(tbs)?,
118119
Profile::SubCA {
119120
path_len_constraint,
120121
..
121122
} => BasicConstraints {
122123
ca: true,
123124
path_len_constraint: *path_len_constraint,
124125
}
125-
.to_extension()?,
126+
.to_extension(tbs)?,
126127
Profile::Leaf { .. } => BasicConstraints {
127128
ca: false,
128129
path_len_constraint: None,
129130
}
130-
.to_extension()?,
131+
.to_extension(tbs)?,
131132
});
132133

133134
// Build Key Usage extension
@@ -137,7 +138,7 @@ impl Profile {
137138
KeyUsage(
138139
KeyUsages::DigitalSignature | KeyUsages::KeyCertSign | KeyUsages::CRLSign,
139140
)
140-
.to_extension()?,
141+
.to_extension(tbs)?,
141142
);
142143
}
143144
Profile::Leaf { .. } => {
@@ -230,18 +231,7 @@ impl From<CertificateVersion> for Version {
230231
/// .expect("Create certificate");
231232
/// ```
232233
pub struct CertificateBuilder<'s, S: Signer> {
233-
version: Version,
234-
serial_number: SerialNumber,
235-
signature_alg: AlgorithmIdentifierOwned,
236-
issuer: Name,
237-
validity: Validity,
238-
subject: Name,
239-
subject_public_key_info: SubjectPublicKeyInfoOwned,
240-
issuer_unique_id: Option<BitString>,
241-
subject_unique_id: Option<BitString>,
242-
243-
extensions: Vec<Extension>,
244-
234+
tbs: TbsCertificate,
245235
signer: &'s S,
246236
}
247237

@@ -264,63 +254,59 @@ impl<'s, S: Signer> CertificateBuilder<'s, S> {
264254
validity.not_before.rfc5280_adjust_utc_time()?;
265255
validity.not_after.rfc5280_adjust_utc_time()?;
266256

267-
let (version, (issuer_unique_id, subject_unique_id), extensions) = match version {
268-
CertificateVersion::V1 => (Version::V1, (None, None), Vec::new()),
269-
CertificateVersion::V2(uids) => (Version::V2, uids.get_unique_ids(), Vec::new()),
270-
CertificateVersion::V3(uids) => (
271-
Version::V3,
272-
uids.get_unique_ids(),
273-
profile.build_extensions(&subject_public_key_info, &signer_pub)?,
274-
),
257+
let (version, (issuer_unique_id, subject_unique_id)) = match version {
258+
CertificateVersion::V1 => (Version::V1, (None, None)),
259+
CertificateVersion::V2(uids) => (Version::V2, uids.get_unique_ids()),
260+
CertificateVersion::V3(uids) => (Version::V3, uids.get_unique_ids()),
275261
};
276262

277-
Ok(Self {
263+
let mut tbs = TbsCertificate {
278264
version,
279-
issuer_unique_id,
280-
subject_unique_id,
281265
serial_number,
266+
signature: signature_alg,
282267
issuer,
283-
subject,
284268
validity,
269+
subject,
285270
subject_public_key_info,
286-
signature_alg,
287-
extensions,
288-
signer,
289-
})
271+
issuer_unique_id,
272+
subject_unique_id,
273+
extensions: None,
274+
};
275+
276+
if tbs.version == Version::V3 {
277+
let extensions =
278+
profile.build_extensions(&tbs.subject_public_key_info, &signer_pub, &tbs)?;
279+
if !extensions.is_empty() {
280+
tbs.extensions = Some(extensions);
281+
}
282+
}
283+
284+
Ok(Self { tbs, signer })
290285
}
291286

292287
/// Add an extension to this certificate
293288
pub fn add_extension<E: AsExtension>(&mut self, extension: &E) -> Result<()> {
294-
self.extensions.push(extension.to_extension()?);
289+
if self.tbs.version == Version::V3 {
290+
let ext = extension.to_extension(&self.tbs)?;
291+
292+
if let Some(extensions) = self.tbs.extensions.as_mut() {
293+
extensions.push(ext);
294+
} else {
295+
let extensions = vec![ext];
296+
self.tbs.extensions = Some(extensions);
297+
}
298+
}
299+
295300
Ok(())
296301
}
297302

298303
/// Run the certificate through the signer and build the end certificate.
299304
pub fn build(&mut self) -> Result<Certificate> {
300-
let extensions = if self.version == Version::V3 {
301-
Some(self.extensions.clone())
302-
} else {
303-
None
304-
};
305-
306-
let tbs_certificate = TbsCertificate {
307-
version: self.version,
308-
serial_number: self.serial_number.clone(),
309-
signature: self.signature_alg.clone(),
310-
issuer: self.issuer.clone(),
311-
validity: self.validity,
312-
subject: self.subject.clone(),
313-
subject_public_key_info: self.subject_public_key_info.clone(),
314-
issuer_unique_id: self.issuer_unique_id.clone(),
315-
subject_unique_id: self.subject_unique_id.clone(),
316-
extensions,
317-
};
318-
319-
let signature = BitString::from_bytes(&self.signer.sign(&tbs_certificate))?;
305+
let signature = BitString::from_bytes(&self.signer.sign(&self.tbs))?;
320306

321307
let cert = Certificate {
322-
tbs_certificate,
323-
signature_algorithm: self.signature_alg.clone(),
308+
tbs_certificate: self.tbs.clone(),
309+
signature_algorithm: self.tbs.signature.clone(),
324310
signature,
325311
};
326312

@@ -346,5 +332,5 @@ pub trait Signer {
346332
fn public_key(&self) -> SubjectPublicKeyInfoOwned;
347333

348334
/// The sign method should return the signature of the payload.
349-
fn sign(&self, input: &TbsCertificate) -> Vec<u8>;
335+
fn sign(&self, input: &TbsCertificate) -> vec::Vec<u8>;
350336
}

x509-cert/src/ext.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Standardized X.509 Certificate Extensions
22
3+
use crate::certificate;
34
use const_oid::AssociatedOid;
45
use der::{asn1::OctetString, Sequence, ValueOrd};
56
use spki::ObjectIdentifier;
@@ -48,15 +49,15 @@ pub type Extensions = alloc::vec::Vec<Extension>;
4849
/// builder.
4950
pub trait AsExtension: AssociatedOid + der::Encode {
5051
/// Should the extension be marked critical
51-
const CRITICAL: bool;
52+
fn critical(&self, tbs: &certificate::TbsCertificate) -> bool;
5253

5354
/// Returns the Extension with the content encoded.
54-
fn to_extension(&self) -> Result<Extension, der::Error> {
55+
fn to_extension(&self, tbs: &certificate::TbsCertificate) -> Result<Extension, der::Error> {
5556
let content = OctetString::new(<Self as der::Encode>::to_vec(self)?)?;
5657

5758
Ok(Extension {
5859
extn_id: <Self as AssociatedOid>::OID,
59-
critical: Self::CRITICAL,
60+
critical: self.critical(tbs),
6061
extn_value: content,
6162
})
6263
}

x509-cert/src/ext/pkix.rs

+17
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,23 @@ impl AssociatedOid for SubjectAltName {
6666

6767
impl_newtype!(SubjectAltName, name::GeneralNames);
6868

69+
impl crate::ext::AsExtension for SubjectAltName {
70+
fn critical(&self, tbs: &crate::certificate::TbsCertificate) -> bool {
71+
// https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.6
72+
// Further, if the only subject identity included in the certificate is
73+
// an alternative name form (e.g., an electronic mail address), then the
74+
// subject distinguished name MUST be empty (an empty sequence), and the
75+
// subjectAltName extension MUST be present. If the subject field
76+
// contains an empty sequence, then the issuing CA MUST include a
77+
// subjectAltName extension that is marked as critical. When including
78+
// the subjectAltName extension in a certificate that has a non-empty
79+
// subject distinguished name, conforming CAs SHOULD mark the
80+
// subjectAltName extension as non-critical.
81+
82+
tbs.subject.is_empty()
83+
}
84+
}
85+
6986
/// IssuerAltName as defined in [RFC 5280 Section 4.2.1.7].
7087
///
7188
/// ```text

x509-cert/src/ext/pkix/certpolicy.rs

+6
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ impl AssociatedOid for CertificatePolicies {
2222
}
2323

2424
impl_newtype!(CertificatePolicies, Vec<PolicyInformation>);
25+
// TODO
26+
// If this extension is
27+
// critical, the path validation software MUST be able to interpret this
28+
// extension (including the optional qualifier), or MUST reject the
29+
// certificate.
30+
impl_extension!(CertificatePolicies);
2531

2632
/// PolicyInformation as defined in [RFC 5280 Section 4.2.1.4].
2733
///

x509-cert/src/ext/pkix/constraints/basic.rs

+14
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,17 @@ pub struct BasicConstraints {
2222
impl AssociatedOid for BasicConstraints {
2323
const OID: ObjectIdentifier = ID_CE_BASIC_CONSTRAINTS;
2424
}
25+
26+
// TODO
27+
// Conforming CAs MUST include this extension in all CA certificates
28+
// that contain public keys used to validate digital signatures on
29+
// certificates and MUST mark the extension as critical in such
30+
// certificates. This extension MAY appear as a critical or non-
31+
// critical extension in CA certificates that contain public keys used
32+
// exclusively for purposes other than validating digital signatures on
33+
// certificates. Such CA certificates include ones that contain public
34+
// keys used exclusively for validating digital signatures on CRLs and
35+
// ones that contain key management public keys used with certificate
36+
// enrollment protocols. This extension MAY appear as a critical or
37+
// non-critical extension in end entity certificates.
38+
impl_extension!(BasicConstraints, critical = true);

x509-cert/src/ext/pkix/constraints/name.rs

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ impl AssociatedOid for NameConstraints {
3131
const OID: ObjectIdentifier = ID_CE_NAME_CONSTRAINTS;
3232
}
3333

34+
impl_extension!(NameConstraints, critical = true);
35+
3436
/// GeneralSubtrees as defined in [RFC 5280 Section 4.2.1.10].
3537
///
3638
/// ```text

x509-cert/src/ext/pkix/keyusage.rs

+14
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,20 @@ impl AssociatedOid for ExtendedKeyUsage {
7878
}
7979

8080
impl_newtype!(ExtendedKeyUsage, Vec<ObjectIdentifier>);
81+
// TODO
82+
// This extension MAY, at the option of the certificate issuer, be
83+
// either critical or non-critical.
84+
//
85+
// If a CA includes extended key usages to satisfy such applications,
86+
// but does not wish to restrict usages of the key, the CA can include
87+
// the special KeyPurposeId anyExtendedKeyUsage in addition to the
88+
// particular key purposes required by the applications. Conforming CAs
89+
// SHOULD NOT mark this extension as critical if the anyExtendedKeyUsage
90+
// KeyPurposeId is present. Applications that require the presence of a
91+
// particular purpose MAY reject certificates that include the
92+
// anyExtendedKeyUsage OID but not the particular OID expected for the
93+
// application.
94+
impl_extension!(ExtendedKeyUsage, critical = true);
8195

8296
/// PrivateKeyUsagePeriod as defined in [RFC 3280 Section 4.2.1.4].
8397
///

x509-cert/src/macros.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ macro_rules! impl_extension {
8686
};
8787
($newtype:ty, critical = $critical:expr) => {
8888
impl crate::ext::AsExtension for $newtype {
89-
const CRITICAL: bool = $critical;
89+
fn critical(&self, _tbs: &crate::certificate::TbsCertificate) -> bool {
90+
$critical
91+
}
9092
}
9193
};
9294
}

x509-cert/src/name.rs

+5
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ impl RdnSequence {
4343

4444
RdnSequence(out).to_vec()
4545
}
46+
47+
/// Is this [`RdnSequence`] empty?
48+
pub fn is_empty(&self) -> bool {
49+
self.0.is_empty()
50+
}
4651
}
4752

4853
/// Serializes the structure according to the rules in [RFC 4514].

0 commit comments

Comments
 (0)