1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
|
# coding: utf-8
from datetime import datetime
import pytest
from asn1crypto.util import timezone
from pyhanko_certvalidator import (
CertificateValidator,
PKIXValidationParams,
ValidationContext,
)
from pyhanko_certvalidator.errors import (
InvalidCertificateError,
PathValidationError,
)
from tests.common import load_cert_object, load_nist_cert
@pytest.mark.asyncio
async def test_basic_certificate_validator_tls():
cert = load_cert_object('mozilla.org.crt')
other_certs = [load_cert_object('digicert-sha2-secure-server-ca.crt')]
moment = datetime(2019, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
context = ValidationContext(moment=moment)
validator = CertificateValidator(cert, other_certs, context)
await validator.async_validate_tls('www.mozilla.org')
@pytest.mark.asyncio
async def test_basic_certificate_validator_tls_expired():
cert = load_cert_object('mozilla.org.crt')
other_certs = [load_cert_object('digicert-sha2-secure-server-ca.crt')]
moment = datetime(2020, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
context = ValidationContext(moment=moment)
validator = CertificateValidator(cert, other_certs, context)
with pytest.raises(PathValidationError, match='expired'):
await validator.async_validate_tls('www.mozilla.org')
@pytest.mark.asyncio
async def test_basic_certificate_validator_tls_invalid_hostname():
cert = load_cert_object('mozilla.org.crt')
other_certs = [load_cert_object('digicert-sha2-secure-server-ca.crt')]
moment = datetime(2019, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
context = ValidationContext(moment=moment)
validator = CertificateValidator(cert, other_certs, context)
with pytest.raises(InvalidCertificateError, match='not valid'):
await validator.async_validate_tls('google.com')
@pytest.mark.asyncio
async def test_basic_certificate_validator_tls_invalid_key_usage():
cert = load_cert_object('mozilla.org.crt')
other_certs = [load_cert_object('digicert-sha2-secure-server-ca.crt')]
moment = datetime(2019, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
context = ValidationContext(moment=moment)
validator = CertificateValidator(cert, other_certs, context)
with pytest.raises(InvalidCertificateError, match='for the purpose'):
await validator.async_validate_usage({'crl_sign'})
@pytest.mark.asyncio
async def test_basic_certificate_validator_tls_whitelist():
cert = load_cert_object('mozilla.org.crt')
other_certs = [load_cert_object('digicert-sha2-secure-server-ca.crt')]
moment = datetime(2020, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
context = ValidationContext(
whitelisted_certs=[cert.sha1_fingerprint], moment=moment
)
validator = CertificateValidator(cert, other_certs, context)
# If whitelist does not work, this will raise exception for expiration
await validator.async_validate_tls('www.mozilla.org')
# If whitelist does not work, this will raise exception for hostname
await validator.async_validate_tls('google.com')
# If whitelist does not work, this will raise exception for key usage
await validator.async_validate_usage({'crl_sign'})
@pytest.mark.asyncio
async def test_certvalidator_with_params():
cert = load_nist_cert('ValidPolicyMappingTest12EE.crt')
ca_certs = [load_nist_cert('TrustAnchorRootCertificate.crt')]
other_certs = [load_nist_cert('P12Mapping1to3CACert.crt')]
context = ValidationContext(
trust_roots=ca_certs,
other_certs=other_certs,
revocation_mode="soft-fail",
weak_hash_algos={'md2', 'md5'},
)
validator = CertificateValidator(
cert,
validation_context=context,
pkix_params=PKIXValidationParams(
user_initial_policy_set=frozenset(['2.16.840.1.101.3.2.1.48.1'])
),
)
path = await validator.async_validate_usage(key_usage={'digital_signature'})
# check if we got the right policy processing
# (i.e. if our params got through)
qps = path.qualified_policies()
(qp,) = qps
assert 1 == len(qp.qualifiers)
(qual_obj,) = qp.qualifiers
assert qual_obj['policy_qualifier_id'].native == 'user_notice'
assert qual_obj['qualifier']['explicit_text'].native == (
'q7: This is the user notice from qualifier 7 associated with '
'NIST-test-policy-3. This user notice should be displayed '
'when NIST-test-policy-1 is in the user-constrained-policy-set'
)
@pytest.mark.asyncio
async def test_self_signed_with_policy():
# tests whether a corner case in the policy validation logic when the
# path length is zero is handled gracefully
cert = load_cert_object('self-signed-with-policy.crt')
context = ValidationContext(trust_roots=[cert], allow_fetching=False)
validator = CertificateValidator(cert, validation_context=context)
path = await validator.async_validate_usage({'digital_signature'})
(qp,) = path.qualified_policies()
# Note: the cert declares a concrete policy, but for the purposes
# of PKIX validation, any policy is valid, since we're validating
# a -signed certificate (so everything breaks down anyway)
assert qp.user_domain_policy_id == 'any_policy'
assert qp.issuer_domain_policy_id == 'any_policy'
|