Skip to content

Commit 534b4d3

Browse files
committed
Implement chain-of-trust verification
1 parent 67aafc2 commit 534b4d3

2 files changed

Lines changed: 46 additions & 0 deletions

File tree

autograph_utils/__init__.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from cryptography import x509
1919
from cryptography.hazmat.backends import default_backend
2020
from cryptography.hazmat.primitives.asymmetric import ec as cryptography_ec
21+
from cryptography.hazmat.primitives.asymmetric import padding
2122
from cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signature
2223
from cryptography.hazmat.primitives.hashes import SHA256, SHA384
2324
from cryptography.x509.oid import NameOID
@@ -154,6 +155,19 @@ def detail(self):
154155
)
155156

156157

158+
class CertificateChainBroken(BadCertificate):
159+
def __init__(self, previous_cert, next_cert):
160+
self.previous_cert = previous_cert
161+
self.next_cert = next_cert
162+
163+
@property
164+
def detail(self):
165+
return (
166+
"Certificate chain is not continuous. "
167+
f"Expected {self.previous_cert!r} to sign {self.next_cert!r}"
168+
)
169+
170+
157171
class BadSignature(Exception):
158172
detail = "Unknown signature problem"
159173

@@ -274,6 +288,19 @@ async def verify_x5u(self, url):
274288
if root_hash != self.root_hash:
275289
raise CertificateHasWrongRoot(expected=self.root_hash, actual=root_hash)
276290

291+
current_cert = chain[0]
292+
for next_cert in chain[1:]:
293+
try:
294+
current_cert.public_key().verify(
295+
next_cert.signature,
296+
next_cert.tbs_certificate_bytes,
297+
padding.PKCS1v15(),
298+
next_cert.signature_hash_algorithm,
299+
)
300+
except cryptography.exceptions.InvalidSignature:
301+
raise CertificateChainBroken(current_cert, next_cert)
302+
current_cert = next_cert
303+
277304
leaf_subject_name = (
278305
certs[0].subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value
279306
)

tests/test_autograph_utils.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,25 @@ async def test_verify_wrong_root_hash(aiohttp_session, mock_with_x5u, cache, now
236236
)
237237

238238

239+
async def test_verify_broken_chain(
240+
aiohttp_session, mock_aioresponses, cache, now_fixed
241+
):
242+
# Drop next-to-last cert in cert list
243+
broken_chain = CERT_LIST[:1] + CERT_LIST[2:]
244+
mock_aioresponses.get(FAKE_CERT_URL, status=200, body=b"\n".join(broken_chain))
245+
s = SignatureVerifier(aiohttp_session, cache, DEV_ROOT_HASH)
246+
with pytest.raises(autograph_utils.CertificateChainBroken) as excinfo:
247+
await s.verify_x5u(FAKE_CERT_URL)
248+
249+
assert excinfo.value.detail.startswith("Certificate chain is not continuous. ")
250+
assert excinfo.value.previous_cert == cryptography.x509.load_pem_x509_certificate(
251+
CERT_LIST[2], backend=default_backend()
252+
)
253+
assert excinfo.value.next_cert == cryptography.x509.load_pem_x509_certificate(
254+
CERT_LIST[0], backend=default_backend()
255+
)
256+
257+
239258
def test_command_line_interface():
240259
"""Test the CLI."""
241260
runner = CliRunner()

0 commit comments

Comments
 (0)