security: a transfer's memo, ticker and nonce are in no public object (pre-audit item 4) - #1102
Merged
Merged
Conversation
…truction v2) Pre-audit item 17. Version 1 gave every leaf of a bottom tree one FORS key (the key pair word carried the FORS tree number, never the leaf), hashed WOTS+ compression under the hashtree's own addresses, shared addresses between FORS leaves and the first internal level (heights off by one) and between every PRF call and its chain, and left PK.seed out of PRF. Owner ruling 2026-10-01: "Fix structure, keep BLAKE3." Every algorithm now follows FIPS 205 with its 32-byte ADRS, seven address types and setTypeAndClear; PRF, the tweakable hash, PRF_msg and H_msg each run under their own BLAKE3 KDF context; H_msg yields FIPS m bytes; signing verifies before it returns. Tests record every hash call by address and hold the separation, the FORS-to-leaf binding and the PRF's public seed; frozen vectors for SPX128f and SPX256f. A clean cut: every key and signature changes.
…rithm id 0x0002 dsm::crypto::sphincs becomes the host's view of crates/dsm-sphincs, so the host and the anchor firmware run one construction (no second copy to drift). SPHINCS_PLUS_SPX256F is 0x0002, construction version 2; 0x0001 is retired and undeclared, so nothing under version 1 is recognized. The unused DSM/sphincs-kdf tag, the sphincs-trace feature and the duplicate KAT file go; fixtures derive their algorithm id; the keygen digests are regenerated.
The genesis parameters' independent encoding writes algorithm 0x0002; the SoFi golden digests over a setup or precommit body (the setup reference, the precommit id, and the single and multivault external commitments) follow the body's algorithm id; the altered-algorithm decode writes the retired id.
CONFORMANCE §6.70 records the ruling (owner, 2026-10-01: "Fix structure, keep BLAKE3"), the fix, the tests, the eight mutation controls and the vectors that moved, and keeps the owner's distinction: the structure restores what a SPHINCS+-style proof assumes; BLAKE3 remains a non-FIPS, custom instantiation. MR-DSM-0259 re-verified; matrix rows for the structure; SECURITY.md's limitations rewritten for version 2; the evidence index covers dsm_sphincs; the anchor crates' lockfiles take the new dependency edge (only this change's lines).
Two conflicts, the conformance record and the verification matrix: each side appended at the same place (main's §6.69 and item 11 rows, this branch's §6.70 and SPHINCS+ rows). Both kept, §6.69 before §6.70. Conformance evidence passes; the real-code guard passes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4wDTToHmJfkYYJvMKXt3u
…shed) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4wDTToHmJfkYYJvMKXt3u
…y commit) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4wDTToHmJfkYYJvMKXt3u
… (pre-audit item 4) Owner ruling 2026-10-02, one shape, BLE carries terms. The signed transfer keeps the recipient, amount, policy commit and authority policy, plus a salted commitment to its terms (token, nonce, mode, memo). The terms ride inside the sealed spool request online and beside the operation on BLE; a recipient refuses a transfer whose terms are missing or do not open the commitment. Tag 3 is retired; the transfer is tag 37. CONFORMANCE §6.71. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4wDTToHmJfkYYJvMKXt3u
The map built locally at de67d9d. Every moved pin moved for its code alone; its evidence ran on this tree and passed: dsm (all suites), dsm_storage_node (all), and the 47 dsm_sdk tests the moved pins cite. make requirement-map-intent: 635 PINNED, 0 failing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4wDTToHmJfkYYJvMKXt3u
CodeQL (rust/hard-coded-cryptographic-value) flagged two flows into the new terms constructor's nonce: the salt-freshness test's literal nonce, and the fee transfer's OS-random nonce (the zeroed buffer OsRng fills). The fee transfer's nonce was redundant: nothing spends it, and its fresh terms salt already makes each fee transfer's operation bytes its own. Both now pass an empty nonce, as the offline and generic transfers do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4wDTToHmJfkYYJvMKXt3u
Map built locally at e60df85; each moved pin's evidence ran and passed (the dsm suites and the 38 dsm_sdk tests they cite). make requirement-map-intent: 635 PINNED, 0 failing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4wDTToHmJfkYYJvMKXt3u
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Pre-audit item 4, recorded in
CONFORMANCE_GAPS.md§6.71. Owner ruling, 2026-10-02: "one shape, BLE carries terms".The finding. A transfer's signed operation carried its memo, token ticker, nonce, mode, the recipient's public key and the recipient's id as text, all in the clear. Those bytes hash into the tip, ride the economic admission into the register, and are read again by later counterparties walking a credit's source. So the memo, and a nonce predictable from public identifiers (§6.67), outlived the sealed spool in every public object that carries the operation.
What changes.
Operation::Transferis{to_device_id, amount, policy_commit, terms_commitment, signature, authority_policy}, canonical tag 37. Tag 3 is retired and never reused.recipientandtoare deleted.TransferTerms {token_id, nonce, mode, memo, salt}. The commitment isH(DSM/transfer-terms/v1; terms), under a fresh 256-bit salt; a salt under 128 bits is refused.OnlineTransferRequest.transfer_terms(field 15), inside the sealed spool payload.BilateralPrepareRequest.transfer_terms(field 19), besideoperation_data.recipient_dispatch): missing, undecodable or non-opening terms mean the transfer is not recognized and nothing is staged;step_terms): refused before any session exists, and checked again when a stored session is restored;policy_commit, and each requires the opened ticker to resolve to it. The recovery egress gate names a transfer's lock from its commit, and blocks an asset it cannot name while any lock is held.recipient_staged_transfer.terms_bytesandbilateral_sessions.terms_bytes; client schema 28.dsm_app_pb.tsregenerated.Related Issues
Security pre-audit (#1096, #1099, #1100), item 4.
Testing
cargo clippy --all-targets -- -D warnings(root workspace),cargo fmt, real-code guard andci/conformance_evidence.pyall pass.cargo test -p dsmpasses, including the newtransfer_termstests.cargo test -p dsm_vertical_validationpasses.dsm_sdklib suite passes: 1,078 of 1,079. The remaining one,pairing_smoke_identity_before_connection, fails the same way on main's binary when run alone; it depends on test order, and CI runs in that order.a_transfer_without_terms_that_open_it_is_not_recognized(online)a_bearer_prepare_without_terms_that_open_it_is_refused(BLE)no_node_holds_a_transfers_memo_ticker_or_nonce: scans every row of every node table for the memo, nonce, salt and length-prefixed tickera_transfer_settles_only_with_terms_that_open_itNotes
🤖 Generated with Claude Code
https://claude.ai/code/session_01Y4wDTToHmJfkYYJvMKXt3u
Generated by Claude Code