Skip to content

Verifier

Verification API machinery.

Verifier(*, rekor, trusted_root)

The primary API for verification operations.

Create a new Verifier.

rekor is a RekorClient capable of connecting to a Rekor instance containing logs for the file(s) being verified.

trusted_root is the TrustedRoot object containing the root of trust for the verification process.

Source code in sigstore/verify/verifier.py
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
def __init__(self, *, rekor: RekorClient, trusted_root: TrustedRoot):
    """
    Create a new `Verifier`.

    `rekor` is a `RekorClient` capable of connecting to a Rekor instance
    containing logs for the file(s) being verified.

    `trusted_root` is the `TrustedRoot` object containing the root of trust
    for the verification process.
    """
    self._rekor = rekor
    self._fulcio_certificate_chain: list[X509] = [
        X509.from_cryptography(parent_cert)
        for parent_cert in trusted_root.get_fulcio_certs()
    ]
    self._trusted_root = trusted_root

production(*, offline=False) classmethod

Return a Verifier instance configured against Sigstore's production-level services.

offline controls the Trusted Root refresh behavior: if True, the verifier uses the Trusted Root in the local TUF cache. If False, a TUF repository refresh is attempted.

Source code in sigstore/verify/verifier.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
@classmethod
def production(cls, *, offline: bool = False) -> Verifier:
    """
    Return a `Verifier` instance configured against Sigstore's production-level services.

    `offline` controls the Trusted Root refresh behavior: if `True`,
    the verifier uses the Trusted Root in the local TUF cache. If `False`,
    a TUF repository refresh is attempted.
    """
    return cls(
        rekor=RekorClient.production(),
        trusted_root=TrustedRoot.production(offline=offline),
    )

staging(*, offline=False) classmethod

Return a Verifier instance configured against Sigstore's staging-level services.

offline controls the Trusted Root refresh behavior: if True, the verifier uses the Trusted Root in the local TUF cache. If False, a TUF repository refresh is attempted.

Source code in sigstore/verify/verifier.py
103
104
105
106
107
108
109
110
111
112
113
114
115
@classmethod
def staging(cls, *, offline: bool = False) -> Verifier:
    """
    Return a `Verifier` instance configured against Sigstore's staging-level services.

    `offline` controls the Trusted Root refresh behavior: if `True`,
    the verifier uses the Trusted Root in the local TUF cache. If `False`,
    a TUF repository refresh is attempted.
    """
    return cls(
        rekor=RekorClient.staging(),
        trusted_root=TrustedRoot.staging(offline=offline),
    )

verify_dsse(bundle, policy)

Verifies an bundle's DSSE envelope, returning the encapsulated payload and its content type.

This method is only for DSSE-enveloped payloads. To verify an arbitrary input against a bundle, use the verify_artifact method.

bundle is the Sigstore Bundle to both verify and verify against.

policy is the VerificationPolicy to verify against.

Returns a tuple of (type, payload), where type is the payload's type as encoded in the DSSE envelope and payload is the raw bytes of the payload. No validation of either type or payload is performed; users of this API must assert that type is known to them before proceeding to handle payload in an application-dependent manner.

Source code in sigstore/verify/verifier.py
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
def verify_dsse(
    self, bundle: Bundle, policy: VerificationPolicy
) -> tuple[str, bytes]:
    """
    Verifies an bundle's DSSE envelope, returning the encapsulated payload
    and its content type.

    This method is only for DSSE-enveloped payloads. To verify
    an arbitrary input against a bundle, use the `verify_artifact`
    method.

    `bundle` is the Sigstore `Bundle` to both verify and verify against.

    `policy` is the `VerificationPolicy` to verify against.

    Returns a tuple of `(type, payload)`, where `type` is the payload's
    type as encoded in the DSSE envelope and `payload` is the raw `bytes`
    of the payload. No validation of either `type` or `payload` is
    performed; users of this API **must** assert that `type` is known
    to them before proceeding to handle `payload` in an application-dependent
    manner.
    """

    # (1) through (6) are performed by `_verify_common_signing_cert`.
    self._verify_common_signing_cert(bundle, policy)

    # (7): verify the bundle's signature and DSSE envelope against the
    #      signing certificate's public key.
    envelope = bundle._dsse_envelope
    if envelope is None:
        raise VerificationError(
            "cannot perform DSSE verification on a bundle without a DSSE envelope"
        )

    signing_key = bundle.signing_certificate.public_key()
    signing_key = cast(ec.EllipticCurvePublicKey, signing_key)
    dsse._verify(signing_key, envelope)

    # (8): verify the consistency of the log entry's body against
    #      the other bundle materials.
    # NOTE: This is very slightly weaker than the consistency check
    # for hashedrekord entries, due to how inclusion is recorded for DSSE:
    # the included entry for DSSE includes an envelope hash that we
    # *cannot* verify, since the envelope is uncanonicalized JSON.
    # Instead, we manually pick apart the entry body below and verify
    # the parts we can (namely the payload hash and signature list).
    entry = bundle.log_entry
    try:
        entry_body = rekor_types.Dsse.model_validate_json(
            base64.b64decode(entry.body)
        )
    except ValidationError as exc:
        raise VerificationError(f"invalid DSSE log entry: {exc}")

    payload_hash = sha256_digest(envelope._inner.payload).digest.hex()
    if (
        entry_body.spec.root.payload_hash.algorithm  # type: ignore[union-attr]
        != rekor_types.dsse.Algorithm.SHA256
    ):
        raise VerificationError("expected SHA256 payload hash in DSSE log entry")
    if payload_hash != entry_body.spec.root.payload_hash.value:  # type: ignore[union-attr]
        raise VerificationError("log entry payload hash does not match bundle")

    # NOTE: Like `dsse._verify`: multiple signatures would be frivolous here,
    # but we handle them just in case the signer has somehow produced multiple
    # signatures for their envelope with the same signing key.
    signatures = [
        rekor_types.dsse.Signature(
            signature=base64.b64encode(signature.sig).decode(),
            verifier=base64_encode_pem_cert(bundle.signing_certificate),
        )
        for signature in envelope._inner.signatures
    ]
    if signatures != entry_body.spec.root.signatures:
        raise VerificationError("log entry signatures do not match bundle")

    return (envelope._inner.payload_type, envelope._inner.payload)

verify_artifact(input_, bundle, policy)

Public API for verifying.

input_ is the input to verify, either as a buffer of contents or as a prehashed Hashed object.

bundle is the Sigstore Bundle to verify against.

policy is the VerificationPolicy to verify against.

On failure, this method raises VerificationError.

Source code in sigstore/verify/verifier.py
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
def verify_artifact(
    self,
    input_: bytes | Hashed,
    bundle: Bundle,
    policy: VerificationPolicy,
) -> None:
    """
    Public API for verifying.

    `input_` is the input to verify, either as a buffer of contents or as
    a prehashed `Hashed` object.

    `bundle` is the Sigstore `Bundle` to verify against.

    `policy` is the `VerificationPolicy` to verify against.

    On failure, this method raises `VerificationError`.
    """

    # (1) through (6) are performed by `_verify_common_signing_cert`.
    self._verify_common_signing_cert(bundle, policy)

    hashed_input = sha256_digest(input_)

    # (7): verify that the signature was signed by the public key in the signing certificate.
    try:
        signing_key = bundle.signing_certificate.public_key()
        signing_key = cast(ec.EllipticCurvePublicKey, signing_key)
        signing_key.verify(
            bundle._inner.message_signature.signature,
            hashed_input.digest,
            ec.ECDSA(hashed_input._as_prehashed()),
        )
    except InvalidSignature:
        raise VerificationError("Signature is invalid for input")

    _logger.debug("Successfully verified signature...")

    # (8): verify the consistency of the log entry's body against
    #      the other bundle materials (and input being verified).
    entry = bundle.log_entry

    expected_body = _hashedrekord_from_parts(
        bundle.signing_certificate,
        bundle._inner.message_signature.signature,
        hashed_input,
    )
    actual_body = rekor_types.Hashedrekord.model_validate_json(
        base64.b64decode(entry.body)
    )
    if expected_body != actual_body:
        raise VerificationError(
            "transparency log entry is inconsistent with other materials"
        )