Skip to content

Commit 8c29e12

Browse files
authored
x509-cert: builder updates (#1001)
- Per RFC5280, DigitalSignature 'is asserted when the subject public key is used for verifying digital signatures, other than signatures on certificates (bit 5) and CRLs (bit 6)'. Using CA keys to sign random data would definitely be a bad practice and should be avoided. Thus remove the DigitalSignature keyUsage from these certificates. - RSA PSS implements DynSignatureAlgorithmIdentifier only for the SigningKey, not for the verifying key. To allow using CertificateBuilder with RSA PSS keys require DynSignatureAlgorithmIdentifier implementation on S rather than on S::VerifyingKey. - Signer (unlike SignerMut) is not expected to be mutable. Don't require mutability of the Signer argument. - ECDSA keys can not be used for keyEncipherment. Make this keyUsage bit optional. - Follow the rules from RFC 5280 Section 4.1.2.1 to set the certificate's version depending on the presence of the extensions and identifiers. - Remove unused conversion when building RDN fields. Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
1 parent 498e20e commit 8c29e12

4 files changed

Lines changed: 55 additions & 73 deletions

File tree

x509-cert/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ spki = { version = "0.7.1", features = ["alloc"] }
2222
# optional dependencies
2323
arbitrary = { version = "1.3", features = ["derive"], optional = true }
2424
sha1 = { version = "0.10.0", optional = true }
25-
signature = { version = "2.1.0", features = ["digest"], optional = true }
25+
signature = { version = "2.1.0", optional = true }
2626

2727
[dev-dependencies]
2828
hex-literal = "0.4"

x509-cert/src/builder.rs

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ pub enum Error {
3636
Signature(signature::Error),
3737
}
3838

39+
#[cfg(feature = "std")]
40+
impl std::error::Error for Error {}
41+
3942
impl fmt::Display for Error {
4043
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4144
match self {
@@ -87,6 +90,8 @@ pub enum Profile {
8790
issuer: Name,
8891
/// should the key agreement flag of KeyUsage be enabled
8992
enable_key_agreement: bool,
93+
/// should the key encipherment flag of KeyUsage be enabled
94+
enable_key_encipherment: bool,
9095
},
9196
#[cfg(feature = "hazmat")]
9297
/// Opt-out of the default extensions
@@ -161,20 +166,18 @@ impl Profile {
161166
// Build Key Usage extension
162167
match self {
163168
Profile::Root | Profile::SubCA { .. } => {
164-
extensions.push(
165-
KeyUsage(
166-
KeyUsages::DigitalSignature | KeyUsages::KeyCertSign | KeyUsages::CRLSign,
167-
)
168-
.to_extension(tbs)?,
169-
);
169+
extensions
170+
.push(KeyUsage(KeyUsages::KeyCertSign | KeyUsages::CRLSign).to_extension(tbs)?);
170171
}
171172
Profile::Leaf {
172173
enable_key_agreement,
174+
enable_key_encipherment,
173175
..
174176
} => {
175-
let mut key_usage = KeyUsages::DigitalSignature
176-
| KeyUsages::NonRepudiation
177-
| KeyUsages::KeyEncipherment;
177+
let mut key_usage = KeyUsages::DigitalSignature | KeyUsages::NonRepudiation;
178+
if *enable_key_encipherment {
179+
key_usage |= KeyUsages::KeyEncipherment;
180+
}
178181
if *enable_key_agreement {
179182
key_usage |= KeyUsages::KeyAgreement;
180183
}
@@ -194,7 +197,6 @@ impl Profile {
194197
/// ```
195198
/// use der::Decode;
196199
/// use x509_cert::spki::SubjectPublicKeyInfoOwned;
197-
/// use x509_cert::certificate::Version;
198200
/// use x509_cert::builder::{CertificateBuilder, Profile};
199201
/// use x509_cert::name::Name;
200202
/// use x509_cert::serial_number::SerialNumber;
@@ -223,35 +225,32 @@ impl Profile {
223225
/// let mut signer = rsa_signer();
224226
/// let mut builder = CertificateBuilder::new(
225227
/// profile,
226-
/// Version::V3,
227228
/// serial_number,
228229
/// validity,
229230
/// subject,
230231
/// pub_key,
231-
/// &mut signer,
232+
/// &signer,
232233
/// )
233234
/// .expect("Create certificate");
234235
/// ```
235236
pub struct CertificateBuilder<'s, S> {
236237
tbs: TbsCertificate,
237-
signer: &'s mut S,
238+
signer: &'s S,
238239
}
239240

240241
impl<'s, S> CertificateBuilder<'s, S>
241242
where
242-
S: Keypair,
243+
S: Keypair + DynSignatureAlgorithmIdentifier,
243244
S::VerifyingKey: EncodePublicKey,
244-
S::VerifyingKey: DynSignatureAlgorithmIdentifier,
245245
{
246246
/// Creates a new certificate builder
247247
pub fn new<Signature>(
248248
profile: Profile,
249-
version: Version,
250249
serial_number: SerialNumber,
251250
mut validity: Validity,
252251
subject: Name,
253252
subject_public_key_info: SubjectPublicKeyInfoOwned,
254-
signer: &'s mut S,
253+
signer: &'s S,
255254
) -> Result<Self>
256255
where
257256
S: Signer<Signature>,
@@ -261,14 +260,14 @@ where
261260
.to_public_key_der()?
262261
.decode_msg::<SubjectPublicKeyInfoOwned>()?;
263262

264-
let signature_alg = verifying_key.signature_algorithm_identifier()?;
263+
let signature_alg = signer.signature_algorithm_identifier()?;
265264
let issuer = profile.get_issuer(&subject);
266265

267266
validity.not_before.rfc5280_adjust_utc_time()?;
268267
validity.not_after.rfc5280_adjust_utc_time()?;
269268

270269
let mut tbs = TbsCertificate {
271-
version,
270+
version: Version::V3,
272271
serial_number,
273272
signature: signature_alg,
274273
issuer,
@@ -286,15 +285,13 @@ where
286285
subject_unique_id: None,
287286
};
288287

289-
if tbs.version == Version::V3 {
290-
let extensions = profile.build_extensions(
291-
tbs.subject_public_key_info.owned_to_ref(),
292-
signer_pub.owned_to_ref(),
293-
&tbs,
294-
)?;
295-
if !extensions.is_empty() {
296-
tbs.extensions = Some(extensions);
297-
}
288+
let extensions = profile.build_extensions(
289+
tbs.subject_public_key_info.owned_to_ref(),
290+
signer_pub.owned_to_ref(),
291+
&tbs,
292+
)?;
293+
if !extensions.is_empty() {
294+
tbs.extensions = Some(extensions);
298295
}
299296

300297
Ok(Self { tbs, signer })
@@ -317,17 +314,24 @@ where
317314
}
318315

319316
/// Run the certificate through the signer and build the end certificate.
320-
pub fn build<Signature>(&mut self) -> Result<Certificate>
317+
pub fn build<Signature>(mut self) -> Result<Certificate>
321318
where
322319
S: Signer<Signature>,
323320
Signature: SignatureEncoding,
324321
{
322+
if self.tbs.extensions.is_none() {
323+
if self.tbs.issuer_unique_id.is_some() || self.tbs.subject_unique_id.is_some() {
324+
self.tbs.version = Version::V2;
325+
} else {
326+
self.tbs.version = Version::V1;
327+
}
328+
}
325329
let signature = self.signer.try_sign(&self.tbs.to_der()?)?;
326330
let signature = BitString::from_bytes(signature.to_bytes().as_ref())?;
327331

328332
let cert = Certificate {
329333
tbs_certificate: self.tbs.clone(),
330-
signature_algorithm: self.tbs.signature.clone(),
334+
signature_algorithm: self.tbs.signature,
331335
signature,
332336
};
333337

x509-cert/test-support/src/zlint.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::{
1111
};
1212
use tempfile::tempdir;
1313

14-
#[derive(Debug, Copy, Clone, PartialEq)]
14+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1515
pub enum Status {
1616
NotApplicable,
1717
NotEffective,
@@ -29,7 +29,7 @@ impl Status {
2929
}
3030
}
3131

32-
#[derive(Debug, Clone, PartialEq)]
32+
#[derive(Debug, Clone, PartialEq, Eq)]
3333
pub struct LintStatus {
3434
pub status: Status,
3535
pub details: Option<String>,

x509-cert/tests/builder.rs

Lines changed: 18 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use spki::SubjectPublicKeyInfoOwned;
99
use std::{str::FromStr, time::Duration};
1010
use x509_cert::{
1111
builder::{CertificateBuilder, Profile},
12-
certificate::Version,
1312
name::Name,
1413
serial_number::SerialNumber,
1514
time::Validity,
@@ -31,17 +30,10 @@ fn root_ca_certificate() {
3130
let pub_key =
3231
SubjectPublicKeyInfoOwned::try_from(RSA_2048_DER_EXAMPLE).expect("get rsa pub key");
3332

34-
let mut signer = rsa_signer();
35-
let mut builder = CertificateBuilder::new(
36-
profile,
37-
Version::V3,
38-
serial_number,
39-
validity,
40-
subject,
41-
pub_key,
42-
&mut signer,
43-
)
44-
.expect("Create certificate");
33+
let signer = rsa_signer();
34+
let builder =
35+
CertificateBuilder::new(profile, serial_number, validity, subject, pub_key, &signer)
36+
.expect("Create certificate");
4537

4638
let certificate = builder.build().unwrap();
4739

@@ -57,33 +49,26 @@ fn sub_ca_certificate() {
5749
let serial_number = SerialNumber::from(42u32);
5850
let validity = Validity::from_now(Duration::new(5, 0)).unwrap();
5951

60-
let issuer = Name::from_str("CN=World domination corporation,O=World domination Inc,C=US")
61-
.unwrap()
62-
.to_der()
63-
.unwrap();
64-
let issuer = Name::from_der(&issuer).unwrap();
52+
let issuer =
53+
Name::from_str("CN=World domination corporation,O=World domination Inc,C=US").unwrap();
6554
let profile = Profile::SubCA {
6655
issuer,
6756
path_len_constraint: Some(0),
6857
};
6958

70-
let subject = Name::from_str("CN=World domination task force,O=World domination Inc,C=US")
71-
.unwrap()
72-
.to_der()
73-
.unwrap();
74-
let subject = Name::from_der(&subject).unwrap();
59+
let subject =
60+
Name::from_str("CN=World domination task force,O=World domination Inc,C=US").unwrap();
7561
let pub_key =
7662
SubjectPublicKeyInfoOwned::try_from(RSA_2048_DER_EXAMPLE).expect("get rsa pub key");
7763

78-
let mut signer = ecdsa_signer();
79-
let mut builder = CertificateBuilder::new::<ecdsa::Signature<NistP256>>(
64+
let signer = ecdsa_signer();
65+
let builder = CertificateBuilder::new::<ecdsa::Signature<NistP256>>(
8066
profile,
81-
Version::V3,
8267
serial_number,
8368
validity,
8469
subject,
8570
pub_key,
86-
&mut signer,
71+
&signer,
8772
)
8873
.expect("Create certificate");
8974

@@ -108,33 +93,26 @@ fn leaf_certificate() {
10893
let serial_number = SerialNumber::from(42u32);
10994
let validity = Validity::from_now(Duration::new(5, 0)).unwrap();
11095

111-
let issuer = Name::from_str("CN=World domination corporation,O=World domination Inc,C=US")
112-
.unwrap()
113-
.to_der()
114-
.unwrap();
115-
let issuer = Name::from_der(&issuer).unwrap();
96+
let issuer =
97+
Name::from_str("CN=World domination corporation,O=World domination Inc,C=US").unwrap();
11698
let profile = Profile::Leaf {
11799
issuer,
118100
enable_key_agreement: false,
101+
enable_key_encipherment: false,
119102
};
120103

121-
let subject = Name::from_str("CN=service.domination.world")
122-
.unwrap()
123-
.to_der()
124-
.unwrap();
125-
let subject = Name::from_der(&subject).unwrap();
104+
let subject = Name::from_str("CN=service.domination.world").unwrap();
126105
let pub_key =
127106
SubjectPublicKeyInfoOwned::try_from(RSA_2048_DER_EXAMPLE).expect("get rsa pub key");
128107

129-
let mut signer = ecdsa_signer();
130-
let mut builder = CertificateBuilder::new::<ecdsa::Signature<NistP256>>(
108+
let signer = ecdsa_signer();
109+
let builder = CertificateBuilder::new::<ecdsa::Signature<NistP256>>(
131110
profile,
132-
Version::V3,
133111
serial_number,
134112
validity,
135113
subject,
136114
pub_key,
137-
&mut signer,
115+
&signer,
138116
)
139117
.expect("Create certificate");
140118

0 commit comments

Comments
 (0)