File: test_certificate_validator.py

package info (click to toggle)
python-pyhanko-certvalidator 0.26.3-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,956 kB
  • sloc: python: 9,254; sh: 47; makefile: 4
file content (147 lines) | stat: -rw-r--r-- 5,291 bytes parent folder | download | duplicates (2)
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'