diff --git a/README.md b/README.md index 6c32c57..f73b868 100644 --- a/README.md +++ b/README.md @@ -20,22 +20,22 @@ See the full API documentation [here]. ```python from pathlib import Path -from pypi_attestation_models import sigstore_to_pypi -from sigstore_protobuf_specs.dev.sigstore.bundle.v1 import Bundle +from pypi_attestation_models import pypi_to_sigstore, sigstore_to_pypi, Attestation +from sigstore.models import Bundle # Sigstore Bundle -> PEP 740 Attestation object bundle_path = Path("test_package-0.0.1-py3-none-any.whl.sigstore") with bundle_path.open("rb") as f: - sigstore_bundle = Bundle().from_json(f.read()) + sigstore_bundle = Bundle.from_json(f.read()) attestation_object = sigstore_to_pypi(sigstore_bundle) -print(attestation_object.to_json()) +print(attestation_object.model_dump_json()) # PEP 740 Attestation object -> Sigstore Bundle attestation_path = Path("attestation.json") with attestation_path.open("rb") as f: - attestation = impl.Attestation.from_dict(json.load(f)) -bundle = impl.pypi_to_sigstore(attestation) + attestation = Attestation.model_validate_json(f.read()) +bundle = pypi_to_sigstore(attestation) print(bundle.to_json()) ``` diff --git a/pyproject.toml b/pyproject.toml index 3c4b62e..dfced75 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,8 @@ classifiers = [ "Programming Language :: Python :: 3", "License :: OSI Approved :: Apache Software License", ] -dependencies = ["sigstore-protobuf-specs"] +# TODO pin sigstore since we deppend on Bundle._inner, which is an object from the protobuf models and could change +dependencies = ["cryptography", "pydantic", "sigstore @ git+https://github.com/sigstore/sigstore-python.git@7583a787ab808d3780e1fcdae86b8420fde939b8"] requires-python = ">=3.9" [project.optional-dependencies] diff --git a/src/pypi_attestation_models/__init__.py b/src/pypi_attestation_models/__init__.py index f1816b2..b3ba743 100644 --- a/src/pypi_attestation_models/__init__.py +++ b/src/pypi_attestation_models/__init__.py @@ -6,7 +6,6 @@ Attestation, ConversionError, InvalidAttestationError, - InvalidBundleError, VerificationMaterial, pypi_to_sigstore, sigstore_to_pypi, @@ -16,7 +15,6 @@ "Attestation", "ConversionError", "InvalidAttestationError", - "InvalidBundleError", "VerificationMaterial", "pypi_to_sigstore", "sigstore_to_pypi", diff --git a/src/pypi_attestation_models/_impl.py b/src/pypi_attestation_models/_impl.py index d57df42..75f8dd8 100644 --- a/src/pypi_attestation_models/_impl.py +++ b/src/pypi_attestation_models/_impl.py @@ -6,30 +6,20 @@ from __future__ import annotations import binascii -import json from base64 import b64decode, b64encode -from dataclasses import asdict, dataclass -from typing import Any, Literal +from typing import Annotated, Any, Literal, NewType -import sigstore_protobuf_specs.dev.sigstore.bundle.v1 as sigstore -from sigstore_protobuf_specs.dev.sigstore.common.v1 import MessageSignature, X509Certificate -from sigstore_protobuf_specs.dev.sigstore.rekor.v1 import TransparencyLogEntry - -_NO_CERTIFICATES_ERROR_MESSAGE = "No certificates found in Sigstore Bundle" +from annotated_types import MinLen # noqa: TCH002 +from cryptography import x509 +from cryptography.hazmat.primitives import serialization +from pydantic import BaseModel +from sigstore.models import Bundle, LogEntry class ConversionError(ValueError): """The base error for all errors during conversion.""" -class InvalidBundleError(ConversionError): - """The Sigstore Bundle given as input is not valid.""" - - def __init__(self: InvalidBundleError, msg: str) -> None: - """Initialize an `InvalidBundleError`.""" - super().__init__(f"Could not convert input Bundle: {msg}") - - class InvalidAttestationError(ConversionError): """The PyPI Attestation given as input is not valid.""" @@ -38,8 +28,10 @@ def __init__(self: InvalidAttestationError, msg: str) -> None: super().__init__(f"Could not convert input Attestation: {msg}") -@dataclass -class VerificationMaterial: +TransparencyLogEntry = NewType("TransparencyLogEntry", dict[str, Any]) + + +class VerificationMaterial(BaseModel): """Cryptographic materials used to verify attestation objects.""" certificate: str @@ -47,23 +39,14 @@ class VerificationMaterial: The signing certificate, as `base64(DER(cert))`. """ - transparency_entries: list[dict[str, Any]] + transparency_entries: Annotated[list[TransparencyLogEntry], MinLen(1)] """ One or more transparency log entries for this attestation's signature and certificate. """ - @staticmethod - def from_dict(dict_input: dict[str, Any]) -> VerificationMaterial: - """Create a VerificationMaterial object from a dict.""" - return VerificationMaterial( - certificate=dict_input["certificate"], - transparency_entries=dict_input["transparency_entries"], - ) - -@dataclass -class Attestation: +class Attestation(BaseModel): """Attestation object as defined in PEP 740.""" version: Literal[1] @@ -82,71 +65,25 @@ class Attestation: is the raw bytes of the signing operation. """ - def to_json(self: Attestation) -> str: - """Serialize the attestation object into JSON.""" - return json.dumps(asdict(self)) - - @staticmethod - def from_dict(dict_input: dict[str, Any]) -> Attestation: - """Create an Attestation object from a dict.""" - return Attestation( - version=dict_input["version"], - verification_material=VerificationMaterial.from_dict( - dict_input["verification_material"], - ), - message_signature=dict_input["message_signature"], - ) - - -@dataclass -class Provenance: - """Provenance object as defined in PEP 740.""" - - version: Literal[1] - """ - The provenance object's version, which is always 1. - """ - - publisher: object | None - """ - An optional open-ended JSON object, specific to the kind of Trusted - Publisher used to publish the file, if one was used. - """ - attestations: list[Attestation] - """ - One or more attestation objects. - """ - - -def sigstore_to_pypi(sigstore_bundle: sigstore.Bundle) -> Attestation: - """Convert a Sigstore Bundle into a PyPI attestation object, as defined in PEP 740.""" - certificate = sigstore_bundle.verification_material.certificate.raw_bytes - if certificate == b"": - # If there's no single certificate, we check for a leaf certificate in the - # x509_certificate_chain.certificates` field. - certificates = sigstore_bundle.verification_material.x509_certificate_chain.certificates - if not certificates: - raise InvalidBundleError(_NO_CERTIFICATES_ERROR_MESSAGE) - # According to the spec, the first member of the sequence MUST be the leaf certificate - # conveying the signing key - certificate = certificates[0].raw_bytes - - certificate = b64encode(certificate).decode("ascii") - tlog_entries = [t.to_dict() for t in sigstore_bundle.verification_material.tlog_entries] - verification_material = VerificationMaterial( - certificate=certificate, - transparency_entries=tlog_entries, +def sigstore_to_pypi(sigstore_bundle: Bundle) -> Attestation: + """Convert a Sigstore Bundle into a PyPI attestation as defined in PEP 740.""" + certificate = sigstore_bundle.signing_certificate.public_bytes( + encoding=serialization.Encoding.DER ) + signature = sigstore_bundle._inner.message_signature.signature # noqa: SLF001 return Attestation( version=1, - verification_material=verification_material, - message_signature=b64encode(sigstore_bundle.message_signature.signature).decode("ascii"), + verification_material=VerificationMaterial( + certificate=b64encode(certificate).decode("ascii"), + transparency_entries=[sigstore_bundle.log_entry._to_dict_rekor()], # noqa: SLF001 + ), + message_signature=b64encode(signature).decode("ascii"), ) -def pypi_to_sigstore(pypi_attestation: Attestation) -> sigstore.Bundle: +def pypi_to_sigstore(pypi_attestation: Attestation) -> Bundle: """Convert a PyPI attestation object as defined in PEP 740 into a Sigstore Bundle.""" try: certificate_bytes = b64decode(pypi_attestation.verification_material.certificate) @@ -154,18 +91,10 @@ def pypi_to_sigstore(pypi_attestation: Attestation) -> sigstore.Bundle: except binascii.Error as err: raise InvalidAttestationError(str(err)) from err - certificate = X509Certificate(raw_bytes=certificate_bytes) - tlog_entries = [ - TransparencyLogEntry().from_dict(x) - for x in pypi_attestation.verification_material.transparency_entries - ] + tlog_entry = pypi_attestation.verification_material.transparency_entries[0] - verification_material = sigstore.VerificationMaterial( - certificate=certificate, - tlog_entries=tlog_entries, - ) - return sigstore.Bundle( - media_type="application/vnd.dev.sigstore.bundle+json;version=0.3", - verification_material=verification_material, - message_signature=MessageSignature(signature=signature_bytes), + return Bundle.from_parts( + cert=x509.load_der_x509_certificate(certificate_bytes), + sig=signature_bytes, + log_entry=LogEntry._from_dict_rekor(tlog_entry), # noqa: SLF001 ) diff --git a/test/assets/rfc8785-0.0.2-py3-none-any.whl.json b/test/assets/rfc8785-0.0.2-py3-none-any.whl.json deleted file mode 100644 index 2d61d51..0000000 --- a/test/assets/rfc8785-0.0.2-py3-none-any.whl.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "version": 1, - "verification_material": { - "certificate": "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", - "transparency_entries": [ - { - "logIndex": "76153733", - "logId": { - "keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=" - }, - "kindVersion": { - "kind": "hashedrekord", - "version": "0.0.1" - }, - "integratedTime": "1709764175", - "inclusionPromise": { - "signedEntryTimestamp": "MEUCIBf3PdlV0ZcrEbGAEgk4MqZ/2XrXVaoNgNY7SPtb4UAjAiEAtaNeC7d3W2nVXv3k/bO9kVn8tGm/VLqNmq6HmXip7gk=" - }, - "inclusionProof": { - "logIndex": "71990302", - "rootHash": "H4vwFjFMXB1wMptG/HzWZxTAgVMQeRLyxYVAjj0V77Q=", - "treeSize": "71990304", - "hashes": [ - "XXmMcYxafHY92ufCKlDRFQwaNYJ3DKDjVCuLrI3RgS4=", - "MAl1oWo7cvENduy3CJT4vKGWWm2THPYS857XyrIIGnQ=", - "oL5/l+vGeEBm3EMXuGB6bKzrDQgob9s16l2LqzsmZg0=", - "17Uy+jw+z+pkXH5rjqeDC2vmP9CRuCa6Ci7AZw99HjU=", - "yg2mFeuGCgCZW2AYrQZWlRs8VYsz1xInyl3sFzTOC84=", - "yTX4XLfADd7KZrB1B1BOaHpH122CDK+GzwDXlay8j+k=", - "mV2QNtrV+WDkXBNPjE3Im6+kLyPsZoMfQguW8X1KBKo=", - "hdXMInhF1t95XTq7cglzKp5fw6gL4Z/NKAOpfM8X0hE=", - "kpmAb9wGPYqvRAzw4czyQLynD593JMN/wxSv0qD66JE=", - "AcSXgK1w5baiJJ/RBVtApFQMMIZJtwE1Q6UASzPH+zs=", - "CayiUjRB6Htq5omYI+/lGCgRJmGU1DDkMvBLZiP1r4w=", - "XE7+Pykrktsdsy1ru6V4IsFAOKTJosu3KUa0//TCa0w=", - "7Z18YLBAvejEV4nJHIKoks/xlijnhR005qTW2w4QtHg=", - "98enzMaC+x5oCMvIZQA5z8vu2apDMCFvE/935NfuPw8=" - ], - "checkpoint": { - "envelope": "rekor.sigstore.dev - 2605736670972794746\n71990304\nH4vwFjFMXB1wMptG/HzWZxTAgVMQeRLyxYVAjj0V77Q=\n\n— rekor.sigstore.dev wNI9ajBFAiBXGe9HyQ/f0gV0XkOPNYPiKxN43AbeZdMD0SppBabIowIhAL+jT6Z/wWrGrtn7qdldgVH/jlRAweoFIhTnKvvLNysu\n" - } - }, - "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiIwYTE4NjY2NjM0MGI1N2MxYzhmMzY1NDI5YTA3ZjU1MjYxYTFhNjBiMjk1N2Y2M2IyOTQ4MjBjNGUzYzBiNWU0In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJSElLV3pTd3dJNjJHcUhkb3Zjc1dpVzBLYk5hSFMwYjQyTEJzeHlSVk5EckFpRUErdEFLbkcrdk9mbXpGNVNmeEJrRlduaDdvR2xsNWhPRWl6YVdvK1dYdHZnPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVZDZha05EUW14VFowRjNTVUpCWjBsVlQzaFNZbXd6VUVSU2VVWnNWbFZwU0ZwVFZHcElWRFZ3ZGpnNGQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFJkMDE2UVRKTmFrbDVUMVJOTVZkb1kwNU5hbEYzVFhwQk1rMXFTWHBQVkUweFYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVUzTURsWVdGZHRSazlGZEhKWlMzcHFSRkJxWjIwMVRVdFRZV05QVVdGRVJXbFhkVWtLVVVwSU5IVjROV3h1U1Rndk1XTTFWVlIyVkVOMGRtZzNVR2RNVW05aVFXWmpiamhUWmtGdmJtbHJNa3RCUlhoak1HRlBRMEpZVFhkbloxWjJUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZWZVdaUENtRm1RMUY1UVV0cFYzQlRlVkZuUzFvMGFEUktURGRaZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDFwbldVUldVakJTUVZGSUwwSkdkM2RYYjFwWllVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVEROU2VWbFhiSE5pTWxwcFlWaFNlZ3BNTTBwdFdYcG5NMDlFVlhWalNHdDJURzFrY0dSSGFERlphVGt6WWpOS2NscHRlSFprTTAxMlkyMVdjMXBYUm5wYVV6VTFZbGQ0UVdOdFZtMWplVGt3Q2xsWFpIcE1NMWwzVEdwQmRVMXFRVFZDWjI5eVFtZEZSVUZaVHk5TlFVVkNRa04wYjJSSVVuZGplbTkyVEROU2RtRXlWblZNYlVacVpFZHNkbUp1VFhVS1dqSnNNR0ZJVm1sa1dFNXNZMjFPZG1KdVVteGlibEYxV1RJNWRFMUNWVWREYVhOSFFWRlJRbWMzT0hkQlVVbEZRak5LYkdKSFZtaGpNbFYzVG1kWlN3cExkMWxDUWtGSFJIWjZRVUpCZDFGdlRWUkthMXBxWXpST1ZGWnBUa1JDYWxsWFdUSk5WR3N6VFdwRk1WbHRSbXhaYlZreVRWUk5kMXBFVW1sT2VrcHRDbGxYVVhkTmVrRldRbWR2Y2tKblJVVkJXVTh2VFVGRlJVSkJaSGxhVjNoc1dWaE9iRTFEVVVkRGFYTkhRVkZSUW1jM09IZEJVVlZGUm01U2VWbFhiSE1LWWpKYWFXRllVbnBNTTBwdFdYcG5NMDlFVlhWalNHdDNTR2RaUzB0M1dVSkNRVWRFZG5wQlFrSm5VVkZqYlZadFkzazVNRmxYWkhwTU0xbDNUR3BCZFFwTmFrRTNRbWR2Y2tKblJVVkJXVTh2VFVGRlNVSkRNRTFMTW1nd1pFaENlazlwT0haa1J6bHlXbGMwZFZsWFRqQmhWemwxWTNrMWJtRllVbTlrVjBveENtTXlWbmxaTWpsMVpFZFdkV1JETldwaU1qQjNZVUZaUzB0M1dVSkNRVWRFZG5wQlFrTlJVbUZFUm1odlpFaFNkMk42YjNaTU1tUndaRWRvTVZscE5Xb0tZakl3ZG1SSVNtaGhWM2gyV20xS2NHUklUWFpqYlZwcVQwUmpORTVUTlhkbFV6aDFXakpzTUdGSVZtbE1NMlIyWTIxMGJXSkhPVE5qZVRsNVdsZDRiQXBaV0U1c1RHNXNkR0pGUW5sYVYxcDZURE5TYUZvelRYWmtha0YxVFVNMGVVMUVaMGREYVhOSFFWRlJRbWMzT0hkQlVXOUZTMmQzYjAxVVNtdGFhbU0wQ2s1VVZtbE9SRUpxV1ZkWk1rMVVhek5OYWtVeFdXMUdiRmx0V1RKTlZFMTNXa1JTYVU1NlNtMVpWMUYzVFhwQlpFSm5iM0pDWjBWRlFWbFBMMDFCUlV3S1FrRTRUVVJYWkhCa1IyZ3hXV2t4YjJJelRqQmFWMUYzVDFGWlMwdDNXVUpDUVVkRWRucEJRa1JCVVhKRVEyeHZaRWhTZDJONmIzWk1NbVJ3WkVkb01RcFphVFZxWWpJd2RtUklTbWhoVjNoMldtMUtjR1JJVFhaamJWcHFUMFJqTkU1VE5YZGxWRUUwUW1kdmNrSm5SVVZCV1U4dlRVRkZUa0pEYjAxTFJFVjVDbHBIV1ROUFJGVXhXV3BSZDFreVJtMU9ha1UxVG5wSmVFNVhTbWhhVjBwdFRtcEZlazFIVVRCWmFtTjVXbTFHYTAxRVRYZEpRVmxMUzNkWlFrSkJSMFFLZG5wQlFrUm5VVk5FUWtKNVdsZGFla3d6VW1oYU0wMTJaR3BCZFUxRE5IbE5RbXRIUTJselIwRlJVVUpuTnpoM1FWRTRSVU4zZDBwT2VsazBUV3BGZWdwUFZHc3pUVU0wUjBOcGMwZEJVVkZDWnpjNGQwRlNRVVZKUVhkbFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVEROU2VWbFhiSE5pTWxwcENtRllVbnBOUW1OSFEybHpSMEZSVVVKbk56aDNRVkpGUlVOUmQwaE5hazE0VGtSUmVVMTZRbTlDWjI5eVFtZEZSVUZaVHk5TlFVVlRRa1p2VFZkSGFEQUtaRWhDZWs5cE9IWmFNbXd3WVVoV2FVeHRUblppVXprd1kyMUdjR0pIT1cxWmJXd3dZM2s1ZVZwdFRUUk9lbWN4VEc1Q05VeDVOVzVoV0ZKdlpGZEpkZ3BrTWpsNVlUSmFjMkl6WkhwTU0wcHNZa2RXYUdNeVZYVmxWekZ6VVVoS2JGcHVUWFprUjBadVkzazVNazFETkhkTWFrbDNUMEZaUzB0M1dVSkNRVWRFQ25aNlFVSkZkMUZ4UkVObmVFMXRVbTFPZW1jeFRsZEpNRTFIVG1oYWFsbDRUMVJqZVUxVVZtbFpWMVpwV21wWmVFMTZRbXRPUjBrelRXMWFhRnBFUVhvS1RVSmpSME5wYzBkQlVWRkNaemM0ZDBGU1VVVkRVWGRJWTIxV2MxcFhSbnBhVkVKalFtZHZja0puUlVWQldVOHZUVUZGVmtKRk5FMVVSMmd3WkVoQ2VncFBhVGgyV2pKc01HRklWbWxNYlU1MllsTTVNR050Um5CaVJ6bHRXVzFzTUdONU9YbGFiVTAwVG5wbk1VeHVRalZNTWtacVpFZHNkbUp1VFhaamJsWjFDbU41T0RSTlZHTTFUbXBuZVUxRVRUUk1Na1l3WkVkV2RHTklVbnBNZWtWM1JtZFpTMHQzV1VKQ1FVZEVkbnBCUWtablVVbEVRVnAzWkZkS2MyRlhUWGNLWjFsdlIwTnBjMGRCVVZGQ01XNXJRMEpCU1VWbVFWSTJRVWhuUVdSblJHUlFWRUp4ZUhOalVrMXRUVnBJYUhsYVducGpRMjlyY0dWMVRqUTRjbVlyU0FwcGJrdEJUSGx1ZFdwblFVRkJXVFJXTkhWWVkwRkJRVVZCZDBKSVRVVlZRMGxGYjFONmJEUlNkWHA2TW5jM2RqQXZWbFl4WWprMlJuQmtVRXMwZEhkekNuaDBUM2xtWkhKNVNYSnFhVUZwUlVFd1MwRjROSEkwV0cxaFFXOWpSa0Z0WTNvMUwxQXhOMFk0VDFSaFlucE9UbFJaTVdKV01UbG1lbGRyZDBObldVa0tTMjlhU1hwcU1FVkJkMDFFWVVGQmQxcFJTWGRJWmxVNVlqbElVVk50WTNCVWNqTnhSMVZpVTJWS1pHa3haa3AzUVRad1oyRTFaRTl5YmtGcFFtd3hWZ280ZWt0aFQzUkdlbkpIUTNWaGRsSnJNMXBHYmtGcVJVRXJjMUJsYlZwaVRuRnVObmt2UkhwVU5YQkRaMk5rUmpWc1JWZDJlRTlWUXpOaWN6QjVSMU53Q20weVJWWktSRWxJYkU5elRDdHVZWE15YVRscmRWcDVVUW90TFMwdExVVk9SQ0JEUlZKVVNVWkpRMEZVUlMwdExTMHRDZz09In19fX0=" - } - ] - }, - "message_signature": "MEUCIHIKWzSwwI62GqHdovcsWiW0KbNaHS0b42LBsxyRVNDrAiEA+tAKnG+vOfmzF5SfxBkFWnh7oGll5hOEizaWo+WXtvg=" -} \ No newline at end of file diff --git a/test/assets/rfc8785-0.0.2-py3-none-any.whl.sigstore b/test/assets/rfc8785-0.0.2-py3-none-any.whl.sigstore deleted file mode 100644 index 483e943..0000000 --- a/test/assets/rfc8785-0.0.2-py3-none-any.whl.sigstore +++ /dev/null @@ -1,60 +0,0 @@ -{ - "mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.2", - "verificationMaterial": { - "x509CertificateChain": { - "certificates": [ - { - "rawBytes": "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" - } - ] - }, - "tlogEntries": [ - { - "logIndex": "76153733", - "logId": { - "keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=" - }, - "kindVersion": { - "kind": "hashedrekord", - "version": "0.0.1" - }, - "integratedTime": "1709764175", - "inclusionPromise": { - "signedEntryTimestamp": "MEUCIBf3PdlV0ZcrEbGAEgk4MqZ/2XrXVaoNgNY7SPtb4UAjAiEAtaNeC7d3W2nVXv3k/bO9kVn8tGm/VLqNmq6HmXip7gk=" - }, - "inclusionProof": { - "logIndex": "71990302", - "rootHash": "H4vwFjFMXB1wMptG/HzWZxTAgVMQeRLyxYVAjj0V77Q=", - "treeSize": "71990304", - "hashes": [ - "XXmMcYxafHY92ufCKlDRFQwaNYJ3DKDjVCuLrI3RgS4=", - "MAl1oWo7cvENduy3CJT4vKGWWm2THPYS857XyrIIGnQ=", - "oL5/l+vGeEBm3EMXuGB6bKzrDQgob9s16l2LqzsmZg0=", - "17Uy+jw+z+pkXH5rjqeDC2vmP9CRuCa6Ci7AZw99HjU=", - "yg2mFeuGCgCZW2AYrQZWlRs8VYsz1xInyl3sFzTOC84=", - "yTX4XLfADd7KZrB1B1BOaHpH122CDK+GzwDXlay8j+k=", - "mV2QNtrV+WDkXBNPjE3Im6+kLyPsZoMfQguW8X1KBKo=", - "hdXMInhF1t95XTq7cglzKp5fw6gL4Z/NKAOpfM8X0hE=", - "kpmAb9wGPYqvRAzw4czyQLynD593JMN/wxSv0qD66JE=", - "AcSXgK1w5baiJJ/RBVtApFQMMIZJtwE1Q6UASzPH+zs=", - "CayiUjRB6Htq5omYI+/lGCgRJmGU1DDkMvBLZiP1r4w=", - "XE7+Pykrktsdsy1ru6V4IsFAOKTJosu3KUa0//TCa0w=", - "7Z18YLBAvejEV4nJHIKoks/xlijnhR005qTW2w4QtHg=", - "98enzMaC+x5oCMvIZQA5z8vu2apDMCFvE/935NfuPw8=" - ], - "checkpoint": { - "envelope": "rekor.sigstore.dev - 2605736670972794746\n71990304\nH4vwFjFMXB1wMptG/HzWZxTAgVMQeRLyxYVAjj0V77Q=\n\n— rekor.sigstore.dev wNI9ajBFAiBXGe9HyQ/f0gV0XkOPNYPiKxN43AbeZdMD0SppBabIowIhAL+jT6Z/wWrGrtn7qdldgVH/jlRAweoFIhTnKvvLNysu\n" - } - }, - "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiIwYTE4NjY2NjM0MGI1N2MxYzhmMzY1NDI5YTA3ZjU1MjYxYTFhNjBiMjk1N2Y2M2IyOTQ4MjBjNGUzYzBiNWU0In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJSElLV3pTd3dJNjJHcUhkb3Zjc1dpVzBLYk5hSFMwYjQyTEJzeHlSVk5EckFpRUErdEFLbkcrdk9mbXpGNVNmeEJrRlduaDdvR2xsNWhPRWl6YVdvK1dYdHZnPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVZDZha05EUW14VFowRjNTVUpCWjBsVlQzaFNZbXd6VUVSU2VVWnNWbFZwU0ZwVFZHcElWRFZ3ZGpnNGQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFJkMDE2UVRKTmFrbDVUMVJOTVZkb1kwNU5hbEYzVFhwQk1rMXFTWHBQVkUweFYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVUzTURsWVdGZHRSazlGZEhKWlMzcHFSRkJxWjIwMVRVdFRZV05QVVdGRVJXbFhkVWtLVVVwSU5IVjROV3h1U1Rndk1XTTFWVlIyVkVOMGRtZzNVR2RNVW05aVFXWmpiamhUWmtGdmJtbHJNa3RCUlhoak1HRlBRMEpZVFhkbloxWjJUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZWZVdaUENtRm1RMUY1UVV0cFYzQlRlVkZuUzFvMGFEUktURGRaZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDFwbldVUldVakJTUVZGSUwwSkdkM2RYYjFwWllVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVEROU2VWbFhiSE5pTWxwcFlWaFNlZ3BNTTBwdFdYcG5NMDlFVlhWalNHdDJURzFrY0dSSGFERlphVGt6WWpOS2NscHRlSFprTTAxMlkyMVdjMXBYUm5wYVV6VTFZbGQ0UVdOdFZtMWplVGt3Q2xsWFpIcE1NMWwzVEdwQmRVMXFRVFZDWjI5eVFtZEZSVUZaVHk5TlFVVkNRa04wYjJSSVVuZGplbTkyVEROU2RtRXlWblZNYlVacVpFZHNkbUp1VFhVS1dqSnNNR0ZJVm1sa1dFNXNZMjFPZG1KdVVteGlibEYxV1RJNWRFMUNWVWREYVhOSFFWRlJRbWMzT0hkQlVVbEZRak5LYkdKSFZtaGpNbFYzVG1kWlN3cExkMWxDUWtGSFJIWjZRVUpCZDFGdlRWUkthMXBxWXpST1ZGWnBUa1JDYWxsWFdUSk5WR3N6VFdwRk1WbHRSbXhaYlZreVRWUk5kMXBFVW1sT2VrcHRDbGxYVVhkTmVrRldRbWR2Y2tKblJVVkJXVTh2VFVGRlJVSkJaSGxhVjNoc1dWaE9iRTFEVVVkRGFYTkhRVkZSUW1jM09IZEJVVlZGUm01U2VWbFhiSE1LWWpKYWFXRllVbnBNTTBwdFdYcG5NMDlFVlhWalNHdDNTR2RaUzB0M1dVSkNRVWRFZG5wQlFrSm5VVkZqYlZadFkzazVNRmxYWkhwTU0xbDNUR3BCZFFwTmFrRTNRbWR2Y2tKblJVVkJXVTh2VFVGRlNVSkRNRTFMTW1nd1pFaENlazlwT0haa1J6bHlXbGMwZFZsWFRqQmhWemwxWTNrMWJtRllVbTlrVjBveENtTXlWbmxaTWpsMVpFZFdkV1JETldwaU1qQjNZVUZaUzB0M1dVSkNRVWRFZG5wQlFrTlJVbUZFUm1odlpFaFNkMk42YjNaTU1tUndaRWRvTVZscE5Xb0tZakl3ZG1SSVNtaGhWM2gyV20xS2NHUklUWFpqYlZwcVQwUmpORTVUTlhkbFV6aDFXakpzTUdGSVZtbE1NMlIyWTIxMGJXSkhPVE5qZVRsNVdsZDRiQXBaV0U1c1RHNXNkR0pGUW5sYVYxcDZURE5TYUZvelRYWmtha0YxVFVNMGVVMUVaMGREYVhOSFFWRlJRbWMzT0hkQlVXOUZTMmQzYjAxVVNtdGFhbU0wQ2s1VVZtbE9SRUpxV1ZkWk1rMVVhek5OYWtVeFdXMUdiRmx0V1RKTlZFMTNXa1JTYVU1NlNtMVpWMUYzVFhwQlpFSm5iM0pDWjBWRlFWbFBMMDFCUlV3S1FrRTRUVVJYWkhCa1IyZ3hXV2t4YjJJelRqQmFWMUYzVDFGWlMwdDNXVUpDUVVkRWRucEJRa1JCVVhKRVEyeHZaRWhTZDJONmIzWk1NbVJ3WkVkb01RcFphVFZxWWpJd2RtUklTbWhoVjNoMldtMUtjR1JJVFhaamJWcHFUMFJqTkU1VE5YZGxWRUUwUW1kdmNrSm5SVVZCV1U4dlRVRkZUa0pEYjAxTFJFVjVDbHBIV1ROUFJGVXhXV3BSZDFreVJtMU9ha1UxVG5wSmVFNVhTbWhhVjBwdFRtcEZlazFIVVRCWmFtTjVXbTFHYTAxRVRYZEpRVmxMUzNkWlFrSkJSMFFLZG5wQlFrUm5VVk5FUWtKNVdsZGFla3d6VW1oYU0wMTJaR3BCZFUxRE5IbE5RbXRIUTJselIwRlJVVUpuTnpoM1FWRTRSVU4zZDBwT2VsazBUV3BGZWdwUFZHc3pUVU0wUjBOcGMwZEJVVkZDWnpjNGQwRlNRVVZKUVhkbFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVEROU2VWbFhiSE5pTWxwcENtRllVbnBOUW1OSFEybHpSMEZSVVVKbk56aDNRVkpGUlVOUmQwaE5hazE0VGtSUmVVMTZRbTlDWjI5eVFtZEZSVUZaVHk5TlFVVlRRa1p2VFZkSGFEQUtaRWhDZWs5cE9IWmFNbXd3WVVoV2FVeHRUblppVXprd1kyMUdjR0pIT1cxWmJXd3dZM2s1ZVZwdFRUUk9lbWN4VEc1Q05VeDVOVzVoV0ZKdlpGZEpkZ3BrTWpsNVlUSmFjMkl6WkhwTU0wcHNZa2RXYUdNeVZYVmxWekZ6VVVoS2JGcHVUWFprUjBadVkzazVNazFETkhkTWFrbDNUMEZaUzB0M1dVSkNRVWRFQ25aNlFVSkZkMUZ4UkVObmVFMXRVbTFPZW1jeFRsZEpNRTFIVG1oYWFsbDRUMVJqZVUxVVZtbFpWMVpwV21wWmVFMTZRbXRPUjBrelRXMWFhRnBFUVhvS1RVSmpSME5wYzBkQlVWRkNaemM0ZDBGU1VVVkRVWGRJWTIxV2MxcFhSbnBhVkVKalFtZHZja0puUlVWQldVOHZUVUZGVmtKRk5FMVVSMmd3WkVoQ2VncFBhVGgyV2pKc01HRklWbWxNYlU1MllsTTVNR050Um5CaVJ6bHRXVzFzTUdONU9YbGFiVTAwVG5wbk1VeHVRalZNTWtacVpFZHNkbUp1VFhaamJsWjFDbU41T0RSTlZHTTFUbXBuZVUxRVRUUk1Na1l3WkVkV2RHTklVbnBNZWtWM1JtZFpTMHQzV1VKQ1FVZEVkbnBCUWtablVVbEVRVnAzWkZkS2MyRlhUWGNLWjFsdlIwTnBjMGRCVVZGQ01XNXJRMEpCU1VWbVFWSTJRVWhuUVdSblJHUlFWRUp4ZUhOalVrMXRUVnBJYUhsYVducGpRMjlyY0dWMVRqUTRjbVlyU0FwcGJrdEJUSGx1ZFdwblFVRkJXVFJXTkhWWVkwRkJRVVZCZDBKSVRVVlZRMGxGYjFONmJEUlNkWHA2TW5jM2RqQXZWbFl4WWprMlJuQmtVRXMwZEhkekNuaDBUM2xtWkhKNVNYSnFhVUZwUlVFd1MwRjROSEkwV0cxaFFXOWpSa0Z0WTNvMUwxQXhOMFk0VDFSaFlucE9UbFJaTVdKV01UbG1lbGRyZDBObldVa0tTMjlhU1hwcU1FVkJkMDFFWVVGQmQxcFJTWGRJWmxVNVlqbElVVk50WTNCVWNqTnhSMVZpVTJWS1pHa3haa3AzUVRad1oyRTFaRTl5YmtGcFFtd3hWZ280ZWt0aFQzUkdlbkpIUTNWaGRsSnJNMXBHYmtGcVJVRXJjMUJsYlZwaVRuRnVObmt2UkhwVU5YQkRaMk5rUmpWc1JWZDJlRTlWUXpOaWN6QjVSMU53Q20weVJWWktSRWxJYkU5elRDdHVZWE15YVRscmRWcDVVUW90TFMwdExVVk9SQ0JEUlZKVVNVWkpRMEZVUlMwdExTMHRDZz09In19fX0=" - } - ] - }, - "messageSignature": { - "messageDigest": { - "algorithm": "SHA2_256", - "digest": "ChhmZjQLV8HI82VCmgf1UmGhpgspV/Y7KUggxOPAteQ=" - }, - "signature": "MEUCIHIKWzSwwI62GqHdovcsWiW0KbNaHS0b42LBsxyRVNDrAiEA+tAKnG+vOfmzF5SfxBkFWnh7oGll5hOEizaWo+WXtvg=" - } -} \ No newline at end of file diff --git a/test/assets/rfc8785-0.1.2-py3-none-any.whl b/test/assets/rfc8785-0.1.2-py3-none-any.whl new file mode 100644 index 0000000..00225ac Binary files /dev/null and b/test/assets/rfc8785-0.1.2-py3-none-any.whl differ diff --git a/test/assets/rfc8785-0.1.2-py3-none-any.whl.json b/test/assets/rfc8785-0.1.2-py3-none-any.whl.json new file mode 100644 index 0000000..bf2affa --- /dev/null +++ b/test/assets/rfc8785-0.1.2-py3-none-any.whl.json @@ -0,0 +1,49 @@ +{ + "version": 1, + "verification_material": { + "certificate": "MIIC1zCCAl2gAwIBAgIUZk9ToGFUJexy+/rxwIF8BB+3C5YwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjQwNDI5MTY1MTMzWhcNMjQwNDI5MTcwMTMzWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEM5H5f7A4HutVTfKFimTd2UbTzgUOY7rph9GKqgsZ7ChAp8FGJbrrgn6o+nprUEFKqEaIi+fWQJvR+RJkoQcWMKOCAXwwggF4MA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUnrc9nJ2dxJd1a5sCFj/P+y3MuhQwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wLAYDVR0RAQH/BCIwIIEeZmFjdW5kby50dWVzY2FAdHJhaWxvZmJpdHMuY29tMCkGCisGAQQBg78wAQEEG2h0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbTArBgorBgEEAYO/MAEIBB0MG2h0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbTCBigYKKwYBBAHWeQIEAgR8BHoAeAB2AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6OAAABjyrE0sQAAAQDAEcwRQIhAORhP1HaCcD4QK8+8VcNL46W0AAk6cIDUAH3SV4stJUVAiBafyw+FTpgvoTU+2U7QCyjlQZ5J2dPpVqv9Up3vV2GTDAKBggqhkjOPQQDAwNoADBlAjEAzoA4cMHxHCEXA80ahwJUSz/1kYotTXRNzeWU69SyaZE7Po+vZ5/ANfKvbCv9s19rAjABbw/INkA4dGKWEDNtSjnloZuH5N9aPBOV425+iKCe2bmf9cYVlFvCbGmHiEg/r5k=", + "transparency_entries": [ + { + "logIndex": "89569370", + "logId": { + "keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=" + }, + "kindVersion": { + "kind": "hashedrekord", + "version": "0.0.1" + }, + "integratedTime": "1714409493", + "inclusionPromise": { + "signedEntryTimestamp": "MEUCIQC29r0j8BCu5Zye9dvFaVBmCFzHIDBDDWH+LqcZ8aTAyAIgas85UjhG4jrVwqBr/U/nQ86vwOWQnHKjGnd/bdnQafQ=" + }, + "inclusionProof": { + "logIndex": "85405939", + "rootHash": "CdYMKt8P8arrU1iilSLHbPDwMzhWgUbA6xvb1DSZktc=", + "treeSize": "85405941", + "hashes": [ + "btr1R1ZOzi0Kqk+vdfHrDcC1zasNMvXaNehse4NeMu8=", + "8IgEN8pEU3WrVRCsbbCkFHeamV5xoyN1OByng98lPow=", + "i6x3rMfR1HZCafUIGTyYgvtfwjF9zUe9Q5MPKNdWhS8=", + "kE6NsBxRpnT9Q/DgLDqQELBaor5pThUMmHIRuKapA7c=", + "Y55DSeWN5DUnIuvK5RRsaF4b35EtjKasFpV2n2LfrA0=", + "UeVeMpyn/bEywYJvThS5PltcrELznbc/OFTARixCNUQ=", + "MaMJLiuvSigqcgOZ6BulADPyhWaoYE1C+sGj/twciwM=", + "YrLY+ujALEUYcIeyq2ri+QBsl7Sxh+frMg7GlVcIvZ0=", + "27GLifvQI1aATtmSwJQGmbKXDoBpa0R5Q8fQUuX3/kQ=", + "xhLV7tE4kPldhHSKGpGuBkcxIUSpEvNntVSzM+5raW4=", + "btR5C6cRDz4AcGce5sOqhKIlEYTQ29AJLmv7L3kPyDI=", + "TuI0yJQvmNIs3J9pfzPMu2aYibKQ0MUpGgkcmjsS30g=", + "TxcrLS66touilnjU30hIZ8JkbH6bfnBbD6pQ5OoIpXs=", + "sjohk/3DQIfXTgf/5XpwtdF7yNbrf8YykOMHr1CyBYQ=", + "98enzMaC+x5oCMvIZQA5z8vu2apDMCFvE/935NfuPw8=" + ], + "checkpoint": { + "envelope": "rekor.sigstore.dev - 2605736670972794746\n85405941\nCdYMKt8P8arrU1iilSLHbPDwMzhWgUbA6xvb1DSZktc=\n\n— rekor.sigstore.dev wNI9ajBGAiEAkEVEjIsNbuvKywuzjow1tyD1IkIpHu/bCVK73fSpBzECIQD5ctzIS0Rp3cC3PF0ZcFEP8ObC2KJqojg4hfKjFxxy9A==\n" + } + }, + "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjNGU5MmU5ZWNjODI4YmVmMmFhN2RiYTFkZThhYzk4MzUxMWY3NTMyYTBkZjExYzc3MGQzOTA5OWEyNWNmMjAxIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJSGlXY2JRY2szTzE2K3dGR3pyR09sYWVWRkpodkNwT1EwajZJd0ZtUnp0YUFpQWJWL3NOSWg0OFJQSHdIdm9IZklDcFA3Y29seGpjbUx6WEhsU2pleGEvUVE9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTXhla05EUVd3eVowRjNTVUpCWjBsVldtczVWRzlIUmxWS1pYaDVLeTl5ZUhkSlJqaENRaXN6UXpWWmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFJkMDVFU1RWTlZGa3hUVlJOZWxkb1kwNU5hbEYzVGtSSk5VMVVZM2ROVkUxNlYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZOTlVnMVpqZEJORWgxZEZaVVprdEdhVzFVWkRKVllsUjZaMVZQV1RkeWNHZzVSMHNLY1dkeldqZERhRUZ3T0VaSFNtSnljbWR1Tm04cmJuQnlWVVZHUzNGRllVbHBLMlpYVVVwMlVpdFNTbXR2VVdOWFRVdFBRMEZZZDNkblowWTBUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZ1Y21NNUNtNUtNbVI0U21ReFlUVnpRMFpxTDFBcmVUTk5kV2hSZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDB4QldVUldVakJTUVZGSUwwSkRTWGRKU1VWbFdtMUdhbVJYTld0aWVUVXdaRmRXZWxreVJrRmtTRXBvWVZkNGRscHRTbkJrU0UxMVdUSTVkQXBOUTJ0SFEybHpSMEZSVVVKbk56aDNRVkZGUlVjeWFEQmtTRUo2VDJrNGRsbFhUbXBpTTFaMVpFaE5kVm95T1haYU1uaHNURzFPZG1KVVFYSkNaMjl5Q2tKblJVVkJXVTh2VFVGRlNVSkNNRTFITW1nd1pFaENlazlwT0haWlYwNXFZak5XZFdSSVRYVmFNamwyV2pKNGJFeHRUblppVkVOQ2FXZFpTMHQzV1VJS1FrRklWMlZSU1VWQloxSTRRa2h2UVdWQlFqSkJUakE1VFVkeVIzaDRSWGxaZUd0bFNFcHNiazUzUzJsVGJEWTBNMnA1ZEM4MFpVdGpiMEYyUzJVMlR3cEJRVUZDYW5seVJUQnpVVUZCUVZGRVFVVmpkMUpSU1doQlQxSm9VREZJWVVOalJEUlJTemdyT0ZaalRrdzBObGN3UVVGck5tTkpSRlZCU0ROVFZqUnpDblJLVlZaQmFVSmhabmwzSzBaVWNHZDJiMVJWS3pKVk4xRkRlV3BzVVZvMVNqSmtVSEJXY1hZNVZYQXpkbFl5UjFSRVFVdENaMmR4YUd0cVQxQlJVVVFLUVhkT2IwRkVRbXhCYWtWQmVtOUJOR05OU0hoSVEwVllRVGd3WVdoM1NsVlRlaTh4YTFsdmRGUllVazU2WlZkVk5qbFRlV0ZhUlRkUWJ5dDJXalV2UVFwT1prdDJZa04yT1hNeE9YSkJha0ZDWW5jdlNVNXJRVFJrUjB0WFJVUk9kRk5xYm14dlduVklOVTQ1WVZCQ1QxWTBNalVyYVV0RFpUSmliV1k1WTFsV0NteEdka05pUjIxSWFVVm5MM0kxYXowS0xTMHRMUzFGVGtRZ1EwVlNWRWxHU1VOQlZFVXRMUzB0TFFvPSJ9fX19" + } + ] + }, + "message_signature": "MEQCIHiWcbQck3O16+wFGzrGOlaeVFJhvCpOQ0j6IwFmRztaAiAbV/sNIh48RPHwHvoHfICpP7colxjcmLzXHlSjexa/QQ==" +} \ No newline at end of file diff --git a/test/assets/rfc8785-0.1.2-py3-none-any.whl.sigstore b/test/assets/rfc8785-0.1.2-py3-none-any.whl.sigstore new file mode 100644 index 0000000..290da36 --- /dev/null +++ b/test/assets/rfc8785-0.1.2-py3-none-any.whl.sigstore @@ -0,0 +1,57 @@ +{ + "mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json", + "verificationMaterial": { + "certificate": { + "rawBytes": "MIIC1zCCAl2gAwIBAgIUZk9ToGFUJexy+/rxwIF8BB+3C5YwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjQwNDI5MTY1MTMzWhcNMjQwNDI5MTcwMTMzWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEM5H5f7A4HutVTfKFimTd2UbTzgUOY7rph9GKqgsZ7ChAp8FGJbrrgn6o+nprUEFKqEaIi+fWQJvR+RJkoQcWMKOCAXwwggF4MA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUnrc9nJ2dxJd1a5sCFj/P+y3MuhQwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wLAYDVR0RAQH/BCIwIIEeZmFjdW5kby50dWVzY2FAdHJhaWxvZmJpdHMuY29tMCkGCisGAQQBg78wAQEEG2h0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbTArBgorBgEEAYO/MAEIBB0MG2h0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbTCBigYKKwYBBAHWeQIEAgR8BHoAeAB2AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6OAAABjyrE0sQAAAQDAEcwRQIhAORhP1HaCcD4QK8+8VcNL46W0AAk6cIDUAH3SV4stJUVAiBafyw+FTpgvoTU+2U7QCyjlQZ5J2dPpVqv9Up3vV2GTDAKBggqhkjOPQQDAwNoADBlAjEAzoA4cMHxHCEXA80ahwJUSz/1kYotTXRNzeWU69SyaZE7Po+vZ5/ANfKvbCv9s19rAjABbw/INkA4dGKWEDNtSjnloZuH5N9aPBOV425+iKCe2bmf9cYVlFvCbGmHiEg/r5k=" + }, + "tlogEntries": [ + { + "logIndex": "89569370", + "logId": { + "keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=" + }, + "kindVersion": { + "kind": "hashedrekord", + "version": "0.0.1" + }, + "integratedTime": "1714409493", + "inclusionPromise": { + "signedEntryTimestamp": "MEUCIQC29r0j8BCu5Zye9dvFaVBmCFzHIDBDDWH+LqcZ8aTAyAIgas85UjhG4jrVwqBr/U/nQ86vwOWQnHKjGnd/bdnQafQ=" + }, + "inclusionProof": { + "logIndex": "85405939", + "rootHash": "CdYMKt8P8arrU1iilSLHbPDwMzhWgUbA6xvb1DSZktc=", + "treeSize": "85405941", + "hashes": [ + "btr1R1ZOzi0Kqk+vdfHrDcC1zasNMvXaNehse4NeMu8=", + "8IgEN8pEU3WrVRCsbbCkFHeamV5xoyN1OByng98lPow=", + "i6x3rMfR1HZCafUIGTyYgvtfwjF9zUe9Q5MPKNdWhS8=", + "kE6NsBxRpnT9Q/DgLDqQELBaor5pThUMmHIRuKapA7c=", + "Y55DSeWN5DUnIuvK5RRsaF4b35EtjKasFpV2n2LfrA0=", + "UeVeMpyn/bEywYJvThS5PltcrELznbc/OFTARixCNUQ=", + "MaMJLiuvSigqcgOZ6BulADPyhWaoYE1C+sGj/twciwM=", + "YrLY+ujALEUYcIeyq2ri+QBsl7Sxh+frMg7GlVcIvZ0=", + "27GLifvQI1aATtmSwJQGmbKXDoBpa0R5Q8fQUuX3/kQ=", + "xhLV7tE4kPldhHSKGpGuBkcxIUSpEvNntVSzM+5raW4=", + "btR5C6cRDz4AcGce5sOqhKIlEYTQ29AJLmv7L3kPyDI=", + "TuI0yJQvmNIs3J9pfzPMu2aYibKQ0MUpGgkcmjsS30g=", + "TxcrLS66touilnjU30hIZ8JkbH6bfnBbD6pQ5OoIpXs=", + "sjohk/3DQIfXTgf/5XpwtdF7yNbrf8YykOMHr1CyBYQ=", + "98enzMaC+x5oCMvIZQA5z8vu2apDMCFvE/935NfuPw8=" + ], + "checkpoint": { + "envelope": "rekor.sigstore.dev - 2605736670972794746\n85405941\nCdYMKt8P8arrU1iilSLHbPDwMzhWgUbA6xvb1DSZktc=\n\n\u2014 rekor.sigstore.dev wNI9ajBGAiEAkEVEjIsNbuvKywuzjow1tyD1IkIpHu/bCVK73fSpBzECIQD5ctzIS0Rp3cC3PF0ZcFEP8ObC2KJqojg4hfKjFxxy9A==\n" + } + }, + "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjNGU5MmU5ZWNjODI4YmVmMmFhN2RiYTFkZThhYzk4MzUxMWY3NTMyYTBkZjExYzc3MGQzOTA5OWEyNWNmMjAxIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJSGlXY2JRY2szTzE2K3dGR3pyR09sYWVWRkpodkNwT1EwajZJd0ZtUnp0YUFpQWJWL3NOSWg0OFJQSHdIdm9IZklDcFA3Y29seGpjbUx6WEhsU2pleGEvUVE9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTXhla05EUVd3eVowRjNTVUpCWjBsVldtczVWRzlIUmxWS1pYaDVLeTl5ZUhkSlJqaENRaXN6UXpWWmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFJkMDVFU1RWTlZGa3hUVlJOZWxkb1kwNU5hbEYzVGtSSk5VMVVZM2ROVkUxNlYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZOTlVnMVpqZEJORWgxZEZaVVprdEdhVzFVWkRKVllsUjZaMVZQV1RkeWNHZzVSMHNLY1dkeldqZERhRUZ3T0VaSFNtSnljbWR1Tm04cmJuQnlWVVZHUzNGRllVbHBLMlpYVVVwMlVpdFNTbXR2VVdOWFRVdFBRMEZZZDNkblowWTBUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZ1Y21NNUNtNUtNbVI0U21ReFlUVnpRMFpxTDFBcmVUTk5kV2hSZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDB4QldVUldVakJTUVZGSUwwSkRTWGRKU1VWbFdtMUdhbVJYTld0aWVUVXdaRmRXZWxreVJrRmtTRXBvWVZkNGRscHRTbkJrU0UxMVdUSTVkQXBOUTJ0SFEybHpSMEZSVVVKbk56aDNRVkZGUlVjeWFEQmtTRUo2VDJrNGRsbFhUbXBpTTFaMVpFaE5kVm95T1haYU1uaHNURzFPZG1KVVFYSkNaMjl5Q2tKblJVVkJXVTh2VFVGRlNVSkNNRTFITW1nd1pFaENlazlwT0haWlYwNXFZak5XZFdSSVRYVmFNamwyV2pKNGJFeHRUblppVkVOQ2FXZFpTMHQzV1VJS1FrRklWMlZSU1VWQloxSTRRa2h2UVdWQlFqSkJUakE1VFVkeVIzaDRSWGxaZUd0bFNFcHNiazUzUzJsVGJEWTBNMnA1ZEM4MFpVdGpiMEYyUzJVMlR3cEJRVUZDYW5seVJUQnpVVUZCUVZGRVFVVmpkMUpSU1doQlQxSm9VREZJWVVOalJEUlJTemdyT0ZaalRrdzBObGN3UVVGck5tTkpSRlZCU0ROVFZqUnpDblJLVlZaQmFVSmhabmwzSzBaVWNHZDJiMVJWS3pKVk4xRkRlV3BzVVZvMVNqSmtVSEJXY1hZNVZYQXpkbFl5UjFSRVFVdENaMmR4YUd0cVQxQlJVVVFLUVhkT2IwRkVRbXhCYWtWQmVtOUJOR05OU0hoSVEwVllRVGd3WVdoM1NsVlRlaTh4YTFsdmRGUllVazU2WlZkVk5qbFRlV0ZhUlRkUWJ5dDJXalV2UVFwT1prdDJZa04yT1hNeE9YSkJha0ZDWW5jdlNVNXJRVFJrUjB0WFJVUk9kRk5xYm14dlduVklOVTQ1WVZCQ1QxWTBNalVyYVV0RFpUSmliV1k1WTFsV0NteEdka05pUjIxSWFVVm5MM0kxYXowS0xTMHRMUzFGVGtRZ1EwVlNWRWxHU1VOQlZFVXRMUzB0TFFvPSJ9fX19" + } + ] + }, + "messageSignature": { + "messageDigest": { + "algorithm": "SHA2_256", + "digest": "xOkunsyCi+8qp9uh3orJg1EfdTKg3xHHcNOQmaJc8gE=" + }, + "signature": "MEQCIHiWcbQck3O16+wFGzrGOlaeVFJhvCpOQ0j6IwFmRztaAiAbV/sNIh48RPHwHvoHfICpP7colxjcmLzXHlSjexa/QQ==" + } +} diff --git a/test/test_impl.py b/test/test_impl.py index 293e7b6..ab7d70a 100644 --- a/test/test_impl.py +++ b/test/test_impl.py @@ -5,63 +5,78 @@ import pypi_attestation_models._impl as impl import pytest -from sigstore_protobuf_specs.dev.sigstore.bundle.v1 import Bundle +from sigstore.models import Bundle +from sigstore.verify import Verifier, policy -bundle_path = Path(__file__).parent / "assets" / "rfc8785-0.0.2-py3-none-any.whl.sigstore" -attestation_path = Path(__file__).parent / "assets" / "rfc8785-0.0.2-py3-none-any.whl.json" +artifact_path = Path(__file__).parent / "assets" / "rfc8785-0.1.2-py3-none-any.whl" +bundle_path = Path(__file__).parent / "assets" / "rfc8785-0.1.2-py3-none-any.whl.sigstore" +attestation_path = Path(__file__).parent / "assets" / "rfc8785-0.1.2-py3-none-any.whl.json" def test_sigstore_to_pypi() -> None: + # Load an existing Sigstore bundle, convert it to a PyPI attestation, + # and check that the result is what we expect. with bundle_path.open("rb") as f: - sigstore_bundle = Bundle().from_json(f.read()) + sigstore_bundle = Bundle.from_json(f.read()) attestation = impl.sigstore_to_pypi(sigstore_bundle) with attestation_path.open("rb") as expected_file: - assert json.loads(attestation.to_json()) == json.load(expected_file) - - -def test_sigstore_to_pypi_empty_certs() -> None: - with bundle_path.open("rb") as f: - sigstore_bundle = Bundle().from_json(f.read()) - sigstore_bundle.verification_material.certificate.raw_bytes = b"" - sigstore_bundle.verification_material.x509_certificate_chain.certificates = [] - - with pytest.raises(impl.InvalidBundleError): - impl.sigstore_to_pypi(sigstore_bundle) + assert json.loads(attestation.model_dump_json()) == json.load(expected_file) def test_pypi_to_sigstore() -> None: + # Load an existing PyPI attestation, convert it to a Sigstore bundle, + # and check that the result matches the original Sigstore bundle used + # to generate the attestation with attestation_path.open("rb") as f: - attestation = impl.Attestation.from_dict(json.load(f)) + attestation = impl.Attestation.model_validate_json(f.read()) bundle = impl.pypi_to_sigstore(attestation) with bundle_path.open("rb") as original_bundle_file: - original_bundle = Bundle().from_json(original_bundle_file.read()) + original_bundle = Bundle.from_json(original_bundle_file.read()) # Sigstore Bundle -> PyPI attestation is a lossy operation, so when we go backwards - # the resulting Bundle will have fewer fields than the original Bundle. Not only that, - # but the fields present might be different (e.g: the original bundle might have a - # `x509_certificate_chain` field, but the converted bundle will use the `certificate` field - # instead). - assert bundle.media_type == "application/vnd.dev.sigstore.bundle+json;version=0.3" - assert bundle.message_signature.signature == original_bundle.message_signature.signature + # the resulting Bundle will have fewer fields than the original Bundle. + assert bundle._inner.media_type == original_bundle._inner.media_type # noqa: SLF001 + assert bundle._inner.verification_material == original_bundle._inner.verification_material # noqa: SLF001 assert ( - bundle.verification_material.tlog_entries - == original_bundle.verification_material.tlog_entries + bundle._inner.message_signature.signature # noqa: SLF001 + == original_bundle._inner.message_signature.signature # noqa: SLF001 ) - if original_bundle.verification_material.certificate.raw_bytes != b"": - assert ( - bundle.verification_material.certificate - == original_bundle.verification_material.certificate - ) - else: - assert ( - bundle.verification_material.certificate - == original_bundle.verification_material.x509_certificate_chain.certificates[0] - ) + assert bundle.log_entry == original_bundle.log_entry + assert bundle.signing_certificate == original_bundle.signing_certificate def test_pypi_to_sigstore_invalid_certificate_base64() -> None: with attestation_path.open("rb") as f: - attestation = impl.Attestation.from_dict(json.load(f)) + attestation = impl.Attestation.model_validate_json(f.read()) attestation.verification_material.certificate = "invalid base64 @@@@ string" with pytest.raises(impl.InvalidAttestationError): impl.pypi_to_sigstore(attestation) + + +def test_verification_roundtrip() -> None: + # Load an existing Sigstore bundle, check that verification passes, + # convert it to a PyPI attestation and then back again to a Sigstore + # bundle, and check that verification still passes. + with bundle_path.open("rb") as f: + sigstore_bundle = Bundle.from_json(f.read()) + + verifier = Verifier.production() + with artifact_path.open("rb") as f: + verifier.verify_artifact( + f.read(), + sigstore_bundle, + policy.Identity( + identity="facundo.tuesca@trailofbits.com", issuer="https://accounts.google.com" + ), + ) + + attestation = impl.sigstore_to_pypi(sigstore_bundle) + roundtrip_bundle = impl.pypi_to_sigstore(attestation) + with artifact_path.open("rb") as f: + verifier.verify_artifact( + f.read(), + roundtrip_bundle, + policy.Identity( + identity="facundo.tuesca@trailofbits.com", issuer="https://accounts.google.com" + ), + )