From b1585d3a014ee2c33b2af65fc84cb90533b4e4e0 Mon Sep 17 00:00:00 2001 From: eunwoo song Date: Mon, 7 Sep 2026 02:07:09 +0900 Subject: [PATCH] fix: handle malformed signature keys --- CHANGELOG.md | 5 +++++ src/answerproof/verifier.py | 5 ++++- tests/test_verifier.py | 21 +++++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d366cb..a8df9f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,11 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Fixed + +- Verification now reports malformed embedded Ed25519 public keys as a failed + signature check instead of raising a decoding exception. + ## [0.1.0] - 2026-07-27 ### Added diff --git a/src/answerproof/verifier.py b/src/answerproof/verifier.py index 9b85d52..4e4cd27 100644 --- a/src/answerproof/verifier.py +++ b/src/answerproof/verifier.py @@ -57,7 +57,10 @@ def failures(self) -> list[CheckResult]: def verify_signature(receipt: Receipt) -> CheckResult: - vk = VerifyKey.from_base64(receipt.signature.public_key) + try: + vk = VerifyKey.from_base64(receipt.signature.public_key) + except ValueError: + return CheckResult("signature", False, "invalid Ed25519 public key") ok = vk.verify(receipt.payload.canonical_bytes(), receipt.signature.signature) return CheckResult("signature", ok, "" if ok else "Ed25519 signature does not match payload") diff --git a/tests/test_verifier.py b/tests/test_verifier.py index ab44fb8..d63858e 100644 --- a/tests/test_verifier.py +++ b/tests/test_verifier.py @@ -32,6 +32,27 @@ def test_signer_pinning_failure(receipt, sources): assert any(c.name == "signer_pin" and not c.passed for c in verdict.checks) +@pytest.mark.parametrize( + "malformed_key", + [ + "!!!not-base64!!!", + "c2hvcnQ", + "not-ascii-🔑", + ], +) +def test_malformed_public_key_returns_failed_signature_check(receipt, malformed_key): + signature = receipt.signature.model_copy(update={"public_key": malformed_key}) + corrupted = receipt.model_copy(update={"signature": signature}) + + verdict = verify_receipt(corrupted) + + assert not verdict.valid + assert len(verdict.failures()) == 1 + failure = verdict.failures()[0] + assert failure.name == "signature" + assert failure.detail == "invalid Ed25519 public key" + + def test_wrong_source_content_fails(receipt): bad = {"s1": "This is not the original content."} verdict = verify_receipt(receipt, source_contents=bad)