Skip to content

Commit cd1bf94

Browse files
committed
get extensions built
1 parent 051b0df commit cd1bf94

File tree

2 files changed

+138
-22
lines changed

2 files changed

+138
-22
lines changed

x509-cert/src/builder.rs

+68-11
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ use crate::{
77
asn1::{BitString, BitStringRef, UIntRef},
88
Decode, Encode, SliceReader,
99
},
10+
ext::{pkix::BasicConstraints, Extension},
1011
name::Name,
1112
spki::{AlgorithmIdentifier, SubjectPublicKeyInfo},
1213
time::Validity,
1314
};
15+
use const_oid::AssociatedOid;
1416

1517
pub type Result<T> = core::result::Result<T, der::Error>;
1618

@@ -34,10 +36,53 @@ impl UniqueIds {
3436
}
3537
}
3638

39+
#[derive(Clone, Debug, PartialEq)]
40+
pub enum Profile<'i> {
41+
Root,
42+
SubCA { issuer: Name<'i> },
43+
Leaf { issuer: Name<'i> },
44+
}
45+
46+
impl<'i> Profile<'i> {
47+
fn get_issuer<'s: 'i>(&'i self, subject: &'s Name<'_>) -> &'i Name<'_> {
48+
match self {
49+
Profile::Root => subject,
50+
Profile::SubCA { issuer } => issuer,
51+
Profile::Leaf { issuer } => issuer,
52+
}
53+
}
54+
55+
fn build_extensions(&self) -> Result<Vec<Vec<u8>>> {
56+
let mut extensions = Vec::new();
57+
58+
match self {
59+
Profile::Root => {
60+
let ca_true = BasicConstraints {
61+
ca: true,
62+
path_len_constraint: None,
63+
}
64+
.to_vec()?;
65+
66+
extensions.push(
67+
Extension {
68+
extn_id: BasicConstraints::OID,
69+
critical: true,
70+
extn_value: &ca_true,
71+
}
72+
.to_vec()?,
73+
);
74+
}
75+
_ => {}
76+
};
77+
78+
Ok(extensions)
79+
}
80+
}
81+
3782
pub enum CertificateVersion {
3883
V1,
3984
V2(UniqueIds),
40-
V3(UniqueIds),
85+
V3 { uids: UniqueIds },
4186
}
4287

4388
impl Into<Version> for CertificateVersion {
@@ -46,7 +91,7 @@ impl Into<Version> for CertificateVersion {
4691
match self {
4792
V1 => Version::V1,
4893
V2(_) => Version::V2,
49-
V3(_) => Version::V3,
94+
V3 { .. } => Version::V3,
5095
}
5196
}
5297
}
@@ -73,28 +118,29 @@ pub struct CertificateBuilder<SA: CertSignatureAlgorithm> {
73118

74119
impl<SA: CertSignatureAlgorithm> CertificateBuilder<SA> {
75120
pub fn new(
121+
profile: Profile<'_>,
76122
version: CertificateVersion,
77123
serial_number: UIntRef<'_>,
78124
signature_alg: SA,
79125
validity: Validity,
80-
issuer: Name<'_>,
81126
subject: Name<'_>,
82127
subject_public_key_info: SubjectPublicKeyInfo<'_>,
83128
) -> Result<Self> {
84129
let signature_alg = signature_alg.signature_algorithm().to_vec()?;
85-
let issuer = issuer.to_vec()?;
130+
let issuer = profile.get_issuer(&subject).to_vec()?;
86131
let subject = subject.to_vec()?;
87132
let serial_number = serial_number.to_vec()?;
88133
let subject_public_key_info = subject_public_key_info.to_vec()?;
89134

90-
let (version, (issuer_unique_id, subject_unique_id)) = match version {
91-
CertificateVersion::V1 => (Version::V1, (None, None)),
92-
CertificateVersion::V2(uids) => (Version::V2, uids.to_vec()?),
93-
CertificateVersion::V3(uids) => (Version::V3, uids.to_vec()?),
135+
let (version, (issuer_unique_id, subject_unique_id), extensions) = match version {
136+
CertificateVersion::V1 => (Version::V1, (None, None), Vec::new()),
137+
CertificateVersion::V2(uids) => (Version::V2, uids.to_vec()?, Vec::new()),
138+
CertificateVersion::V3 { uids } => {
139+
(Version::V3, uids.to_vec()?, profile.build_extensions()?)
140+
}
94141
};
95142

96143
let signature = Default::default();
97-
let extensions = Default::default();
98144

99145
Ok(Self {
100146
version,
@@ -119,7 +165,7 @@ impl<SA: CertSignatureAlgorithm> CertificateBuilder<SA> {
119165
let signature_alg =
120166
AlgorithmIdentifier::decode(&mut SliceReader::new(&self.signature_alg)?)?;
121167
let issuer = Name::decode(&mut SliceReader::new(&self.issuer)?)?;
122-
let serial_number = UIntRef::new(&self.serial_number).unwrap();
168+
let serial_number = UIntRef::decode(&mut SliceReader::new(&self.serial_number)?)?;
123169
let subject = Name::decode(&mut SliceReader::new(&self.subject)?)?;
124170
let subject_public_key_info =
125171
SubjectPublicKeyInfo::decode(&mut SliceReader::new(&self.subject_public_key_info)?)?;
@@ -135,6 +181,17 @@ impl<SA: CertSignatureAlgorithm> CertificateBuilder<SA> {
135181
let issuer_unique_id = unique_id_mapper(&self.issuer_unique_id)?;
136182
let subject_unique_id = unique_id_mapper(&self.subject_unique_id)?;
137183

184+
let extensions = if self.version == Version::V3 {
185+
let mut extensions = Vec::new();
186+
for extension in &self.extensions {
187+
extensions.push(Extension::decode(&mut SliceReader::new(&extension)?)?);
188+
}
189+
190+
Some(extensions)
191+
} else {
192+
None
193+
};
194+
138195
let tbs_certificate = TbsCertificate {
139196
version: self.version,
140197
serial_number,
@@ -145,7 +202,7 @@ impl<SA: CertSignatureAlgorithm> CertificateBuilder<SA> {
145202
subject_public_key_info,
146203
issuer_unique_id,
147204
subject_unique_id,
148-
extensions: None,
205+
extensions,
149206
};
150207

151208
self.signature = signer.sign(&tbs_certificate);

x509-cert/tests/builder.rs

+70-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![cfg(all(feature = "builder", feature = "pem"))]
22

3-
use der::{asn1::UIntRef, pem::LineEnding, EncodePem};
3+
use der::{asn1::UIntRef, pem::LineEnding, Decode, EncodePem};
44
use spki::SubjectPublicKeyInfo;
55
use std::{
66
fs::File,
@@ -10,7 +10,7 @@ use std::{
1010
};
1111
use tempfile::tempdir;
1212
use x509_cert::{
13-
builder::{CertificateBuilder, CertificateVersion, Signer, UniqueIds},
13+
builder::{CertificateBuilder, CertificateVersion, Profile, Signer, UniqueIds},
1414
certificate::{Certificate, TbsCertificate},
1515
constants,
1616
name::Name,
@@ -72,6 +72,8 @@ fn check_certificate(cert: &Certificate) {
7272
.read_to_end(&mut output_buf)
7373
.expect("read zlint output");
7474

75+
//println!("{}", String::from_utf8(output_buf.clone()).unwrap());
76+
7577
let output: zlint::LintResult =
7678
serde_json::from_slice(&output_buf).expect("parse zlint output");
7779

@@ -91,16 +93,19 @@ fn basic_certificate() {
9193
let serial_number = 42u32.to_be_bytes();
9294
let serial_number = UIntRef::new(&serial_number[..]).expect("create serial");
9395
let validity = Validity::from_now(Duration::new(5, 0)).unwrap();
94-
let issuer = Name::default();
95-
let subject = Name::default();
96+
let profile = Profile::Root;
97+
let subject =
98+
Name::encode_from_string("CN=World domination task force,O=World domination Inc,C=US")
99+
.unwrap();
100+
let subject = Name::from_der(&subject).unwrap();
96101
let pub_key = SubjectPublicKeyInfo::try_from(RSA_2048_DER_EXAMPLE).expect("get rsa pub key");
97102

98103
let mut builder = CertificateBuilder::new(
99-
CertificateVersion::V3(uids),
104+
profile,
105+
CertificateVersion::V3 { uids },
100106
serial_number,
101107
constants::RsaWithSha256,
102108
validity,
103-
issuer,
104109
subject,
105110
pub_key,
106111
)
@@ -116,7 +121,11 @@ fn basic_certificate() {
116121
}
117122
}
118123

119-
check_certificate(&builder.build(MockSigner).unwrap());
124+
let certificate = builder.build(MockSigner).unwrap();
125+
126+
println!("{}", openssl::text_output(&certificate));
127+
128+
check_certificate(&certificate);
120129
}
121130

122131
#[test]
@@ -140,11 +149,14 @@ mod zlint {
140149

141150
#[derive(Debug, Copy, Clone, PartialEq)]
142151
pub enum Status {
143-
NA,
152+
NotApplicable,
153+
NotEffective,
144154
Pass,
155+
Notice,
145156
Info,
146157
Warn,
147158
Error,
159+
Fatal,
148160
}
149161

150162
impl Status {
@@ -176,8 +188,11 @@ mod zlint {
176188
while let Some((key, value)) = access.next_entry::<&str, &str>()? {
177189
if key == "result" {
178190
value_output = Some(match value {
179-
"NA" => Status::NA,
191+
"NA" => Status::NotApplicable,
192+
"NE" => Status::NotEffective,
180193
"pass" => Status::Pass,
194+
"notice" => Status::Notice,
195+
"fatal" => Status::Fatal,
181196
"error" => Status::Error,
182197
"warn" => Status::Warn,
183198
"info" => Status::Info,
@@ -208,10 +223,11 @@ mod zlint {
208223

209224
impl LintResult {
210225
pub fn check_lints(&self, ignored: &[&str]) -> bool {
211-
let mut failed = Vec::new();
226+
let mut failed = HashMap::<String, Status>::new();
227+
212228
for (key, value) in &self.0 {
213229
if !value.is_successful() && !ignored.contains(&key.as_str()) {
214-
failed.push(String::from(key));
230+
failed.insert(String::from(key), value.clone());
215231
}
216232
}
217233

@@ -221,3 +237,46 @@ mod zlint {
221237
}
222238
}
223239
}
240+
241+
mod openssl {
242+
use der::{pem::LineEnding, EncodePem};
243+
use std::{
244+
fs::File,
245+
io::{Read, Write},
246+
process::{Command, Stdio},
247+
};
248+
use tempfile::tempdir;
249+
use x509_cert::certificate::Certificate;
250+
251+
pub fn text_output(cert: &Certificate) -> String {
252+
let tmp_dir = tempdir().expect("create tempdir");
253+
let cert_path = tmp_dir.path().join("cert.pem");
254+
255+
let pem = cert.to_pem(LineEnding::LF).expect("generate pem");
256+
let mut cert_file = File::create(&cert_path).expect("create pem file");
257+
cert_file
258+
.write_all(pem.as_bytes())
259+
.expect("Create pem file");
260+
261+
let mut child = Command::new("openssl")
262+
.arg("x509")
263+
.arg("-in")
264+
.arg(&cert_path)
265+
.arg("-noout")
266+
.arg("-text")
267+
.stderr(Stdio::inherit())
268+
.stdout(Stdio::piped())
269+
.spawn()
270+
.expect("zlint failed");
271+
let mut stdout = child.stdout.take().unwrap();
272+
let exit_status = child.wait().expect("get openssl x509 status");
273+
274+
assert!(exit_status.success(), "openssl failed");
275+
let mut output_buf = Vec::new();
276+
stdout
277+
.read_to_end(&mut output_buf)
278+
.expect("read openssl output");
279+
280+
String::from_utf8(output_buf.clone()).unwrap()
281+
}
282+
}

0 commit comments

Comments
 (0)