Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 195 additions & 0 deletions .github/scripts/release-integrity-test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
# Copyright 2026 Hodeitek S.L.
"""Tests of release-integrity.py that need no network: certificate parsing and the matching rules.

Usage: python3 .github/scripts/release-integrity-test.py

Tests whose names contain "negative" feed changed data on purpose; they run in CI.
"""

import importlib.util
import json
import pathlib
import tempfile
import unittest

spec = importlib.util.spec_from_file_location(
"release_integrity", pathlib.Path(__file__).with_name("release-integrity.py")
)
ri = importlib.util.module_from_spec(spec)
spec.loader.exec_module(ri)

REPO = "Hodeitek/hodeishield-cli"
COMMIT_041 = "b90bb412c45df7318e1300dfdb04f80e87ab77f5"
RUN_041 = 38235963722

# The certificate of the Rekor entry for SHA256SUMS of v0.4.1 (log index 3226122241), public data.
CERT_041 = b"""-----BEGIN CERTIFICATE-----
MIIHDzCCBpagAwIBAgIUWE8VP/HNaszjJPJvW74SgHLvfucwCgYIKoZIzj0EAwMw
NzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRl
cm1lZGlhdGUwHhcNMjYxMDEyMTAzMTU4WhcNMjYxMDEyMTA0MTU4WjAAMFkwEwYH
KoZIzj0CAQYIKoZIzj0DAQcDQgAEMqNSAObLo6cpTWUTKgPZip2AmZutq/BdQ7EI
GFM3ThzIef8qNB0zf9IVoQg7pXDetns1X/Gvvv6I50QWhd2ZJqOCBbUwggWxMA4G
A1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAfBgNVHSMEGDAWgBTf
0+nPViQRlvmo2OkoVaLGLhhkPzBoBgNVHREBAf8EXjBchlpodHRwczovL2dpdGh1
Yi5jb20vSG9kZWl0ZWsvaG9kZWlzaGllbGQtY2xpLy5naXRodWIvd29ya2Zsb3dz
L3JlbGVhc2UueW1sQHJlZnMvdGFncy92MC40LjEwOQYKKwYBBAGDvzABAQQraHR0
cHM6Ly90b2tlbi5hY3Rpb25zLmdpdGh1YnVzZXJjb250ZW50LmNvbTASBgorBgEE
AYO/MAECBARwdXNoMDYGCisGAQQBg78wAQMEKGI5MGJiNDEyYzQ1ZGY3MzE4ZTEz
MDBkZmRiMDRmODBlODdhYjc3ZjUwFQYKKwYBBAGDvzABBAQHUmVsZWFzZTAmBgor
BgEEAYO/MAEFBBhIb2RlaXRlay9ob2RlaXNoaWVsZC1jbGkwHgYKKwYBBAGDvzAB
BgQQcmVmcy90YWdzL3YwLjQuMTA7BgorBgEEAYO/MAEIBC0MK2h0dHBzOi8vdG9r
ZW4uYWN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20wagYKKwYBBAGDvzABCQRc
DFpodHRwczovL2dpdGh1Yi5jb20vSG9kZWl0ZWsvaG9kZWlzaGllbGQtY2xpLy5n
aXRodWIvd29ya2Zsb3dzL3JlbGVhc2UueW1sQHJlZnMvdGFncy92MC40LjEwOAYK
KwYBBAGDvzABCgQqDChiOTBiYjQxMmM0NWRmNzMxOGUxMzAwZGZkYjA0ZjgwZTg3
YWI3N2Y1MB0GCisGAQQBg78wAQsEDwwNZ2l0aHViLWhvc3RlZDA7BgorBgEEAYO/
MAEMBC0MK2h0dHBzOi8vZ2l0aHViLmNvbS9Ib2RlaXRlay9ob2RlaXNoaWVsZC1j
bGkwOAYKKwYBBAGDvzABDQQqDChiOTBiYjQxMmM0NWRmNzMxOGUxMzAwZGZkYjA0
ZjgwZTg3YWI3N2Y1MCAGCisGAQQBg78wAQ4EEgwQcmVmcy90YWdzL3YwLjQuMTAa
BgorBgEEAYO/MAEPBAwMCjEzOTI0OTU3NTkwKwYKKwYBBAGDvzABEAQdDBtodHRw
czovL2dpdGh1Yi5jb20vSG9kZWl0ZWswGQYKKwYBBAGDvzABEQQLDAkxNDg0Mzgz
MjgwagYKKwYBBAGDvzABEgRcDFpodHRwczovL2dpdGh1Yi5jb20vSG9kZWl0ZWsv
aG9kZWlzaGllbGQtY2xpLy5naXRodWIvd29ya2Zsb3dzL3JlbGVhc2UueW1sQHJl
ZnMvdGFncy92MC40LjEwOAYKKwYBBAGDvzABEwQqDChiOTBiYjQxMmM0NWRmNzMx
OGUxMzAwZGZkYjA0ZjgwZTg3YWI3N2Y1MBQGCisGAQQBg78wARQEBgwEcHVzaDBf
BgorBgEEAYO/MAEVBFEMT2h0dHBzOi8vZ2l0aHViLmNvbS9Ib2RlaXRlay9ob2Rl
aXNoaWVsZC1jbGkvYWN0aW9ucy9ydW5zLzM4MjM1OTYzNzIyL2F0dGVtcHRzLzEw
FgYKKwYBBAGDvzABFgQIDAZwdWJsaWMwVwYKKwYBBAGDvzABGARJDEdyZXBvOkhv
ZGVpdGVrQDE0ODQzODMyOC9ob2RlaXNoaWVsZC1jbGlAMTM5MjQ5NTc1OTpyZWY6
cmVmcy90YWdzL3YwLjQuMTCBiwYKKwYBBAHWeQIEAgR9BHsAeQB3AN09MGrGxxEy
YxkeHJlnNwKiSl643jyt/4eKcoAvKe6OAAABoS+rT98AAAQDAEgwRgIhANthYw/v
Cw9kgTo0isgfmp6O+YLofMuiq5pH5IOF0sZ+AiEAymmgynFJ5b3K625NWO6GcR2H
NzpYKdtUT9U1mxlzTOUwCgYIKoZIzj0EAwMDZwAwZAIwLpVXYbtm/e3BxGbXZooo
78R+2TfpZmEMySsqMcGmiz0o/SrBh/zyPFQ2pjIQrP3zAjBmeXHzGhn+2NiLw/l0
HRDT5K2jj/SVAV+Ew477J6XEFQbuJ93C16d7eFGa6t9LbUo=
-----END CERTIFICATE-----
"""


def certificate(tag="v0.4.1", commit=COMMIT_041, run=RUN_041, issuer=ri.ISSUER, repo=REPO):
return {
"san": f"https://github.com/{repo}/.github/workflows/release.yml@refs/tags/{tag}",
"issuer": issuer,
"commit": commit,
"run": f"https://github.com/{repo}/actions/runs/{run}/attempts/1",
}


RELEASES = {
"v0.4.1": (COMMIT_041, {RUN_041}),
"v0.4.0": ("3405f04" + "0" * 33, {38194904546}),
}


class Certificate(unittest.TestCase):
def test_reads_the_identity_of_a_real_release_certificate(self):
self.assertEqual(ri.identity(CERT_041), certificate())

def test_a_truncated_certificate_is_an_error_not_an_identity(self):
der = ri.base64.b64decode(b"".join(CERT_041.strip().splitlines()[1:-1]))
with self.assertRaises(ValueError):
ri.identity(der[:200])


class Rekor(unittest.TestCase):
def judge(self, *certificates, tag="v0.4.1"):
entries = [(index, c) for index, c in enumerate(certificates)]
return ri.judge_rekor(entries, repository=REPO, tag=tag, releases=RELEASES)

def test_the_release_s_own_entry_matches(self):
self.assertEqual(self.judge(certificate()), (True, []))

def test_no_entry_of_the_release_is_not_a_match(self):
self.assertEqual(self.judge(), (False, []))

def test_an_entry_of_another_published_release_is_not_a_mismatch(self):
other = certificate("v0.4.0", RELEASES["v0.4.0"][0], 38194904546)
self.assertEqual(self.judge(certificate(), other), (True, []))

def test_entries_made_by_others_are_ignored(self):
self.assertEqual(self.judge(certificate(repo="someone/else"), None), (False, []))

def test_an_entry_with_this_identity_from_an_unknown_run_is_reported(self):
matched, problems = self.judge(certificate(), certificate(run=1))
self.assertTrue(matched)
self.assertEqual(len(problems), 1)
self.assertIn("matches no published release", problems[0])
self.assertIn("/actions/runs/1/", problems[0])

def test_a_tag_without_a_release_a_wrong_commit_or_issuer_is_reported(self):
for wrong in (
certificate(tag="v9.9.9"),
certificate(commit="0" * 40),
certificate(issuer="https://example.test"),
):
with self.subTest(wrong=wrong):
matched, problems = self.judge(wrong)
self.assertFalse(matched)
self.assertEqual(len(problems), 1)

def test_the_identity_prefix_is_compared_without_case(self):
matched, problems = self.judge(certificate(repo="hodeitek/HodeiShield-CLI"))
self.assertFalse(matched)
self.assertEqual(len(problems), 1)


FIXTURE = json.loads(pathlib.Path(__file__).with_name("release-integrity-testdata.json").read_text())


class TrustRoot(unittest.TestCase):
def setUp(self):
self.workdir = tempfile.TemporaryDirectory()
self.trust = ri.Trust(FIXTURE["trusted_root"], self.workdir.name)
self.entry = FIXTURE["entry"]
body = json.loads(ri.base64.b64decode(self.entry["body"]))
self.der = ri.base64.b64decode(
b"".join(
line
for line in ri.base64.b64decode(body["spec"]["signature"]["publicKey"]["content"]).splitlines()
if not line.startswith(b"-----")
)
)

def tearDown(self):
self.workdir.cleanup()

def test_rekor_signed_the_real_entry(self):
self.assertTrue(self.trust.timestamp_verifies(self.entry))

def test_the_real_certificate_chains_when_it_was_logged(self):
self.assertTrue(self.trust.certificate_chains(self.der, self.entry["integratedTime"]))

def test_negative_a_changed_entry_is_not_signed_by_rekor(self):
for field, value in (("integratedTime", self.entry["integratedTime"] + 1), ("logIndex", 1)):
with self.subTest(field=field):
self.assertFalse(self.trust.timestamp_verifies({**self.entry, field: value}))

def test_negative_the_certificate_does_not_chain_outside_its_ten_minutes(self):
self.assertFalse(self.trust.certificate_chains(self.der, self.entry["integratedTime"] + 3600))

def test_negative_a_certificate_whose_signature_does_not_verify_does_not_chain(self):
changed = self.der[:-1] + bytes([self.der[-1] ^ 1]) # the last byte of its signature
self.assertFalse(self.trust.certificate_chains(changed, self.entry["integratedTime"]))


class Parsing(unittest.TestCase):
def test_sha256sums_in_text_and_binary_mode(self):
text = "a" * 64 + " one.tar.gz\n" + "b" * 64 + " *two.zip\n\n"
self.assertEqual(
ri.parse_sha256sums(text), {"one.tar.gz": "a" * 64, "two.zip": "b" * 64}
)

def test_a_line_that_is_not_a_checksum_is_unreadable(self):
with self.assertRaises(ri.Unreadable):
ri.parse_sha256sums("not a checksum line\n")

def test_stable_versions_only(self):
self.assertEqual(ri.stable_version("v0.4.1"), (0, 4, 1))
self.assertIsNone(ri.stable_version("v0.5.0-rc.1"))
self.assertIsNone(ri.stable_version("0.4.1"))


if __name__ == "__main__":
unittest.main()
106 changes: 106 additions & 0 deletions .github/scripts/release-integrity-testdata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
{
"about": "Public data: Sigstore's Fulcio certificate authorities and Rekor key (from trusted_root.json, as cosign initialize fetched it on 2026-10-12), and the Rekor entry for SHA256SUMS of v0.4.1 (log index 3226122241).",
"trusted_root": {
"certificateAuthorities": [
{
"subject": {
"organization": "sigstore.dev",
"commonName": "sigstore"
},
"uri": "https://fulcio.sigstore.dev",
"certChain": {
"certificates": [
{
"rawBytes": "MIIB+DCCAX6gAwIBAgITNVkDZoCiofPDsy7dfm6geLbuhzAKBggqhkjOPQQDAzAqMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIxMDMwNzAzMjAyOVoXDTMxMDIyMzAzMjAyOVowKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLSyA7Ii5k+pNO8ZEWY0ylemWDowOkNa3kL+GZE5Z5GWehL9/A9bRNA3RbrsZ5i0JcastaRL7Sp5fp/jD5dxqc/UdTVnlvS16an+2Yfswe/QuLolRUCrcOE2+2iA5+tzd6NmMGQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFMjFHQBBmiQpMlEk6w2uSu1KBtPsMB8GA1UdIwQYMBaAFMjFHQBBmiQpMlEk6w2uSu1KBtPsMAoGCCqGSM49BAMDA2gAMGUCMH8liWJfMui6vXXBhjDgY4MwslmN/TJxVe/83WrFomwmNf056y1X48F9c4m3a3ozXAIxAKjRay5/aj/jsKKGIkmQatjI8uupHr/+CxFvaJWmpYqNkLDGRU+9orzh5hI2RrcuaQ=="
}
]
},
"validFor": {
"start": "2021-03-07T03:20:29Z",
"end": "2022-12-31T23:59:59.999Z"
}
},
{
"subject": {
"organization": "sigstore.dev",
"commonName": "sigstore"
},
"uri": "https://fulcio.sigstore.dev",
"certChain": {
"certificates": [
{
"rawBytes": "MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow="
},
{
"rawBytes": "MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ"
}
]
},
"validFor": {
"start": "2022-04-13T20:06:15Z"
}
}
],
"tlogs": [
{
"baseUrl": "https://rekor.sigstore.dev",
"hashAlgorithm": "SHA2_256",
"publicKey": {
"rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2G2Y+2tabdTV5BcGiBIx0a9fAFwrkBbmLSGtks4L3qX6yYY0zufBnhC8Ur/iy55GhWP/9A/bY2LhC30M9+RYtw==",
"keyDetails": "PKIX_ECDSA_P256_SHA_256",
"validFor": {
"start": "2021-01-12T11:53:27Z"
}
},
"logId": {
"keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="
}
}
]
},
"entry": {
"body": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiIxZjk5NTI0OWZlMjQ4ZjExMmQ2MjI4ZGQyMDU2MjlkYjA3NmFmM2VlODE3NDk5ZjA3MzEyMDA5NmJlNjY1MDYyIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJUURJWm4wR0ZYaFBPQmlFMXNQam5jVW1Qd1hLYStoZ1lIZXNGdjlXRTJJTXVBSWdURFdJejBLaVFkVjYvTmRRblcxR1pUUUFTa01EdVRyb0tnVEJCNjRRK2o4PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVaEVla05EUW5CaFowRjNTVUpCWjBsVlYwVTRWbEF2U0U1aGMzcHFTbEJLZGxjM05GTm5TRXgyWm5WamQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFplRTFFUlhsTlZFRjZUVlJWTkZkb1kwNU5hbGw0VFVSRmVVMVVRVEJOVkZVMFYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZOY1U1VFFVOWlURzgyWTNCVVYxVlVTMmRRV21sd01rRnRXblYwY1M5Q1pGRTNSVWtLUjBaTk0xUm9la2xsWmpoeFRrSXdlbVk1U1ZadlVXYzNjRmhFWlhSdWN6RllMMGQyZG5ZMlNUVXdVVmRvWkRKYVNuRlBRMEppVlhkbloxZDRUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXWkNaMDVXU0ZOTlJVZEVRVmRuUWxSbUNqQXJibEJXYVZGU2JIWnRiekpQYTI5V1lVeEhUR2hvYTFCNlFtOUNaMDVXU0ZKRlFrRm1PRVZZYWtKamFHeHdiMlJJVW5kamVtOTJUREprY0dSSGFERUtXV2sxYW1JeU1IWlRSemxyV2xkc01GcFhjM1poUnpscldsZHNlbUZIYkd4aVIxRjBXVEo0Y0V4NU5XNWhXRkp2WkZkSmRtUXlPWGxoTWxwellqTmtlZ3BNTTBwc1lrZFdhR015VlhWbFZ6RnpVVWhLYkZwdVRYWmtSMFp1WTNrNU1rMUROREJNYWtWM1QxRlpTMHQzV1VKQ1FVZEVkbnBCUWtGUlVYSmhTRkl3Q21OSVRUWk1lVGt3WWpKMGJHSnBOV2haTTFKd1lqSTFla3h0WkhCa1IyZ3hXVzVXZWxwWVNtcGlNalV3V2xjMU1FeHRUblppVkVGVFFtZHZja0puUlVVS1FWbFBMMDFCUlVOQ1FWSjNaRmhPYjAxRVdVZERhWE5IUVZGUlFtYzNPSGRCVVUxRlMwZEpOVTFIU21sT1JFVjVXWHBSTVZwSFdUTk5la1UwV2xSRmVncE5SRUpyV20xU2FVMUVVbTFQUkVKc1QwUmthRmxxWXpOYWFsVjNSbEZaUzB0M1dVSkNRVWRFZG5wQlFrSkJVVWhWYlZaeldsZEdlbHBVUVcxQ1oyOXlDa0puUlVWQldVOHZUVUZGUmtKQ2FFbGlNbEpzWVZoU2JHRjVPVzlpTWxKc1lWaE9iMkZYVm5OYVF6RnFZa2RyZDBobldVdExkMWxDUWtGSFJIWjZRVUlLUW1kUlVXTnRWbTFqZVRrd1dWZGtla3d6V1hkTWFsRjFUVlJCTjBKbmIzSkNaMFZGUVZsUEwwMUJSVWxDUXpCTlN6Sm9NR1JJUW5wUGFUaDJaRWM1Y2dwYVZ6UjFXVmRPTUdGWE9YVmplVFZ1WVZoU2IyUlhTakZqTWxaNVdUSTVkV1JIVm5Wa1F6VnFZakl3ZDJGbldVdExkMWxDUWtGSFJIWjZRVUpEVVZKakNrUkdjRzlrU0ZKM1kzcHZka3d5WkhCa1IyZ3hXV2sxYW1JeU1IWlRSemxyV2xkc01GcFhjM1poUnpscldsZHNlbUZIYkd4aVIxRjBXVEo0Y0V4NU5XNEtZVmhTYjJSWFNYWmtNamw1WVRKYWMySXpaSHBNTTBwc1lrZFdhR015VlhWbFZ6RnpVVWhLYkZwdVRYWmtSMFp1WTNrNU1rMUROREJNYWtWM1QwRlpTd3BMZDFsQ1FrRkhSSFo2UVVKRFoxRnhSRU5vYVU5VVFtbFphbEY0VFcxTk1FNVhVbTFPZWsxNFQwZFZlRTE2UVhkYVIxcHJXV3BCTUZwcVozZGFWR2N6Q2xsWFNUTk9NbGt4VFVJd1IwTnBjMGRCVVZGQ1p6YzRkMEZSYzBWRWQzZE9XakpzTUdGSVZtbE1WMmgyWXpOU2JGcEVRVGRDWjI5eVFtZEZSVUZaVHk4S1RVRkZUVUpETUUxTE1tZ3daRWhDZWs5cE9IWmFNbXd3WVVoV2FVeHRUblppVXpsSllqSlNiR0ZZVW14aGVUbHZZakpTYkdGWVRtOWhWMVp6V2tNeGFncGlSMnQzVDBGWlMwdDNXVUpDUVVkRWRucEJRa1JSVVhGRVEyaHBUMVJDYVZscVVYaE5iVTB3VGxkU2JVNTZUWGhQUjFWNFRYcEJkMXBIV210WmFrRXdDbHBxWjNkYVZHY3pXVmRKTTA0eVdURk5RMEZIUTJselIwRlJVVUpuTnpoM1FWRTBSVVZuZDFGamJWWnRZM2s1TUZsWFpIcE1NMWwzVEdwUmRVMVVRV0VLUW1kdmNrSm5SVVZCV1U4dlRVRkZVRUpCZDAxRGFrVjZUMVJKTUU5VVZUTk9WR3QzUzNkWlMwdDNXVUpDUVVkRWRucEJRa1ZCVVdSRVFuUnZaRWhTZHdwamVtOTJUREprY0dSSGFERlphVFZxWWpJd2RsTkhPV3RhVjJ3d1dsZHpkMGRSV1V0TGQxbENRa0ZIUkhaNlFVSkZVVkZNUkVGcmVFNUVaekJOZW1kNkNrMXFaM2RoWjFsTFMzZFpRa0pCUjBSMmVrRkNSV2RTWTBSR2NHOWtTRkozWTNwdmRrd3laSEJrUjJneFdXazFhbUl5TUhaVFJ6bHJXbGRzTUZwWGMzWUtZVWM1YTFwWGJIcGhSMnhzWWtkUmRGa3llSEJNZVRWdVlWaFNiMlJYU1haa01qbDVZVEphYzJJelpIcE1NMHBzWWtkV2FHTXlWWFZsVnpGelVVaEtiQXBhYmsxMlpFZEdibU41T1RKTlF6UXdUR3BGZDA5QldVdExkMWxDUWtGSFJIWjZRVUpGZDFGeFJFTm9hVTlVUW1sWmFsRjRUVzFOTUU1WFVtMU9lazE0Q2s5SFZYaE5la0YzV2tkYWExbHFRVEJhYW1kM1dsUm5NMWxYU1ROT01sa3hUVUpSUjBOcGMwZEJVVkZDWnpjNGQwRlNVVVZDWjNkRlkwaFdlbUZFUW1ZS1FtZHZja0puUlVWQldVOHZUVUZGVmtKR1JVMVVNbWd3WkVoQ2VrOXBPSFphTW13d1lVaFdhVXh0VG5aaVV6bEpZakpTYkdGWVVteGhlVGx2WWpKU2JBcGhXRTV2WVZkV2MxcERNV3BpUjJ0MldWZE9NR0ZYT1hWamVUbDVaRmMxZWt4NlRUUk5hazB4VDFSWmVrNTZTWGxNTWtZd1pFZFdkR05JVW5wTWVrVjNDa1puV1V0TGQxbENRa0ZIUkhaNlFVSkdaMUZKUkVGYWQyUlhTbk5oVjAxM1ZuZFpTMHQzV1VKQ1FVZEVkbnBCUWtkQlVrcEVSV1I1V2xoQ2RrOXJhSFlLV2tkV2NHUkhWbkpSUkVVd1QwUlJlazlFVFhsUFF6bHZZakpTYkdGWVRtOWhWMVp6V2tNeGFtSkhiRUZOVkUwMVRXcFJOVTVVWXpGUFZIQjVXbGRaTmdwamJWWnRZM2s1TUZsWFpIcE1NMWwzVEdwUmRVMVVRMEpwZDFsTFMzZFpRa0pCU0ZkbFVVbEZRV2RTT1VKSWMwRmxVVUl6UVU0d09VMUhja2Q0ZUVWNUNsbDRhMlZJU214dVRuZExhVk5zTmpRemFubDBMelJsUzJOdlFYWkxaVFpQUVVGQlFtOVRLM0pVT1RoQlFVRlJSRUZGWjNkU1owbG9RVTUwYUZsM0wzWUtRM2M1YTJkVWJ6QnBjMmRtYlhBMlR5dFpURzltVFhWcGNUVndTRFZKVDBZd2Mxb3JRV2xGUVhsdGJXZDVia1pLTldJelN6WXlOVTVYVHpaSFkxSXlTQXBPZW5CWlMyUjBWVlE1VlRGdGVHeDZWRTlWZDBObldVbExiMXBKZW1vd1JVRjNUVVJhZDBGM1drRkpkMHh3VmxoWlluUnRMMlV6UW5oSFlsaGFiMjl2Q2pjNFVpc3lWR1p3V20xRlRYbFRjM0ZOWTBkdGFYb3dieTlUY2tKb0wzcDVVRVpSTW5CcVNWRnlVRE42UVdwQ2JXVllTSHBIYUc0ck1rNXBUSGN2YkRBS1NGSkVWRFZMTW1wcUwxTldRVllyUlhjME56ZEtObGhGUmxGaWRVbzVNME14Tm1RM1pVWkhZVFowT1V4aVZXODlDaTB0TFMwdFJVNUVJRU5GVWxSSlJrbERRVlJGTFMwdExTMEsifX19fQ==",
"integratedTime": 1791801119,
"logID": "c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d",
"logIndex": 3226122241,
"verification": {
"inclusionProof": {
"checkpoint": "rekor.sigstore.dev - 1193050959916656506\n3109757619\nbe1CaZRueKDmCSOb2a6rII5cokxVok+T0xHXECkG9EI=\n\n\u2014 rekor.sigstore.dev wNI9ajBFAiEAvE9IZPbrbChTIw2ebmXzJAjgI7Ez9wsgIf6kDQD30VoCIBUurDCAHr0/ye2Z2GGbH4XlVRh3YfOq0aqFZKiawGSU\n",
"hashes": [
"a2562fb5088d0682277c11b14330326494967731f874283694835ed2aaf5e5b5",
"b604e44408687c19c12b9ed1aa66cbd0bc61b754730b02236f046028308a60e5",
"62ea0e237fe14033398a6cd540e7cfb157ce92929d871ddd357535e6fcb55bba",
"ddf5d9c20508d64108673d84257b346d6d7dda26b52b91ca0cbb4925ec08033d",
"92897a57d5e37a85cebd9e2a63c105fc03b2d00534bb4f517fcd7e5dd446ad9c",
"1267d6fa6b0c404150e812084cc0eb29d1237de0e31f246d631f55a2b480aef0",
"1dba676c2498e59cf7cf93cf7098b0be41987c8f743df89608a0cb0d9e16153d",
"8d72d71ef43b1fbd2d2a1e2d2f1d272522c8ee8954f856d22214a94fc56f2c4d",
"513dad835e653682b8214f20141001c55f9a35c972fcd6423755a0f3facd7723",
"e05f384eb9d45b5ac2e14cd1ceebcd27ae12ab1b0aa77fc022169ace6971fab9",
"bd73b38472166d260eb59ec0bd39f23d7c966ba4f4ff3f9c9f87e203883bc6a5",
"76bdef9c0133842a54aa2c11c758205ddb3c578c6a49b0ae0cb1f5a4b292e9bd",
"5ae1cca088a96d6b4be1d4f26c3bc580b22e753b694041555953544901c46cc6",
"dee7352de578eabfd016ba94b800146ecd6ac736bb67a4c56cfb0549558f209f",
"3b1d4d34c94a6beaa6af53617f7c3bcf7e5cfaac1898215f716c1c107d0ca2d6",
"8b5f672c617369e8e981c9c0b70c62ce76ebd5bf16327008c54a1431294e2844",
"099204e813e13e7c005ac6f054ecec3c6d2a15f536bafd40fe00d28cedebe4f6",
"57467ab00840ab5c371748dc97e538413017224014e9b8b412bf111bdd827670",
"c0bac3ac8576a1b0bd6d2c7915f6e5531d198f260b0e5d63e669877fb5d15d0e",
"386f1ecc64ad79c80edb9b24711fd532efff2313469a1aa3d0a0d8c18f61bac3",
"073fe5fabea764e56ac50251ddee7a471a0f707a57377c2df85f4b8d35f284b9",
"3fba07ebd071d49402710de13bd5970982beb5a687296b731291de1d6fde14eb",
"9eb78a52a5cbdb6653934411fecc7d3fd1b24ba6b0c7c10e6e1273149bf85cd4",
"6e97f1dc2bf12409d83a085df818b66ac2b70248223c8b41628086a0b55996b2",
"820f46340a4d91edfeccd865e2db16b6623270216b38c081de5cace97b9240d4",
"36539c96576628f035b4c288b24a87030b900d81e9df5630a4af8e38cacc71c7",
"ab1cc76a7033cf9ed20dd9897b407b6c92bbd8d4c805bc043060412af0ce44ec",
"c47fc30ac78b1ebf5e2a8613f2ab0e4592bbcd5744198587b95b6c56b0fde706"
],
"logIndex": 3104217979,
"rootHash": "6ded4269946e78a0e609239bd9aeab208e5ca24c55a24f93d311d7102906f442",
"treeSize": 3109757619
},
"signedEntryTimestamp": "MEYCIQC60O/JNeSrxOnBkTt+kTUuxT8fOlWnjYG3GhVEZL2jdgIhAI+MwiEvGs8uUk5dndfAvSQsvkTtWQwka16ZFAAedLaI"
}
}
}
Loading
Loading