From 7466e1bfb8a263f62be135ad0b0d19e29479f9ae Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Wed, 31 Jul 2024 14:38:44 -0400 Subject: [PATCH] API: make _StatementBuilder public (#1077) --- CHANGELOG.md | 8 ++++++++ sigstore/dsse.py | 28 ++++++++++++++++------------ test/unit/test_sign.py | 4 ++-- test/unit/verify/test_verifier.py | 4 ++-- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 572b8437..9cea0120 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,14 @@ All versions prior to 0.9.0 are untracked. ## [Unreleased] +### Added + +* API: `dsse.StatementBuilder` has been added. It can be used to construct an + in-toto `Statement` for subsequent enveloping and signing. + This API is public but is **not considered stable until the next major + release.** + ([#1077](https://github.com/sigstore/sigstore-python/pull/1077)) + ### Changed * API: `verify_dsse` now rejects bundles with DSSE envelopes that have more than diff --git a/sigstore/dsse.py b/sigstore/dsse.py index 123f8f97..8d76c883 100644 --- a/sigstore/dsse.py +++ b/sigstore/dsse.py @@ -89,7 +89,7 @@ class Statement: See: """ - def __init__(self, contents: bytes) -> None: + def __init__(self, contents: bytes | _Statement) -> None: """ Construct a new Statement. @@ -97,11 +97,15 @@ def __init__(self, contents: bytes) -> None: `StatementBuilder` to manually construct an in-toto statement from constituent pieces. """ - self._contents = contents - try: - self._inner = _Statement.model_validate_json(contents) - except ValidationError: - raise Error("malformed in-toto statement") + if isinstance(contents, bytes): + self._contents = contents + try: + self._inner = _Statement.model_validate_json(contents) + except ValidationError: + raise Error("malformed in-toto statement") + else: + self._contents = contents.model_dump_json(by_alias=True).encode() + self._inner = contents def _matches_digest(self, digest: Hashed) -> bool: """ @@ -130,7 +134,7 @@ def _pae(self) -> bytes: return _pae(Envelope._TYPE, self._contents) -class _StatementBuilder: +class StatementBuilder: """ A builder-style API for constructing in-toto Statements. """ @@ -142,27 +146,27 @@ def __init__( predicate: Optional[Dict[str, Any]] = None, ): """ - Create a new `_StatementBuilder`. + Create a new `StatementBuilder`. """ self._subjects = subjects or [] self._predicate_type = predicate_type self._predicate = predicate - def subjects(self, subjects: list[_Subject]) -> _StatementBuilder: + def subjects(self, subjects: list[_Subject]) -> StatementBuilder: """ Configure the subjects for this builder. """ self._subjects = subjects return self - def predicate_type(self, predicate_type: str) -> _StatementBuilder: + def predicate_type(self, predicate_type: str) -> StatementBuilder: """ Configure the predicate type for this builder. """ self._predicate_type = predicate_type return self - def predicate(self, predicate: dict[str, Any]) -> _StatementBuilder: + def predicate(self, predicate: dict[str, Any]) -> StatementBuilder: """ Configure the predicate for this builder. """ @@ -183,7 +187,7 @@ def build(self) -> Statement: except ValidationError as e: raise Error(f"invalid statement: {e}") - return Statement(stmt.model_dump_json(by_alias=True).encode()) + return Statement(stmt) class Envelope: diff --git a/test/unit/test_sign.py b/test/unit/test_sign.py index 5c2e2a49..54812fab 100644 --- a/test/unit/test_sign.py +++ b/test/unit/test_sign.py @@ -20,7 +20,7 @@ from sigstore_protobuf_specs.dev.sigstore.common.v1 import HashAlgorithm import sigstore.oidc -from sigstore.dsse import _StatementBuilder, _Subject +from sigstore.dsse import StatementBuilder, _Subject from sigstore.errors import VerificationError from sigstore.hashes import Hashed from sigstore.sign import SigningContext @@ -152,7 +152,7 @@ def test_sign_dsse(staging): ctx = sign_ctx() stmt = ( - _StatementBuilder() + StatementBuilder() .subjects( [_Subject(name="null", digest={"sha256": hashlib.sha256(b"").hexdigest()})] ) diff --git a/test/unit/verify/test_verifier.py b/test/unit/verify/test_verifier.py index 1d4b99e5..6ca44e0c 100644 --- a/test/unit/verify/test_verifier.py +++ b/test/unit/verify/test_verifier.py @@ -18,7 +18,7 @@ import pretend import pytest -from sigstore.dsse import _StatementBuilder, _Subject +from sigstore.dsse import StatementBuilder, _Subject from sigstore.errors import VerificationError from sigstore.models import Bundle from sigstore.verify import policy @@ -159,7 +159,7 @@ def test_verifier_dsse_roundtrip(staging): ctx = signer_cls() stmt = ( - _StatementBuilder() + StatementBuilder() .subjects( [_Subject(name="null", digest={"sha256": hashlib.sha256(b"").hexdigest()})] )