1 /**
2 * OCSP
3 * 
4 * Copyright:
5 * (C) 2012 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.cert.x509.ocsp;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_X509_CERTIFICATES):
15 
16 public import botan.cert.x509.cert_status;
17 public import botan.cert.x509.certstor;
18 public import botan.cert.x509.x509cert;
19 import botan.cert.x509.ocsp_types;
20 import botan.asn1.asn1_time;
21 import botan.asn1.x509_dn;
22 import botan.asn1.der_enc;
23 import botan.asn1.ber_dec;
24 import botan.asn1.asn1_obj;
25 import botan.cert.x509.x509_ext;
26 import botan.asn1.oids;
27 import botan.codec.base64;
28 import botan.pubkey.pubkey;
29 import botan.cert.x509.x509path;
30 import botan.utils.http_util.http_util;
31 import botan.utils.types;
32 import std.datetime;
33 import std.algorithm : splitter;
34 import core.sync.mutex;
35 import botan.utils.mem_ops;
36 
37 alias OCSPResponse = RefCounted!OCSPResponseImpl;
38 
39 struct OCSPRequest
40 {
41 public:
42     @disable this();
43 
44     this(X509Certificate issuer_cert,
45          X509Certificate subject_cert) 
46         
47     {
48         logTrace("OCSPRequest: Issuer Cert: ", issuer_cert.toString());
49         logTrace("OCSPRequest: Subject Cert: ", subject_cert.toString());
50         m_issuer = issuer_cert;
51         m_subject = subject_cert;
52     }
53 
54     Vector!ubyte BER_encode() const
55     {
56         CertID certid = CertID(m_issuer, m_subject);
57         
58         return DEREncoder().startCons(ASN1Tag.SEQUENCE)
59                 .startCons(ASN1Tag.SEQUENCE)
60                 .startExplicit(0)
61                 .encode(cast(size_t)(0)) // version #
62                 .endExplicit()
63                 .startCons(ASN1Tag.SEQUENCE)
64                 .startCons(ASN1Tag.SEQUENCE)
65                 .encode(certid)
66                 .endCons()
67                 .endCons()
68                 .endCons()
69                 .endCons().getContentsUnlocked();
70     }
71 
72     string base64Encode() const
73     {
74         return .base64Encode(BER_encode());
75     }
76 
77     const(X509Certificate) issuer() const { return m_issuer; }
78 
79     const(X509Certificate) subject() const { return m_subject; }
80 private:
81     X509Certificate m_issuer, m_subject;
82 }
83 
84 class OCSPResponseImpl
85 {
86 public:
87     this(in CertificateStore trusted_roots,
88          const(X509Certificate)* issuer,
89          in string response_bits)
90     {
91         this.trusted_roots = trusted_roots;
92         this.issuer = issuer;
93         this.response_bits = response_bits;
94     }
95 
96     void check()
97     {
98         Vector!ubyte response_vec = Vector!ubyte(response_bits);
99         BERDecoder response_outer = BERDecoder(response_vec).startCons(ASN1Tag.SEQUENCE);
100         
101         size_t resp_status = 0;
102         
103         response_outer.decode(resp_status, ASN1Tag.ENUMERATED, ASN1Tag.UNIVERSAL);
104         
105         if (resp_status != 0)
106             throw new Exception("OCSP response status " ~ to!string(resp_status));
107         
108         if (response_outer.moreItems())
109         {
110             BERDecoder response_bytes = response_outer.startCons((cast(ASN1Tag) 0), ASN1Tag.CONTEXT_SPECIFIC).startCons(ASN1Tag.SEQUENCE);
111             
112             response_bytes.decodeAndCheck(OID("1.3.6.1.5.5.7.48.1.1"), "Unknown response type in OCSP response");
113             
114             BERDecoder basicresponse = BERDecoder(response_bytes.getNextOctetString()).startCons(ASN1Tag.SEQUENCE);
115             
116             Vector!ubyte tbs_bits;
117             auto sig_algo = AlgorithmIdentifier();
118             Vector!ubyte signature;
119             Vector!X509Certificate certs;
120             
121             basicresponse.startCons(ASN1Tag.SEQUENCE)
122                     .rawBytes(tbs_bits)
123                     .endCons()
124                     .decode(sig_algo)
125                     .decode(signature, ASN1Tag.BIT_STRING);
126 
127             decodeOptionalList(basicresponse, (cast(ASN1Tag) 0), certs);
128             
129             size_t responsedata_version;
130             X509DN name = X509DN();
131             Vector!ubyte key_hash;
132             X509Time produced_at = X509Time(Clock.currTime(UTC()));
133             X509Extensions extensions = X509Extensions(true);
134             
135             BERDecoder(tbs_bits)
136                     .decodeOptional(responsedata_version, (cast(ASN1Tag) 0), ASN1Tag.CONSTRUCTED | ASN1Tag.CONTEXT_SPECIFIC)
137                     .decodeOptional(name, (cast(ASN1Tag)1), ASN1Tag.CONSTRUCTED | ASN1Tag.CONTEXT_SPECIFIC)                    
138                     .decodeOptionalString(key_hash, ASN1Tag.OCTET_STRING, 2, ASN1Tag.CONSTRUCTED | ASN1Tag.CONTEXT_SPECIFIC)
139                     .decode(produced_at)
140                     .decodeList(m_responses)
141                     .decodeOptional(extensions, (cast(ASN1Tag)1), ASN1Tag.CONSTRUCTED | ASN1Tag.CONTEXT_SPECIFIC);
142             
143             if (certs.empty && issuer && issuer.subjectDn() == name)
144                 checkSignature(tbs_bits, sig_algo, signature, *issuer);
145             else
146             {
147                 if (certs.empty)
148                 {
149                     if (auto cert = trusted_roots.findCert(name, Vector!ubyte()))
150                         certs.pushBack(cert);
151                     else
152                         throw new Exception("Could not find certificate that signed OCSP response");
153                 }
154 
155                 checkSignature(tbs_bits, sig_algo, signature, trusted_roots, certs);
156             }
157         }
158         
159         response_outer.endCons();
160     }
161 
162     CertificateStatusCode statusFor(in X509Certificate issuer,
163                                     in X509Certificate subject) const
164     {
165         //logTrace("Responses: ", m_responses.length);
166         foreach (response; m_responses[])
167         {
168             if (response.certid().isIdFor(issuer, subject))
169             {
170                 X509Time current_time = X509Time(Clock.currTime(UTC()));
171                 
172                 if (response.certStatus() == 1)
173                     return CertificateStatusCode.CERT_IS_REVOKED;
174                 
175                 if (response.thisUpdate() > current_time)
176                     return CertificateStatusCode.OCSP_NOT_YET_VALID;
177                 
178                 if (response.nextUpdate().timeIsSet() && current_time > response.nextUpdate())
179                     return CertificateStatusCode.OCSP_HAS_EXPIRED;
180                 
181                 if (response.certStatus() == 0)
182                     return CertificateStatusCode.OCSP_RESPONSE_GOOD;
183                 else
184                     return CertificateStatusCode.OCSP_BAD_STATUS;
185             }
186         }
187         
188         return CertificateStatusCode.OCSP_CERT_NOT_LISTED;
189     }
190 
191     @property bool empty() {
192         return m_responses.length == 0;
193     }
194 private:
195     const(CertificateStore) trusted_roots;
196     const(X509Certificate)* issuer;
197     string response_bits;
198     Vector!( SingleResponse ) m_responses;
199 }
200 
201 
202 void decodeOptionalList(ref BERDecoder ber,
203                         ASN1Tag tag,
204                         ref Vector!X509Certificate output)
205 {
206     BERObject obj = ber.getNextObject();
207     
208     if (obj.type_tag != tag || obj.class_tag != (ASN1Tag.CONTEXT_SPECIFIC | ASN1Tag.CONSTRUCTED))
209     {
210         ber.pushBack(obj);
211         return;
212     }
213     
214     BERDecoder list = BERDecoder(obj.value);
215     
216     while (list.moreItems())
217     {
218         BERObject certbits = list.getNextObject();
219         X509Certificate cert = X509Certificate(unlock(certbits.value));
220         output.pushBack(cert);
221     }
222 }
223 
224 /// Does not use trusted roots
225 /// Throws if not trusted
226 void checkSignature(ALLOC)(auto const ref Vector!(ubyte, ALLOC) tbs_response,
227                                const AlgorithmIdentifier sig_algo,
228                                const ref Vector!ubyte signature,
229                                const X509Certificate cert)
230 {
231     Unique!PublicKey pub_key = cert.subjectPublicKey();
232     
233     const Vector!string sig_info = splitter(OIDS.lookup(sig_algo.oid), '/');
234     
235     if (sig_info.length != 2 || sig_info[0] != pub_key.algoName)
236         throw new Exception("Information in OCSP response does not match cert");
237 
238     string padding = sig_info[1];
239     SignatureFormat format = (pub_key.messageParts() >= 2) ? DER_SEQUENCE : IEEE_1363;
240     
241     PKVerifier verifier = PKVerifier(*pub_key, padding, format);
242     if (!verifier.verifyMessage(putInSequence(tbs_response), signature))
243         throw new Exception("Signature on OCSP response does not verify");
244 }
245 
246 /// Iterates over trusted roots certificate store
247 /// throws if not trusted
248 void checkSignature(ALLOC)(auto const ref Vector!(ubyte, ALLOC) tbs_response,
249                                  const AlgorithmIdentifier sig_algo,
250                                  const ref Vector!ubyte signature,
251                                  const CertificateStore trusted_roots,
252                                  const ref Vector!X509Certificate certs)
253 {
254     if (certs.length < 1)
255         throw new InvalidArgument("Short cert chain for checkSignature");
256     
257     if (trusted_roots.certificateKnown(certs[0]))
258         return checkSignature(tbs_response, sig_algo, signature, certs[0]);
259     
260     // Otherwise attempt to chain the signing cert to a trust root
261     
262     if (!certs[0].allowedUsage("PKIX.OCSPSigning"))
263         throw new Exception("OCSP response cert does not allow OCSP signing");
264     
265     auto result = x509PathValidate(certs, PathValidationRestrictions(), trusted_roots);
266     
267     if (!result.successfulValidation())
268         throw new Exception("Certificate validation failure: " ~ result.resultString());
269     
270     if (!trusted_roots.certificateKnown(result.trustRoot())) // not needed anymore?
271         throw new Exception("Certificate chain roots in unknown/untrusted CA");
272         
273     checkSignature(tbs_response, sig_algo, signature, result.certPath()[0]);
274 }
275 
276 //version(Have_vibe_d) import vibe.core.concurrency : Tid, send; else 
277 import core.sync.mutex : Mutex;
278 /// Checks the certificate online
279 struct OnlineCheck {
280 	size_t id;
281 	OCSPResponse* resp;
282 	const(X509Certificate)* issuer;
283 	const(X509Certificate)* subject;
284 	const(CertificateStore)* trusted_roots;
285 
286 	void run() {
287 	    /// TODO: Test OCSP with correct BER Decoding
288 	    logTrace("Checking OCSP online");
289 		string responder_url;
290 		if (!issuer) {
291 			*cast(OCSPResponse*)resp = OCSPResponse.init;
292 			return;
293 		}
294 		responder_url = (*cast(X509Certificate*)subject).ocspResponder();
295 	    logTrace("Responder url: ", responder_url.length);
296 
297 	    if (responder_url.length == 0) {
298 	        logTrace("Aborting OCSP for ID#", id.to!string);
299 	        *cast(OCSPResponse*)resp = OCSPResponse.init;
300 	        return;
301 	    }
302 
303 	    OCSPRequest req = OCSPRequest(*cast(X509Certificate*)issuer, *cast(X509Certificate*)subject);
304 	    
305 	    logTrace("POST_sync");
306 	    HTTPResponse res = POST_sync(responder_url, "application/ocsp-request", req.BER_encode());
307 	    
308 	    res.throwUnlessOk();
309 	    
310 	    // Check the MIME type?
311 		*cast(OCSPResponse*)resp = OCSPResponse(*(cast(CertificateStore*)trusted_roots),
312             issuer is subject ? null : issuer,
313             res._body());
314         resp.check();
315 	}
316 }