From 9f0d8d74826bde5df34fdebdd9dc50c85251225e Mon Sep 17 00:00:00 2001 From: Ramon Petgrave Date: Mon, 9 Jun 2025 19:25:30 +0000 Subject: [PATCH 1/6] refactor entry validation Signed-off-by: Ramon Petgrave --- sigstore/verify/verifier.py | 253 ++++++++++++++++++++---------------- 1 file changed, 141 insertions(+), 112 deletions(-) diff --git a/sigstore/verify/verifier.py b/sigstore/verify/verifier.py index d885fde8a..536387a1b 100644 --- a/sigstore/verify/verifier.py +++ b/sigstore/verify/verifier.py @@ -440,73 +440,9 @@ def verify_dsse( entry._kind_version.kind == "dsse" and entry._kind_version.version == "0.0.2" ): - try: - v2_body = v2.Entry().from_json(base64.b64decode(entry.body)) - except ValidationError as exc: - raise VerificationError(f"invalid DSSE log entry: {exc}") - - if v2_body.spec.dsse_v002 is None: - raise VerificationError( - "invalid DSSE log entry: missing dsse_v002 field" - ) - - if ( - v2_body.spec.dsse_v002.payload_hash.algorithm - != v1.HashAlgorithm.SHA2_256 - ): - raise VerificationError("expected SHA256 hash in DSSE entry") - - digest = sha256_digest(envelope._inner.payload).digest - if v2_body.spec.dsse_v002.payload_hash.digest != digest: - raise VerificationError("DSSE entry payload hash does not match bundle") - - v2_signatures = [ - v2.Signature( - content=signature.sig, - verifier=v2.Verifier( - x509_certificate=v1.X509Certificate( - bundle.signing_certificate.public_bytes( - encoding=serialization.Encoding.DER - ) - ), - key_details=self._get_key_details(bundle.signing_certificate), - ), - ) - for signature in envelope._inner.signatures - ] - if v2_signatures != v2_body.spec.dsse_v002.signatures: - raise VerificationError("log entry signatures do not match bundle") + validate_dsse_v002_entry_body(bundle) else: - try: - entry_body = rekor_types.Dsse.model_validate_json( - base64.b64decode(entry.body) - ) - except ValidationError as exc: - raise VerificationError(f"invalid DSSE log entry: {exc}") - - payload_hash = sha256_digest(envelope._inner.payload).digest.hex() - if ( - entry_body.spec.root.payload_hash.algorithm # type: ignore[union-attr] - != rekor_types.dsse.Algorithm.SHA256 - ): - raise VerificationError( - "expected SHA256 payload hash in DSSE log entry" - ) - if payload_hash != entry_body.spec.root.payload_hash.value: # type: ignore[union-attr] - raise VerificationError("log entry payload hash does not match bundle") - - # NOTE: Like `dsse._verify`: multiple signatures would be frivolous here, - # but we handle them just in case the signer has somehow produced multiple - # signatures for their envelope with the same signing key. - signatures = [ - rekor_types.dsse.Signature( - signature=base64.b64encode(signature.sig).decode(), - verifier=base64_encode_pem_cert(bundle.signing_certificate), - ) - for signature in envelope._inner.signatures - ] - if signatures != entry_body.spec.root.signatures: - raise VerificationError("log entry signatures do not match bundle") + validate_dsse_v001_entry_body(bundle) return (envelope._inner.payload_type, envelope._inner.payload) @@ -551,58 +487,151 @@ def verify_artifact( # (8): verify the consistency of the log entry's body against # the other bundle materials (and input being verified). entry = bundle.log_entry - if ( entry._kind_version.kind == "hashedrekord" and entry._kind_version.version == "0.0.2" ): - if bundle._inner.message_signature is None: - raise VerificationError( - "invalid hashedrekord log entry: missing message signature" - ) + validate_hashedrekord_v002_entry_body(bundle) + else: + validate_hashedrekord_v001_entry_body(bundle, hashed_input) - v2_expected_body = v2.Entry( - kind=entry._kind_version.kind, - api_version=entry._kind_version.version, - spec=v2.Spec( - hashed_rekord_v002=v2.HashedRekordLogEntryV002( - data=v1.HashOutput( - algorithm=bundle._inner.message_signature.message_digest.algorithm, - digest=bundle._inner.message_signature.message_digest.digest, - ), - signature=v2.Signature( - content=bundle._inner.message_signature.signature, - verifier=v2.Verifier( - x509_certificate=v1.X509Certificate( - bundle.signing_certificate.public_bytes( - encoding=serialization.Encoding.DER - ) - ), - key_details=self._get_key_details( - bundle.signing_certificate - ), - ), - ), + +def validate_dsse_v001_entry_body(bundle: Bundle) -> None: + """ + Valideate the Entry body for dsse v001. + """ + entry = bundle.log_entry + envelope = bundle._dsse_envelope + if envelope is None: + raise VerificationError( + "cannot perform DSSE verification on a bundle without a DSSE envelope" + ) + try: + entry_body = rekor_types.Dsse.model_validate_json(base64.b64decode(entry.body)) + except ValidationError as exc: + raise VerificationError(f"invalid DSSE log entry: {exc}") + + payload_hash = sha256_digest(envelope._inner.payload).digest.hex() + if ( + entry_body.spec.root.payload_hash.algorithm # type: ignore[union-attr] + != rekor_types.dsse.Algorithm.SHA256 + ): + raise VerificationError("expected SHA256 payload hash in DSSE log entry") + if payload_hash != entry_body.spec.root.payload_hash.value: # type: ignore[union-attr] + raise VerificationError("log entry payload hash does not match bundle") + + # NOTE: Like `dsse._verify`: multiple signatures would be frivolous here, + # but we handle them just in case the signer has somehow produced multiple + # signatures for their envelope with the same signing key. + signatures = [ + rekor_types.dsse.Signature( + signature=base64.b64encode(signature.sig).decode(), + verifier=base64_encode_pem_cert(bundle.signing_certificate), + ) + for signature in envelope._inner.signatures + ] + if signatures != entry_body.spec.root.signatures: + raise VerificationError("log entry signatures do not match bundle") + + +def validate_dsse_v002_entry_body(bundle: Bundle) -> None: + """ + Valideate the Entry body for dsse v002. + """ + entry = bundle.log_entry + envelope = bundle._dsse_envelope + if envelope is None: + raise VerificationError( + "cannot perform DSSE verification on a bundle without a DSSE envelope" + ) + try: + v2_body = v2.Entry().from_json(base64.b64decode(entry.body)) + except ValidationError as exc: + raise VerificationError(f"invalid DSSE log entry: {exc}") + + if v2_body.spec.dsse_v002 is None: + raise VerificationError("invalid DSSE log entry: missing dsse_v002 field") + + if v2_body.spec.dsse_v002.payload_hash.algorithm != v1.HashAlgorithm.SHA2_256: + raise VerificationError("expected SHA256 hash in DSSE entry") + + digest = sha256_digest(envelope._inner.payload).digest + if v2_body.spec.dsse_v002.payload_hash.digest != digest: + raise VerificationError("DSSE entry payload hash does not match bundle") + + v2_signatures = [ + v2.Signature( + content=signature.sig, + verifier=v2.Verifier( + x509_certificate=v1.X509Certificate( + bundle.signing_certificate.public_bytes( + encoding=serialization.Encoding.DER ) ), - ) - v2_actual_body = v2.Entry().from_json(base64.b64decode(entry.body)) - if v2_expected_body != v2_actual_body: - raise VerificationError( - "transparency log entry is inconsistent with other materials" - ) + key_details=Verifier._get_key_details(bundle.signing_certificate), + ), + ) + for signature in envelope._inner.signatures + ] + if v2_signatures != v2_body.spec.dsse_v002.signatures: + raise VerificationError("log entry signatures do not match bundle") - else: - expected_body = _hashedrekord_from_parts( - bundle.signing_certificate, - bundle._inner.message_signature.signature, # type: ignore[union-attr] - hashed_input, - ) - actual_body = rekor_types.Hashedrekord.model_validate_json( - base64.b64decode(entry.body) - ) - if expected_body != actual_body: - raise VerificationError( - "transparency log entry is inconsistent with other materials" - ) +def validate_hashedrekord_v001_entry_body(bundle: Bundle, hashed_input: Hashed) -> None: + """ + Valideate the Entry body for hashedrekord v001. + """ + entry = bundle.log_entry + expected_body = _hashedrekord_from_parts( + bundle.signing_certificate, + bundle._inner.message_signature.signature, # type: ignore[union-attr] + hashed_input, + ) + actual_body = rekor_types.Hashedrekord.model_validate_json( + base64.b64decode(entry.body) + ) + if expected_body != actual_body: + raise VerificationError( + "transparency log entry is inconsistent with other materials" + ) + + +def validate_hashedrekord_v002_entry_body(bundle: Bundle) -> None: + """ + Valideate the Entry body for hashedrekord v002. + """ + entry = bundle.log_entry + if bundle._inner.message_signature is None: + raise VerificationError( + "invalid hashedrekord log entry: missing message signature" + ) + v2_expected_body = v2.Entry( + kind=entry._kind_version.kind, + api_version=entry._kind_version.version, + spec=v2.Spec( + hashed_rekord_v002=v2.HashedRekordLogEntryV002( + data=v1.HashOutput( + algorithm=bundle._inner.message_signature.message_digest.algorithm, + digest=bundle._inner.message_signature.message_digest.digest, + ), + signature=v2.Signature( + content=bundle._inner.message_signature.signature, + verifier=v2.Verifier( + x509_certificate=v1.X509Certificate( + bundle.signing_certificate.public_bytes( + encoding=serialization.Encoding.DER + ) + ), + key_details=Verifier._get_key_details( + bundle.signing_certificate + ), + ), + ), + ) + ), + ) + v2_actual_body = v2.Entry().from_json(base64.b64decode(entry.body)) + if v2_expected_body != v2_actual_body: + raise VerificationError( + "transparency log entry is inconsistent with other materials" + ) From 81cce076605e9bc38eac14a1feb4eb56cd6ac9b5 Mon Sep 17 00:00:00 2001 From: Ramon Petgrave Date: Mon, 9 Jun 2025 19:32:22 +0000 Subject: [PATCH 2/6] use staging_with_rekorv2 fixture Signed-off-by: Ramon Petgrave --- test/unit/internal/rekor/test_client_v2.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/test/unit/internal/rekor/test_client_v2.py b/test/unit/internal/rekor/test_client_v2.py index e8058223a..41c0e52a2 100644 --- a/test/unit/internal/rekor/test_client_v2.py +++ b/test/unit/internal/rekor/test_client_v2.py @@ -19,23 +19,19 @@ from sigstore import dsse from sigstore._internal.rekor.client_v2 import ( LogEntry, - RekorV2Client, ) from sigstore.models import rekor_v1 -ALPHA_REKOR_V2_URL = "https://log2025-alpha1.rekor.sigstage.dev" - @pytest.mark.staging @pytest.mark.ambient_oidc -def test_rekor_v2_create_entry_dsse(staging): +def test_rekor_v2_create_entry_dsse(staging_with_rekorv2): # This is not a real unit test: it requires not only staging rekor but also TUF # fulcio and oidc -- maybe useful only until we have real integration tests in place - sign_ctx_cls, _, identity = staging + sign_ctx_cls, _, identity = staging_with_rekorv2 # Hack to run Signer.sign() with staging rekor v2 sign_ctx = sign_ctx_cls() - sign_ctx._rekor = RekorV2Client(ALPHA_REKOR_V2_URL) stmt = ( dsse.StatementBuilder() @@ -64,14 +60,13 @@ def test_rekor_v2_create_entry_dsse(staging): @pytest.mark.staging @pytest.mark.ambient_oidc -def test_rekor_v2_create_entry_hashed_rekord(staging): +def test_rekor_v2_create_entry_hashed_rekord(staging_with_rekorv2): # This is not a real unit test: it requires not only staging rekor but also TUF # fulcio and oidc -- maybe useful only until we have real integration tests in place - sign_ctx_cls, _, identity = staging + sign_ctx_cls, _, identity = staging_with_rekorv2 # Hack to run Signer.sign() with staging rekor v2 sign_ctx = sign_ctx_cls() - sign_ctx._rekor = RekorV2Client(ALPHA_REKOR_V2_URL) with sign_ctx.signer(identity) as signer: bundle = signer.sign_artifact(b"") From d7ddd509d7dff60e732a17b15cb8c8e561e54f8d Mon Sep 17 00:00:00 2001 From: Ramon Petgrave Date: Mon, 9 Jun 2025 19:58:03 +0000 Subject: [PATCH 3/6] staging_with_rekorv2 uses embedded trust config Signed-off-by: Ramon Petgrave --- test/unit/conftest.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/unit/conftest.py b/test/unit/conftest.py index da3ca1573..d53be369e 100644 --- a/test/unit/conftest.py +++ b/test/unit/conftest.py @@ -31,7 +31,6 @@ from id import ( detect_credential, ) -from sigstore_protobuf_specs.dev.sigstore.trustroot.v1 import Service from tuf.api.exceptions import DownloadHTTPError from tuf.ngclient import FetcherInterface, updater @@ -239,9 +238,9 @@ def signer(): @pytest.fixture -def staging_with_rekorv2() -> tuple[ - type[SigningContext], type[Verifier], IdentityToken -]: +def staging_with_rekorv2( + asset, +) -> tuple[type[SigningContext], type[Verifier], IdentityToken]: """ Returns a SigningContext, Verifier, and IdentityToken for the staging environment. The signingContext will use the Rekor V2 instance even if it is not yet enabled in @@ -249,9 +248,10 @@ def staging_with_rekorv2() -> tuple[ """ def signer(): - trust_config = ClientTrustConfig.staging() - trust_config.signing_config._tlogs.append( - Service("https://log2025-alpha1.rekor.sigstage.dev", 2) + trust_config = ClientTrustConfig.from_json( + asset( + os.path.join("trust_config", "staging-but-sign-with-rekor-v2.json") + ).read_text() ) return SigningContext.from_trust_config(trust_config) From 5127e8158d5f389de4bb668c1b9a9a35b7473308 Mon Sep 17 00:00:00 2001 From: Ramon Petgrave Date: Mon, 9 Jun 2025 21:34:40 +0000 Subject: [PATCH 4/6] verify rekorv2 dssse bundle Signed-off-by: Ramon Petgrave --- test/assets/a.dsse.staging-rekor-v2.txt | 5 +++++ .../a.dsse.staging-rekor-v2.txt.sigstore.json | 1 + test/unit/verify/test_verifier.py | 14 +++++++++++++- 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 test/assets/a.dsse.staging-rekor-v2.txt create mode 100644 test/assets/a.dsse.staging-rekor-v2.txt.sigstore.json diff --git a/test/assets/a.dsse.staging-rekor-v2.txt b/test/assets/a.dsse.staging-rekor-v2.txt new file mode 100644 index 000000000..8d0585ac7 --- /dev/null +++ b/test/assets/a.dsse.staging-rekor-v2.txt @@ -0,0 +1,5 @@ +DO NOT MODIFY ME! + +this is "a.txt", a sample input for sigstore-python's unit tests. + +DO NOT MODIFY ME! diff --git a/test/assets/a.dsse.staging-rekor-v2.txt.sigstore.json b/test/assets/a.dsse.staging-rekor-v2.txt.sigstore.json new file mode 100644 index 000000000..af2fe26f5 --- /dev/null +++ b/test/assets/a.dsse.staging-rekor-v2.txt.sigstore.json @@ -0,0 +1 @@ +{"mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json", "verificationMaterial": {"certificate": {"rawBytes": "MIIDBDCCAoqgAwIBAgIUYlZafqye+P/bWSMSdvxrr7y+NUEwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjUwNjA5MjEwNjI1WhcNMjUwNjA5MjExNjI1WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwDj9XB2rrkUTaCgPE3OGPJ+176EZM3u2SK2XLKoMUQn79zywhocahVPybzn/6nMkWkew8SFaDhkL4PCAENNzcqOCAakwggGlMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUQ/OiAAk5AAqjN5apYfVwt/M4S5UwHwYDVR0jBBgwFoAUcYYwphR8Ym/599b0BRp/X//rb6wwWQYDVR0RAQH/BE8wTYFLaW5zZWN1cmUtY2xvdWR0b3Atc2hhcmVkLXVzZXJAY2xvdWR0b3AtcHJvZC11cy1lYXN0LmlhbS5nc2VydmljZWFjY291bnQuY29tMCkGCisGAQQBg78wAQEEG2h0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbTArBgorBgEEAYO/MAEIBB0MG2h0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbTCBigYKKwYBBAHWeQIEAgR8BHoAeAB2ACswvNxoiMni4dgmKV50H0g5MZYC8pwzy15DQP6yrIZ6AAABl1aEEo4AAAQDAEcwRQIhAJzFA8xqE8owuQqk9ao7NLQy/YoTsy23A+ZU3cdL+MM1AiAZyN3FSWf13Fl3oL+P5jAvv0xRyqGrWEyZJw4KO7XhnDAKBggqhkjOPQQDAwNoADBlAjA9OgkRsqwLbt59TB0Jb15NBBQiaNBRRqUdo2FuSrvEWWDnnynmqo0GygnbCmz2CJwCMQDFCWJExAUGX7v5UQUzDz1pc1b0WvX1wAP2fhbgir2yZZRcsr4OdWz31arOo6USvVI="}, "tlogEntries": [{"logIndex": "689", "logId": {"keyId": "8w1amZ2S5mJIQkQmPxdMuOrL/oJkvFg9MnQXmeOCXck="}, "kindVersion": {"kind": "dsse", "version": "0.0.2"}, "inclusionProof": {"logIndex": "689", "rootHash": "VLopDAB81ENEy7SM2Oe4gxf026TulneLw22pUPlt0qE=", "treeSize": "690", "hashes": ["7G2mWiDIVCMp4cUCF9+qqADG/ICLRt3I2I9nqIWaKnA=", "/Fm4+swicRuu0gv27PWsZ2C1hw3IbCcatPnSV6oTbOw=", "9AF3UpKoSTEa5MS8BHGJxKHH9zVkJgn29s03k14ZtdI=", "QMesRTEZdIgthOEinYE/9J7wGv+VmArDZTICj9POmhY=", "UNUMG62rMwoqCqFKknh4R5Ubkf5Z6dj+Pk0m/1xu8uo="], "checkpoint": {"envelope": "log2025-alpha1.rekor.sigstage.dev\n690\nVLopDAB81ENEy7SM2Oe4gxf026TulneLw22pUPlt0qE=\n\n\u2014 log2025-alpha1.rekor.sigstage.dev 8w1amfdsl47Li2mk9esQ1K+vF9tg8WCLlNKBcoVTzrHr4howD6z2171ij8XW6d48AUEoV4PK1DDz5jHUlCQ98okwLQw=\n"}}, "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiZHNzZSIsInNwZWMiOnsiZHNzZVYwMDIiOnsicGF5bG9hZEhhc2giOnsiYWxnb3JpdGhtIjoiU0hBMl8yNTYiLCJkaWdlc3QiOiI0a2QxR3VyKzFmZE1wMHVBZFJyQnBQYTZONXB3OWx0b25pZXdlekg4MmhvPSJ9LCJzaWduYXR1cmVzIjpbeyJjb250ZW50IjoiTUVZQ0lRQ3F6dEJCTXpiYmU3alN6NXFQOE93U3hKWDBFb0VTSGg5d21uRXljUzd3S3dJaEFMd1BIaWt0b2dRY3greFZMWEhsSU56dTI1clRTNW5YRkJ3OEtxcXp5OGZkIiwidmVyaWZpZXIiOnsia2V5RGV0YWlscyI6IlBLSVhfRUNEU0FfUDI1Nl9TSEFfMjU2IiwieDUwOUNlcnRpZmljYXRlIjp7InJhd0J5dGVzIjoiTUlJREJEQ0NBb3FnQXdJQkFnSVVZbFphZnF5ZStQL2JXU01TZHZ4cnI3eStOVUV3Q2dZSUtvWkl6ajBFQXdNd056RVZNQk1HQTFVRUNoTU1jMmxuYzNSdmNtVXVaR1YyTVI0d0hBWURWUVFERXhWemFXZHpkRzl5WlMxcGJuUmxjbTFsWkdsaGRHVXdIaGNOTWpVd05qQTVNakV3TmpJMVdoY05NalV3TmpBNU1qRXhOakkxV2pBQU1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRXdEajlYQjJycmtVVGFDZ1BFM09HUEorMTc2RVpNM3UyU0syWExLb01VUW43OXp5d2hvY2FoVlB5YnpuLzZuTWtXa2V3OFNGYURoa0w0UENBRU5OemNxT0NBYWt3Z2dHbE1BNEdBMVVkRHdFQi93UUVBd0lIZ0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREF6QWRCZ05WSFE0RUZnUVVRL09pQUFrNUFBcWpONWFwWWZWd3QvTTRTNVV3SHdZRFZSMGpCQmd3Rm9BVWNZWXdwaFI4WW0vNTk5YjBCUnAvWC8vcmI2d3dXUVlEVlIwUkFRSC9CRTh3VFlGTGFXNXpaV04xY21VdFkyeHZkV1IwYjNBdGMyaGhjbVZrTFhWelpYSkFZMnh2ZFdSMGIzQXRjSEp2WkMxMWN5MWxZWE4wTG1saGJTNW5jMlZ5ZG1salpXRmpZMjkxYm5RdVkyOXRNQ2tHQ2lzR0FRUUJnNzh3QVFFRUcyaDBkSEJ6T2k4dllXTmpiM1Z1ZEhNdVoyOXZaMnhsTG1OdmJUQXJCZ29yQmdFRUFZTy9NQUVJQkIwTUcyaDBkSEJ6T2k4dllXTmpiM1Z1ZEhNdVoyOXZaMnhsTG1OdmJUQ0JpZ1lLS3dZQkJBSFdlUUlFQWdSOEJIb0FlQUIyQUNzd3ZOeG9pTW5pNGRnbUtWNTBIMGc1TVpZQzhwd3p5MTVEUVA2eXJJWjZBQUFCbDFhRUVvNEFBQVFEQUVjd1JRSWhBSnpGQTh4cUU4b3d1UXFrOWFvN05MUXkvWW9Uc3kyM0ErWlUzY2RMK01NMUFpQVp5TjNGU1dmMTNGbDNvTCtQNWpBdnYweFJ5cUdyV0V5Wkp3NEtPN1hobkRBS0JnZ3Foa2pPUFFRREF3Tm9BREJsQWpBOU9na1JzcXdMYnQ1OVRCMEpiMTVOQkJRaWFOQlJScVVkbzJGdVNydkVXV0RubnlubXFvMEd5Z25iQ216MkNKd0NNUURGQ1dKRXhBVUdYN3Y1VVFVekR6MXBjMWIwV3ZYMXdBUDJmaGJnaXIyeVpaUmNzcjRPZFd6MzFhck9vNlVTdlZJPSJ9fX1dfX19"}], "timestampVerificationData": {"rfc3161Timestamps": [{"signedTimestamp": "MIIE5zADAgEAMIIE3gYJKoZIhvcNAQcCoIIEzzCCBMsCAQMxDTALBglghkgBZQMEAgEwgcEGCyqGSIb3DQEJEAEEoIGxBIGuMIGrAgEBBgkrBgEEAYO/MAIwMTANBglghkgBZQMEAgEFAAQg7mKrZuedCow8ht74HmPFNT7ZP18+JAF/WDRwwOFuzn8CFBKaF0PyLXni4RkH6K+ZuzF9x2JcGA8yMDI1MDYwOTIxMDYyOFowAwIBAQIIWJ9Fv2Y6K7CgMqQwMC4xFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEVMBMGA1UEAxMMc2lnc3RvcmUtdHNhoIICEzCCAg8wggGWoAMCAQICFAo1oQZh1eJBc8aJlqfyffJ+A3ynMAoGCCqGSM49BAMDMDkxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEgMB4GA1UEAxMXc2lnc3RvcmUtdHNhLXNlbGZzaWduZWQwHhcNMjUwMzI4MDkxNDA2WhcNMzUwMzI2MDgxNDA2WjAuMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxFTATBgNVBAMTDHNpZ3N0b3JlLXRzYTB2MBAGByqGSM49AgEGBSuBBAAiA2IABMdb+Rdx6Q/XoB7pJ6QRZUc+0AUQybuGnlc7fcyS0WNJb5sdZRe1gTNnPQDfGRj0LJg6h5STdkf+/kcS5L5S85HNfSDsd/Le5hhhHAe2oFA3Qhfyst0Uy0itF6P9AIB0HaNqMGgwDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBSo/GT2KN4u5jtzT1SMUsThnN1TpTAfBgNVHSMEGDAWgBQ7IEZZXrUyTUcwzm5j7nN0R/IEfTAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAKBggqhkjOPQQDAwNnADBkAjBEr1UuhhrRd9/idfU38BDViV40b+ItPx0BcC1EpF+k31e4NJxvFZ6jRyS7xKQLTo0CMFA97ssE16K0D9Q4G1dPaxfWHp/ghKrP4hKYniVj7LdvNEkjmeTWvncj1ZPf/EhZOjGCAdowggHWAgEBMFEwOTEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MSAwHgYDVQQDExdzaWdzdG9yZS10c2Etc2VsZnNpZ25lZAIUCjWhBmHV4kFzxomWp/J98n4DfKcwCwYJYIZIAWUDBAIBoIH8MBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcNMjUwNjA5MjEwNjI4WjAvBgkqhkiG9w0BCQQxIgQgm3w3T24hj0XJHfurAzfPAUM+UpN9mOfHY9jwsQe6eYkwgY4GCyqGSIb3DQEJEAIvMX8wfTB7MHkEIAb0/+BH/rNZmbczsNejI1Ac/BjkwDNmqEXXdTbnSydEMFUwPaQ7MDkxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEgMB4GA1UEAxMXc2lnc3RvcmUtdHNhLXNlbGZzaWduZWQCFAo1oQZh1eJBc8aJlqfyffJ+A3ynMAoGCCqGSM49BAMCBGYwZAIwJQ/ArYnYtKS38pLXrZ1A/CT1VGgDRUoSkslIGKlHU98qwoWUjjgmmdbeYakSqfENAjABbYaUoMwznhyQd8CKMo7f092Z3Plwa/enOQqgmyu1dAPpmD8rYr2VEjVEGKcvVoY="}]}}, "dsseEnvelope": {"payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoiYS50eHQiLCJkaWdlc3QiOnsic2hhMjU2IjoiZTI0OGE1ZGI0OTMzZGJhNjU3ODIwMDIzOGM5MWE1N2Y1ZTY1YjkyNWI3MzA1MGFlNzg2OTMzNDY4YjdhYzEwMSJ9fV0sInByZWRpY2F0ZVR5cGUiOiJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjEiLCJwcmVkaWNhdGUiOnsiYnVpbGREZWZpbml0aW9uIjp7ImJ1aWxkVHlwZSI6Imh0dHBzOi8vYWN0aW9ucy5naXRodWIuaW8vYnVpbGR0eXBlcy93b3JrZmxvdy92MSIsImV4dGVybmFsUGFyYW1ldGVycyI6eyJ3b3JrZmxvdyI6eyJyZWYiOiJyZWZzL3RhZ3MvMS4yMS4wIiwicmVwb3NpdG9yeSI6Imh0dHBzOi8vZ2l0aHViLmNvbS9vY3RvLW9yZy9vY3RvLXJlcG8iLCJwYXRoIjoiLmdpdGh1Yi93b3JrZmxvd3MvY2kueWFtbCJ9fSwiaW50ZXJuYWxQYXJhbWV0ZXJzIjp7ImdpdGh1YiI6eyJldmVudF9uYW1lIjoicHVzaCIsInJlcG9zaXRvcnlfaWQiOiIwMDAwMDAwMDAiLCJyZXBvc2l0b3J5X293bmVyX2lkIjoiMDAwMDAwMCIsInJ1bm5lcl9lbnZpcm9ubWVudCI6ImdpdGh1Yi1ob3N0ZWQifX0sInJlc29sdmVkRGVwZW5kZW5jaWVzIjpbeyJ1cmkiOiJnaXQraHR0cHM6Ly9naXRodWIuY29tL29jdG8tb3JnL29jdG8tcmVwb0ByZWZzL3RhZ3MvMS4yMS4wIiwiZGlnZXN0Ijp7ImdpdENvbW1pdCI6IjFhYzkzY2UyMWVlNTI2YjM2ZmQxNTRiOTA1OGQ5N2RmYWE0MjRjNTAifX1dfSwicnVuRGV0YWlscyI6eyJidWlsZGVyIjp7ImlkIjoiaHR0cHM6Ly9naXRodWIuY29tL29jdG8tb3JnL29jdG8tcmVwby8uZ2l0aHViL3dvcmtmbG93cy9kb2NrZXIueWFtbEByZWZzL2hlYWRzL2RldmVsb3BtZW50In0sIm1ldGFkYXRhIjp7Imludm9jYXRpb25JZCI6Imh0dHBzOi8vZ2l0aHViLmNvbS9vY3RvLW9yZy9vY3RvLXJlcG8vYWN0aW9ucy9ydW5zLzEwMzEzOTgzMjE4L2F0dGVtcHRzLzIifX19fQ==", "payloadType": "application/vnd.in-toto+json", "signatures": [{"sig": "MEYCIQCqztBBMzbbe7jSz5qP8OwSxJX0EoESHh9wmnEycS7wKwIhALwPHiktogQcx+xVLXHlINzu25rTS5nXFBw8Kqqzy8fd"}]}} diff --git a/test/unit/verify/test_verifier.py b/test/unit/verify/test_verifier.py index dcbcb6181..1f48a703b 100644 --- a/test/unit/verify/test_verifier.py +++ b/test/unit/verify/test_verifier.py @@ -79,13 +79,25 @@ def test_verifier_multiple_verifications(signing_materials, null_policy): "filename", ("bundle.txt", "bundle_v3.txt", "bundle_v3_alt.txt", "staging-rekor-v2.txt"), ) -def test_verifier_bundle(signing_bundle, null_policy, filename): +def test_verifier_bundle_artifact(signing_bundle, null_policy, filename): (file, bundle) = signing_bundle(filename) verifier = Verifier.staging() verifier.verify_artifact(file.read_bytes(), bundle, null_policy) +@pytest.mark.online +@pytest.mark.parametrize( + "filename", + ("a.dsse.staging-rekor-v2.txt",), +) +def test_verifier_bundle_dsse(signing_bundle, null_policy, filename): + (file, bundle) = signing_bundle(filename) + + verifier = Verifier.staging() + verifier.verify_dsse(bundle, null_policy) + + @pytest.mark.parametrize( "filename", ("bundle.txt", "bundle_v3.txt", "bundle_v3_alt.txt") ) From 0ecd9a08c4a981d0591cce62b44290996621cb70 Mon Sep 17 00:00:00 2001 From: Ramon Petgrave Date: Mon, 9 Jun 2025 21:34:49 +0000 Subject: [PATCH 5/6] Revert "staging_with_rekorv2 uses embedded trust config" This reverts commit d7ddd509d7dff60e732a17b15cb8c8e561e54f8d. Signed-off-by: Ramon Petgrave --- test/unit/conftest.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/unit/conftest.py b/test/unit/conftest.py index d53be369e..da3ca1573 100644 --- a/test/unit/conftest.py +++ b/test/unit/conftest.py @@ -31,6 +31,7 @@ from id import ( detect_credential, ) +from sigstore_protobuf_specs.dev.sigstore.trustroot.v1 import Service from tuf.api.exceptions import DownloadHTTPError from tuf.ngclient import FetcherInterface, updater @@ -238,9 +239,9 @@ def signer(): @pytest.fixture -def staging_with_rekorv2( - asset, -) -> tuple[type[SigningContext], type[Verifier], IdentityToken]: +def staging_with_rekorv2() -> tuple[ + type[SigningContext], type[Verifier], IdentityToken +]: """ Returns a SigningContext, Verifier, and IdentityToken for the staging environment. The signingContext will use the Rekor V2 instance even if it is not yet enabled in @@ -248,10 +249,9 @@ def staging_with_rekorv2( """ def signer(): - trust_config = ClientTrustConfig.from_json( - asset( - os.path.join("trust_config", "staging-but-sign-with-rekor-v2.json") - ).read_text() + trust_config = ClientTrustConfig.staging() + trust_config.signing_config._tlogs.append( + Service("https://log2025-alpha1.rekor.sigstage.dev", 2) ) return SigningContext.from_trust_config(trust_config) From 29f14bd1fe2420d88a99eec1e85c8073f4138060 Mon Sep 17 00:00:00 2001 From: Ramon Petgrave Date: Mon, 9 Jun 2025 22:12:17 +0000 Subject: [PATCH 6/6] use ambient oidc Signed-off-by: Ramon Petgrave --- test/unit/test_sign.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit/test_sign.py b/test/unit/test_sign.py index 46a26fb99..b7f056379 100644 --- a/test/unit/test_sign.py +++ b/test/unit/test_sign.py @@ -52,6 +52,7 @@ def test_sign_rekor_entry_consistent(sign_ctx_and_ident_for_env): @pytest.mark.staging +@pytest.mark.ambient_oidc def test_sign_with_staging_rekor_v2(staging_with_rekorv2, null_policy): ctx_cls, verifier_cls, identity = staging_with_rekorv2