Skip to content
Merged
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
30 changes: 30 additions & 0 deletions src/tinfoil/sigstore.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from sigstore.verify.policy import AllOf, OIDCIssuer, OIDCIssuerV2, GitHubWorkflowRepository, Certificate, _OIDC_GITHUB_WORKFLOW_REF_OID, _OIDC_ISSUER_V2_OID, ExtensionNotFound
from sigstore.models import Bundle
from sigstore.errors import VerificationError
from cryptography.x509 import PrecertificateSignedCertificateTimestamps
import json
import re

Expand All @@ -12,6 +13,32 @@
OIDC_ISSUER = "https://token.actions.githubusercontent.com"


def reject_duplicate_sct_logs(bundle: Bundle) -> None:
Comment thread
sachaservan marked this conversation as resolved.
"""SPEC §5.2 anti-replay guard: reject a leaf certificate whose embedded
SCT list contains two or more SCTs that share the same CT log ID, so a
single (compromised or replayed) log cannot contribute more than one SCT
toward the requirement.

sigstore-python verifies exactly one SCT, so it already rejects this case
generically ("Expected one certificate timestamp, found N"); we check
explicitly so the rejection is principled and on the same log-ID basis as
tinfoil-rs / -js / -go.
"""
try:
scts = bundle.signing_certificate.extensions.get_extension_for_class(
PrecertificateSignedCertificateTimestamps
).value
except ExtensionNotFound:
# No SCT extension: the missing-SCT case is the main verifier's concern.
return
log_ids = [sct.log_id for sct in scts]
if len(set(log_ids)) != len(log_ids):
raise VerificationError(
"duplicate SCT log id: leaf certificate carries multiple SCTs "
"from the same CT log"
)


class OIDCIssuerV2Preferred:
"""
Verifies the certificate's OIDC issuer, preferring the canonical V2
Expand Down Expand Up @@ -85,6 +112,9 @@ def _verify_dsse_bundle(bundle_json: bytes, digest: str, repo: str) -> dict:
verifier = Verifier.production()
bundle = Bundle.from_json(bundle_json)

# SPEC §5.2: reject duplicate-log SCTs before signature/SCT verification.
reject_duplicate_sct_logs(bundle)

policy = AllOf([
OIDCIssuerV2Preferred(OIDC_ISSUER),
GitHubWorkflowRepository(repo),
Expand Down