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 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
|
import ipaddress
import pytest
from cryptography.hazmat.backends import default_backend
from cryptography.x509 import load_pem_x509_certificate
from service_identity.cryptography import (
extract_ids,
extract_patterns,
verify_certificate_hostname,
verify_certificate_ip_address,
)
from service_identity.exceptions import (
CertificateError,
DNSMismatch,
IPAddressMismatch,
VerificationError,
)
from service_identity.hazmat import (
DNS_ID,
DNSPattern,
IPAddress_ID,
IPAddressPattern,
URIPattern,
)
from .certificates import (
PEM_CN_ONLY,
PEM_DNS_ONLY,
PEM_EVERYTHING,
PEM_OTHER_NAME,
)
backend = default_backend()
X509_DNS_ONLY = load_pem_x509_certificate(PEM_DNS_ONLY, backend)
X509_CN_ONLY = load_pem_x509_certificate(PEM_CN_ONLY, backend)
X509_OTHER_NAME = load_pem_x509_certificate(PEM_OTHER_NAME, backend)
CERT_EVERYTHING = load_pem_x509_certificate(PEM_EVERYTHING, backend)
class TestPublicAPI:
def test_no_cert_patterns_hostname(self):
"""
A certificate without subjectAltNames raises a helpful
CertificateError.
"""
with pytest.raises(
CertificateError,
match="Certificate does not contain any `subjectAltName`s.",
):
verify_certificate_hostname(X509_CN_ONLY, "example.com")
@pytest.mark.parametrize("ip", ["203.0.113.0", "2001:db8::"])
def test_no_cert_patterns_ip_address(self, ip):
"""
A certificate without subjectAltNames raises a helpful
CertificateError.
"""
with pytest.raises(
CertificateError,
match="Certificate does not contain any `subjectAltName`s.",
):
verify_certificate_ip_address(X509_CN_ONLY, ip)
def test_certificate_verify_hostname_ok(self):
"""
verify_certificate_hostname succeeds if the hostnames match.
"""
verify_certificate_hostname(X509_DNS_ONLY, "twistedmatrix.com")
def test_certificate_verify_hostname_fail(self):
"""
verify_certificate_hostname fails if the hostnames don't match and
provides the user with helpful information.
"""
with pytest.raises(VerificationError) as ei:
verify_certificate_hostname(X509_DNS_ONLY, "google.com")
assert [
DNSMismatch(mismatched_id=DNS_ID("google.com"))
] == ei.value.errors
@pytest.mark.parametrize("ip", ["1.1.1.1", "::1"])
def test_verify_certificate_ip_address_ok(self, ip):
"""
verify_certificate_ip_address succeeds if the addresses match. Works
both with IPv4 and IPv6.
"""
verify_certificate_ip_address(CERT_EVERYTHING, ip)
@pytest.mark.parametrize("ip", ["1.1.1.2", "::2"])
def test_verify_ip_address_fail(self, ip):
"""
verify_ip_address fails if the addresses don't match and provides the
user with helpful information. Works both with IPv4 and IPv6.
"""
with pytest.raises(VerificationError) as ei:
verify_certificate_ip_address(CERT_EVERYTHING, ip)
assert [
IPAddressMismatch(mismatched_id=IPAddress_ID(ip))
] == ei.value.errors
class TestExtractPatterns:
def test_dns(self):
"""
Returns the correct DNSPattern from a certificate.
"""
rv = extract_patterns(X509_DNS_ONLY)
assert [
DNSPattern.from_bytes(b"www.twistedmatrix.com"),
DNSPattern.from_bytes(b"twistedmatrix.com"),
] == rv
def test_cn_ids_are_ignored(self):
"""
commonName is not supported anymore and therefore ignored.
"""
assert [] == extract_patterns(X509_CN_ONLY)
def test_uri(self):
"""
Returns the correct URIPattern from a certificate.
"""
rv = extract_patterns(X509_OTHER_NAME)
assert [URIPattern.from_bytes(b"http://example.com/")] == [
id for id in rv if isinstance(id, URIPattern)
]
def test_ip(self):
"""
Returns IP patterns.
"""
rv = extract_patterns(CERT_EVERYTHING)
assert [
DNSPattern.from_bytes(pattern=b"service.identity.invalid"),
DNSPattern.from_bytes(
pattern=b"*.wildcard.service.identity.invalid"
),
DNSPattern.from_bytes(pattern=b"service.identity.invalid"),
DNSPattern.from_bytes(pattern=b"single.service.identity.invalid"),
IPAddressPattern(pattern=ipaddress.IPv4Address("1.1.1.1")),
IPAddressPattern(pattern=ipaddress.IPv6Address("::1")),
IPAddressPattern(pattern=ipaddress.IPv4Address("2.2.2.2")),
IPAddressPattern(pattern=ipaddress.IPv6Address("2a00:1c38::53")),
] == rv
def test_extract_ids_deprecated(self):
"""
`extract_ids` raises a DeprecationWarning with correct stacklevel.
"""
with pytest.deprecated_call() as wr:
extract_ids(CERT_EVERYTHING)
w = wr.pop()
assert (
"`extract_ids()` is deprecated, please use `extract_patterns()`."
== w.message.args[0]
)
assert __file__ == w.filename
|