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          in string response_bits)
89     {
90         Vector!ubyte response_vec = Vector!ubyte(response_bits);
91         BERDecoder response_outer = BERDecoder(response_vec).startCons(ASN1Tag.SEQUENCE);
92         
93         size_t resp_status = 0;
94         
95         response_outer.decode(resp_status, ASN1Tag.ENUMERATED, ASN1Tag.UNIVERSAL);
96         
97         if (resp_status != 0)
98             throw new Exception("OCSP response status " ~ to!string(resp_status));
99         
100         if (response_outer.moreItems())
101         {
102             BERDecoder response_bytes = response_outer.startCons((cast(ASN1Tag) 0), ASN1Tag.CONTEXT_SPECIFIC).startCons(ASN1Tag.SEQUENCE);
103             
104             response_bytes.decodeAndCheck(OID("1.3.6.1.5.5.7.48.1.1"), "Unknown response type in OCSP response");
105             
106             BERDecoder basicresponse = BERDecoder(response_bytes.getNextOctetString()).startCons(ASN1Tag.SEQUENCE);
107             
108             Vector!ubyte tbs_bits;
109             auto sig_algo = AlgorithmIdentifier();
110             Vector!ubyte signature;
111             Vector!X509Certificate certs;
112             
113             basicresponse.startCons(ASN1Tag.SEQUENCE)
114                     .rawBytes(tbs_bits)
115                     .endCons()
116                     .decode(sig_algo)
117                     .decode(signature, ASN1Tag.BIT_STRING);
118 
119             decodeOptionalList(basicresponse, (cast(ASN1Tag) 0), certs);
120             
121             size_t responsedata_version;
122             X509DN name = X509DN();
123             Vector!ubyte key_hash;
124             X509Time produced_at;
125             X509Extensions extensions;
126             
127             BERDecoder(tbs_bits)
128                     .decodeOptional(responsedata_version, (cast(ASN1Tag) 0), ASN1Tag.CONSTRUCTED | ASN1Tag.CONTEXT_SPECIFIC)
129                     .decodeOptional(name, (cast(ASN1Tag)1), ASN1Tag.CONSTRUCTED | ASN1Tag.CONTEXT_SPECIFIC)                    
130                     .decodeOptionalString(key_hash, ASN1Tag.OCTET_STRING, 2, ASN1Tag.CONSTRUCTED | ASN1Tag.CONTEXT_SPECIFIC)
131                     .decode(produced_at)
132                     .decodeList(m_responses)
133                     .decodeOptional(extensions, (cast(ASN1Tag)1), ASN1Tag.CONSTRUCTED | ASN1Tag.CONTEXT_SPECIFIC);
134             
135             if (certs.empty)
136             {
137                 if (auto cert = trusted_roots.findCert(name, Vector!ubyte()))
138                     certs.pushBack(cert);
139                 else
140                     throw new Exception("Could not find certificate that signed OCSP response");
141             }
142             
143             checkSignature(tbs_bits, sig_algo, signature, trusted_roots, certs);
144         }
145         
146         response_outer.endCons();
147     }
148 
149     CertificateStatusCode statusFor(in X509Certificate issuer,
150                                     in X509Certificate subject) const
151     {
152         //logTrace("Responses: ", m_responses.length);
153         foreach (response; m_responses[])
154         {
155             if (response.certid().isIdFor(issuer, subject))
156             {
157                 X509Time current_time = X509Time(Clock.currTime(UTC()));
158                 
159                 if (response.certStatus() == 1)
160                     return CertificateStatusCode.CERT_IS_REVOKED;
161                 
162                 if (response.thisUpdate() > current_time)
163                     return CertificateStatusCode.OCSP_NOT_YET_VALID;
164                 
165                 if (response.nextUpdate().timeIsSet() && current_time > response.nextUpdate())
166                     return CertificateStatusCode.OCSP_HAS_EXPIRED;
167                 
168                 if (response.certStatus() == 0)
169                     return CertificateStatusCode.OCSP_RESPONSE_GOOD;
170                 else
171                     return CertificateStatusCode.OCSP_BAD_STATUS;
172             }
173         }
174         
175         return CertificateStatusCode.OCSP_CERT_NOT_LISTED;
176     }
177 
178     @property bool empty() {
179         return m_responses.length == 0;
180     }
181 private:
182     Vector!( SingleResponse ) m_responses;
183 }
184 
185 
186 void decodeOptionalList(ref BERDecoder ber,
187                         ASN1Tag tag,
188                         ref Vector!X509Certificate output)
189 {
190     BERObject obj = ber.getNextObject();
191     
192     if (obj.type_tag != tag || obj.class_tag != (ASN1Tag.CONTEXT_SPECIFIC | ASN1Tag.CONSTRUCTED))
193     {
194         ber.pushBack(obj);
195         return;
196     }
197     
198     BERDecoder list = BERDecoder(obj.value);
199     
200     while (list.moreItems())
201     {
202         BERObject certbits = list.getNextObject();
203         X509Certificate cert = X509Certificate(unlock(certbits.value));
204         output.pushBack(cert);
205     }
206 }
207 
208 /// Does not use trusted roots
209 /// Throws if not trusted
210 void checkSignature(ALLOC)(auto const ref Vector!(ubyte, ALLOC) tbs_response,
211                                const AlgorithmIdentifier sig_algo,
212                                const ref Vector!ubyte signature,
213                                const X509Certificate cert)
214 {
215     Unique!PublicKey pub_key = cert.subjectPublicKey();
216     
217     const Vector!string sig_info = splitter(OIDS.lookup(sig_algo.oid), '/');
218     
219     if (sig_info.length != 2 || sig_info[0] != pub_key.algoName)
220         throw new Exception("Information in OCSP response does not match cert");
221 
222     string padding = sig_info[1];
223     SignatureFormat format = (pub_key.messageParts() >= 2) ? DER_SEQUENCE : IEEE_1363;
224     
225     PKVerifier verifier = PKVerifier(*pub_key, padding, format);
226     if (!verifier.verifyMessage(putInSequence(tbs_response), signature))
227         throw new Exception("Signature on OCSP response does not verify");
228 }
229 
230 /// Iterates over trusted roots certificate store
231 /// throws if not trusted
232 void checkSignature(ALLOC)(auto const ref Vector!(ubyte, ALLOC) tbs_response,
233                                  const AlgorithmIdentifier sig_algo,
234                                  const ref Vector!ubyte signature,
235                                  const CertificateStore trusted_roots,
236                                  const ref Vector!X509Certificate certs)
237 {
238     if (certs.length < 1)
239         throw new InvalidArgument("Short cert chain for checkSignature");
240     
241     if (trusted_roots.certificateKnown(certs[0]))
242         return checkSignature(tbs_response, sig_algo, signature, certs[0]);
243     
244     // Otherwise attempt to chain the signing cert to a trust root
245     
246     if (!certs[0].allowedUsage("PKIX.OCSPSigning"))
247         throw new Exception("OCSP response cert does not allow OCSP signing");
248     
249     auto result = x509PathValidate(certs, PathValidationRestrictions(), trusted_roots);
250     
251     if (!result.successfulValidation())
252         throw new Exception("Certificate validation failure: " ~ result.resultString());
253     
254     if (!trusted_roots.certificateKnown(result.trustRoot())) // not needed anymore?
255         throw new Exception("Certificate chain roots in unknown/untrusted CA");
256         
257     checkSignature(tbs_response, sig_algo, signature, result.certPath()[0]);
258 }
259 
260 //version(Have_vibe_d) import vibe.core.concurrency : Tid, send; else 
261 import std.concurrency : Mutex;
262 /// Checks the certificate online
263 struct OnlineCheck {
264 	shared(Mutex) mtx;
265 	shared(size_t) id;
266 	shared(OCSPResponse*) resp;
267 	shared(const(X509Certificate)*) issuer;
268 	shared(const(X509Certificate)*) subject;
269 	shared(const(CertificateStore)*) trusted_roots;
270 
271 	void run() {
272 	    /// TODO: Test OCSP with correct BER Decoding
273 	    logTrace("Checking OCSP online");
274 		string responder_url;
275 		synchronized(mtx) {
276 			if (!issuer) {
277 				*cast(OCSPResponse*)resp = OCSPResponse.init;
278 				return;
279 			}
280 			responder_url = (*cast(X509Certificate*)issuer).ocspResponder();
281 		}
282 	    logTrace("Responder url: ", responder_url.length);
283 
284 	    if (responder_url.length == 0) {
285 	        logTrace("Aborting OCSP for ID#", id.to!string);
286 	        synchronized(mtx)
287 				*cast(OCSPResponse*)resp = OCSPResponse.init;
288 	        return;
289 	    }
290 
291 	    OCSPRequest req = OCSPRequest(*cast(X509Certificate*)issuer, *cast(X509Certificate*)subject);
292 	    
293 	    logTrace("POST_sync");
294 	    HTTPResponse res = POST_sync(responder_url, "application/ocsp-request", req.BER_encode());
295 	    
296 	    res.throwUnlessOk();
297 	    
298 	    // Check the MIME type?
299 		synchronized(mtx)
300 	    	*cast(OCSPResponse*)resp = OCSPResponse(*(cast(CertificateStore*)trusted_roots), res._body());
301 	}
302 }