Skip to content

Commit 7f53061

Browse files
cronokirbyclaude
andcommitted
[cryptography/stream] Version SAKE and bind identities before signing
Add sake::Version and cups::Version. V1 commits both static identities before every handshake signature, so the Syn signature covers the dialer identity, and uses injective transcript::Version::V1 framing. V0 keeps the existing transcript byte-for-byte. Both sake::Context::new and cups::Handshake::new require the version: it is not negotiated, so every caller chooses it explicitly, and library code never picks one on behalf of an operator. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1 parent ff558dc commit 7f53061

35 files changed

Lines changed: 609 additions & 211 deletions

File tree

RELEASE_NOTES.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ To retain a path back to v2026.7.1:
1616
before the upgraded binary first opens storage. The previous binary cannot
1717
read V1 blobs, and this release has no V1-to-V0 downgrade tool. Narrowing the
1818
range after V1 blobs exist rejects them instead of converting them ([#4595]).
19+
- **V1 handshakes:** Pass `cups::Version::V0` to `cups::Handshake::new` on
20+
every peer. A V1 peer cannot complete a handshake with a V0 peer, and the
21+
version is not negotiated.
1922
- **Page-size changes:** Keep each existing store's logical page size.
2023
Replacing a logical size of 4096 with `buffer::paged::page_size(4096)` changes
2124
it to 4084 and can truncate existing data. Switching the setting back cannot
@@ -381,6 +384,19 @@ are the same types and separate trait impls for both now conflict ([#4318]). The
381384
`Blocker` trait gained a required `blocked()` method returning a latest-wins
382385
subscription to the set of peers the network currently blocks ([#4645]).
383386

387+
SAKE handshakes are versioned. `handshake::sake::Context::new` and
388+
`cups::Handshake::new` take a mandatory version, and `cups::Handshake` gains a
389+
public `version` field. V1 commits both static identities before every signature, so the
390+
first handshake message's signature covers the dialer identity, and uses the
391+
injective `transcript::Version::V1` framing. Under V0 that signature covers only
392+
the timestamp, listener identity, and ephemeral key, which lets a signature
393+
scheme that allows selecting a public key for an existing signature pass the
394+
listener's first check under an identity the sender does not own; the exchange
395+
still cannot complete under a mismatched identity. Message encodings are
396+
unchanged, but peers must agree on a version: a mismatch fails with
397+
`HandshakeFailed`. Upgrade a running network in two steps: first deploy with
398+
`V0` everywhere, then switch every peer to `V1` together.
399+
384400
### Resolver
385401

386402
Resolver consumers report a typed `Outcome` instead of a bool ([#4383],

cryptography/conformance.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,14 @@ hash = "9010ca9b2849258179377f855fb77967ff003c8f349258d868bab76b0bfa1c5a"
150150
n_cases = 65536
151151
hash = "0cbb8c8644dffe0be78d1307fb5034dd804921d66799c1aaec3d335b44e9616c"
152152

153-
["commonware_cryptography::handshake::sake::conformance::Sake"]
153+
["commonware_cryptography::handshake::sake::conformance::SakeV0"]
154154
n_cases = 4096
155155
hash = "057cc42a74390f0b43250b8fba2c42abad651bee6beacb510f2f2a950185a694"
156156

157+
["commonware_cryptography::handshake::sake::conformance::SakeV1"]
158+
n_cases = 4096
159+
hash = "564d2e1cdc69b2047bd75ed78ae89402371db5ea8d8abd9f647e4b134fcf98bf"
160+
157161
["commonware_cryptography::handshake::sake::key_exchange::conformance::CodecConformance<EphemeralPublicKey>"]
158162
n_cases = 65536
159163
hash = "c0501d4a691d1fccec7c5906e8608228569d24164150edd215838593e3b77512"

0 commit comments

Comments
 (0)