blob: 0d29a3eae238919f740c50014a2044f8bd634685 [file] [log] [blame]
Paul Kehrerbfac2d12015-12-19 23:32:08 -06001# 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
7import datetime
8
9import pytest
10
InvalidInterrupt8e66ca62016-08-16 19:39:31 -070011import pytz
12
Paul Kehrerbfac2d12015-12-19 23:32:08 -060013from cryptography import x509
14from cryptography.hazmat.backends.interfaces import (
15 DSABackend, EllipticCurveBackend, RSABackend, X509Backend
16)
17from cryptography.hazmat.primitives import hashes
18from cryptography.hazmat.primitives.asymmetric import ec
Paul Kehrer426b48d2015-12-24 20:50:43 -060019from cryptography.x509.oid import AuthorityInformationAccessOID, NameOID
Paul Kehrerbfac2d12015-12-19 23:32:08 -060020
21from .hazmat.primitives.fixtures_dsa import DSA_KEY_2048
22from .hazmat.primitives.fixtures_rsa import RSA_KEY_2048, RSA_KEY_512
23from .hazmat.primitives.test_ec import _skip_curve_unsupported
24
25
26class TestCertificateRevocationListBuilder(object):
27 def test_issuer_name_invalid(self):
28 builder = x509.CertificateRevocationListBuilder()
29 with pytest.raises(TypeError):
30 builder.issuer_name("notanx509name")
31
32 def test_set_issuer_name_twice(self):
33 builder = x509.CertificateRevocationListBuilder().issuer_name(
34 x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
35 )
36 with pytest.raises(ValueError):
37 builder.issuer_name(
38 x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
39 )
40
InvalidInterrupt8e66ca62016-08-16 19:39:31 -070041 @pytest.mark.requires_backend_interface(interface=RSABackend)
42 @pytest.mark.requires_backend_interface(interface=X509Backend)
43 def test_aware_last_update(self, backend):
44 last_time = datetime.datetime(2012, 1, 16, 22, 43)
45 tz = pytz.timezone("US/Pacific")
46 last_time = tz.localize(last_time)
47 utc_last = datetime.datetime(2012, 1, 17, 6, 43)
48 next_time = datetime.datetime(2022, 1, 17, 6, 43)
49 private_key = RSA_KEY_2048.private_key(backend)
50 builder = x509.CertificateRevocationListBuilder().issuer_name(
51 x509.Name([
52 x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
53 ])
54 ).last_update(last_time).next_update(next_time)
55
56 crl = builder.sign(private_key, hashes.SHA256(), backend)
57 assert crl.last_update == utc_last
58
Paul Kehrerbfac2d12015-12-19 23:32:08 -060059 def test_last_update_invalid(self):
60 builder = x509.CertificateRevocationListBuilder()
61 with pytest.raises(TypeError):
62 builder.last_update("notadatetime")
63
64 def test_last_update_before_unix_epoch(self):
65 builder = x509.CertificateRevocationListBuilder()
66 with pytest.raises(ValueError):
67 builder.last_update(datetime.datetime(1960, 8, 10))
68
69 def test_set_last_update_twice(self):
70 builder = x509.CertificateRevocationListBuilder().last_update(
71 datetime.datetime(2002, 1, 1, 12, 1)
72 )
73 with pytest.raises(ValueError):
74 builder.last_update(datetime.datetime(2002, 1, 1, 12, 1))
75
InvalidInterrupt8e66ca62016-08-16 19:39:31 -070076 @pytest.mark.requires_backend_interface(interface=RSABackend)
77 @pytest.mark.requires_backend_interface(interface=X509Backend)
78 def test_aware_next_update(self, backend):
79 next_time = datetime.datetime(2022, 1, 16, 22, 43)
80 tz = pytz.timezone("US/Pacific")
81 next_time = tz.localize(next_time)
82 utc_next = datetime.datetime(2022, 1, 17, 6, 43)
83 last_time = datetime.datetime(2012, 1, 17, 6, 43)
84 private_key = RSA_KEY_2048.private_key(backend)
85 builder = x509.CertificateRevocationListBuilder().issuer_name(
86 x509.Name([
87 x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
88 ])
89 ).last_update(last_time).next_update(next_time)
90
91 crl = builder.sign(private_key, hashes.SHA256(), backend)
92 assert crl.next_update == utc_next
93
Paul Kehrerbfac2d12015-12-19 23:32:08 -060094 def test_next_update_invalid(self):
95 builder = x509.CertificateRevocationListBuilder()
96 with pytest.raises(TypeError):
97 builder.next_update("notadatetime")
98
99 def test_next_update_before_unix_epoch(self):
100 builder = x509.CertificateRevocationListBuilder()
101 with pytest.raises(ValueError):
102 builder.next_update(datetime.datetime(1960, 8, 10))
103
104 def test_set_next_update_twice(self):
105 builder = x509.CertificateRevocationListBuilder().next_update(
106 datetime.datetime(2002, 1, 1, 12, 1)
107 )
108 with pytest.raises(ValueError):
109 builder.next_update(datetime.datetime(2002, 1, 1, 12, 1))
110
111 def test_last_update_after_next_update(self):
112 builder = x509.CertificateRevocationListBuilder()
113
114 builder = builder.next_update(
115 datetime.datetime(2002, 1, 1, 12, 1)
116 )
117 with pytest.raises(ValueError):
118 builder.last_update(datetime.datetime(2003, 1, 1, 12, 1))
119
120 def test_next_update_after_last_update(self):
121 builder = x509.CertificateRevocationListBuilder()
122
123 builder = builder.last_update(
124 datetime.datetime(2002, 1, 1, 12, 1)
125 )
126 with pytest.raises(ValueError):
127 builder.next_update(datetime.datetime(2001, 1, 1, 12, 1))
128
Paul Kehrer426b48d2015-12-24 20:50:43 -0600129 def test_add_extension_checks_for_duplicates(self):
130 builder = x509.CertificateRevocationListBuilder().add_extension(
131 x509.CRLNumber(1), False
132 )
133
134 with pytest.raises(ValueError):
135 builder.add_extension(x509.CRLNumber(2), False)
136
Paul Kehrerd8359692015-12-24 22:48:38 -0600137 def test_add_invalid_extension(self):
138 builder = x509.CertificateRevocationListBuilder()
139
140 with pytest.raises(TypeError):
141 builder.add_extension(
142 object(), False
143 )
144
Paul Kehrer4c7fd5f2015-12-25 13:40:55 -0600145 def test_add_invalid_revoked_certificate(self):
146 builder = x509.CertificateRevocationListBuilder()
147
148 with pytest.raises(TypeError):
149 builder.add_revoked_certificate(object())
150
Paul Kehrerbfac2d12015-12-19 23:32:08 -0600151 @pytest.mark.requires_backend_interface(interface=RSABackend)
152 @pytest.mark.requires_backend_interface(interface=X509Backend)
153 def test_no_issuer_name(self, backend):
154 private_key = RSA_KEY_2048.private_key(backend)
155 builder = x509.CertificateRevocationListBuilder().last_update(
156 datetime.datetime(2002, 1, 1, 12, 1)
157 ).next_update(
158 datetime.datetime(2030, 1, 1, 12, 1)
159 )
160
161 with pytest.raises(ValueError):
162 builder.sign(private_key, hashes.SHA256(), backend)
163
164 @pytest.mark.requires_backend_interface(interface=RSABackend)
165 @pytest.mark.requires_backend_interface(interface=X509Backend)
166 def test_no_last_update(self, backend):
167 private_key = RSA_KEY_2048.private_key(backend)
168 builder = x509.CertificateRevocationListBuilder().issuer_name(
169 x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
170 ).next_update(
171 datetime.datetime(2030, 1, 1, 12, 1)
172 )
173
174 with pytest.raises(ValueError):
175 builder.sign(private_key, hashes.SHA256(), backend)
176
177 @pytest.mark.requires_backend_interface(interface=RSABackend)
178 @pytest.mark.requires_backend_interface(interface=X509Backend)
179 def test_no_next_update(self, backend):
180 private_key = RSA_KEY_2048.private_key(backend)
181 builder = x509.CertificateRevocationListBuilder().issuer_name(
182 x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
183 ).last_update(
184 datetime.datetime(2030, 1, 1, 12, 1)
185 )
186
187 with pytest.raises(ValueError):
188 builder.sign(private_key, hashes.SHA256(), backend)
189
190 @pytest.mark.requires_backend_interface(interface=RSABackend)
191 @pytest.mark.requires_backend_interface(interface=X509Backend)
192 def test_sign_empty_list(self, backend):
193 private_key = RSA_KEY_2048.private_key(backend)
194 last_update = datetime.datetime(2002, 1, 1, 12, 1)
195 next_update = datetime.datetime(2030, 1, 1, 12, 1)
196 builder = x509.CertificateRevocationListBuilder().issuer_name(
197 x509.Name([
198 x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
199 ])
200 ).last_update(last_update).next_update(next_update)
201
202 crl = builder.sign(private_key, hashes.SHA256(), backend)
203 assert len(crl) == 0
204 assert crl.last_update == last_update
205 assert crl.next_update == next_update
206
Paul Kehrer426b48d2015-12-24 20:50:43 -0600207 @pytest.mark.parametrize(
208 "extension",
209 [
210 x509.CRLNumber(13),
211 x509.AuthorityKeyIdentifier(
212 b"\xc3\x9c\xf3\xfc\xd3F\x084\xbb\xceF\x7f\xa0|[\xf3\xe2\x08"
213 b"\xcbY",
214 None,
215 None
216 ),
217 x509.AuthorityInformationAccess([
218 x509.AccessDescription(
219 AuthorityInformationAccessOID.CA_ISSUERS,
220 x509.DNSName(u"cryptography.io")
221 )
222 ]),
223 x509.IssuerAlternativeName([
224 x509.UniformResourceIdentifier(u"https://cryptography.io"),
225 ])
226 ]
227 )
228 @pytest.mark.requires_backend_interface(interface=RSABackend)
229 @pytest.mark.requires_backend_interface(interface=X509Backend)
230 def test_sign_extensions(self, backend, extension):
231 private_key = RSA_KEY_2048.private_key(backend)
232 last_update = datetime.datetime(2002, 1, 1, 12, 1)
233 next_update = datetime.datetime(2030, 1, 1, 12, 1)
234 builder = x509.CertificateRevocationListBuilder().issuer_name(
235 x509.Name([
236 x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
237 ])
238 ).last_update(
239 last_update
240 ).next_update(
241 next_update
242 ).add_extension(
243 extension, False
244 )
245
246 crl = builder.sign(private_key, hashes.SHA256(), backend)
247 assert len(crl) == 0
248 assert len(crl.extensions) == 1
Paul Kehrerbbc1ba92015-12-25 10:05:46 -0600249 ext = crl.extensions.get_extension_for_class(type(extension))
Paul Kehrer426b48d2015-12-24 20:50:43 -0600250 assert ext.critical is False
251 assert ext.value == extension
252
253 @pytest.mark.requires_backend_interface(interface=RSABackend)
254 @pytest.mark.requires_backend_interface(interface=X509Backend)
255 def test_sign_multiple_extensions_critical(self, backend):
256 private_key = RSA_KEY_2048.private_key(backend)
257 last_update = datetime.datetime(2002, 1, 1, 12, 1)
258 next_update = datetime.datetime(2030, 1, 1, 12, 1)
259 ian = x509.IssuerAlternativeName([
260 x509.UniformResourceIdentifier(u"https://cryptography.io"),
261 ])
262 crl_number = x509.CRLNumber(13)
263 builder = x509.CertificateRevocationListBuilder().issuer_name(
264 x509.Name([
265 x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
266 ])
267 ).last_update(
268 last_update
269 ).next_update(
270 next_update
271 ).add_extension(
272 crl_number, False
273 ).add_extension(
274 ian, True
275 )
276
277 crl = builder.sign(private_key, hashes.SHA256(), backend)
278 assert len(crl) == 0
279 assert len(crl.extensions) == 2
280 ext1 = crl.extensions.get_extension_for_class(x509.CRLNumber)
281 assert ext1.critical is False
282 assert ext1.value == crl_number
283 ext2 = crl.extensions.get_extension_for_class(
284 x509.IssuerAlternativeName
285 )
286 assert ext2.critical is True
287 assert ext2.value == ian
288
289 @pytest.mark.requires_backend_interface(interface=RSABackend)
290 @pytest.mark.requires_backend_interface(interface=X509Backend)
291 def test_add_unsupported_extension(self, backend):
292 private_key = RSA_KEY_2048.private_key(backend)
293 last_update = datetime.datetime(2002, 1, 1, 12, 1)
294 next_update = datetime.datetime(2030, 1, 1, 12, 1)
295 builder = x509.CertificateRevocationListBuilder().issuer_name(
296 x509.Name([
297 x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
298 ])
299 ).last_update(
300 last_update
301 ).next_update(
302 next_update
303 ).add_extension(
304 x509.OCSPNoCheck(), False
305 )
306 with pytest.raises(NotImplementedError):
307 builder.sign(private_key, hashes.SHA256(), backend)
308
Paul Kehrerbfac2d12015-12-19 23:32:08 -0600309 @pytest.mark.requires_backend_interface(interface=RSABackend)
310 @pytest.mark.requires_backend_interface(interface=X509Backend)
311 def test_sign_rsa_key_too_small(self, backend):
312 private_key = RSA_KEY_512.private_key(backend)
313 last_update = datetime.datetime(2002, 1, 1, 12, 1)
314 next_update = datetime.datetime(2030, 1, 1, 12, 1)
315 builder = x509.CertificateRevocationListBuilder().issuer_name(
316 x509.Name([
317 x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
318 ])
319 ).last_update(
320 last_update
321 ).next_update(
322 next_update
323 )
324
325 with pytest.raises(ValueError):
326 builder.sign(private_key, hashes.SHA512(), backend)
327
328 @pytest.mark.requires_backend_interface(interface=RSABackend)
329 @pytest.mark.requires_backend_interface(interface=X509Backend)
330 def test_sign_with_invalid_hash(self, backend):
331 private_key = RSA_KEY_2048.private_key(backend)
332 last_update = datetime.datetime(2002, 1, 1, 12, 1)
333 next_update = datetime.datetime(2030, 1, 1, 12, 1)
334 builder = x509.CertificateRevocationListBuilder().issuer_name(
335 x509.Name([
336 x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
337 ])
338 ).last_update(
339 last_update
340 ).next_update(
341 next_update
342 )
343
344 with pytest.raises(TypeError):
345 builder.sign(private_key, object(), backend)
346
347 @pytest.mark.requires_backend_interface(interface=DSABackend)
348 @pytest.mark.requires_backend_interface(interface=X509Backend)
Paul Kehrer9d345312015-12-26 18:09:52 -0600349 def test_sign_dsa_key(self, backend):
Alex Gaynor3e3444f2016-07-11 17:03:13 -0400350 if backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_101:
Paul Kehrer9d345312015-12-26 18:09:52 -0600351 pytest.skip("Requires a newer OpenSSL. Must be >= 1.0.1")
Paul Kehrerbfac2d12015-12-19 23:32:08 -0600352 private_key = DSA_KEY_2048.private_key(backend)
Paul Kehrer9d345312015-12-26 18:09:52 -0600353 invalidity_date = x509.InvalidityDate(
354 datetime.datetime(2002, 1, 1, 0, 0)
355 )
356 ian = x509.IssuerAlternativeName([
357 x509.UniformResourceIdentifier(u"https://cryptography.io"),
358 ])
359 revoked_cert0 = x509.RevokedCertificateBuilder().serial_number(
360 2
361 ).revocation_date(
362 datetime.datetime(2012, 1, 1, 1, 1)
363 ).add_extension(
364 invalidity_date, False
365 ).build(backend)
Paul Kehrerbfac2d12015-12-19 23:32:08 -0600366 last_update = datetime.datetime(2002, 1, 1, 12, 1)
367 next_update = datetime.datetime(2030, 1, 1, 12, 1)
368 builder = x509.CertificateRevocationListBuilder().issuer_name(
369 x509.Name([
370 x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
371 ])
372 ).last_update(
373 last_update
374 ).next_update(
375 next_update
Paul Kehrer9d345312015-12-26 18:09:52 -0600376 ).add_revoked_certificate(
377 revoked_cert0
378 ).add_extension(
379 ian, False
Paul Kehrerbfac2d12015-12-19 23:32:08 -0600380 )
381
Paul Kehrer9d345312015-12-26 18:09:52 -0600382 crl = builder.sign(private_key, hashes.SHA256(), backend)
383 assert crl.extensions.get_extension_for_class(
384 x509.IssuerAlternativeName
385 ).value == ian
386 assert crl[0].serial_number == revoked_cert0.serial_number
387 assert crl[0].revocation_date == revoked_cert0.revocation_date
388 assert len(crl[0].extensions) == 1
389 ext = crl[0].extensions.get_extension_for_class(x509.InvalidityDate)
390 assert ext.critical is False
391 assert ext.value == invalidity_date
Paul Kehrerbfac2d12015-12-19 23:32:08 -0600392
393 @pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
394 @pytest.mark.requires_backend_interface(interface=X509Backend)
395 def test_sign_ec_key_unsupported(self, backend):
Alex Gaynor3e3444f2016-07-11 17:03:13 -0400396 if backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_101:
Paul Kehrer9d345312015-12-26 18:09:52 -0600397 pytest.skip("Requires a newer OpenSSL. Must be >= 1.0.1")
Paul Kehrerbfac2d12015-12-19 23:32:08 -0600398 _skip_curve_unsupported(backend, ec.SECP256R1())
399 private_key = ec.generate_private_key(ec.SECP256R1(), backend)
Paul Kehrer9d345312015-12-26 18:09:52 -0600400 invalidity_date = x509.InvalidityDate(
401 datetime.datetime(2002, 1, 1, 0, 0)
402 )
403 ian = x509.IssuerAlternativeName([
404 x509.UniformResourceIdentifier(u"https://cryptography.io"),
405 ])
406 revoked_cert0 = x509.RevokedCertificateBuilder().serial_number(
407 2
408 ).revocation_date(
409 datetime.datetime(2012, 1, 1, 1, 1)
410 ).add_extension(
411 invalidity_date, False
412 ).build(backend)
Paul Kehrerbfac2d12015-12-19 23:32:08 -0600413 last_update = datetime.datetime(2002, 1, 1, 12, 1)
414 next_update = datetime.datetime(2030, 1, 1, 12, 1)
415 builder = x509.CertificateRevocationListBuilder().issuer_name(
416 x509.Name([
417 x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
418 ])
419 ).last_update(
420 last_update
421 ).next_update(
422 next_update
Paul Kehrer9d345312015-12-26 18:09:52 -0600423 ).add_revoked_certificate(
424 revoked_cert0
425 ).add_extension(
426 ian, False
Paul Kehrerbfac2d12015-12-19 23:32:08 -0600427 )
428
Paul Kehrer9d345312015-12-26 18:09:52 -0600429 crl = builder.sign(private_key, hashes.SHA256(), backend)
430 assert crl.extensions.get_extension_for_class(
431 x509.IssuerAlternativeName
432 ).value == ian
433 assert crl[0].serial_number == revoked_cert0.serial_number
434 assert crl[0].revocation_date == revoked_cert0.revocation_date
435 assert len(crl[0].extensions) == 1
436 ext = crl[0].extensions.get_extension_for_class(x509.InvalidityDate)
437 assert ext.critical is False
438 assert ext.value == invalidity_date
Paul Kehrer4c7fd5f2015-12-25 13:40:55 -0600439
440 @pytest.mark.requires_backend_interface(interface=RSABackend)
441 @pytest.mark.requires_backend_interface(interface=X509Backend)
442 def test_sign_with_revoked_certificates(self, backend):
443 private_key = RSA_KEY_2048.private_key(backend)
444 last_update = datetime.datetime(2002, 1, 1, 12, 1)
445 next_update = datetime.datetime(2030, 1, 1, 12, 1)
Paul Kehrere5f152b2015-12-25 23:55:47 -0600446 invalidity_date = x509.InvalidityDate(
447 datetime.datetime(2002, 1, 1, 0, 0)
448 )
Paul Kehrer4c7fd5f2015-12-25 13:40:55 -0600449 revoked_cert0 = x509.RevokedCertificateBuilder().serial_number(
450 38
451 ).revocation_date(
452 datetime.datetime(2011, 1, 1, 1, 1)
453 ).build(backend)
454 revoked_cert1 = x509.RevokedCertificateBuilder().serial_number(
455 2
456 ).revocation_date(
457 datetime.datetime(2012, 1, 1, 1, 1)
Paul Kehrere5f152b2015-12-25 23:55:47 -0600458 ).add_extension(
459 invalidity_date, False
Paul Kehrer4c7fd5f2015-12-25 13:40:55 -0600460 ).build(backend)
461 builder = x509.CertificateRevocationListBuilder().issuer_name(
462 x509.Name([
463 x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
464 ])
465 ).last_update(
466 last_update
467 ).next_update(
468 next_update
469 ).add_revoked_certificate(
470 revoked_cert0
471 ).add_revoked_certificate(
472 revoked_cert1
473 )
474
475 crl = builder.sign(private_key, hashes.SHA256(), backend)
476 assert len(crl) == 2
477 assert crl.last_update == last_update
478 assert crl.next_update == next_update
479 assert crl[0].serial_number == revoked_cert0.serial_number
480 assert crl[0].revocation_date == revoked_cert0.revocation_date
481 assert len(crl[0].extensions) == 0
482 assert crl[1].serial_number == revoked_cert1.serial_number
483 assert crl[1].revocation_date == revoked_cert1.revocation_date
Paul Kehrere5f152b2015-12-25 23:55:47 -0600484 assert len(crl[1].extensions) == 1
485 ext = crl[1].extensions.get_extension_for_class(x509.InvalidityDate)
486 assert ext.critical is False
487 assert ext.value == invalidity_date