sigstore.verify.policy

APIs for describing identity verification "policies", which describe how the identities passed into an individual verification step are verified.

  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 describing identity verification "policies", which describe how the identities
 17passed into an individual verification step are verified.
 18"""
 19
 20from __future__ import annotations
 21
 22import logging
 23from abc import ABC, abstractmethod
 24from typing import Protocol
 25
 26from cryptography.x509 import (
 27    Certificate,
 28    ExtensionNotFound,
 29    ObjectIdentifier,
 30    OtherName,
 31    RFC822Name,
 32    SubjectAlternativeName,
 33    UniformResourceIdentifier,
 34)
 35from pyasn1.codec.der.decoder import decode as der_decode
 36from pyasn1.type.char import UTF8String
 37
 38from sigstore.errors import VerificationError
 39
 40_logger = logging.getLogger(__name__)
 41
 42# From: https://github.com/sigstore/fulcio/blob/main/docs/oid-info.md
 43_OIDC_ISSUER_OID = ObjectIdentifier("1.3.6.1.4.1.57264.1.1")
 44_OIDC_GITHUB_WORKFLOW_TRIGGER_OID = ObjectIdentifier("1.3.6.1.4.1.57264.1.2")
 45_OIDC_GITHUB_WORKFLOW_SHA_OID = ObjectIdentifier("1.3.6.1.4.1.57264.1.3")
 46_OIDC_GITHUB_WORKFLOW_NAME_OID = ObjectIdentifier("1.3.6.1.4.1.57264.1.4")
 47_OIDC_GITHUB_WORKFLOW_REPOSITORY_OID = ObjectIdentifier("1.3.6.1.4.1.57264.1.5")
 48_OIDC_GITHUB_WORKFLOW_REF_OID = ObjectIdentifier("1.3.6.1.4.1.57264.1.6")
 49_OTHERNAME_OID = ObjectIdentifier("1.3.6.1.4.1.57264.1.7")
 50_OIDC_ISSUER_V2_OID = ObjectIdentifier("1.3.6.1.4.1.57264.1.8")
 51_OIDC_BUILD_SIGNER_URI_OID = ObjectIdentifier("1.3.6.1.4.1.57264.1.9")
 52_OIDC_BUILD_SIGNER_DIGEST_OID = ObjectIdentifier("1.3.6.1.4.1.57264.1.10")
 53_OIDC_RUNNER_ENVIRONMENT_OID = ObjectIdentifier("1.3.6.1.4.1.57264.1.11")
 54_OIDC_SOURCE_REPOSITORY_URI_OID = ObjectIdentifier("1.3.6.1.4.1.57264.1.12")
 55_OIDC_SOURCE_REPOSITORY_DIGEST_OID = ObjectIdentifier("1.3.6.1.4.1.57264.1.13")
 56_OIDC_SOURCE_REPOSITORY_REF_OID = ObjectIdentifier("1.3.6.1.4.1.57264.1.14")
 57_OIDC_SOURCE_REPOSITORY_IDENTIFIER_OID = ObjectIdentifier("1.3.6.1.4.1.57264.1.15")
 58_OIDC_SOURCE_REPOSITORY_OWNER_URI_OID = ObjectIdentifier("1.3.6.1.4.1.57264.1.16")
 59_OIDC_SOURCE_REPOSITORY_OWNER_IDENTIFIER_OID = ObjectIdentifier(
 60    "1.3.6.1.4.1.57264.1.17"
 61)
 62_OIDC_BUILD_CONFIG_URI_OID = ObjectIdentifier("1.3.6.1.4.1.57264.1.18")
 63_OIDC_BUILD_CONFIG_DIGEST_OID = ObjectIdentifier("1.3.6.1.4.1.57264.1.19")
 64_OIDC_BUILD_TRIGGER_OID = ObjectIdentifier("1.3.6.1.4.1.57264.1.20")
 65_OIDC_RUN_INVOCATION_URI_OID = ObjectIdentifier("1.3.6.1.4.1.57264.1.21")
 66_OIDC_SOURCE_REPOSITORY_VISIBILITY_OID = ObjectIdentifier("1.3.6.1.4.1.57264.1.22")
 67
 68
 69class _SingleX509ExtPolicy(ABC):
 70    """
 71    An ABC for verification policies that boil down to checking a single
 72    X.509 extension's value.
 73    """
 74
 75    oid: ObjectIdentifier
 76    """
 77    The OID of the extension being checked.
 78    """
 79
 80    def __init__(self, value: str) -> None:
 81        """
 82        Creates the new policy, with `value` as the expected value during
 83        verification.
 84        """
 85        self._value = value
 86
 87    def verify(self, cert: Certificate) -> None:
 88        """
 89        Verify this policy against `cert`.
 90
 91        Raises `VerificationError` on failure.
 92        """
 93        try:
 94            ext = cert.extensions.get_extension_for_oid(self.oid).value
 95        except ExtensionNotFound:
 96            raise VerificationError(
 97                (
 98                    f"Certificate does not contain {self.__class__.__name__} "
 99                    f"({self.oid.dotted_string}) extension"
100                )
101            )
102
103        # NOTE(ww): mypy is confused by the `Extension[ExtensionType]` returned
104        # by `get_extension_for_oid` above.
105        ext_value = ext.value.decode()  # type: ignore[attr-defined]
106        if ext_value != self._value:
107            raise VerificationError(
108                (
109                    f"Certificate's {self.__class__.__name__} does not match "
110                    f"(got '{ext_value}', expected '{self._value}')"
111                )
112            )
113
114
115class _SingleX509ExtPolicyV2(_SingleX509ExtPolicy):
116    """
117    An base class for verification policies that boil down to checking a single
118    X.509 extension's value, where the value is formatted as a DER-encoded string,
119    the ASN.1 tag is UTF8String (0x0C) and the tag class is universal.
120    """
121
122    def verify(self, cert: Certificate) -> None:
123        """
124        Verify this policy against `cert`.
125
126        Raises `VerificationError` on failure.
127        """
128        try:
129            ext = cert.extensions.get_extension_for_oid(self.oid).value
130        except ExtensionNotFound:
131            raise VerificationError(
132                (
133                    f"Certificate does not contain {self.__class__.__name__} "
134                    f"({self.oid.dotted_string}) extension"
135                )
136            )
137
138        # NOTE(ww): mypy is confused by the `Extension[ExtensionType]` returned
139        # by `get_extension_for_oid` above.
140        ext_value = der_decode(ext.value, UTF8String)[0].decode()  # type: ignore[attr-defined]
141        if ext_value != self._value:
142            raise VerificationError(
143                (
144                    f"Certificate's {self.__class__.__name__} does not match "
145                    f"(got {ext_value}, expected {self._value})"
146                )
147            )
148
149
150class OIDCIssuer(_SingleX509ExtPolicy):
151    """
152    Verifies the certificate's OIDC issuer, identified by
153    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.1`.
154    """
155
156    oid = _OIDC_ISSUER_OID
157
158
159class GitHubWorkflowTrigger(_SingleX509ExtPolicy):
160    """
161    Verifies the certificate's GitHub Actions workflow trigger,
162    identified by an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.2`.
163    """
164
165    oid = _OIDC_GITHUB_WORKFLOW_TRIGGER_OID
166
167
168class GitHubWorkflowSHA(_SingleX509ExtPolicy):
169    """
170    Verifies the certificate's GitHub Actions workflow commit SHA,
171    identified by an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.3`.
172    """
173
174    oid = _OIDC_GITHUB_WORKFLOW_SHA_OID
175
176
177class GitHubWorkflowName(_SingleX509ExtPolicy):
178    """
179    Verifies the certificate's GitHub Actions workflow name,
180    identified by an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.4`.
181    """
182
183    oid = _OIDC_GITHUB_WORKFLOW_NAME_OID
184
185
186class GitHubWorkflowRepository(_SingleX509ExtPolicy):
187    """
188    Verifies the certificate's GitHub Actions workflow repository,
189    identified by an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.5`.
190    """
191
192    oid = _OIDC_GITHUB_WORKFLOW_REPOSITORY_OID
193
194
195class GitHubWorkflowRef(_SingleX509ExtPolicy):
196    """
197    Verifies the certificate's GitHub Actions workflow ref,
198    identified by an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.6`.
199    """
200
201    oid = _OIDC_GITHUB_WORKFLOW_REF_OID
202
203
204class OIDCIssuerV2(_SingleX509ExtPolicyV2):
205    """
206    Verifies the certificate's OIDC issuer, identified by
207    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.8`.
208    The difference with `OIDCIssuer` is that the value for
209    this extension is formatted to the RFC 5280 specification
210    as a DER-encoded string.
211    """
212
213    oid = _OIDC_ISSUER_V2_OID
214
215
216class OIDCBuildSignerURI(_SingleX509ExtPolicyV2):
217    """
218    Verifies the certificate's OIDC Build Signer URI, identified by
219    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.9`.
220    """
221
222    oid = _OIDC_BUILD_SIGNER_URI_OID
223
224
225class OIDCBuildSignerDigest(_SingleX509ExtPolicyV2):
226    """
227    Verifies the certificate's OIDC Build Signer Digest, identified by
228    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.10`.
229    """
230
231    oid = _OIDC_BUILD_SIGNER_DIGEST_OID
232
233
234class OIDCRunnerEnvironment(_SingleX509ExtPolicyV2):
235    """
236    Verifies the certificate's OIDC Runner Environment, identified by
237    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.11`.
238    """
239
240    oid = _OIDC_RUNNER_ENVIRONMENT_OID
241
242
243class OIDCSourceRepositoryURI(_SingleX509ExtPolicyV2):
244    """
245    Verifies the certificate's OIDC Source Repository URI, identified by
246    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.12`.
247    """
248
249    oid = _OIDC_SOURCE_REPOSITORY_URI_OID
250
251
252class OIDCSourceRepositoryDigest(_SingleX509ExtPolicyV2):
253    """
254    Verifies the certificate's OIDC Source Repository Digest, identified by
255    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.13`.
256    """
257
258    oid = _OIDC_SOURCE_REPOSITORY_DIGEST_OID
259
260
261class OIDCSourceRepositoryRef(_SingleX509ExtPolicyV2):
262    """
263    Verifies the certificate's OIDC Source Repository Ref, identified by
264    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.14`.
265    """
266
267    oid = _OIDC_SOURCE_REPOSITORY_REF_OID
268
269
270class OIDCSourceRepositoryIdentifier(_SingleX509ExtPolicyV2):
271    """
272    Verifies the certificate's OIDC Source Repository Identifier, identified by
273    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.15`.
274    """
275
276    oid = _OIDC_SOURCE_REPOSITORY_IDENTIFIER_OID
277
278
279class OIDCSourceRepositoryOwnerURI(_SingleX509ExtPolicyV2):
280    """
281    Verifies the certificate's OIDC Source Repository Owner URI, identified by
282    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.16`.
283    """
284
285    oid = _OIDC_SOURCE_REPOSITORY_OWNER_URI_OID
286
287
288class OIDCSourceRepositoryOwnerIdentifier(_SingleX509ExtPolicyV2):
289    """
290    Verifies the certificate's OIDC Source Repository Owner Identifier, identified by
291    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.17`.
292    """
293
294    oid = _OIDC_SOURCE_REPOSITORY_OWNER_IDENTIFIER_OID
295
296
297class OIDCBuildConfigURI(_SingleX509ExtPolicyV2):
298    """
299    Verifies the certificate's OIDC Build Config URI, identified by
300    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.18`.
301    """
302
303    oid = _OIDC_BUILD_CONFIG_URI_OID
304
305
306class OIDCBuildConfigDigest(_SingleX509ExtPolicyV2):
307    """
308    Verifies the certificate's OIDC Build Config Digest, identified by
309    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.19`.
310    """
311
312    oid = _OIDC_BUILD_CONFIG_DIGEST_OID
313
314
315class OIDCBuildTrigger(_SingleX509ExtPolicyV2):
316    """
317    Verifies the certificate's OIDC Build Trigger, identified by
318    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.20`.
319    """
320
321    oid = _OIDC_BUILD_TRIGGER_OID
322
323
324class OIDCRunInvocationURI(_SingleX509ExtPolicyV2):
325    """
326    Verifies the certificate's OIDC Run Invocation URI, identified by
327    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.21`.
328    """
329
330    oid = _OIDC_RUN_INVOCATION_URI_OID
331
332
333class OIDCSourceRepositoryVisibility(_SingleX509ExtPolicyV2):
334    """
335    Verifies the certificate's OIDC Source Repository Visibility
336    At Signing, identified by an X.509v3 extension tagged with
337    `1.3.6.1.4.1.57264.1.22`.
338    """
339
340    oid = _OIDC_SOURCE_REPOSITORY_VISIBILITY_OID
341
342
343class VerificationPolicy(Protocol):
344    """
345    A protocol type describing the interface that all verification policies
346    conform to.
347    """
348
349    @abstractmethod
350    def verify(self, cert: Certificate) -> None:
351        """
352        Verify the given `cert` against this policy, raising `VerificationError`
353        on failure.
354        """
355        raise NotImplementedError  # pragma: no cover
356
357
358class AnyOf:
359    """
360    The "any of" policy, corresponding to a logical OR between child policies.
361
362    An empty list of child policies is considered trivially invalid.
363    """
364
365    def __init__(self, children: list[VerificationPolicy]):
366        """
367        Create a new `AnyOf`, with the given child policies.
368        """
369        self._children = children
370
371    def verify(self, cert: Certificate) -> None:
372        """
373        Verify `cert` against the policy.
374
375        Raises `VerificationError` on failure.
376        """
377
378        for child in self._children:
379            try:
380                child.verify(cert)
381            except VerificationError:
382                pass
383            else:
384                return
385
386        raise VerificationError(f"0 of {len(self._children)} policies succeeded")
387
388
389class AllOf:
390    """
391    The "all of" policy, corresponding to a logical AND between child
392    policies.
393
394    An empty list of child policies is considered trivially invalid.
395    """
396
397    def __init__(self, children: list[VerificationPolicy]):
398        """
399        Create a new `AllOf`, with the given child policies.
400        """
401
402        self._children = children
403
404    def verify(self, cert: Certificate) -> None:
405        """
406        Verify `cert` against the policy.
407        """
408
409        # Without this, we'd consider empty lists of child policies trivially valid.
410        # This is almost certainly not what the user wants and is a potential
411        # source of API misuse, so we explicitly disallow it.
412        if len(self._children) < 1:
413            raise VerificationError("no child policies to verify")
414
415        for child in self._children:
416            child.verify(cert)
417
418
419class UnsafeNoOp:
420    """
421    The "no-op" policy, corresponding to a no-op "verification".
422
423    **This policy is fundamentally insecure. You cannot use it safely.
424    It must not be used to verify any sort of certificate identity, because
425    it cannot do so. Using this policy is equivalent to reducing the
426    verification proof down to an integrity check against a completely
427    untrusted and potentially attacker-created signature. It must only
428    be used for testing purposes.**
429    """
430
431    def verify(self, cert: Certificate) -> None:
432        """
433        Verify `cert` against the policy.
434        """
435
436        _logger.warning(
437            "unsafe (no-op) verification policy used! no verification performed!"
438        )
439
440
441class Identity:
442    """
443    Verifies the certificate's "identity", corresponding to the X.509v3 SAN.
444
445    Identities can be verified modulo an OIDC issuer, to prevent an unexpected
446    issuer from offering a particular identity.
447
448    Supported SAN types include emails, URIs, and Sigstore-specific "other names".
449    """
450
451    _issuer: OIDCIssuer | None
452
453    def __init__(self, *, identity: str, issuer: str | None = None):
454        """
455        Create a new `Identity`, with the given expected identity and issuer values.
456        """
457
458        self._identity = identity
459        if issuer:
460            self._issuer = OIDCIssuer(issuer)
461        else:
462            self._issuer = None
463
464    def verify(self, cert: Certificate) -> None:
465        """
466        Verify `cert` against the policy.
467        """
468
469        if self._issuer:
470            self._issuer.verify(cert)
471
472        # Build a set of all valid identities.
473        san_ext = cert.extensions.get_extension_for_class(SubjectAlternativeName).value
474        all_sans = set(san_ext.get_values_for_type(RFC822Name))
475        all_sans.update(san_ext.get_values_for_type(UniformResourceIdentifier))
476        all_sans.update(
477            [
478                on.value.decode()
479                for on in san_ext.get_values_for_type(OtherName)
480                if on.type_id == _OTHERNAME_OID
481            ]
482        )
483
484        verified = self._identity in all_sans
485        if not verified:
486            raise VerificationError(
487                f"Certificate's SANs do not match {self._identity}; actual SANs: {all_sans}"
488            )
class OIDCIssuer(_SingleX509ExtPolicy):
151class OIDCIssuer(_SingleX509ExtPolicy):
152    """
153    Verifies the certificate's OIDC issuer, identified by
154    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.1`.
155    """
156
157    oid = _OIDC_ISSUER_OID

Verifies the certificate's OIDC issuer, identified by an X.509v3 extension tagged with 1.3.6.1.4.1.57264.1.1.

oid = <ObjectIdentifier(oid=1.3.6.1.4.1.57264.1.1, name=Unknown OID)>

The OID of the extension being checked.

class GitHubWorkflowTrigger(_SingleX509ExtPolicy):
160class GitHubWorkflowTrigger(_SingleX509ExtPolicy):
161    """
162    Verifies the certificate's GitHub Actions workflow trigger,
163    identified by an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.2`.
164    """
165
166    oid = _OIDC_GITHUB_WORKFLOW_TRIGGER_OID

Verifies the certificate's GitHub Actions workflow trigger, identified by an X.509v3 extension tagged with 1.3.6.1.4.1.57264.1.2.

oid = <ObjectIdentifier(oid=1.3.6.1.4.1.57264.1.2, name=Unknown OID)>

The OID of the extension being checked.

class GitHubWorkflowSHA(_SingleX509ExtPolicy):
169class GitHubWorkflowSHA(_SingleX509ExtPolicy):
170    """
171    Verifies the certificate's GitHub Actions workflow commit SHA,
172    identified by an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.3`.
173    """
174
175    oid = _OIDC_GITHUB_WORKFLOW_SHA_OID

Verifies the certificate's GitHub Actions workflow commit SHA, identified by an X.509v3 extension tagged with 1.3.6.1.4.1.57264.1.3.

oid = <ObjectIdentifier(oid=1.3.6.1.4.1.57264.1.3, name=Unknown OID)>

The OID of the extension being checked.

class GitHubWorkflowName(_SingleX509ExtPolicy):
178class GitHubWorkflowName(_SingleX509ExtPolicy):
179    """
180    Verifies the certificate's GitHub Actions workflow name,
181    identified by an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.4`.
182    """
183
184    oid = _OIDC_GITHUB_WORKFLOW_NAME_OID

Verifies the certificate's GitHub Actions workflow name, identified by an X.509v3 extension tagged with 1.3.6.1.4.1.57264.1.4.

oid = <ObjectIdentifier(oid=1.3.6.1.4.1.57264.1.4, name=Unknown OID)>

The OID of the extension being checked.

class GitHubWorkflowRepository(_SingleX509ExtPolicy):
187class GitHubWorkflowRepository(_SingleX509ExtPolicy):
188    """
189    Verifies the certificate's GitHub Actions workflow repository,
190    identified by an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.5`.
191    """
192
193    oid = _OIDC_GITHUB_WORKFLOW_REPOSITORY_OID

Verifies the certificate's GitHub Actions workflow repository, identified by an X.509v3 extension tagged with 1.3.6.1.4.1.57264.1.5.

oid = <ObjectIdentifier(oid=1.3.6.1.4.1.57264.1.5, name=Unknown OID)>

The OID of the extension being checked.

class GitHubWorkflowRef(_SingleX509ExtPolicy):
196class GitHubWorkflowRef(_SingleX509ExtPolicy):
197    """
198    Verifies the certificate's GitHub Actions workflow ref,
199    identified by an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.6`.
200    """
201
202    oid = _OIDC_GITHUB_WORKFLOW_REF_OID

Verifies the certificate's GitHub Actions workflow ref, identified by an X.509v3 extension tagged with 1.3.6.1.4.1.57264.1.6.

oid = <ObjectIdentifier(oid=1.3.6.1.4.1.57264.1.6, name=Unknown OID)>

The OID of the extension being checked.

class OIDCIssuerV2(_SingleX509ExtPolicyV2):
205class OIDCIssuerV2(_SingleX509ExtPolicyV2):
206    """
207    Verifies the certificate's OIDC issuer, identified by
208    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.8`.
209    The difference with `OIDCIssuer` is that the value for
210    this extension is formatted to the RFC 5280 specification
211    as a DER-encoded string.
212    """
213
214    oid = _OIDC_ISSUER_V2_OID

Verifies the certificate's OIDC issuer, identified by an X.509v3 extension tagged with 1.3.6.1.4.1.57264.1.8. The difference with OIDCIssuer is that the value for this extension is formatted to the RFC 5280 specification as a DER-encoded string.

oid = <ObjectIdentifier(oid=1.3.6.1.4.1.57264.1.8, name=Unknown OID)>

The OID of the extension being checked.

class OIDCBuildSignerURI(_SingleX509ExtPolicyV2):
217class OIDCBuildSignerURI(_SingleX509ExtPolicyV2):
218    """
219    Verifies the certificate's OIDC Build Signer URI, identified by
220    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.9`.
221    """
222
223    oid = _OIDC_BUILD_SIGNER_URI_OID

Verifies the certificate's OIDC Build Signer URI, identified by an X.509v3 extension tagged with 1.3.6.1.4.1.57264.1.9.

oid = <ObjectIdentifier(oid=1.3.6.1.4.1.57264.1.9, name=Unknown OID)>

The OID of the extension being checked.

class OIDCBuildSignerDigest(_SingleX509ExtPolicyV2):
226class OIDCBuildSignerDigest(_SingleX509ExtPolicyV2):
227    """
228    Verifies the certificate's OIDC Build Signer Digest, identified by
229    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.10`.
230    """
231
232    oid = _OIDC_BUILD_SIGNER_DIGEST_OID

Verifies the certificate's OIDC Build Signer Digest, identified by an X.509v3 extension tagged with 1.3.6.1.4.1.57264.1.10.

oid = <ObjectIdentifier(oid=1.3.6.1.4.1.57264.1.10, name=Unknown OID)>

The OID of the extension being checked.

class OIDCRunnerEnvironment(_SingleX509ExtPolicyV2):
235class OIDCRunnerEnvironment(_SingleX509ExtPolicyV2):
236    """
237    Verifies the certificate's OIDC Runner Environment, identified by
238    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.11`.
239    """
240
241    oid = _OIDC_RUNNER_ENVIRONMENT_OID

Verifies the certificate's OIDC Runner Environment, identified by an X.509v3 extension tagged with 1.3.6.1.4.1.57264.1.11.

oid = <ObjectIdentifier(oid=1.3.6.1.4.1.57264.1.11, name=Unknown OID)>

The OID of the extension being checked.

class OIDCSourceRepositoryURI(_SingleX509ExtPolicyV2):
244class OIDCSourceRepositoryURI(_SingleX509ExtPolicyV2):
245    """
246    Verifies the certificate's OIDC Source Repository URI, identified by
247    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.12`.
248    """
249
250    oid = _OIDC_SOURCE_REPOSITORY_URI_OID

Verifies the certificate's OIDC Source Repository URI, identified by an X.509v3 extension tagged with 1.3.6.1.4.1.57264.1.12.

oid = <ObjectIdentifier(oid=1.3.6.1.4.1.57264.1.12, name=Unknown OID)>

The OID of the extension being checked.

class OIDCSourceRepositoryDigest(_SingleX509ExtPolicyV2):
253class OIDCSourceRepositoryDigest(_SingleX509ExtPolicyV2):
254    """
255    Verifies the certificate's OIDC Source Repository Digest, identified by
256    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.13`.
257    """
258
259    oid = _OIDC_SOURCE_REPOSITORY_DIGEST_OID

Verifies the certificate's OIDC Source Repository Digest, identified by an X.509v3 extension tagged with 1.3.6.1.4.1.57264.1.13.

oid = <ObjectIdentifier(oid=1.3.6.1.4.1.57264.1.13, name=Unknown OID)>

The OID of the extension being checked.

class OIDCSourceRepositoryRef(_SingleX509ExtPolicyV2):
262class OIDCSourceRepositoryRef(_SingleX509ExtPolicyV2):
263    """
264    Verifies the certificate's OIDC Source Repository Ref, identified by
265    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.14`.
266    """
267
268    oid = _OIDC_SOURCE_REPOSITORY_REF_OID

Verifies the certificate's OIDC Source Repository Ref, identified by an X.509v3 extension tagged with 1.3.6.1.4.1.57264.1.14.

oid = <ObjectIdentifier(oid=1.3.6.1.4.1.57264.1.14, name=Unknown OID)>

The OID of the extension being checked.

class OIDCSourceRepositoryIdentifier(_SingleX509ExtPolicyV2):
271class OIDCSourceRepositoryIdentifier(_SingleX509ExtPolicyV2):
272    """
273    Verifies the certificate's OIDC Source Repository Identifier, identified by
274    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.15`.
275    """
276
277    oid = _OIDC_SOURCE_REPOSITORY_IDENTIFIER_OID

Verifies the certificate's OIDC Source Repository Identifier, identified by an X.509v3 extension tagged with 1.3.6.1.4.1.57264.1.15.

oid = <ObjectIdentifier(oid=1.3.6.1.4.1.57264.1.15, name=Unknown OID)>

The OID of the extension being checked.

class OIDCSourceRepositoryOwnerURI(_SingleX509ExtPolicyV2):
280class OIDCSourceRepositoryOwnerURI(_SingleX509ExtPolicyV2):
281    """
282    Verifies the certificate's OIDC Source Repository Owner URI, identified by
283    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.16`.
284    """
285
286    oid = _OIDC_SOURCE_REPOSITORY_OWNER_URI_OID

Verifies the certificate's OIDC Source Repository Owner URI, identified by an X.509v3 extension tagged with 1.3.6.1.4.1.57264.1.16.

oid = <ObjectIdentifier(oid=1.3.6.1.4.1.57264.1.16, name=Unknown OID)>

The OID of the extension being checked.

class OIDCSourceRepositoryOwnerIdentifier(_SingleX509ExtPolicyV2):
289class OIDCSourceRepositoryOwnerIdentifier(_SingleX509ExtPolicyV2):
290    """
291    Verifies the certificate's OIDC Source Repository Owner Identifier, identified by
292    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.17`.
293    """
294
295    oid = _OIDC_SOURCE_REPOSITORY_OWNER_IDENTIFIER_OID

Verifies the certificate's OIDC Source Repository Owner Identifier, identified by an X.509v3 extension tagged with 1.3.6.1.4.1.57264.1.17.

oid = <ObjectIdentifier(oid=1.3.6.1.4.1.57264.1.17, name=Unknown OID)>

The OID of the extension being checked.

class OIDCBuildConfigURI(_SingleX509ExtPolicyV2):
298class OIDCBuildConfigURI(_SingleX509ExtPolicyV2):
299    """
300    Verifies the certificate's OIDC Build Config URI, identified by
301    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.18`.
302    """
303
304    oid = _OIDC_BUILD_CONFIG_URI_OID

Verifies the certificate's OIDC Build Config URI, identified by an X.509v3 extension tagged with 1.3.6.1.4.1.57264.1.18.

oid = <ObjectIdentifier(oid=1.3.6.1.4.1.57264.1.18, name=Unknown OID)>

The OID of the extension being checked.

class OIDCBuildConfigDigest(_SingleX509ExtPolicyV2):
307class OIDCBuildConfigDigest(_SingleX509ExtPolicyV2):
308    """
309    Verifies the certificate's OIDC Build Config Digest, identified by
310    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.19`.
311    """
312
313    oid = _OIDC_BUILD_CONFIG_DIGEST_OID

Verifies the certificate's OIDC Build Config Digest, identified by an X.509v3 extension tagged with 1.3.6.1.4.1.57264.1.19.

oid = <ObjectIdentifier(oid=1.3.6.1.4.1.57264.1.19, name=Unknown OID)>

The OID of the extension being checked.

class OIDCBuildTrigger(_SingleX509ExtPolicyV2):
316class OIDCBuildTrigger(_SingleX509ExtPolicyV2):
317    """
318    Verifies the certificate's OIDC Build Trigger, identified by
319    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.20`.
320    """
321
322    oid = _OIDC_BUILD_TRIGGER_OID

Verifies the certificate's OIDC Build Trigger, identified by an X.509v3 extension tagged with 1.3.6.1.4.1.57264.1.20.

oid = <ObjectIdentifier(oid=1.3.6.1.4.1.57264.1.20, name=Unknown OID)>

The OID of the extension being checked.

class OIDCRunInvocationURI(_SingleX509ExtPolicyV2):
325class OIDCRunInvocationURI(_SingleX509ExtPolicyV2):
326    """
327    Verifies the certificate's OIDC Run Invocation URI, identified by
328    an X.509v3 extension tagged with `1.3.6.1.4.1.57264.1.21`.
329    """
330
331    oid = _OIDC_RUN_INVOCATION_URI_OID

Verifies the certificate's OIDC Run Invocation URI, identified by an X.509v3 extension tagged with 1.3.6.1.4.1.57264.1.21.

oid = <ObjectIdentifier(oid=1.3.6.1.4.1.57264.1.21, name=Unknown OID)>

The OID of the extension being checked.

class OIDCSourceRepositoryVisibility(_SingleX509ExtPolicyV2):
334class OIDCSourceRepositoryVisibility(_SingleX509ExtPolicyV2):
335    """
336    Verifies the certificate's OIDC Source Repository Visibility
337    At Signing, identified by an X.509v3 extension tagged with
338    `1.3.6.1.4.1.57264.1.22`.
339    """
340
341    oid = _OIDC_SOURCE_REPOSITORY_VISIBILITY_OID

Verifies the certificate's OIDC Source Repository Visibility At Signing, identified by an X.509v3 extension tagged with 1.3.6.1.4.1.57264.1.22.

oid = <ObjectIdentifier(oid=1.3.6.1.4.1.57264.1.22, name=Unknown OID)>

The OID of the extension being checked.

class VerificationPolicy(typing.Protocol):
344class VerificationPolicy(Protocol):
345    """
346    A protocol type describing the interface that all verification policies
347    conform to.
348    """
349
350    @abstractmethod
351    def verify(self, cert: Certificate) -> None:
352        """
353        Verify the given `cert` against this policy, raising `VerificationError`
354        on failure.
355        """
356        raise NotImplementedError  # pragma: no cover

A protocol type describing the interface that all verification policies conform to.

@abstractmethod
def verify(self, cert: cryptography.x509.base.Certificate) -> None:
350    @abstractmethod
351    def verify(self, cert: Certificate) -> None:
352        """
353        Verify the given `cert` against this policy, raising `VerificationError`
354        on failure.
355        """
356        raise NotImplementedError  # pragma: no cover

Verify the given cert against this policy, raising VerificationError on failure.

class AnyOf:
359class AnyOf:
360    """
361    The "any of" policy, corresponding to a logical OR between child policies.
362
363    An empty list of child policies is considered trivially invalid.
364    """
365
366    def __init__(self, children: list[VerificationPolicy]):
367        """
368        Create a new `AnyOf`, with the given child policies.
369        """
370        self._children = children
371
372    def verify(self, cert: Certificate) -> None:
373        """
374        Verify `cert` against the policy.
375
376        Raises `VerificationError` on failure.
377        """
378
379        for child in self._children:
380            try:
381                child.verify(cert)
382            except VerificationError:
383                pass
384            else:
385                return
386
387        raise VerificationError(f"0 of {len(self._children)} policies succeeded")

The "any of" policy, corresponding to a logical OR between child policies.

An empty list of child policies is considered trivially invalid.

AnyOf(children: list[VerificationPolicy])
366    def __init__(self, children: list[VerificationPolicy]):
367        """
368        Create a new `AnyOf`, with the given child policies.
369        """
370        self._children = children

Create a new AnyOf, with the given child policies.

def verify(self, cert: cryptography.x509.base.Certificate) -> None:
372    def verify(self, cert: Certificate) -> None:
373        """
374        Verify `cert` against the policy.
375
376        Raises `VerificationError` on failure.
377        """
378
379        for child in self._children:
380            try:
381                child.verify(cert)
382            except VerificationError:
383                pass
384            else:
385                return
386
387        raise VerificationError(f"0 of {len(self._children)} policies succeeded")

Verify cert against the policy.

Raises VerificationError on failure.

class AllOf:
390class AllOf:
391    """
392    The "all of" policy, corresponding to a logical AND between child
393    policies.
394
395    An empty list of child policies is considered trivially invalid.
396    """
397
398    def __init__(self, children: list[VerificationPolicy]):
399        """
400        Create a new `AllOf`, with the given child policies.
401        """
402
403        self._children = children
404
405    def verify(self, cert: Certificate) -> None:
406        """
407        Verify `cert` against the policy.
408        """
409
410        # Without this, we'd consider empty lists of child policies trivially valid.
411        # This is almost certainly not what the user wants and is a potential
412        # source of API misuse, so we explicitly disallow it.
413        if len(self._children) < 1:
414            raise VerificationError("no child policies to verify")
415
416        for child in self._children:
417            child.verify(cert)

The "all of" policy, corresponding to a logical AND between child policies.

An empty list of child policies is considered trivially invalid.

AllOf(children: list[VerificationPolicy])
398    def __init__(self, children: list[VerificationPolicy]):
399        """
400        Create a new `AllOf`, with the given child policies.
401        """
402
403        self._children = children

Create a new AllOf, with the given child policies.

def verify(self, cert: cryptography.x509.base.Certificate) -> None:
405    def verify(self, cert: Certificate) -> None:
406        """
407        Verify `cert` against the policy.
408        """
409
410        # Without this, we'd consider empty lists of child policies trivially valid.
411        # This is almost certainly not what the user wants and is a potential
412        # source of API misuse, so we explicitly disallow it.
413        if len(self._children) < 1:
414            raise VerificationError("no child policies to verify")
415
416        for child in self._children:
417            child.verify(cert)

Verify cert against the policy.

class UnsafeNoOp:
420class UnsafeNoOp:
421    """
422    The "no-op" policy, corresponding to a no-op "verification".
423
424    **This policy is fundamentally insecure. You cannot use it safely.
425    It must not be used to verify any sort of certificate identity, because
426    it cannot do so. Using this policy is equivalent to reducing the
427    verification proof down to an integrity check against a completely
428    untrusted and potentially attacker-created signature. It must only
429    be used for testing purposes.**
430    """
431
432    def verify(self, cert: Certificate) -> None:
433        """
434        Verify `cert` against the policy.
435        """
436
437        _logger.warning(
438            "unsafe (no-op) verification policy used! no verification performed!"
439        )

The "no-op" policy, corresponding to a no-op "verification".

This policy is fundamentally insecure. You cannot use it safely. It must not be used to verify any sort of certificate identity, because it cannot do so. Using this policy is equivalent to reducing the verification proof down to an integrity check against a completely untrusted and potentially attacker-created signature. It must only be used for testing purposes.

def verify(self, cert: cryptography.x509.base.Certificate) -> None:
432    def verify(self, cert: Certificate) -> None:
433        """
434        Verify `cert` against the policy.
435        """
436
437        _logger.warning(
438            "unsafe (no-op) verification policy used! no verification performed!"
439        )

Verify cert against the policy.

class Identity:
442class Identity:
443    """
444    Verifies the certificate's "identity", corresponding to the X.509v3 SAN.
445
446    Identities can be verified modulo an OIDC issuer, to prevent an unexpected
447    issuer from offering a particular identity.
448
449    Supported SAN types include emails, URIs, and Sigstore-specific "other names".
450    """
451
452    _issuer: OIDCIssuer | None
453
454    def __init__(self, *, identity: str, issuer: str | None = None):
455        """
456        Create a new `Identity`, with the given expected identity and issuer values.
457        """
458
459        self._identity = identity
460        if issuer:
461            self._issuer = OIDCIssuer(issuer)
462        else:
463            self._issuer = None
464
465    def verify(self, cert: Certificate) -> None:
466        """
467        Verify `cert` against the policy.
468        """
469
470        if self._issuer:
471            self._issuer.verify(cert)
472
473        # Build a set of all valid identities.
474        san_ext = cert.extensions.get_extension_for_class(SubjectAlternativeName).value
475        all_sans = set(san_ext.get_values_for_type(RFC822Name))
476        all_sans.update(san_ext.get_values_for_type(UniformResourceIdentifier))
477        all_sans.update(
478            [
479                on.value.decode()
480                for on in san_ext.get_values_for_type(OtherName)
481                if on.type_id == _OTHERNAME_OID
482            ]
483        )
484
485        verified = self._identity in all_sans
486        if not verified:
487            raise VerificationError(
488                f"Certificate's SANs do not match {self._identity}; actual SANs: {all_sans}"
489            )

Verifies the certificate's "identity", corresponding to the X.509v3 SAN.

Identities can be verified modulo an OIDC issuer, to prevent an unexpected issuer from offering a particular identity.

Supported SAN types include emails, URIs, and Sigstore-specific "other names".

Identity(*, identity: str, issuer: str | None = None)
454    def __init__(self, *, identity: str, issuer: str | None = None):
455        """
456        Create a new `Identity`, with the given expected identity and issuer values.
457        """
458
459        self._identity = identity
460        if issuer:
461            self._issuer = OIDCIssuer(issuer)
462        else:
463            self._issuer = None

Create a new Identity, with the given expected identity and issuer values.

def verify(self, cert: cryptography.x509.base.Certificate) -> None:
465    def verify(self, cert: Certificate) -> None:
466        """
467        Verify `cert` against the policy.
468        """
469
470        if self._issuer:
471            self._issuer.verify(cert)
472
473        # Build a set of all valid identities.
474        san_ext = cert.extensions.get_extension_for_class(SubjectAlternativeName).value
475        all_sans = set(san_ext.get_values_for_type(RFC822Name))
476        all_sans.update(san_ext.get_values_for_type(UniformResourceIdentifier))
477        all_sans.update(
478            [
479                on.value.decode()
480                for on in san_ext.get_values_for_type(OtherName)
481                if on.type_id == _OTHERNAME_OID
482            ]
483        )
484
485        verified = self._identity in all_sans
486        if not verified:
487            raise VerificationError(
488                f"Certificate's SANs do not match {self._identity}; actual SANs: {all_sans}"
489            )

Verify cert against the policy.