1
1
//! Basic OCSP Response
2
2
3
+ use crate :: {
4
+ ext:: Nonce , AsResponseBytes , CertId , CertStatus , OcspGeneralizedTime , ResponderId , Version ,
5
+ } ;
3
6
use alloc:: vec:: Vec ;
4
- use const_oid:: AssociatedOid ;
7
+ use const_oid:: {
8
+ db:: rfc6960:: { ID_PKIX_OCSP_BASIC , ID_PKIX_OCSP_NONCE } ,
9
+ AssociatedOid ,
10
+ } ;
5
11
use core:: { default:: Default , option:: Option } ;
6
12
use der:: {
7
- asn1:: { BitString , GeneralizedTime , Null , OctetString } ,
8
- Choice , Decode , Enumerated , Sequence ,
13
+ asn1:: { BitString , ObjectIdentifier } ,
14
+ Decode , Sequence ,
9
15
} ;
10
16
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
- }
17
+ use x509_cert:: { certificate:: Certificate , ext:: Extensions } ;
35
18
36
19
/// BasicOcspResponse structure as defined in [RFC 6960 Section 4.2.1].
37
20
///
@@ -55,6 +38,20 @@ pub struct BasicOcspResponse {
55
38
pub certs : Option < Vec < Certificate > > ,
56
39
}
57
40
41
+ impl BasicOcspResponse {
42
+ /// Returns the response's nonce value, if any. This method will return `None` if the response
43
+ /// has no `Nonce` extension or decoding of the `Nonce` extension fails.
44
+ pub fn nonce ( & self ) -> Option < Nonce > {
45
+ self . tbs_response_data . nonce ( )
46
+ }
47
+ }
48
+
49
+ impl AssociatedOid for BasicOcspResponse {
50
+ const OID : ObjectIdentifier = ID_PKIX_OCSP_BASIC ;
51
+ }
52
+
53
+ impl AsResponseBytes for BasicOcspResponse { }
54
+
58
55
/// ResponseData structure as defined in [RFC 6960 Section 4.2.1].
59
56
///
60
57
/// ```text
@@ -77,45 +74,30 @@ pub struct ResponseData {
77
74
) ]
78
75
pub version : Version ,
79
76
pub responder_id : ResponderId ,
80
- pub produced_at : GeneralizedTime ,
77
+ pub produced_at : OcspGeneralizedTime ,
81
78
pub responses : Vec < SingleResponse > ,
82
79
83
80
#[ asn1( context_specific = "1" , optional = "true" , tag_mode = "EXPLICIT" ) ]
84
81
pub response_extensions : Option < Extensions > ,
85
82
}
86
83
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 ) ,
84
+ impl ResponseData {
85
+ /// Returns the response's nonce value, if any. This method will return `None` if the response
86
+ /// has no `Nonce` extension or decoding of the `Nonce` extension fails.
87
+ pub fn nonce ( & self ) -> Option < Nonce > {
88
+ match & self . response_extensions {
89
+ Some ( extns) => {
90
+ let mut filter = extns. iter ( ) . filter ( |e| e. extn_id == ID_PKIX_OCSP_NONCE ) ;
91
+ match filter. next ( ) {
92
+ Some ( extn) => Nonce :: from_der ( extn. extn_value . as_bytes ( ) ) . ok ( ) ,
93
+ None => None ,
94
+ }
95
+ }
96
+ None => None ,
97
+ }
98
+ }
104
99
}
105
100
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
101
/// SingleResponse structure as defined in [RFC 6960 Section 4.2.1].
120
102
///
121
103
/// ```text
@@ -133,112 +115,116 @@ pub type KeyHash = OctetString;
133
115
pub struct SingleResponse {
134
116
pub cert_id : CertId ,
135
117
pub cert_status : CertStatus ,
136
- pub this_update : GeneralizedTime ,
118
+ pub this_update : OcspGeneralizedTime ,
137
119
138
120
#[ asn1( context_specific = "0" , optional = "true" , tag_mode = "EXPLICIT" ) ]
139
- pub next_update : Option < GeneralizedTime > ,
121
+ pub next_update : Option < OcspGeneralizedTime > ,
140
122
141
123
#[ asn1( context_specific = "1" , optional = "true" , tag_mode = "EXPLICIT" ) ]
142
124
pub single_extensions : Option < Extensions > ,
143
125
}
144
126
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 ) ,
127
+ #[ cfg( feature = "builder" ) ]
128
+ mod builder {
129
+ use crate :: { builder:: Error , CertId , CertStatus , OcspGeneralizedTime , SingleResponse } ;
130
+ use const_oid:: AssociatedOid ;
131
+ use digest:: Digest ;
132
+ use x509_cert:: {
133
+ crl:: CertificateList , ext:: AsExtension , name:: Name , serial_number:: SerialNumber ,
134
+ Certificate ,
135
+ } ;
136
+
137
+ impl SingleResponse {
138
+ /// Returns a `SingleResponse` given the `CertID`, `CertStatus`, and `This Update`. `Next
139
+ /// Update` is set to `None`.
140
+ pub fn new (
141
+ cert_id : CertId ,
142
+ cert_status : CertStatus ,
143
+ this_update : OcspGeneralizedTime ,
144
+ ) -> Self {
145
+ Self {
146
+ cert_id,
147
+ cert_status,
148
+ this_update,
149
+ next_update : None ,
150
+ single_extensions : None ,
151
+ }
152
+ }
183
153
184
- #[ asn1( context_specific = "2" , tag_mode = "IMPLICIT" ) ]
185
- Unknown ( UnknownInfo ) ,
186
- }
154
+ /// Sets `thisUpdate` in the `singleResponse` as defined in [RFC 6960 Section 4.2.1].
155
+ ///
156
+ /// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
157
+ pub fn with_this_update ( mut self , this_update : OcspGeneralizedTime ) -> Self {
158
+ self . this_update = this_update;
159
+ self
160
+ }
187
161
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 ,
162
+ /// Sets `nextUpdate` in the `singleResponse` as defined in [RFC 6960 Section 4.2.1].
163
+ ///
164
+ /// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
165
+ pub fn with_next_update ( mut self , next_update : OcspGeneralizedTime ) -> Self {
166
+ self . next_update = Some ( next_update) ;
167
+ self
168
+ }
201
169
202
- #[ asn1( context_specific = "0" , optional = "true" , tag_mode = "EXPLICIT" ) ]
203
- pub revocation_reason : Option < CrlReason > ,
204
- }
170
+ /// Adds a single response extension as specified in [RFC 6960 Section 4.4]. Errors when the
171
+ /// extension encoding fails.
172
+ ///
173
+ /// [RFC 6960 Section 4.4]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.4
174
+ pub fn with_extension ( mut self , ext : impl AsExtension ) -> Result < Self , Error > {
175
+ let ext = ext. to_extension ( & Name :: default ( ) , & [ ] ) ?;
176
+ match self . single_extensions {
177
+ Some ( ref mut exts) => exts. push ( ext) ,
178
+ None => self . single_extensions = Some ( alloc:: vec![ ext] ) ,
179
+ }
180
+ Ok ( self )
181
+ }
205
182
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
- } ,
183
+ /// Returns a `SingleResponse` by searching through the CRL to see if `serial` is revoked. If
184
+ /// not, the `CertStatus` is set to good. The `CertID` is built from the issuer and serial
185
+ /// number. This method does not ensure the CRL is issued by the issuer and only asserts the
186
+ /// serial is not revoked in the provided CRL.
187
+ ///
188
+ /// `thisUpdate` and `nextUpdate` will be pulled from the CRL.
189
+ ///
190
+ /// NOTE: this method complies with [RFC 2560 Section 2.2] and not [RFC 6960 Section 2.2].
191
+ /// [RFC 6960] limits the `good` status to only issued certificates. [RFC 2560] only asserts
192
+ /// the serial was not revoked and makes no assertion the serial was ever issued.
193
+ ///
194
+ /// [RFC 2560]: https://datatracker.ietf.org/doc/html/rfc2560
195
+ /// [RFC 2560 Section 2.2]: https://datatracker.ietf.org/doc/html/rfc2560#section-2.2
196
+ /// [RFC 6960]: https://datatracker.ietf.org/doc/html/rfc6960
197
+ /// [RFC 6960 Section 2.2]: https://datatracker.ietf.org/doc/html/rfc6960#section-2.2
198
+ pub fn from_crl < D > (
199
+ issuer : & Certificate ,
200
+ crl : & CertificateList ,
201
+ serial_number : SerialNumber ,
202
+ ) -> Result < Self , Error >
203
+ where
204
+ D : Digest + AssociatedOid ,
205
+ {
206
+ let cert_status = match & crl. tbs_cert_list . revoked_certificates {
207
+ Some ( revoked_certs) => {
208
+ let mut filter = revoked_certs
209
+ . iter ( )
210
+ . filter ( |rc| rc. serial_number == serial_number) ;
211
+ match filter. next ( ) {
212
+ None => CertStatus :: good ( ) ,
213
+ Some ( rc) => CertStatus :: revoked ( rc) ,
214
+ }
223
215
}
224
- } else {
225
- None
226
- } ,
216
+ None => CertStatus :: good ( ) ,
217
+ } ;
218
+ let cert_id = CertId :: from_issuer :: < D > ( issuer, serial_number) ?;
219
+ let this_update = crl. tbs_cert_list . this_update . into ( ) ;
220
+ let next_update = crl. tbs_cert_list . next_update . map ( |t| t. into ( ) ) ;
221
+ Ok ( Self {
222
+ cert_id,
223
+ cert_status,
224
+ this_update,
225
+ next_update,
226
+ single_extensions : None ,
227
+ } )
227
228
}
228
229
}
229
230
}
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