18 KiB
Scope and questions
Section numbers (§) refer to DateKeys_Protocol_Specification_v0.15.md, tag spec-v0.15. "Known" marks a problem the project already knows about, with its source, so that you do not spend time rediscovering it; your opinion on it is still welcome.
0. Levels of scope
- Level 1, required: the protocol, with the Go reference implementation as evidence. Questions Q1 to Q4, Q6 to Q8 and Q10 to Q12.
- Level 2, separate or optional: Q5 and Q9, the CMS/X.509/RFC 3161 reader and the rules of the locator addresses. They are parser reviews and may need another profile of reviewer.
- Not in the paid scope: the TypeScript and Dart implementations. They are available, with the same vectors, if the reviewer wants to look at them.
1. In scope
- The tlock core: the composition of tlock with age, the time-only and time-and-key nesting, the three file keys, and the stanza rules (§28 to §36, §63 steps 5 to 13).
- The root of trust, byte by byte: the profile and its pinning (§10 to §13), canonical points (§12.2), the round message and hash to G1 (§63 step 10), and H2, H3 and H4 of the IBE (§63 step 11 and the paragraphs after the flow). Vectors:
tlock_ibe.json,tlock_steps.json. - Bindings and commitments: header_binding (§26), the I_PAYLOAD binding (§29, §30.1), what the author signature covers (§29.8), the seal subject (§29.11).
- Format 3: BODY, the head and its salt, the security area and its fixed size, verdicts (§29.2 to §29.7).
- Author signature: strict Ed25519 (§29.9), the CMS/CAdES profile and its parser (§29.10), the RFC 3161 token profile (§29.11), author keys (§29.12).
- Metadata privacy: padding (§29.1), 16 slots and decoys (§39), the list of §55.2.
- Key of words: normalisation, PBKDF2 parameters, salt (§38.1).
.dkk, locator and envelope: §40 to §44.1, including the address rules.- Error precedence as a possible oracle (§63, §69.1).
- Writer rules that affect security: randomness, decoy disposal, self-checks (§62.1).
- Long-term recovery and the provider (v0.15): the provider model (§7.6, §71), the release object (§47.1), network sources and a release in hand (§49), archives and cache services (§50), step 9.c and the release checks of step 10 (§63), the writer rules 26 and 27 (§62.1), and the informative recovery annex (§79).
The reference implementation (Go), and the TypeScript and Dart implementations, are in scope as aids and as evidence: please report where an implementation disagrees with the text.
2. Out of scope
- The security of drand, BLS12-381, age, X25519, ChaCha20-Poly1305, Ed25519, ECDSA, RSA and SHA-2 themselves. DateKeys assumes them (§3, §7.6). How DateKeys uses them is in scope.
- The drand network's operation, its threshold and its key management; the protocol states the dependency (§7.6).
- Storage, discovery, distribution and delivery of
.dkcand.dkk(§6). - Legal validity of signatures and seals, and the trust in certificate chains and TSAs (§5, §29.10, §29.11).
- The Release API server, the Release Queue and a Release Cache service: §45 to §47 fix only what they serve (the release object of §47.1, which is in scope) and the HTTP form of §45 is informative; none is implemented (
datekeys-go/README.md). Where and how a release archive or cache service would be hosted (§50, §74). - The landing site and the product application.
- The web pages
/inspectand/createofdatekeys-tsas a deployed service. Their threat is in §7.10; a review of the page code is welcome but optional. - Side channels in the TypeScript and Dart code. Both state that they are not constant-time (see section 4).
3. Questions, ranked
The order is our estimate of impact. Each question gives the sections to read.
Q1. Composition of tlock with age, and the inner and outer layers (§28, §32 to §36, §63 steps 11 to 13)
- Is it sound to use FK_TIME, the output of the tlock IBE-CCA decryption, directly as the age file key of OUTER_TIME_AGE, with age's header MAC and STREAM on top (§35)?
- In
time_and_key, is the nesting age(tlock → age(X25519 × 16 → CONTROL_CBOR)) a real AND of the two factors, against an adversary that knows the round signature early (a colluding threshold), and against a credential holder before the date? - Are the stanza cardinality rules (exactly one tlock stanza, exactly one X25519 stanza in PAYLOAD_AGE, exactly 16 in INNER, no repeated ephemeral share, no identity that unwraps two stanzas) sufficient to exclude escrow paths and multiple valid plaintexts?
- Is there any way, for a creator, to make different recipients see different CONTROL_CBOR? The project's reading is that age's MAC commits to the file key, so no (completeness review of v0.8.2, section 2.4).
Q2. header_binding and the control commitment (§26, §29.8, §30.1, §31, §70)
- header_binding is SHA-256(PRELUDE || PUBLIC_HEADER) inside the sealed control. Given that anyone can seal a control to a public DateKey (§36.1), is "internal coherence" correctly the only claim made? Is any reader decision taken on PUBLIC_HEADER data before step 15 that should wait?
- Is the downgrade analysis of §70 correct: VERSION is unauthenticated until steps 14 and 15, and the version inside CONTROL_CBOR prevents relabelling by anyone who cannot seal a control?
- CONTROL_SIG replaces I_PAYLOAD by payload_commit and zeroes L (§29.8). Is the hiding of I_PAYLOAD sufficient (a 32-byte random X25519 scalar hashed with a domain prefix)? Is excluding L from the signature safe, given that L is bounded by P and checked at step 17?
Q3. What the author signature covers; replay and transplant (§29.8, §29.10, §29.11, §7.9)
- Do control_commit, head_digest and signers_digest, combined into D and AUTHOR_MESSAGE, prevent transplanting a signature to another capsule, another control or another signer list?
- The signature does not cover the area or the padding, so the area can be widened after signing (§29.2, §62.1 rule 13). Does that open any substitution?
- SEAL_SUBJECT and SIG_PART (§29.11): is a seal over SEAL_SUBJECT bound tightly enough to the signature it accompanies, including when the signature is unreadable, unsupported or invalid?
- Equivocation (two capsules with one
capsule_id, both signed) and remaking after the date are stated limits (§7.9, §36.1). Are there further replay forms we missed?
Q4. Strict Ed25519 (§29.9, ed25519_strict.json)
- Is the four-condition profile (canonical A, A not small order, S < ℓ with the top-bits check, cofactorless equation with exact R) exactly what we intend: unique accept/reject across implementations, with no malleability?
- The Go and TypeScript implementations add checks around
crypto/ed25519and noble 2.4; Dart and TypeScript sign with code of their own (TweetNaCl ports). Please check the extra checks (internal/ed25519strictin Go).
Q5. CMS, X.509 and RFC 3161 parsing surface (§29.10, §29.11)
- This is the largest parsing surface of the protocol: DER, X.509, RSA-PSS and ECDSA read field by field, with code of its own in each language (completeness review of v0.13, 3.1). Is the profile tight enough that two conforming readers reach the same verdict?
- Is the order "form (F1), then per-signer results, then F2/F5/F6" free of confusion that would let an attacker turn F2 into F5 or F5 into F6?
- Any risk in accepting repeated SET OF elements, explicit SHA-256 in ESSCertIDv2, or
signing-certificatev1 beside v2? - Known: the algorithm table and certificate profile are provisional until tested with real signatures and seals from several countries (§74, §75 item 13). The v0.12 work found about 1 600 certificates with different verdicts between Go and TypeScript, since fixed (completeness review of v0.13, 3.1).
Q6. Padding leakage (§29.1, §29.2, §55.2)
- Is Padmé over the format 3 BODY, with a fixed 32 KiB area, a reasonable trade-off? Are the leakage bounds in §55.2 (P bounds L; P bounds the number of files;
bloque256sometimes revealed) complete? - Does anything else leak L or the number of credentials: SEALED_CONTROL_LEN, the CONTROL_CBOR size (fixed 8-byte L), STREAM chunk boundaries, timing of step 17?
- Known: the 32 KiB area size is unmeasured; a widened 64 KiB area reveals that a capsule is signed (§55.2, §74).
Q7. Decoys and slot uniformity (§39, §62.1 rules 4 and 5)
- Is the indistinguishability argument (age X25519 recipient anonymity under Diffie–Hellman; 98-byte stanzas; fresh ephemeral share) sufficient, including against a holder of FK_ACCESS?
- The permutation must be uniform and independent of which slots are credentials. Is that requirement sufficient and testable?
- Known: nobody can verify that decoy private keys were discarded (§55.2); age APIs do not allow erasing them, and the reference obtains decoy scalars through an immutable string (§39, informative note).
Q8. Key of words: entropy and KDF (§38.1)
- PBKDF2-HMAC-SHA256 with 600 000 iterations, salted with chain hash, round and
capsule_id. After the date the.dkcis an offline oracle. Is this acceptable for its stated use, and what minimum entropy should the text require? - Known: PBKDF2 is not memory-hard; human-chosen words may have little entropy; v0.14 adds only SHOULD rules (random words by default, a warning). Changing the KDF needs a new dependency under the project's dependency rule, or own code (completeness review of v0.13, 3.3). The normalisation (NFD, mark stripping, simple lowercase) reduces the space somewhat; please comment.
Q9. Locator and SSRF-style rules (§44.1, locator.json, resolved_ip.json)
- Are the address rules (no decoding, LDH hosts, public IP only, special-purpose blocks, local names, CID canonical form, check on every connection, no redirects to rejected hosts) enough to prevent a malicious
.dkkfrom making a reader reach its local network or a confusable host? - Is the NAT64 rule of v0.13 safe (a resolved address in
64:ff9b::/96, or in the network's NAT64 prefix, counts by its embedded IPv4)? - Is the envelope sound: a fresh I_SOBRE, the rest indistinguishable from random,
rest_digestandcapsule_digestchecked before use? - Known: the locator is not authenticated against whoever wrote the
.dkk(§44.1, by design). The locator plaintext is padded to 4096 bytes so its length does not reveal the addresses.
Q10. The H3 shift and the rest of the root of trust (§12.2, §63 steps 10 and 11)
- Is the byte-level text of steps 10 and 11 complete and correct, so that an implementer needs no drand, kyber or kilic code? Since v0.15 the informative annex §79 repeats it for someone opening a capsule without DateKeys software (79.3 and 79.4); please also say whether the annex is correct and enough. In particular: the DST and suite, M = SHA-256(uint64_be(round)), the GT serialisation order, the H3 loop (counter from 1, uint16 little-endian, at most 65534 attempts, shift of the first byte only) and its failure path.
- Does the specification's H3 match drand/kyber
encrypt/ibeh3exactly? The text says the shift is the mask of 256 − 255 = 1 bit applied to the first byte; please confirm against kyber. - Known:
tlock_steps.jsongives M, H(M) and the full step-11 trace, but not the intermediate values of RFC 9380 hash_to_curve; the author decided not to add them (v0.14 decision 10). Dart checks its hash to G1 against the RFC 9380 vectors.
Q11. Error precedence as an oracle (§63, §69.1)
- The codes and their order are fixed so that all implementations report the same code. Does this create an oracle that helps an attacker: for example, before step 13 versus after, or
ERR_ACCESS_INVALIDversusERR_POLICY_STRUCTURE_MISMATCHfor an identity, or the rule that a format 3 reader reads to EOF before reporting a non-integrity code? - Steps 1 to 8 use no secret and no network. Is anything secret-dependent reported before the age MAC is verified?
- (v0.15) A release in hand may be decoded on receipt, but its errors are reported only at step 10, after steps 1 to 9 (§47.1, §69.1). Does the order layers →
chain_hash(ERR_PROFILE_MISMATCH) → round → signature leak anything, or let a release source learn something about a capsule?
Q12. Long-term recovery: the release object, the archives and step 9.c (§7.6, §47.1, §49, §50, §53, §62.1 rules 26 and 27, §63 steps 9 and 10, §79; v0.15)
v0.15 gives long-term recovery a design (§76, «Cambios normativos de la v0.15»; design_overview.md section 8):
- Release object (§47.1). Is the record
{0: "datekeys-release", 1: 1, 2: chain_hash, 3: round, 4: signature}, unsigned and without a "verified" flag, sound as the single form a cache, the Release API and an archive entry use? Is naming the chain by drand'schain_hash, checked against the pinned profile first at step 10 (ERR_PROFILE_MISMATCH), enough, given that drand's JSON, also accepted as input, names no chain? Any issue in the size limit (1 to 1024 bytes,ERR_NON_CANONICAL_CBOR) or in reporting its errors only at step 10? - Release in hand and step 9.c (§49, §63 step 9.c). Since v0.15 a release in hand (a release object or drand's JSON from a file, or the entry of a local archive) is not compared with the local clock; 9.c applies only before a network request, and the person MAY ask a network source before
round_time. The specification argues that a valid signature of a future round exists only if drand is compromised (§7.6), when the clock no longer protects confidentiality. Is that argument complete? Is there any risk in accepting a release in hand without the clock: for example a product that presents "opened before its date" in a misleading way, a test or debugging path that could be abused, or a future scheme where a signature for a future round could exist without a compromise? - Archives and cache services (§50). Is it sound to rest long-term recovery on archives of all rounds and on cache services, local or remote, from DateKeys or others, verified with the pinned key and never trusted, rather than on a release kept next to each capsule (the rejected
.dkr, §76 v0.15 change 4)? Is the informative archive format (a CBOR header, then fixed-size signatures, missing rounds as zeros) adequate, and is treating a zero-filled, short, other-chain or out-of-range entry asERR_RELEASE_UNAVAILABLEat step 9 right? Is the privacy statement complete (a remote archive or cache service learns the round; a local one does not; keeping only the rounds of known capsules reveals their dates)? - The annex (§79). Is the annex correct and sufficient to open a capsule decades later without DateKeys software, and is its list of what it does not check (§79.8) honest about the weaker guarantee?
- Is the written provider model (§7.6, v0.14) still accurate and complete with this design?
- Known: the specification promises no hosted archive or cache service; a project archive or service and its hosting are future work (§74), and none exists today. A capsule years ahead still depends on someone keeping the release of its round, the
.dkcand, with a locator, the envelope rest (§50). If the network stops signing before the round, nothing opens the capsule (§7.6). The SDK rules 26 and 27 (warn when sealing; keep the annex next to the.dkc) are SHOULD and are not implemented in the clients yet; the Go CLI does not implement them either, nor the MAY of asking a network source beforeround_time(docs/spec_v0.15/decisiones.md, decision 11).
4. Known open problems (summary)
So that they are not rediscovered. Sources: §74, §75, docs/REVISION_completitud_protocolo.md (v0.8.2), docs/REVISION_completitud_v0.13.md, datekeys-go/SECURITY.md.
| # | Problem | Source |
|---|---|---|
| K1 | Byte-level schemas of PUBLIC_HEADER, CONTROL_CBOR and .dkk, and the final field limits, are still provisional |
§74, §75 item 1 |
| K2 | The 32 KiB area and the CMS algorithm table and certificate profile are unmeasured against real signatures, seals and OCSP responses | §74, §75 item 13 |
| K3 | No release archive or cache service is hosted, and the specification promises none: long-term opening depends on someone keeping the releases. (v0.15 closed the rest of this item: the release object and the archive format exist, and the clock no longer vetoes a release in hand.) | §50, §74 future work; review v0.13, 2.6 |
| K4 | No post-quantum access type; X25519, Ed25519, ECDSA, RSA and TSA signatures are not post-quantum | §7.7, §74 |
| K5 | The .dkk is 32 raw bytes with no MAC, password protection or check value |
§38, §55.1; review v0.13, 2.8 |
| K6 | Key of words: PBKDF2 is not memory-hard; entropy only a SHOULD | §38.1; review v0.13, 3.3 |
| K7 | A writer with a late clock can seal to an already published round (MAY compare with the last round) | §62.1 rule 2; review v0.13, 2.2 |
| K8 | The writer's self-check does not include a tlock known-answer test | review v0.13, 2.2 |
| K9 | Decoy key disposal is unverifiable | §55.2 |
| K10 | The locator is not authenticated against the .dkk writer |
§44.1 |
| K11 | No formal security model or proof; goals are informal | review v0.8.2, 4.4 |
| K12 | No implementation was written from the text alone: TypeScript and Dart were written with the Go code in view, and Dart copies some Go error texts | review v0.13, 2.4; docs/HANDOFF.md |
| K13 | The tlock encryption in Dart uses BigInt and is not constant-time (accepted by the author); JavaScript promises neither constant time nor memory erasure |
datekeys-dart/README.md; datekeys-ts/README.md |
| K14 | kilic/bls12-381 is archived; drand/tlock has had no tagged release since August 2024 |
datekeys-go/SECURITY.md |
| K15 | The datekeys. prefix of extension_id is used but not reserved |
review v0.13, 2.8 |
| K16 | Three formats must be read forever, which multiplies cases in §64 and §69.1 (decided: capsules of older formats exist) | §70; review v0.13, 3.4 |
| K17 | Editorial, still in v0.15: header line 8 says «Implementación de referencia prevista»; §1 lists the Release API and Release Cache among what the document defines, while §45 to §47 fix only what they serve (the release object) and leave the HTTP form informative, and no service exists; the second list of §74 ends with a semicolon | review v0.13, 3.8 |
5. Not a question: stated non-goals
Please do not report as findings the non-goals of §5 (for example: nobody can check before the date that a capsule will open; no cancellation; no expiry) or the limits in section 8 of threat_model.md, unless you think the text states them wrongly or a product could easily misrepresent them.