Skip to content

Commit b7d2138

Browse files
jkuramonpetgrave64
andauthored
Sign and verify with rekorv2 (#1432)
* trust: Start returning RekorV2Client from signingconfig If signingconfig contains rekor v2, let's start preferring it Make sure we test the status quo (no rekor v2 in signing config) and the case where there is a rekor v2 in signing config. * test: Add trust config for signing with staging rekor v2 This is current staging trust root and signing config, with just the rekor v2 instance added to signing config $ TRUSTCONFIG=test/assets/trust_config/staging-but-sign-with-rekor-v2.json $ sigstore --trust-config $TRUSTCONFIG sign README.md * verify: Initial support for rekor v2 verify This code is originally from Ramon, updated by Jussi $ TRUSTCONFIG=test/assets/trust_config/staging-but-sign-with-rekor-v2.json $ sigstore --trust-config $TRUSTCONFIG sign README.md $ sigstore --staging verify identity \ --cert-identity [email protected] \ --cert-oidc-issuer https://github.com/login/oauth README.md OK: README.md * tests: Add tests for signing and verifying rekorv2 bundles * verify: Handle more ECDSA keys in signing cert This change affects the signing certificate verification in rekor v2 entries: * Support all ECDSA keys listed in https://github.com/sigstore/architecture-docs/blob/main/algorithm-registry.md * Don't support other algorithms yet since the actual signature verification does not support them currently --------- Signed-off-by: Jussi Kukkonen <[email protected]> Signed-off-by: Ramon Petgrave <[email protected]> Co-authored-by: Ramon Petgrave <[email protected]>
1 parent 50eee0d commit b7d2138

13 files changed

+521
-52
lines changed

sigstore/_internal/trust.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@
6060
)
6161

6262
from sigstore._internal.fulcio.client import FulcioClient
63+
from sigstore._internal.rekor import RekorLogSubmitter
6364
from sigstore._internal.rekor.client import RekorClient
65+
from sigstore._internal.rekor.client_v2 import RekorV2Client
6466
from sigstore._internal.timestamp import TimestampAuthorityClient
6567
from sigstore._internal.tuf import DEFAULT_TUF_URL, STAGING_TUF_URL, TrustUpdater
6668
from sigstore._utils import (
@@ -73,7 +75,7 @@
7375
from sigstore.errors import Error, MetadataError, TUFError, VerificationError
7476

7577
# Versions supported by this client
76-
REKOR_VERSIONS = [1]
78+
REKOR_VERSIONS = [1, 2]
7779
TSA_VERSIONS = [1]
7880
FULCIO_VERSIONS = [1]
7981
OIDC_VERSIONS = [1]
@@ -420,11 +422,19 @@ def _get_valid_services(
420422

421423
return result[:count]
422424

423-
def get_tlogs(self) -> list[RekorClient]:
425+
def get_tlogs(self) -> list[RekorLogSubmitter]:
424426
"""
425427
Returns the rekor transparency log clients to sign with.
426428
"""
427-
return [RekorClient(tlog.url) for tlog in self._tlogs]
429+
result: list[RekorLogSubmitter] = []
430+
for tlog in self._tlogs:
431+
if tlog.major_api_version == 1:
432+
result.append(RekorClient(tlog.url))
433+
elif tlog.major_api_version == 2:
434+
result.append(RekorV2Client(tlog.url))
435+
else:
436+
raise AssertionError(f"Unexpected Rekor v{tlog.major_api_version}")
437+
return result
428438

429439
def get_fulcio(self) -> FulcioClient:
430440
"""

sigstore/verify/verifier.py

Lines changed: 182 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@
2525

2626
import rekor_types
2727
from cryptography.exceptions import InvalidSignature
28+
from cryptography.hazmat.primitives import serialization
2829
from cryptography.hazmat.primitives.asymmetric import ec
29-
from cryptography.x509 import ExtendedKeyUsage, KeyUsage
30+
from cryptography.x509 import Certificate, ExtendedKeyUsage, KeyUsage
3031
from cryptography.x509.oid import ExtendedKeyUsageOID
3132
from OpenSSL.crypto import (
3233
X509,
@@ -38,6 +39,8 @@
3839
from pydantic import ValidationError
3940
from rfc3161_client import TimeStampResponse, VerifierBuilder
4041
from rfc3161_client import VerificationError as Rfc3161VerificationError
42+
from sigstore_protobuf_specs.dev.sigstore.common import v1
43+
from sigstore_protobuf_specs.dev.sigstore.rekor import v2
4144

4245
from sigstore import dsse
4346
from sigstore._internal.rekor import _hashedrekord_from_parts
@@ -417,34 +420,18 @@ def verify_dsse(
417420
# Instead, we manually pick apart the entry body below and verify
418421
# the parts we can (namely the payload hash and signature list).
419422
entry = bundle.log_entry
420-
try:
421-
entry_body = rekor_types.Dsse.model_validate_json(
422-
base64.b64decode(entry.body)
423+
if entry._kind_version.kind != "dsse":
424+
raise VerificationError(
425+
f"Expected entry type dsse, got {entry._kind_version.kind}"
423426
)
424-
except ValidationError as exc:
425-
raise VerificationError(f"invalid DSSE log entry: {exc}")
426-
427-
payload_hash = sha256_digest(envelope._inner.payload).digest.hex()
428-
if (
429-
entry_body.spec.root.payload_hash.algorithm # type: ignore[union-attr]
430-
!= rekor_types.dsse.Algorithm.SHA256
431-
):
432-
raise VerificationError("expected SHA256 payload hash in DSSE log entry")
433-
if payload_hash != entry_body.spec.root.payload_hash.value: # type: ignore[union-attr]
434-
raise VerificationError("log entry payload hash does not match bundle")
435-
436-
# NOTE: Like `dsse._verify`: multiple signatures would be frivolous here,
437-
# but we handle them just in case the signer has somehow produced multiple
438-
# signatures for their envelope with the same signing key.
439-
signatures = [
440-
rekor_types.dsse.Signature(
441-
signature=base64.b64encode(signature.sig).decode(),
442-
verifier=base64_encode_pem_cert(bundle.signing_certificate),
427+
if entry._kind_version.version == "0.0.2":
428+
_validate_dsse_v002_entry_body(bundle)
429+
elif entry._kind_version.version == "0.0.1":
430+
_validate_dsse_v001_entry_body(bundle)
431+
else:
432+
raise VerificationError(
433+
f"Unsupported dsse version {entry._kind_version.version}"
443434
)
444-
for signature in envelope._inner.signatures
445-
]
446-
if signatures != entry_body.spec.root.signatures:
447-
raise VerificationError("log entry signatures do not match bundle")
448435

449436
return (envelope._inner.payload_type, envelope._inner.payload)
450437

@@ -489,16 +476,175 @@ def verify_artifact(
489476
# (8): verify the consistency of the log entry's body against
490477
# the other bundle materials (and input being verified).
491478
entry = bundle.log_entry
479+
if entry._kind_version.kind != "hashedrekord":
480+
raise VerificationError(
481+
f"Expected entry type hashedrekord, got {entry._kind_version.kind}"
482+
)
483+
484+
if entry._kind_version.version == "0.0.2":
485+
_validate_hashedrekord_v002_entry_body(bundle)
486+
elif entry._kind_version.version == "0.0.1":
487+
_validate_hashedrekord_v001_entry_body(bundle, hashed_input)
488+
else:
489+
raise VerificationError(
490+
f"Unsupported hashedrekord version {entry._kind_version.version}"
491+
)
492492

493-
expected_body = _hashedrekord_from_parts(
494-
bundle.signing_certificate,
495-
bundle._inner.message_signature.signature, # type: ignore[union-attr]
496-
hashed_input,
493+
494+
def _validate_dsse_v001_entry_body(bundle: Bundle) -> None:
495+
"""
496+
Validate the Entry body for dsse v001.
497+
"""
498+
entry = bundle.log_entry
499+
envelope = bundle._dsse_envelope
500+
if envelope is None:
501+
raise VerificationError(
502+
"cannot perform DSSE verification on a bundle without a DSSE envelope"
497503
)
498-
actual_body = rekor_types.Hashedrekord.model_validate_json(
499-
base64.b64decode(entry.body)
504+
try:
505+
entry_body = rekor_types.Dsse.model_validate_json(base64.b64decode(entry.body))
506+
except ValidationError as exc:
507+
raise VerificationError(f"invalid DSSE log entry: {exc}")
508+
509+
payload_hash = sha256_digest(envelope._inner.payload).digest.hex()
510+
if (
511+
entry_body.spec.root.payload_hash.algorithm # type: ignore[union-attr]
512+
!= rekor_types.dsse.Algorithm.SHA256
513+
):
514+
raise VerificationError("expected SHA256 payload hash in DSSE log entry")
515+
if payload_hash != entry_body.spec.root.payload_hash.value: # type: ignore[union-attr]
516+
raise VerificationError("log entry payload hash does not match bundle")
517+
518+
# NOTE: Like `dsse._verify`: multiple signatures would be frivolous here,
519+
# but we handle them just in case the signer has somehow produced multiple
520+
# signatures for their envelope with the same signing key.
521+
signatures = [
522+
rekor_types.dsse.Signature(
523+
signature=base64.b64encode(signature.sig).decode(),
524+
verifier=base64_encode_pem_cert(bundle.signing_certificate),
500525
)
501-
if expected_body != actual_body:
502-
raise VerificationError(
503-
"transparency log entry is inconsistent with other materials"
526+
for signature in envelope._inner.signatures
527+
]
528+
if signatures != entry_body.spec.root.signatures:
529+
raise VerificationError("log entry signatures do not match bundle")
530+
531+
532+
def _validate_dsse_v002_entry_body(bundle: Bundle) -> None:
533+
"""
534+
Validate Entry body for dsse v002.
535+
"""
536+
entry = bundle.log_entry
537+
envelope = bundle._dsse_envelope
538+
if envelope is None:
539+
raise VerificationError(
540+
"cannot perform DSSE verification on a bundle without a DSSE envelope"
541+
)
542+
try:
543+
v2_body = v2.Entry().from_json(base64.b64decode(entry.body))
544+
except ValidationError as exc:
545+
raise VerificationError(f"invalid DSSE log entry: {exc}")
546+
547+
if v2_body.spec.dsse_v002 is None:
548+
raise VerificationError("invalid DSSE log entry: missing dsse_v002 field")
549+
550+
if v2_body.spec.dsse_v002.payload_hash.algorithm != v1.HashAlgorithm.SHA2_256:
551+
raise VerificationError("expected SHA256 hash in DSSE entry")
552+
553+
digest = sha256_digest(envelope._inner.payload).digest
554+
if v2_body.spec.dsse_v002.payload_hash.digest != digest:
555+
raise VerificationError("DSSE entry payload hash does not match bundle")
556+
557+
v2_signatures = [
558+
v2.Signature(
559+
content=signature.sig,
560+
verifier=_v2_verifier_from_certificate(bundle.signing_certificate),
561+
)
562+
for signature in envelope._inner.signatures
563+
]
564+
if v2_signatures != v2_body.spec.dsse_v002.signatures:
565+
raise VerificationError("log entry signatures do not match bundle")
566+
567+
568+
def _validate_hashedrekord_v001_entry_body(
569+
bundle: Bundle, hashed_input: Hashed
570+
) -> None:
571+
"""
572+
Validate the Entry body for hashedrekord v001.
573+
"""
574+
entry = bundle.log_entry
575+
expected_body = _hashedrekord_from_parts(
576+
bundle.signing_certificate,
577+
bundle._inner.message_signature.signature, # type: ignore[union-attr]
578+
hashed_input,
579+
)
580+
actual_body = rekor_types.Hashedrekord.model_validate_json(
581+
base64.b64decode(entry.body)
582+
)
583+
if expected_body != actual_body:
584+
raise VerificationError(
585+
"transparency log entry is inconsistent with other materials"
586+
)
587+
588+
589+
def _validate_hashedrekord_v002_entry_body(bundle: Bundle) -> None:
590+
"""
591+
Validate Entry body for hashedrekord v002.
592+
"""
593+
entry = bundle.log_entry
594+
if bundle._inner.message_signature is None:
595+
raise VerificationError(
596+
"invalid hashedrekord log entry: missing message signature"
597+
)
598+
v2_expected_body = v2.Entry(
599+
kind=entry._kind_version.kind,
600+
api_version=entry._kind_version.version,
601+
spec=v2.Spec(
602+
hashed_rekord_v002=v2.HashedRekordLogEntryV002(
603+
data=v1.HashOutput(
604+
algorithm=bundle._inner.message_signature.message_digest.algorithm,
605+
digest=bundle._inner.message_signature.message_digest.digest,
606+
),
607+
signature=v2.Signature(
608+
content=bundle._inner.message_signature.signature,
609+
verifier=_v2_verifier_from_certificate(bundle.signing_certificate),
610+
),
504611
)
612+
),
613+
)
614+
v2_actual_body = v2.Entry().from_json(base64.b64decode(entry.body))
615+
if v2_expected_body != v2_actual_body:
616+
raise VerificationError(
617+
"transparency log entry is inconsistent with other materials"
618+
)
619+
620+
621+
def _v2_verifier_from_certificate(certificate: Certificate) -> v2.Verifier:
622+
"""
623+
Return a Rekor v2 protobuf Verifier for the signing certificate.
624+
625+
This method decides which signature algorithms are supported for verification
626+
(in a rekor v2 entry), see
627+
https://github.com/sigstore/architecture-docs/blob/main/algorithm-registry.md.
628+
Note that actual signature verification happens in verify_artifact() and
629+
verify_dsse(): New keytypes need to be added here and in those methods.
630+
"""
631+
public_key = certificate.public_key()
632+
633+
if isinstance(public_key, ec.EllipticCurvePublicKey):
634+
if isinstance(public_key.curve, ec.SECP256R1):
635+
key_details = v1.PublicKeyDetails.PKIX_ECDSA_P256_SHA_256
636+
elif isinstance(public_key.curve, ec.SECP384R1):
637+
key_details = v1.PublicKeyDetails.PKIX_ECDSA_P384_SHA_384
638+
elif isinstance(public_key.curve, ec.SECP521R1):
639+
key_details = v1.PublicKeyDetails.PKIX_ECDSA_P521_SHA_512
640+
else:
641+
raise ValueError(f"Unsupported EC curve: {public_key.curve.name}")
642+
else:
643+
raise ValueError(f"Unsupported public key type: {type(public_key)}")
644+
645+
return v2.Verifier(
646+
x509_certificate=v1.X509Certificate(
647+
certificate.public_bytes(encoding=serialization.Encoding.DER)
648+
),
649+
key_details=cast(v1.PublicKeyDetails, key_details),
650+
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
DO NOT MODIFY ME!
2+
3+
this is "a.txt", a sample input for sigstore-python's unit tests.
4+
5+
DO NOT MODIFY ME!
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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"}]}}

0 commit comments

Comments
 (0)