8.0 KiB
Review of the DateKeys v0.9 design: consistency, compatibility and implementability
These checks passed, so the core design holds:
- Arithmetic. Every padding and length figure in the note recomputes exactly, and all 5 fixtures fit the length formulas. The v2 control is 103 bytes, and
reforzado(L_MAX+1)is 2^53. - Old readers. None of the 1825
inspect_differential.jsoncases produces VERSION 2 (byte 4 comes out as a7, 05, e8 or 20). Goframing.go:111and TSframing.ts:47both reject VERSION 2 at step 2 with no network request. - Precedence. Every existing §69.1 example stays true, and so do the six new rows.
- Format-1 codes. No invalid format-1 input changes its code except those with VERSION 2. A control v1 with keys 6/7 gives
ERR_NON_CANONICAL_CBORin both readers. A control v2 in format 1 givesERR_UNSUPPORTED_VERSIONin both.
Corrections, most serious first:
-
HIGH: relabel cases that offer a
.dkkfail at step 9.a, not at 12 or 14. Every writer-made.dkkhas acapsule_digest, and editing byte 4 breaks it.- Evidence:
encrypt.go:232always setsVerification{CapsuleDigest}.time_and_key_portable.dkk.jsonhascapsule_digest 2e97….- The existing mutation "capsule_digest of the .dkk does not match" (one byte edit) gives
ERR_ACCESS_INVALID, step 9,network:false. - The corpus reader gets a seekable file, so the digest is checked (
testdata/README.md:212-214).
- Wrong as written:
- §11 row "format 1 time_and_key relabeled format 2 | time_and_key_portable.dkc + .dkk → ERR_POLICY_STRUCTURE_MISMATCH 12".
- §11 row "format 2 time_and_key relabeled format 1 | … + .dkk → ERR_UNSUPPORTED_VERSION 14".
- Unconditional claims that need qualifying: the time_and_key rows of the §1.3 table, the §26 addition ("Antes falla en el paso 12 o en el 14"), the last paragraph of §70, and §76 case 4 ("falla en el paso 14 en los dos lectores").
- Fix: build these cases with
identities(the rawaccess_material), not the.dkk. In §64 write "…con una identity". Add to §26 and §70: "o en el paso 9.a, conERR_ACCESS_INVALID, si se ofrece una.dkkconcapsule_digesty se comprueba (§43, §69.1 Alcance)".
- Evidence:
-
MEDIUM: "En ningún caso entrega el contenido con su relleno" (§70) and "No path outputs padding as content" (§1.3) claim too much.
- For a format-2
time_onlycapsule, anyone can do this after the release:- Open the control with
FK_TIMEand readI_PAYLOAD. - Set VERSION to 1.
- Seal a v1 control with the same
I_PAYLOADand a recomputedheader_binding(§36.1).
- Open the control with
- Both readers then open it as format 1 and output content‖zeros.
- Fix: limit the claim to "una cápsula de formato 2 cuyo control no se ha vuelto a sellar", and cite §36.1 and §55.1.
- For a format-2
-
MEDIUM: new normative rules outside D1–D7 that §12 does not list for the author's confirmation.
L_MAX = 2^53 − 2^46. It is a new writer MUST NOT and a new reader rejection (ERR_NON_CANONICAL_CBOR). D2 only said "up to the format limits", and todayPAYLOAD_AGEhas no bound.- The MUST to shuffle the 16 stanzas with a CSPRNG. It is needed for D1: the portable-key holder is last in
encrypt.go:294and would learn the exact count. The text should also require an unbiased shuffle. - "Control schema version MUST equal the format". This ties every future format to a new control version. Better as a format → version table. Also state that a new padding code needs a new format, so old readers still fail at step 2.
- The §72 clause "salvo que su registro diga otra cosa". It adds a registry declaration that is not in §72's MUST-declare list: either add it to the list or drop the clause.
-
MEDIUM: writer implementability — L must be known up front, and a streaming writer cannot take back what it wrote.
- Go:
Encrypt(dst, src io.Reader, …)streams a source of unknown length (encrypt.go:218), after writing PRELUDE, header and control todst(207-211). - TS: the phase 3 plan (decision 2) accepts a
ReadableStream, whose length is unknown. - Both APIs need an explicit length input, or must spool the source to a temporary file first. §61.1 and §62.1 rule 6 should say a writer MAY spool and MUST NOT guess L.
- "abortar y descartar lo escrito" (§61 step 13, rule 9) is something a streaming writer cannot do. Rephrase as: the writer MUST report the error, and whoever receives the output MUST discard it, as with the capsule-rejected case of §56.
- Go:
-
LOW-MEDIUM: nothing says a reader must not reserve memory from L. A control may declare L up to 8.9·10^15. Add to §57 and §63 step 16: "L no es una longitud de trama: un lector no reserva memoria según L; la acota el ciphertext". The TS reader already bounds its buffer by the ciphertext length (
open.ts:422); the text should keep that. -
LOW-MEDIUM: §69.1 paragraph L2106 (cross-object checks) is not updated. The design moves one cross-object check (control version against PRELUDE VERSION) into layer 2, but L2106 still lists every cross-object check as belonging to its own step. Add "y la versión de
CONTROL_CBORfrente al formato (paso 14, capa 2)" to that list. -
LOW: a proposed
cbor.jsonvector cannot test what it claims. "padding 3 and an unknown critical extension (layer 3 before layer 4)" does not work there: schema vectors never check critical extensions (testdata/README.md:115-118, 131-133). Keep it only as a mutation or aTestPrecedenceAcrossStepscase at step 14. -
LOW: the informative note of §62.1 is wrong. It says "Con C = 91 y k = 1 o 3, las fórmulas dan las longitudes de los cinco fixtures". But
time_only_extensionshas C = 127 (482 = 335 + 4 + 127 + 16), and three of the fixtures aretime_only, with no k. Fix: "C = 91 (127 en time_only_extensions); k = 1 o 3 en los dos time_and_key". -
LOW: the floating-point log2 example is outside the valid range. 2^53 − 1 is above L_MAX. Use L = 2^49 − 1, the first failing case in range:
Math.log2gives 49 and the correct value is 48 (checked in Node). -
LOW: "bloque256 añade menos de 256 bytes" is false for L = 0, which adds 256. Write "como mucho 256".
-
LOW: new §39 says "En formato 1, INNER_ACCESS_AGE MAY contener uno o más stanzas". That weakens the MUST (≥ 1) of §33 and §36. Write "contiene (MUST, §33) uno o más".
-
LOW: §74 contradicts itself and the README.
- The design declares the encoding of L and the 16 slots "no provisionales". §74 still lists "schema CBOR final byte-a-byte de CONTROL_CBOR" as open, and spec/README says v0.9 is "not frozen".
- The out-of-scope list sits under "Aspectos todavía provisionales". Put it in its own "Trabajo futuro" paragraph.
-
LOW: forward references. §29.1 cites
testdata/vectors/padding.json, which does not exist. §76 cites "TODO" tests. Every existing §76 entry cites only tests that exist. Mark these "a generar con la implementación de v0.9", or keep the test list out of §76 until they exist. -
LOW: wording in §55.2.
- The ".dkk lleva en claro …" line leaves out the credential itself (
access_material). - "Credencial" in §39 means a recipient (a public key). In §63 step 9 it means an identity or
.dkkthe reader offers. Define the term once.
- The ".dkk lleva en claro …" line leaves out the credential itself (
-
INFO: effects on the TS phase 3 plan (
App/docs/PLAN_fase3_escritura.md).- Decision 6 keeps
R_ACCESSlast; it must become the shuffle. - The test "1 024 recipients + portable" becomes a rejection above 16.
- Decision 8 ("no vuelve a descifrar") and plan §6, which wipes
I_PAYLOADright after the real seal, conflict with the new SHOULD self-check thatI_PAYLOADopens thePAYLOAD_AGEheader. Wipe it after that check.
- Decision 6 keeps
-
INFO: test data.
- The
cbor.jsonvector "unknown key 6" (control, key 6 = uint 0) has a misleading name once key 6 exists; rename it. - Go's
Opennever records a step 17 stage (open.go:246goes to261), so fixturestageslack step 17. The new step 17 checks make that entry worth adding for §67.
- The