diff --git a/.github/workflows/test-sigstore.yml b/.github/workflows/test-sigstore.yml index 55c7cd28..e4cdced1 100644 --- a/.github/workflows/test-sigstore.yml +++ b/.github/workflows/test-sigstore.yml @@ -1,11 +1,10 @@ name: Run Sigstore Signer tests on: - ## Disabled temporarily: #781 - #push: - # branches: - # - main - #pull_request: + push: + branches: + - main + pull_request: workflow_dispatch: permissions: {} diff --git a/mypy.ini b/mypy.ini index e889fc4f..2b97eb98 100644 --- a/mypy.ini +++ b/mypy.ini @@ -20,9 +20,6 @@ ignore_missing_imports = True [mypy-asn1crypto.*] ignore_missing_imports = True -[mypy-sigstore.*] -ignore_missing_imports = True - [mypy-sigstore_protobuf_specs.*] ignore_missing_imports = True diff --git a/pyproject.toml b/pyproject.toml index afe220e7..d8d7510c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,7 +52,7 @@ azurekms = ["azure-identity", "azure-keyvault-keys", "cryptography>=40.0.0"] awskms = ["boto3", "botocore", "cryptography>=40.0.0"] hsm = ["asn1crypto", "cryptography>=40.0.0", "PyKCS11"] PySPX = ["PySPX>=0.5.0"] -sigstore = ["sigstore~=2.0"] +sigstore = ["sigstore~=3.0"] vault = ["hvac", "cryptography>=40.0.0"] [tool.hatch.version] diff --git a/requirements-sigstore.txt b/requirements-sigstore.txt index 759166af..3e5fa176 100644 --- a/requirements-sigstore.txt +++ b/requirements-sigstore.txt @@ -1 +1 @@ -sigstore==2.1.5 +sigstore==3.0.0 diff --git a/securesystemslib/signer/_sigstore_signer.py b/securesystemslib/signer/_sigstore_signer.py index 43b1851f..d14358ff 100644 --- a/securesystemslib/signer/_sigstore_signer.py +++ b/securesystemslib/signer/_sigstore_signer.py @@ -1,13 +1,7 @@ """Signer implementation for project sigstore. - -NOTE: SigstoreSigner and -Key are disabled temporarily around -the Securesystemslib 1.0 release as the cyclic dependency -(securesystemslib -> sigstore-python -> tuf -> securesystemslib) -is problematic during API deprecations. -See issue #781. """ -import io +import json import logging from typing import Any, Dict, Optional, Tuple from urllib import parse @@ -66,37 +60,38 @@ def to_dict(self) -> Dict: def verify_signature(self, signature: Signature, data: bytes) -> None: # pylint: disable=import-outside-toplevel,import-error - result = None try: - from sigstore.verify import VerificationMaterials, Verifier + from sigstore.errors import VerificationError as SigstoreVerifyError + from sigstore.models import Bundle + from sigstore.verify import Verifier from sigstore.verify.policy import Identity - from sigstore_protobuf_specs.dev.sigstore.bundle.v1 import Bundle + except ImportError as e: + raise VerificationError(IMPORT_ERROR) from e + try: verifier = Verifier.production() identity = Identity( identity=self.keyval["identity"], issuer=self.keyval["issuer"] ) - bundle = Bundle().from_dict(signature.unrecognized_fields["bundle"]) - materials = VerificationMaterials.from_bundle( - input_=io.BytesIO(data), bundle=bundle, offline=True - ) - result = verifier.verify(materials, identity) + bundle_data = signature.unrecognized_fields["bundle"] + bundle = Bundle.from_json(json.dumps(bundle_data)) - except Exception as e: - logger.info("Key %s failed to verify sig: %s", self.keyid, str(e)) - raise VerificationError( - f"Unknown failure to verify signature by {self.keyid}" - ) from e + verifier.verify_artifact(data, bundle, identity) - if not result: + except SigstoreVerifyError as e: logger.info( "Key %s failed to verify sig: %s", self.keyid, - getattr(result, "reason", ""), + e, ) raise UnverifiedSignatureError( f"Failed to verify signature by {self.keyid}" - ) + ) from e + except Exception as e: + logger.info("Key %s failed to verify sig: %s", self.keyid, str(e)) + raise VerificationError( + f"Unknown failure to verify signature by {self.keyid}" + ) from e class SigstoreSigner(Signer): @@ -189,9 +184,9 @@ def from_priv_key_uri( key_identity = public_key.keyval["identity"] key_issuer = public_key.keyval["issuer"] - if key_issuer != token.expected_certificate_subject: + if key_issuer != token.federated_issuer: raise ValueError( - f"Signer identity issuer {token.expected_certificate_subject} " + f"Signer identity issuer {token.federated_issuer} " f"did not match key: {key_issuer}" ) # TODO: should check ambient identity too: unfortunately IdentityToken does @@ -246,9 +241,7 @@ def import_via_auth(cls) -> Tuple[str, SigstoreKey]: # authenticate to get the identity and issuer token = Issuer.production().identity_token() - return cls.import_( - token.identity, token.expected_certificate_subject, False - ) + return cls.import_(token.identity, token.federated_issuer, False) def sign(self, payload: bytes) -> Signature: """Signs payload using the OIDC token on the signer instance. @@ -273,12 +266,12 @@ def sign(self, payload: bytes) -> Signature: context = SigningContext.production() with context.signer(self._token) as sigstore_signer: - result = sigstore_signer.sign(io.BytesIO(payload)) - - bundle = result.to_bundle() - + bundle = sigstore_signer.sign_artifact(payload) + # We want to access the actual signature, see + # https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_bundle.proto + bundle_json = json.loads(bundle.to_json()) return Signature( self.public_key.keyid, - bundle.message_signature.signature.hex(), - {"bundle": bundle.to_dict()}, + bundle_json["messageSignature"]["signature"], + {"bundle": bundle_json}, ) diff --git a/tests/check_sigstore_signer.py b/tests/check_sigstore_signer.py index 6e24df7c..166da527 100644 --- a/tests/check_sigstore_signer.py +++ b/tests/check_sigstore_signer.py @@ -20,6 +20,10 @@ from tempfile import TemporaryDirectory from unittest import mock +from securesystemslib.exceptions import ( + UnverifiedSignatureError, + VerificationError, +) from securesystemslib.signer import ( SIGNER_FOR_URI_SCHEME, Signer, @@ -100,8 +104,19 @@ def test_sign(self): signer = Signer.from_priv_key_uri(uri, public_key) sig = signer.sign(b"data") + + # Successful verification public_key.verify_signature(sig, b"data") + # Signature mismatch + with self.assertRaises(UnverifiedSignatureError): + public_key.verify_signature(sig, b"incorrect data") + + # Broken bundle + sig.unrecognized_fields["bundle"]["verificationMaterial"] = None + with self.assertRaises(VerificationError): + public_key.verify_signature(sig, b"data") + if __name__ == "__main__": unittest.main(verbosity=4, buffer=False) diff --git a/tox.ini b/tox.ini index 64649f45..2493fb02 100644 --- a/tox.ini +++ b/tox.ini @@ -63,6 +63,7 @@ commands = deps = -r{toxinidir}/requirements-pinned.txt -r{toxinidir}/requirements-lint.txt + -r{toxinidir}/requirements-sigstore.txt commands = black --check --diff . isort --check --diff .