From dddbf4ef01bf5e584781161ce23f817c20f6d9ad Mon Sep 17 00:00:00 2001 From: Andrew Pan <3821575+tnytown@users.noreply.github.com> Date: Fri, 17 Mar 2023 02:01:02 -0500 Subject: [PATCH] sct, keyring: specialize errors (#555) * sct, keyring: specialize errors Signed-off-by: Andrew Pan * CHANGELOG: blurb for SCT changes Signed-off-by: Andrew Pan * CHANGELOG: add ref to PR Signed-off-by: Andrew Pan * Apply suggestions from code review Signed-off-by: Andrew Pan --------- Signed-off-by: Andrew Pan --- CHANGELOG.md | 2 + sigstore/_internal/keyring.py | 8 +++- sigstore/_internal/sct.py | 69 ++++++++++++++++++++++++++++------- 3 files changed, 65 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f223ee5..175b7f46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,8 @@ All versions prior to 0.9.0 are untracked. ([#535](https://github.com/sigstore/sigstore-python/pull/535)) * Revamped error diagnostics reporting. All errors with diagnostics now implement `sigstore.errors.Error`. +* Improved diagnostics around Signed Certificate Timestamp verification failures. + ([#555](https://github.com/sigstore/sigstore-python/pull/555)) ### Fixed diff --git a/sigstore/_internal/keyring.py b/sigstore/_internal/keyring.py index 4f006b28..0a5a38ec 100644 --- a/sigstore/_internal/keyring.py +++ b/sigstore/_internal/keyring.py @@ -44,6 +44,12 @@ class KeyringLookupError(KeyringError): pass +class KeyringSignatureError(KeyringError): + """ + Raised when `Keyring.verify()` is passed an invalid signature. + """ + + class Keyring: """ Represents a set of CT signing keys, each of which is a potentially @@ -101,4 +107,4 @@ def verify(self, *, key_id: KeyID, signature: bytes, data: bytes) -> None: # NOTE(ww): Unreachable without API misuse. raise KeyringError(f"unsupported key type: {key}") except InvalidSignature as exc: - raise KeyringError("invalid signature") from exc + raise KeyringSignatureError("invalid signature") from exc diff --git a/sigstore/_internal/sct.py b/sigstore/_internal/sct.py index 84e4487d..8fbee283 100644 --- a/sigstore/_internal/sct.py +++ b/sigstore/_internal/sct.py @@ -32,7 +32,11 @@ from cryptography.x509.oid import ExtendedKeyUsageOID from sigstore._internal.ctfe import CTKeyring -from sigstore._internal.keyring import KeyringError, KeyringLookupError +from sigstore._internal.keyring import ( + KeyringError, + KeyringLookupError, + KeyringSignatureError, +) from sigstore._utils import DERCert, KeyID, key_id from sigstore.errors import Error @@ -142,13 +146,34 @@ class InvalidSCTError(Error): def diagnostics(self) -> str: """Returns diagnostics for the error.""" - # We specialize this error case, since it usually indicates one of - # two conditions: either the current sigstore client is out-of-date, - # or that the SCT is well-formed but invalid for the current configuration - # (indicating that the user has asked for the wrong instance). - if isinstance(self.__cause__, KeyringLookupError): - return dedent( - f""" + + ctx = f"\nContext: {self.__context__}" if self.__context__ else "" + return dedent( + f""" + SCT verification failed. + + Additional context: + + Message: {str(self)} + """ + + ctx + ) + + +class InvalidSCTKeyError(InvalidSCTError): + """ + Raised during SCT verification if the SCT can't be validated against the given keyring. + + We specialize this error case, since it usually indicates one of + two conditions: either the current sigstore client is out-of-date, + or that the SCT is well-formed but invalid for the current configuration + (indicating that the user has asked for the wrong instance). + """ + + def diagnostics(self) -> str: + """Returns diagnostics for the error.""" + return dedent( + f""" Invalid key ID in SCT: not found in current keyring. This may be a result of an outdated `sigstore` installation. @@ -161,9 +186,27 @@ def diagnostics(self) -> str: {self.__cause__} """ - ) + ) - return str(self) + +class SCTSignatureError(InvalidSCTError): + """ + Raised during SCT verification if the signature of the SCT is invalid. + """ + + def diagnostics(self) -> str: + """Returns diagnostics for the error.""" + return dedent( + f""" + Invalid signature on SCT. + + If validating a certificate, the certificate associated with this SCT should not be trusted. + + Additional context: + + {self.__cause__} + """ + ) def verify_sct( @@ -214,8 +257,8 @@ def verify_sct( key_id=KeyID(sct.log_id), signature=sct.signature, data=digitally_signed ) except KeyringLookupError as exc: - raise InvalidSCTError( - "Invalid key ID in SCT: not found in current keyring" - ) from exc + raise InvalidSCTKeyError from exc + except KeyringSignatureError as exc: + raise SCTSignatureError from exc except KeyringError as exc: raise InvalidSCTError from exc