Skip to content

Commit

Permalink
fix: require an inclusion promise when log integration time is used (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
woodruffw authored Dec 10, 2024
1 parent 9ee7ac2 commit 300b502
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 4 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ All versions prior to 0.9.0 are untracked.
verifying legacy bundles was never shown
([#1198](https://github.com/sigstore/sigstore-python/pull/1198))

* Strengthened the requirement that an inclusion promise is present
*if* no other source of signed time is present
([#1247](https://github.com/sigstore/sigstore-python/pull/1247))

## [3.5.3]

### Fixed
Expand Down
15 changes: 13 additions & 2 deletions sigstore/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -525,8 +525,11 @@ def _verify(self) -> None:
# * For 0.2+, an inclusion proof is required; the client MUST
# verify the inclusion proof. The inclusion prof MUST contain
# a checkpoint.
# The inclusion promise is NOT required; if present, the client
# SHOULD verify it.
#
# The inclusion promise is NOT required if another source of signed
# time (such as a signed timestamp) is present. If no other source
# of signed time is present, then the inclusion promise MUST be
# present.
#
# Before all of this, we require that the inclusion proof be present
# (when constructing the LogEntry).
Expand All @@ -543,6 +546,14 @@ def _verify(self) -> None:
if not log_entry.inclusion_proof.checkpoint:
raise InvalidBundle("expected checkpoint in inclusion proof")

if (
not log_entry.inclusion_promise
and not self._inner.verification_material.timestamp_verification_data.rfc3161_timestamps
):
raise InvalidBundle(
"bundle must contain an inclusion promise or signed timestamp(s)"
)

self._log_entry = log_entry

@property
Expand Down
7 changes: 6 additions & 1 deletion sigstore/verify/verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,12 @@ def _establish_time(self, bundle: Bundle) -> List[TimestampVerificationResult]:

# If a timestamp from the Transparency Service is available, the Verifier MUST
# perform path validation using the timestamp from the Transparency Service.
if timestamp := bundle.log_entry.integrated_time:
# NOTE: We only include this timestamp if it's accompanied by an inclusion
# promise that cryptographically binds it. We verify the inclusion promise
# itself later, as part of log entry verification.
if (
timestamp := bundle.log_entry.integrated_time
) and bundle.log_entry.inclusion_promise:
verified_timestamps.append(
TimestampVerificationResult(
source=TimestampSource.TRANSPARENCY_SERVICE,
Expand Down
63 changes: 62 additions & 1 deletion test/assets/bundle_v3_github.whl.sigstore

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions test/assets/bundle_v3_no_signed_time.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
DO NOT MODIFY ME!

this is the input for bundle_v3_no_signed_time, which ensures clients reject
bundles that don't have a source of signed time.

DO NOT MODIFY ME!
50 changes: 50 additions & 0 deletions test/assets/bundle_v3_no_signed_time.txt.sigstore.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json",
"verificationMaterial": {
"certificate": {
"rawBytes": "MIIC1DCCAlugAwIBAgIUXgKINnY7rbT5gHmj9yeiZXGg3rkwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjQxMjEwMjE0MTI1WhcNMjQxMjEwMjE1MTI1WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4ul4I08UFGizCla6qRUGFiwEPNsFRnvBPDvQ4ViJ+Q83HOlYWWxCAjoJpGd9FWtyxTPKDsG0n4t6Mr+jSwz22KOCAXowggF2MA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUZ7cNLqQlnKAXnf6jmb9cv70dppgwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wIwYDVR0RAQH/BBkwF4EVd2lsbGlhbUB5b3NzYXJpYW4ubmV0MCwGCisGAQQBg78wAQEEHmh0dHBzOi8vZ2l0aHViLmNvbS9sb2dpbi9vYXV0aDAuBgorBgEEAYO/MAEIBCAMHmh0dHBzOi8vZ2l0aHViLmNvbS9sb2dpbi9vYXV0aDCBiwYKKwYBBAHWeQIEAgR9BHsAeQB3AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6OAAABk7KFEeIAAAQDAEgwRgIhAOeS6rR2aksHhN9Rxbx+ANuAlXhP4vTPKMLBHd6JAm4lAiEAx+/kzKJ2SxSCAYm582jKeAa1LCVmUaO85FO2WTV7MYEwCgYIKoZIzj0EAwMDZwAwZAIwDXrVAPgutWZWPfE3QWy/4gG/PbMbYUfqNsEpQEeMm8GeraZN3zffzw16FFhWsMbXAjApxDNgKvmztHOKStyvmOXPiJCixzx/gLFbhVn7Q+qY6vjC83B0XgPsyQ2T0i8Ldzg="
},
"tlogEntries": [
{
"logIndex": "154562758",
"logId": {
"keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="
},
"kindVersion": {
"kind": "hashedrekord",
"version": "0.0.1"
},
"integratedTime": "1733866885",
"inclusionProof": {
"logIndex": "32658496",
"rootHash": "IbC2+n9aYhFlm5nFwkp+j7/Hc9XuYWxyE5OlXIoIijY=",
"treeSize": "32658497",
"hashes": [
"CVvwGSdkZ5FUDnltf3Me3nXyco4G9mwTsYbIxz0RS+U=",
"DJrEpKAKhEPhZ5aKvlaRImFebTv5tc17rsfOkhSS6fY=",
"tsYfO+hUsl4KKY+qsPx/k4NzOzE5zWRsc4Ufgn4oh/U=",
"ZjSpDQt5kIQfJd6B/BDNWLRhYOGwnlxE6pT4JJaiD5s=",
"OMoiMVnwD3sG6Cc6HCg+ySmqBAH1nn0mA5+tjFxiyeg=",
"gSWKL2k1ZGZm45C8hSdNwWan8qOrszl5X7Ws56h+FVM=",
"R7hO1X+KgSw8Oojd8i2+G3BzBYztkRBE6LpYSXPg33U=",
"oOecFfN3YqDOkbijS/ej1WF5Da/Gt/AZNhbwE9uoOE8=",
"4lUF0YOu9XkIDXKXA0wMSzd6VeDY3TZAgmoOeWmS2+Y=",
"gf+9m552B3PnkWnO0o4KdVvjcT3WVHLrCbf1DoVYKFw="
],
"checkpoint": {
"envelope": "rekor.sigstore.dev - 1193050959916656506\n32658497\nIbC2+n9aYhFlm5nFwkp+j7/Hc9XuYWxyE5OlXIoIijY=\n\n\u2014 rekor.sigstore.dev wNI9ajBGAiEAgjFaCZlVvHUnDgxLf+4XjN6ahWNkkKh9QFTOqHBpyw4CIQDmy4JQs+2BKtvheo/HQogyhh5EYGYZeBDdRvyyX1fg+w==\n"
}
},
"canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYTZkZTk5YTExZDNkMzgwNTZkODM4YzdkYzlhMjNhMTFhMGM4MWJjYWNlMGQxMWVhYTMwMWEyZmZiNDgyYzQyIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUUMzc2pYZVZoTHRqbE13dG0yRE5CYVdVaFBWOVJ1U1dsWW1EcHQzRzFQVW5RSWhBUElxRHUwTVkza1FtelE2QmswS2VSTW5mQ3Y0VVdEVU5jclRnN0cyYjdzTCIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTXhSRU5EUVd4MVowRjNTVUpCWjBsVldHZExTVTV1V1RkeVlsUTFaMGh0YWpsNVpXbGFXRWRuTTNKcmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFJlRTFxUlhkTmFrVXdUVlJKTVZkb1kwNU5hbEY0VFdwRmQwMXFSVEZOVkVreFYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVUwZFd3MFNUQTRWVVpIYVhwRGJHRTJjVkpWUjBacGQwVlFUbk5HVW01MlFsQkVkbEVLTkZacFNpdFJPRE5JVDJ4WlYxZDRRMEZxYjBwd1IyUTVSbGQwZVhoVVVFdEVjMGN3YmpSME5rMXlLMnBUZDNveU1rdFBRMEZZYjNkblowWXlUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZhTjJOT0NreHhVV3h1UzBGWWJtWTJhbTFpT1dOMk56QmtjSEJuZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBsM1dVUldVakJTUVZGSUwwSkNhM2RHTkVWV1pESnNjMkpIYkdoaVZVSTFZak5PZWxsWVNuQlpWelIxWW0xV01FMURkMGREYVhOSFFWRlJRZ3BuTnpoM1FWRkZSVWh0YURCa1NFSjZUMms0ZGxveWJEQmhTRlpwVEcxT2RtSlRPWE5pTW1Sd1ltazVkbGxZVmpCaFJFRjFRbWR2Y2tKblJVVkJXVTh2Q2sxQlJVbENRMEZOU0cxb01HUklRbnBQYVRoMldqSnNNR0ZJVm1sTWJVNTJZbE01YzJJeVpIQmlhVGwyV1ZoV01HRkVRMEpwZDFsTFMzZFpRa0pCU0ZjS1pWRkpSVUZuVWpsQ1NITkJaVkZDTTBGT01EbE5SM0pIZUhoRmVWbDRhMlZJU214dVRuZExhVk5zTmpRemFubDBMelJsUzJOdlFYWkxaVFpQUVVGQlFncHJOMHRHUldWSlFVRkJVVVJCUldkM1VtZEphRUZQWlZNMmNsSXlZV3R6U0doT09WSjRZbmdyUVU1MVFXeFlhRkEwZGxSUVMwMU1Ra2hrTmtwQmJUUnNDa0ZwUlVGNEt5OXJla3RLTWxONFUwTkJXVzAxT0RKcVMyVkJZVEZNUTFadFZXRlBPRFZHVHpKWFZGWTNUVmxGZDBObldVbExiMXBKZW1vd1JVRjNUVVFLV25kQmQxcEJTWGRFV0hKV1FWQm5kWFJYV2xkUVprVXpVVmQ1THpSblJ5OVFZazFpV1ZWbWNVNXpSWEJSUldWTmJUaEhaWEpoV2s0emVtWm1lbmN4TmdwR1JtaFhjMDFpV0VGcVFYQjRSRTVuUzNadGVuUklUMHRUZEhsMmJVOVlVR2xLUTJsNGVuZ3ZaMHhHWW1oV2JqZFJLM0ZaTm5acVF6Z3pRakJZWjFCekNubFJNbFF3YVRoTVpIcG5QUW90TFMwdExVVk9SQ0JEUlZKVVNVWkpRMEZVUlMwdExTMHRDZz09In19fX0="
}
],
"timestampVerificationData": {}
},
"messageSignature": {
"messageDigest": {
"algorithm": "SHA2_256",
"digest": "ym3pmhHT04BW2DjH3JojoRoMgbys4NEeqjAaL/tILEI="
},
"signature": "MEYCIQC3sjXeVhLtjlMwtm2DNBaWUhPV9RuSWlYmDpt3G1PUnQIhAPIqDu0MY3kQmzQ6Bk0KeRMnfCv4UWDUNcrTg7G2b7sL"
}
}
2 changes: 2 additions & 0 deletions test/unit/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ def signing_bundle(asset):
def _signing_bundle(name: str) -> tuple[Path, Bundle]:
file = asset(name)
bundle_path = asset(f"{name}.sigstore")
if not bundle_path.is_file():
bundle_path = asset(f"{name}.sigstore.json")
bundle = Bundle.from_json(bundle_path.read_bytes())

return (file, bundle)
Expand Down
7 changes: 7 additions & 0 deletions test/unit/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,13 @@ def test_bundle_roundtrip(self, signing_bundle):
bundle.to_json()
)

def test_bundle_missing_signed_time(self, signing_bundle):
with pytest.raises(
InvalidBundle,
match=r"bundle must contain an inclusion promise or signed timestamp\(s\)",
):
signing_bundle("bundle_v3_no_signed_time.txt")


class TestKnownBundleTypes:
def test_str(self):
Expand Down

0 comments on commit 300b502

Please sign in to comment.