Skip to content

Commit 581ee05

Browse files
committed
Custom extension policy tests.
1 parent 7b09f8f commit 581ee05

File tree

1 file changed

+163
-0
lines changed

1 file changed

+163
-0
lines changed

tests/x509/verification/test_verification.py

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@
1111

1212
from cryptography import utils, x509
1313
from cryptography.hazmat._oid import ExtendedKeyUsageOID
14+
from cryptography.x509.extensions import ExtendedKeyUsage
1415
from cryptography.x509.general_name import DNSName, IPAddress
1516
from cryptography.x509.verification import (
17+
Criticality,
18+
ExtensionPolicy,
19+
Policy,
1620
PolicyBuilder,
1721
Store,
1822
VerificationError,
@@ -261,3 +265,162 @@ def test_error_message(self):
261265
match=r"<Certificate\(subject=.*?CN=www.cryptography.io.*?\)>",
262266
):
263267
verifier.verify(leaf, [])
268+
269+
270+
class TestCustomExtensionPolicies:
271+
leaf = _load_cert(
272+
os.path.join("x509", "cryptography.io.pem"),
273+
x509.load_pem_x509_certificate,
274+
)
275+
ca = _load_cert(
276+
os.path.join("x509", "rapidssl_sha256_ca_g3.pem"),
277+
x509.load_pem_x509_certificate,
278+
)
279+
store = Store([ca])
280+
validation_time = datetime.datetime.fromisoformat(
281+
"2018-11-16T00:00:00+00:00"
282+
)
283+
284+
def test_builder_methods(self):
285+
ext_policy = ExtensionPolicy.permit_all()
286+
ext_policy = ext_policy.require_not_present(x509.BasicConstraints)
287+
with pytest.raises(
288+
ValueError, match=x509.BasicConstraints.oid.dotted_string
289+
):
290+
ext_policy.require_not_present(x509.BasicConstraints)
291+
with pytest.raises(
292+
ValueError, match=x509.BasicConstraints.oid.dotted_string
293+
):
294+
ext_policy.may_be_present(
295+
x509.BasicConstraints, Criticality.NON_CRITICAL, None
296+
)
297+
with pytest.raises(
298+
ValueError, match=x509.BasicConstraints.oid.dotted_string
299+
):
300+
ext_policy.require_present(
301+
x509.BasicConstraints, Criticality.CRITICAL, None
302+
)
303+
304+
with pytest.raises(TypeError):
305+
306+
class _Extension:
307+
pass
308+
309+
ext_policy.require_present(
310+
_Extension, # type: ignore
311+
Criticality.AGNOSTIC,
312+
None,
313+
)
314+
315+
@staticmethod
316+
def _eku_validator_cb(policy, cert, ext: ExtendedKeyUsage | None):
317+
assert isinstance(policy, Policy)
318+
assert (
319+
policy.validation_time
320+
== TestCustomExtensionPolicies.validation_time.replace(tzinfo=None)
321+
)
322+
assert isinstance(cert, x509.Certificate)
323+
assert ext is None or isinstance(ext, x509.ExtendedKeyUsage)
324+
325+
def test_custom_cb_pass(self):
326+
ca_ext_policy = ExtensionPolicy.webpki_defaults_ca()
327+
ee_ext_policy = ExtensionPolicy.webpki_defaults_ee()
328+
329+
ee_ext_policy = ee_ext_policy.may_be_present(
330+
ExtendedKeyUsage, Criticality.AGNOSTIC, self._eku_validator_cb
331+
)
332+
ca_ext_policy = ca_ext_policy.may_be_present(
333+
ExtendedKeyUsage, Criticality.AGNOSTIC, self._eku_validator_cb
334+
)
335+
336+
ca_validator_called = False
337+
338+
def ca_basic_constraints_validator(policy, cert, ext):
339+
assert cert == self.ca
340+
assert isinstance(policy, Policy)
341+
assert isinstance(cert, x509.Certificate)
342+
assert isinstance(ext, x509.BasicConstraints)
343+
nonlocal ca_validator_called
344+
ca_validator_called = True
345+
346+
ca_ext_policy = ca_ext_policy.may_be_present(
347+
x509.BasicConstraints,
348+
Criticality.AGNOSTIC,
349+
ca_basic_constraints_validator,
350+
)
351+
352+
builder = PolicyBuilder().store(self.store)
353+
builder = builder.time(self.validation_time).max_chain_depth(16)
354+
builder = builder.extension_policies(ca_ext_policy, ee_ext_policy)
355+
356+
builder.build_client_verifier().verify(self.leaf, [])
357+
assert ca_validator_called
358+
ca_validator_called = False
359+
360+
path = builder.build_server_verifier(
361+
DNSName("cryptography.io")
362+
).verify(self.leaf, [])
363+
assert ca_validator_called
364+
assert path == [self.leaf, self.ca]
365+
366+
def test_custom_cb_no_retval_enforced(self):
367+
ca_ext_policy = ExtensionPolicy.webpki_defaults_ca()
368+
ee_ext_policy = ExtensionPolicy.webpki_defaults_ee()
369+
370+
def validator(*_):
371+
return False
372+
373+
ca_ext_policy = ca_ext_policy.may_be_present(
374+
x509.BasicConstraints,
375+
Criticality.AGNOSTIC,
376+
validator,
377+
)
378+
ee_ext_policy = ee_ext_policy.may_be_present(
379+
ExtendedKeyUsage,
380+
Criticality.AGNOSTIC,
381+
validator,
382+
)
383+
384+
builder = PolicyBuilder().store(self.store).time(self.validation_time)
385+
builder = builder.extension_policies(ca_ext_policy, ee_ext_policy)
386+
387+
for verifier in (
388+
builder.build_client_verifier(),
389+
builder.build_server_verifier(DNSName("cryptography.io")),
390+
):
391+
with pytest.raises(
392+
VerificationError,
393+
match="Python validator must return None.",
394+
):
395+
verifier.verify(self.leaf, [])
396+
397+
def test_custom_cb_exception_fails_verification(self):
398+
ca_ext_policy = ExtensionPolicy.webpki_defaults_ca()
399+
ee_ext_policy = ExtensionPolicy.webpki_defaults_ee()
400+
401+
def validator(*_):
402+
raise ValueError("test")
403+
404+
ca_ext_policy = ca_ext_policy.may_be_present(
405+
x509.BasicConstraints,
406+
Criticality.AGNOSTIC,
407+
validator,
408+
)
409+
ee_ext_policy = ee_ext_policy.may_be_present(
410+
ExtendedKeyUsage,
411+
Criticality.AGNOSTIC,
412+
validator,
413+
)
414+
415+
builder = PolicyBuilder().store(self.store).time(self.validation_time)
416+
builder = builder.extension_policies(ca_ext_policy, ee_ext_policy)
417+
418+
for verifier in (
419+
builder.build_client_verifier(),
420+
builder.build_server_verifier(DNSName("cryptography.io")),
421+
):
422+
with pytest.raises(
423+
VerificationError,
424+
match="Python extension validator failed: ValueError: test",
425+
):
426+
verifier.verify(self.leaf, [])

0 commit comments

Comments
 (0)