blob: 3ee6a26eb836cc92406b8320b248fd64c17165b3 [file] [log] [blame]
Paul Kehrer732cf642018-08-15 18:04:28 -05001# This file is dual licensed under the terms of the Apache License, Version
2# 2.0, and the BSD License. See the LICENSE file in the root of this repository
3# for complete details.
4
5from __future__ import absolute_import, division, print_function
6
Paul Kehrer002fa752018-08-30 10:41:32 -04007import base64
Paul Kehrera07de312018-10-02 07:54:31 +08008import datetime
Paul Kehrer732cf642018-08-15 18:04:28 -05009import os
10
11import pytest
12
Paul Kehrer002fa752018-08-30 10:41:32 -040013from cryptography import x509
Paul Kehrer732cf642018-08-15 18:04:28 -050014from cryptography.exceptions import UnsupportedAlgorithm
15from cryptography.hazmat.primitives import hashes, serialization
Paul Kehrera07de312018-10-02 07:54:31 +080016from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
Paul Kehrer732cf642018-08-15 18:04:28 -050017from cryptography.x509 import ocsp
18
Paul Kehrer002fa752018-08-30 10:41:32 -040019from .test_x509 import _load_cert
Paul Kehrer732cf642018-08-15 18:04:28 -050020from ..utils import load_vectors_from_file
21
22
23def _load_data(filename, loader):
24 return load_vectors_from_file(
25 filename=filename,
26 loader=lambda data: loader(data.read()),
27 mode="rb"
28 )
29
30
Paul Kehrer002fa752018-08-30 10:41:32 -040031def _cert_and_issuer():
32 from cryptography.hazmat.backends.openssl.backend import backend
33 cert = _load_cert(
34 os.path.join("x509", "cryptography.io.pem"),
35 x509.load_pem_x509_certificate,
36 backend
37 )
38 issuer = _load_cert(
39 os.path.join("x509", "rapidssl_sha256_ca_g3.pem"),
40 x509.load_pem_x509_certificate,
41 backend
42 )
43 return cert, issuer
44
45
Paul Kehrer732cf642018-08-15 18:04:28 -050046class TestOCSPRequest(object):
47 def test_bad_request(self):
48 with pytest.raises(ValueError):
49 ocsp.load_der_ocsp_request(b"invalid")
50
Paul Kehrer0f629bb2018-08-31 10:47:56 -040051 def test_load_request(self):
Paul Kehrer732cf642018-08-15 18:04:28 -050052 req = _load_data(
53 os.path.join("x509", "ocsp", "req-sha1.der"),
54 ocsp.load_der_ocsp_request,
55 )
Paul Kehrer0f629bb2018-08-31 10:47:56 -040056 assert req.issuer_name_hash == (b"8\xcaF\x8c\x07D\x8d\xf4\x81\x96"
57 b"\xc7mmLpQ\x9e`\xa7\xbd")
58 assert req.issuer_key_hash == (b"yu\xbb\x84:\xcb,\xdez\t\xbe1"
59 b"\x1bC\xbc\x1c*MSX")
60 assert isinstance(req.hash_algorithm, hashes.SHA1)
61 assert req.serial_number == int(
Paul Kehrer732cf642018-08-15 18:04:28 -050062 "98D9E5C0B4C373552DF77C5D0F1EB5128E4945F9", 16
63 )
Paul Kehrer09403102018-09-09 21:57:21 -050064 assert len(req.extensions) == 0
65
66 def test_load_request_with_extensions(self):
67 req = _load_data(
68 os.path.join("x509", "ocsp", "req-ext-nonce.der"),
69 ocsp.load_der_ocsp_request,
70 )
71 assert len(req.extensions) == 1
72 ext = req.extensions[0]
73 assert ext.critical is False
74 assert ext.value == x509.OCSPNonce(
75 b"\x04\x10{\x80Z\x1d7&\xb8\xb8OH\xd2\xf8\xbf\xd7-\xfd"
76 )
Paul Kehrer732cf642018-08-15 18:04:28 -050077
Paul Kehrer0f629bb2018-08-31 10:47:56 -040078 def test_load_request_two_requests(self):
79 with pytest.raises(NotImplementedError):
80 _load_data(
81 os.path.join("x509", "ocsp", "req-multi-sha1.der"),
82 ocsp.load_der_ocsp_request,
83 )
Paul Kehrer732cf642018-08-15 18:04:28 -050084
85 def test_invalid_hash_algorithm(self):
86 req = _load_data(
87 os.path.join("x509", "ocsp", "req-invalid-hash-alg.der"),
88 ocsp.load_der_ocsp_request,
89 )
90 with pytest.raises(UnsupportedAlgorithm):
Paul Kehrer0f629bb2018-08-31 10:47:56 -040091 req.hash_algorithm
Paul Kehrer732cf642018-08-15 18:04:28 -050092
93 def test_serialize_request(self):
94 req_bytes = load_vectors_from_file(
95 filename=os.path.join("x509", "ocsp", "req-sha1.der"),
96 loader=lambda data: data.read(),
97 mode="rb"
98 )
99 req = ocsp.load_der_ocsp_request(req_bytes)
100 assert req.public_bytes(serialization.Encoding.DER) == req_bytes
101
102 def test_invalid_serialize_encoding(self):
103 req = _load_data(
104 os.path.join("x509", "ocsp", "req-sha1.der"),
105 ocsp.load_der_ocsp_request,
106 )
107 with pytest.raises(ValueError):
108 req.public_bytes("invalid")
109 with pytest.raises(ValueError):
110 req.public_bytes(serialization.Encoding.PEM)
Paul Kehrer002fa752018-08-30 10:41:32 -0400111
112
113class TestOCSPRequestBuilder(object):
Paul Kehrer0f629bb2018-08-31 10:47:56 -0400114 def test_add_two_certs(self):
115 cert, issuer = _cert_and_issuer()
116 builder = ocsp.OCSPRequestBuilder()
117 builder = builder.add_certificate(cert, issuer, hashes.SHA1())
118 with pytest.raises(ValueError):
119 builder.add_certificate(cert, issuer, hashes.SHA1())
120
Paul Kehrer002fa752018-08-30 10:41:32 -0400121 def test_create_ocsp_request_no_req(self):
122 builder = ocsp.OCSPRequestBuilder()
123 with pytest.raises(ValueError):
124 builder.build()
125
126 def test_create_ocsp_request_invalid_alg(self):
127 cert, issuer = _cert_and_issuer()
128 builder = ocsp.OCSPRequestBuilder()
129 with pytest.raises(ValueError):
Paul Kehrer0f629bb2018-08-31 10:47:56 -0400130 builder.add_certificate(cert, issuer, hashes.MD5())
Paul Kehrer002fa752018-08-30 10:41:32 -0400131
Paul Kehrer0c075802018-10-07 10:10:09 +0800132 def test_add_extension_twice(self):
133 builder = ocsp.OCSPRequestBuilder()
134 builder = builder.add_extension(x509.OCSPNonce(b"123"), False)
135 with pytest.raises(ValueError):
136 builder.add_extension(x509.OCSPNonce(b"123"), False)
137
138 def test_add_invalid_extension(self):
139 builder = ocsp.OCSPRequestBuilder()
140 with pytest.raises(TypeError):
141 builder.add_extension("notanext", False)
142
Paul Kehrer002fa752018-08-30 10:41:32 -0400143 def test_create_ocsp_request_invalid_cert(self):
144 cert, issuer = _cert_and_issuer()
145 builder = ocsp.OCSPRequestBuilder()
146 with pytest.raises(TypeError):
Paul Kehrer0f629bb2018-08-31 10:47:56 -0400147 builder.add_certificate(b"notacert", issuer, hashes.SHA1())
Paul Kehrer002fa752018-08-30 10:41:32 -0400148
149 with pytest.raises(TypeError):
Paul Kehrer0f629bb2018-08-31 10:47:56 -0400150 builder.add_certificate(cert, b"notacert", hashes.SHA1())
Paul Kehrer002fa752018-08-30 10:41:32 -0400151
152 def test_create_ocsp_request(self):
153 cert, issuer = _cert_and_issuer()
154 builder = ocsp.OCSPRequestBuilder()
Paul Kehrer0f629bb2018-08-31 10:47:56 -0400155 builder = builder.add_certificate(cert, issuer, hashes.SHA1())
Paul Kehrer002fa752018-08-30 10:41:32 -0400156 req = builder.build()
157 serialized = req.public_bytes(serialization.Encoding.DER)
158 assert serialized == base64.b64decode(
159 b"MEMwQTA/MD0wOzAJBgUrDgMCGgUABBRAC0Z68eay0wmDug1gfn5ZN0gkxAQUw5zz"
160 b"/NNGCDS7zkZ/oHxb8+IIy1kCAj8g"
161 )
Paul Kehrera07de312018-10-02 07:54:31 +0800162
Paul Kehrer0c075802018-10-07 10:10:09 +0800163 @pytest.mark.parametrize(
164 ("ext", "critical"),
165 [
166 [x509.OCSPNonce(b"0000"), False],
167 [x509.OCSPNonce(b"\x00\x01\x02"), True],
168 ]
169 )
170 def test_create_ocsp_request_with_extension(self, ext, critical):
171 cert, issuer = _cert_and_issuer()
172 builder = ocsp.OCSPRequestBuilder()
173 builder = builder.add_certificate(
174 cert, issuer, hashes.SHA1()
175 ).add_extension(
176 ext, critical
177 )
178 req = builder.build()
179 assert len(req.extensions) == 1
180 assert req.extensions[0].value == ext
181 assert req.extensions[0].oid == ext.oid
182 assert req.extensions[0].critical is critical
183
Paul Kehrera07de312018-10-02 07:54:31 +0800184
185class TestOCSPResponse(object):
186 def test_bad_response(self):
187 with pytest.raises(ValueError):
188 ocsp.load_der_ocsp_response(b"invalid")
189
190 def test_load_response(self):
191 resp = _load_data(
192 os.path.join("x509", "ocsp", "resp-sha256.der"),
193 ocsp.load_der_ocsp_response,
194 )
195 from cryptography.hazmat.backends.openssl.backend import backend
196 issuer = _load_cert(
197 os.path.join("x509", "letsencryptx3.pem"),
198 x509.load_pem_x509_certificate,
199 backend
200 )
201 assert resp.response_status == ocsp.OCSPResponseStatus.SUCCESSFUL
202 assert (resp.signature_algorithm_oid ==
203 x509.SignatureAlgorithmOID.RSA_WITH_SHA256)
204 assert resp.signature == base64.b64decode(
205 b"I9KUlyLV/2LbNCVu1BQphxdNlU/jBzXsPYVscPjW5E93pCrSO84GkIWoOJtqsnt"
206 b"78DLcQPnF3W24NXGzSGKlSWfXIsyoXCxnBm0mIbD5ZMnKyXEnqSR33Z9He/A+ML"
207 b"A8gbrDUipGNPosesenkKUnOtFIzEGv29hV5E6AMP2ORPVsVlTAZegPJFbbVIWc0"
208 b"rZGFCXKxijDxtUtgWzBhpBAI50JbPHi+IVuaOe4aDJLYgZ0BIBNa6bDI+rScyoy"
209 b"5U0DToV7SZn6CoJ3U19X7BHdYn6TLX0xi43eXuzBGzdHnSzmsc7r/DvkAKJm3vb"
210 b"dVECXqe/gFlXJUBcZ25jhs70MUA=="
211 )
212 assert resp.tbs_response_bytes == base64.b64decode(
213 b"MIHWoUwwSjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzA"
214 b"hBgNVBAMTGkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzGA8yMDE4MDgzMDExMT"
215 b"UwMFowdTBzMEswCQYFKw4DAhoFAAQUfuZq53Kas/z4oiBkbBahLWBxCF0EFKhKa"
216 b"mMEfd265tE5t6ZFZe/zqOyhAhIDHHh6fckClQB7xfIiCztSevCAABgPMjAxODA4"
217 b"MzAxMTAwMDBaoBEYDzIwMTgwOTA2MTEwMDAwWg=="
218 )
219 issuer.public_key().verify(
220 resp.signature,
221 resp.tbs_response_bytes,
222 PKCS1v15(),
223 hashes.SHA256()
224 )
225 assert resp.certificates == []
226 assert resp.responder_key_hash is None
227 assert resp.responder_name == issuer.subject
228 assert resp.produced_at == datetime.datetime(2018, 8, 30, 11, 15)
229 assert resp.certificate_status == ocsp.OCSPCertStatus.GOOD
230 assert resp.revocation_time is None
231 assert resp.revocation_reason is None
232 assert resp.this_update == datetime.datetime(2018, 8, 30, 11, 0)
233 assert resp.next_update == datetime.datetime(2018, 9, 6, 11, 0)
234 assert resp.issuer_key_hash == (
235 b'\xa8Jjc\x04}\xdd\xba\xe6\xd19\xb7\xa6Ee\xef\xf3\xa8\xec\xa1'
236 )
237 assert resp.issuer_name_hash == (
238 b'~\xe6j\xe7r\x9a\xb3\xfc\xf8\xa2 dl\x16\xa1-`q\x08]'
239 )
240 assert isinstance(resp.hash_algorithm, hashes.SHA1)
241 assert resp.serial_number == 271024907440004808294641238224534273948400
Paul Kehrerb8db6682018-10-07 03:44:30 +0800242 assert len(resp.extensions) == 0
Paul Kehrera07de312018-10-02 07:54:31 +0800243
244 def test_load_unauthorized(self):
245 resp = _load_data(
246 os.path.join("x509", "ocsp", "resp-unauthorized.der"),
247 ocsp.load_der_ocsp_response,
248 )
249 assert resp.response_status == ocsp.OCSPResponseStatus.UNAUTHORIZED
250 with pytest.raises(ValueError):
251 assert resp.signature_algorithm_oid
252 with pytest.raises(ValueError):
253 assert resp.signature
254 with pytest.raises(ValueError):
255 assert resp.tbs_response_bytes
256 with pytest.raises(ValueError):
257 assert resp.certificates
258 with pytest.raises(ValueError):
259 assert resp.responder_key_hash
260 with pytest.raises(ValueError):
261 assert resp.responder_name
262 with pytest.raises(ValueError):
263 assert resp.produced_at
264 with pytest.raises(ValueError):
265 assert resp.certificate_status
266 with pytest.raises(ValueError):
267 assert resp.revocation_time
268 with pytest.raises(ValueError):
269 assert resp.revocation_reason
270 with pytest.raises(ValueError):
271 assert resp.this_update
272 with pytest.raises(ValueError):
273 assert resp.next_update
274 with pytest.raises(ValueError):
275 assert resp.issuer_key_hash
276 with pytest.raises(ValueError):
277 assert resp.issuer_name_hash
278 with pytest.raises(ValueError):
279 assert resp.hash_algorithm
280 with pytest.raises(ValueError):
281 assert resp.serial_number
Paul Kehrerb8db6682018-10-07 03:44:30 +0800282 with pytest.raises(ValueError):
283 assert resp.extensions
Paul Kehrera07de312018-10-02 07:54:31 +0800284
285 def test_load_revoked(self):
286 resp = _load_data(
287 os.path.join("x509", "ocsp", "resp-revoked.der"),
288 ocsp.load_der_ocsp_response,
289 )
290 assert resp.certificate_status == ocsp.OCSPCertStatus.REVOKED
291 assert resp.revocation_time == datetime.datetime(
292 2016, 9, 2, 21, 28, 48
293 )
294 assert resp.revocation_reason is None
295
296 def test_load_delegate_unknown_cert(self):
297 resp = _load_data(
298 os.path.join("x509", "ocsp", "resp-delegate-unknown-cert.der"),
299 ocsp.load_der_ocsp_response,
300 )
301 assert len(resp.certificates) == 1
302 assert isinstance(resp.certificates[0], x509.Certificate)
303 assert resp.certificate_status == ocsp.OCSPCertStatus.UNKNOWN
304
305 def test_load_responder_key_hash(self):
306 resp = _load_data(
307 os.path.join("x509", "ocsp", "resp-responder-key-hash.der"),
308 ocsp.load_der_ocsp_response,
309 )
310 assert resp.responder_name is None
311 assert resp.responder_key_hash == (
312 b'\x0f\x80a\x1c\x821a\xd5/(\xe7\x8dF8\xb4,\xe1\xc6\xd9\xe2'
313 )
314
315 def test_load_revoked_reason(self):
316 resp = _load_data(
317 os.path.join("x509", "ocsp", "resp-revoked-reason.der"),
318 ocsp.load_der_ocsp_response,
319 )
320 assert resp.revocation_reason is x509.ReasonFlags.superseded
Paul Kehrerb8db6682018-10-07 03:44:30 +0800321
Paul Kehrera9b4f862018-10-24 08:58:07 +0800322 def test_load_revoked_no_next_update(self):
323 resp = _load_data(
324 os.path.join("x509", "ocsp", "resp-revoked-no-next-update.der"),
325 ocsp.load_der_ocsp_response,
326 )
327 assert resp.serial_number == 16160
328 assert resp.next_update is None
329
Paul Kehrerb8db6682018-10-07 03:44:30 +0800330 def test_response_extensions(self):
331 resp = _load_data(
332 os.path.join("x509", "ocsp", "resp-revoked-reason.der"),
333 ocsp.load_der_ocsp_response,
334 )
335 assert len(resp.extensions) == 1
336 ext = resp.extensions[0]
337 assert ext.critical is False
338 assert ext.value == x509.OCSPNonce(
339 b'\x04\x105\x957\x9fa\x03\x83\x87\x89rW\x8f\xae\x99\xf7"'
340 )
Paul Kehrer788b8592018-10-07 11:07:14 +0800341
342 def test_serialize_reponse(self):
343 resp_bytes = load_vectors_from_file(
344 filename=os.path.join("x509", "ocsp", "resp-revoked.der"),
345 loader=lambda data: data.read(),
346 mode="rb"
347 )
348 resp = ocsp.load_der_ocsp_response(resp_bytes)
349 assert resp.public_bytes(serialization.Encoding.DER) == resp_bytes
350
351 def test_invalid_serialize_encoding(self):
352 resp = _load_data(
353 os.path.join("x509", "ocsp", "resp-revoked.der"),
354 ocsp.load_der_ocsp_response,
355 )
356 with pytest.raises(ValueError):
357 resp.public_bytes("invalid")
358 with pytest.raises(ValueError):
359 resp.public_bytes(serialization.Encoding.PEM)