Format 3, step 6a: specification 0.10 and ERR_HEAD_INVALID

- SpecVersion is 0.10: the version command, the catalogue test and the
  spec field of every test data file name spec v0.10. The regenerated
  test data change in that field only.
- All lists ERR_HEAD_INVALID, last, as section 69 of the spec does.
- The tests of the path rules and of format 3 held literal invisible
  and combining characters (ZWJ, VS16, U+202E, soft hyphen, the Kelvin
  sign and others), which an editor could normalize or hide; they are
  Go escapes now, with the same values.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.10
dev 1 week ago
parent a0de80fd85
commit 3c3933627d

@ -135,7 +135,7 @@ func TestSecurityVerdicts(t *testing.T) {
func sampleHead() *capsule.Head { func sampleHead() *capsule.Head {
h := &capsule.Head{ h := &capsule.Head{
Comment: "Para ti ❤️", Comment: "Para ti ❤\ufe0f",
Author: "Ana López", Author: "Ana López",
Files: []capsule.File{ Files: []capsule.File{
{Path: "fotos/playa.jpg", Size: 10, Start: 0, End: 10, MTime: 1759190400, HasMTime: true}, {Path: "fotos/playa.jpg", Size: 10, Start: 0, End: 10, MTime: 1759190400, HasMTime: true},
@ -240,19 +240,19 @@ func TestDecodeHeadLayers(t *testing.T) {
"an unknown key 8": {head(t, map[uint64]any{8: uint64(1)}), datekeys.ErrNonCanonicalCBOR}, "an unknown key 8": {head(t, map[uint64]any{8: uint64(1)}), datekeys.ErrNonCanonicalCBOR},
"a byte more": {append(head(t, nil), 0), datekeys.ErrNonCanonicalCBOR}, "a byte more": {append(head(t, nil), 0), datekeys.ErrNonCanonicalCBOR},
// Layer 4, in key order. // Layer 4, in key order.
"a comment with U+202E": {head(t, map[uint64]any{3: "a‮b"}), datekeys.ErrHeadInvalid}, "a comment with U+202E": {head(t, map[uint64]any{3: "a\u202eb"}), datekeys.ErrHeadInvalid},
"a comment with the tag U+E0041": {head(t, map[uint64]any{3: "a\U000E0041"}), datekeys.ErrHeadInvalid}, "a comment with the tag U+E0041": {head(t, map[uint64]any{3: "a\U000E0041"}), datekeys.ErrHeadInvalid},
"an author with LF": {head(t, map[uint64]any{4: "a\nb"}), datekeys.ErrHeadInvalid}, "an author with LF": {head(t, map[uint64]any{4: "a\nb"}), datekeys.ErrHeadInvalid},
"R3: ..": {head(t, map[uint64]any{5: []any{file("..", 0, 0, 0)}}), datekeys.ErrHeadInvalid}, "R3: ..": {head(t, map[uint64]any{5: []any{file("..", 0, 0, 0)}}), datekeys.ErrHeadInvalid},
"R2: /a": {head(t, map[uint64]any{5: []any{file("/a", 0, 0, 0)}}), datekeys.ErrHeadInvalid}, "R2: /a": {head(t, map[uint64]any{5: []any{file("/a", 0, 0, 0)}}), datekeys.ErrHeadInvalid},
"R4b: a and VS16": {head(t, map[uint64]any{5: []any{file("a️", 0, 0, 0)}}), datekeys.ErrHeadInvalid}, "R4b: a and VS16": {head(t, map[uint64]any{5: []any{file("a\ufe0f", 0, 0, 0)}}), datekeys.ErrHeadInvalid},
"R6: CON.txt": {head(t, map[uint64]any{5: []any{file("CON.txt", 0, 0, 0)}}), datekeys.ErrHeadInvalid}, "R6: CON.txt": {head(t, map[uint64]any{5: []any{file("CON.txt", 0, 0, 0)}}), datekeys.ErrHeadInvalid},
"R10: .datekeys-x": {head(t, map[uint64]any{5: []any{file(".datekeys-x", 0, 0, 0)}}), datekeys.ErrHeadInvalid}, "R10: .datekeys-x": {head(t, map[uint64]any{5: []any{file(".datekeys-x", 0, 0, 0)}}), datekeys.ErrHeadInvalid},
"layout: a first start that is not 0": {head(t, map[uint64]any{5: []any{file("a", 1, 1, 2)}}), datekeys.ErrHeadInvalid}, "layout: a first start that is not 0": {head(t, map[uint64]any{5: []any{file("a", 1, 1, 2)}}), datekeys.ErrHeadInvalid},
"layout: end minus start is not size": {head(t, map[uint64]any{5: []any{file("a", 2, 0, 1)}}), datekeys.ErrHeadInvalid}, "layout: end minus start is not size": {head(t, map[uint64]any{5: []any{file("a", 2, 0, 1)}}), datekeys.ErrHeadInvalid},
"layout: a gap": {head(t, map[uint64]any{5: []any{file("a", 1, 0, 1), file("b", 1, 2, 3)}}), datekeys.ErrHeadInvalid}, "layout: a gap": {head(t, map[uint64]any{5: []any{file("a", 1, 0, 1), file("b", 1, 2, 3)}}), datekeys.ErrHeadInvalid},
"R7: A.txt and a.txt": {head(t, map[uint64]any{5: []any{file("A.txt", 0, 0, 0), file("a.txt", 0, 0, 0)}}), datekeys.ErrHeadInvalid}, "R7: A.txt and a.txt": {head(t, map[uint64]any{5: []any{file("A.txt", 0, 0, 0), file("a.txt", 0, 0, 0)}}), datekeys.ErrHeadInvalid},
"a comment and a path that break": {head(t, map[uint64]any{3: "a‮", 5: []any{file("..", 0, 0, 0)}}), datekeys.ErrHeadInvalid}, "a comment and a path that break": {head(t, map[uint64]any{3: "a\u202e", 5: []any{file("..", 0, 0, 0)}}), datekeys.ErrHeadInvalid},
"a path that breaks, then an unknown critical extension": {head(t, map[uint64]any{5: []any{file("..", 0, 0, 0)}, 6: []any{map[uint64]any{0: "x.example", 1: uint64(1)}}}), datekeys.ErrHeadInvalid}, "a path that breaks, then an unknown critical extension": {head(t, map[uint64]any{5: []any{file("..", 0, 0, 0)}, 6: []any{map[uint64]any{0: "x.example", 1: uint64(1)}}}), datekeys.ErrHeadInvalid},
"an unknown critical extension": {head(t, map[uint64]any{6: []any{map[uint64]any{0: "x.example", 1: uint64(1)}}}), datekeys.ErrExtensionCriticalUnknown}, "an unknown critical extension": {head(t, map[uint64]any{6: []any{map[uint64]any{0: "x.example", 1: uint64(1)}}}), datekeys.ErrExtensionCriticalUnknown},
} { } {
@ -269,10 +269,10 @@ func TestDecodeHeadLayers(t *testing.T) {
} }
} }
// codes counts the normative errors err wraps, ERR_HEAD_INVALID included. // codes counts the normative errors err wraps.
func codes(err error) int { func codes(err error) int {
n := 0 n := 0
for _, e := range append(datekeys.All(), datekeys.ErrHeadInvalid) { for _, e := range datekeys.All() {
if errors.Is(err, e) { if errors.Is(err, e) {
n++ n++
} }

@ -110,7 +110,7 @@ func TestOpen3(t *testing.T) {
c := capsule3{ c := capsule3{
paths: []string{"fotos/playa.jpg", "nota.txt", "vacío.txt"}, paths: []string{"fotos/playa.jpg", "nota.txt", "vacío.txt"},
contents: [][]byte{photo, []byte("Hola.\n"), {}}, contents: [][]byte{photo, []byte("Hola.\n"), {}},
comment: "Para ti ❤️", author: "Ana López", comment: "Para ti ❤\ufe0f", author: "Ana López",
} }
dkc, body := c.build(t) dkc, body := c.build(t)

@ -73,8 +73,7 @@ var (
ErrExtensionDataInvalid = &Error{"ERR_EXTENSION_DATA_INVALID"} ErrExtensionDataInvalid = &Error{"ERR_EXTENSION_DATA_INVALID"}
// ErrHeadInvalid: the head of a format 3 capsule is well encoded but one // ErrHeadInvalid: the head of a format 3 capsule is well encoded but one
// of its fields breaks its rules: a path, the comment, the declared author // of its fields breaks its rules: a path, the comment, the declared author
// or the layout of the files (spec §29.4 to §29.6). New in v0.10; All // or the layout of the files (spec §29.4 to §29.6). New in v0.10.
// lists it once SpecVersion is 0.10.
ErrHeadInvalid = &Error{"ERR_HEAD_INVALID"} ErrHeadInvalid = &Error{"ERR_HEAD_INVALID"}
) )
@ -85,7 +84,7 @@ func All() []*Error {
ErrUnknownProfile, ErrProfileMismatch, ErrDateKeyInvalid, ErrDateKeyNonCanonical, ErrUnknownProfile, ErrProfileMismatch, ErrDateKeyInvalid, ErrDateKeyNonCanonical,
ErrRoundMismatch, ErrReleaseUnavailable, ErrReleaseInvalid, ErrAccessRequired, ErrRoundMismatch, ErrReleaseUnavailable, ErrReleaseInvalid, ErrAccessRequired,
ErrAccessInvalid, ErrPolicyStructureMismatch, ErrHeaderBinding, ErrIntegrity, ErrAccessInvalid, ErrPolicyStructureMismatch, ErrHeaderBinding, ErrIntegrity,
ErrExtensionCriticalUnknown, ErrExtensionDataInvalid, ErrExtensionCriticalUnknown, ErrExtensionDataInvalid, ErrHeadInvalid,
} }
} }

@ -46,15 +46,15 @@ func TestProperties(t *testing.T) {
func TestNFD(t *testing.T) { func TestNFD(t *testing.T) {
for _, c := range []struct{ in, want string }{ for _, c := range []struct{ in, want string }{
{"é", "é"}, {"é", "e\u0301"},
{"Å", "Å"}, // ANGSTROM SIGN, a singleton decomposition {"\u212b", "A\u030a"}, // ANGSTROM SIGN, a singleton decomposition
{"K", "K"}, // KELVIN SIGN {"\u212a", "K"}, // KELVIN SIGN
{"한", "한"}, // Hangul, with a final jamo {"한", "한"}, // Hangul, with a final jamo
{"하", "하"}, // Hangul, without {"하", "하"}, // Hangul, without
{"ạ́", "ạ́"}, // canonical order {"a\u0301\u0323", "a\u0323\u0301"}, // canonical order
{"ΐ", "ΐ"}, {"ΐ", "ι\u0308\u0301"},
{"ǖ", "ǖ"}, // recursive decomposition {"ǖ", "u\u0308\u0304"}, // recursive decomposition
{"á‍̣", "á‍̣"}, // ZWJ is a starter {"a\u0301\u200d\u0323", "a\u0301\u200d\u0323"}, // ZWJ is a starter
} { } {
if got := NFD(c.in); got != c.want { if got := NFD(c.in); got != c.want {
t.Errorf("NFD(%+q) = %+q, want %+q", c.in, got, c.want) t.Errorf("NFD(%+q) = %+q, want %+q", c.in, got, c.want)
@ -66,11 +66,11 @@ func TestKey(t *testing.T) {
for _, same := range [][2]string{ for _, same := range [][2]string{
{"A.txt", "a.txt"}, {"A.txt", "a.txt"},
{"Straße", "STRASSE"}, {"Straße", "STRASSE"},
{"K", "k"}, {"\u212a", "k"},
{"ı", "i"}, {"ı", "i"},
{"é", "é"}, {"é", "e\u0301"},
{"ab", "a‌b"}, {"ab", "a\u200cb"},
{"á‍̣", "ạ́"}, {"a\u0301\u200d\u0323", "a\u0323\u0301"},
{".DateKeys-x", ".datekeys-x"}, {".DateKeys-x", ".datekeys-x"},
} { } {
if Key(same[0]) != Key(same[1]) { if Key(same[0]) != Key(same[1]) {
@ -96,9 +96,9 @@ func rule(err error) string {
func TestCheckPath(t *testing.T) { func TestCheckPath(t *testing.T) {
const ( const (
zwj = "‍" zwj = "\u200d"
zwnj = "‌" zwnj = "\u200c"
vs16 = "️" vs16 = "\ufe0f"
) )
scotland := "\U0001F3F4\U000E0067\U000E0062\U000E0073\U000E0063\U000E0074\U000E007F" scotland := "\U0001F3F4\U000E0067\U000E0062\U000E0073\U000E0063\U000E0074\U000E007F"
for _, c := range []struct { for _, c := range []struct {
@ -111,7 +111,7 @@ func TestCheckPath(t *testing.T) {
{"Para ti ♥.jpg", ""}, // bestfit874 maps ♥ to 0x03 {"Para ti ♥.jpg", ""}, // bestfit874 maps ♥ to 0x03
{"§ 3 contrato.pdf", ""}, // bestfit874 maps § to 0x15 {"§ 3 contrato.pdf", ""}, // bestfit874 maps § to 0x15
{"Madrid → Lisboa", ""}, // bestfit1253 maps → to '>' {"Madrid → Lisboa", ""}, // bestfit1253 maps → to '>'
{"❤" + vs16 + ".txt", ""}, // ❤️ {"❤" + vs16 + ".txt", ""}, // ❤\ufe0f
{"\U0001F3F3" + vs16 + zwj + "\U0001F308", ""}, // rainbow flag {"\U0001F3F3" + vs16 + zwj + "\U0001F308", ""}, // rainbow flag
{"\U0001F468" + zwj + "\U0001F469" + zwj + "\U0001F467", ""}, {"\U0001F468" + zwj + "\U0001F469" + zwj + "\U0001F467", ""},
{"ab" + zwnj + "c", ""}, {"ab" + zwnj + "c", ""},
@ -121,7 +121,7 @@ func TestCheckPath(t *testing.T) {
{"report~2023.txt", ""}, {"report~2023.txt", ""},
{strings.Repeat("a", 255), ""}, {strings.Repeat("a", 255), ""},
{strings.Repeat("ΐ", 85), ""}, // 170 bytes, 255 UTF-16 units after NFD {strings.Repeat("ΐ", 85), ""}, // 170 bytes, 255 UTF-16 units after NFD
{" a ", ""}, // no table maps U+00A0 to U+0020... checked below {"\u00a0a\u00a0", ""}, // no table maps U+00A0 to U+0020... checked below
// R2. // R2.
{"/a", "R2"}, {"/a", "R2"},
@ -139,13 +139,13 @@ func TestCheckPath(t *testing.T) {
{"a\\b", "R4"}, {"a\\b", "R4"},
{"a?b", "R4"}, {"a?b", "R4"},
{"a\tb", "R4"}, {"a\tb", "R4"},
{"a
b", "R4"}, {"a\u2028b", "R4"},
{"a­b", "R4"}, {"a\u00adb", "R4"},
{"ab", "R4"}, {"a\u206ab", "R4"},
{"a‮b", "R4"}, {"a\u202eb", "R4"},
{"informeanexo", "R4"}, {"informe\uf03aanexo", "R4"},
{"a͸b", "R4"}, {"a\u0378b", "R4"},
{"a￾b", "R4"}, {"a\ufffeb", "R4"},
{scotland, "R4"}, {scotland, "R4"},
{"\U0001F600\U000E0100", "R4"}, // VS17 {"\U0001F600\U000E0100", "R4"}, // VS17
// R4b. // R4b.
@ -174,7 +174,7 @@ func TestCheckPath(t *testing.T) {
{"CON.txt", "R6c"}, // full-width CON {"CON.txt", "R6c"}, // full-width CON
{"a∖b", "R6c"}, // SET MINUS {"a∖b", "R6c"}, // SET MINUS
{"a∶b", "R6c"}, // RATIO {"a∶b", "R6c"}, // RATIO
{" a", "R6c"}, // bestfit1252 maps U+3000 to U+0020, which breaks R5 {"\u3000a", "R6c"}, // bestfit1252 maps U+3000 to U+0020, which breaks R5
{"a/b", "R6c"}, // full-width solidus {"a/b", "R6c"}, // full-width solidus
{"..", "R6c"}, // full-width dots project to ".." {"..", "R6c"}, // full-width dots project to ".."
// R10. // R10.
@ -189,7 +189,7 @@ func TestCheckPath(t *testing.T) {
} }
continue continue
} }
if c.path == " a " { if c.path == "\u00a0a\u00a0" {
// Whatever the tables give, it must be R6c or nothing (R5 is // Whatever the tables give, it must be R6c or nothing (R5 is
// about U+0020 only). // about U+0020 only).
if got != "" && got != "R6c" { if got != "" && got != "R6c" {
@ -214,8 +214,8 @@ func TestCheckTree(t *testing.T) {
{[]string{"a/b", "a/b/c"}, "R7"}, {[]string{"a/b", "a/b/c"}, "R7"},
{[]string{"Fotos/b", "fotos/a"}, "R7"}, {[]string{"Fotos/b", "fotos/a"}, "R7"},
{[]string{"STRASSE", "Straße"}, "R7"}, {[]string{"STRASSE", "Straße"}, "R7"},
{[]string{"ab", "a‌b"}, "R7"}, {[]string{"ab", "a\u200cb"}, "R7"},
{[]string{"k", "K"}, "R7"}, {[]string{"k", "\u212a"}, "R7"},
} { } {
if got := rule(CheckTree(c.paths)); got != c.rule { if got := rule(CheckTree(c.paths)); got != c.rule {
t.Errorf("CheckTree(%+q): %s, want %s", c.paths, got, c.rule) t.Errorf("CheckTree(%+q): %s, want %s", c.paths, got, c.rule)
@ -240,22 +240,22 @@ func TestTexts(t *testing.T) {
rule string rule string
}{ }{
{"Hola\tmundo\nsegunda línea", true, ""}, {"Hola\tmundo\nsegunda línea", true, ""},
{"❤️ para ti", true, ""}, {"❤\ufe0f para ti", true, ""},
{"Hola\r\n", true, "text"}, {"Hola\r\n", true, "text"},
{"a‮b", true, "text"}, {"a\u202eb", true, "text"},
{"Hola\U000E0049\U000E0047", true, "text"}, // tag characters {"Hola\U000E0049\U000E0047", true, "text"}, // tag characters
{"\U0001F600\U000E0100", true, "text"}, // VS17 {"\U0001F600\U000E0100", true, "text"}, // VS17
{"a️", true, "text"}, // VS16 after a letter {"a\ufe0f", true, "text"}, // VS16 after a letter
{"a‍\nb", true, "text"}, // ZWJ at the end of a line {"a\u200d\nb", true, "text"}, // ZWJ at the end of a line
{"a￾", true, "text"}, {"a\ufffe", true, "text"},
{"a﷐", true, "text"}, {"a\ufdd0", true, "text"},
{"a­b", true, "text"}, {"a\u00adb", true, "text"},
{"Ana López", false, ""}, {"Ana López", false, ""},
{"Ana\tLópez", false, "text"}, {"Ana\tLópez", false, "text"},
{"Ana\nLópez", false, "text"}, {"Ana\nLópez", false, "text"},
{" Ana", false, "text"}, {" Ana", false, "text"},
{"Ana ", false, "text"}, {"Ana ", false, "text"},
{"a‎b", false, "text"}, {"a\u200eb", false, "text"},
} { } {
check := CheckAuthor check := CheckAuthor
if c.comment { if c.comment {

@ -1,6 +1,6 @@
{ {
"description": "time_only capsule with an empty payload", "description": "time_only capsule with an empty payload",
"spec": "0.9", "spec": "0.10",
"format": 1, "format": 1,
"file": "empty_payload.dkc", "file": "empty_payload.dkc",
"sha256": "871e9bf05b52bbae17f3adfbbf97b46e7f0e53aa8f57bcaa506e43f36f53a9d4", "sha256": "871e9bf05b52bbae17f3adfbbf97b46e7f0e53aa8f57bcaa506e43f36f53a9d4",

@ -1,6 +1,6 @@
{ {
"description": "format 2 time_only capsule with an empty content: L = 0, P = 256", "description": "format 2 time_only capsule with an empty content: L = 0, P = 256",
"spec": "0.9", "spec": "0.10",
"format": 2, "format": 2,
"file": "format2_empty_payload.dkc", "file": "format2_empty_payload.dkc",
"sha256": "7aea2b5aa48b1a46053716f733d50fab9cd0b80b1be67631bcc06c5bb765dc21", "sha256": "7aea2b5aa48b1a46053716f733d50fab9cd0b80b1be67631bcc06c5bb765dc21",

@ -1,6 +1,6 @@
{ {
"description": "portable X25519 .dkk of format2_time_and_key_portable.dkc", "description": "portable X25519 .dkk of format2_time_and_key_portable.dkc",
"spec": "0.9", "spec": "0.10",
"file": "format2_time_and_key_portable.dkk", "file": "format2_time_and_key_portable.dkk",
"sha256": "095b7bc516a22bf0c2366f0af3cd48bfe857a2354d6e2a9b285278b95e450fe0", "sha256": "095b7bc516a22bf0c2366f0af3cd48bfe857a2354d6e2a9b285278b95e450fe0",
"credential_id": "e3c7be83cbf1fbd6b115c96411b3bd01", "credential_id": "e3c7be83cbf1fbd6b115c96411b3bd01",

@ -1,6 +1,6 @@
{ {
"description": "format 2 time_and_key capsule with one credential, a portable .dkk, and 15 dummies", "description": "format 2 time_and_key capsule with one credential, a portable .dkk, and 15 dummies",
"spec": "0.9", "spec": "0.10",
"format": 2, "format": 2,
"file": "format2_time_and_key_portable.dkc", "file": "format2_time_and_key_portable.dkc",
"sha256": "600892659fe4890223e895876275f656995d170fda42b07fb2bec0ca51ce4b43", "sha256": "600892659fe4890223e895876275f656995d170fda42b07fb2bec0ca51ce4b43",

@ -1,6 +1,6 @@
{ {
"description": "portable X25519 .dkk of format2_time_and_key_recipients.dkc", "description": "portable X25519 .dkk of format2_time_and_key_recipients.dkc",
"spec": "0.9", "spec": "0.10",
"file": "format2_time_and_key_recipients.dkk", "file": "format2_time_and_key_recipients.dkk",
"sha256": "2ad99b1556086ec311d7f0b3bd3aaba05e75f45c4fa22490b0d5e8bb0b1a222e", "sha256": "2ad99b1556086ec311d7f0b3bd3aaba05e75f45c4fa22490b0d5e8bb0b1a222e",
"credential_id": "93cedf68421710e83908ec683b104436", "credential_id": "93cedf68421710e83908ec683b104436",

@ -1,6 +1,6 @@
{ {
"description": "format 2 time_and_key capsule for three known X25519 recipients and a portable .dkk, and 12 dummies", "description": "format 2 time_and_key capsule for three known X25519 recipients and a portable .dkk, and 12 dummies",
"spec": "0.9", "spec": "0.10",
"format": 2, "format": 2,
"file": "format2_time_and_key_recipients.dkc", "file": "format2_time_and_key_recipients.dkc",
"sha256": "1a44fd8708c92e2e0a10cfcb1d864a71331ea9af25d97e1a42e969dc898959e3", "sha256": "1a44fd8708c92e2e0a10cfcb1d864a71331ea9af25d97e1a42e969dc898959e3",

@ -1,6 +1,6 @@
{ {
"description": "format 2 time_and_key capsule for sixteen known X25519 recipients, without dummies", "description": "format 2 time_and_key capsule for sixteen known X25519 recipients, without dummies",
"spec": "0.9", "spec": "0.10",
"format": 2, "format": 2,
"file": "format2_time_and_key_sixteen.dkc", "file": "format2_time_and_key_sixteen.dkc",
"sha256": "7aaac5c18f216bf53df326ecc817179640a53408cf25dfd50488910a762dc381", "sha256": "7aaac5c18f216bf53df326ecc817179640a53408cf25dfd50488910a762dc381",

@ -1,6 +1,6 @@
{ {
"description": "format 2 time_only capsule, padding code 2 (reforzado): L = 78000, P = 79872, two STREAM chunks", "description": "format 2 time_only capsule, padding code 2 (reforzado): L = 78000, P = 79872, two STREAM chunks",
"spec": "0.9", "spec": "0.10",
"format": 2, "format": 2,
"file": "format2_time_only.dkc", "file": "format2_time_only.dkc",
"sha256": "f5a40ac6b8a08a0c12db6114c2bca23522d6a77b512b509a217fb15f367813c4", "sha256": "f5a40ac6b8a08a0c12db6114c2bca23522d6a77b512b509a217fb15f367813c4",

@ -1,6 +1,6 @@
{ {
"description": "format 2 time_only capsule with the content of format2_time_only and padding code 1 (bloque256): L = 78000, P = 78080", "description": "format 2 time_only capsule with the content of format2_time_only and padding code 1 (bloque256): L = 78000, P = 78080",
"spec": "0.9", "spec": "0.10",
"format": 2, "format": 2,
"file": "format2_time_only_bloque256.dkc", "file": "format2_time_only_bloque256.dkc",
"sha256": "aae769c30d04920801d8b293d30864fbe223c9c9353ec2b4907a1ee1996e39f9", "sha256": "aae769c30d04920801d8b293d30864fbe223c9c9353ec2b4907a1ee1996e39f9",

@ -1,6 +1,6 @@
{ {
"description": "format 2 time_only capsule with a noncritical PUBLIC_HEADER extension and a noncritical CONTROL_CBOR extension", "description": "format 2 time_only capsule with a noncritical PUBLIC_HEADER extension and a noncritical CONTROL_CBOR extension",
"spec": "0.9", "spec": "0.10",
"format": 2, "format": 2,
"file": "format2_time_only_extensions.dkc", "file": "format2_time_only_extensions.dkc",
"sha256": "fb406100d5703a2e888983b3175ed34a09a34469cc722256e5cf535dd728fbe9", "sha256": "fb406100d5703a2e888983b3175ed34a09a34469cc722256e5cf535dd728fbe9",

@ -1,6 +1,6 @@
{ {
"description": "portable X25519 .dkk of time_and_key_portable.dkc", "description": "portable X25519 .dkk of time_and_key_portable.dkc",
"spec": "0.9", "spec": "0.10",
"file": "time_and_key_portable.dkk", "file": "time_and_key_portable.dkk",
"sha256": "e528fa2c832c91119f0684bb9d6fb3c4c2d0d55183482890e7c4fe92f668426a", "sha256": "e528fa2c832c91119f0684bb9d6fb3c4c2d0d55183482890e7c4fe92f668426a",
"credential_id": "3955e944a3c60cfa1fd6485e9693c77d", "credential_id": "3955e944a3c60cfa1fd6485e9693c77d",

@ -1,6 +1,6 @@
{ {
"description": "time_and_key capsule whose only recipient is a portable .dkk", "description": "time_and_key capsule whose only recipient is a portable .dkk",
"spec": "0.9", "spec": "0.10",
"format": 1, "format": 1,
"file": "time_and_key_portable.dkc", "file": "time_and_key_portable.dkc",
"sha256": "2e97878078bae6358037a9c264f379a3cbe839f767d69836b0343f35657b2972", "sha256": "2e97878078bae6358037a9c264f379a3cbe839f767d69836b0343f35657b2972",

@ -1,6 +1,6 @@
{ {
"description": "portable X25519 .dkk of time_and_key_portable.dkc with a noncritical extension: the credential of time_and_key_portable.dkk re-issued with org.example.delivery", "description": "portable X25519 .dkk of time_and_key_portable.dkc with a noncritical extension: the credential of time_and_key_portable.dkk re-issued with org.example.delivery",
"spec": "0.9", "spec": "0.10",
"file": "time_and_key_portable_extension.dkk", "file": "time_and_key_portable_extension.dkk",
"sha256": "0bf463a7c65627b7dda2234d728df89ec5b835816a2a37b91497d8fecc5ea548", "sha256": "0bf463a7c65627b7dda2234d728df89ec5b835816a2a37b91497d8fecc5ea548",
"credential_id": "3955e944a3c60cfa1fd6485e9693c77d", "credential_id": "3955e944a3c60cfa1fd6485e9693c77d",

@ -1,6 +1,6 @@
{ {
"description": "portable X25519 .dkk of time_and_key_recipients.dkc", "description": "portable X25519 .dkk of time_and_key_recipients.dkc",
"spec": "0.9", "spec": "0.10",
"file": "time_and_key_recipients.dkk", "file": "time_and_key_recipients.dkk",
"sha256": "19f6c47150c3194712d454f43c7392b7344e6b4e7b074d83e9ca5f563a8e072f", "sha256": "19f6c47150c3194712d454f43c7392b7344e6b4e7b074d83e9ca5f563a8e072f",
"credential_id": "b89292aedf6d05d584cec9a871ce8735", "credential_id": "b89292aedf6d05d584cec9a871ce8735",

@ -1,6 +1,6 @@
{ {
"description": "time_and_key capsule for two known X25519 recipients and a portable .dkk", "description": "time_and_key capsule for two known X25519 recipients and a portable .dkk",
"spec": "0.9", "spec": "0.10",
"format": 1, "format": 1,
"file": "time_and_key_recipients.dkc", "file": "time_and_key_recipients.dkc",
"sha256": "69ac110380f5d768b5b6afaa157a50ed17d8ceccfbd4604ffa5b6da38539b635", "sha256": "69ac110380f5d768b5b6afaa157a50ed17d8ceccfbd4604ffa5b6da38539b635",

@ -1,6 +1,6 @@
{ {
"description": "time_only capsule, two STREAM chunks, no extensions", "description": "time_only capsule, two STREAM chunks, no extensions",
"spec": "0.9", "spec": "0.10",
"format": 1, "format": 1,
"file": "time_only.dkc", "file": "time_only.dkc",
"sha256": "99e915810d595f1092700b728f5e5081d78efe83f5343e76325b1bcc2c33ccf2", "sha256": "99e915810d595f1092700b728f5e5081d78efe83f5343e76325b1bcc2c33ccf2",

@ -1,6 +1,6 @@
{ {
"description": "time_only capsule with a noncritical PUBLIC_HEADER extension and a noncritical CONTROL_CBOR extension", "description": "time_only capsule with a noncritical PUBLIC_HEADER extension and a noncritical CONTROL_CBOR extension",
"spec": "0.9", "spec": "0.10",
"format": 1, "format": 1,
"file": "time_only_extensions.dkc", "file": "time_only_extensions.dkc",
"sha256": "0446c9b73e267adcb24e5cc89afba2544a386ec9a050016e06517a4a57aa2085", "sha256": "0446c9b73e267adcb24e5cc89afba2544a386ec9a050016e06517a4a57aa2085",

@ -1,5 +1,5 @@
{ {
"spec": "0.9", "spec": "0.10",
"description": "CBOR profile of spec §58 and the schemas of spec/datekeys.cddl, generated by the reference implementation. accept and reject are walked as one data item of the profile with the limits of walk; schemas are decoded with the decoder of their schema. See testdata/README.md.", "description": "CBOR profile of spec §58 and the schemas of spec/datekeys.cddl, generated by the reference implementation. accept and reject are walked as one data item of the profile with the limits of walk; schemas are decoded with the decoder of their schema. See testdata/README.md.",
"walk": { "walk": {
"max_depth": 3, "max_depth": 3,

@ -1,5 +1,5 @@
{ {
"spec": "0.9", "spec": "0.10",
"description": "Canonical dk1_ strings and rejected encodings (spec §18, §19, §66), generated by the reference implementation.", "description": "Canonical dk1_ strings and rejected encodings (spec §18, §19, §66), generated by the reference implementation.",
"vectors": [ "vectors": [
{ {

@ -1,5 +1,5 @@
{ {
"spec": "0.9", "spec": "0.10",
"description": "Differential corpus of the pre-unlock checks (spec §63 steps 1 to 8): deterministic mutations of the official .dkc fixtures with the verdict of the reference implementation. See testdata/README.md.", "description": "Differential corpus of the pre-unlock checks (spec §63 steps 1 to 8): deterministic mutations of the official .dkc fixtures with the verdict of the reference implementation. See testdata/README.md.",
"format": "Each mutation is bases[base].file (in testdata/fixtures) with its edits applied. An edit is [at, delete, insert]: the delete bytes at offset at of the base are replaced by the bytes of the hex string insert. The edits of one mutation refer to offsets of the unmodified base, are sorted by offset and do not overlap. result is the verdict of steps 1 to 8 of spec §63 (capsule.Inspect, the Quicknet profile pinned, no extension known, no network, no secret): ok, or the normative error code, with step the step that failed. kind names the generator of the mutation and is informative.", "format": "Each mutation is bases[base].file (in testdata/fixtures) with its edits applied. An edit is [at, delete, insert]: the delete bytes at offset at of the base are replaced by the bytes of the hex string insert. The edits of one mutation refer to offsets of the unmodified base, are sorted by offset and do not overlap. result is the verdict of steps 1 to 8 of spec §63 (capsule.Inspect, the Quicknet profile pinned, no extension known, no network, no secret): ok, or the normative error code, with step the step that failed. kind names the generator of the mutation and is informative.",
"seed": 20260925, "seed": 20260925,

@ -1,5 +1,5 @@
{ {
"spec": "0.9", "spec": "0.10",
"description": "Mutation corpus of spec §64 and further cases of capsule.TestMutationCorpus, generated by the reference implementation: each case is a .dkc and what the reader is given, with the normative error and the step of spec §63 at which capsule.Open fails. See testdata/README.md.", "description": "Mutation corpus of spec §64 and further cases of capsule.TestMutationCorpus, generated by the reference implementation: each case is a .dkc and what the reader is given, with the normative error and the step of spec §63 at which capsule.Open fails. See testdata/README.md.",
"cases": [ "cases": [
{ {

@ -1,5 +1,5 @@
{ {
"spec": "0.9", "spec": "0.10",
"description": "Padding rules of the payload of a format 2 capsule (spec §29.1): for each content length L, P with code 1 (bloque256) and code 2 (reforzado), and the length of PAYLOAD_AGE for each. e, s and last_bits are informative. Generated by the reference implementation. See testdata/README.md.", "description": "Padding rules of the payload of a format 2 capsule (spec §29.1): for each content length L, P with code 1 (bloque256) and code 2 (reforzado), and the length of PAYLOAD_AGE for each. e, s and last_bits are informative. Generated by the reference implementation. See testdata/README.md.",
"l_max": 8936830510563328, "l_max": 8936830510563328,
"vectors": [ "vectors": [

@ -1,5 +1,5 @@
{ {
"spec": "0.9", "spec": "0.10",
"description": "Quicknet Provider Profile V1: exact Deterministic CBOR and profile_hash (spec §11, §12, §75 item 2), generated by the reference implementation.", "description": "Quicknet Provider Profile V1: exact Deterministic CBOR and profile_hash (spec §11, §12, §75 item 2), generated by the reference implementation.",
"profile_id": "datekeys:quicknet:v1", "profile_id": "datekeys:quicknet:v1",
"provider": "drand", "provider": "drand",

@ -1,5 +1,5 @@
{ {
"spec": "0.9", "spec": "0.10",
"profile": "datekeys:quicknet:v1", "profile": "datekeys:quicknet:v1",
"description": "Quicknet date to round resolution (spec §15, §16, §65), generated by the reference implementation.", "description": "Quicknet date to round resolution (spec §15, §16, §65), generated by the reference implementation.",
"vectors": [ "vectors": [

@ -1,5 +1,5 @@
{ {
"spec": "0.9", "spec": "0.10",
"description": "H2 of the IBE-CCA of tlock (spec §63 step 11): SHA-256 of \"IBE-H2\" and the 576 bytes of an element of GT, c1 before c0 at every level of the tower and each coordinate of Fp in 48 bytes big-endian (the order of kilic/bls12-381), truncated to 16 bytes. Generated by the reference implementation with drand/kyber-bls12381, the pairing of tlock.", "description": "H2 of the IBE-CCA of tlock (spec §63 step 11): SHA-256 of \"IBE-H2\" and the 576 bytes of an element of GT, c1 before c0 at every level of the tower and each coordinate of Fp in 48 bytes big-endian (the order of kilic/bls12-381), truncated to 16 bytes. Generated by the reference implementation with drand/kyber-bls12381, the pairing of tlock.",
"vectors": [ "vectors": [
{ {

@ -6,11 +6,11 @@ import (
) )
// SpecVersion is the version of the DateKeys Protocol Specification that this // SpecVersion is the version of the DateKeys Protocol Specification that this
// module implements: spec/DateKeys_Protocol_Specification_v0.9.md, tagged // module implements: spec/DateKeys_Protocol_Specification_v0.10.md, tagged
// spec-v0.9 in its repository. It is neither the version of the module (see // spec-v0.10 in its repository once it is closed. It is neither the version
// Version) nor the versions inside the objects: the capsule format, 1 or 2, // of the module (see Version) nor the versions inside the objects: the
// and the schema versions (spec §22, §70). // capsule format, 1 to 3, and the schema versions (spec §22, §70).
const SpecVersion = "0.9" const SpecVersion = "0.10"
// modulePath is the path of this module: the import path of its root // modulePath is the path of this module: the import path of its root
// package, whatever the module is called. // package, whatever the module is called.

Loading…
Cancel
Save

Powered by TurnKey Linux.