feat: complete and harden Rekor v2 support - #176
Conversation
fd25a06 to
cfe3f68
Compare
a105bce to
f1e604c
Compare
|
Adversarial follow-up pushed: v2 write responses are now rejected unless the logged digest, signature, and verifier match the submitted request; managed-key hashedrekord verifiers must match the caller-supplied key; canonical legacy intoto support and cooperative hashing are preserved. This branch currently contains #169 and #175 so it remains mergeable before they land; merge #169 and #175 first, then this PR. |
f1e604c to
5c658c4
Compare
|
Status update: rebased onto current |
ae83a1d to
f89f3d6
Compare
2dfe9ed to
f89f3d6
Compare
tnytown
left a comment
There was a problem hiding this comment.
just a few code quality nits that caught my eye; looks like this behaves correctly
| if is_v2 { | ||
| let leaf_index = entry | ||
| .log_index | ||
| .as_u64() |
There was a problem hiding this comment.
i wonder if it's possible to deserialize and store the expected representation instead of having getters on types containing the original format. this pattern appears a lot in the codebase where we're effectively performing deserialization outside of the deserialization layer
There was a problem hiding this comment.
Agreed on deserializing into semantic types as early as possible. Commit 52026a1 now makes log indices and proof tree sizes validated non-negative types at deserialization, removing the downstream conversions. A fully protocol-specific normalized v1/v2 entry representation would require a broader public bundle-model change, so I have left that part for a focused follow-up.
Signed-off-by: Wolf Vollprecht <w.vollprecht@gmail.com>
Signed-off-by: Wolf Vollprecht <w.vollprecht@gmail.com>
Pass the caller-supplied public key into Rekor consistency checks so canonical intoto entries remain safely verifiable for managed-key bundles. Signed-off-by: Wolf Vollprecht <w.vollprecht@gmail.com>
Signed-off-by: Wolf Vollprecht <w.vollprecht@gmail.com>
Signed-off-by: Wolf Vollprecht <w.vollprecht@gmail.com>
Signed-off-by: Wolf Vollprecht <w.vollprecht@gmail.com>
tile_path left the most-significant base-1000 group unpadded, producing paths like x1/x234/067 instead of the spec-mandated x001/x234/067 for any index whose digit count is not a multiple of three. Encode via base-1000 decomposition with each group zero-padded to three digits, and cover the group-boundary shapes in unit and wire-path tests. Signed-off-by: Wolf Vollprecht <w.vollprecht@gmail.com>
The two Rekor protocols share no endpoints: v1 is a JSON REST API, v2 is a write endpoint plus C2SP tile reads with different request and response types. Model that as two structs instead of a runtime tag. RekorClient keeps its name and the v1 surface (including the cache, which only ever applied to v1 endpoints). The new RekorV2Client carries create_entry, get_checkpoint, get_tile, and get_entry_bundle, so calling an endpoint of the wrong version is now a compile error. This removes the require_version guards, the UnsupportedOperation error variant, and the builder's with_api_version. RekorApiVersion remains as signing configuration. Signed-off-by: Wolf Vollprecht <w.vollprecht@gmail.com>
Reject Rekor v2 write responses for a different submission and require logged public-key verifiers to match the caller-supplied managed key. Signed-off-by: Wolf Vollprecht <w.vollprecht@gmail.com>
Signed-off-by: Wolf Vollprecht <w.vollprecht@gmail.com>
Signed-off-by: Wolf Vollprecht <w.vollprecht@gmail.com>
Signed-off-by: Wolf Vollprecht <w.vollprecht@gmail.com>
Signed-off-by: Wolf Vollprecht <w.vollprecht@gmail.com>
52026a1 to
e40fc0a
Compare
…-path # Conflicts: # crates/sigstore-rekor/src/body.rs # crates/sigstore-verify/src/verify_impl/helpers.rs # crates/sigstore-verify/src/verify_impl/rekor.rs
Signed-off-by: Wolf Vollprecht <w.vollprecht@gmail.com>
jku
left a comment
There was a problem hiding this comment.
I think this looks really good. I can't find real problems and it contains multiple major improvements. Left one comment on the "default URLs", but I don't think that's a blocker as it matches the rest of the low level API.
Reviewing PRs this large is kind of painful even with the separate commits. If the pr-stacks worked for you maybe that would work for this sort of thing in future
| pub fn public() -> Self { | ||
| Self::new(RekorApiVersion::V2.default_url()) | ||
| } |
There was a problem hiding this comment.
I wish we didn't have these methods especially for v2 -- leaving URLs visible in the examples would make it clearer that the caller is responsible for the URL and that V2.default_url() is unlikely to point to a usable log 100% of the time.
Summary
Complete and harden Rekor v2 support across the client, signer, bundle validator, and verifier.
This deliberately removes the obsolete
dsse/0.0.2API and the lossy v2-to-v1 response conversion. Rekor v2 supports DSSE envelopes throughhashedrekord/0.0.2, and itsTransparencyLogEntryresponse is now stored directly in the bundle.Changes
Rekor client
new_v2,public_v2,staging_v2, and builder selection)TransparencyLogEntryvalues directly from v2 writesRemoved code
DsseEntryV2,DsseRequestV002, andcreate_dsse_entry_v2LogEntryV2/InclusionProofV2modelsunwrap_or_default()numeric fallbackSigning
hashedrekord/0.0.2Bundle validation and verification
hashedrekord/0.0.2as Rekor v2inclusionProof.logIndex,treeSize, androotHashfieldsDocumentation
Test-first validation
The new regressions failed before implementation:
cargo test -p sigstore-rekor --test v2_clientfailed to compile because the versioned/read APIs and typed request constructors did not exist.rekor_v2_ignores_untrusted_duplicate_inclusion_proof_fieldsfailed because structural validation trusted the duplicate proof root.rekor_v2_accepts_a_valid_log_signature_after_an_invalid_matching_signaturefailed on the first invalid matching signature instead of checking the later valid signature.Testing
All pass locally. Existing live Rust/Cosign v2 interoperability coverage remains in
.github/workflows/interop.yml.Breaking changes
This intentionally removes the never-standardized
dsse/0.0.2submission API and changescreate_entry_v2to returnTransparencyLogEntryrather than the v1LogEntrycompatibility type.Cross-implementation references
The implementation and compatibility tests are cross-checked against pinned versions of the other Sigstore clients:
sigstore-pythonRekor v2 request construction for directTransparencyLogEntryresponses and DSSE-as-hashedrekord submission.sigstore-pythonRekor v2 bundle binding for PAE digest, signature, certificate, and key-details checks.sigstore-goRekor v2 submission for algorithm-derived prehashing and direct protobuf response storage.sigstore-goRekor v2 verification for tile-log checkpoint/inclusion verification distinct from v1.Pinned upstream sigstore-python message-signature and DSSE Rekor v2 fixtures are now included with provenance under
crates/sigstore-verify/test_data/upstream/sigstore-python/; both are verified by the Rust test suite.Protobuf-spec compatibility
The non-published
sigstore-protobuf-compatcrate now also checks Rekor v2 directly againstsigstore_protobuf_specs0.5.1:HashedRekordRequestV002/CreateEntryRequestHashedRekordRequestV002/CreateEntryRequestBundleJSONEach check verifies fixture acceptance by generated pbjson types, acceptance of the Rust reserialization, structural equality after a Rust round trip, and Rust acceptance of canonical JSON emitted by the generated types. This catches misspelled/invented fields, wrong oneof shapes, incorrect bytes encoding, default-elision incompatibility, and dropped protobuf data.