Skip to content

Commit 9f646ca

Browse files
committed
docs(s024): the design checked against DSSE, RFC 8725, RFC 7638, RFC 5848, C2SP and FIPS 186-5; five G1 amendments
A DSSE-style PAE with the scheme as payload type (one key will sign two protocols); the verifier's allowed-algorithm set and the one key, one algorithm rule from RFC 8725; key ids as RFC 7638 thumbprints; checkpoints shaped like RFC 5848 signature blocks with their coverage and the boot they belong to; C2SP's unknown-key and consistency rules as the verifier's and the writer's tests. The linear chain stays, with the Merkle upgrade path recorded, and the P-384 selection stays, with FIPS 186-5's approval of EdDSA noted as OTP's limit rather than the standard's. Signed-off-by: Ayla Croft <aylacroft@proton.me>
1 parent 95b3660 commit 9f646ca

1 file changed

Lines changed: 63 additions & 0 deletions

File tree

  • slices/024-effect-catalog-authority-modes-receipts

‎slices/024-effect-catalog-authority-modes-receipts/NOTES.md‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,66 @@ row, because `receipts` is append-only and never updated (docs/05); "the tail ca
124124
"a checkpoint row names the tail"; (c) the boot receipt's page is `/receipts/boot`, not Settings (none exists);
125125
(d) the `ToolRunner` refactor lands as an executor function on `Tools.Runner` with `Effects.Runner` as the seam
126126
implementation, so the boundary table's arrow (Effects depends on Tools) holds without a cycle.
127+
128+
## Research, 2026-09-20, and the G1 amendments it produces
129+
130+
The owner asked for the design to be checked against what the field does, not against this tree's own plan.
131+
Read this date, primary sources only; each amendment names the line of the G1 plan it changes.
132+
133+
1. **DSSE (secure-systems-lab/dsse, protocol.md).** The signature is over `PAE(UTF8(PAYLOAD_TYPE),
134+
SERIALIZED_BODY)` where PAE is `"DSSEv1" SP LEN(type) SP type SP LEN(body) SP body`, so the payload type is
135+
inside the signed bytes: "two different applications could use the same encoding (e.g. JSON) but interpret the
136+
payload differently". `KEYID` is "an unauthenticated hint" that "MUST NOT be used for security decisions".
137+
**Amendment A (lines 2, 3, 4):** the signature is over `PAE("trinity/receipt/" <> scheme, canonical_body)`, the
138+
DSSE construction with the scheme string as the payload type, and the checkpoint signature over
139+
`PAE("trinity/checkpoint/" <> scheme, canonical_checkpoint)`. The reason is concrete here: SLICE.md amendment 1
140+
has one key-custody module signing both receipts and, at 061, the MCP core's envelopes with the same key, and
141+
without domain separation a signature made for one could be presented as the other. `key_id` stays inside the
142+
body as SLICE.md amendment 3 says, which is stronger than DSSE's hint; the verifier still treats it only as
143+
the registry lookup key and takes the algorithm from the registry row.
144+
2. **RFC 8725, JWT Best Current Practices, section 3.1.** "Libraries MUST enable the caller to specify a supported
145+
set of algorithms and MUST NOT use any other algorithms", and "each key MUST be used with exactly one
146+
algorithm, and this MUST be checked when the cryptographic operation is performed". **Amendment B (line 4):**
147+
the verifier takes the allowed scheme set as an argument (the three schemes, or fewer), the registry row binds
148+
one algorithm to one key id, and a receipt whose scheme names an algorithm other than its key's row is
149+
refused before any signature check, which is SLICE.md amendments 3 and 4 with the RFC's wording as the test
150+
names.
151+
3. **RFC 7638, JWK Thumbprint.** A key identifier computed by anyone from the public key: the required JWK
152+
members (`crv`, `kty`, `x`, `y` for EC; `crv`, `kty`, `x` for OKP per RFC 8037) serialised "with no whitespace
153+
... ordered lexicographically", hashed with SHA-256, base64url. **Amendment C (line 2):** `key_id` is the RFC
154+
7638 thumbprint of the public key, and the registry row carries the JWK, so a stranger recomputes the id from
155+
the key instead of trusting a label. ML-DSA-87 has no JWK form registered at this date; its id is the SHA-256
156+
of the raw public key with the same base64url encoding, and the registry row says so by a `kid_scheme` field.
157+
4. **RFC 5848, Signed Syslog Messages.** The existing standard for signing a stream of records in groups: a
158+
Signature Block carries a Reboot Session ID (RSID, "expected to strictly monotonically increase"), a Global
159+
Block Counter, the First Message Number, a Count of hashes, the hashes, and a signature over the block; blocks
160+
are emitted by count or by `sigMaxDelay`. **Amendment D (line 3):** the `receipt_checkpoints` row carries
161+
`chain_scope`, `boot_receipt_hash` (the RSID's role: which boot this checkpoint belongs to), `first_seq`,
162+
`last_seq`, `tail_hash`, `key_id`, `signature`, `at`, so a checkpoint states its coverage and a gap between
163+
checkpoints is visible as a gap, not as silence; N = 100 rows or T = 5 s stands (RFC 5848 caps a block at 99
164+
hashes because of syslog message size, a limit this table does not have).
165+
5. **C2SP `tlog-checkpoint` and `signed-note`.** "Verifiers MUST ignore signatures from unknown keys ... If no
166+
known key successfully verifies, clients MUST reject the note"; "A log MUST not sign any checkpoint which is
167+
inconsistent with any checkpoint it previously signed". **Amendment E (lines 3, 4):** the verifier's exit 5
168+
(trust not established) is exactly the first rule and is stated with it; the ChainWriter, on rehydrate,
169+
verifies the newest checkpoint against the tail before re-signing it, so it can never sign a checkpoint
170+
inconsistent with one it signed before (a test: a tail row altered on disk stops the writer from starting,
171+
with the reason).
172+
6. **Crosby and Wallach, "Efficient Data Structures for Tamper-Evident Logging" (USENIX Security 2009), and RFC
173+
9162.** A flat hash chain proves inclusion in O(n) and a history tree or Merkle tree in O(log n), with
174+
consistency proofs between two tree heads. **No amendment; the trade-off recorded:** Trinity's scopes are per
175+
session and verified wholesale, offline, by an operator holding the rows, so the linear chain with RFC
176+
5848-style checkpoints is the right size; the day a third party needs inclusion proofs without the rows (026's
177+
store-and-forward, or 061's MCP consumers), the checkpoint gains a Merkle root over the scope under a new
178+
scheme string, which this design leaves room for and does not build.
179+
7. **FIPS 186-5 (NIST, February 2023) approves EdDSA.** OpenSSL's maintainers (openssl/openssl #22105, 2023-09):
180+
Ed25519 "could be changed to approved once the corresponding self tests are implemented", which later
181+
providers carry; the 3.0.7 module the 003 image runs does not, and OTP's `pkey.c` refuses `eddsa` in the mode
182+
on its own (measured on the leg). **No amendment; a note for the register:** the mode selection (P-384 when
183+
`info_fips()` is `enabled`) is the right rule for this OTP and this provider, and it lives in one function; when
184+
OTP lifts its refusal on a provider that approves Ed25519, that function changes and nothing else, because the
185+
registry binds each key to its algorithm and old chains verify as written.
186+
187+
What the research did not change: RFC 8785 canonicalisation (the tree already uses it, `jcs` 0.2.0, the RFC
188+
8785 vector passes); one writer per scope (ADR-0013); signing every decision, effect, boot and cap receipt;
189+
denial when no approved signer exists; the registry as the only source of the algorithm.

0 commit comments

Comments
 (0)