gitsign

repository·main·Indexed 22 days ago

https://github.com/sigstore/gitsign

A tool for keyless Git signing using Sigstore, enabling users to sign Git commits and tags using GitHub or OIDC identities without managing long-lived cryptographic keys. It includes gitsign-credential-cache for in-memory credential caching and gitsign-attest for experimental commit and tree attestations.

Tokens
18.2K
Snippets
61
Records
77
Agent score
77%

What's inside gitsign

  1. Use gitsign-attest for experimental attestations

    main

    The gitsign-attest tool is an experimental demo used to add attestations to the latest commit SHA in your Git working directory. It stores data as a commit under refs/attestations/commits or refs/attestations/trees, ensuring the original commit remains unmodified.

    For each attested commit SHA, a folder is created containing:

    • The raw data provided by the user.
    • A signed DSSE message attesting to the file (suffixed with .sig).

    Currently, only public Sigstore is supported.

  2. What is gitsign-credential-cache and how does it work?

    main

    The gitsign-credential-cache is an optional helper binary that allows users to cache signing credentials in memory. This is useful for performing multiple signing operations in rapid succession without re-authenticating.

    How it works

    • Storage: Credentials (Ephemeral Private Key and Fulcio Code Signing certificate + chain) are stored in memory.
    • Isolation: Data is keyed to your Git working directory, meaning different repository paths use different cached keys.
    • Interface: The cache is exposed via a Unix socket.

    Security Warnings

    • Socket Access: Any user with access to the Unix socket can access the cached data. Avoid using this on shared systems where other admins might access the socket.
    • OIDC Environments: If you are in an environment with ambient OIDC credentials (e.g., GCE/GKE, AWS, GitHub Actions), gitsign will automatically use those. You do not need the cache in these environments.
    • Expiration: Cached data allows anyone with socket access to sign as you until the signing certificate expires (typically within ten minutes).
  3. What is stored in Rekor during offline signing

    main

    When using offline mode, Gitsign stores a HashedRekord in Rekor. This record contains the following data for both commits and tags:

    • Content checksum: The checksum of the commit or tag content.
    • Fulcio certificate:
      • Public Key
      • Signer Identity info
  4. How online verification works in Gitsign

    main

    In online Rekor storage mode, Gitsign does not persist all Rekor log details directly in the Git commit. Instead, it stores the Git commit SHA in Rekor.

    Signing Process:

    1. Obtain a Fulcio Certificate.
    2. Sign the commit body using the certificate.
    3. Generate the commit SHA (note: the commit doesn't exist yet because the signature is part of the content).
    4. Sign the commit SHA using the same certificate.
    5. Upload a HashedRekord of the commit SHA to Rekor.
    6. Store the signed commit body signature in the commit.

    Verification Process: Verification ensures the signature matches the commit and that the commit exists in the Rekor transparency log:

    1. Recompute and compare the commit content checksum.
    2. Validate the checksum signature using the public key in the signature's certificate (ignoring NotAfter time).
    3. (If present) Verify the signature against a TSA certificate.
    4. Search Rekor for an entry matching the commit SHA + certificate (this step requires internet access).
    5. Verify Rekor LogEntry inclusion (performed offline).
  5. Understand Gitsign data storage and privacy

    main

    Gitsign stores data in two primary locations:

    1. Within the Git commit: The commit contains a signed digest of the content (author, committer, message, parents, etc.) and the code signing certificate. This is part of your repository.
    2. Within the Rekor transparency log: To allow verification of ephemeral certificates after they expire, Gitsign records the commit and certificate to Rekor.

    Rekor Modes

    • rekorMode = online (default): Data is written to the public Rekor instance as a HashedRekord containing a SHA256 hash of the commit SHA and the code signing certificate. Note that certificates may contain sensitive information like user emails or repo identifiers.
    • rekorMode = offline: This mode is currently experimental. It allows for local/private transparency log usage.
  6. How Gitsign relates CMS signatures to Sigstore bundles

    main

    Gitsign stores signatures in the git gpgsig header using the Cryptographic Message Syntax (CMS/PKCS7) format. However, the wider Sigstore ecosystem and the sigstore-go libraries use the Sigstore bundle format.

    To maintain compatibility, Gitsign performs a conversion between these two formats. The key insight is that the actual artifact being signed is not the git commit/tag body itself, but rather the marshaled SignedAttrs (CMS signed attributes). This structure contains the content type, the message digest (sha256 of the git object), and the signing time.

    Because the signature is computed over these attributes, the messageDigest in a Sigstore bundle corresponds to the sha256(DER(SignedAttrs)) in CMS.

  7. How Sigstore bundle to CMS conversion works (Signing)

    main

    During signing, Gitsign performs the inverse operation to ensure the resulting CMS object is compatible with git's expectations.

    Workflow:

    1. BuildSignedAttributes(body) $\rightarrow$ (SignedAttrs, marshaled-for-signing)
    2. sign.Bundle(PlainData{marshaled}, ...) $\rightarrow$ bundle (signature + cert + tlog)
    3. BundleToSignedData(body, SignedAttrs, b) $\rightarrow$ cms.SignedData (the final stored signature)

    Implementation Details:

    • Identity: Gitsign uses its existing fulcio.Identity (adapted to sign.Keypair and sign.CertificateProvider). The OIDC/Fulcio flow and credential caching remain unchanged; sigstore-go only handles the signing and Rekor upload.
    • Assembly: The CMS SignerInfo is assembled around the signature using internal fork helpers. The result is byte-for-byte equivalent to what a native CMS signer would produce.
    • Timestamps: RFC3161 timestamps are applied to the assembled CMS rather than via sigstore-go.
  8. How offline verification works in Gitsign

    main

    In offline Rekor storage mode, Gitsign stores a HashedRekord in Rekor that corresponds to the commit or tag content. Because this is complex to query manually, the Rekor log entry fields and inclusion proof are stored within the PKCS7 object as unauthenticated attributes (meaning they are not part of the cryptographic signature itself).

    Verification Process

    For Commits:

    1. Recompute and compare the commit content checksum from the commit.
    2. Retrieve the Rekor LogEntry from the signature.
    3. Verify the Certificate against the commit content checksum (ignoring the certificate's NotAfter time).
    4. If a TSA (Time Stamping Authority) signature is present, verify the signature against the TSA certificate.
    5. Verify the Rekor LogEntry inclusion offline.

    For Tags:

    1. Recompute and compare the tag content checksum from the tag.
    2. Retrieve the Rekor LogEntry from the signature.
    3. Verify the Certificate against the tag content checksum (ignoring the certificate's NotAfter time).
    4. If a TSA signature is present, verify the signature against the TSA certificate.
    5. Verify the Rekor LogEntry inclusion offline.

    Data Stored in the Signature

    Both commits and tags store the following in their signatures:

    • Content checksum (sha256)
    • Signing time (untrusted system time)
    • Protobuf encoded Rekor TransparencyLogEntry
    • (Optional) TSA signature and certificate
    sha256(der(sort(system time | commit data | content type)))
  9. How CMS to Sigstore bundle conversion works (Verification)

    main

    During verification, Gitsign parses the stored CMS and projects each signer onto a bundle.

    Workflow:

    1. ParseSignaturePEM(sig) $\rightarrow$ cms.SignedData
    2. SignerInfoToBundle(sd, signer) $\rightarrow$ { Bundle, Artifact }

    Important Notes:

    • The Artifact is the marshaled SignedAttrs. Callers must pass this as the verification artifact because the bundle's messageSignature only contains the digest, not the full attributes.
    • If a CMS signature contains multiple signers, SignedDataToBundle returns one bundle per signer.
    • Content Binding Check: Since sigstore-go cannot verify if the SignedAttrs actually describe the specific git object from the bundle alone, Gitsign performs a manual check: it compares sha256(git object) against the SignedAttrs message-digest attribute.
  10. Enable Committer Verification in Gitsign

    main

    Gitsign can be configured to verify that the identity in the Fulcio certificate matches your local Git configuration (user.name and user.email).

    Verification works by matching the certificate's Subject Alternative Name (SAN) against your Git config in this priority order:

    1. Email: An EmailAddresses SAN value matches user.email. This is the standard method for human users.
    2. URI: A URI SAN value matches user.name. This is the standard method for automated workloads.

    If multiple SAN values are present in the certificate, verification succeeds if at least one matches.

    git config gitsign.matchCommitter true
  11. Disable the sigstore-go path

    main

    The sigstore-go conversion path is enabled by default for both signing and verification. If you need to fall back to the legacy CMS + Rekor path, you can disable it using either a git configuration or an environment variable.

    Note that the on-disk CMS signature format remains identical regardless of this setting; only the internal implementation of signing and verification changes.

    # Via git config
    git config gitsign.enableSigstoreGo false
    
    # Via environment variable
    export GITSIGN_ENABLE_SIGSTORE_GO=false