Skip to content

Commit f25035c

Browse files
committed
Ensure correct extended key usage for leaf cert
1 parent 534b4d3 commit f25035c

2 files changed

Lines changed: 81 additions & 0 deletions

File tree

autograph_utils/__init__.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,19 @@ def detail(self):
168168
)
169169

170170

171+
class CertificateLeafHasWrongKeyUsage(BadCertificate):
172+
def __init__(self, cert, key_usage):
173+
self.cert = cert
174+
self.key_usage = key_usage
175+
176+
@property
177+
def detail(self):
178+
return (
179+
f"Leaf certificate {self.cert!r} should have extended key usage of just "
180+
f"Code Signing. Got {self.key_usage!r}"
181+
)
182+
183+
171184
class BadSignature(Exception):
172185
detail = "Unknown signature problem"
173186

@@ -309,6 +322,15 @@ async def verify_x5u(self, url):
309322
leaf_subject_name, check_description=self.subject_name_check.describe()
310323
)
311324

325+
code_signing = cryptography.x509.oid.ExtendedKeyUsageOID.CODE_SIGNING
326+
extended_key_usage = (
327+
certs[0]
328+
.extensions.get_extension_for_class(cryptography.x509.ExtendedKeyUsage)
329+
.value
330+
)
331+
if list(extended_key_usage) != [code_signing]:
332+
raise CertificateLeafHasWrongKeyUsage(certs[0], extended_key_usage)
333+
312334
res = certs[0]
313335
self.cache.set(url, res)
314336
return res

tests/test_autograph_utils.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,23 @@ async def aiohttp_session(loop):
9292
yield s
9393

9494

95+
def mock_cert(real_cert):
96+
"""Utility function to create a mock of a cert that has all the same
97+
data but can have fields overridden.
98+
99+
"""
100+
mock_cert = mock.create_autospec(spec=real_cert)
101+
mock_cert.not_valid_before = real_cert.not_valid_before
102+
mock_cert.not_valid_after = real_cert.not_valid_after
103+
mock_cert.signature = real_cert.signature
104+
mock_cert.tbs_certificate_bytes = real_cert.tbs_certificate_bytes
105+
mock_cert.signature_hash_algorithm = real_cert.signature_hash_algorithm
106+
mock_cert.subject = real_cert.subject
107+
mock_cert.extensions = real_cert.extensions
108+
109+
return mock_cert
110+
111+
95112
def test_decode_mozilla_hash():
96113
assert decode_mozilla_hash("4C:35:B1:C3") == b"\x4c\x35\xb1\xc3"
97114

@@ -255,6 +272,48 @@ async def test_verify_broken_chain(
255272
)
256273

257274

275+
async def test_verify_leaf_code_signing(
276+
aiohttp_session, mock_with_x5u, cache, now_fixed
277+
):
278+
certs = [
279+
cryptography.x509.load_pem_x509_certificate(pem, backend=default_backend())
280+
for pem in CERT_LIST
281+
]
282+
283+
# Change extended_key_usage for leaf cert
284+
real_leaf = certs[0]
285+
mock_leaf = mock_cert(real_leaf)
286+
287+
fake_uses = mock.Mock()
288+
fake_uses.value = [
289+
cryptography.x509.oid.ExtendedKeyUsageOID.CODE_SIGNING,
290+
cryptography.x509.oid.ExtendedKeyUsageOID.TIME_STAMPING,
291+
]
292+
293+
def get_extensions(x509_cls):
294+
if x509_cls == cryptography.x509.ExtendedKeyUsage:
295+
return fake_uses
296+
297+
return real_leaf.extensions.get_extension_for_class(x509_cls)
298+
299+
mock_leaf.extensions = mock.Mock()
300+
mock_leaf.extensions.get_extension_for_class.side_effect = get_extensions
301+
certs[0] = mock_leaf
302+
303+
with mock.patch("cryptography.x509.load_pem_x509_certificate") as load_cert_mock:
304+
load_cert_mock.side_effect = lambda *args, **kwargs: certs.pop(0)
305+
s = SignatureVerifier(aiohttp_session, cache, DEV_ROOT_HASH)
306+
with pytest.raises(autograph_utils.CertificateLeafHasWrongKeyUsage) as excinfo:
307+
await s.verify_x5u(FAKE_CERT_URL)
308+
309+
assert excinfo.value.detail.startswith(
310+
f"Leaf certificate {mock_leaf!r} should have extended key usage of just "
311+
"Code Signing. "
312+
)
313+
assert excinfo.value.cert == mock_leaf
314+
assert excinfo.value.key_usage == fake_uses.value
315+
316+
258317
def test_command_line_interface():
259318
"""Test the CLI."""
260319
runner = CliRunner()

0 commit comments

Comments
 (0)