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.

fulcio_certificate_chain is a list of PEM-encoded X.509 certificates, establishing the trust chain for the signing certificate and signature.

Source code in sigstore/verify/verifier.py
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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.

    `fulcio_certificate_chain` is a list of PEM-encoded X.509 certificates,
    establishing the trust chain for the signing certificate and signature.
    """
    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.

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

Source code in sigstore/verify/verifier.py
100
101
102
103
104
105
106
107
108
@classmethod
def staging(cls, *, offline: bool = False) -> Verifier:
    """
    Return a `Verifier` instance configured against Sigstore's staging-level services.
    """
    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
388
389
390
391
392
393
394
395
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
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
466
467
468
469
470
471
472
473
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
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"
        )