Skip to content

Commit 2c89de5

Browse files
committed
x509-cert: wip: add certificate builder
1 parent 86fee0c commit 2c89de5

File tree

4 files changed

+188
-0
lines changed

4 files changed

+188
-0
lines changed

x509-cert/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ rstest = "0.12"
3030

3131
[features]
3232
alloc = ["der/alloc"]
33+
builder = ["std"]
3334
std = ["der/std", "spki/std"]
3435
pem = ["alloc", "der/pem"]
3536

x509-cert/src/builder.rs

+167
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
use std::println;
2+
use std::vec::Vec;
3+
4+
use crate::{
5+
certificate::{Certificate, TbsCertificate, Version},
6+
der::{
7+
asn1::{BitString, BitStringRef, UIntRef},
8+
Decode, Encode, SliceReader,
9+
},
10+
name::Name,
11+
spki::{AlgorithmIdentifier, SubjectPublicKeyInfo},
12+
time::Validity,
13+
};
14+
15+
pub type Result<T> = core::result::Result<T, der::Error>;
16+
17+
pub struct UniqueIds {
18+
pub issuer_unique_id: Option<BitString>,
19+
pub subject_unique_id: Option<BitString>,
20+
}
21+
22+
impl UniqueIds {
23+
fn to_vec(&self) -> Result<(Option<Vec<u8>>, Option<Vec<u8>>)> {
24+
Ok((
25+
self.issuer_unique_id
26+
.as_ref()
27+
.map(|bs| bs.to_vec())
28+
.transpose()?,
29+
self.subject_unique_id
30+
.as_ref()
31+
.map(|bs| bs.to_vec())
32+
.transpose()?,
33+
))
34+
}
35+
}
36+
37+
pub enum CertificateVersion {
38+
V1,
39+
V2(UniqueIds),
40+
V3(UniqueIds),
41+
}
42+
43+
impl Into<Version> for CertificateVersion {
44+
fn into(self) -> Version {
45+
use CertificateVersion::*;
46+
match self {
47+
V1 => Version::V1,
48+
V2(_) => Version::V2,
49+
V3(_) => Version::V3,
50+
}
51+
}
52+
}
53+
54+
pub struct CertificateBuilder<SA: CertSignatureAlgorithm> {
55+
version: Version,
56+
serial_number: Vec<u8>,
57+
signature_alg: Vec<u8>,
58+
issuer: Vec<u8>,
59+
validity: Validity,
60+
subject: Vec<u8>,
61+
subject_public_key_info: Vec<u8>,
62+
issuer_unique_id: Option<Vec<u8>>,
63+
subject_unique_id: Option<Vec<u8>>,
64+
65+
extensions: Vec<Vec<u8>>,
66+
67+
// Buffer used to store the signature once the certificate is built.
68+
// It needs to be co-located with the rest.
69+
signature: Vec<u8>,
70+
71+
signature_alg_marker: core::marker::PhantomData<SA>,
72+
}
73+
74+
impl<SA: CertSignatureAlgorithm> CertificateBuilder<SA> {
75+
pub fn new(
76+
version: CertificateVersion,
77+
serial_number: UIntRef<'_>,
78+
signature_alg: SA,
79+
validity: Validity,
80+
issuer: Name<'_>,
81+
subject: Name<'_>,
82+
subject_public_key_info: SubjectPublicKeyInfo<'_>,
83+
) -> Result<Self> {
84+
let signature_alg = signature_alg.signature_algorithm().to_vec()?;
85+
let issuer = issuer.to_vec()?;
86+
let subject = subject.to_vec()?;
87+
let serial_number = serial_number.to_vec()?;
88+
let subject_public_key_info = subject_public_key_info.to_vec()?;
89+
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()?),
94+
};
95+
96+
let signature = Default::default();
97+
let extensions = Default::default();
98+
99+
Ok(Self {
100+
version,
101+
issuer_unique_id,
102+
subject_unique_id,
103+
serial_number,
104+
issuer,
105+
subject,
106+
validity,
107+
subject_public_key_info,
108+
signature_alg,
109+
signature_alg_marker: core::marker::PhantomData,
110+
signature,
111+
extensions,
112+
})
113+
}
114+
115+
pub fn build<S>(&mut self, signer: S) -> Result<Certificate<'_>>
116+
where
117+
S: Signer<Alg = SA>,
118+
{
119+
let signature_alg =
120+
AlgorithmIdentifier::decode(&mut SliceReader::new(&self.signature_alg)?)?;
121+
let issuer = Name::decode(&mut SliceReader::new(&self.issuer)?)?;
122+
let serial_number = UIntRef::new(&self.serial_number).unwrap();
123+
let subject = Name::decode(&mut SliceReader::new(&self.subject)?)?;
124+
let subject_public_key_info =
125+
SubjectPublicKeyInfo::decode(&mut SliceReader::new(&self.subject_public_key_info)?)?;
126+
let issuer_unique_id =
127+
BitStringRef::decode(&mut SliceReader::new(&self.issuer_unique_id)?)?;
128+
let subject_unique_id =
129+
BitStringRef::decode(&mut SliceReader::new(&self.subject_unique_id)?)?;
130+
131+
let tbs_certificate = TbsCertificate {
132+
version: self.version,
133+
serial_number,
134+
signature: signature_alg,
135+
issuer,
136+
validity: self.validity,
137+
subject,
138+
subject_public_key_info,
139+
issuer_unique_id,
140+
subject_unique_id,
141+
extensions: None,
142+
};
143+
144+
self.signature = signer.sign(&tbs_certificate);
145+
let signature = BitStringRef::from_bytes(&self.signature)?;
146+
147+
let cert = Certificate {
148+
tbs_certificate,
149+
signature_algorithm: signature_alg,
150+
signature,
151+
};
152+
153+
println!("cert: {:?}", cert);
154+
155+
Ok(cert)
156+
}
157+
}
158+
159+
pub trait CertSignatureAlgorithm {
160+
fn signature_algorithm(&self) -> AlgorithmIdentifier<'_>;
161+
}
162+
163+
pub trait Signer {
164+
type Alg: CertSignatureAlgorithm;
165+
166+
fn sign(&self, input: &TbsCertificate<'_>) -> Vec<u8>;
167+
}

x509-cert/src/constants.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use crate::{builder::CertSignatureAlgorithm, der::asn1, spki::AlgorithmIdentifier};
2+
3+
pub struct RsaWithSha256;
4+
5+
impl RsaWithSha256 {
6+
const OID: asn1::ObjectIdentifier = asn1::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.11");
7+
}
8+
9+
impl CertSignatureAlgorithm for RsaWithSha256 {
10+
fn signature_algorithm(&self) -> AlgorithmIdentifier<'_> {
11+
AlgorithmIdentifier {
12+
oid: RsaWithSha256::OID.clone(),
13+
parameters: None,
14+
}
15+
}
16+
}

x509-cert/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@ mod macros;
2323

2424
pub mod anchor;
2525
pub mod attr;
26+
#[cfg(feature = "builder")]
27+
pub mod builder;
2628
pub mod certificate;
29+
#[cfg(feature = "builder")]
30+
pub mod constants;
2731
pub mod crl;
2832
pub mod ext;
2933
pub mod name;

0 commit comments

Comments
 (0)