sigstore._internal.fulcio

APIs for interacting with Fulcio.

 1# Copyright 2022 The Sigstore Authors
 2#
 3# Licensed under the Apache License, Version 2.0 (the "License");
 4# you may not use this file except in compliance with the License.
 5# You may obtain a copy of the License at
 6#
 7#      http://www.apache.org/licenses/LICENSE-2.0
 8#
 9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""
16APIs for interacting with Fulcio.
17"""
18
19from .client import (
20    DetachedFulcioSCT,
21    ExpiredCertificate,
22    FulcioCertificateSigningResponse,
23    FulcioClient,
24)
25
26__all__ = [
27    "DetachedFulcioSCT",
28    "ExpiredCertificate",
29    "FulcioCertificateSigningResponse",
30    "FulcioClient",
31]
class DetachedFulcioSCT(pydantic.main.BaseModel):
 99class DetachedFulcioSCT(BaseModel):
100    """
101    Represents a "detached" SignedCertificateTimestamp from Fulcio.
102    """
103
104    model_config = ConfigDict(populate_by_name=True, arbitrary_types_allowed=True)
105
106    version: Version = Field(..., alias="sct_version")
107    log_id: bytes = Field(..., alias="id")
108    timestamp: datetime.datetime
109    digitally_signed: bytes = Field(..., alias="signature")
110    extension_bytes: bytes = Field(..., alias="extensions")
111
112    @field_validator("timestamp")
113    def _validate_timestamp(cls, v: datetime.datetime) -> datetime.datetime:
114        return v.replace(tzinfo=datetime.timezone.utc)
115
116    @field_validator("digitally_signed", mode="before")
117    def _validate_digitally_signed(cls, v: bytes) -> bytes:
118        digitally_signed = base64.b64decode(v)
119
120        if len(digitally_signed) <= 4:
121            raise ValueError("impossibly small digitally-signed struct")
122
123        return digitally_signed
124
125    @field_validator("log_id", mode="before")
126    def _validate_log_id(cls, v: bytes) -> bytes:
127        return base64.b64decode(v)
128
129    @field_validator("extension_bytes", mode="before")
130    def _validate_extensions(cls, v: bytes) -> bytes:
131        return base64.b64decode(v)
132
133    @property
134    def entry_type(self) -> LogEntryType:
135        """
136        Returns the kind of CT log entry this detached SCT is signing for.
137        """
138        return LogEntryType.X509_CERTIFICATE
139
140    @property
141    def signature_hash_algorithm(self) -> hashes.HashAlgorithm:
142        """
143        Returns the hash algorithm used in this detached SCT's signature.
144        """
145        hash_ = SCTHashAlgorithm(self.digitally_signed[0])
146        return hash_.to_cryptography()
147
148    @property
149    def signature_algorithm(self) -> SignatureAlgorithm:
150        """
151        Returns the signature algorithm used in this detached SCT's signature.
152        """
153        return SignatureAlgorithm(self.digitally_signed[1])
154
155    @property
156    def signature(self) -> bytes:
157        """
158        Returns the raw signature inside the detached SCT.
159        """
160        (sig_size,) = struct.unpack("!H", self.digitally_signed[2:4])
161        if len(self.digitally_signed[4:]) != sig_size:
162            raise FulcioSCTError(
163                f"signature size mismatch: expected {sig_size} bytes, "
164                f"got {len(self.digitally_signed[4:])}"
165            )
166        return self.digitally_signed[4:]

Represents a "detached" SignedCertificateTimestamp from Fulcio.

model_config = {'populate_by_name': True, 'arbitrary_types_allowed': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

version: cryptography.x509.certificate_transparency.Version
log_id: bytes
timestamp: datetime.datetime
digitally_signed: bytes
extension_bytes: bytes
entry_type: cryptography.x509.certificate_transparency.LogEntryType
133    @property
134    def entry_type(self) -> LogEntryType:
135        """
136        Returns the kind of CT log entry this detached SCT is signing for.
137        """
138        return LogEntryType.X509_CERTIFICATE

Returns the kind of CT log entry this detached SCT is signing for.

signature_hash_algorithm: cryptography.hazmat.primitives.hashes.HashAlgorithm
140    @property
141    def signature_hash_algorithm(self) -> hashes.HashAlgorithm:
142        """
143        Returns the hash algorithm used in this detached SCT's signature.
144        """
145        hash_ = SCTHashAlgorithm(self.digitally_signed[0])
146        return hash_.to_cryptography()

Returns the hash algorithm used in this detached SCT's signature.

signature_algorithm: cryptography.x509.certificate_transparency.SignatureAlgorithm
148    @property
149    def signature_algorithm(self) -> SignatureAlgorithm:
150        """
151        Returns the signature algorithm used in this detached SCT's signature.
152        """
153        return SignatureAlgorithm(self.digitally_signed[1])

Returns the signature algorithm used in this detached SCT's signature.

signature: bytes
155    @property
156    def signature(self) -> bytes:
157        """
158        Returns the raw signature inside the detached SCT.
159        """
160        (sig_size,) = struct.unpack("!H", self.digitally_signed[2:4])
161        if len(self.digitally_signed[4:]) != sig_size:
162            raise FulcioSCTError(
163                f"signature size mismatch: expected {sig_size} bytes, "
164                f"got {len(self.digitally_signed[4:])}"
165            )
166        return self.digitally_signed[4:]

Returns the raw signature inside the detached SCT.

model_fields: ClassVar[Dict[str, pydantic.fields.FieldInfo]] = {'version': FieldInfo(annotation=Version, required=True, alias='sct_version', alias_priority=2), 'log_id': FieldInfo(annotation=bytes, required=True, alias='id', alias_priority=2), 'timestamp': FieldInfo(annotation=datetime, required=True), 'digitally_signed': FieldInfo(annotation=bytes, required=True, alias='signature', alias_priority=2), 'extension_bytes': FieldInfo(annotation=bytes, required=True, alias='extensions', alias_priority=2)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo] objects.

This replaces Model.__fields__ from Pydantic V1.

model_computed_fields: ClassVar[Dict[str, pydantic.fields.ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

class ExpiredCertificate(builtins.Exception):
174class ExpiredCertificate(Exception):
175    """An error raised when the Certificate is expired."""

An error raised when the Certificate is expired.

@dataclass(frozen=True)
class FulcioCertificateSigningResponse:
178@dataclass(frozen=True)
179class FulcioCertificateSigningResponse:
180    """Certificate response"""
181
182    cert: Certificate
183    chain: List[Certificate]
184    sct: SignedCertificateTimestamp

Certificate response

FulcioCertificateSigningResponse( cert: cryptography.x509.base.Certificate, chain: List[cryptography.x509.base.Certificate], sct: cryptography.x509.certificate_transparency.SignedCertificateTimestamp)
cert: cryptography.x509.base.Certificate
chain: List[cryptography.x509.base.Certificate]
sct: cryptography.x509.certificate_transparency.SignedCertificateTimestamp
class FulcioClient:
335class FulcioClient:
336    """The internal Fulcio client"""
337
338    def __init__(self, url: str = DEFAULT_FULCIO_URL) -> None:
339        """Initialize the client"""
340        _logger.debug(f"Fulcio client using URL: {url}")
341        self.url = url
342        self.session = requests.Session()
343        self.session.headers.update(
344            {
345                "User-Agent": USER_AGENT,
346            }
347        )
348
349    def __del__(self) -> None:
350        """
351        Destroys the underlying network session.
352        """
353        self.session.close()
354
355    @classmethod
356    def production(cls) -> FulcioClient:
357        """
358        Returns a `FulcioClient` for the Sigstore production instance of Fulcio.
359        """
360        return cls(DEFAULT_FULCIO_URL)
361
362    @classmethod
363    def staging(cls) -> FulcioClient:
364        """
365        Returns a `FulcioClient` for the Sigstore staging instance of Fulcio.
366        """
367        return cls(STAGING_FULCIO_URL)
368
369    @property
370    def signing_cert(self) -> FulcioSigningCert:
371        """
372        Returns a model capable of interacting with Fulcio's signing certificate endpoints.
373        """
374        return FulcioSigningCert(
375            urljoin(self.url, SIGNING_CERT_ENDPOINT), session=self.session
376        )
377
378    @property
379    def trust_bundle(self) -> FulcioTrustBundle:
380        """
381        Returns a model capable of interacting with Fulcio's trust bundle endpoints.
382        """
383        return FulcioTrustBundle(
384            urljoin(self.url, TRUST_BUNDLE_ENDPOINT), session=self.session
385        )

The internal Fulcio client

FulcioClient(url: str = 'https://fulcio.sigstore.dev')
338    def __init__(self, url: str = DEFAULT_FULCIO_URL) -> None:
339        """Initialize the client"""
340        _logger.debug(f"Fulcio client using URL: {url}")
341        self.url = url
342        self.session = requests.Session()
343        self.session.headers.update(
344            {
345                "User-Agent": USER_AGENT,
346            }
347        )

Initialize the client

url
session
@classmethod
def production(cls) -> FulcioClient:
355    @classmethod
356    def production(cls) -> FulcioClient:
357        """
358        Returns a `FulcioClient` for the Sigstore production instance of Fulcio.
359        """
360        return cls(DEFAULT_FULCIO_URL)

Returns a FulcioClient for the Sigstore production instance of Fulcio.

@classmethod
def staging(cls) -> FulcioClient:
362    @classmethod
363    def staging(cls) -> FulcioClient:
364        """
365        Returns a `FulcioClient` for the Sigstore staging instance of Fulcio.
366        """
367        return cls(STAGING_FULCIO_URL)

Returns a FulcioClient for the Sigstore staging instance of Fulcio.

signing_cert: sigstore._internal.fulcio.client.FulcioSigningCert
369    @property
370    def signing_cert(self) -> FulcioSigningCert:
371        """
372        Returns a model capable of interacting with Fulcio's signing certificate endpoints.
373        """
374        return FulcioSigningCert(
375            urljoin(self.url, SIGNING_CERT_ENDPOINT), session=self.session
376        )

Returns a model capable of interacting with Fulcio's signing certificate endpoints.

trust_bundle: sigstore._internal.fulcio.client.FulcioTrustBundle
378    @property
379    def trust_bundle(self) -> FulcioTrustBundle:
380        """
381        Returns a model capable of interacting with Fulcio's trust bundle endpoints.
382        """
383        return FulcioTrustBundle(
384            urljoin(self.url, TRUST_BUNDLE_ENDPOINT), session=self.session
385        )

Returns a model capable of interacting with Fulcio's trust bundle endpoints.