|
| 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