Skip to content

Commit 7543df3

Browse files
authored
x509-ocsp: remove lifetimes and implement AsExtension (#1241)
Reorganizes `x509-ocsp` into separate files for readability and makes use of alloc to remove lifetimes. It also implements an OCSP version of AsExtension for all possible Extensions outlined in RFC-6960 (in a similar fashion to x509-cert) to be used in a future builder. Also adds optional `rand_core` feature for generating nonces.
1 parent 3d1f70f commit 7543df3

File tree

7 files changed

+701
-459
lines changed

7 files changed

+701
-459
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

x509-ocsp/Cargo.toml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@ edition = "2021"
1515
rust-version = "1.65"
1616

1717
[dependencies]
18-
der = { version = "0.7", features = ["alloc", "derive", "oid"] }
19-
spki = { version = "0.7" }
20-
x509-cert = { version = "0.2", default-features = false }
18+
const-oid = { version = "0.9.5", default-features = false, features = ["db"] }
19+
der = { version = "0.7.8", features = ["alloc", "derive", "oid"] }
20+
spki = { version = "0.7.2", features = ["alloc"] }
21+
x509-cert = { version = "0.2.4", default-features = false }
22+
23+
# Optional
24+
rand_core = { version = "0.6.4", optional = true, default-features = false }
2125

2226
[dev-dependencies]
23-
const-oid = { version = "0.9", features = ["db"] } # TODO: path = "../const-oid"
2427
hex-literal = "0.4.1"
2528

2629
[package.metadata.docs.rs]

x509-ocsp/src/basic.rs

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
//! Basic OCSP Response
2+
3+
use alloc::vec::Vec;
4+
use const_oid::AssociatedOid;
5+
use core::{default::Default, option::Option};
6+
use der::{
7+
asn1::{BitString, GeneralizedTime, Null, OctetString},
8+
Choice, Decode, Enumerated, Sequence,
9+
};
10+
use spki::AlgorithmIdentifierOwned;
11+
use x509_cert::{
12+
certificate::Certificate,
13+
crl::RevokedCert,
14+
ext::{pkix::CrlReason, Extensions},
15+
name::Name,
16+
serial_number::SerialNumber,
17+
time::Time,
18+
};
19+
20+
/// OCSP `Version` as defined in [RFC 6960 Section 4.1.1].
21+
///
22+
/// ```text
23+
/// Version ::= INTEGER { v1(0) }
24+
/// ```
25+
///
26+
/// [RFC 6960 Section 4.1.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.1.1
27+
#[derive(Clone, Debug, Default, Copy, PartialEq, Eq, Enumerated)]
28+
#[asn1(type = "INTEGER")]
29+
#[repr(u8)]
30+
pub enum Version {
31+
/// Version 1 (default)
32+
#[default]
33+
V1 = 0,
34+
}
35+
36+
/// BasicOcspResponse structure as defined in [RFC 6960 Section 4.2.1].
37+
///
38+
/// ```text
39+
/// BasicOCSPResponse ::= SEQUENCE {
40+
/// tbsResponseData ResponseData,
41+
/// signatureAlgorithm AlgorithmIdentifier,
42+
/// signature BIT STRING,
43+
/// certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
44+
/// ```
45+
///
46+
/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
47+
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
48+
#[allow(missing_docs)]
49+
pub struct BasicOcspResponse {
50+
pub tbs_response_data: ResponseData,
51+
pub signature_algorithm: AlgorithmIdentifierOwned,
52+
pub signature: BitString,
53+
54+
#[asn1(context_specific = "0", optional = "true", tag_mode = "EXPLICIT")]
55+
pub certs: Option<Vec<Certificate>>,
56+
}
57+
58+
/// ResponseData structure as defined in [RFC 6960 Section 4.2.1].
59+
///
60+
/// ```text
61+
/// ResponseData ::= SEQUENCE {
62+
/// version [0] EXPLICIT Version DEFAULT v1,
63+
/// responderID ResponderID,
64+
/// producedAt GeneralizedTime,
65+
/// responses SEQUENCE OF SingleResponse,
66+
/// responseExtensions [1] EXPLICIT Extensions OPTIONAL }
67+
/// ```
68+
///
69+
/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
70+
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
71+
#[allow(missing_docs)]
72+
pub struct ResponseData {
73+
#[asn1(
74+
context_specific = "0",
75+
default = "Default::default",
76+
tag_mode = "EXPLICIT"
77+
)]
78+
pub version: Version,
79+
pub responder_id: ResponderId,
80+
pub produced_at: GeneralizedTime,
81+
pub responses: Vec<SingleResponse>,
82+
83+
#[asn1(context_specific = "1", optional = "true", tag_mode = "EXPLICIT")]
84+
pub response_extensions: Option<Extensions>,
85+
}
86+
87+
/// ResponderID structure as defined in [RFC 6960 Section 4.2.1].
88+
///
89+
/// ```text
90+
/// ResponderID ::= CHOICE {
91+
/// byName [1] Name,
92+
/// byKey [2] KeyHash }
93+
/// ```
94+
///
95+
/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
96+
#[derive(Clone, Debug, Eq, PartialEq, Choice)]
97+
#[allow(missing_docs)]
98+
pub enum ResponderId {
99+
#[asn1(context_specific = "1", tag_mode = "EXPLICIT", constructed = "true")]
100+
ByName(Name),
101+
102+
#[asn1(context_specific = "2", tag_mode = "EXPLICIT", constructed = "true")]
103+
ByKey(KeyHash),
104+
}
105+
106+
/// KeyHash structure as defined in [RFC 6960 Section 4.2.1].
107+
///
108+
/// ```text
109+
/// KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
110+
/// -- (i.e., the SHA-1 hash of the value of the
111+
/// -- BIT STRING subjectPublicKey [excluding
112+
/// -- the tag, length, and number of unused
113+
/// -- bits] in the responder's certificate)
114+
/// ```
115+
///
116+
/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
117+
pub type KeyHash = OctetString;
118+
119+
/// SingleResponse structure as defined in [RFC 6960 Section 4.2.1].
120+
///
121+
/// ```text
122+
/// SingleResponse ::= SEQUENCE {
123+
/// certID CertID,
124+
/// certStatus CertStatus,
125+
/// thisUpdate GeneralizedTime,
126+
/// nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
127+
/// singleExtensions [1] EXPLICIT Extensions OPTIONAL }
128+
/// ```
129+
///
130+
/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
131+
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
132+
#[allow(missing_docs)]
133+
pub struct SingleResponse {
134+
pub cert_id: CertId,
135+
pub cert_status: CertStatus,
136+
pub this_update: GeneralizedTime,
137+
138+
#[asn1(context_specific = "0", optional = "true", tag_mode = "EXPLICIT")]
139+
pub next_update: Option<GeneralizedTime>,
140+
141+
#[asn1(context_specific = "1", optional = "true", tag_mode = "EXPLICIT")]
142+
pub single_extensions: Option<Extensions>,
143+
}
144+
145+
/// CertID structure as defined in [RFC 6960 Section 4.1.1].
146+
///
147+
/// ```text
148+
/// CertID ::= SEQUENCE {
149+
/// hashAlgorithm AlgorithmIdentifier,
150+
/// issuerNameHash OCTET STRING, -- Hash of issuer's DN
151+
/// issuerKeyHash OCTET STRING, -- Hash of issuer's public key
152+
/// serialNumber CertificateSerialNumber }
153+
/// ```
154+
///
155+
/// [RFC 6960 Section 4.1.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.1.1
156+
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
157+
#[allow(missing_docs)]
158+
pub struct CertId {
159+
pub hash_algorithm: AlgorithmIdentifierOwned,
160+
pub issuer_name_hash: OctetString,
161+
pub issuer_key_hash: OctetString,
162+
pub serial_number: SerialNumber,
163+
}
164+
165+
/// CertStatus structure as defined in [RFC 6960 Section 4.2.1].
166+
///
167+
/// ```text
168+
/// CertStatus ::= CHOICE {
169+
/// good [0] IMPLICIT NULL,
170+
/// revoked [1] IMPLICIT RevokedInfo,
171+
/// unknown [2] IMPLICIT UnknownInfo }
172+
/// ```
173+
///
174+
/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
175+
#[derive(Clone, Debug, Eq, PartialEq, Choice)]
176+
#[allow(missing_docs)]
177+
pub enum CertStatus {
178+
#[asn1(context_specific = "0", tag_mode = "IMPLICIT")]
179+
Good(Null),
180+
181+
#[asn1(context_specific = "1", tag_mode = "IMPLICIT", constructed = "true")]
182+
Revoked(RevokedInfo),
183+
184+
#[asn1(context_specific = "2", tag_mode = "IMPLICIT")]
185+
Unknown(UnknownInfo),
186+
}
187+
188+
/// RevokedInfo structure as defined in [RFC 6960 Section 4.2.1].
189+
///
190+
/// ```text
191+
/// RevokedInfo ::= SEQUENCE {
192+
/// revocationTime GeneralizedTime,
193+
/// revocationReason [0] EXPLICIT CRLReason OPTIONAL }
194+
/// ```
195+
///
196+
/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
197+
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
198+
#[allow(missing_docs)]
199+
pub struct RevokedInfo {
200+
pub revocation_time: GeneralizedTime,
201+
202+
#[asn1(context_specific = "0", optional = "true", tag_mode = "EXPLICIT")]
203+
pub revocation_reason: Option<CrlReason>,
204+
}
205+
206+
impl From<&RevokedCert> for RevokedInfo {
207+
fn from(rc: &RevokedCert) -> Self {
208+
Self {
209+
revocation_time: match rc.revocation_date {
210+
Time::UtcTime(t) => GeneralizedTime::from_date_time(t.to_date_time()),
211+
Time::GeneralTime(t) => t,
212+
},
213+
revocation_reason: if let Some(extensions) = &rc.crl_entry_extensions {
214+
let mut filter = extensions
215+
.iter()
216+
.filter(|ext| ext.extn_id == CrlReason::OID);
217+
match filter.next() {
218+
None => None,
219+
Some(ext) => match CrlReason::from_der(ext.extn_value.as_bytes()) {
220+
Ok(reason) => Some(reason),
221+
Err(_) => None,
222+
},
223+
}
224+
} else {
225+
None
226+
},
227+
}
228+
}
229+
}
230+
231+
impl From<RevokedCert> for RevokedInfo {
232+
fn from(rc: RevokedCert) -> Self {
233+
Self::from(&rc)
234+
}
235+
}
236+
237+
/// RevokedInfo structure as defined in [RFC 6960 Section 4.2.1].
238+
///
239+
/// ```text
240+
/// UnknownInfo ::= NULL
241+
/// ```
242+
///
243+
/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
244+
pub type UnknownInfo = Null;

0 commit comments

Comments
 (0)