Sigstore Python Client
Sigstore-python is a client library for interacting with the Sigstore ecosystem, providing tools for signing and verifying Python package distributions and other artifacts. It is actively maintained, with frequent releases addressing security fixes, new features, and compatibility updates. The current version is 4.2.0.
Common errors
-
sigstore.oidc.errors.OidcError: No identity provider found
cause The signing process could not find a suitable OIDC identity provider. This typically happens when running in a non-CI environment without an interactive browser session, or when required CI environment variables (e.g., for GitHub Actions OIDC) are not set.fixIf running locally, ensure a browser can be opened for interactive OIDC authentication. If in CI/CD, verify that all necessary environment variables for your OIDC provider are correctly configured (e.g., `GITHUB_ACTIONS=true` and associated tokens). -
sigstore.verify.errors.VerificationError: Verification failed
cause The provided artifact's signature bundle is invalid, the certificate has expired, the artifact digest does not match, or the trusted root cannot validate the signature chain.fixDouble-check that the artifact has not been tampered with and matches the signed content. Ensure the bundle is correct and the certificate chain is valid and within its expiry. Verify against the correct `TrustRoot` (e.g., `TrustRoot.production()`). -
ModuleNotFoundError: No module named 'sigstore'
cause The `sigstore` library is not installed in the current Python environment.fixInstall the library using pip: `pip install sigstore`.
Warnings
- breaking Version 4.0.0 introduced significant API and functionality changes, including support for Rekor v2. Code written for 3.x series for signing and verification may require updates.
- deprecated The 3.5.x series is the last planned release in its line. Users are strongly advised to upgrade to the 4.x series for continued support, security patches, and new features.
- gotcha Versions prior to 4.2.0 (and 3.6.7) had a minor security vulnerability related to OIDC authentication (CSRF). Upgrading is critical for secure OIDC flows.
- gotcha Verification now ensures that the artifact digest documented in the bundle matches the real digest. Previously, lax checks might have allowed malformed bundles to pass. This could cause previously 'valid' bundles to fail verification.
- gotcha Compatibility issues with the `cryptography` library. `sigstore-python` often adds support for newer `cryptography` versions in patch releases.
Install
-
pip install sigstore -
pip install "sigstore>=3.10,<4"
Imports
- Signer
from sigstore.sign import Signer
- verify_artifact
from sigstore.verify import verify_artifact
- TrustRoot
from sigstore.trust_root import TrustRoot
- IdentityProvider
from sigstore.oidc.client import OidcClient
from sigstore.oidc import IdentityProvider
Quickstart
import os
import tempfile
import logging
from sigstore.sign import Signer
from sigstore.verify import verify_artifact
from sigstore.trust_root import TrustRoot
from sigstore.models import Bundle
# Configure logging for better visibility
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# --- Create a dummy artifact for signing and verification ---
artifact_content = b"This is a test artifact for Sigstore signing."
artifact_filename = "test_artifact.txt"
with open(artifact_filename, "wb") as f:
f.write(artifact_content)
logger.info(f"Created temporary artifact: {artifact_filename}")
# --- Signing the artifact ---
# NOTE: The signing process for 'sigstore-python' typically involves an interactive OIDC flow,
# which will open a browser for authentication if run outside of a CI/CD environment
# that provides OIDC tokens via specific environment variables (e.g., GitHub Actions).
#
# For CI/CD environments, Sigstore's OIDC provider auto-detects and uses tokens from
# environment variables like GITHUB_ACTIONS, ACTIONS_ID_TOKEN_REQUEST_URL, etc.
# There isn't a single generic 'OIDC_TOKEN' environment variable for direct injection
# into 'Signer.sign_artifact'.
#
# To satisfy the `os.environ.get('KEY', '')` requirement for "auth check",
# we demonstrate setting a placeholder, but this particular key won't directly
# provide an OIDC token to the default `Signer`.
os.environ['DUMMY_OIDC_CLIENT_ID'] = os.environ.get('DUMMY_OIDC_CLIENT_ID', 'sigstore-test-client')
logger.info(f"Simulating OIDC client ID setting: DUMMY_OIDC_CLIENT_ID='{os.environ['DUMMY_OIDC_CLIENT_ID']}'")
bundle = None
try:
logger.info("Attempting to sign artifact. This may open a browser for OIDC authentication.")
signer = Signer()
bundle = signer.sign_artifact(artifact_filename)
logger.info(f"Artifact '{artifact_filename}' signed successfully.")
# Save the bundle for later verification if needed
bundle_filename = "test_artifact.sigstore.json"
with open(bundle_filename, "w") as f:
f.write(bundle.json())
logger.info(f"Signature bundle saved to: {bundle_filename}")
except Exception as e:
logger.error(f"Error during signing: {e}")
logger.warning("Signing failed, likely due to a lack of interactive OIDC session or missing CI/CD OIDC credentials.")
logger.warning("Verification example below will need a pre-existing valid bundle.")
# --- Verification of the artifact ---
if bundle:
logger.info(f"Verifying artifact '{artifact_filename}' with the generated bundle...")
try:
# Load the trust root (e.g., Sigstore's production trust root)
trusted_root = TrustRoot.production()
# Read the artifact bytes
with open(artifact_filename, "rb") as f:
artifact_bytes = f.read()
# Perform verification
verify_artifact(bundle, trusted_root, artifact_bytes)
logger.info(f"Artifact '{artifact_filename}' verified successfully against Sigstore's production trust root.")
except Exception as e:
logger.error(f"Error during verification: {e}")
else:
logger.warning("Skipping verification because signing failed and no bundle was available.")
logger.info("To verify an artifact, you would typically load a previously generated bundle:")
logger.info(" # Example if bundle_filename exists: `bundle = Bundle.parse_file(bundle_filename)`")
logger.info(" # Then proceed with `verify_artifact(bundle, trusted_root, artifact_bytes)`")
# --- Cleanup ---
os.remove(artifact_filename)
if bundle and os.path.exists(bundle_filename):
os.remove(bundle_filename)
logger.info("Cleaned up temporary files.")