Skip to content

Commit 8d54db8

Browse files
fix: upgrade sigstore Signer to use sigstore v4 (#532)
* fix: upgrade sigstore Signer to use `sigstore` v4 1. Manually specify `sigstore_protobuf_specs` as a dependency as `sigstore` no longer includes it. 2. Handle the sigstore_signer breaking changes based on: https://www.github.com/sigstore/sigstore-python/pull/1363 Signed-off-by: Spencer Schrock <[email protected]> * specify utf-8 encoding for signatures Rekor v2 makes use of an em dash `—` in its checkpoint format, which was causing issues when writing the JSON bundle to disk. Linux and macOS default to "utf-8" when writing text files without a provided encoding, while Windows defaults to "cp1252". RFC8259 states JSON text must be "utf-8": https://datatracker.ietf.org/doc/html/rfc8259#section-8.1 Signed-off-by: Spencer Schrock <[email protected]> --------- Signed-off-by: Spencer Schrock <[email protected]>
1 parent 5ac3666 commit 8d54db8

File tree

5 files changed

+33
-21
lines changed

5 files changed

+33
-21
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ All versions prior to 1.0.0 are untracked.
2727
- Added support trace sigstore sign and verify operations using OpenTelemetry.
2828
- cli: Added support for `--ignore_unsigned_files` option
2929
- Implemented a new, minimal container image. This variant excludes optional dependencies (like OTel and PKCS#11) to reduce footprint, focusing solely on core signing and verification mechanisms.
30+
- The library now requires at least v4.0.0 of `sigstore` due to breaking changes in that library
3031

3132
## [1.0.1] - 2024-04-18
3233

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ dependencies = [
3333
"cryptography",
3434
"in-toto-attestation",
3535
"sigstore-protobuf-specs == 0.3.2",
36-
"sigstore==3.6.5",
36+
"sigstore>=4.0",
3737
"typing_extensions",
3838
]
3939
requires-python = ">=3.9"

src/model_signing/_signing/sign_sigstore.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ def __init__(self, bundle: sigstore_models.Bundle):
5151

5252
@override
5353
def write(self, path: pathlib.Path) -> None:
54-
path.write_text(self.bundle.to_json())
54+
path.write_text(self.bundle.to_json(), encoding="utf-8")
5555

5656
@classmethod
5757
@override
5858
def read(cls, path: pathlib.Path) -> Self:
59-
content = path.read_text()
59+
content = path.read_text(encoding="utf-8")
6060
return cls(sigstore_models.Bundle.from_json(content))
6161

6262

@@ -109,15 +109,17 @@ def __init__(
109109
secret.
110110
"""
111111
if use_staging:
112-
self._signing_context = sigstore_signer.SigningContext.staging()
113-
self._issuer = sigstore_oidc.Issuer.staging()
112+
trust_config = sigstore_models.ClientTrustConfig.staging()
114113
else:
115-
self._signing_context = sigstore_signer.SigningContext.production()
116-
if oidc_issuer is not None:
117-
self._issuer = sigstore_oidc.Issuer(oidc_issuer)
118-
else:
119-
self._issuer = sigstore_oidc.Issuer.production()
114+
trust_config = sigstore_models.ClientTrustConfig.production()
120115

116+
if not oidc_issuer:
117+
oidc_issuer = trust_config.signing_config.get_oidc_url()
118+
119+
self._issuer = sigstore_oidc.Issuer(oidc_issuer)
120+
self._signing_context = (
121+
sigstore_signer.SigningContext.from_trust_config(trust_config)
122+
)
121123
self._use_ambient_credentials = use_ambient_credentials
122124
self._identity_token = identity_token
123125
self._force_oob = force_oob

src/model_signing/_signing/sign_sigstore_pb.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,12 @@ def __init__(self, bundle: bundle_pb.Bundle):
105105

106106
@override
107107
def write(self, path: pathlib.Path) -> None:
108-
path.write_text(self.bundle.to_json())
108+
path.write_text(self.bundle.to_json(), encoding="utf-8")
109109

110110
@classmethod
111111
@override
112112
def read(cls, path: pathlib.Path) -> Self:
113-
content = path.read_text()
113+
content = path.read_text(encoding="utf-8")
114114
parsed_dict = json.loads(content)
115115
return cls(bundle_pb.Bundle().from_dict(parsed_dict))
116116

tests/_signing/sigstore_test.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,25 @@ def mocked_oidc_provider_no_ambient():
102102

103103
@pytest.fixture
104104
def mocked_sigstore_models():
105-
with mock.patch.object(
106-
sigstore.sigstore_models, "Bundle", autospec=True
107-
) as mocked_bundle:
105+
with mock.patch.multiple(
106+
sigstore.sigstore_models,
107+
Bundle=mock.DEFAULT,
108+
ClientTrustConfig=mock.DEFAULT,
109+
autospec=True,
110+
) as mocked_objects:
111+
mocked_bundle = mocked_objects["Bundle"]
108112
mocked_bundle.from_json = MockedSigstoreBundle.from_json
109-
yield mocked_bundle
113+
114+
mocked_config = mock.MagicMock()
115+
mocked_client_trust_config = mocked_objects["ClientTrustConfig"]
116+
mocked_client_trust_config.production.return_value = mocked_config
117+
mocked_client_trust_config.staging.return_value = mocked_config
118+
119+
yield mocked_objects
110120

111121

112122
@pytest.fixture
113-
def mocked_sigstore_signer():
123+
def mocked_sigstore_signer(mocked_sigstore_models):
114124
with mock.patch.multiple(
115125
sigstore.sigstore_signer,
116126
Signer=mock.DEFAULT,
@@ -128,8 +138,7 @@ def mocked_sigstore_signer():
128138
mocked_context.signer.return_value = signer
129139

130140
mocked_signing_context = mocked_objects["SigningContext"]
131-
mocked_signing_context.staging.return_value = mocked_context
132-
mocked_signing_context.production.return_value = mocked_context
141+
mocked_signing_context.from_trust_config.return_value = mocked_context
133142

134143
yield mocked_objects
135144

@@ -316,11 +325,11 @@ def test_verify_not_intoto_statement(
316325
signature_path = tmp_path / "model.sig"
317326
self._sign_manifest(manifest, signature_path, sigstore.Signer)
318327

319-
correct_signature = signature_path.read_text()
328+
correct_signature = signature_path.read_text(encoding="utf-8")
320329
json_signature = json.loads(correct_signature)
321330
json_signature["_type"] = "Not in-toto"
322331
invalid_signature = json.dumps(json_signature)
323-
signature_path.write_text(invalid_signature)
332+
signature_path.write_text(invalid_signature, encoding="utf-8")
324333

325334
with pytest.raises(ValueError, match="Expected in-toto .* payload"):
326335
self._verify_dsse_signature(signature_path)

0 commit comments

Comments
 (0)