hashicorp/packer · error
verify Fulcio certificate SCT: %w
Error message
verify Fulcio certificate SCT: %w
What it means
When no custom trusted root is configured, verifyKeylessCertificate additionally requires the Fulcio certificate to carry a valid Signed Certificate Timestamp (SCT) verifiable against at least 1 CT log in the public trusted material, via sigstore-go's VerifySignedCertificateTimestamp. This rejects certificates issued outside the public CT logs (e.g. by a private Fulcio or a rogue CA). The error means the SCT was missing, malformed, or failed log-signature verification.
Source
Thrown at internal/attestation/sign_keyless.go:62
trustedRootPath := strings.TrimSpace(cfg.TrustedRootPath)
if trustedRootPath == "" {
return sigstoreroot.FetchTrustedRoot()
}
return sigstoreroot.NewTrustedRootFromPath(trustedRootPath)
}
var verifyKeylessCertificate = func(certificate *x509.Certificate, trustedMaterial sigstoreroot.TrustedMaterial, expectedIdentity, expectedOIDCIssuer, trustedRootPath string) error {
chains, err := sigstoreverify.VerifyLeafCertificate(time.Now().UTC(), certificate, trustedMaterial)
if err != nil {
return fmt.Errorf("verify Fulcio certificate chain: %w", err)
}
// When using the public Sigstore root (no custom trusted root configured),
// require a valid SCT so certificates issued outside a public CT log are rejected.
if strings.TrimSpace(trustedRootPath) == "" {
if err := sigstoreverify.VerifySignedCertificateTimestamp(chains, 1, trustedMaterial); err != nil {
return fmt.Errorf("verify Fulcio certificate SCT: %w", err)
}
}
summary, err := fulciocertificate.SummarizeCertificate(certificate)
if err != nil {
return fmt.Errorf("summarize Fulcio certificate: %w", err)
}
identity, err := sigstoreverify.NewShortCertificateIdentity(expectedOIDCIssuer, "", expectedIdentity, "")
if err != nil {
return fmt.Errorf("build keyless identity policy: %w", err)
}
if err := identity.Verify(summary); err != nil {
return fmt.Errorf("verify keyless certificate identity: %w", err)
}
return nil
}View on GitHub (pinned to eb36e3c3e4)
Solutions
- If the certificates genuinely come from a private Fulcio/CT setup, provide a custom trusted_root_path containing that deployment's CT log keys — the SCT check is then skipped by design.
- If using the public Sigstore infrastructure, refresh the trusted root (clear trusted_root_path) so the CT log that issued the SCT is present.
- Ensure attestations were produced via the public Fulcio (https://fulcio.sigstore.dev) which embeds/records SCTs; re-run signing so a new certificate with a valid SCT is issued.
- Inspect the wrapped inner error from VerifySignedCertificateTimestamp to see whether the SCT is absent or merely failed log verification.
- Check clock skew — SCT verification is time-sensitive.
Example fix
// before: private Fulcio cert verified against public root -> no matching SCT // (no trusted_root_path set) // after: supply the private deployment's trusted root // trusted_root_path = "/etc/cosign/trusted_root_private.json" // contains private CT log + Fulcio CA
Defensive patterns
Strategy: validation
Validate before calling
// confirm the verification context matches the certificate's origin before calling Verify
func sctCheckExpected(trustedRootPath string, cert *x509.Certificate) error {
// public root -> SCT check runs; cert must have been logged in a public CT log
if strings.TrimSpace(trustedRootPath) == "" {
if issuedPrivately(cert) { // e.g. issuer CN/URL not the public Fulcio
return fmt.Errorf("cert from private Fulcio but no trusted_root_path set; SCT check against public logs will fail")
}
return nil
}
return nil
} Type guard
func isSCTError(err error) bool {
return err != nil && strings.Contains(err.Error(), "verify Fulcio certificate SCT:")
} Try / catch
err := keylessVerifier.Verify(ctx, payloadType, payload, sig)
if err != nil {
if isSCTError(err) {
return fmt.Errorf("no valid CT timestamp for certificate: either re-sign via public Fulcio, or set trusted_root_path to a root containing your private CT logs: %w", err)
}
return err
} Prevention
- Only sign keyless attestations through a Fulcio/CT configuration your verifiers' trusted root actually covers.
- If operating a private Sigstore instance, always distribute its trusted_root.json (CT logs + Fulcio CAs) to verifiers.
- Refresh the public trusted root periodically so current CT log keys remain available for SCT verification.
- Document that omitting trusted_root_path intentionally enforces the public-CT SCT requirement.
When it happens
Trigger: keylessVerifier.Verify with empty trusted_root_path calls VerifySignedCertificateTimestamp(chains, 1, trustedMaterial) and it fails: the leaf certificate has no SCT extension/embedded proof, the SCT's log is absent from the trusted material, or the SCT signature does not verify.
Common situations: Verifying a certificate issued by a private/enterprise Fulcio deployment (no public CT log involvement) while still using the public trusted root; a stale trusted root that no longer contains the CT log that countersigned the cert; certificates issued by misconfigured Fulcio instances that skip CT inclusion.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- verify Fulcio certificate chain: %w
- summarize Fulcio certificate: %w
- verifier overrides are not supported for keyless attestation
- bundle-based Rekor or timestamp verification currently requi
- bundle-based Rekor or timestamp verification requires keyles
AI-assisted analysis of hashicorp/packer@eb36e3c3e4 (2026-09-05).
Data as JSON: /api/errors/6d5da11e3b39571c.
Report an issue: GitHub.