Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
|
|
|
|
# Changelog
|
|
|
|
|
|
|
|
|
|
|
|
All notable changes to this module are documented here. The project follows
|
|
|
|
|
|
semantic versioning; `v0.x` versions make no API stability promise.
|
|
|
|
|
|
|
Docs at spec v0.11 and the draft v0.12: READMEs, SECURITY, traceability, CHANGELOG
The session that closed v0.11 left the documentation at v0.10 (review of
2 October, G14).
- README.md and README.es.md say the same again: the reference implements
v0.11, tagged, and the branch v0.12 the draft; SpecVersion 0.11; the area
of 32 KiB in the picture of BODY; the table of modules with authorkey,
internal/cms, internal/der, locator, wordkey and the public note; and what
the CLI does now: encrypt -sign shows the key and the code of
AUTHOR_MESSAGE before it signs, decrypt -expect-author writes nothing
unless the key of an F4 matches, the lines of the verdicts break behind a
mark, and decrypt and inspect say when a public note is not shown. The
security properties no longer say that no signature is checked.
- SECURITY.md: the scope is v0.11 and the draft v0.12; the limits of a
signature, a seal and a key of words; the standard library among the
cryptographic dependencies.
- docs/traceability.md at the draft v0.12: rows for 24.1, 29.8 to 29.12,
38.1 and 44.1, and rows 29.2, 29.3, 29.7, 62.1, 64, 67, 70, 72 and 76 up
to date, with the code and the tests of each. The cases of spec 64 that
security_cms.json and locator.json still lack are marked pending.
- CHANGELOG.md: an entry for the draft v0.12: the review and its fixes, the
draft, the CMS reader with its own profile, the addresses of the locator,
the CLI, the new test data, and what is pending.
- capsule/format3.go: the comments of EncodeSecurityWith,
EncodeAuthorSignature and EncodeSeal no longer say that this version
defines no alg and no seal_type.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
|
## Unreleased — specification v0.12 draft
|
|
|
|
|
|
|
|
|
|
|
|
Implements the draft v0.12 of the DateKeys Protocol Specification, on the
|
|
|
|
|
|
branch `v0.12` and not approved yet. It fixes what the review of the
|
|
|
|
|
|
implementation of v0.11 found on 2 October 2026, and changes no format: a
|
|
|
|
|
|
reader of v0.11 opens these capsules, and this one opens those of v0.11.
|
|
|
|
|
|
`SpecVersion` stays 0.11 until the author approves the draft.
|
|
|
|
|
|
|
|
|
|
|
|
- **Review of 2 October.** Six adversarial reviews of the implementation of
|
|
|
|
|
|
v0.11 found nothing blocking: what is signed, the strict Ed25519, the CMS
|
|
|
|
|
|
and RFC 3161 checks and the envelope hold, and the failures were at the
|
|
|
|
|
|
edges. What the text of v0.11 already asked for is fixed in the code, as
|
|
|
|
|
|
this entry says; what it left open or got wrong goes to the draft.
|
|
|
|
|
|
- A typed nil in `EncryptOptions.AuthorKey`, `CMSSigner` or `Sealer` is an
|
|
|
|
|
|
error, never a capsule without the signature or the seal that was asked
|
|
|
|
|
|
for.
|
|
|
|
|
|
- A panic while evaluating the signature or the seal fails only that part,
|
|
|
|
|
|
F1 or S2, not both.
|
|
|
|
|
|
- `OpenOptions.Accept` sees the verdicts after step 17 and before step 18,
|
|
|
|
|
|
and can refuse to publish the files.
|
|
|
|
|
|
- `extension.CheckWrite`, the rule of encoders of spec §72: the writers of
|
|
|
|
|
|
capsules and `.dkk` files refuse `datekeys.note` and `datekeys.capsule`
|
|
|
|
|
|
outside the arrays where they are registered, or with invalid data.
|
|
|
|
|
|
- `authorkey.Key.String` and `GoString` hide the secret key, which only
|
|
|
|
|
|
`Key.Secret` returns. `ParsePublic` refuses a key that is not a point of
|
|
|
|
|
|
the curve (`ed25519strict.OnCurve`).
|
|
|
|
|
|
- `Header.UnusableNote` tells a public note that breaks the rules of text
|
|
|
|
|
|
from no note.
|
|
|
|
|
|
- **Specification.** `spec/DateKeys_Protocol_Specification_v0.12.md`, a draft
|
|
|
|
|
|
whose §76 lists each change with its case: the names of certificates in the
|
|
|
|
|
|
verdicts, between « and », shown only with at most 64 code points and no two
|
|
|
|
|
|
spaces in a row; the authority of each seal in the lines of F6, with the
|
|
|
|
|
|
warning that nobody checks who issued it; the holder by `givenName` and
|
|
|
|
|
|
`surname` before the `commonName`, which in the certificates of the FNMT
|
|
|
|
|
|
carries the NIF, when both have text that is not empty, and the issuer by
|
|
|
|
|
|
its `commonName` or, without one that has text, its `organizationName`;
|
|
|
|
|
|
the profile of the certificate field by field; object
|
Docs at spec v0.11 and the draft v0.12: READMEs, SECURITY, traceability, CHANGELOG
The session that closed v0.11 left the documentation at v0.10 (review of
2 October, G14).
- README.md and README.es.md say the same again: the reference implements
v0.11, tagged, and the branch v0.12 the draft; SpecVersion 0.11; the area
of 32 KiB in the picture of BODY; the table of modules with authorkey,
internal/cms, internal/der, locator, wordkey and the public note; and what
the CLI does now: encrypt -sign shows the key and the code of
AUTHOR_MESSAGE before it signs, decrypt -expect-author writes nothing
unless the key of an F4 matches, the lines of the verdicts break behind a
mark, and decrypt and inspect say when a public note is not shown. The
security properties no longer say that no signature is checked.
- SECURITY.md: the scope is v0.11 and the draft v0.12; the limits of a
signature, a seal and a key of words; the standard library among the
cryptographic dependencies.
- docs/traceability.md at the draft v0.12: rows for 24.1, 29.8 to 29.12,
38.1 and 44.1, and rows 29.2, 29.3, 29.7, 62.1, 64, 67, 70, 72 and 76 up
to date, with the code and the tests of each. The cases of spec 64 that
security_cms.json and locator.json still lack are marked pending.
- CHANGELOG.md: an entry for the draft v0.12: the review and its fixes, the
draft, the CMS reader with its own profile, the addresses of the locator,
the CLI, the new test data, and what is pending.
- capsule/format3.go: the comments of EncodeSecurityWith,
EncodeAuthorSignature and EncodeSeal no longer say that this version
defines no alg and no seal_type.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
|
identifiers by their bytes, repeated elements of a SET OF and the edge cases
|
|
|
|
|
|
of the token; the addresses and the padding of the locator; and the errata
|
|
|
|
|
|
of §44.1, §55.2, §64, §67 and §76. The CDDL fixes the sizes of the locator.
|
|
|
|
|
|
`Verdicts.Lines` follows it, and writes the result of a foreign signer in
|
|
|
|
|
|
Spanish.
|
|
|
|
|
|
- **CMS reader with a profile of its own.** `internal/cms` reads certificates
|
|
|
|
|
|
field by field, as the profile of §29.10 says, instead of with
|
|
|
|
|
|
`encoding/asn1` and `crypto/x509`, so that a second implementation reads
|
|
|
|
|
|
them the same; a certificate that breaks it decides nothing unless a
|
|
|
|
|
|
`SignerInfo` names it. The text of a name comes only from UTF8String,
|
|
|
|
|
|
PrintableString, IA5String, TeletexString in ASCII and BMPString without
|
|
|
|
|
|
surrogates, with nothing removed. The key is RSA with NULL parameters and
|
|
|
|
|
|
an odd modulus, or EC uncompressed on P-256, P-384 and P-521. Object
|
|
|
|
|
|
identifiers are compared by the bytes of their DER, so an arc of 2^31 or
|
|
|
|
|
|
more no longer makes an attribute that decides nothing fail the signature;
|
|
|
|
|
|
a SET OF may repeat an element, and two copies of a certificate are one; a
|
|
|
|
|
|
key of another scheme than its algorithm is F2; a `messageImprint` of
|
|
|
|
|
|
another length is S3; the `crls` of a token decide nothing. `internal/der`
|
|
|
|
|
|
checks UTCTime and GeneralizedTime in their forms of X.690, a date that
|
|
|
|
|
|
exists, and millis and micros as minimal INTEGERs.
|
|
|
|
|
|
- **Addresses of the locator.** A reader rejects an address that breaks §44.1
|
|
|
|
|
|
and uses the others (`Locator.Usable`); a writer never writes one. Addresses
|
|
|
|
|
|
refuse the special-purpose blocks of IANA, IPv6 outside 2000::/3,
|
|
|
|
|
|
`localhost` and local names, characters outside RFC 3986, dot segments, and
|
Spec v0.12 draft: the rules of an address that the reference applied
The text of 44.1 now says what the reference already refused and a
second implementation that followed the text would have accepted:
segments of 1 to 63 characters that neither start nor end with a hyphen,
the scheme in lower case, 0x and the local names in either case, an IPv4
without leading zeros, and a CID of at most 128 characters in canonical
base32, with minimal varints and a digest of at least one byte. A port is
written without leading zeros: the reference accepted 0443 and 00443 and
refused 000443, the same number, and now refuses the three. Change 6 of
section 76 records it, and section 64 lists the cases.
locator.json is made again with 30 more addresses, 247 in all, and
TestAddresses checks the same rules.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 days ago
|
|
|
|
a CID that does not decode to version 1 and a multihash. A port with a
|
|
|
|
|
|
leading zero is refused too, as the draft asks, which also writes the rules
|
|
|
|
|
|
that the reference applied without a text: segments of 1 to 63 characters
|
|
|
|
|
|
that neither start nor end with a hyphen, the scheme in lower case, `0x`
|
|
|
|
|
|
and local names in either case, an IPv4 without leading zeros and a CID in
|
|
|
|
|
|
canonical form. `ParseInfo` checks
|
Docs at spec v0.11 and the draft v0.12: READMEs, SECURITY, traceability, CHANGELOG
The session that closed v0.11 left the documentation at v0.10 (review of
2 October, G14).
- README.md and README.es.md say the same again: the reference implements
v0.11, tagged, and the branch v0.12 the draft; SpecVersion 0.11; the area
of 32 KiB in the picture of BODY; the table of modules with authorkey,
internal/cms, internal/der, locator, wordkey and the public note; and what
the CLI does now: encrypt -sign shows the key and the code of
AUTHOR_MESSAGE before it signs, decrypt -expect-author writes nothing
unless the key of an F4 matches, the lines of the verdicts break behind a
mark, and decrypt and inspect say when a public note is not shown. The
security properties no longer say that no signature is checked.
- SECURITY.md: the scope is v0.11 and the draft v0.12; the limits of a
signature, a seal and a key of words; the standard library among the
cryptographic dependencies.
- docs/traceability.md at the draft v0.12: rows for 24.1, 29.8 to 29.12,
38.1 and 44.1, and rows 29.2, 29.3, 29.7, 62.1, 64, 67, 70, 72 and 76 up
to date, with the code and the tests of each. The cases of spec 64 that
security_cms.json and locator.json still lack are marked pending.
- CHANGELOG.md: an entry for the draft v0.12: the review and its fixes, the
draft, the CMS reader with its own profile, the addresses of the locator,
the CLI, the new test data, and what is pending.
- capsule/format3.go: the comments of EncodeSecurityWith,
EncodeAuthorSignature and EncodeSeal no longer say that this version
defines no alg and no seal_type.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
|
that the locator is an age file with one tlock stanza for the round of its
|
|
|
|
|
|
DateKey, `Info.Extension` reads what it writes, and `Info.OpenLocator`
|
|
|
|
|
|
refuses a nil registry. The errors of `Open` and `Unmarshal` carry no
|
|
|
|
|
|
normative code.
|
|
|
|
|
|
- **CLI.** `encrypt -sign` shows the author key and the code of
|
|
|
|
|
|
`AUTHOR_MESSAGE` before it signs (spec §62.1 rule 20).
|
|
|
|
|
|
`decrypt -expect-author` compares the key of an F4, and writes nothing
|
|
|
|
|
|
unless it matches. `decrypt` and `inspect` say when a public note is not
|
|
|
|
|
|
shown because it breaks the rules of text; `inspect -json` gives
|
|
|
|
|
|
`public_note_unusable`. The lines of the verdicts break at the last space
|
|
|
|
|
|
that fits, each row after the first behind ` ↳ `, so that the terminal
|
|
|
|
|
|
never breaks them. `encrypt` reports L as the length of the payload, not of
|
|
|
|
|
|
the content.
|
|
|
|
|
|
- **Tables for Dart.** `internal/pathrule/gen -dart` writes the Unicode and
|
|
|
|
|
|
best-fit tables as a Dart library for `datekeys-dart`, the third
|
|
|
|
|
|
implementation, as `-ts` does for `datekeys-ts`: the same lists and the same
|
|
|
|
|
|
`TablesDigest`. The tables and the TypeScript module do not change.
|
Docs at spec v0.11 and the draft v0.12: READMEs, SECURITY, traceability, CHANGELOG
The session that closed v0.11 left the documentation at v0.10 (review of
2 October, G14).
- README.md and README.es.md say the same again: the reference implements
v0.11, tagged, and the branch v0.12 the draft; SpecVersion 0.11; the area
of 32 KiB in the picture of BODY; the table of modules with authorkey,
internal/cms, internal/der, locator, wordkey and the public note; and what
the CLI does now: encrypt -sign shows the key and the code of
AUTHOR_MESSAGE before it signs, decrypt -expect-author writes nothing
unless the key of an F4 matches, the lines of the verdicts break behind a
mark, and decrypt and inspect say when a public note is not shown. The
security properties no longer say that no signature is checked.
- SECURITY.md: the scope is v0.11 and the draft v0.12; the limits of a
signature, a seal and a key of words; the standard library among the
cryptographic dependencies.
- docs/traceability.md at the draft v0.12: rows for 24.1, 29.8 to 29.12,
38.1 and 44.1, and rows 29.2, 29.3, 29.7, 62.1, 64, 67, 70, 72 and 76 up
to date, with the code and the tests of each. The cases of spec 64 that
security_cms.json and locator.json still lack are marked pending.
- CHANGELOG.md: an entry for the draft v0.12: the review and its fixes, the
draft, the CMS reader with its own profile, the addresses of the locator,
the CLI, the new test data, and what is pending.
- capsule/format3.go: the comments of EncodeSecurityWith,
EncodeAuthorSignature and EncodeSeal no longer say that this version
defines no alg and no seal_type.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
|
- **Test data.**
|
|
|
|
|
|
- `genfixtures -force` writes the fixtures of v0.10 again with their area
|
|
|
|
|
|
of 512 bytes, through `EncryptOptions.TestAreaLen`, which needs
|
|
|
|
|
|
`TestVectors`; it used to rewrite them with 32 KiB and fail halfway.
|
|
|
|
|
|
- `format3_seal_unsupported` uses `seal_type` 4294967295, reserved for
|
|
|
|
|
|
tests, instead of `seal_type` 1, which a later version may define;
|
|
|
|
|
|
`capsule.AlgTest` and `SealTypeTest` name the two values.
|
|
|
|
|
|
- `security.json` carries the context of a capsule, its commitments and the
|
|
|
|
|
|
time of its round, and each case the verdicts and the lines of a reader of
|
|
|
|
|
|
this version: a signature of `alg` 1 that does not verify is F2, a token
|
|
|
|
|
|
of `seal_type` 2 that is not DER is S2, and new cases give a valid
|
|
|
|
|
|
signature of `alg` 1 (F4), and `alg` and `seal_type` 4294967295 (F1, S1).
|
|
|
|
|
|
24 cases.
|
|
|
|
|
|
- `note.json`, new: 16 public notes, those that pass and those that a
|
|
|
|
|
|
writer refuses and a reader does not show, each with the text of the rule
|
|
|
|
|
|
it breaks.
|
|
|
|
|
|
- New fixtures `format3_unsigned`, the capsule of `format3_signed` without
|
|
|
|
|
|
its signature, with the same P, and `format3_note`, with a public note:
|
|
|
|
|
|
26 capsules, 14 of format 3. The records of `format3_signed_cms` and
|
|
|
|
|
|
`format3_sealed` give the lines of v0.12.
|
|
|
|
|
|
- `mutations.json` gains 8 mutations of the list of v0.11 of §64: the
|
|
|
|
|
|
signature of `alg` 1 altered (F2), removed (F0), made again with another
|
|
|
|
|
|
key (F4 of that key) and transplanted to another capsule (F2); a key of 31
|
|
|
|
|
|
bytes and a signature of 65 (F1); the area widened to 64 KiB after signing
|
|
|
|
|
|
(F4, the same `AUTHOR_MESSAGE`); and the public note changed
|
|
|
|
|
|
(`ERR_HEADER_BINDING`, step 15). The signature of `alg` 1 that does not
|
|
|
|
|
|
verify (F2) is now a case of §64. 218 cases, 178 of the spec.
|
Docs: the test data of v0.11 and of the draft v0.12, nothing pending
- testdata/README.md: the files of v0.11 and of the draft, the spec
label 0.11 until the approval, 26 fixtures with format3_unsigned and
format3_note, security.json in its context with lines, security_cms.json
without the cases of no context, note.json, the new parts of
locator.json, and the corpus of 218 cases, 178 of the spec, with the
eight of the list of v0.11. The release of round 1000 is in the records
and in mutations.json, not in quicknet_rounds.json.
- docs/traceability.md: the cases of section 64 that security_cms.json
and locator.json now hold are no longer pending.
- CHANGELOG: security_cms.json and locator.json under the test data of
the draft, instead of pending.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 days ago
|
|
|
|
- `security_cms.json`, made again with the profile of v0.12: 135 cases,
|
|
|
|
|
|
each with the lines of `Verdicts.Lines`, for each row of §29.7 and each
|
|
|
|
|
|
item of the lists of §64 of v0.11 and v0.12: the names of the holder and
|
|
|
|
|
|
of the issuer, the profile of the certificate, identifiers, repetitions,
|
|
|
|
|
|
every hash and curve of the table, and the edges of the token. The two
|
|
|
|
|
|
cases without a context, which gave the verdicts of a reader of v0.10,
|
|
|
|
|
|
are gone.
|
|
|
|
|
|
- `locator.json`, made again with the cases of §64 of v0.11 and v0.12: the
|
|
|
|
|
|
first and the last address of each IPv4 block with their public
|
Spec v0.12 draft: the rules of an address that the reference applied
The text of 44.1 now says what the reference already refused and a
second implementation that followed the text would have accepted:
segments of 1 to 63 characters that neither start nor end with a hyphen,
the scheme in lower case, 0x and the local names in either case, an IPv4
without leading zeros, and a CID of at most 128 characters in canonical
base32, with minimal varints and a digest of at least one byte. A port is
written without leading zeros: the reference accepted 0443 and 00443 and
refused 000443, the same number, and now refuses the three. Change 6 of
section 76 records it, and section 64 lists the cases.
locator.json is made again with 30 more addresses, 247 in all, and
TestAddresses checks the same rules.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 days ago
|
|
|
|
neighbours, the IPv6 blocks, local names, characters outside RFC 3986, dot
|
|
|
|
|
|
segments and base32 that is not a CID v1, and what the draft fixes about
|
|
|
|
|
|
segments, case, leading zeros and the form of a CID; a locator whose
|
|
|
|
|
|
`http` and NAT64 addresses are rejected and whose third is used; resources
|
|
|
|
|
|
of the rest, with bytes after it, changed, cut or at another offset; data
|
|
|
|
|
|
of the extension that a reader cannot use, a locator for another round
|
|
|
|
|
|
among them; and plaintexts with one defect each, the bases 4094, 4070 and
|
|
|
|
|
|
3837 completed to 4096 by a key 6 that is empty or not in its shortest
|
|
|
|
|
|
form (change 7) among them. The bases 3837, 4070, 4094 and 4095 and those
|
|
|
|
|
|
of the next multiple join `padding_cases`.
|
|
|
|
|
|
- `wordkey.json`, new: the key of words of §38.1, which §64 of v0.11
|
|
|
|
|
|
asks for and was only in the tests of `wordkey`. 45 texts and their
|
|
|
|
|
|
words, among them each space of the list and three that are not; 20
|
|
|
|
|
|
texts and what a writer does with them, the zero width space that it
|
|
|
|
|
|
refuses among them; and 6 identities with their recipients, the vector
|
|
|
|
|
|
of §38.1 and those of the next round, another `capsule_id`, another
|
|
|
|
|
|
chain hash, round 2^53 − 1 and words that are not ASCII. For
|
|
|
|
|
|
`datekeys-ts` and `datekeys-dart`.
|
Docs: the test data of v0.11 and of the draft v0.12, nothing pending
- testdata/README.md: the files of v0.11 and of the draft, the spec
label 0.11 until the approval, 26 fixtures with format3_unsigned and
format3_note, security.json in its context with lines, security_cms.json
without the cases of no context, note.json, the new parts of
locator.json, and the corpus of 218 cases, 178 of the spec, with the
eight of the list of v0.11. The release of round 1000 is in the records
and in mutations.json, not in quicknet_rounds.json.
- docs/traceability.md: the cases of section 64 that security_cms.json
and locator.json now hold are no longer pending.
- CHANGELOG: security_cms.json and locator.json under the test data of
the draft, instead of pending.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 days ago
|
|
|
|
- `testdata/README.md` describes the files of v0.11 and of the draft.
|
Docs at spec v0.11 and the draft v0.12: READMEs, SECURITY, traceability, CHANGELOG
The session that closed v0.11 left the documentation at v0.10 (review of
2 October, G14).
- README.md and README.es.md say the same again: the reference implements
v0.11, tagged, and the branch v0.12 the draft; SpecVersion 0.11; the area
of 32 KiB in the picture of BODY; the table of modules with authorkey,
internal/cms, internal/der, locator, wordkey and the public note; and what
the CLI does now: encrypt -sign shows the key and the code of
AUTHOR_MESSAGE before it signs, decrypt -expect-author writes nothing
unless the key of an F4 matches, the lines of the verdicts break behind a
mark, and decrypt and inspect say when a public note is not shown. The
security properties no longer say that no signature is checked.
- SECURITY.md: the scope is v0.11 and the draft v0.12; the limits of a
signature, a seal and a key of words; the standard library among the
cryptographic dependencies.
- docs/traceability.md at the draft v0.12: rows for 24.1, 29.8 to 29.12,
38.1 and 44.1, and rows 29.2, 29.3, 29.7, 62.1, 64, 67, 70, 72 and 76 up
to date, with the code and the tests of each. The cases of spec 64 that
security_cms.json and locator.json still lack are marked pending.
- CHANGELOG.md: an entry for the draft v0.12: the review and its fixes, the
draft, the CMS reader with its own profile, the addresses of the locator,
the CLI, the new test data, and what is pending.
- capsule/format3.go: the comments of EncodeSecurityWith,
EncodeAuthorSignature and EncodeSeal no longer say that this version
defines no alg and no seal_type.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
|
|
Spec v0.11 approved: the text of the review, SpecVersion 0.11 and its SHA-256
The author approved the text and the six open decisions on 1 October 2026.
The spec says now what the review left open: the form of the CMS signature
and of the TSTInfo field by field, the ESSCertIDv2 with SHA-256 written, the
padding of the locator at its boundaries, base32 CIDs, the addresses read
without decoding, the issuer shown by the rules of the holder, and the area
decided after the signatures. SpecVersion is 0.11, the records of fixtures
and vectors say so, and decrypt shows an mtime later than a valid seal as an
inconsistency, which 29.7 asks as a SHOULD.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
|
## Unreleased — specification v0.11
|
|
|
|
|
|
|
Spec v0.11 approved: the text of the review, SpecVersion 0.11 and its SHA-256
The author approved the text and the six open decisions on 1 October 2026.
The spec says now what the review left open: the form of the CMS signature
and of the TSTInfo field by field, the ESSCertIDv2 with SHA-256 written, the
padding of the locator at its boundaries, base32 CIDs, the addresses read
without decoding, the issuer shown by the rules of the holder, and the area
decided after the signatures. SpecVersion is 0.11, the records of fixtures
and vectors say so, and decrypt shows an mtime later than a valid seal as an
inconsistency, which 29.7 asks as a SHOULD.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
|
Moves the module to the DateKeys Protocol Specification v0.11, approved by its
|
|
|
|
|
|
author on 1 October 2026 and tagged `spec-v0.11`, which adds to format 3 what
|
|
|
|
|
|
the v0.10 reserved, without changing any format: a reader of v0.10 opens these
|
|
|
|
|
|
capsules. `SpecVersion` is 0.11.
|
|
|
|
|
|
|
|
|
|
|
|
- **Area of 32 KiB.** `capsule.AreaLen` is 32768, and `LargeAreaLen`, 65536,
|
Review fixes: the CMS reader, the area after the signature, and the issuer on screen
The TSTInfo is read field by field in DER, with accuracy from zero and
millis and micros from 1 to 999, genTime in UTC with Z, no default written
and nothing after the last field. The ContentInfo and the SignerInfo must be
SEQUENCEs, a SignerInfo version must match its sid, an attribute needs a
value and is counted by attribute and not by value, a signing-certificate
beside the v2 decides nothing, PSS parameters come in order without the
trailer, and der.Check refuses the end of contents and the universal tags
the profile does not use.
The writer signs before L is fixed: write asks prepare for the final L, so
the area grows to 64 KiB only when what was signed does not fit and LargeArea
allows it, and nobody signs twice for it. Typed nils are nil, the exclusions
are checked before a file is read, Encrypt refuses the signing options, and
EvaluateSecurityIn gives X if a parser panics. The issuer of a certificate is
filtered like its holder.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
|
which `EncryptOptions.LargeArea` lets the writer use, after the signatures
|
|
|
|
|
|
are made, only when what they produced does not fit in 32 KiB. The fixtures of v0.10 keep their 512 bytes
|
|
|
|
|
|
(`AreaUnit`), which a reader accepts.
|
|
|
|
|
|
- **Key of words.** Package `wordkey` derives a credential of `time_and_key`
|
|
|
|
|
|
from six or more words that the person chooses, with PBKDF2-HMAC-SHA256 of
|
|
|
|
|
|
600 000 iterations salted with the chain, the round and `capsule_id`
|
|
|
|
|
|
(§38.1); the words are normalised with the tables of Unicode 18.0.0.
|
|
|
|
|
|
`EncryptOptions.Words` adds it as one more credential, and the CLI takes
|
|
|
|
|
|
`-words` and `-words-file` in `encrypt` and `decrypt`.
|
|
|
|
|
|
- **Author signature, `alg` 1.** `internal/ed25519strict` verifies with the
|
|
|
|
|
|
strict profile of §29.9 and `authorkey` keeps the keys `dkauthor1…`.
|
|
|
|
|
|
`EncryptOptions.AuthorKey` signs, inside the writer and after its checks;
|
|
|
|
|
|
`OpenOptions.AuthorKeys` are the keys the person saved; `Verdicts` gives F2,
|
|
|
|
|
|
F3 and F4. `capsule.PayloadCommit`, `ControlCommit`, `HeadDigest`,
|
|
|
|
|
|
`AuthorMessage` and `SealSubject` compute what is signed.
|
|
|
|
|
|
- **Signature with certificates, `alg` 2.** `internal/cms` and `internal/der`
|
|
|
|
|
|
read the CMS signature and the RFC 3161 token of §29.10 and §29.11 with the
|
|
|
|
|
|
standard library only, a closed table of algorithms, and DER checked byte by
|
|
|
|
|
|
byte. `EncryptOptions.CMSSigner` gets `AUTHOR_MESSAGE` and returns what the
|
|
|
|
|
|
person signed outside; the writer checks it and writes nothing unless it is
|
|
|
|
|
|
F6. The reader gives F1, F2, F5 and F6 and names the signers.
|
|
|
|
|
|
- **Time seal, `seal_type` 2.** `EncryptOptions.Sealer` asks for the token
|
|
|
|
|
|
over `SEAL_SUBJECT`; the reader gives S1 to S5 and the authority of a valid
|
|
|
|
|
|
seal.
|
|
|
|
|
|
- **Public note.** `EncryptOptions.PublicNote` writes `datekeys.note`;
|
|
|
|
|
|
`Header.PublicNote` reads it; `extension.Standard` registers it.
|
|
|
|
|
|
- **Locator and envelope.** Package `locator`: the data of `datekeys.capsule`,
|
|
|
|
|
|
the locator sealed with tlock, and the envelope split into a header and a
|
|
|
|
|
|
rest that can hide inside another file.
|
|
|
|
|
|
- **Test data.** `format3_signed` and the vectors of its signature, and the
|
|
|
|
|
|
fixtures `format3_signature_unsupported` and `format3_seal_unsupported`
|
|
|
|
|
|
remade with `alg` 4294967295.
|
Spec v0.11 approved: the text of the review, SpecVersion 0.11 and its SHA-256
The author approved the text and the six open decisions on 1 October 2026.
The spec says now what the review left open: the form of the CMS signature
and of the TSTInfo field by field, the ESSCertIDv2 with SHA-256 written, the
padding of the locator at its boundaries, base32 CIDs, the addresses read
without decoding, the issuer shown by the rules of the holder, and the area
decided after the signatures. SpecVersion is 0.11, the records of fixtures
and vectors say so, and decrypt shows an mtime later than a valid seal as an
inconsistency, which 29.7 asks as a SHOULD.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
|
- **Review.** Three independent reviews, of the CMS reader, of the signing
|
|
|
|
|
|
logic and writer, and of the locator and the CLI, fixed the form of the
|
|
|
|
|
|
signature and of the TSTInfo field by field, the area decided after the
|
|
|
|
|
|
signatures, the text of an issuer on screen and the rules of the addresses;
|
|
|
|
|
|
spec §76 item 8 records them. Under a valid seal, `decrypt` shows an mtime
|
|
|
|
|
|
later than the seal as an inconsistency (§29.7).
|
|
|
|
|
|
- **CLI.** `author keygen` and `author public`, `encrypt -sign`, `-note` and
|
|
|
|
|
|
`-large-area`, and `decrypt -expect-author`. Passphrases come from a file or
|
|
|
|
|
|
from the standard input.
|
|
|
|
|
|
|
Format 3, step 7: the documentation of spec v0.10
- README and README.es: specification 0.10, format 3 written and formats
1 to 3 read; BODY in the picture of a capsule; the CLI with folders,
-comment, -author, -no-mtime and decrypt into a new folder, with the
presentation of 29.7; EncryptFiles, Source and Sink in the library; the
metadata that format 3 hides, safe extraction, and the declared author
and comment that prove nothing; the fixtures, vectors and mutations of
format 3; 18 normative errors.
- CHANGELOG: the section of specification v0.10.
- docs/traceability.md: rows 29.2 to 29.7 and 29.5.1, and the rows that
format 3 changes (22, 23, 28, 29, 29.1, 31, 33, 39, 56, 57, 61 to 64,
67 to 70, 75 and 76); ERR_HEAD_INVALID in the error mapping; two
implementation decisions, the Sink and the width of the terminal.
- testdata/README.md: the nine fixtures of format 3 and their records,
the four new vector files, the mutation corpus of 209 cases with its
list of format 3 and the verdicts of the cases that open, and the two
bases of format 3 of the differential corpus.
- SECURITY.md: specification v0.10, and the head as untrusted input.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
|
## Unreleased — specification v0.10
|
|
|
|
|
|
|
|
|
|
|
|
Moves the module to the DateKeys Protocol Specification v0.10, which adds
|
|
|
|
|
|
capsule format 3, delivery 1 of its design: a capsule holds files, with
|
|
|
|
|
|
their paths, sizes, SHA-256 and modification times, a comment and a
|
|
|
|
|
|
declared author, and a security area that later versions fill with an
|
|
|
|
|
|
author signature and a time seal. `EncryptFiles` writes format 3; `Open`
|
|
|
|
|
|
and `Inspect` read the three formats, and capsules of formats 1 and 2 keep
|
|
|
|
|
|
their verdicts.
|
|
|
|
|
|
|
|
|
|
|
|
### Format 3
|
|
|
|
|
|
|
|
|
|
|
|
- `VERSION` 3 in the PRELUDE and CONTROL_CBOR of schema version 3, with the
|
|
|
|
|
|
keys of version 2: L is the length of BODY (spec §22, §31).
|
|
|
|
|
|
- BODY (spec §29.2): a frame of three uint32, `AREA_LEN`, `SECURITY_LEN` and
|
|
|
|
|
|
`HEAD_LEN`, the security area, 512 bytes when written and from 512 to
|
|
|
|
|
|
65536 when read, the head and the files. A violation of the frame, L
|
|
|
|
|
|
under 12 included, or a byte of the area after SECURITY_CBOR that is not
|
|
|
|
|
|
zero, is `ERR_INTEGRITY` at step 17. `capsule.BodyFrame`,
|
|
|
|
|
|
`ParseBodyFrame`, `CheckArea`.
|
|
|
|
|
|
- Security (spec §29.3, §29.7): a map of version 1 whose keys 2 and 3 are
|
|
|
|
|
|
byte strings holding the author signature and the seal, encoded apart.
|
|
|
|
|
|
It never fails and never decides the opening: `capsule.EvaluateSecurity`
|
|
|
|
|
|
gives the verdicts X, F0, F1, S0, S1 and S2, and `Verdicts.Lines` the
|
|
|
|
|
|
texts of the table, in Spanish. This version implements no `alg` and no
|
|
|
|
|
|
`seal_type`.
|
|
|
|
|
|
- The head (spec §29.4): its type tag and version 1, then the CDDL with R1
|
|
|
|
|
|
and R8, then, in key order, the comment and the declared author, each
|
|
|
|
|
|
file with R2 to R6c, R10 and its layout, R7 and R9 over the tree, and the
|
|
|
|
|
|
critical extensions. A violation of a rule of layer 4 is the new
|
|
|
|
|
|
`ERR_HEAD_INVALID`. `capsule.Head`, `File`, `EncodeHead`, `DecodeHead`,
|
|
|
|
|
|
and `extension.Head` for its extensions.
|
|
|
|
|
|
- Paths and texts (spec §29.5, §29.6, §29.5.1): `internal/pathrule` applies
|
|
|
|
|
|
the rules with tables generated from 19 pinned data files, Unicode 18.0.0
|
|
|
|
|
|
and the 15 WindowsBestFit tables, never with the Unicode functions of the
|
|
|
|
|
|
platform; `TablesDigest` pins them. Its errors name the rule and the
|
|
|
|
|
|
character, never echo the text, and read the same in every
|
|
|
|
|
|
implementation. R4b and the invisibles rule of §29.6 keep ZWJ, ZWNJ,
|
|
|
|
|
|
VS15 and VS16 to their emoji and script uses.
|
|
|
|
|
|
|
|
|
|
|
|
### Reader (spec §63 step 17, §56, §57)
|
|
|
|
|
|
|
|
|
|
|
|
- `capsule.Sink` receives the files: `Begin` with the validated head,
|
|
|
|
|
|
`Create` for each file, `Commit` at step 18 only, and `Abort` once after
|
|
|
|
|
|
any failure that follows `Begin`. A format 3 capsule without
|
|
|
|
|
|
`OpenOptions.Sink` fails right after step 2 with `capsule.ErrSinkRequired`,
|
|
|
|
|
|
a caller error without a code, before any request; a capsule of format 1
|
|
|
|
|
|
or 2 without `dst` fails there too.
|
|
|
|
|
|
- Step 17 in its substeps: the frame and the area, security, the head, the
|
|
|
|
|
|
files filling CONTENT, the SHA-256 of each file and the padding. A failure
|
|
|
|
|
|
of age, or a plaintext whose length is not P, prevails; otherwise the
|
|
|
|
|
|
first substep that fails decides, and a code other than `ERR_INTEGRITY`
|
|
|
|
|
|
is reported only after reading PAYLOAD_AGE to its end.
|
|
|
|
|
|
- Reads of BODY grow with the bytes received, never with `AREA_LEN`,
|
|
|
|
|
|
`HEAD_LEN` or a declared size (spec §57).
|
|
|
|
|
|
- `Opened` gains `Head`, `Verdicts`, `AreaLen` and `UnusableHeadExtensions`.
|
|
|
|
|
|
|
|
|
|
|
|
### Writer (spec §61, §62, §62.1)
|
|
|
|
|
|
|
|
|
|
|
|
- `capsule.EncryptFiles` writes format 3 from a list of `capsule.Source`,
|
|
|
|
|
|
each read twice: first to check the paths and the texts with the rules
|
|
|
|
|
|
of the reader, measure L with a head whose salt and SHA-256 are zero, and
|
|
|
|
|
|
hash each file; then to write it, failing if its size or SHA-256 changed
|
|
|
|
|
|
(rules 14 to 18). Files go in the byte order of their paths; the comment
|
|
|
|
|
|
has its CR LF turned into LF; the mtime is kept from 1970 to 9999 and
|
|
|
|
|
|
omitted otherwise. The head, the control and security are decoded with
|
|
|
|
|
|
the rules of the reader before anything is written (rule 17), and the
|
|
|
|
|
|
area is always 512 bytes with the empty security (rule 13).
|
|
|
|
|
|
- `EncryptOptions` gains `Comment`, `Author`, `HeadCritical`,
|
|
|
|
|
|
`HeadNoncritical` and `TestVectors`. `Encrypt` writes format 2 only with
|
|
|
|
|
|
`TestVectors`, for generators of test vectors (rule 1). `Result.Head` is
|
|
|
|
|
|
the head written.
|
|
|
|
|
|
|
|
|
|
|
|
### CLI
|
|
|
|
|
|
|
|
|
|
|
|
- `datekeys encrypt` takes `-in` several times, files and folders; a folder
|
|
|
|
|
|
gives its name as the first segment and is walked with `Lstat`, following
|
|
|
|
|
|
no link, taking regular files only and leaving out `.DS_Store`,
|
|
|
|
|
|
`Thumbs.db`, `desktop.ini`, `._*` and `__MACOSX`, which it reports. New
|
|
|
|
|
|
`-comment`, `-author` and `-no-mtime`. A pipe is no longer accepted.
|
|
|
|
|
|
- `datekeys decrypt` writes the files of a format 3 capsule to the new
|
|
|
|
|
|
folder `-out`: `os.Mkdir` claims it, only when there are files; the tree
|
|
|
|
|
|
is staged in `-out/.datekeys-*` through an `os.Root`, with `O_EXCL` and
|
|
|
|
|
|
mode 0600; the mtimes are set and each entry of the first level is moved
|
|
|
|
|
|
into place at step 18, and any failure removes the folder. Formats 1 and
|
|
|
|
|
|
2 still write a file.
|
|
|
|
|
|
- The presentation of spec §29.7 goes to stdout: the verdicts, the declared
|
|
|
|
|
|
author and the comment box with their labels, the paths, and the
|
|
|
|
|
|
verdicts again. Each line of the creator goes in pieces of at most W − 3
|
|
|
|
|
|
columns behind `│ `, counting 2 for anything but printable ASCII, with
|
|
|
|
|
|
its TABs expanded; W is the width of the terminal, asked through
|
|
|
|
|
|
`syscall` on Unix and Windows, or 80. Shortcuts, `desktop.ini`, `.git`,
|
|
|
|
|
|
programs and a leading dash get a warning.
|
|
|
|
|
|
|
|
|
|
|
|
### Errors
|
|
|
|
|
|
|
|
|
|
|
|
- `ERR_HEAD_INVALID` joins the catalogue, last (spec §69): 18 codes.
|
|
|
|
|
|
|
|
|
|
|
|
### Test data
|
|
|
|
|
|
|
|
|
|
|
|
- `datekeys.SpecVersion` is `0.10`, and every test data file says so.
|
|
|
|
|
|
- Nine fixtures of format 3 (spec §67): `format3_single`, `format3_tree`,
|
|
|
|
|
|
`format3_comment_only`, `format3_bloque256` and
|
|
|
|
|
|
`format3_time_and_key_portable`, written with `EncryptFiles`, and
|
|
|
|
|
|
`format3_area_1024`, `format3_security_v2`,
|
|
|
|
|
|
`format3_signature_unsupported` and `format3_seal_unsupported`, which only
|
|
|
|
|
|
a generator of test vectors writes. Their records add the area, security,
|
|
|
|
|
|
the head, the salt, the comment, the declared author, each file and the
|
|
|
|
|
|
verdicts, and their plaintext file is BODY. The .dkk of formats 2 and 3
|
|
|
|
|
|
join the .dkk tests (spec §68).
|
|
|
|
|
|
- New vectors: `paths.json`, `path_fold.json`, `head_schema.json` and
|
|
|
|
|
|
`security.json`; `cbor.json` gains the control of version 3.
|
|
|
|
|
|
- The mutation corpus gains the 33 mutations of the first two lists of
|
|
|
|
|
|
spec §64 on format 3 and the 47 of the list of format 3, three of which
|
|
|
|
|
|
open with their verdicts; format 2 gains "format 2 time_only relabeled
|
|
|
|
|
|
format 3", and "version changed" sets `VERSION` 4. 209 cases, 169 of the
|
|
|
|
|
|
spec. `testkit.Splice` gives an edit for each run of changed bytes.
|
|
|
|
|
|
- The differential corpus gains two bases of format 3, one per policy:
|
|
|
|
|
|
5110 cases, the earlier ones unchanged.
|
|
|
|
|
|
- The tests of the paths and of format 3 hold their invisible and combining
|
|
|
|
|
|
characters as Go escapes.
|
|
|
|
|
|
|
Implement capsule format 2 of spec v0.9
The reference moves to the DateKeys Protocol Specification v0.9, approved
by its author on 29 September 2026. Encrypt writes capsule format 2 only;
Open and Inspect read formats 1 and 2, and a format 1 capsule keeps the
verdict v0.8.2 gave it.
Format 2 (spec §22, §29.1, §31, §39):
- VERSION in the PRELUDE is the capsule format, capsule.Format; any other
value is ERR_UNSUPPORTED_VERSION at step 2.
- CONTROL_CBOR has the schema version of its format. Version 2 adds key 6,
payload_length (8 bytes, big-endian, at most L_MAX = 2^53 - 2^46), and
key 7, padding (1 bloque256, 2 reforzado); it is 103 bytes without
extensions, whatever L.
- The payload is the content padded with zeros to P = rule(L). Step 17
checks the length and the zeros, and Open writes only the first L bytes.
- INNER_ACCESS_AGE holds exactly 16 X25519 stanzas: 1 to 16 credentials,
and a dummy in each slot left, in a uniformly random order.
Writer rules (spec §62.1): EncryptOptions.Length is required and the
source must deliver exactly that many bytes; recipients that are not
canonical or of low order are rejected (agewrap.CheckX25519Recipient);
self-checks of the header, the control, INNER_ACCESS_AGE and PAYLOAD_AGE.
The CLI measures its input, takes -padding and reports the format.
Test data: seven format 2 fixtures, padding vectors checked against
math/big, format 2 CBOR vectors, and the mutation corpus in both formats
with the 22 cases of the third list of spec §64, built without randomness
by sealing the fixtures again with their known keys and nonces. The
format 1 fixtures are kept byte for byte and never regenerated; the
differential corpus keeps its 1825 cases and adds a block per format 2
fixture. The spec copy loses its "to be implemented" markers, and the
READMEs, CHANGELOG, traceability and testdata/README.md follow v0.9.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
|
## Unreleased — specification v0.9
|
|
|
|
|
|
|
|
|
|
|
|
Moves the module to the DateKeys Protocol Specification v0.9, which adds
|
|
|
|
|
|
capsule format 2 (spec §76, "Cambios normativos de la v0.9"): until the
|
|
|
|
|
|
unlock date it hides the exact length of the content and the number of
|
|
|
|
|
|
credentials. `Encrypt` writes format 2 only; `Open` and `Inspect` read both
|
|
|
|
|
|
formats, and a format 1 capsule, as v0.8.2 wrote it, keeps its verdict.
|
|
|
|
|
|
|
|
|
|
|
|
### Format 2
|
|
|
|
|
|
|
|
|
|
|
|
- `VERSION` in the PRELUDE is the capsule format, 1 or 2 (spec §22, §23):
|
|
|
|
|
|
`capsule.Format`, `Format1`, `Format2` and `Prelude.Format`. Any other
|
|
|
|
|
|
value is `ERR_UNSUPPORTED_VERSION` at step 2, before any request, so a
|
|
|
|
|
|
v0.8.2 reader rejects a format 2 capsule without a network request.
|
|
|
|
|
|
- CONTROL_CBOR has the schema version of its format (spec §31). Version 2
|
|
|
|
|
|
adds key 6, `payload_length`, L in exactly 8 bytes, big-endian, at most
|
|
|
|
|
|
L_MAX = 2^53 − 2^46, and key 7, `padding`, 1 (bloque256) or 2
|
|
|
|
|
|
(reforzado). A control of the other version is `ERR_UNSUPPORTED_VERSION`
|
|
|
|
|
|
at step 14; a violation of keys 6 and 7 is `ERR_NON_CANONICAL_CBOR`.
|
|
|
|
|
|
Without extensions a version 2 control is 103 bytes, whatever L and the
|
|
|
|
|
|
code. `EncodeControl` and `DecodeControl` take the format.
|
|
|
|
|
|
- The payload is padded (spec §29.1): its plaintext is the content followed
|
|
|
|
|
|
by zeros up to P = rule(L). `capsule.PaddedLength` computes both rules on
|
|
|
|
|
|
64-bit integers, and `capsule.PayloadAgeLength` the length of PAYLOAD_AGE.
|
|
|
|
|
|
Step 17 checks that the plaintext is exactly P bytes and its padding zero,
|
|
|
|
|
|
or `ERR_INTEGRITY`. `Open` writes only the first L bytes to `dst`, never
|
|
|
|
|
|
the padding (spec §56), records step 17, and reports the format, L, the
|
|
|
|
|
|
rule and P in `Opened`.
|
|
|
|
|
|
- In format 2 INNER_ACCESS_AGE holds exactly 16 X25519 stanzas (spec §39),
|
|
|
|
|
|
`agewrap.AccessSlots`: `agewrap.CheckAccessStanzas` and
|
|
|
|
|
|
`agewrap.NewAccessIdentity` take the number of slots, 0 for format 1.
|
|
|
|
|
|
Another number is `ERR_POLICY_STRUCTURE_MISMATCH` at step 12, and again at
|
|
|
|
|
|
step 13.
|
|
|
|
|
|
|
|
|
|
|
|
### Writer rules (spec §62.1)
|
|
|
|
|
|
|
|
|
|
|
|
- `EncryptOptions.Length`, L, is required: the control is sealed before the
|
|
|
|
|
|
content is read, and a source that delivers another number of bytes is an
|
|
|
|
|
|
error. `EncryptOptions.Padding` chooses the rule; zero means reforzado.
|
|
|
|
|
|
- `time_and_key` takes from 1 to 16 credentials, the recipients and the
|
|
|
|
|
|
portable key together. Each free slot gets a dummy, a fresh X25519 public
|
|
|
|
|
|
key whose private key is dropped at once, and the 16 recipients are
|
|
|
|
|
|
shuffled with an unbiased Fisher–Yates over `crypto/rand`.
|
|
|
|
|
|
- A recipient that is not canonical (bit 255 set, or u ≥ p) or of low order
|
|
|
|
|
|
is rejected: `agewrap.CheckX25519Recipient`.
|
|
|
|
|
|
- Self-checks: INNER_ACCESS_AGE has 16 X25519 stanzas with distinct shares
|
|
|
|
|
|
and the portable key opens exactly one; PAYLOAD_AGE has the length P gives
|
|
|
|
|
|
and I_PAYLOAD opens its header.
|
|
|
|
|
|
- `Result` reports the format, L, the rule and P.
|
|
|
|
|
|
|
|
|
|
|
|
### CLI
|
|
|
|
|
|
|
|
|
|
|
|
- `datekeys encrypt` measures its input, copying a file that is not regular
|
|
|
|
|
|
to a temporary file first, and takes `-padding reforzado|bloque256`. Its
|
|
|
|
|
|
report and that of `decrypt` give the format and the lengths, and `decrypt`
|
|
|
|
|
|
warns that format 1 hides neither the number of credentials nor the exact
|
|
|
|
|
|
length. `datekeys inspect` reports the format, `"format"` in `-json`.
|
|
|
|
|
|
|
|
|
|
|
|
### Test data
|
|
|
|
|
|
|
|
|
|
|
|
- Seven format 2 fixtures (spec §67), whose records give L, the rule, P and
|
|
|
|
|
|
the INNER_ACCESS_AGE stanza each credential opens. The five format 1
|
|
|
|
|
|
fixtures of v0.8.2 keep their bytes as compatibility fixtures, and
|
|
|
|
|
|
`genfixtures` never regenerates them; their records name spec 0.9, the
|
|
|
|
|
|
format and stage 17.
|
|
|
|
|
|
- `vectors/padding.json`, new: both rules for the rows of spec §29.1,
|
|
|
|
|
|
checked against a Padmé computed with `math/big`.
|
|
|
|
|
|
- `vectors/cbor.json`: the control vectors carry `format`, absent meaning 1,
|
|
|
|
|
|
and gain the cases of version 2; "unknown key 6" is now "key 6, defined
|
|
|
|
|
|
only in schema version 2".
|
|
|
|
|
|
- `vectors/mutations.json`: "version changed" writes `VERSION` 3; the 33
|
|
|
|
|
|
mutations of the first two lists of spec §64 also run on format 2 fixtures
|
|
|
|
|
|
("format 2: …"), followed by the 22 of its third list and their companions
|
|
|
|
|
|
with a `.dkk`. The new cases derive from fixtures without randomness:
|
|
|
|
|
|
`testkit` seals them again with their known file keys and nonces
|
|
|
|
|
|
(`reseal.go`), and reproduces each fixture byte for byte when nothing is
|
|
|
|
|
|
edited.
|
|
|
|
|
|
- `vectors/inspect_differential.json` keeps its 1 825 cases and adds a block
|
|
|
|
|
|
for each format 2 fixture: 4 380 cases.
|
|
|
|
|
|
|
|
|
|
|
|
## Unreleased — specification v0.8.2
|
|
|
|
|
|
|
Spec v0.8.2: second-round corrections from the formal review
The second round of the formal review confirmed the nine corrections of
c57ed48 and asked for these, recorded in §76 as corrections 4 to 6 and
an editorial note:
- §72: an encoder MUST NOT write a registered extension in an object or
array it is not registered for; §54: a reader MUST NOT interpret the
data of a noncritical one it ignores for that reason. capsule.Encrypt
and accesskey.Encode take no Registry, so the application applies the
rule; their documentation and extension.Placement say so.
- §17 and §51 give the step-10 codes only for a directly supplied
release, as step 10 does; a network source discards a failing one at
step 9.
- Step 9 reports ERR_RELEASE_UNAVAILABLE and no other code, whatever the
failure of the source. provider/drand.Client keeps each relay's failure
as text only (errors.Join made a relay's ERR_ROUND_MISMATCH match with
errors.Is), and capsule.Open keeps only the text of a source error that
carries another code (a caller's source failing with
ERR_RELEASE_INVALID gave that code at step 9). A context that ended
stays detectable: Fetch now has a single failure path, so the canceled
and deadline cases are deterministic.
- TestExtensionPlacement covers the noncritical array of a .dkk: with
the object-blind extension.CheckNoncritical at step 9.a it fails.
- Editorial: §28.1 "analizan solo la cabecera age", one arrow at step 9,
two §76 introductions; §73 lines for release sources and placement.
- testdata/README.md says the corpus registers its extensions in both
arrays of every object; traceability, CHANGELOG and both READMEs
(integrity holds against whoever lacks the file keys, §27, §55.1)
follow. Spec dated 28 September 2026; new SHA-256 in spec/README.md.
No fixture or vector changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
|
Moves the module to the DateKeys Protocol Specification v0.8.2, whose
|
Spec v0.8.2: corrections from the formal review
A formal review of the whole v0.8.2 text found it approvable after
these corrections, recorded in §76 ("Correcciones de la revisión
formal"):
- §27 no longer calls header_binding the authenticity of PUBLIC_HEADER:
it binds the header to the opened control, never authorship or date
(§55.1); the age MAC only protects against whoever lacks the file key.
- §63 steps 9 and 10: a network source (relay, Release API, cache) MUST
verify every response and gives ERR_RELEASE_UNAVAILABLE at step 9 when
none verifies; the step-10 codes are for a directly supplied release.
The reference already behaved so; TestReleaseFromANetworkSource pins
both paths.
- §54 and §72: registrations declare the objects and arrays where an
extension may appear, and a known extension out of place counts as
unknown there. The reference gains the optional extension.Placement
interface, used at steps 4, 9.a and 14.
- §63 step 11 fixes the GT serialization hashed by H2 (kilic/kyber order)
with the frozen vector H2(e(G1, G2))[:16] = cb87319f..., shared as
testdata/vectors/tlock_ibe.json; H2-H4 are cited to drand/kyber.
- Step 5 makes the SEALED_CONTROL read mandatory, step 15 names
ERR_HEADER_BINDING, §21 makes capsule_id 16 CSPRNG bytes a MUST, §76
is made accurate (four dk1.json vectors, the §36 time_only rule, two
cases rewritten against the texts that really existed), and editorial
fixes in §5, §36, §55.1, §69.1 and §77. §73 lists the three new
decisions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
|
normative change closes the extension format (spec §76), refined, amended and
|
|
|
|
|
|
corrected before release (see "Specification refinements", "Specification
|
|
|
|
|
|
amendment: point canonicality" and "Specification corrections: formal
|
|
|
|
|
|
review"). Framing and schema versions do not change.
|
|
|
|
|
|
|
|
|
|
|
|
The CBOR library is replaced by a codec of the module's own, without
|
|
|
|
|
|
reflection or dependencies. Every valid object encodes to the same bytes as
|
|
|
|
|
|
before: the official vectors and fixtures are unchanged, and every error code
|
|
|
|
|
|
and inspection step of the test suite and the mutation corpus is the same.
|
|
|
|
|
|
|
Spec v0.8.2 refinements: error precedence, trust model, strict order
Approved refinements, each recorded with its reproducible case in the
§76 v0.8.2 subsection:
- §69.1: layered error model with normative precedence (frame, type tag
and version, CBOR profile and CDDL, then fields with their own code in
ascending key order; across steps the §63 order decides), with a scope
paragraph for the optional steps 5, 6 and 8.
- §55.1: normative trust table per section (who can write it, from which
step it is bound, what it never proves); §72: security-relevant claims
go in CONTROL_CBOR or under a signature, .dkk data is advisory.
- §31/§54: extension arrays in strictly ascending unsigned byte order of
extension_id (one rule for order and uniqueness).
- Gaps a second implementation needed: §28.1 malformed age headers,
§15/§19 latest unlock time and dk1_ reading rules, §22/§23/§57 length
lower bounds, §63 step 8 tlock argument comparison and step 9 order,
§12.1 profile validation with the drand chain-hash formula, §74 table
of implementation limits.
Reference alignment: .dkk errors only at step 9.a (new
OpenOptions.AccessKeyFile, used by the CLI), CR/LF in dk1_ is
ERR_DATEKEY_INVALID, BODY_LEN 0 is ERR_INTEGRITY, nil identities are not
credentials, and AccessIdentity tries every identity on every stanza so
its verdict does not depend on their order. dk1.json gains three
vectors; every other testdata file is byte-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
|
### Specification refinements
|
|
|
|
|
|
|
|
|
|
|
|
v0.8.2 is unreleased, so these refinements amend it without a version change;
|
|
|
|
|
|
spec §76 records each with its reproducible cases. No valid object changes,
|
|
|
|
|
|
nor the verdict of any existing official vector or fixture; `dk1.json` gains
|
Spec v0.8.2: corrections from the formal review
A formal review of the whole v0.8.2 text found it approvable after
these corrections, recorded in §76 ("Correcciones de la revisión
formal"):
- §27 no longer calls header_binding the authenticity of PUBLIC_HEADER:
it binds the header to the opened control, never authorship or date
(§55.1); the age MAC only protects against whoever lacks the file key.
- §63 steps 9 and 10: a network source (relay, Release API, cache) MUST
verify every response and gives ERR_RELEASE_UNAVAILABLE at step 9 when
none verifies; the step-10 codes are for a directly supplied release.
The reference already behaved so; TestReleaseFromANetworkSource pins
both paths.
- §54 and §72: registrations declare the objects and arrays where an
extension may appear, and a known extension out of place counts as
unknown there. The reference gains the optional extension.Placement
interface, used at steps 4, 9.a and 14.
- §63 step 11 fixes the GT serialization hashed by H2 (kilic/kyber order)
with the frozen vector H2(e(G1, G2))[:16] = cb87319f..., shared as
testdata/vectors/tlock_ibe.json; H2-H4 are cited to drand/kyber.
- Step 5 makes the SEALED_CONTROL read mandatory, step 15 names
ERR_HEADER_BINDING, §21 makes capsule_id 16 CSPRNG bytes a MUST, §76
is made accurate (four dk1.json vectors, the §36 time_only rule, two
cases rewritten against the texts that really existed), and editorial
fixes in §5, §36, §55.1, §69.1 and §77. §73 lists the three new
decisions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
|
four vectors for the reading rules of §19.
|
Spec v0.8.2 refinements: error precedence, trust model, strict order
Approved refinements, each recorded with its reproducible case in the
§76 v0.8.2 subsection:
- §69.1: layered error model with normative precedence (frame, type tag
and version, CBOR profile and CDDL, then fields with their own code in
ascending key order; across steps the §63 order decides), with a scope
paragraph for the optional steps 5, 6 and 8.
- §55.1: normative trust table per section (who can write it, from which
step it is bound, what it never proves); §72: security-relevant claims
go in CONTROL_CBOR or under a signature, .dkk data is advisory.
- §31/§54: extension arrays in strictly ascending unsigned byte order of
extension_id (one rule for order and uniqueness).
- Gaps a second implementation needed: §28.1 malformed age headers,
§15/§19 latest unlock time and dk1_ reading rules, §22/§23/§57 length
lower bounds, §63 step 8 tlock argument comparison and step 9 order,
§12.1 profile validation with the drand chain-hash formula, §74 table
of implementation limits.
Reference alignment: .dkk errors only at step 9.a (new
OpenOptions.AccessKeyFile, used by the CLI), CR/LF in dk1_ is
ERR_DATEKEY_INVALID, BODY_LEN 0 is ERR_INTEGRITY, nil identities are not
credentials, and AccessIdentity tries every identity on every stanza so
its verdict does not depend on their order. dk1.json gains three
vectors; every other testdata file is byte-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
|
|
|
|
|
|
|
- Layered error precedence (spec §69.1, with §57 and §63): within one object
|
|
|
|
|
|
the code of the first failing layer is reported: frame, then type tag and
|
|
|
|
|
|
schema version (keys 0 and 1), then the CBOR profile and the CDDL, then the
|
|
|
|
|
|
fields with codes of their own in ascending key order; the rules with
|
|
|
|
|
|
codes of their own belong to the last layer, not to the CDDL layer. Across
|
|
|
|
|
|
objects and steps, the step order of §63 decides; step 9 now spells out its
|
|
|
|
|
|
order (the `.dkk` as an object, then its binding to the capsule, then
|
|
|
|
|
|
credentials, then the round time, then the request), and the errors of a
|
|
|
|
|
|
`.dkk` come at step 9.a even when it is decoded earlier, and never under
|
Spec v0.8.2: corrections from the formal review
A formal review of the whole v0.8.2 text found it approvable after
these corrections, recorded in §76 ("Correcciones de la revisión
formal"):
- §27 no longer calls header_binding the authenticity of PUBLIC_HEADER:
it binds the header to the opened control, never authorship or date
(§55.1); the age MAC only protects against whoever lacks the file key.
- §63 steps 9 and 10: a network source (relay, Release API, cache) MUST
verify every response and gives ERR_RELEASE_UNAVAILABLE at step 9 when
none verifies; the step-10 codes are for a directly supplied release.
The reference already behaved so; TestReleaseFromANetworkSource pins
both paths.
- §54 and §72: registrations declare the objects and arrays where an
extension may appear, and a known extension out of place counts as
unknown there. The reference gains the optional extension.Placement
interface, used at steps 4, 9.a and 14.
- §63 step 11 fixes the GT serialization hashed by H2 (kilic/kyber order)
with the frozen vector H2(e(G1, G2))[:16] = cb87319f..., shared as
testdata/vectors/tlock_ibe.json; H2-H4 are cited to drand/kyber.
- Step 5 makes the SEALED_CONTROL read mandatory, step 15 names
ERR_HEADER_BINDING, §21 makes capsule_id 16 CSPRNG bytes a MUST, §76
is made accurate (four dk1.json vectors, the §36 time_only rule, two
cases rewritten against the texts that really existed), and editorial
fixes in §5, §36, §55.1, §69.1 and §77. §73 lists the three new
decisions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
|
`time_only`. The optional inspection of steps 5, 6 and 8 and the
|
|
|
|
|
|
`capsule_digest` check can change the code, and so could, until the
|
|
|
|
|
|
corrections of the formal review, whether a network source verified the
|
|
|
|
|
|
release. Steps 10, 11, 13 and 17 give the codes of the release
|
|
|
|
|
|
verification (round, then signature) and of each identity.
|
Spec v0.8.2 refinements: error precedence, trust model, strict order
Approved refinements, each recorded with its reproducible case in the
§76 v0.8.2 subsection:
- §69.1: layered error model with normative precedence (frame, type tag
and version, CBOR profile and CDDL, then fields with their own code in
ascending key order; across steps the §63 order decides), with a scope
paragraph for the optional steps 5, 6 and 8.
- §55.1: normative trust table per section (who can write it, from which
step it is bound, what it never proves); §72: security-relevant claims
go in CONTROL_CBOR or under a signature, .dkk data is advisory.
- §31/§54: extension arrays in strictly ascending unsigned byte order of
extension_id (one rule for order and uniqueness).
- Gaps a second implementation needed: §28.1 malformed age headers,
§15/§19 latest unlock time and dk1_ reading rules, §22/§23/§57 length
lower bounds, §63 step 8 tlock argument comparison and step 9 order,
§12.1 profile validation with the drand chain-hash formula, §74 table
of implementation limits.
Reference alignment: .dkk errors only at step 9.a (new
OpenOptions.AccessKeyFile, used by the CLI), CR/LF in dk1_ is
ERR_DATEKEY_INVALID, BODY_LEN 0 is ERR_INTEGRITY, nil identities are not
credentials, and AccessIdentity tries every identity on every stanza so
its verdict does not depend on their order. dk1.json gains three
vectors; every other testdata file is byte-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
|
- Trust model (spec §55.1): who can write PUBLIC_HEADER, CONTROL_CBOR,
|
|
|
|
|
|
PAYLOAD_AGE and the `.dkk` body, from which step each is bound and by what,
|
|
|
|
|
|
and what none of them proves. Spec §72: an extension with security-relevant
|
|
|
|
|
|
claims lives in CONTROL_CBOR or is signed by a signature extension; the
|
|
|
|
|
|
data of `.dkk` extensions is advisory for its holder only.
|
|
|
|
|
|
- One ordering rule for extension arrays (spec §31, §54, CDDL): strictly
|
|
|
|
|
|
ascending unsigned bytewise order of the UTF-8 bytes of `extension_id`, a
|
|
|
|
|
|
proper prefix first, never UTF-16 code units or a locale collation.
|
|
|
|
|
|
- Rules that only `testdata/README.md` stated are now normative text: the age
|
|
|
|
|
|
header grammar of C2SP and its codes (§28.1, §36), the round-time bound of
|
|
|
|
|
|
9999-12-31T23:59:59Z and pre-genesis instants (§15), the reading rules of
|
Spec v0.8.2: corrections from the formal review
A formal review of the whole v0.8.2 text found it approvable after
these corrections, recorded in §76 ("Correcciones de la revisión
formal"):
- §27 no longer calls header_binding the authenticity of PUBLIC_HEADER:
it binds the header to the opened control, never authorship or date
(§55.1); the age MAC only protects against whoever lacks the file key.
- §63 steps 9 and 10: a network source (relay, Release API, cache) MUST
verify every response and gives ERR_RELEASE_UNAVAILABLE at step 9 when
none verifies; the step-10 codes are for a directly supplied release.
The reference already behaved so; TestReleaseFromANetworkSource pins
both paths.
- §54 and §72: registrations declare the objects and arrays where an
extension may appear, and a known extension out of place counts as
unknown there. The reference gains the optional extension.Placement
interface, used at steps 4, 9.a and 14.
- §63 step 11 fixes the GT serialization hashed by H2 (kilic/kyber order)
with the frozen vector H2(e(G1, G2))[:16] = cb87319f..., shared as
testdata/vectors/tlock_ibe.json; H2-H4 are cited to drand/kyber.
- Step 5 makes the SEALED_CONTROL read mandatory, step 15 names
ERR_HEADER_BINDING, §21 makes capsule_id 16 CSPRNG bytes a MUST, §76
is made accurate (four dk1.json vectors, the §36 time_only rule, two
cases rewritten against the texts that really existed), and editorial
fixes in §5, §36, §55.1, §69.1 and §77. §73 lists the three new
decisions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
|
`dk1_` (§19: either Base64 alphabet but no CR or LF, one JSON object in
|
|
|
|
|
|
valid UTF-8 without a byte order mark, numbers by their exact decimal
|
|
|
|
|
|
value), lengths
|
Spec v0.8.2 refinements: error precedence, trust model, strict order
Approved refinements, each recorded with its reproducible case in the
§76 v0.8.2 subsection:
- §69.1: layered error model with normative precedence (frame, type tag
and version, CBOR profile and CDDL, then fields with their own code in
ascending key order; across steps the §63 order decides), with a scope
paragraph for the optional steps 5, 6 and 8.
- §55.1: normative trust table per section (who can write it, from which
step it is bound, what it never proves); §72: security-relevant claims
go in CONTROL_CBOR or under a signature, .dkk data is advisory.
- §31/§54: extension arrays in strictly ascending unsigned byte order of
extension_id (one rule for order and uniqueness).
- Gaps a second implementation needed: §28.1 malformed age headers,
§15/§19 latest unlock time and dk1_ reading rules, §22/§23/§57 length
lower bounds, §63 step 8 tlock argument comparison and step 9 order,
§12.1 profile validation with the drand chain-hash formula, §74 table
of implementation limits.
Reference alignment: .dkk errors only at step 9.a (new
OpenOptions.AccessKeyFile, used by the CLI), CR/LF in dk1_ is
ERR_DATEKEY_INVALID, BODY_LEN 0 is ERR_INTEGRITY, nil identities are not
credentials, and AccessIdentity tries every identity on every stanza so
its verdict does not depend on their order. dk1.json gains three
vectors; every other testdata file is byte-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
|
of at least 1 (§22, §23, §40, §57), the comparison of the tlock stanza
|
|
|
|
|
|
arguments (§35, §63 step 8), the Provider Profile rules and the chain-hash
|
|
|
|
|
|
formula (§12.1, with `period` at most 2^32 − 1 and the name alphabets
|
|
|
|
|
|
normative; the name lengths stay implementation limits), `extension_id` of
|
|
|
|
|
|
at least one byte (§31), a repeated X25519 ephemeral share in
|
|
|
|
|
|
INNER_ACCESS_AGE (§36), and the implementation limits of the reference
|
|
|
|
|
|
(§74).
|
|
|
|
|
|
|
|
|
|
|
|
These error codes of the reference change, for inputs that no existing
|
|
|
|
|
|
official vector holds:
|
|
|
|
|
|
|
|
|
|
|
|
- `capsule.Open` checks an offered `.dkk` as an object before binding it to
|
|
|
|
|
|
the capsule: `access_type` and `access_material`, then its critical
|
|
|
|
|
|
extensions, then `capsule_id`, then `capsule_digest`. A `.dkk` for another
|
|
|
|
|
|
capsule with an unknown critical extension is now
|
|
|
|
|
|
`ERR_EXTENSION_CRITICAL_UNKNOWN` (was `ERR_ACCESS_INVALID`), and one with
|
|
|
|
|
|
an unsupported `access_type` and an unknown critical extension is
|
|
|
|
|
|
`ERR_ACCESS_INVALID` (was `ERR_EXTENSION_CRITICAL_UNKNOWN`).
|
|
|
|
|
|
- `accesskey.Decode` rejects a `BODY_LEN` of 0 with `ERR_INTEGRITY`, like a
|
|
|
|
|
|
`PUBLIC_HEADER_LEN` of 0; it was `ERR_NON_CANONICAL_CBOR`, the empty body
|
|
|
|
|
|
failing to decode.
|
|
|
|
|
|
- `datekey.Parse` rejects CR and LF in a `dk1_` string with
|
|
|
|
|
|
`ERR_DATEKEY_INVALID`; the Go Base64 decoders skipped them, and the
|
Spec v0.8.2: corrections from the formal review
A formal review of the whole v0.8.2 text found it approvable after
these corrections, recorded in §76 ("Correcciones de la revisión
formal"):
- §27 no longer calls header_binding the authenticity of PUBLIC_HEADER:
it binds the header to the opened control, never authorship or date
(§55.1); the age MAC only protects against whoever lacks the file key.
- §63 steps 9 and 10: a network source (relay, Release API, cache) MUST
verify every response and gives ERR_RELEASE_UNAVAILABLE at step 9 when
none verifies; the step-10 codes are for a directly supplied release.
The reference already behaved so; TestReleaseFromANetworkSource pins
both paths.
- §54 and §72: registrations declare the objects and arrays where an
extension may appear, and a known extension out of place counts as
unknown there. The reference gains the optional extension.Placement
interface, used at steps 4, 9.a and 14.
- §63 step 11 fixes the GT serialization hashed by H2 (kilic/kyber order)
with the frozen vector H2(e(G1, G2))[:16] = cb87319f..., shared as
testdata/vectors/tlock_ibe.json; H2-H4 are cited to drand/kyber.
- Step 5 makes the SEALED_CONTROL read mandatory, step 15 names
ERR_HEADER_BINDING, §21 makes capsule_id 16 CSPRNG bytes a MUST, §76
is made accurate (four dk1.json vectors, the §36 time_only rule, two
cases rewritten against the texts that really existed), and editorial
fixes in §5, §36, §55.1, §69.1 and §77. §73 lists the three new
decisions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
|
result was `ERR_DATEKEY_NON_CANONICAL`. It also rejects invalid UTF-8 in
|
|
|
|
|
|
the JSON at step 2 (see "Fixed").
|
Spec v0.8.2 refinements: error precedence, trust model, strict order
Approved refinements, each recorded with its reproducible case in the
§76 v0.8.2 subsection:
- §69.1: layered error model with normative precedence (frame, type tag
and version, CBOR profile and CDDL, then fields with their own code in
ascending key order; across steps the §63 order decides), with a scope
paragraph for the optional steps 5, 6 and 8.
- §55.1: normative trust table per section (who can write it, from which
step it is bound, what it never proves); §72: security-relevant claims
go in CONTROL_CBOR or under a signature, .dkk data is advisory.
- §31/§54: extension arrays in strictly ascending unsigned byte order of
extension_id (one rule for order and uniqueness).
- Gaps a second implementation needed: §28.1 malformed age headers,
§15/§19 latest unlock time and dk1_ reading rules, §22/§23/§57 length
lower bounds, §63 step 8 tlock argument comparison and step 9 order,
§12.1 profile validation with the drand chain-hash formula, §74 table
of implementation limits.
Reference alignment: .dkk errors only at step 9.a (new
OpenOptions.AccessKeyFile, used by the CLI), CR/LF in dk1_ is
ERR_DATEKEY_INVALID, BODY_LEN 0 is ERR_INTEGRITY, nil identities are not
credentials, and AccessIdentity tries every identity on every stanza so
its verdict does not depend on their order. dk1.json gains three
vectors; every other testdata file is byte-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
|
- `datekeys decrypt -dkk` hands the `.dkk` to `capsule.Open` still encoded,
|
|
|
|
|
|
through the new `OpenOptions.AccessKeyFile`, so its decoding errors come at
|
|
|
|
|
|
step 9 of a `time_and_key` capsule, after any failure of steps 1 to 8, and
|
|
|
|
|
|
a `time_only` capsule ignores it; the CLI used to fail on it before
|
|
|
|
|
|
reading the capsule.
|
|
|
|
|
|
- `capsule.Open` ignores nil entries of `OpenOptions.Identities`: with no
|
|
|
|
|
|
other credential, the result is `ERR_ACCESS_REQUIRED` at step 9, before
|
|
|
|
|
|
the clock is consulted, instead of `ERR_RELEASE_UNAVAILABLE` or a release
|
|
|
|
|
|
request.
|
|
|
|
|
|
- `agewrap.AccessIdentity` tries every identity on every stanza: an identity
|
|
|
|
|
|
that unwraps two INNER_ACCESS_AGE stanzas is
|
|
|
|
|
|
`ERR_POLICY_STRUCTURE_MISMATCH` even when another identity unwraps exactly
|
|
|
|
|
|
one, whatever their order; the file used to open when the other came
|
|
|
|
|
|
first.
|
|
|
|
|
|
- `profile.NewRegistry` encodes and decodes each profile before comparing its
|
|
|
|
|
|
`profile_hash`, so a profile gets the code `profile.Decode` reports: a
|
|
|
|
|
|
`period` of 86401 s is `ERR_NON_CANONICAL_CBOR`, as in `Decode`, not
|
|
|
|
|
|
`ERR_UNKNOWN_PROFILE`. `Profile.Validate` checks the schema rules a value
|
|
|
|
|
|
can break (a period that is not a whole number of seconds in 1..86400, a
|
|
|
|
|
|
genesis time outside 0..2^53−1, a name that is not valid UTF-8) first,
|
|
|
|
|
|
with `ERR_NON_CANONICAL_CBOR`.
|
|
|
|
|
|
|
|
|
|
|
|
Tests: `capsule.TestPrecedenceWithinPublicHeader`, `TestPrecedenceAcrossSteps`,
|
|
|
|
|
|
`TestFrameLengthLowerBounds`, `TestMalformedAgeHeaders`,
|
|
|
|
|
|
`TestTlockStanzaArgumentComparison`, `TestAccessKeyCheckOrder`,
|
|
|
|
|
|
`TestAccessKeyFileAtStep9`, `TestControlCriticalBeforeHeaderBinding` and
|
|
|
|
|
|
`TestTrustModel`; `accesskey.TestDecodePrecedence`;
|
|
|
|
|
|
`agewrap.TestAccessIdentityStrictness`; `datekey.TestReadingRules`;
|
|
|
|
|
|
`profile.TestDecodePrecedence` (a G1 point outside the prime-order
|
|
|
|
|
|
subgroup), `TestPinPathMatchesDecode` and `TestChainHashFormula`;
|
|
|
|
|
|
`provider.TestVerifyRejects`; `extension.TestOrderIsUnsignedBytewise`;
|
|
|
|
|
|
`cmd/datekeys.TestDecryptAccessKeyOrder`.
|
|
|
|
|
|
|
Spec v0.8.2 amendment: canonical point encoding; no library error text
Amendment of the unreleased v0.8.2, recorded in §76 with its case: the
second implementation's phase-2 research found that tlock-js over
@noble/curves 1.9.7 accepts U re-encoded as c0 + p and a signature
x + p and returns the same file key, while the reference rejects both
(noble 1.9.7 differed from kilic on 5,615 of 41,686 encodings), and the
spec did not say which encodings are valid.
- §12.2 defines the canonical encoding of a BLS12-381 point (drand's
compressed ZCash form) and requires decoders to reject every other
byte string; §12.1 applies it to public_key.
- §63 step 10 applies it to the release signature (ERR_RELEASE_INVALID)
and step 11 defines the tlock stanza body U || V || W (96 + 16 + 16
bytes for Quicknet) with a canonical, non-infinity U (ERR_INTEGRITY).
- §64 gains ten mutations, exported to mutations.json (65 cases). The
signature x + p case uses published Quicknet round 1004, the first
after 1000 whose x allows x + p < 2^381. The reference already gave
every stated code and step.
Errors no longer copy text from tlock, kyber, age, drand or
kyber-bls12381. kyber's IBE error carried the candidate plaintext and r,
and with one bit of W flipped the message disclosed the real tlock file
key with that bit flipped. Every such place now uses a fixed reason with
its normative sentinel; TestTlockFailureDiagnosticsCarryNoSecrets fails
with the old wrapping.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
|
### Specification amendment: point canonicality
|
|
|
|
|
|
|
|
|
|
|
|
An amendment of the unreleased v0.8.2, recorded with its case in spec §76.
|
|
|
|
|
|
The new §12.2 defines the canonical encoding of a BLS12-381 point, the
|
|
|
|
|
|
compressed form of drand: the compression flag set, the infinity flag only for
|
|
|
|
|
|
the point at infinity with every other bit zero, the sort flag for the
|
|
|
|
|
|
lexicographically largest y, big-endian coordinates below p (c1 then c0 in
|
|
|
|
|
|
G2) and a point of the prime-order subgroup. A decoder rejects every other
|
|
|
|
|
|
string, among them x + p and an identity with a payload. The Provider Profile
|
|
|
|
|
|
public key (§12.1), the release signature (§63 step 10) and the U of the
|
|
|
|
|
|
tlock stanza (§63 step 11) are canonical and never the point at infinity, and
|
|
|
|
|
|
step 11 defines the stanza body, `U || V || W` with |V| = |W| = 16 (128 bytes
|
|
|
|
|
|
for Quicknet), and the IBE check r·G == U. The case, from the second
|
|
|
|
|
|
implementation: `tlock-js` on `@noble/curves` 1.9.7 accepted U re-encoded as
|
|
|
|
|
|
c0 + p and a signature re-encoded as x + p and returned the same file key,
|
|
|
|
|
|
where the reference rejects both.
|
|
|
|
|
|
|
|
|
|
|
|
No error code or step of the reference changes: its decoder,
|
|
|
|
|
|
`kilic/bls12-381` through drand, already rejected those encodings. A U at
|
|
|
|
|
|
infinity, which only the IBE check used to reject, is refused before
|
|
|
|
|
|
decryption, with the same `ERR_INTEGRITY`. Spec §64 gains ten mutations,
|
|
|
|
|
|
exported in `mutations.json`: U with c0 + p, U at infinity, U with the
|
|
|
|
|
|
infinity flag and a payload, and tlock stanza bodies of 127 and 129 bytes,
|
|
|
|
|
|
each with a valid header MAC (`ERR_INTEGRITY`, step 11); a release signature
|
|
|
|
|
|
with x + p, at infinity, with the infinity flag and a payload, negated, and
|
|
|
|
|
|
negated together with U with c0 + p (`ERR_RELEASE_INVALID`, step 10). The
|
|
|
|
|
|
x + p case is a capsule for round 1004, whose published signature `testkit`
|
|
|
|
|
|
now knows (`testkit.XPlusPRound`): no fixture round has an x below
|
|
|
|
|
|
2^381 − p. No fixture and no other vector changes.
|
|
|
|
|
|
|
|
|
|
|
|
Tests: `profile.TestDrandPointDecodersAreCanonical`, which fails if a
|
|
|
|
|
|
dependency update makes the decoders of drand lenient, and
|
|
|
|
|
|
`TestPublicKeyEncodingIsCanonical`; new cases in `provider.TestVerifyRejects`,
|
|
|
|
|
|
`agewrap.TestTimeIdentityStrictness` and `TestTimeIdentityRelease`;
|
|
|
|
|
|
`capsule.TestPointMutationsChangeOnlyTheEncoding`, which checks that each
|
|
|
|
|
|
point mutation differs from a capsule that opens only in one encoding;
|
|
|
|
|
|
`internal/testkit.TestPointReencodings`.
|
|
|
|
|
|
|
Spec v0.8.2: corrections from the formal review
A formal review of the whole v0.8.2 text found it approvable after
these corrections, recorded in §76 ("Correcciones de la revisión
formal"):
- §27 no longer calls header_binding the authenticity of PUBLIC_HEADER:
it binds the header to the opened control, never authorship or date
(§55.1); the age MAC only protects against whoever lacks the file key.
- §63 steps 9 and 10: a network source (relay, Release API, cache) MUST
verify every response and gives ERR_RELEASE_UNAVAILABLE at step 9 when
none verifies; the step-10 codes are for a directly supplied release.
The reference already behaved so; TestReleaseFromANetworkSource pins
both paths.
- §54 and §72: registrations declare the objects and arrays where an
extension may appear, and a known extension out of place counts as
unknown there. The reference gains the optional extension.Placement
interface, used at steps 4, 9.a and 14.
- §63 step 11 fixes the GT serialization hashed by H2 (kilic/kyber order)
with the frozen vector H2(e(G1, G2))[:16] = cb87319f..., shared as
testdata/vectors/tlock_ibe.json; H2-H4 are cited to drand/kyber.
- Step 5 makes the SEALED_CONTROL read mandatory, step 15 names
ERR_HEADER_BINDING, §21 makes capsule_id 16 CSPRNG bytes a MUST, §76
is made accurate (four dk1.json vectors, the §36 time_only rule, two
cases rewritten against the texts that really existed), and editorial
fixes in §5, §36, §55.1, §69.1 and §77. §73 lists the three new
decisions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
|
### Specification corrections: formal review
|
|
|
|
|
|
|
|
|
|
|
|
Corrections of the unreleased v0.8.2 from the formal review of the
|
|
|
|
|
|
specification, recorded with their cases in spec §76 ("Correcciones de la
|
Spec v0.8.2: second-round corrections from the formal review
The second round of the formal review confirmed the nine corrections of
c57ed48 and asked for these, recorded in §76 as corrections 4 to 6 and
an editorial note:
- §72: an encoder MUST NOT write a registered extension in an object or
array it is not registered for; §54: a reader MUST NOT interpret the
data of a noncritical one it ignores for that reason. capsule.Encrypt
and accesskey.Encode take no Registry, so the application applies the
rule; their documentation and extension.Placement say so.
- §17 and §51 give the step-10 codes only for a directly supplied
release, as step 10 does; a network source discards a failing one at
step 9.
- Step 9 reports ERR_RELEASE_UNAVAILABLE and no other code, whatever the
failure of the source. provider/drand.Client keeps each relay's failure
as text only (errors.Join made a relay's ERR_ROUND_MISMATCH match with
errors.Is), and capsule.Open keeps only the text of a source error that
carries another code (a caller's source failing with
ERR_RELEASE_INVALID gave that code at step 9). A context that ended
stays detectable: Fetch now has a single failure path, so the canceled
and deadline cases are deterministic.
- TestExtensionPlacement covers the noncritical array of a .dkk: with
the object-blind extension.CheckNoncritical at step 9.a it fails.
- Editorial: §28.1 "analizan solo la cabecera age", one arrow at step 9,
two §76 introductions; §73 lines for release sources and placement.
- testdata/README.md says the corpus registers its extensions in both
arrays of every object; traceability, CHANGELOG and both READMEs
(integrity holds against whoever lacks the file keys, §27, §55.1)
follow. Spec dated 28 September 2026; new SHA-256 in spec/README.md.
No fixture or vector changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
|
revisión formal"). Three change normative rules:
|
Spec v0.8.2: corrections from the formal review
A formal review of the whole v0.8.2 text found it approvable after
these corrections, recorded in §76 ("Correcciones de la revisión
formal"):
- §27 no longer calls header_binding the authenticity of PUBLIC_HEADER:
it binds the header to the opened control, never authorship or date
(§55.1); the age MAC only protects against whoever lacks the file key.
- §63 steps 9 and 10: a network source (relay, Release API, cache) MUST
verify every response and gives ERR_RELEASE_UNAVAILABLE at step 9 when
none verifies; the step-10 codes are for a directly supplied release.
The reference already behaved so; TestReleaseFromANetworkSource pins
both paths.
- §54 and §72: registrations declare the objects and arrays where an
extension may appear, and a known extension out of place counts as
unknown there. The reference gains the optional extension.Placement
interface, used at steps 4, 9.a and 14.
- §63 step 11 fixes the GT serialization hashed by H2 (kilic/kyber order)
with the frozen vector H2(e(G1, G2))[:16] = cb87319f..., shared as
testdata/vectors/tlock_ibe.json; H2-H4 are cited to drand/kyber.
- Step 5 makes the SEALED_CONTROL read mandatory, step 15 names
ERR_HEADER_BINDING, §21 makes capsule_id 16 CSPRNG bytes a MUST, §76
is made accurate (four dk1.json vectors, the §36 time_only rule, two
cases rewritten against the texts that really existed), and editorial
fixes in §5, §36, §55.1, §69.1 and §77. §73 lists the three new
decisions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
|
|
|
|
|
|
|
- A source that fetches releases over a network (a relay, the Release API or
|
|
|
|
|
|
a cache) must verify every response with the rules of step 10 and discard
|
|
|
|
|
|
the one that fails; when none passes, the code is `ERR_RELEASE_UNAVAILABLE`
|
|
|
|
|
|
at step 9 (spec §63). The codes of step 10 are those of a release supplied
|
|
|
|
|
|
directly, as in the official vectors. `provider/drand.Client` already did
|
|
|
|
|
|
this; the contract of `provider.ReleaseSource` and the documentation of
|
|
|
|
|
|
`capsule.OpenOptions.Source` now say so.
|
|
|
|
|
|
- Each registered extension declares the objects (PUBLIC_HEADER,
|
|
|
|
|
|
CONTROL_CBOR, `.dkk`) and the arrays where it may appear, and a known
|
|
|
|
|
|
extension elsewhere is treated as unknown there (spec §31, §54, §72): a
|
|
|
|
|
|
critical one is `ERR_EXTENSION_CRITICAL_UNKNOWN`, a noncritical one is
|
|
|
|
|
|
ignored. The new optional interface `extension.Placement` of a `Registry`
|
|
|
|
|
|
tells where each extension is registered; `capsule.Inspect` and
|
|
|
|
|
|
`capsule.Open` check the arrays of PUBLIC_HEADER (step 4), of the `.dkk`
|
|
|
|
|
|
(step 9.a) and of CONTROL_CBOR (step 14) with the new
|
|
|
|
|
|
`extension.CheckCriticalIn` and `extension.CheckNoncriticalIn`. A
|
|
|
|
|
|
`Registry` that does not implement it behaves as before.
|
|
|
|
|
|
- H2, H3 and H4 of step 11 are those of drand/kyber `encrypt/ibe`, and H2
|
|
|
|
|
|
hashes the element of GT in the order of `kilic/bls12-381`: c1 before c0 at
|
|
|
|
|
|
every level of the tower, each coordinate of Fp in 48 bytes big-endian. The
|
|
|
|
|
|
new vector file `testdata/vectors/tlock_ibe.json` freezes H2(e(G1, G2)) =
|
|
|
|
|
|
`cb87319f24560b5231579a09ad79f12e`; the order of `Fp12.toBytes` of noble,
|
|
|
|
|
|
c0 first, gives `0118eea9d5971745f71e3c94926f1717` and another file key.
|
|
|
|
|
|
|
|
|
|
|
|
The other findings are editorial: §27 defers the authenticity of
|
|
|
|
|
|
PUBLIC_HEADER to the trust model (§55.1) and says that the age header MAC
|
|
|
|
|
|
protects only against whoever does not know the file key; step 5 separates
|
|
|
|
|
|
the mandatory read of SEALED_CONTROL from the optional inspection of its age
|
|
|
|
|
|
header; step 15 names `ERR_HEADER_BINDING`; §21 makes the 16 CSPRNG bytes of
|
|
|
|
|
|
`capsule_id` a MUST; §77 gains drand/kyber, RFC 8259, RFC 8610 and RFC 4648;
|
|
|
|
|
|
§76 retitles its section "Cambios normativos de la v0.8.2" and corrects its
|
|
|
|
|
|
record: `dk1.json` gained four vectors, not three, and two cases of the
|
|
|
|
|
|
refinements quoted texts that no earlier version contained.
|
|
|
|
|
|
|
|
|
|
|
|
No error code of the reference changes, nor any fixture or existing vector.
|
|
|
|
|
|
`github.com/drand/kyber-bls12381`, already an indirect dependency through
|
|
|
|
|
|
drand and tlock, becomes a direct requirement: `internal/testkit` and a test
|
|
|
|
|
|
compute the pairing with it.
|
|
|
|
|
|
|
|
|
|
|
|
Tests: `capsule.TestReleaseFromANetworkSource` (a relay whose only answer is
|
|
|
|
|
|
a release of another round, or a negated signature, gives
|
|
|
|
|
|
`ERR_RELEASE_UNAVAILABLE` at step 9 through `provider/drand.Client`, and the
|
|
|
|
|
|
same release supplied directly the code of step 10);
|
|
|
|
|
|
`capsule.TestExtensionPlacement` (an extension registered for CONTROL_CBOR
|
|
|
|
|
|
only is unknown in PUBLIC_HEADER and in a `.dkk`, one registered as
|
|
|
|
|
|
noncritical only is unknown in a critical array, and a noncritical copy
|
|
|
|
|
|
outside its registration is ignored); `extension.TestPlacement`;
|
|
|
|
|
|
`agewrap.TestTlockH2Vector` (the frozen vector, and step 11 recomputed with
|
|
|
|
|
|
H2 and H4 against the file key that tlock unwraps).
|
|
|
|
|
|
|
Spec v0.8.2: second-round corrections from the formal review
The second round of the formal review confirmed the nine corrections of
c57ed48 and asked for these, recorded in §76 as corrections 4 to 6 and
an editorial note:
- §72: an encoder MUST NOT write a registered extension in an object or
array it is not registered for; §54: a reader MUST NOT interpret the
data of a noncritical one it ignores for that reason. capsule.Encrypt
and accesskey.Encode take no Registry, so the application applies the
rule; their documentation and extension.Placement say so.
- §17 and §51 give the step-10 codes only for a directly supplied
release, as step 10 does; a network source discards a failing one at
step 9.
- Step 9 reports ERR_RELEASE_UNAVAILABLE and no other code, whatever the
failure of the source. provider/drand.Client keeps each relay's failure
as text only (errors.Join made a relay's ERR_ROUND_MISMATCH match with
errors.Is), and capsule.Open keeps only the text of a source error that
carries another code (a caller's source failing with
ERR_RELEASE_INVALID gave that code at step 9). A context that ended
stays detectable: Fetch now has a single failure path, so the canceled
and deadline cases are deterministic.
- TestExtensionPlacement covers the noncritical array of a .dkk: with
the object-blind extension.CheckNoncritical at step 9.a it fails.
- Editorial: §28.1 "analizan solo la cabecera age", one arrow at step 9,
two §76 introductions; §73 lines for release sources and placement.
- testdata/README.md says the corpus registers its extensions in both
arrays of every object; traceability, CHANGELOG and both READMEs
(integrity holds against whoever lacks the file keys, §27, §55.1)
follow. Spec dated 28 September 2026; new SHA-256 in spec/README.md.
No fixture or vector changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
|
A second round of the same review confirmed these corrections and asked for
|
|
|
|
|
|
three more, recorded in spec §76 as corrections 4 to 6:
|
|
|
|
|
|
|
|
|
|
|
|
- An encoder must not write a registered extension in an object or array it
|
|
|
|
|
|
is not registered for, and a reader that ignores a noncritical one for that
|
|
|
|
|
|
reason must not interpret its data (spec §54, §72). A writer could seal in
|
|
|
|
|
|
the critical extensions of CONTROL_CBOR one registered there as
|
|
|
|
|
|
noncritical only, which a reader that knows the registration rejects at
|
|
|
|
|
|
step 14, after the unlock. `capsule.Encrypt` and `accesskey.Encode` take
|
|
|
|
|
|
no `Registry`: they write the extensions they are given, and the
|
|
|
|
|
|
application, which knows the registration, applies the rule.
|
|
|
|
|
|
- §17 and §51 give the codes of step 10 only for a release supplied
|
|
|
|
|
|
directly, as step 10 does; one that a network source fetches and that
|
|
|
|
|
|
breaks those rules is discarded at step 9.
|
|
|
|
|
|
- Whatever the failure of the release source, step 9 reports
|
|
|
|
|
|
`ERR_RELEASE_UNAVAILABLE` and no other code (spec §63).
|
|
|
|
|
|
|
|
|
|
|
|
The other findings are editorial: §28.1 says that steps 5 and 6 parse only
|
|
|
|
|
|
the age header, since step 5 reads all of SEALED_CONTROL; step 9 has a single
|
|
|
|
|
|
arrow to `ERR_RELEASE_UNAVAILABLE`; §76 rewords two introductions.
|
|
|
|
|
|
`testdata/README.md` says where the extensions of the mutation corpus are
|
|
|
|
|
|
registered: in both arrays of every object.
|
|
|
|
|
|
|
|
|
|
|
|
These error results of the reference change, for inputs that no official
|
|
|
|
|
|
vector holds:
|
|
|
|
|
|
|
|
|
|
|
|
- `capsule.Open` reports any error of `OpenOptions.Source` at step 9 with
|
|
|
|
|
|
`ERR_RELEASE_UNAVAILABLE` as its only code. An error that carries another
|
|
|
|
|
|
normative code, or none, keeps only its text: a caller's source that
|
|
|
|
|
|
failed with `ERR_RELEASE_INVALID` gave `ERR_RELEASE_INVALID` at step 9. An
|
|
|
|
|
|
error that wraps `ERR_RELEASE_UNAVAILABLE` alone is returned as it is, with
|
|
|
|
|
|
its other causes, such as a context error.
|
|
|
|
|
|
- The error of `provider/drand.Client.Fetch` wraps `ERR_RELEASE_UNAVAILABLE`
|
|
|
|
|
|
and no other normative error, as `errors.go` promises for every error of
|
|
|
|
|
|
the module. The failure of each relay is kept in its text only, where
|
|
|
|
|
|
`errors.Join` wrapped it: a relay that answered with a release of another
|
|
|
|
|
|
round also made the error match `ERR_ROUND_MISMATCH`. A context that ended
|
|
|
|
|
|
is still wrapped, whether `Fetch` sees it end before or after the relays
|
|
|
|
|
|
fail because of it.
|
|
|
|
|
|
|
|
|
|
|
|
Tests: `capsule.TestReleaseSourceErrorsAtStep9` (a source error with another
|
|
|
|
|
|
code, two codes or none gives `ERR_RELEASE_UNAVAILABLE` alone at step 9 and
|
|
|
|
|
|
keeps its text); `capsule.TestExtensionPlacement`, which now also covers the
|
|
|
|
|
|
noncritical array of a `.dkk`, so that checking it with the object-blind
|
|
|
|
|
|
`extension.CheckNoncritical` fails; `provider/drand.TestFetchErrorHasOneCode`
|
|
|
|
|
|
and `TestUnavailabilityAndCancellation` (a canceled context and a deadline
|
|
|
|
|
|
during the request); `datekeys.TestCode`.
|
|
|
|
|
|
|
|
|
|
|
|
### Breaking changes
|
|
|
|
|
|
|
|
|
|
|
|
- `extension.New(id, version, data []byte)` takes the opaque data bytes instead
|
|
|
|
|
|
of a value that it encoded as CBOR, and rejects nil or empty data. An
|
|
|
|
|
|
extension without data is the literal `extension.Extension{ID, Version}`,
|
|
|
|
|
|
which omits key 2.
|
|
|
|
|
|
- Extension data (key 2) must be a byte string of at least one byte. Any other
|
|
|
|
|
|
CBOR type, `null` or `h''` at key 2 is now `ERR_NON_CANONICAL_CBOR`, so a
|
|
|
|
|
|
v0.8.1 object with such data no longer decodes. The base protocol never
|
|
|
|
|
|
decodes the content (§54).
|
|
|
|
|
|
- `codec.Valid` and its fuzz target `codec.FuzzValid` are removed: nothing
|
|
|
|
|
|
decodes extension data any more.
|
|
|
|
|
|
- Package `codec` is rewritten without reflection, struct tags or dependencies
|
|
|
|
|
|
(§58). Removed: `Marshal`, the reflection-based `Unmarshal(data, v)` and
|
|
|
|
|
|
`Peek(data, v)`, and `MaxNestedLevels`, `MaxArrayElements` and
|
|
|
|
|
|
`MaxMapPairs`. Each schema now writes its encoding with a `codec.Encoder`
|
|
|
|
|
|
(`Map`, `Array`, `Uint`, `Bstr`, `Text`, `Out`, with a sticky first error,
|
|
|
|
|
|
and `Fail`, which records an error of the schema so that `Out` never returns
|
|
|
|
|
|
bytes its decoder rejects) and reads it with a strict `codec.Decoder`
|
|
|
|
|
|
(`NewDecoder`, `Map`, `Key`, `EndMap`, `Array`, `Uint`, `Bstr`, `Text`,
|
|
|
|
|
|
`Done`). `codec.Unmarshal(in, decode, encode)` runs the decoder of a schema
|
|
|
|
|
|
and requires that its re-encoding reproduces the input; `codec.Peek(in)`
|
|
|
|
|
|
returns the type tag, of at most `codec.MaxTypeTagLen` (64) bytes, and the
|
|
|
|
|
|
schema version; `codec.Walk(in, maxDepth, maxLen)` checks that bytes are one
|
|
|
|
|
|
item of the §58 profile, for vectors, fuzzing and diagnostics.
|
|
|
|
|
|
`CheckSchema` and `MaxSafeUint` keep their names.
|
|
|
|
|
|
- `extension.Wire` and its `UnmarshalCBOR` are removed. `extension.Encode`
|
|
|
|
|
|
becomes `extension.Canonical`, which returns the validated array in
|
|
|
|
|
|
canonical order as `[]Extension`; `extension.Decode([]Wire)` becomes
|
|
|
|
|
|
`extension.DecodeArray(*codec.Decoder)`, which reads and validates one array
|
|
|
|
|
|
and rejects more than 64 entries from the array head, before reading any;
|
|
|
|
|
|
`extension.EncodeArray(*codec.Encoder, []Extension)` writes one, and
|
|
|
|
|
|
records in the Encoder, instead of writing it, an array that `DecodeArray`
|
|
|
|
|
|
would reject.
|
|
|
|
|
|
- `codec.CheckSchema` reads keys 0 and 1 only, and nothing after them (§70):
|
|
|
|
|
|
the map head, key 0, a type tag of at most 64 bytes, key 1 and the version
|
|
|
|
|
|
must be in the profile, each head in its shortest form, and the version at
|
|
|
|
|
|
most 2^53−1. A schema version other than the expected one read that way is
|
|
|
|
|
|
`ERR_UNSUPPORTED_VERSION` whatever follows it; it was
|
|
|
|
|
|
`ERR_NON_CANONICAL_CBOR` when the rest of the object was malformed. Every
|
|
|
|
|
|
other form of the version is now `ERR_NON_CANONICAL_CBOR`, where it was
|
|
|
|
|
|
`ERR_UNSUPPORTED_VERSION` whenever the value read was not the expected one:
|
|
|
|
|
|
a missing version, `null` or `undefined`, a version not in its shortest
|
|
|
|
|
|
form, a version above 2^53−1 (up to 2^64−1), a version that is not the
|
|
|
|
|
|
second key (placed before key 0 or after another key), and a version
|
|
|
|
|
|
behind a map head or a type tag head not in its shortest form. `true` and
|
|
|
|
|
|
`false` were already `ERR_NON_CANONICAL_CBOR`.
|
|
|
|
|
|
- `capsule.DecodeHeader` checks every CDDL rule of PUBLIC_HEADER, including
|
|
|
|
|
|
`access_policy`, the extension arrays and the cross-array rule, before it
|
|
|
|
|
|
parses the DateKey (§57, §63 step 4). A header that breaks both reports
|
|
|
|
|
|
`ERR_NON_CANONICAL_CBOR` where it reported `ERR_DATEKEY_INVALID` or
|
|
|
|
|
|
`ERR_DATEKEY_NON_CANONICAL`; a header with one fault keeps its code.
|
|
|
|
|
|
- `profile.Profile.CanonicalCBOR` and `Hash` refuse a `profile_id`,
|
|
|
|
|
|
`provider`, `network` or `scheme` that is not valid UTF-8, with
|
|
|
|
|
|
`ERR_NON_CANONICAL_CBOR`: they wrote it as an invalid text string. Every
|
|
|
|
|
|
encoder refuses such text.
|
|
|
|
|
|
- At most 64 extensions per array and `extension_version` at most 2^32−1, on
|
|
|
|
|
|
encode and decode (`ERR_NON_CANONICAL_CBOR`). An `extension_id` appears at
|
|
|
|
|
|
most once per object, and arrays are ordered by `extension_id` only.
|
|
|
|
|
|
- Provider Profile: `genesis_time` is an unsigned integer, and `period` and
|
|
|
|
|
|
`genesis_time` are at most 2^53−1; a negative or larger value is
|
|
|
|
|
|
`ERR_NON_CANONICAL_CBOR`.
|
|
|
|
|
|
- `null` in a byte-string field is `ERR_NON_CANONICAL_CBOR` (for example a
|
|
|
|
|
|
`null` `access_material` was `ERR_ACCESS_INVALID`): nil byte strings, arrays
|
|
|
|
|
|
and maps now encode as empty ones, never as `null`.
|
|
|
|
|
|
- `capsule.EncodeHeader` and `capsule.DecodeHeader` enforce the 1 MiB
|
|
|
|
|
|
PUBLIC_HEADER limit and `accesskey.DecodeBody` the 16 MiB BODY limit. Every
|
|
|
|
|
|
frame-limit refusal, including those of `accesskey.MarshalBody` and of
|
|
|
|
|
|
`capsule.Encrypt` for SEALED_CONTROL, now wraps `ERR_INTEGRITY` (§57).
|
|
|
|
|
|
- `profile.Profile.CanonicalCBOR` refuses a `period` or `genesis_time` outside
|
|
|
|
|
|
the schema with `ERR_NON_CANONICAL_CBOR`, as `profile.Decode` does.
|
|
|
|
|
|
- The official fixture `time_only_extensions` is regenerated: its header data
|
|
|
|
|
|
is the raw UTF-8 bytes of "public label" and its control data is
|
|
|
|
|
|
`{0: 7, 1: "sealed"}` (`a2000701667365616c6564`). Every other `.dkc` and
|
|
|
|
|
|
`.dkk` keeps its bytes; the fixture and vector metadata name spec 0.8.2.
|
|
|
|
|
|
|
|
|
|
|
|
### Added
|
|
|
|
|
|
|
Version constants: the specification and the module
- datekeys.SpecVersion ("0.8.2") names the specification the module
implements. A test ties it to the spec file, its title and
spec/README.md, and TestCatalogueMatchesSpec and the vector files use
it (testkit.SpecVersion now aliases it), so the vectors regenerate
unchanged.
- datekeys.Version() is the version of the module as the go command
recorded it. That is a tag, or for a binary built in a checkout the
pseudo-version of its commit (for example
v0.0.0-20260928105528-9ac9cd952f04), or (devel) when it is unknown, as
in tests or under a replace directive to a directory. It works as the
main module and as a dependency, whatever the module path, which it
reads from the root package.
- `datekeys version` (also -version and --version) prints both and the
Go toolchain.
- README.md and README.es.md explain the three versions (format,
specification, module) and what the code on main covers.
traceability §70 and CHANGELOG follow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
|
- `datekeys.SpecVersion` (`0.8.2`), the specification the module implements,
|
|
|
|
|
|
and `datekeys.Version()`, the version of the module as the go command
|
|
|
|
|
|
recorded it: a tag, the pseudo-version of the commit of a checkout build,
|
|
|
|
|
|
or `(devel)`. `datekeys version` prints both and the Go toolchain.
|
Spec v0.8.2 refinements: error precedence, trust model, strict order
Approved refinements, each recorded with its reproducible case in the
§76 v0.8.2 subsection:
- §69.1: layered error model with normative precedence (frame, type tag
and version, CBOR profile and CDDL, then fields with their own code in
ascending key order; across steps the §63 order decides), with a scope
paragraph for the optional steps 5, 6 and 8.
- §55.1: normative trust table per section (who can write it, from which
step it is bound, what it never proves); §72: security-relevant claims
go in CONTROL_CBOR or under a signature, .dkk data is advisory.
- §31/§54: extension arrays in strictly ascending unsigned byte order of
extension_id (one rule for order and uniqueness).
- Gaps a second implementation needed: §28.1 malformed age headers,
§15/§19 latest unlock time and dk1_ reading rules, §22/§23/§57 length
lower bounds, §63 step 8 tlock argument comparison and step 9 order,
§12.1 profile validation with the drand chain-hash formula, §74 table
of implementation limits.
Reference alignment: .dkk errors only at step 9.a (new
OpenOptions.AccessKeyFile, used by the CLI), CR/LF in dk1_ is
ERR_DATEKEY_INVALID, BODY_LEN 0 is ERR_INTEGRITY, nil identities are not
credentials, and AccessIdentity tries every identity on every stanza so
its verdict does not depend on their order. dk1.json gains three
vectors; every other testdata file is byte-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
|
- `capsule.OpenOptions.AccessKeyFile`, a `.dkk` still encoded, which `Open`
|
|
|
|
|
|
decodes at step 9.a and only for a `time_and_key` capsule (§63, §69.1).
|
|
|
|
|
|
- `ErrExtensionDataInvalid` (`ERR_EXTENSION_DATA_INVALID`, §69).
|
Spec v0.8.2: corrections from the formal review
A formal review of the whole v0.8.2 text found it approvable after
these corrections, recorded in §76 ("Correcciones de la revisión
formal"):
- §27 no longer calls header_binding the authenticity of PUBLIC_HEADER:
it binds the header to the opened control, never authorship or date
(§55.1); the age MAC only protects against whoever lacks the file key.
- §63 steps 9 and 10: a network source (relay, Release API, cache) MUST
verify every response and gives ERR_RELEASE_UNAVAILABLE at step 9 when
none verifies; the step-10 codes are for a directly supplied release.
The reference already behaved so; TestReleaseFromANetworkSource pins
both paths.
- §54 and §72: registrations declare the objects and arrays where an
extension may appear, and a known extension out of place counts as
unknown there. The reference gains the optional extension.Placement
interface, used at steps 4, 9.a and 14.
- §63 step 11 fixes the GT serialization hashed by H2 (kilic/kyber order)
with the frozen vector H2(e(G1, G2))[:16] = cb87319f..., shared as
testdata/vectors/tlock_ibe.json; H2-H4 are cited to drand/kyber.
- Step 5 makes the SEALED_CONTROL read mandatory, step 15 names
ERR_HEADER_BINDING, §21 makes capsule_id 16 CSPRNG bytes a MUST, §76
is made accurate (four dk1.json vectors, the §36 time_only rule, two
cases rewritten against the texts that really existed), and editorial
fixes in §5, §36, §55.1, §69.1 and §77. §73 lists the three new
decisions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
|
- `extension.Placement`, an optional interface of a `Registry` that tells in
|
|
|
|
|
|
which objects and arrays each extension is registered, with
|
|
|
|
|
|
`extension.Object` (`PublicHeader`, `Control`, `AccessKey`),
|
|
|
|
|
|
`extension.Array` (`Critical`, `Noncritical`), `extension.KnownIn`,
|
|
|
|
|
|
`extension.CheckCriticalIn` and `extension.CheckNoncriticalIn` (§54, §72).
|
|
|
|
|
|
`CheckCritical` and `CheckNoncritical`, which do not know the object, keep
|
|
|
|
|
|
their behaviour and consult no `Placement`.
|
|
|
|
|
|
- `testdata/vectors/tlock_ibe.json`, the H2 vector of §63 step 11, generated
|
|
|
|
|
|
by `internal/testkit.IBEVectors` and documented in `testdata/README.md`.
|
|
|
|
|
|
- `extension.DataValidator`, an optional interface of a `Registry` that
|
|
|
|
|
|
validates the data of the extensions it knows: a known critical extension
|
|
|
|
|
|
with invalid data fails with `ErrExtensionDataInvalid` (§63 steps 4 and 14,
|
|
|
|
|
|
and the `.dkk` check); a known noncritical one is reported in
|
|
|
|
|
|
`capsule.Inspection.UnusableExtensions`, `capsule.Opened.UnusableControlExtensions`
|
|
|
|
|
|
or `capsule.Opened.UnusableAccessKeyExtensions` (`extension.CheckNoncritical`,
|
|
|
|
|
|
`extension.Unusable`) and does not fail.
|
|
|
|
|
|
- Encoder self-checks: `capsule.Encrypt` decodes its PUBLIC_HEADER and
|
|
|
|
|
|
CONTROL_CBOR, and `accesskey.MarshalBody` its body, with the readers'
|
|
|
|
|
|
decoders before sealing or writing (§72).
|
|
|
|
|
|
- `extension.MaxExtensions`, `MaxVersion`, `MaxDataLen`, `codec.MaxSafeUint`
|
|
|
|
|
|
and `codec.MaxTypeTagLen`.
|
|
|
|
|
|
- The `.dkk` fixture `time_and_key_portable_extension`, which carries a
|
|
|
|
|
|
noncritical extension with data (§68).
|
|
|
|
|
|
- `genfixtures -only NAME[,NAME...]` regenerates the named fixtures only.
|
|
|
|
|
|
- Tests: the three new §64 mutations (data that is not a byte string, `h''`
|
|
|
|
|
|
data, 65 extensions), regression tests for the cases of §76, conformance
|
|
|
|
|
|
checks on the exact data bytes, and the fuzz target
|
|
|
|
|
|
`capsule.FuzzEncodeImpliesDecode` (header, control and `.dkk`).
|
|
|
|
|
|
- The mutation corpus moves from `capsule/mutation_test.go` to
|
|
|
|
|
|
`internal/testkit.Mutations`, shared by the test and by `genfixtures`. Its
|
|
|
|
|
|
third-party X25519 identity is now fixed (`testkit.Stranger`), and every
|
|
|
|
|
|
release source answers with one recorded release, as the exported corpus
|
|
|
|
|
|
describes it. The CLI's inspect view moves to `internal/inspectview`, which
|
|
|
|
|
|
`genfixtures` uses to freeze the outputs.
|
|
|
|
|
|
- `internal/cbortest`, an encoder and decoder of generic CBOR values written
|
|
|
|
|
|
independently of `codec`: tests build with it inputs outside the profile
|
|
|
|
|
|
and check `codec` against it.
|
|
|
|
|
|
- Tests of the map structure of every schema (key order, required and unknown
|
|
|
|
|
|
keys, entry counts) and of the codec, whose statement coverage is 100 %.
|
|
|
|
|
|
- `access_policy` values whose low byte is 0 or 1 (256, 257, 65536, 2^32,
|
|
|
|
|
|
2^53−256…) are tested as undefined, as `FuzzDecodeHeader` seeds and as two
|
|
|
|
|
|
mutations with a consistent `header_binding` (`testkit.Build.RawPolicy`).
|
|
|
|
|
|
- Tests `extension.TestEncodeArrayRejects`,
|
|
|
|
|
|
`accesskey.TestEncodeAndDecodeLeaveNoStaleMaterial`,
|
|
|
|
|
|
`accesskey.TestDecodeShortBodyAllocatesLittle` and `capsule.TestDecryptAll`.
|
|
|
|
|
|
- Shared test data for a second implementation, generated by `genfixtures`,
|
|
|
|
|
|
regenerated by the gate and documented in `testdata/README.md`:
|
|
|
|
|
|
- `testdata/vectors/cbor.json`: 36 accepted and 67 rejected items of the
|
|
|
|
|
|
§58 profile, walked with `codec.Walk` (integers above 2^53−1 as decimal
|
|
|
|
|
|
strings), and 135 schema vectors: minimal valid object, unknown key,
|
|
|
|
|
|
missing key, wrong type, size and range for the Provider Profile,
|
|
|
|
|
|
PUBLIC_HEADER, CONTROL_CBOR, the `.dkk` body, `verification_metadata` and
|
|
|
|
|
|
extensions (data `40` and `5801xx`, data of every other type, 64 and 65
|
|
|
|
|
|
extensions, a leading BOM, the U+FF61/U+10000 order, `extension_version`
|
|
|
|
|
|
2^32−1 and 2^32); schema versions 2^53 and 2^64−1 and the order of type
|
|
|
|
|
|
tag and version; and the Provider Profile validation (names, public key,
|
|
|
|
|
|
`genesis_time`, drand scheme, the chain-hash self-check with its formula,
|
|
|
|
|
|
the `period` limit), each vector keeping the chain hash consistent unless
|
|
|
|
|
|
it tests the self-check.
|
Spec v0.8.2 amendment: canonical point encoding; no library error text
Amendment of the unreleased v0.8.2, recorded in §76 with its case: the
second implementation's phase-2 research found that tlock-js over
@noble/curves 1.9.7 accepts U re-encoded as c0 + p and a signature
x + p and returns the same file key, while the reference rejects both
(noble 1.9.7 differed from kilic on 5,615 of 41,686 encodings), and the
spec did not say which encodings are valid.
- §12.2 defines the canonical encoding of a BLS12-381 point (drand's
compressed ZCash form) and requires decoders to reject every other
byte string; §12.1 applies it to public_key.
- §63 step 10 applies it to the release signature (ERR_RELEASE_INVALID)
and step 11 defines the tlock stanza body U || V || W (96 + 16 + 16
bytes for Quicknet) with a canonical, non-infinity U (ERR_INTEGRITY).
- §64 gains ten mutations, exported to mutations.json (65 cases). The
signature x + p case uses published Quicknet round 1004, the first
after 1000 whose x allows x + p < 2^381. The reference already gave
every stated code and step.
Errors no longer copy text from tlock, kyber, age, drand or
kyber-bls12381. kyber's IBE error carried the candidate plaintext and r,
and with one bit of W flipped the message disclosed the real tlock file
key with that bit flipped. Every such place now uses a fixed reason with
its normative sentinel; TestTlockFailureDiagnosticsCarryNoSecrets fails
with the old wrapping.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
|
- `testdata/vectors/mutations.json`: the mutation corpus as frozen data, 65
|
|
|
|
|
|
cases (the 33 of §64 first), each a `.dkc` given as edits of a fixture and
|
|
|
|
|
|
what the reader is given (`.dkk`, identities, the recorded release, clock,
|
Spec v0.8.2 amendment: canonical point encoding; no library error text
Amendment of the unreleased v0.8.2, recorded in §76 with its case: the
second implementation's phase-2 research found that tlock-js over
@noble/curves 1.9.7 accepts U re-encoded as c0 + p and a signature
x + p and returns the same file key, while the reference rejects both
(noble 1.9.7 differed from kilic on 5,615 of 41,686 encodings), and the
spec did not say which encodings are valid.
- §12.2 defines the canonical encoding of a BLS12-381 point (drand's
compressed ZCash form) and requires decoders to reject every other
byte string; §12.1 applies it to public_key.
- §63 step 10 applies it to the release signature (ERR_RELEASE_INVALID)
and step 11 defines the tlock stanza body U || V || W (96 + 16 + 16
bytes for Quicknet) with a canonical, non-infinity U (ERR_INTEGRITY).
- §64 gains ten mutations, exported to mutations.json (65 cases). The
signature x + p case uses published Quicknet round 1004, the first
after 1000 whose x allows x + p < 2^381. The reference already gave
every stated code and step.
Errors no longer copy text from tlock, kyber, age, drand or
kyber-bls12381. kyber's IBE error carried the candidate plaintext and r,
and with one bit of W flipped the message disclosed the real tlock file
key with that bit flipped. Every such place now uses a fixed reason with
its normative sentinel; TestTlockFailureDiagnosticsCarryNoSecrets fails
with the old wrapping.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
|
registry, known extensions), with the expected error and step. The 17
|
|
|
|
|
|
capsules built with age randomness are kept from the committed file;
|
|
|
|
|
|
`genfixtures -only mutations` rebuilds them.
|
|
|
|
|
|
- `testdata/vectors/inspect_differential.json`: 1825 deterministic mutations
|
|
|
|
|
|
of the five `.dkc` fixtures (bit flips, byte changes, truncations,
|
|
|
|
|
|
insertions, deletions, length fields, CBOR-aware header edits, DateKey
|
|
|
|
|
|
edits, age header edits) with the verdict of steps 1 to 8.
|
|
|
|
|
|
- `testdata/fixtures/<name>.inspect.json`: the exact output of
|
|
|
|
|
|
`datekeys inspect -json -in <name>.dkc` for each official capsule.
|
Spec v0.8.2 refinements: error precedence, trust model, strict order
Approved refinements, each recorded with its reproducible case in the
§76 v0.8.2 subsection:
- §69.1: layered error model with normative precedence (frame, type tag
and version, CBOR profile and CDDL, then fields with their own code in
ascending key order; across steps the §63 order decides), with a scope
paragraph for the optional steps 5, 6 and 8.
- §55.1: normative trust table per section (who can write it, from which
step it is bound, what it never proves); §72: security-relevant claims
go in CONTROL_CBOR or under a signature, .dkk data is advisory.
- §31/§54: extension arrays in strictly ascending unsigned byte order of
extension_id (one rule for order and uniqueness).
- Gaps a second implementation needed: §28.1 malformed age headers,
§15/§19 latest unlock time and dk1_ reading rules, §22/§23/§57 length
lower bounds, §63 step 8 tlock argument comparison and step 9 order,
§12.1 profile validation with the drand chain-hash formula, §74 table
of implementation limits.
Reference alignment: .dkk errors only at step 9.a (new
OpenOptions.AccessKeyFile, used by the CLI), CR/LF in dk1_ is
ERR_DATEKEY_INVALID, BODY_LEN 0 is ERR_INTEGRITY, nil identities are not
credentials, and AccessIdentity tries every identity on every stanza so
its verdict does not depend on their order. dk1.json gains three
vectors; every other testdata file is byte-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
|
- `testdata/README.md` points to the sections of the specification that
|
|
|
|
|
|
decide each verdict (§12.1, §15, §19, §22, §23, §28.1, §63, §69.1, §74),
|
|
|
|
|
|
which the refinements above moved into normative text.
|
|
|
|
|
|
- `spec/datekeys.cddl` marks the one-day `period` limit of the Provider
|
|
|
|
|
|
Profile, which `profile.Decode` already applied, as an implementation limit
|
|
|
|
|
|
of the reference (§57, §74); spec §11 allows up to 2^53−1.
|
|
|
|
|
|
- Tests that replay them: `codec.TestSharedVectors`,
|
|
|
|
|
|
`internal/testkit.TestSchemaVectors`, `capsule.TestExportedMutationCorpus`,
|
|
|
|
|
|
`capsule.TestInspectDifferentialCorpus` and
|
|
|
|
|
|
`cmd/datekeys.TestInspectJSONGoldens`.
|
|
|
|
|
|
- Fuzz targets `codec.FuzzDecoder` (the Decoder primitives),
|
|
|
|
|
|
`codec.FuzzWalk` (against the independent decoder), `codec.FuzzPeek`,
|
|
|
|
|
|
`codec.FuzzEncodeImpliesWalk` and `extension.FuzzDecodeArray`, run by
|
|
|
|
|
|
`scripts/fuzz.sh`; `codec.FuzzUnmarshal` now fuzzes a hand-written schema.
|
|
|
|
|
|
|
|
|
|
|
|
### Removed
|
|
|
|
|
|
|
|
|
|
|
|
- The dependencies `github.com/fxamacker/cbor/v2` and
|
|
|
|
|
|
`github.com/x448/float16`. `go.mod` requires nothing new.
|
|
|
|
|
|
|
|
|
|
|
|
### Fixed
|
|
|
|
|
|
|
Spec v0.8.2 amendment: canonical point encoding; no library error text
Amendment of the unreleased v0.8.2, recorded in §76 with its case: the
second implementation's phase-2 research found that tlock-js over
@noble/curves 1.9.7 accepts U re-encoded as c0 + p and a signature
x + p and returns the same file key, while the reference rejects both
(noble 1.9.7 differed from kilic on 5,615 of 41,686 encodings), and the
spec did not say which encodings are valid.
- §12.2 defines the canonical encoding of a BLS12-381 point (drand's
compressed ZCash form) and requires decoders to reject every other
byte string; §12.1 applies it to public_key.
- §63 step 10 applies it to the release signature (ERR_RELEASE_INVALID)
and step 11 defines the tlock stanza body U || V || W (96 + 16 + 16
bytes for Quicknet) with a canonical, non-infinity U (ERR_INTEGRITY).
- §64 gains ten mutations, exported to mutations.json (65 cases). The
signature x + p case uses published Quicknet round 1004, the first
after 1000 whose x allows x + p < 2^381. The reference already gave
every stated code and step.
Errors no longer copy text from tlock, kyber, age, drand or
kyber-bls12381. kyber's IBE error carried the candidate plaintext and r,
and with one bit of W flipped the message disclosed the real tlock file
key with that bit flipped. Every such place now uses a fixed reason with
its normative sentinel; TestTlockFailureDiagnosticsCarryNoSecrets fails
with the old wrapping.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
|
- Error messages no longer copy the text of an error of age, tlock, kyber,
|
|
|
|
|
|
drand or kyber-bls12381. When the IBE check of a tlock stanza failed,
|
|
|
|
|
|
kyber's error carried the candidate plaintext and r, and
|
|
|
|
|
|
`agewrap.TimeIdentity` copied it into its error, and so into the error of
|
|
|
|
|
|
`capsule.Open` and the details of `Inspection.Checks`: a third party who
|
|
|
|
|
|
edited W learned FK_TIME from the message. Each such failure now has a
|
|
|
|
|
|
fixed message with its normative error and a reason of its own: the length
|
|
|
|
|
|
of the tlock stanza body, the encoding of U, U at infinity or the IBE check;
|
|
|
|
|
|
the header or the STREAM of an age file; a malformed X25519 stanza. A
|
|
|
|
|
|
failure of the writer of the plaintext at step 17 keeps its own text.
|
|
|
|
|
|
`capsule.TestTlockFailureDiagnosticsCarryNoSecrets`,
|
|
|
|
|
|
`TestPlaintextWriterFailureKeepsItsText`.
|
|
|
|
|
|
- `datekey.Parse` rejects invalid UTF-8 in the `dk1_` JSON at step 2, as
|
|
|
|
|
|
§19 requires. `encoding/json` replaced it with U+FFFD, so a member that a
|
|
|
|
|
|
repeated name overwrites passed steps 2 and 3 and ended as
|
|
|
|
|
|
`ERR_DATEKEY_NON_CANONICAL` instead of `ERR_DATEKEY_INVALID`. Found by the
|
|
|
|
|
|
second implementation's differential; new `dk1.json` vector.
|
|
|
|
|
|
- `extension.CheckDisjoint` is a linear merge of the two sorted arrays; a
|
|
|
|
|
|
PUBLIC_HEADER with 40 000 + 40 000 extensions took 8.3 s in the pairwise
|
|
|
|
|
|
check (§76, case 6).
|
|
|
|
|
|
- Control data made of 14 or 15 nested arrays was sealed by `Encrypt` and
|
|
|
|
|
|
rejected by `Open` at step 14, after the unlock (§76, case 5).
|
|
|
|
|
|
- Header data `{NaN: 0, NaN: 1}` gave a nondeterministic verdict (§76, case 3).
|
|
|
|
|
|
- `extension.New(id, v, nil)` wrote `null` as data (§76, case 2).
|
|
|
|
|
|
- `codec.Unmarshal` wipes its re-encoding, which after the new self-checks
|
|
|
|
|
|
held a copy of I_PAYLOAD or `access_material`, and `accesskey.DecodeBody`
|
|
|
|
|
|
wipes the material on its error paths. The `codec.Encoder` also wipes every
|
|
|
|
|
|
buffer it outgrows, and `codec.Unmarshal` sizes its re-encoding for the
|
|
|
|
|
|
input; the former library's internal buffers could keep a copy.
|
|
|
|
|
|
- `accesskey.Encode` wipes the body it wrote, and `accesskey.Decode` reads the
|
|
|
|
|
|
body into a buffer that grows with the data read, wiping every buffer it
|
|
|
|
|
|
outgrows, and wipes the body once decoded or on error: both left copies of
|
|
|
|
|
|
`access_material` behind. The in-memory age decryption of SEALED_CONTROL and
|
|
|
|
|
|
INNER_ACCESS_AGE in `capsule.Open` reads the plaintext into one buffer of
|
|
|
|
|
|
the ciphertext's size instead of a growing one, so that no outgrown buffer
|
|
|
|
|
|
keeps a copy of I_PAYLOAD. Buffers internal to `filippo.io/age` and copies
|
|
|
|
|
|
made by the Go runtime stay out of reach (SECURITY.md).
|
|
|
|
|
|
|
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
|
|
|
|
## Unreleased — v0.1.0
|
|
|
|
|
|
|
|
|
|
|
|
First implementation of the DateKeys Protocol Specification v0.8.1.
|
|
|
|
|
|
|
|
|
|
|
|
### Added
|
|
|
|
|
|
|
|
|
|
|
|
- `datekey`: local date → round resolution at full precision (§15), canonical
|
|
|
|
|
|
`dk1_` encoding and strict parsing (§18, §19).
|
|
|
|
|
|
- `profile`: Provider Profile Deterministic CBOR and `profile_hash` (§11), the
|
|
|
|
|
|
pinned Quicknet profile with its chain-hash self-check (§12), and pinned
|
|
|
|
|
|
registries (§13).
|
|
|
|
|
|
- `provider`: release sources and local BLS verification (§51);
|
|
|
|
|
|
`provider/drand`: racing public relays, verifying every answer (§48, §49, §52).
|
|
|
|
|
|
- `codec`: Deterministic CBOR with a re-encoding canonicality check (§58, §58.1).
|
|
|
|
|
|
- `extension`: the generic extension mechanism (§54).
|
|
|
|
|
|
- `agewrap`: strict tlock and X25519 age identities that enforce the stanza
|
|
|
|
|
|
rules (§27, §29, §32, §33, §35), and a secret-free header probe.
|
|
|
|
|
|
- `capsule`: `.dkc` framing, `Encrypt` for `time_only` and `time_and_key`
|
|
|
|
|
|
(§61, §62), `Inspect` (§63 steps 1–8) and `Open` (§63 steps 9–18).
|
|
|
|
|
|
- `accesskey`: `.dkk` encoding and decoding (§40–§44).
|
|
|
|
|
|
- `cmd/datekeys`: `encrypt`, `decrypt`, `inspect`, `datekey resolve`,
|
|
|
|
|
|
`profile hash`, with atomic, non-overwriting outputs.
|
|
|
|
|
|
- Official vectors (§65, §66), `.dkc`/`.dkk` fixtures (§67, §68), the mutation
|
|
|
|
|
|
corpus (§64), fuzz targets for every parser, interoperability tests with the
|
|
|
|
|
|
official `age` and `tle` CLIs, and live Quicknet integration tests.
|
|
|
|
|
|
- `spec/datekeys.cddl` and `docs/traceability.md`.
|