Skip to content

Commit baf3a6b

Browse files
authored
Backport verified time changes (#1492)
1 parent dd952eb commit baf3a6b

File tree

3 files changed

+56
-25
lines changed

3 files changed

+56
-25
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ All versions prior to 0.9.0 are untracked.
88

99
## [Unreleased]
1010

11+
### Fixed
12+
13+
* Fixed verified time handling so that additional timestamps cannot break
14+
otherwise valid signature bundles ([#1492](https://github.com/sigstore/sigstore-python/pull/1492))
15+
1116
## [3.6.4]
1217

1318
### Fixed

sigstore/verify/verifier.py

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@
5959
# From https://github.com/sigstore/sigstore-go/blob/e92142f0734064ebf6001f188b7330a1212245fe/pkg/verify/tsa.go#L29
6060
MAX_ALLOWED_TIMESTAMP: int = 32
6161

62-
# When verifying a timestamp, this threshold represents the minimum number of required
63-
# timestamps to consider a signature valid.
64-
VERIFY_TIMESTAMP_THRESHOLD: int = 1
62+
# When verifying an entry, this threshold represents the minimum number of required
63+
# verified times to consider a signature valid.
64+
VERIFIED_TIME_THRESHOLD: int = 1
6565

6666

6767
class Verifier:
@@ -226,13 +226,6 @@ def _establish_time(self, bundle: Bundle) -> list[TimestampVerificationResult]:
226226
raise VerificationError(msg)
227227

228228
timestamp_from_tsa = self._verify_timestamp_authority(bundle)
229-
if len(timestamp_from_tsa) < VERIFY_TIMESTAMP_THRESHOLD:
230-
msg = (
231-
f"not enough timestamps validated to meet the validation "
232-
f"threshold ({len(timestamp_from_tsa)}/{VERIFY_TIMESTAMP_THRESHOLD})"
233-
)
234-
raise VerificationError(msg)
235-
236229
verified_timestamps.extend(timestamp_from_tsa)
237230

238231
# If a timestamp from the Transparency Service is available, the Verifier MUST
@@ -331,13 +324,12 @@ def _verify_common_signing_cert(
331324
store.add_cert(parent_cert_ossl)
332325

333326
# (0): Establishing a Time for the Signature
334-
# First, establish a time for the signature. This timestamp is required to
327+
# First, establish verified times for the signature. This is required to
335328
# validate the certificate chain, so this step comes first.
336-
# While this step is optional and only performed if timestamp data has been
337-
# provided within the bundle, providing a signed timestamp without a TSA to
338-
# verify it result in a VerificationError.
329+
# These include TSA timestamps and (in the case of rekor v1 entries)
330+
# rekor log integrated time.
339331
verified_timestamps = self._establish_time(bundle)
340-
if not verified_timestamps:
332+
if len(verified_timestamps) < VERIFIED_TIME_THRESHOLD:
341333
raise VerificationError("not enough sources of verified time")
342334

343335
# (1): verify that the signing certificate is signed by the root

test/unit/verify/test_verifier.py

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,11 @@ def verifier(self, asset) -> Verifier:
205205
verifier._trusted_root._inner.timestamp_authorities = [authority._inner]
206206
return verifier
207207

208-
def test_verifier_verify_timestamp(self, verifier, asset, null_policy):
208+
def test_verifier_verify_timestamp(self, verifier, asset, null_policy, monkeypatch):
209+
# asset is a rekor v1 bundle: set threshold to 2 so both integrated time and the
210+
# TSA timestamp are required
211+
monkeypatch.setattr("sigstore.verify.verifier.VERIFIED_TIME_THRESHOLD", 2)
212+
209213
verifier.verify_artifact(
210214
asset("tsa/bundle.txt").read_bytes(),
211215
Bundle.from_json(asset("tsa/bundle.txt.sigstore").read_bytes()),
@@ -241,13 +245,21 @@ def test_verifier_duplicate_timestamp(self, verifier, asset, null_policy):
241245
null_policy,
242246
)
243247

244-
def test_verifier_no_validity(self, caplog, verifier, asset, null_policy):
248+
def test_verifier_no_validity(
249+
self, caplog, verifier, asset, null_policy, monkeypatch
250+
):
251+
# asset is a rekor v1 bundle: set threshold to 2 so both integrated time and the
252+
# TSA timestamp are required
253+
monkeypatch.setattr("sigstore.verify.verifier.VERIFIED_TIME_THRESHOLD", 2)
254+
245255
verifier._trusted_root.get_timestamp_authorities()[
246256
0
247257
]._inner.valid_for.end = None
248258

249259
with caplog.at_level(logging.DEBUG, logger="sigstore.verify.verifier"):
250-
with pytest.raises(VerificationError, match="not enough timestamps"):
260+
with pytest.raises(
261+
VerificationError, match="not enough sources of verified time"
262+
):
251263
verifier.verify_artifact(
252264
asset("tsa/bundle.txt").read_bytes(),
253265
Bundle.from_json(asset("tsa/bundle.txt.sigstore").read_bytes()),
@@ -260,15 +272,21 @@ def test_verifier_no_validity(self, caplog, verifier, asset, null_policy):
260272
)
261273

262274
def test_verifier_outside_validity_range(
263-
self, caplog, verifier, asset, null_policy
275+
self, caplog, verifier, asset, null_policy, monkeypatch
264276
):
277+
# asset is a rekor v1 bundle: set threshold to 2 so both integrated time and the
278+
# TSA timestamp are required
279+
monkeypatch.setattr("sigstore.verify.verifier.VERIFIED_TIME_THRESHOLD", 2)
280+
265281
# Set a date before the timestamp range
266282
verifier._trusted_root.get_timestamp_authorities()[
267283
0
268284
]._inner.valid_for.end = datetime(2024, 10, 31, tzinfo=timezone.utc)
269285

270286
with caplog.at_level(logging.DEBUG, logger="sigstore.verify.verifier"):
271-
with pytest.raises(VerificationError, match="not enough timestamps"):
287+
with pytest.raises(
288+
VerificationError, match="not enough sources of verified time"
289+
):
272290
verifier.verify_artifact(
273291
asset("tsa/bundle.txt").read_bytes(),
274292
Bundle.from_json(asset("tsa/bundle.txt.sigstore").read_bytes()),
@@ -283,13 +301,19 @@ def test_verifier_outside_validity_range(
283301
def test_verifier_rfc3161_error(
284302
self, verifier, asset, null_policy, caplog, monkeypatch
285303
):
304+
# asset is a rekor v1 bundle: set threshold to 2 so both integrated time and the
305+
# TSA timestamp are required
306+
monkeypatch.setattr("sigstore.verify.verifier.VERIFIED_TIME_THRESHOLD", 2)
307+
286308
def verify_function(*args):
287309
raise rfc3161_client.VerificationError()
288310

289311
monkeypatch.setattr(rfc3161_client.verify._Verifier, "verify", verify_function)
290312

291313
with caplog.at_level(logging.DEBUG, logger="sigstore.verify.verifier"):
292-
with pytest.raises(VerificationError, match="not enough timestamps"):
314+
with pytest.raises(
315+
VerificationError, match="not enough sources of verified time"
316+
):
293317
verifier.verify_artifact(
294318
asset("tsa/bundle.txt").read_bytes(),
295319
Bundle.from_json(asset("tsa/bundle.txt.sigstore").read_bytes()),
@@ -309,15 +333,21 @@ def test_verifier_no_authorities(self, asset, null_policy):
309333
null_policy,
310334
)
311335

312-
def test_late_timestamp(self, caplog, verifier, asset, null_policy):
336+
def test_late_timestamp(self, caplog, verifier, asset, null_policy, monkeypatch):
313337
"""
314338
Ensures that verifying the signing certificate fails because the timestamp
315339
is outside the certificate's validity window. The sample bundle
316340
"tsa/bundle.txt.late_timestamp.sigstore" was generated by adding `time.sleep(12*60)`
317341
into `sigstore.sign.Signer._finalize_sign()`, just after the entry is posted to Rekor
318342
but before the timestamp is requested.
319343
"""
320-
with pytest.raises(VerificationError, match="not enough timestamps"):
344+
# asset is a rekor v1 bundle: set threshold to 2 so both integrated time and the
345+
# TSA timestamp are required
346+
monkeypatch.setattr("sigstore.verify.verifier.VERIFIED_TIME_THRESHOLD", 2)
347+
348+
with pytest.raises(
349+
VerificationError, match="not enough sources of verified time"
350+
):
321351
verifier.verify_artifact(
322352
asset("tsa/bundle.txt").read_bytes(),
323353
Bundle.from_json(
@@ -334,8 +364,12 @@ def test_late_timestamp(self, caplog, verifier, asset, null_policy):
334364
def test_verifier_not_enough_timestamp(
335365
self, verifier, asset, null_policy, monkeypatch
336366
):
337-
monkeypatch.setattr("sigstore.verify.verifier.VERIFY_TIMESTAMP_THRESHOLD", 2)
338-
with pytest.raises(VerificationError, match="not enough timestamps"):
367+
# asset is a rekor v1 bundle: set threshold to 3 so integrated time and one
368+
# TSA timestamp are not enough
369+
monkeypatch.setattr("sigstore.verify.verifier.VERIFIED_TIME_THRESHOLD", 3)
370+
with pytest.raises(
371+
VerificationError, match="not enough sources of verified time"
372+
):
339373
verifier.verify_artifact(
340374
asset("tsa/bundle.txt").read_bytes(),
341375
Bundle.from_json(asset("tsa/bundle.txt.sigstore").read_bytes()),

0 commit comments

Comments
 (0)