11//! Basic OCSP Response
22
3+ use crate :: {
4+ ext:: Nonce , AsResponseBytes , CertId , CertStatus , OcspGeneralizedTime , ResponderId , Version ,
5+ } ;
36use 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+ } ;
511use core:: { default:: Default , option:: Option } ;
612use der:: {
7- asn1:: { BitString , GeneralizedTime , Null , OctetString } ,
8- Choice , Decode , Enumerated , Sequence ,
13+ asn1:: { BitString , ObjectIdentifier } ,
14+ Decode , Sequence ,
915} ;
1016use 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 } ;
3518
3619/// BasicOcspResponse structure as defined in [RFC 6960 Section 4.2.1].
3720///
@@ -55,6 +38,20 @@ pub struct BasicOcspResponse {
5538 pub certs : Option < Vec < Certificate > > ,
5639}
5740
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+
5855/// ResponseData structure as defined in [RFC 6960 Section 4.2.1].
5956///
6057/// ```text
@@ -77,45 +74,30 @@ pub struct ResponseData {
7774 ) ]
7875 pub version : Version ,
7976 pub responder_id : ResponderId ,
80- pub produced_at : GeneralizedTime ,
77+ pub produced_at : OcspGeneralizedTime ,
8178 pub responses : Vec < SingleResponse > ,
8279
8380 #[ asn1( context_specific = "1" , optional = "true" , tag_mode = "EXPLICIT" ) ]
8481 pub response_extensions : Option < Extensions > ,
8582}
8683
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+ }
10499}
105100
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-
119101/// SingleResponse structure as defined in [RFC 6960 Section 4.2.1].
120102///
121103/// ```text
@@ -133,112 +115,116 @@ pub type KeyHash = OctetString;
133115pub struct SingleResponse {
134116 pub cert_id : CertId ,
135117 pub cert_status : CertStatus ,
136- pub this_update : GeneralizedTime ,
118+ pub this_update : OcspGeneralizedTime ,
137119
138120 #[ asn1( context_specific = "0" , optional = "true" , tag_mode = "EXPLICIT" ) ]
139- pub next_update : Option < GeneralizedTime > ,
121+ pub next_update : Option < OcspGeneralizedTime > ,
140122
141123 #[ asn1( context_specific = "1" , optional = "true" , tag_mode = "EXPLICIT" ) ]
142124 pub single_extensions : Option < Extensions > ,
143125}
144126
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+ }
183153
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+ }
187161
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+ }
201169
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+ }
205182
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+ }
223215 }
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+ } )
227228 }
228229 }
229230}
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