blob: fad48dab84be6e7683d94a41c8947810973577ee [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 Kehrere617c5a2018-10-29 05:36:34 +080016from cryptography.hazmat.primitives.asymmetric import ec
Paul Kehrera07de312018-10-02 07:54:31 +080017from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
Paul Kehrer732cf642018-08-15 18:04:28 -050018from cryptography.x509 import ocsp
19
Paul Kehrer002fa752018-08-30 10:41:32 -040020from .test_x509 import _load_cert
Paul Kehrere617c5a2018-10-29 05:36:34 +080021from ..hazmat.primitives.fixtures_ec import EC_KEY_SECP256R1
Paul Kehrer732cf642018-08-15 18:04:28 -050022from ..utils import load_vectors_from_file
23
24
25def _load_data(filename, loader):
26 return load_vectors_from_file(
27 filename=filename,
28 loader=lambda data: loader(data.read()),
29 mode="rb"
30 )
31
32
Paul Kehrer002fa752018-08-30 10:41:32 -040033def _cert_and_issuer():
34 from cryptography.hazmat.backends.openssl.backend import backend
35 cert = _load_cert(
36 os.path.join("x509", "cryptography.io.pem"),
37 x509.load_pem_x509_certificate,
38 backend
39 )
40 issuer = _load_cert(
41 os.path.join("x509", "rapidssl_sha256_ca_g3.pem"),
42 x509.load_pem_x509_certificate,
43 backend
44 )
45 return cert, issuer
46
47
Paul Kehrere617c5a2018-10-29 05:36:34 +080048def _generate_root():
49 from cryptography.hazmat.backends.openssl.backend import backend
50
51 private_key = EC_KEY_SECP256R1.private_key(backend)
52 subject = x509.Name([
53 x509.NameAttribute(x509.NameOID.COUNTRY_NAME, u'US'),
54 x509.NameAttribute(x509.NameOID.COMMON_NAME, u'Cryptography CA'),
55 ])
56
57 builder = x509.CertificateBuilder().serial_number(
58 123456789
59 ).issuer_name(
60 subject
61 ).subject_name(
62 subject
63 ).public_key(
64 private_key.public_key()
65 ).not_valid_before(
66 datetime.datetime.now()
67 ).not_valid_after(
68 datetime.datetime.now() + datetime.timedelta(days=3650)
69 )
70
71 cert = builder.sign(private_key, hashes.SHA256(), backend)
72 return cert, private_key
73
74
Paul Kehrer732cf642018-08-15 18:04:28 -050075class TestOCSPRequest(object):
76 def test_bad_request(self):
77 with pytest.raises(ValueError):
78 ocsp.load_der_ocsp_request(b"invalid")
79
Paul Kehrer0f629bb2018-08-31 10:47:56 -040080 def test_load_request(self):
Paul Kehrer732cf642018-08-15 18:04:28 -050081 req = _load_data(
82 os.path.join("x509", "ocsp", "req-sha1.der"),
83 ocsp.load_der_ocsp_request,
84 )
Paul Kehrer0f629bb2018-08-31 10:47:56 -040085 assert req.issuer_name_hash == (b"8\xcaF\x8c\x07D\x8d\xf4\x81\x96"
86 b"\xc7mmLpQ\x9e`\xa7\xbd")
87 assert req.issuer_key_hash == (b"yu\xbb\x84:\xcb,\xdez\t\xbe1"
88 b"\x1bC\xbc\x1c*MSX")
89 assert isinstance(req.hash_algorithm, hashes.SHA1)
90 assert req.serial_number == int(
Paul Kehrer732cf642018-08-15 18:04:28 -050091 "98D9E5C0B4C373552DF77C5D0F1EB5128E4945F9", 16
92 )
Paul Kehrer09403102018-09-09 21:57:21 -050093 assert len(req.extensions) == 0
94
95 def test_load_request_with_extensions(self):
96 req = _load_data(
97 os.path.join("x509", "ocsp", "req-ext-nonce.der"),
98 ocsp.load_der_ocsp_request,
99 )
100 assert len(req.extensions) == 1
101 ext = req.extensions[0]
102 assert ext.critical is False
103 assert ext.value == x509.OCSPNonce(
104 b"\x04\x10{\x80Z\x1d7&\xb8\xb8OH\xd2\xf8\xbf\xd7-\xfd"
105 )
Paul Kehrer732cf642018-08-15 18:04:28 -0500106
Paul Kehrer0f629bb2018-08-31 10:47:56 -0400107 def test_load_request_two_requests(self):
108 with pytest.raises(NotImplementedError):
109 _load_data(
110 os.path.join("x509", "ocsp", "req-multi-sha1.der"),
111 ocsp.load_der_ocsp_request,
112 )
Paul Kehrer732cf642018-08-15 18:04:28 -0500113
114 def test_invalid_hash_algorithm(self):
115 req = _load_data(
116 os.path.join("x509", "ocsp", "req-invalid-hash-alg.der"),
117 ocsp.load_der_ocsp_request,
118 )
119 with pytest.raises(UnsupportedAlgorithm):
Paul Kehrer0f629bb2018-08-31 10:47:56 -0400120 req.hash_algorithm
Paul Kehrer732cf642018-08-15 18:04:28 -0500121
122 def test_serialize_request(self):
123 req_bytes = load_vectors_from_file(
124 filename=os.path.join("x509", "ocsp", "req-sha1.der"),
125 loader=lambda data: data.read(),
126 mode="rb"
127 )
128 req = ocsp.load_der_ocsp_request(req_bytes)
129 assert req.public_bytes(serialization.Encoding.DER) == req_bytes
130
131 def test_invalid_serialize_encoding(self):
132 req = _load_data(
133 os.path.join("x509", "ocsp", "req-sha1.der"),
134 ocsp.load_der_ocsp_request,
135 )
136 with pytest.raises(ValueError):
137 req.public_bytes("invalid")
138 with pytest.raises(ValueError):
139 req.public_bytes(serialization.Encoding.PEM)
Paul Kehrer002fa752018-08-30 10:41:32 -0400140
141
142class TestOCSPRequestBuilder(object):
Paul Kehrer0f629bb2018-08-31 10:47:56 -0400143 def test_add_two_certs(self):
144 cert, issuer = _cert_and_issuer()
145 builder = ocsp.OCSPRequestBuilder()
146 builder = builder.add_certificate(cert, issuer, hashes.SHA1())
147 with pytest.raises(ValueError):
148 builder.add_certificate(cert, issuer, hashes.SHA1())
149
Paul Kehrer002fa752018-08-30 10:41:32 -0400150 def test_create_ocsp_request_no_req(self):
151 builder = ocsp.OCSPRequestBuilder()
152 with pytest.raises(ValueError):
153 builder.build()
154
155 def test_create_ocsp_request_invalid_alg(self):
156 cert, issuer = _cert_and_issuer()
157 builder = ocsp.OCSPRequestBuilder()
158 with pytest.raises(ValueError):
Paul Kehrer0f629bb2018-08-31 10:47:56 -0400159 builder.add_certificate(cert, issuer, hashes.MD5())
Paul Kehrer002fa752018-08-30 10:41:32 -0400160
Paul Kehrer0c075802018-10-07 10:10:09 +0800161 def test_add_extension_twice(self):
162 builder = ocsp.OCSPRequestBuilder()
163 builder = builder.add_extension(x509.OCSPNonce(b"123"), False)
164 with pytest.raises(ValueError):
165 builder.add_extension(x509.OCSPNonce(b"123"), False)
166
167 def test_add_invalid_extension(self):
168 builder = ocsp.OCSPRequestBuilder()
169 with pytest.raises(TypeError):
170 builder.add_extension("notanext", False)
171
Paul Kehrer002fa752018-08-30 10:41:32 -0400172 def test_create_ocsp_request_invalid_cert(self):
173 cert, issuer = _cert_and_issuer()
174 builder = ocsp.OCSPRequestBuilder()
175 with pytest.raises(TypeError):
Paul Kehrer0f629bb2018-08-31 10:47:56 -0400176 builder.add_certificate(b"notacert", issuer, hashes.SHA1())
Paul Kehrer002fa752018-08-30 10:41:32 -0400177
178 with pytest.raises(TypeError):
Paul Kehrer0f629bb2018-08-31 10:47:56 -0400179 builder.add_certificate(cert, b"notacert", hashes.SHA1())
Paul Kehrer002fa752018-08-30 10:41:32 -0400180
181 def test_create_ocsp_request(self):
182 cert, issuer = _cert_and_issuer()
183 builder = ocsp.OCSPRequestBuilder()
Paul Kehrer0f629bb2018-08-31 10:47:56 -0400184 builder = builder.add_certificate(cert, issuer, hashes.SHA1())
Paul Kehrer002fa752018-08-30 10:41:32 -0400185 req = builder.build()
186 serialized = req.public_bytes(serialization.Encoding.DER)
187 assert serialized == base64.b64decode(
188 b"MEMwQTA/MD0wOzAJBgUrDgMCGgUABBRAC0Z68eay0wmDug1gfn5ZN0gkxAQUw5zz"
189 b"/NNGCDS7zkZ/oHxb8+IIy1kCAj8g"
190 )
Paul Kehrera07de312018-10-02 07:54:31 +0800191
Paul Kehrer0c075802018-10-07 10:10:09 +0800192 @pytest.mark.parametrize(
193 ("ext", "critical"),
194 [
195 [x509.OCSPNonce(b"0000"), False],
196 [x509.OCSPNonce(b"\x00\x01\x02"), True],
197 ]
198 )
199 def test_create_ocsp_request_with_extension(self, ext, critical):
200 cert, issuer = _cert_and_issuer()
201 builder = ocsp.OCSPRequestBuilder()
202 builder = builder.add_certificate(
203 cert, issuer, hashes.SHA1()
204 ).add_extension(
205 ext, critical
206 )
207 req = builder.build()
208 assert len(req.extensions) == 1
209 assert req.extensions[0].value == ext
210 assert req.extensions[0].oid == ext.oid
211 assert req.extensions[0].critical is critical
212
Paul Kehrera07de312018-10-02 07:54:31 +0800213
Paul Kehrere617c5a2018-10-29 05:36:34 +0800214class TestOCSPResponseBuilder(object):
215 def test_add_response_twice(self):
216 cert, issuer = _cert_and_issuer()
217 time = datetime.datetime.now()
218 builder = ocsp.OCSPResponseBuilder()
219 builder = builder.add_response(
220 cert, issuer, hashes.SHA256(), ocsp.OCSPCertStatus.GOOD, time,
221 time, None, None
222 )
223 with pytest.raises(ValueError):
224 builder.add_response(
225 cert, issuer, hashes.SHA256(), ocsp.OCSPCertStatus.GOOD, time,
226 time, None, None
227 )
228
229 def test_invalid_add_response(self):
230 cert, issuer = _cert_and_issuer()
231 time = datetime.datetime.utcnow()
232 reason = x509.ReasonFlags.cessation_of_operation
233 builder = ocsp.OCSPResponseBuilder()
234 with pytest.raises(TypeError):
235 builder.add_response(
236 'bad', issuer, hashes.SHA256(), ocsp.OCSPCertStatus.GOOD,
237 time, time, None, None
238 )
239 with pytest.raises(TypeError):
240 builder.add_response(
241 cert, 'bad', hashes.SHA256(), ocsp.OCSPCertStatus.GOOD,
242 time, time, None, None
243 )
244 with pytest.raises(ValueError):
245 builder.add_response(
246 cert, issuer, 'notahash', ocsp.OCSPCertStatus.GOOD,
247 time, time, None, None
248 )
249 with pytest.raises(TypeError):
250 builder.add_response(
251 cert, issuer, hashes.SHA256(), ocsp.OCSPCertStatus.GOOD,
252 'bad', time, None, None
253 )
254 with pytest.raises(TypeError):
255 builder.add_response(
256 cert, issuer, hashes.SHA256(), ocsp.OCSPCertStatus.GOOD,
257 time, 'bad', None, None
258 )
259
260 with pytest.raises(TypeError):
261 builder.add_response(
262 cert, issuer, hashes.SHA256(), 0, time, time, None, None
263 )
264 with pytest.raises(ValueError):
265 builder.add_response(
266 cert, issuer, hashes.SHA256(), ocsp.OCSPCertStatus.GOOD,
267 time, time, time, None
268 )
269 with pytest.raises(ValueError):
270 builder.add_response(
271 cert, issuer, hashes.SHA256(), ocsp.OCSPCertStatus.GOOD,
272 time, time, None, reason
273 )
274 with pytest.raises(TypeError):
275 builder.add_response(
276 cert, issuer, hashes.SHA256(), ocsp.OCSPCertStatus.REVOKED,
277 time, time, None, reason
278 )
279 with pytest.raises(TypeError):
280 builder.add_response(
281 cert, issuer, hashes.SHA256(), ocsp.OCSPCertStatus.REVOKED,
282 time, time, time, 0
283 )
284 with pytest.raises(ValueError):
285 builder.add_response(
286 cert, issuer, hashes.SHA256(), ocsp.OCSPCertStatus.REVOKED,
287 time, time, time - datetime.timedelta(days=36500), None
288 )
289
290 def test_invalid_certificates(self):
291 builder = ocsp.OCSPResponseBuilder()
292 with pytest.raises(ValueError):
293 builder.certificates([])
294 with pytest.raises(TypeError):
295 builder.certificates(['notacert'])
296 with pytest.raises(TypeError):
297 builder.certificates('invalid')
298
299 _, issuer = _cert_and_issuer()
300 builder = builder.certificates([issuer])
301 with pytest.raises(ValueError):
302 builder.certificates([issuer])
303
304 def test_invalid_responder_id(self):
305 builder = ocsp.OCSPResponseBuilder()
306 cert, _ = _cert_and_issuer()
307 with pytest.raises(TypeError):
308 builder.responder_id(ocsp.OCSPResponderEncoding.HASH, 'invalid')
309 with pytest.raises(TypeError):
310 builder.responder_id('notanenum', cert)
311
312 builder = builder.responder_id(ocsp.OCSPResponderEncoding.NAME, cert)
313 with pytest.raises(ValueError):
314 builder.responder_id(ocsp.OCSPResponderEncoding.NAME, cert)
315
316 def test_invalid_extension(self):
317 builder = ocsp.OCSPResponseBuilder()
318 with pytest.raises(TypeError):
319 builder.add_extension("notanextension", True)
320
321 def test_sign_no_response(self):
322 builder = ocsp.OCSPResponseBuilder()
323 root_cert, private_key = _generate_root()
324 builder = builder.responder_id(
325 ocsp.OCSPResponderEncoding.NAME, root_cert
326 )
327 with pytest.raises(ValueError):
328 builder.sign(private_key, hashes.SHA256())
329
330 def test_sign_no_responder_id(self):
331 builder = ocsp.OCSPResponseBuilder()
332 cert, issuer = _cert_and_issuer()
333 _, private_key = _generate_root()
334 current_time = datetime.datetime.utcnow().replace(microsecond=0)
335 this_update = current_time - datetime.timedelta(days=1)
336 next_update = this_update + datetime.timedelta(days=7)
337 builder = builder.add_response(
338 cert, issuer, hashes.SHA1(), ocsp.OCSPCertStatus.GOOD, this_update,
339 next_update, None, None
340 )
341 with pytest.raises(ValueError):
342 builder.sign(private_key, hashes.SHA256())
343
344 def test_sign_invalid_hash_algorithm(self):
345 builder = ocsp.OCSPResponseBuilder()
346 cert, issuer = _cert_and_issuer()
347 root_cert, private_key = _generate_root()
348 current_time = datetime.datetime.utcnow().replace(microsecond=0)
349 this_update = current_time - datetime.timedelta(days=1)
350 next_update = this_update + datetime.timedelta(days=7)
351 builder = builder.responder_id(
352 ocsp.OCSPResponderEncoding.NAME, root_cert
353 ).add_response(
354 cert, issuer, hashes.SHA1(), ocsp.OCSPCertStatus.GOOD, this_update,
355 next_update, None, None
356 )
357 with pytest.raises(TypeError):
358 builder.sign(private_key, 'notahash')
359
360 def test_sign_good_cert(self):
361 builder = ocsp.OCSPResponseBuilder()
362 cert, issuer = _cert_and_issuer()
363 root_cert, private_key = _generate_root()
364 current_time = datetime.datetime.utcnow().replace(microsecond=0)
365 this_update = current_time - datetime.timedelta(days=1)
366 next_update = this_update + datetime.timedelta(days=7)
367 builder = builder.responder_id(
368 ocsp.OCSPResponderEncoding.NAME, root_cert
369 ).add_response(
370 cert, issuer, hashes.SHA1(), ocsp.OCSPCertStatus.GOOD, this_update,
371 next_update, None, None
372 )
373 resp = builder.sign(private_key, hashes.SHA256())
374 assert resp.responder_name == root_cert.subject
375 assert resp.responder_key_hash is None
376 assert (current_time - resp.produced_at).total_seconds() < 10
377 assert (resp.signature_algorithm_oid ==
378 x509.SignatureAlgorithmOID.ECDSA_WITH_SHA256)
379 assert resp.certificate_status == ocsp.OCSPCertStatus.GOOD
380 assert resp.revocation_time is None
381 assert resp.revocation_reason is None
382 assert resp.this_update == this_update
383 assert resp.next_update == next_update
384 private_key.public_key().verify(
385 resp.signature, resp.tbs_response_bytes, ec.ECDSA(hashes.SHA256())
386 )
387
388 def test_sign_revoked_cert(self):
389 builder = ocsp.OCSPResponseBuilder()
390 cert, issuer = _cert_and_issuer()
391 root_cert, private_key = _generate_root()
392 current_time = datetime.datetime.utcnow().replace(microsecond=0)
393 this_update = current_time - datetime.timedelta(days=1)
394 next_update = this_update + datetime.timedelta(days=7)
395 revoked_date = this_update - datetime.timedelta(days=300)
396 builder = builder.responder_id(
397 ocsp.OCSPResponderEncoding.NAME, root_cert
398 ).add_response(
399 cert, issuer, hashes.SHA1(), ocsp.OCSPCertStatus.REVOKED,
400 this_update, next_update, revoked_date, None
401 )
402 resp = builder.sign(private_key, hashes.SHA256())
403 assert resp.certificate_status == ocsp.OCSPCertStatus.REVOKED
404 assert resp.revocation_time == revoked_date
405 assert resp.revocation_reason is None
406 assert resp.this_update == this_update
407 assert resp.next_update == next_update
408 private_key.public_key().verify(
409 resp.signature, resp.tbs_response_bytes, ec.ECDSA(hashes.SHA256())
410 )
411
412 def test_sign_with_appended_certs(self):
413 builder = ocsp.OCSPResponseBuilder()
414 cert, issuer = _cert_and_issuer()
415 root_cert, private_key = _generate_root()
416 current_time = datetime.datetime.utcnow().replace(microsecond=0)
417 this_update = current_time - datetime.timedelta(days=1)
418 next_update = this_update + datetime.timedelta(days=7)
419 builder = builder.responder_id(
420 ocsp.OCSPResponderEncoding.NAME, root_cert
421 ).add_response(
422 cert, issuer, hashes.SHA1(), ocsp.OCSPCertStatus.GOOD, this_update,
423 next_update, None, None
424 ).certificates(
425 [root_cert]
426 )
427 resp = builder.sign(private_key, hashes.SHA256())
428 assert resp.certificates == [root_cert]
429
430 def test_sign_revoked_no_next_update(self):
431 builder = ocsp.OCSPResponseBuilder()
432 cert, issuer = _cert_and_issuer()
433 root_cert, private_key = _generate_root()
434 current_time = datetime.datetime.utcnow().replace(microsecond=0)
435 this_update = current_time - datetime.timedelta(days=1)
436 revoked_date = this_update - datetime.timedelta(days=300)
437 builder = builder.responder_id(
438 ocsp.OCSPResponderEncoding.NAME, root_cert
439 ).add_response(
440 cert, issuer, hashes.SHA1(), ocsp.OCSPCertStatus.REVOKED,
441 this_update, None, revoked_date, None
442 )
443 resp = builder.sign(private_key, hashes.SHA256())
444 assert resp.certificate_status == ocsp.OCSPCertStatus.REVOKED
445 assert resp.revocation_time == revoked_date
446 assert resp.revocation_reason is None
447 assert resp.this_update == this_update
448 assert resp.next_update is None
449 private_key.public_key().verify(
450 resp.signature, resp.tbs_response_bytes, ec.ECDSA(hashes.SHA256())
451 )
452
453 def test_sign_revoked_with_reason(self):
454 builder = ocsp.OCSPResponseBuilder()
455 cert, issuer = _cert_and_issuer()
456 root_cert, private_key = _generate_root()
457 current_time = datetime.datetime.utcnow().replace(microsecond=0)
458 this_update = current_time - datetime.timedelta(days=1)
459 next_update = this_update + datetime.timedelta(days=7)
460 revoked_date = this_update - datetime.timedelta(days=300)
461 builder = builder.responder_id(
462 ocsp.OCSPResponderEncoding.NAME, root_cert
463 ).add_response(
464 cert, issuer, hashes.SHA1(), ocsp.OCSPCertStatus.REVOKED,
465 this_update, next_update, revoked_date,
466 x509.ReasonFlags.key_compromise
467 )
468 resp = builder.sign(private_key, hashes.SHA256())
469 assert resp.certificate_status == ocsp.OCSPCertStatus.REVOKED
470 assert resp.revocation_time == revoked_date
471 assert resp.revocation_reason is x509.ReasonFlags.key_compromise
472 assert resp.this_update == this_update
473 assert resp.next_update == next_update
474 private_key.public_key().verify(
475 resp.signature, resp.tbs_response_bytes, ec.ECDSA(hashes.SHA256())
476 )
477
478 def test_sign_responder_id_key_hash(self):
479 builder = ocsp.OCSPResponseBuilder()
480 cert, issuer = _cert_and_issuer()
481 root_cert, private_key = _generate_root()
482 current_time = datetime.datetime.utcnow().replace(microsecond=0)
483 this_update = current_time - datetime.timedelta(days=1)
484 next_update = this_update + datetime.timedelta(days=7)
485 builder = builder.responder_id(
486 ocsp.OCSPResponderEncoding.HASH, root_cert
487 ).add_response(
488 cert, issuer, hashes.SHA1(), ocsp.OCSPCertStatus.GOOD, this_update,
489 next_update, None, None
490 )
491 resp = builder.sign(private_key, hashes.SHA256())
492 assert resp.responder_name is None
493 assert resp.responder_key_hash == (
494 b'\x8ca\x94\xe0\x948\xed\x89\xd8\xd4N\x89p\t\xd6\xf9^_\xec}'
495 )
496 private_key.public_key().verify(
497 resp.signature, resp.tbs_response_bytes, ec.ECDSA(hashes.SHA256())
498 )
499
500 def test_invalid_sign_responder_cert_does_not_match_private_key(self):
501 builder = ocsp.OCSPResponseBuilder()
502 cert, issuer = _cert_and_issuer()
503 root_cert, private_key = _generate_root()
504 current_time = datetime.datetime.utcnow().replace(microsecond=0)
505 this_update = current_time - datetime.timedelta(days=1)
506 next_update = this_update + datetime.timedelta(days=7)
507 builder = builder.responder_id(
508 ocsp.OCSPResponderEncoding.HASH, root_cert
509 ).add_response(
510 cert, issuer, hashes.SHA1(), ocsp.OCSPCertStatus.GOOD, this_update,
511 next_update, None, None
512 )
513 from cryptography.hazmat.backends.openssl.backend import backend
514 diff_key = ec.generate_private_key(ec.SECP256R1(), backend)
515 with pytest.raises(ValueError):
516 builder.sign(diff_key, hashes.SHA256())
517
518 def test_sign_with_extension(self):
519 builder = ocsp.OCSPResponseBuilder()
520 cert, issuer = _cert_and_issuer()
521 root_cert, private_key = _generate_root()
522 current_time = datetime.datetime.utcnow().replace(microsecond=0)
523 this_update = current_time - datetime.timedelta(days=1)
524 next_update = this_update + datetime.timedelta(days=7)
525 builder = builder.responder_id(
526 ocsp.OCSPResponderEncoding.HASH, root_cert
527 ).add_response(
528 cert, issuer, hashes.SHA1(), ocsp.OCSPCertStatus.GOOD, this_update,
529 next_update, None, None
530 ).add_extension(
531 x509.OCSPNonce(b"012345"), False
532 )
533 resp = builder.sign(private_key, hashes.SHA256())
534 assert len(resp.extensions) == 1
535 assert resp.extensions[0].value == x509.OCSPNonce(b"012345")
536 assert resp.extensions[0].critical is False
537 private_key.public_key().verify(
538 resp.signature, resp.tbs_response_bytes, ec.ECDSA(hashes.SHA256())
539 )
540
541 @pytest.mark.parametrize(
542 ("status", "der"),
543 [
544 (ocsp.OCSPResponseStatus.MALFORMED_REQUEST, b"0\x03\n\x01\x01"),
545 (ocsp.OCSPResponseStatus.INTERNAL_ERROR, b"0\x03\n\x01\x02"),
546 (ocsp.OCSPResponseStatus.TRY_LATER, b"0\x03\n\x01\x03"),
547 (ocsp.OCSPResponseStatus.SIG_REQUIRED, b"0\x03\n\x01\x05"),
548 (ocsp.OCSPResponseStatus.UNAUTHORIZED, b"0\x03\n\x01\x06"),
549 ]
550 )
551 def test_build_non_successful_statuses(self, status, der):
552 resp = ocsp.OCSPResponseBuilder.build_unsuccessful(status)
553 assert resp.response_status is status
554 assert resp.public_bytes(serialization.Encoding.DER) == der
555
556 def test_invalid_build_not_a_status(self):
557 with pytest.raises(TypeError):
558 ocsp.OCSPResponseBuilder.build_unsuccessful("notastatus")
559
560 def test_invalid_build_successful_status(self):
561 with pytest.raises(ValueError):
562 ocsp.OCSPResponseBuilder.build_unsuccessful(
563 ocsp.OCSPResponseStatus.SUCCESSFUL
564 )
565
566
Paul Kehrera07de312018-10-02 07:54:31 +0800567class TestOCSPResponse(object):
568 def test_bad_response(self):
569 with pytest.raises(ValueError):
570 ocsp.load_der_ocsp_response(b"invalid")
571
572 def test_load_response(self):
573 resp = _load_data(
574 os.path.join("x509", "ocsp", "resp-sha256.der"),
575 ocsp.load_der_ocsp_response,
576 )
577 from cryptography.hazmat.backends.openssl.backend import backend
578 issuer = _load_cert(
579 os.path.join("x509", "letsencryptx3.pem"),
580 x509.load_pem_x509_certificate,
581 backend
582 )
583 assert resp.response_status == ocsp.OCSPResponseStatus.SUCCESSFUL
584 assert (resp.signature_algorithm_oid ==
585 x509.SignatureAlgorithmOID.RSA_WITH_SHA256)
586 assert resp.signature == base64.b64decode(
587 b"I9KUlyLV/2LbNCVu1BQphxdNlU/jBzXsPYVscPjW5E93pCrSO84GkIWoOJtqsnt"
588 b"78DLcQPnF3W24NXGzSGKlSWfXIsyoXCxnBm0mIbD5ZMnKyXEnqSR33Z9He/A+ML"
589 b"A8gbrDUipGNPosesenkKUnOtFIzEGv29hV5E6AMP2ORPVsVlTAZegPJFbbVIWc0"
590 b"rZGFCXKxijDxtUtgWzBhpBAI50JbPHi+IVuaOe4aDJLYgZ0BIBNa6bDI+rScyoy"
591 b"5U0DToV7SZn6CoJ3U19X7BHdYn6TLX0xi43eXuzBGzdHnSzmsc7r/DvkAKJm3vb"
592 b"dVECXqe/gFlXJUBcZ25jhs70MUA=="
593 )
594 assert resp.tbs_response_bytes == base64.b64decode(
595 b"MIHWoUwwSjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzA"
596 b"hBgNVBAMTGkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzGA8yMDE4MDgzMDExMT"
597 b"UwMFowdTBzMEswCQYFKw4DAhoFAAQUfuZq53Kas/z4oiBkbBahLWBxCF0EFKhKa"
598 b"mMEfd265tE5t6ZFZe/zqOyhAhIDHHh6fckClQB7xfIiCztSevCAABgPMjAxODA4"
599 b"MzAxMTAwMDBaoBEYDzIwMTgwOTA2MTEwMDAwWg=="
600 )
601 issuer.public_key().verify(
602 resp.signature,
603 resp.tbs_response_bytes,
604 PKCS1v15(),
605 hashes.SHA256()
606 )
607 assert resp.certificates == []
608 assert resp.responder_key_hash is None
609 assert resp.responder_name == issuer.subject
610 assert resp.produced_at == datetime.datetime(2018, 8, 30, 11, 15)
611 assert resp.certificate_status == ocsp.OCSPCertStatus.GOOD
612 assert resp.revocation_time is None
613 assert resp.revocation_reason is None
614 assert resp.this_update == datetime.datetime(2018, 8, 30, 11, 0)
615 assert resp.next_update == datetime.datetime(2018, 9, 6, 11, 0)
616 assert resp.issuer_key_hash == (
617 b'\xa8Jjc\x04}\xdd\xba\xe6\xd19\xb7\xa6Ee\xef\xf3\xa8\xec\xa1'
618 )
619 assert resp.issuer_name_hash == (
620 b'~\xe6j\xe7r\x9a\xb3\xfc\xf8\xa2 dl\x16\xa1-`q\x08]'
621 )
622 assert isinstance(resp.hash_algorithm, hashes.SHA1)
623 assert resp.serial_number == 271024907440004808294641238224534273948400
Paul Kehrerb8db6682018-10-07 03:44:30 +0800624 assert len(resp.extensions) == 0
Paul Kehrera07de312018-10-02 07:54:31 +0800625
626 def test_load_unauthorized(self):
627 resp = _load_data(
628 os.path.join("x509", "ocsp", "resp-unauthorized.der"),
629 ocsp.load_der_ocsp_response,
630 )
631 assert resp.response_status == ocsp.OCSPResponseStatus.UNAUTHORIZED
632 with pytest.raises(ValueError):
633 assert resp.signature_algorithm_oid
634 with pytest.raises(ValueError):
635 assert resp.signature
636 with pytest.raises(ValueError):
637 assert resp.tbs_response_bytes
638 with pytest.raises(ValueError):
639 assert resp.certificates
640 with pytest.raises(ValueError):
641 assert resp.responder_key_hash
642 with pytest.raises(ValueError):
643 assert resp.responder_name
644 with pytest.raises(ValueError):
645 assert resp.produced_at
646 with pytest.raises(ValueError):
647 assert resp.certificate_status
648 with pytest.raises(ValueError):
649 assert resp.revocation_time
650 with pytest.raises(ValueError):
651 assert resp.revocation_reason
652 with pytest.raises(ValueError):
653 assert resp.this_update
654 with pytest.raises(ValueError):
655 assert resp.next_update
656 with pytest.raises(ValueError):
657 assert resp.issuer_key_hash
658 with pytest.raises(ValueError):
659 assert resp.issuer_name_hash
660 with pytest.raises(ValueError):
661 assert resp.hash_algorithm
662 with pytest.raises(ValueError):
663 assert resp.serial_number
Paul Kehrerb8db6682018-10-07 03:44:30 +0800664 with pytest.raises(ValueError):
665 assert resp.extensions
Paul Kehrera07de312018-10-02 07:54:31 +0800666
667 def test_load_revoked(self):
668 resp = _load_data(
669 os.path.join("x509", "ocsp", "resp-revoked.der"),
670 ocsp.load_der_ocsp_response,
671 )
672 assert resp.certificate_status == ocsp.OCSPCertStatus.REVOKED
673 assert resp.revocation_time == datetime.datetime(
674 2016, 9, 2, 21, 28, 48
675 )
676 assert resp.revocation_reason is None
677
678 def test_load_delegate_unknown_cert(self):
679 resp = _load_data(
680 os.path.join("x509", "ocsp", "resp-delegate-unknown-cert.der"),
681 ocsp.load_der_ocsp_response,
682 )
683 assert len(resp.certificates) == 1
684 assert isinstance(resp.certificates[0], x509.Certificate)
685 assert resp.certificate_status == ocsp.OCSPCertStatus.UNKNOWN
686
687 def test_load_responder_key_hash(self):
688 resp = _load_data(
689 os.path.join("x509", "ocsp", "resp-responder-key-hash.der"),
690 ocsp.load_der_ocsp_response,
691 )
692 assert resp.responder_name is None
693 assert resp.responder_key_hash == (
694 b'\x0f\x80a\x1c\x821a\xd5/(\xe7\x8dF8\xb4,\xe1\xc6\xd9\xe2'
695 )
696
697 def test_load_revoked_reason(self):
698 resp = _load_data(
699 os.path.join("x509", "ocsp", "resp-revoked-reason.der"),
700 ocsp.load_der_ocsp_response,
701 )
702 assert resp.revocation_reason is x509.ReasonFlags.superseded
Paul Kehrerb8db6682018-10-07 03:44:30 +0800703
Paul Kehrera9b4f862018-10-24 08:58:07 +0800704 def test_load_revoked_no_next_update(self):
705 resp = _load_data(
706 os.path.join("x509", "ocsp", "resp-revoked-no-next-update.der"),
707 ocsp.load_der_ocsp_response,
708 )
709 assert resp.serial_number == 16160
710 assert resp.next_update is None
711
Paul Kehrerb8db6682018-10-07 03:44:30 +0800712 def test_response_extensions(self):
713 resp = _load_data(
714 os.path.join("x509", "ocsp", "resp-revoked-reason.der"),
715 ocsp.load_der_ocsp_response,
716 )
717 assert len(resp.extensions) == 1
718 ext = resp.extensions[0]
719 assert ext.critical is False
720 assert ext.value == x509.OCSPNonce(
721 b'\x04\x105\x957\x9fa\x03\x83\x87\x89rW\x8f\xae\x99\xf7"'
722 )
Paul Kehrer788b8592018-10-07 11:07:14 +0800723
724 def test_serialize_reponse(self):
725 resp_bytes = load_vectors_from_file(
726 filename=os.path.join("x509", "ocsp", "resp-revoked.der"),
727 loader=lambda data: data.read(),
728 mode="rb"
729 )
730 resp = ocsp.load_der_ocsp_response(resp_bytes)
731 assert resp.public_bytes(serialization.Encoding.DER) == resp_bytes
732
733 def test_invalid_serialize_encoding(self):
734 resp = _load_data(
735 os.path.join("x509", "ocsp", "resp-revoked.der"),
736 ocsp.load_der_ocsp_response,
737 )
738 with pytest.raises(ValueError):
739 resp.public_bytes("invalid")
740 with pytest.raises(ValueError):
741 resp.public_bytes(serialization.Encoding.PEM)