2424from typing import Optional , TextIO , Union , cast
2525
2626from cryptography .x509 import load_pem_x509_certificates
27- from id import GitHubOidcPermissionCredentialError , detect_credential
2827from sigstore_protobuf_specs .dev .sigstore .bundle .v1 import Bundle
2928
3029from sigstore import __version__
31- from sigstore ._errors import Error
3230from sigstore ._internal .ctfe import CTKeyring
3331from sigstore ._internal .fulcio .client import DEFAULT_FULCIO_URL , FulcioClient
3432from sigstore ._internal .keyring import Keyring
4038)
4139from sigstore ._internal .tuf import TrustUpdater
4240from sigstore ._utils import PEMCert
41+ from sigstore .errors import Error
4342from sigstore .oidc import (
4443 DEFAULT_OAUTH_ISSUER_URL ,
4544 STAGING_OAUTH_ISSUER_URL ,
4645 Issuer ,
46+ detect_credential ,
4747)
4848from sigstore .sign import Signer
4949from sigstore .transparency import LogEntry
5050from sigstore .verify import (
5151 CertificateVerificationFailure ,
5252 LogEntryMissing ,
53- VerificationFailure ,
5453 VerificationMaterials ,
5554 Verifier ,
5655 policy ,
5756)
57+ from sigstore .verify .models import VerificationFailure
5858
5959logging .basicConfig ()
6060logger = logging .getLogger (__name__ )
@@ -844,6 +844,60 @@ def _collect_verification_state(
844844 return (verifier , all_materials )
845845
846846
847+ class VerificationError (Error ):
848+ """Raised when the verifier returns a `VerificationFailure` result."""
849+
850+ def __init__ (self , result : VerificationFailure ):
851+ self .message = f"Verification failed: { result .reason } "
852+ self .result = result
853+
854+ def diagnostics (self ) -> str :
855+ message = f"Failure reason: { self .result .reason } \n "
856+
857+ if isinstance (self .result , CertificateVerificationFailure ):
858+ message += dedent (
859+ f"""
860+ The given certificate could not be verified against the
861+ root of trust.
862+
863+ This may be a result of connecting to the wrong Fulcio instance
864+ (for example, staging instead of production, or vice versa).
865+
866+ Additional context:
867+
868+ { self .result .exception }
869+ """
870+ )
871+ elif isinstance (self .result , LogEntryMissing ):
872+ message += dedent (
873+ f"""
874+ These signing artifacts could not be matched to a entry
875+ in the configured transparency log.
876+
877+ This may be a result of connecting to the wrong Rekor instance
878+ (for example, staging instead of production, or vice versa).
879+
880+ Additional context:
881+
882+ Signature: { self .result .signature }
883+
884+ Artifact hash: { self .result .artifact_hash }
885+ """
886+ )
887+ else :
888+ message += dedent (
889+ f"""
890+ A verification error occurred.
891+
892+ Additional context:
893+
894+ { self .result }
895+ """
896+ )
897+
898+ return message
899+
900+
847901def _verify_identity (args : argparse .Namespace ) -> None :
848902 verifier , files_with_materials = _collect_verification_state (args )
849903
@@ -861,64 +915,8 @@ def _verify_identity(args: argparse.Namespace) -> None:
861915 if result :
862916 print (f"OK: { file } " )
863917 else :
864- result = cast (VerificationFailure , result )
865918 print (f"FAIL: { file } " )
866- print (f"Failure reason: { result .reason } " , file = sys .stderr )
867-
868- if isinstance (result , CertificateVerificationFailure ):
869- # If certificate verification failed, it's either because of
870- # a chain issue or some outdated state in sigstore itself.
871- # These might already be resolved in a newer version, so
872- # we suggest that users try to upgrade and retry before
873- # anything else.
874- print (
875- dedent (
876- f"""
877- The given certificate could not be verified against the
878- root of trust.
879-
880- This may be a result of connecting to the wrong Fulcio instance
881- (for example, staging instead of production, or vice versa).
882-
883- Additional context:
884-
885- { result .exception }
886- """
887- ),
888- file = sys .stderr ,
889- )
890- elif isinstance (result , LogEntryMissing ):
891- # If Rekor lookup failed, it's because the certificate either
892- # wasn't logged after creation or because the user requested the
893- # wrong Rekor instance (e.g., staging instead of production).
894- # The latter is significantly more likely, so we add
895- # some additional context to the output indicating it.
896- #
897- # NOTE: Even though the latter is more likely, it's still extremely
898- # unlikely that we'd hit this -- we should always fail with
899- # `CertificateVerificationFailure` instead, as the cert store should
900- # fail to validate due to a mismatch between the leaf and the trusted
901- # root + intermediates.
902- print (
903- dedent (
904- f"""
905- These signing artifacts could not be matched to a entry
906- in the configured transparency log.
907-
908- This may be a result of connecting to the wrong Rekor instance
909- (for example, staging instead of production, or vice versa).
910-
911- Additional context:
912-
913- Signature: { result .signature }
914-
915- Artifact hash: { result .artifact_hash }
916- """
917- ),
918- file = sys .stderr ,
919- )
920-
921- sys .exit (1 )
919+ raise VerificationError (cast (VerificationFailure , result ))
922920
923921
924922def _verify_github (args : argparse .Namespace ) -> None :
@@ -952,106 +950,14 @@ def _verify_github(args: argparse.Namespace) -> None:
952950 if result :
953951 print (f"OK: { file } " )
954952 else :
955- result = cast (VerificationFailure , result )
956953 print (f"FAIL: { file } " )
957- print (f"Failure reason: { result .reason } " , file = sys .stderr )
958-
959- if isinstance (result , CertificateVerificationFailure ):
960- # If certificate verification failed, it's either because of
961- # a chain issue or some outdated state in sigstore itself.
962- # These might already be resolved in a newer version, so
963- # we suggest that users try to upgrade and retry before
964- # anything else.
965- print (
966- dedent (
967- f"""
968- The given certificate could not be verified against the
969- root of trust.
970-
971- This may be a result of connecting to the wrong Fulcio instance
972- (for example, staging instead of production, or vice versa).
973-
974- Additional context:
975-
976- { result .exception }
977- """
978- ),
979- file = sys .stderr ,
980- )
981- elif isinstance (result , LogEntryMissing ):
982- # If Rekor lookup failed, it's because the certificate either
983- # wasn't logged after creation or because the user requested the
984- # wrong Rekor instance (e.g., staging instead of production).
985- # The latter is significantly more likely, so we add
986- # some additional context to the output indicating it.
987- #
988- # NOTE: Even though the latter is more likely, it's still extremely
989- # unlikely that we'd hit this -- we should always fail with
990- # `CertificateVerificationFailure` instead, as the cert store should
991- # fail to validate due to a mismatch between the leaf and the trusted
992- # root + intermediates.
993- print (
994- dedent (
995- f"""
996- These signing artifacts could not be matched to a entry
997- in the configured transparency log.
998-
999- This may be a result of connecting to the wrong Rekor instance
1000- (for example, staging instead of production, or vice versa).
1001-
1002- Additional context:
1003-
1004- Signature: { result .signature }
1005-
1006- Artifact hash: { result .artifact_hash }
1007- """
1008- ),
1009- file = sys .stderr ,
1010- )
1011-
1012- sys .exit (1 )
954+ raise VerificationError (cast (VerificationFailure , result ))
1013955
1014956
1015957def _get_identity_token (args : argparse .Namespace ) -> Optional [str ]:
1016958 token = None
1017959 if not args .oidc_disable_ambient_providers :
1018- try :
1019- token = detect_credential (DEFAULT_AUDIENCE )
1020- except GitHubOidcPermissionCredentialError as exception :
1021- # Provide some common reasons for why we hit permission errors in
1022- # GitHub Actions.
1023- print (
1024- dedent (
1025- f"""
1026- Insufficient permissions for GitHub Actions workflow.
1027-
1028- The most common reason for this is incorrect
1029- configuration of the top-level `permissions` setting of the
1030- workflow YAML file. It should be configured like so:
1031-
1032- permissions:
1033- id-token: write
1034-
1035- Relevant documentation here:
1036-
1037- https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#adding-permissions-settings
1038-
1039- Another possible reason is that the workflow run has been
1040- triggered by a PR from a forked repository. PRs from forked
1041- repositories typically cannot be granted write access.
1042-
1043- Relevant documentation here:
1044-
1045- https://docs.github.com/en/actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token
1046-
1047- Additional context:
1048-
1049- { exception }
1050- """
1051- ),
1052- file = sys .stderr ,
1053- )
1054- sys .exit (1 )
960+ token = detect_credential (DEFAULT_AUDIENCE )
1055961
1056962 if not token :
1057963 if args .staging :
0 commit comments