You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
DateKeys/internal/testkit/genfixtures/main.go

944 lines
37 KiB

// Command genfixtures generates the official DateKeys vectors and fixtures
// (spec §65-§68) into testdata/.
//
// Fixtures are generated once, over rounds that are already published, and
// then committed: age randomness cannot be injected through its public API,
// so they are decryption and validation fixtures, not byte-reproducible
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
// encryption outputs (spec §67). Existing format 2 and 3 fixtures are never
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
// overwritten unless -force (every one) or -only (the named ones) is given;
// vectors are always regenerated, and the tests fail if the implementation
// stops reproducing the committed ones.
//
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
// The format 1 fixtures are the ones of spec v0.8.2, kept as compatibility
// fixtures (spec §67, §70). They are never regenerated: only a generator of
// test vectors may write format 1, and these were written by the v0.8.2
// reference itself. The .dkk with an extension (spec §68) is derived from the
// portable .dkk of time_and_key_portable, a format 1 fixture, with -only.
//
// Derived from the fixtures and always regenerated, like the vectors: the
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
// fields of each fixture record that the implementation computes (spec
// version, format, lengths and the stages of spec §63), the frozen
// "datekeys inspect -json" output of each .dkc (<name>.inspect.json), the
// exported mutation corpus (vectors/mutations.json) and the differential
// corpus of the pre-unlock checks (vectors/inspect_differential.json). The
// mutations whose capsule is built with age randomness keep the bytes
// recorded in the committed mutations.json; -force, or -only mutations,
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
// builds them afresh. A fixture a mutation derives from must not be
// regenerated alone: regenerate the mutations with it.
//
// go run ./internal/testkit/genfixtures -out testdata
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
// go run ./internal/testkit/genfixtures -out testdata -only format2_time_only_extensions,mutations
package main
import (
"bytes"
"context"
"crypto/sha256"
"encoding/hex"
"errors"
"flag"
"fmt"
"io"
"io/fs"
"log"
"os"
"path/filepath"
"strings"
"time"
"filippo.io/age"
"g.activething.com/go/DateKeys/accesskey"
"g.activething.com/go/DateKeys/agewrap"
"g.activething.com/go/DateKeys/authorkey"
"g.activething.com/go/DateKeys/capsule"
"g.activething.com/go/DateKeys/datekey"
"g.activething.com/go/DateKeys/extension"
"g.activething.com/go/DateKeys/internal/inspectview"
"g.activething.com/go/DateKeys/internal/testkit"
"g.activething.com/go/DateKeys/profile"
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
"g.activething.com/go/DateKeys/provider"
)
func main() {
out := flag.String("out", "testdata", "output directory")
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
force := flag.Bool("force", false, "overwrite every existing format 2 and 3 fixture")
only := flag.String("only", "", "comma-separated format 2 and 3 fixture names to regenerate, overwriting them, \""+extDKK+"\" to derive the .dkk with an extension again, and \"mutations\" to rebuild the randomly built mutations; every other fixture is left untouched")
flag.Parse()
sel, err := selection(*force, *only)
if err != nil {
log.Fatal(err)
}
if err := vectors(filepath.Join(*out, "vectors")); err != nil {
log.Fatal(err)
}
if err := fixtures(filepath.Join(*out, "fixtures"), sel); err != nil {
log.Fatal(err)
}
if err := derived(*out, sel); err != nil {
log.Fatal(err)
}
}
// selector decides which fixtures are (re)generated.
type selector struct {
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
force bool // overwrite every format 2 and 3 fixture
only map[string]bool // when non-empty, regenerate exactly these
}
func selection(force bool, only string) (selector, error) {
sel := selector{force: force, only: map[string]bool{}}
if only == "" {
return sel, nil
}
known := map[string]bool{extDKK: true, mutationsName: true}
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
frozen := map[string]bool{}
for _, s := range specs() {
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
if s.format == capsule.Format1 {
frozen[s.name] = true
} else {
known[s.name] = true
}
}
for _, name := range strings.Split(only, ",") {
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
if frozen[name] {
return sel, fmt.Errorf("-only: %q is a format 1 fixture of spec v0.8.2, kept frozen as a compatibility fixture", name)
}
if !known[name] {
return sel, fmt.Errorf("-only: unknown fixture %q", name)
}
sel.only[name] = true
}
return sel, nil
}
// generate reports whether the fixture name, whose main file is path, is
// written.
func (s selector) generate(name, path string) bool {
if len(s.only) > 0 {
return s.only[name]
}
_, err := os.Stat(path)
return s.force || err != nil
}
func vectors(dir string) error {
pv, err := testkit.QuicknetProfileVector()
if err != nil {
return err
}
if err := testkit.WriteJSON(filepath.Join(dir, "profile_quicknet.json"), pv); err != nil {
return err
}
if err := testkit.WriteJSON(filepath.Join(dir, "quicknet_rounds.json"), testkit.RoundVectors()); err != nil {
return err
}
if err := testkit.WriteJSON(filepath.Join(dir, "dk1.json"), testkit.DK1Vectors()); err != nil {
return err
}
cv, err := testkit.CBORVectors()
if err != nil {
return err
}
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
if err := testkit.WriteJSON(filepath.Join(dir, "cbor.json"), cv); err != nil {
return err
}
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
pad, err := testkit.PaddingVectors()
if err != nil {
return err
}
if err := testkit.WriteJSON(filepath.Join(dir, "padding.json"), pad); err != nil {
return err
}
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
iv, err := testkit.IBEVectors()
if err != nil {
return err
}
Format 3, step 6c: the vectors of paths, keys, heads and security The vector files of spec 67 for format 3, generated with the result each case is written for, so that the generator fails when the implementation or the tables change: - paths.json: 83 paths with the result of the rules of one entry, the violation worded as every implementation must word it, and 16 trees, the paths of a head and the result of decoding it: U+00A0, accepted, and U+3000, R6c, at both ends of a segment; best-fit, full-width forms, U+00B4 of cp1253; 8.3 aliases with ~1; Cn; U+206A to U+206F, tags and other ignorables; a dot and ZWJ, and ZWJ alone; 127 and 85 times U+0390; U+F03A; .datekeys-x at two levels; U+FF5E and U+1F600 in both orders; ab with and without ZWNJ; U+00BF, U+00A7 and U+2665, accepted; VS16 after U+2764 and after a; ZWJ at the start, at the end and twice; the rainbow flag and the flag of Scotland; b/.. and a. - path_fold.json: 22 segments with their NFD and their key of R7, among them the entries F of CaseFolding, the dotless i, the Kelvin and Angstrom signs, Cherokee, Hangul and the whitelist dropped before NFD. - head_schema.json: 63 heads through layers 2, 3 and 4, in key order, with the violation of each ERR_HEAD_INVALID; they add comments with tags and with loose variation selectors. - security.json: 21 areas with their verdicts, X, F0, F1, S0, S1 and S2, among them key 2 that is not a byte string, a key 4, a byte more, alg 0, an empty key with the seal intact, and a seal that breaks its schema with an unknown seal_type. - cbor.json gains the control of schema version 3. A test replays every committed vector through the implementation, and another checks that the files are current. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
if err := testkit.WriteJSON(filepath.Join(dir, "tlock_ibe.json"), iv); err != nil {
return err
}
return format3Vectors(dir)
}
// format3Vectors writes the vectors of format 3 (spec §67): the paths, the
// keys of R7, the heads and security.
func format3Vectors(dir string) error {
paths, err := testkit.PathVectors()
if err != nil {
return err
}
fold, err := testkit.PathFoldVectors()
if err != nil {
return err
}
heads, err := testkit.HeadSchemaVectors()
if err != nil {
return err
}
security, err := testkit.SecurityVectors()
if err != nil {
return err
}
strict, err := testkit.Ed25519StrictVectors()
if err != nil {
return err
}
note, err := testkit.NoteVectors()
if err != nil {
return err
}
words, err := testkit.WordKeyVectors()
if err != nil {
return err
}
for name, v := range map[string]any{"paths.json": paths, "path_fold.json": fold, "head_schema.json": heads, "security.json": security, "ed25519_strict.json": strict, "note.json": note, "wordkey.json": words} {
Format 3, step 6c: the vectors of paths, keys, heads and security The vector files of spec 67 for format 3, generated with the result each case is written for, so that the generator fails when the implementation or the tables change: - paths.json: 83 paths with the result of the rules of one entry, the violation worded as every implementation must word it, and 16 trees, the paths of a head and the result of decoding it: U+00A0, accepted, and U+3000, R6c, at both ends of a segment; best-fit, full-width forms, U+00B4 of cp1253; 8.3 aliases with ~1; Cn; U+206A to U+206F, tags and other ignorables; a dot and ZWJ, and ZWJ alone; 127 and 85 times U+0390; U+F03A; .datekeys-x at two levels; U+FF5E and U+1F600 in both orders; ab with and without ZWNJ; U+00BF, U+00A7 and U+2665, accepted; VS16 after U+2764 and after a; ZWJ at the start, at the end and twice; the rainbow flag and the flag of Scotland; b/.. and a. - path_fold.json: 22 segments with their NFD and their key of R7, among them the entries F of CaseFolding, the dotless i, the Kelvin and Angstrom signs, Cherokee, Hangul and the whitelist dropped before NFD. - head_schema.json: 63 heads through layers 2, 3 and 4, in key order, with the violation of each ERR_HEAD_INVALID; they add comments with tags and with loose variation selectors. - security.json: 21 areas with their verdicts, X, F0, F1, S0, S1 and S2, among them key 2 that is not a byte string, a key 4, a byte more, alg 0, an empty key with the seal intact, and a seal that breaks its schema with an unknown seal_type. - cbor.json gains the control of schema version 3. A test replays every committed vector through the implementation, and another checks that the files are current. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
if err := testkit.WriteJSON(filepath.Join(dir, name), v); err != nil {
return err
}
}
return frozenVectors(dir)
}
// mutationsName is the -only name that rebuilds the capsules of the
// mutations built with age randomness.
const mutationsName = "mutations"
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
// derived writes what is computed from the fixtures: the computed fields of
// the records, the inspect outputs, the mutation corpus and the differential
// corpus.
func derived(out string, sel selector) error {
fixtureDir := filepath.Join(out, "fixtures")
var names []string
for _, s := range specs() {
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
if s.format != capsule.Format3 || differentialBases3[s.name] {
names = append(names, s.name)
}
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
if err := refreshRecord(fixtureDir, s.name); err != nil {
return fmt.Errorf("%s: %w", s.name, err)
}
if err := inspectOutput(fixtureDir, s.name); err != nil {
return fmt.Errorf("%s: %w", s.name, err)
}
}
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
keys, err := filepath.Glob(filepath.Join(fixtureDir, "*.dkk.json"))
if err != nil {
return err
}
for _, path := range keys {
var k testkit.DKKFixture
if err := testkit.ReadJSON(path, &k); err != nil {
return err
}
k.Spec = testkit.SpecVersion
if err := testkit.WriteJSON(path, k); err != nil {
return err
}
}
path := filepath.Join(out, "vectors", "mutations.json")
var frozen *testkit.MutationFile
if !sel.force && !sel.only[mutationsName] {
var f testkit.MutationFile
switch err := testkit.ReadJSON(path, &f); {
case err == nil:
frozen = &f
case !errors.Is(err, fs.ErrNotExist):
return err
}
}
m, err := testkit.MutationCorpus(fixtureDir, frozen)
if err != nil {
return err
}
if err := testkit.WriteJSONEdits(path, m); err != nil {
return err
}
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
// The format 2 fixtures come after the format 1 ones, and the format 3
// ones after them, so the corpus keeps the cases of the earlier bases and
// adds a block for each new base.
d, err := testkit.InspectDifferential(fixtureDir, names)
if err != nil {
return err
}
return testkit.WriteDifferential(filepath.Join(out, "vectors", "inspect_differential.json"), d)
}
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
// differentialBases3 are the format 3 fixtures of the differential corpus,
// one for each policy: steps 1 to 8 see nothing of format 3 that format 2
// does not have, but VERSION.
var differentialBases3 = map[string]bool{"format3_single": true, "format3_time_and_key_portable": true}
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
// refreshRecord recomputes the fields of the record of the fixture name that
// the implementation derives from the fixture: the spec version, the
// format, the content length and, from a complete opening through the public
// API with the credentials the record names, the stages of spec §63.
func refreshRecord(dir, name string) error {
path := filepath.Join(dir, name+".json")
var f testkit.DKCFixture
if err := testkit.ReadJSON(path, &f); err != nil {
return err
}
dkc, err := os.ReadFile(filepath.Join(dir, f.File))
if err != nil {
return err
}
plaintext, err := os.ReadFile(filepath.Join(dir, f.PlaintextFile))
if err != nil {
return err
}
sig, err := hex.DecodeString(f.Release.Signature)
if err != nil {
return err
}
unlock, err := time.Parse(time.RFC3339, f.UnlockAt)
if err != nil {
return err
}
release := provider.Release{Round: f.Release.Round, Signature: sig}
oo := capsule.OpenOptions{Registry: testkit.Registry(), Source: testkit.NewSource(release), Now: testkit.Fixed(unlock)}
switch {
case f.AccessKeyFile != "":
b, err := os.ReadFile(filepath.Join(dir, f.AccessKeyFile))
if err != nil {
return err
}
if oo.AccessKey, err = accesskey.Decode(bytes.NewReader(b)); err != nil {
return err
}
default:
for _, s := range f.Identities {
id, err := age.ParseX25519Identity(s)
if err != nil {
return err
}
oo.Identities = append(oo.Identities, id)
}
}
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
sink := &testkit.MemorySink{}
oo.Sink = sink
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
var plain bytes.Buffer
opened, err := capsule.Open(context.Background(), &plain, bytes.NewReader(dkc), oo)
if err != nil {
return fmt.Errorf("fixture does not open: %w", err)
}
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
if opened.Format == capsule.Format3 {
// The plaintext file holds BODY.
if err := record3(&f, plaintext, &opened.Verdicts); err != nil {
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
return err
}
if err := check3(&f, plaintext, opened, sink); err != nil {
return err
}
} else if !bytes.Equal(plain.Bytes(), plaintext) {
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
return errors.New("fixture plaintext mismatch")
}
f.Spec = testkit.SpecVersion
f.Format = int(opened.Format)
f.PayloadLength = opened.PayloadLength
f.Padding, f.PaddedLength = int(opened.Padding), opened.PaddedLength
f.Stages = nil
for _, c := range opened.Inspection.Checks {
f.Stages = append(f.Stages, testkit.FixtureStage{Step: c.Step, Name: c.Name, OK: c.OK, Error: c.Error})
}
return testkit.WriteJSON(path, f)
}
// inspectOutput writes <name>.inspect.json: the output of
// "datekeys inspect -json -in <name>.dkc" run in the fixture directory.
func inspectOutput(dir, name string) error {
file := name + ".dkc"
dkc, err := os.ReadFile(filepath.Join(dir, file))
if err != nil {
return err
}
in, err := capsule.Inspect(bytes.NewReader(dkc), capsule.InspectOptions{Registry: testkit.Registry()})
var b bytes.Buffer
if err := inspectview.New(file, in, err).WriteJSON(&b); err != nil {
return err
}
return os.WriteFile(filepath.Join(dir, name+".inspect.json"), b.Bytes(), 0o644)
}
type spec struct {
name, description string
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
format capsule.Format
round uint64
policy capsule.Policy
recipients int
portable bool
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
padding capsule.Padding
plaintext []byte
headerExt []extension.Extension
controlExt []extension.Extension
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
// Format 3: the files, the comment and the declared author. body, when
// not nil, gives the BODY of a capsule that only a generator of test
// vectors writes (spec §62.1 rule 13), and testkit.Build writes it.
files []file3
comment, author string
// signer, when not nil, is the 32-byte seed of the author key that signs
// a format 3 fixture written by EncryptFiles (spec v0.11, §29.9).
signer []byte
// configure, when not nil, sets what only this fixture needs in the
// options of EncryptFiles: a signer with certificates, or a sealer.
configure func(o *capsule.EncryptOptions) error
body func() ([]byte, error)
// area, when not 0, is the security area of a fixture of an earlier
// version, which EncryptFiles writes only for test vectors: 512 bytes in
// the fixtures of v0.10, which are compatibility fixtures (spec §67).
area uint32
}
// Extension data of the fixtures (spec §54, §72): the header carries the raw
// UTF-8 bytes of a label, which are not CBOR; the control carries
// {0: 7, 1: "sealed"} in the CBOR profile of spec §58; the .dkk carries
// {0: "hand"}. The base protocol decodes none of them.
var (
headerExtData = []byte("public label")
controlExtData = mustHex("a2000701667365616c6564")
dkkExtData = mustHex("a1006468616e64")
// signerSeed is the seed of the test key of format3_signed: the SHA-256
// of a text. It is not a secret: anyone may sign with it.
signerSeed = func() []byte { h := sha256.Sum256([]byte("DateKeys fixture author key 1")); return h[:] }()
)
func mustHex(s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
panic(err)
}
return b
}
func mustExt(id string, version uint64, data []byte) extension.Extension {
e, err := extension.New(id, version, data)
if err != nil {
panic(err)
}
return e
}
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
// specs lists the fixtures: the frozen format 1 fixtures of spec v0.8.2
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
// first, then the format 2 fixtures of spec v0.9, then the format 3
// fixtures of spec §67.
func specs() []spec {
large := []byte(strings.Repeat("DateKeys fixture: this plaintext spans more than one 64 KiB age STREAM chunk.\n", 1000))
hExt := mustExt("org.example.label", 1, headerExtData)
cExt := mustExt("org.example.note", 2, controlExtData)
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
f1, f2, f3 := capsule.Format1, capsule.Format2, capsule.Format3
when := time.Date(2026, 9, 30, 12, 0, 0, 0, time.UTC)
note := []file3{{path: "nota.txt", content: []byte("Hola desde el pasado.\n"), mtime: when}}
tree := []file3{
{path: "carta.txt", content: []byte("Querida familia:\n\nAbrid esto juntos.\n"), mtime: when},
{path: "fotos/2025/atardecer.jpg", content: patterned("atardecer", 3000)},
{path: "fotos/2025/playa.jpg", content: patterned("playa", 80000), mtime: when.Add(-24 * time.Hour)},
{path: "fotos/leeme.txt", content: []byte("Las fotos del verano.\n"), mtime: when},
{path: "música/canción.txt", content: []byte("La, la, la.\n"), mtime: when},
}
report := []file3{{path: "informe.txt", content: []byte(strings.Repeat("Informe trimestral, sin cifras.\n", 625)), mtime: when}}
secret := []file3{{path: "secreto.txt", content: []byte("DateKeys fixture opened with a portable .dkk.\n"), mtime: when}}
return []spec{
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
{name: "time_only", format: f1, description: "time_only capsule, two STREAM chunks, no extensions", round: 1000, policy: capsule.TimeOnly, plaintext: large},
{name: "time_only_extensions", format: f1, description: "time_only capsule with a noncritical PUBLIC_HEADER extension and a noncritical CONTROL_CBOR extension", round: 2000, policy: capsule.TimeOnly, plaintext: []byte("DateKeys fixture with extensions.\n"), headerExt: []extension.Extension{hExt}, controlExt: []extension.Extension{cExt}},
{name: "time_and_key_portable", format: f1, description: "time_and_key capsule whose only recipient is a portable .dkk", round: 1000, policy: capsule.TimeAndKey, portable: true, plaintext: []byte("DateKeys fixture opened with a portable .dkk.\n")},
{name: "time_and_key_recipients", format: f1, description: "time_and_key capsule for two known X25519 recipients and a portable .dkk", round: 1001, policy: capsule.TimeAndKey, recipients: 2, portable: true, plaintext: []byte("DateKeys fixture for several recipients.\n")},
{name: "empty_payload", format: f1, description: "time_only capsule with an empty payload", round: 1001, policy: capsule.TimeOnly, plaintext: []byte{}},
{name: "format2_time_only", format: f2, description: "format 2 time_only capsule, padding code 2 (reforzado): L = 78000, P = 79872, two STREAM chunks", round: 1000, policy: capsule.TimeOnly, padding: capsule.Reforzado, plaintext: large},
{name: "format2_time_only_bloque256", format: f2, description: "format 2 time_only capsule with the content of format2_time_only and padding code 1 (bloque256): L = 78000, P = 78080", round: 1000, policy: capsule.TimeOnly, padding: capsule.Bloque256, plaintext: large},
{name: "format2_empty_payload", format: f2, description: "format 2 time_only capsule with an empty content: L = 0, P = 256", round: 1001, policy: capsule.TimeOnly, padding: capsule.Reforzado, plaintext: []byte{}},
{name: "format2_time_only_extensions", format: f2, description: "format 2 time_only capsule with a noncritical PUBLIC_HEADER extension and a noncritical CONTROL_CBOR extension", round: 2000, policy: capsule.TimeOnly, padding: capsule.Reforzado, plaintext: []byte("DateKeys fixture with extensions.\n"), headerExt: []extension.Extension{hExt}, controlExt: []extension.Extension{cExt}},
{name: "format2_time_and_key_portable", format: f2, description: "format 2 time_and_key capsule with one credential, a portable .dkk, and 15 dummies", round: 1000, policy: capsule.TimeAndKey, portable: true, padding: capsule.Reforzado, plaintext: []byte("DateKeys fixture opened with a portable .dkk.\n")},
{name: "format2_time_and_key_recipients", format: f2, description: "format 2 time_and_key capsule for three known X25519 recipients and a portable .dkk, and 12 dummies", round: 1001, policy: capsule.TimeAndKey, recipients: 3, portable: true, padding: capsule.Reforzado, plaintext: []byte("DateKeys fixture for several recipients.\n")},
{name: "format2_time_and_key_sixteen", format: f2, description: "format 2 time_and_key capsule for sixteen known X25519 recipients, without dummies", round: 2000, policy: capsule.TimeAndKey, recipients: 16, padding: capsule.Reforzado, plaintext: []byte("DateKeys fixture for sixteen recipients.\n")},
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
{name: "format3_single", format: f3, area: capsule.AreaUnit, description: "format 3 time_only capsule with a single file, nota.txt, with its mtime", round: 1000, policy: capsule.TimeOnly, padding: capsule.Reforzado, files: note},
{name: "format3_tree", format: f3, area: capsule.AreaUnit, description: "format 3 time_only capsule with five files in three folders, one of them over two STREAM chunks and one without mtime, a comment of two lines and a declared author", round: 1001, policy: capsule.TimeOnly, padding: capsule.Reforzado, files: tree, comment: "Para abrir en familia.\nCon cariño, desde 2026.", author: "Ana López"},
{name: "format3_comment_only", format: f3, area: capsule.AreaUnit, description: "format 3 time_only capsule with a comment of two lines, the second one with a TAB, a declared author and no files", round: 1004, policy: capsule.TimeOnly, padding: capsule.Reforzado, comment: "Feliz cumpleaños.\n\tAbre esto dentro de diez años.", author: "Ana"},
{name: "format3_bloque256", format: f3, area: capsule.AreaUnit, description: "format 3 time_only capsule with padding code 1 (bloque256) and one file of 20000 bytes", round: 1000, policy: capsule.TimeOnly, padding: capsule.Bloque256, files: report},
{name: "format3_time_and_key_portable", format: f3, area: capsule.AreaUnit, description: "format 3 time_and_key capsule with one credential, a portable .dkk, and 15 dummies", round: 1000, policy: capsule.TimeAndKey, portable: true, padding: capsule.Reforzado, files: secret},
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
{name: "format3_area_1024", format: f3, description: "format 3 time_only capsule with a security area of 1024 bytes, as a later version may write it, holding the empty security", round: 1000, policy: capsule.TimeOnly, padding: capsule.Reforzado,
body: func() ([]byte, error) { return body3(1024, capsule.EncodeSecurity(), "", "", note) }},
{name: "format3_security_v2", format: f3, description: "format 3 time_only capsule whose security is of version 2: verdict X", round: 1000, policy: capsule.TimeOnly, padding: capsule.Reforzado,
body: func() ([]byte, error) {
s, err := securityV2()
if err != nil {
return nil, err
}
return body3(capsule.AreaUnit, s, "", "", note)
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
}},
{name: "format3_signature_unsupported", format: f3, description: "format 3 time_only capsule with an author-signature of alg 4294967295, a random key of 32 bytes and a random signature of 64: verdicts F1 and S0", round: 1001, policy: capsule.TimeOnly, padding: capsule.Reforzado,
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
body: func() ([]byte, error) {
sig, err := unsupportedSignature()
if err != nil {
return nil, err
}
s, err := capsule.EncodeSecurityWith(sig, nil)
if err != nil {
return nil, err
}
return body3(capsule.AreaUnit, s, "", "", note)
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
}},
{name: "format3_seal_unsupported", format: f3, description: "format 3 time_only capsule with an author-signature of alg 4294967295, as in format3_signature_unsupported, and a seal of seal_type 4294967295, reserved for tests, with a random token of 32 bytes: verdicts F1 and S1", round: 2000, policy: capsule.TimeOnly, padding: capsule.Reforzado,
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
body: func() ([]byte, error) {
sig, err := unsupportedSignature()
if err != nil {
return nil, err
}
seal, err := capsule.EncodeSeal(capsule.SealTypeTest, randomBytes(32))
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
if err != nil {
return nil, err
}
s, err := capsule.EncodeSecurityWith(sig, seal)
if err != nil {
return nil, err
}
return body3(capsule.AreaUnit, s, "", "", note)
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
}},
{name: "format3_unsigned", format: f3, description: "format 3 time_only capsule with a single file, nota.txt, as format3_signed, without a signature: the area of 32 KiB of spec v0.11 holds the empty security, and P is the one of format3_signed", round: 1000, policy: capsule.TimeOnly, padding: capsule.Reforzado, files: note},
{name: "format3_signed", format: f3, description: "format 3 time_only capsule with a single file, nota.txt, signed with alg 1 by a test key whose seed the record gives: verdict F4, and the commitments and the message of the signature", round: 1000, policy: capsule.TimeOnly, padding: capsule.Reforzado, files: note, signer: signerSeed},
{name: "format3_signed_cms", format: f3, description: "format 3 time_only capsule with a single file, nota.txt, signed with alg 2 by two test certificates, an ECDSA P-256 one and an RSA 2048 one, each sealed by a test time-stamping authority before the round time: verdict F6, with the certificates, the commitments, SIGNERS and the result of each signer in the record", round: 1000, policy: capsule.TimeOnly, padding: capsule.Reforzado, files: note, configure: configureCMS},
{name: "format3_note", format: f3, description: "format 3 time_only capsule with a single file, nota.txt, and the public note «Cartas del viaje a Lisboa» in the noncritical array of PUBLIC_HEADER (spec v0.11, §24.1)", round: 1000, policy: capsule.TimeOnly, padding: capsule.Reforzado, files: note,
configure: func(o *capsule.EncryptOptions) error { o.PublicNote = "Cartas del viaje a Lisboa"; return nil }},
{name: "format3_sealed", format: f3, description: "format 3 time_only capsule with a single file, nota.txt, signed with alg 1 by the test key of format3_signed and sealed with seal_type 2 by a test time-stamping authority before the round time: verdicts F4 and S4, with SEAL_SUBJECT and the token in the record", round: 1000, policy: capsule.TimeOnly, padding: capsule.Reforzado, files: note, signer: signerSeed, configure: configureSeal},
}
}
func fixtures(dir string, sel selector) error {
if err := os.MkdirAll(dir, 0o755); err != nil {
return err
}
for _, s := range specs() {
path := filepath.Join(dir, s.name+".dkc")
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
if s.format == capsule.Format1 {
if _, err := os.Stat(path); err != nil {
return fmt.Errorf("%s: the frozen format 1 fixture is missing: %w", s.name, err)
}
continue
}
if !sel.generate(s.name, path) {
log.Printf("leaving %s untouched", path)
continue
}
if err := generate(dir, s); err != nil {
return fmt.Errorf("%s: %w", s.name, err)
}
log.Printf("generated %s", path)
}
path := filepath.Join(dir, extDKK+".dkk")
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
if _, err := os.Stat(path); err == nil && !sel.only[extDKK] {
log.Printf("leaving %s untouched", path)
return nil
}
if err := deriveDKK(dir); err != nil {
return fmt.Errorf("%s: %w", extDKK, err)
}
log.Printf("generated %s", path)
return nil
}
// extDKK is the .dkk vector with an extension (spec §68): the portable
// credential of extDKKSource re-issued with a noncritical extension. It keeps
// the credential_id, the key and the capsule_digest, so its bytes are a
// function of the source .dkk.
const (
extDKK = "time_and_key_portable_extension"
extDKKSource = "time_and_key_portable"
)
func deriveDKK(dir string) error {
src, err := os.ReadFile(filepath.Join(dir, extDKKSource+".dkk"))
if err != nil {
return err
}
k, err := accesskey.Decode(bytes.NewReader(src))
if err != nil {
return err
}
k.Noncritical = []extension.Extension{mustExt("org.example.delivery", 1, dkkExtData)}
var kb bytes.Buffer
if err := accesskey.Encode(&kb, k); err != nil {
return err
}
back, err := accesskey.Decode(bytes.NewReader(kb.Bytes()))
if err != nil {
return err
}
// The credential must open its capsule through the public API.
dkcFile := extDKKSource + ".dkc"
dkc, err := os.ReadFile(filepath.Join(dir, dkcFile))
if err != nil {
return err
}
reg := testkit.Registry()
in, err := capsule.Inspect(bytes.NewReader(dkc), capsule.InspectOptions{Registry: reg})
if err != nil {
return err
}
oo := capsule.OpenOptions{Registry: reg, Source: testkit.NewSource(testkit.Release(in.Header.DateKey.Round)),
AccessKey: back, Now: testkit.Fixed(in.UnlockAt)}
if _, err := capsule.Open(context.Background(), io.Discard, bytes.NewReader(dkc), oo); err != nil {
return fmt.Errorf("the .dkk does not open %s: %w", dkcFile, err)
}
ksum := sha256.Sum256(kb.Bytes())
kf := testkit.DKKFixture{
Description: "portable X25519 .dkk of " + dkcFile + " with a noncritical extension: the credential of " + extDKKSource + ".dkk re-issued with org.example.delivery",
Spec: testkit.SpecVersion,
File: extDKK + ".dkk",
SHA256: hex.EncodeToString(ksum[:]),
CredentialID: hex.EncodeToString(back.CredentialID[:]),
CapsuleID: hex.EncodeToString(back.CapsuleID[:]),
AccessType: back.Type,
Material: hex.EncodeToString(back.Material),
CapsuleDigest: hex.EncodeToString(back.Verification.CapsuleDigest),
Extensions: exts(false, back.Noncritical),
Capsule: dkcFile,
ExpectedResult: "opens INNER_ACCESS_AGE of " + dkcFile + " and yields its CONTROL_CBOR",
}
if err := os.WriteFile(filepath.Join(dir, kf.File), kb.Bytes(), 0o644); err != nil {
return err
}
return testkit.WriteJSON(filepath.Join(dir, extDKK+".dkk.json"), kf)
}
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
// written is a fixture as its writer wrote it, with the credentials that
// only the writer knows.
type written struct {
dkc []byte
ids []*age.X25519Identity
portable *accesskey.AccessKey
}
// write writes the fixture s: format 3 with capsule.EncryptFiles, or with
// testkit.Build when only a generator of test vectors may write it, and
// format 2 with capsule.Encrypt, as a generator of test vectors.
func write(s spec) (*written, error) {
p := profile.Quicknet()
unlock, err := datekey.RoundTime(p, s.round)
if err != nil {
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
return nil, err
}
if s.body != nil {
body, err := s.body()
if err != nil {
return nil, err
}
b, err := testkit.Build{Round: s.round, Format: capsule.Format3, Declared: s.policy, Structure: s.policy, Padding: s.padding, Plaintext: body}.Make()
if err != nil {
return nil, err
}
return &written{dkc: b.DKC}, nil
}
opts := capsule.EncryptOptions{
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
Profile: p, UnlockAt: unlock, Policy: s.policy, NewPortableKey: s.portable, Padding: s.padding,
Noncritical: s.headerExt, ControlNoncritical: s.controlExt, Now: testkit.Fixed(testkit.Genesis()),
}
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
w := &written{}
for range s.recipients {
id, err := age.GenerateX25519Identity()
if err != nil {
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
return nil, err
}
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
w.ids = append(w.ids, id)
opts.Recipients = append(opts.Recipients, id.Recipient())
}
var dkc bytes.Buffer
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
var res *capsule.Result
if s.format == capsule.Format3 {
opts.Comment, opts.Author = s.comment, s.author
if s.area != 0 {
opts.TestVectors, opts.TestAreaLen = true, s.area
}
if s.configure != nil {
if err := s.configure(&opts); err != nil {
return nil, err
}
}
if s.signer != nil {
k, err := authorkey.NewFromSeed(s.signer)
if err != nil {
return nil, err
}
defer k.Clear()
opts.AuthorKey = k
}
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
res, err = capsule.EncryptFiles(&dkc, sources(s.files), opts)
} else {
opts.Length, opts.TestVectors = int64(len(s.plaintext)), true
res, err = capsule.Encrypt(&dkc, bytes.NewReader(s.plaintext), opts)
}
if err != nil {
return nil, err
}
w.dkc, w.portable = dkc.Bytes(), res.PortableKey
return w, nil
}
// generate writes the fixture s and records every intermediate value,
// recovered by opening it layer by layer.
func generate(dir string, s spec) error {
p := profile.Quicknet()
reg := testkit.Registry()
unlock, err := datekey.RoundTime(p, s.round)
if err != nil {
return err
}
w, err := write(s)
if err != nil {
return err
}
release := testkit.Release(s.round)
// Recover every intermediate value by opening the fixture step by step.
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
parts, err := testkit.Split(w.dkc)
if err != nil {
return err
}
format := capsule.Format(parts.Prelude[4])
header, err := capsule.DecodeHeader(parts.Header)
if err != nil {
return err
}
timeID, err := agewrap.NewTimeIdentity(p, s.round, release)
if err != nil {
return err
}
inner, err := decrypt(parts.Sealed, timeID)
if err != nil {
return err
}
control := inner
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
f := testkit.DKCFixture{Description: s.description, File: s.name + ".dkc", PlaintextFile: s.name + ".plaintext"}
var dkkFile string
var dkkBytes []byte
if s.policy == capsule.TimeAndKey {
st, err := agewrap.Stanzas(bytes.NewReader(inner))
if err != nil {
return err
}
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
f.InnerStanzas = stanzas(st)
var tryIDs []age.Identity
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
for _, id := range w.ids {
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
i, err := opens(id, st)
if err != nil {
return err
}
f.Identities = append(f.Identities, id.String())
f.IdentityStanzas = append(f.IdentityStanzas, i)
tryIDs = append(tryIDs, id)
}
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
if w.portable != nil {
var kb bytes.Buffer
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
if err := accesskey.Encode(&kb, w.portable); err != nil {
return err
}
dkkBytes = kb.Bytes()
dkkFile = s.name + ".dkk"
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
kid, err := w.portable.Identity()
if err != nil {
return err
}
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
i, err := opens(kid, st)
if err != nil {
return err
}
f.AccessKeyStanza = &i
tryIDs = append(tryIDs, kid)
}
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
accessID, err := agewrap.NewAccessIdentity(agewrap.AccessSlots, tryIDs...)
if err != nil {
return err
}
if control, err = decrypt(inner, accessID); err != nil {
return err
}
}
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
ctrl, err := capsule.DecodeControl(control, format)
if err != nil {
return err
}
outer, err := agewrap.Stanzas(bytes.NewReader(parts.Sealed))
if err != nil {
return err
}
payload, err := agewrap.Stanzas(bytes.NewReader(parts.Payload))
if err != nil {
return err
}
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
// The plaintext of PAYLOAD_AGE: the content and its padding, the content
// being BODY in format 3.
payloadID, err := agewrap.NewPayloadIdentity(ctrl.PayloadIdentity[:])
if err != nil {
return err
}
pt, err := decrypt(parts.Payload, payloadID)
if err != nil {
return err
}
padded, err := capsule.PaddedLength(ctrl.PayloadLength, ctrl.Padding)
if err != nil {
return err
}
if uint64(len(pt)) != padded || len(bytes.Trim(pt[ctrl.PayloadLength:], "\x00")) != 0 {
return errors.New("the plaintext of PAYLOAD_AGE is not the content and its padding")
}
plaintext := pt[:ctrl.PayloadLength]
switch format {
case capsule.Format2:
if !bytes.Equal(plaintext, s.plaintext) {
return errors.New("fixture plaintext mismatch")
}
case capsule.Format3:
// Without the verdicts of Open, which come later: the derived record
// that refreshRecord writes takes them from the opening.
if err := record3(&f, plaintext, nil); err != nil {
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
return err
}
}
// The fixture must open through the public API with the embedded release.
oo := capsule.OpenOptions{Registry: reg, Source: testkit.NewSource(release), Now: testkit.Fixed(unlock)}
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
for _, id := range w.ids {
oo.Identities = append(oo.Identities, id)
}
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
if s.policy == capsule.TimeAndKey && len(w.ids) == 0 {
oo.AccessKey = w.portable
}
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
sink := &testkit.MemorySink{}
oo.Sink = sink
var plain bytes.Buffer
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
opened, err := capsule.Open(context.Background(), &plain, bytes.NewReader(w.dkc), oo)
if err != nil {
return fmt.Errorf("fixture does not open: %w", err)
}
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
if format == capsule.Format3 {
// A signature is checked in the context of its capsule, which the
// record above does not have yet: the verdicts are those of Open.
v := opened.Verdicts
f.Verdicts = &testkit.FixtureVerdicts{Signature: string(v.Signature), Seal: string(v.Seal), Lines: v.Lines()}
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
if err := check3(&f, plaintext, opened, sink); err != nil {
return err
}
} else if !bytes.Equal(plain.Bytes(), plaintext) {
return errors.New("fixture plaintext mismatch")
}
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
sum := sha256.Sum256(w.dkc)
psum := sha256.Sum256(plaintext)
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
f.Spec = testkit.SpecVersion
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
f.Format = int(format)
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
f.SHA256 = hex.EncodeToString(sum[:])
f.Release = testkit.FixtureRelease{Round: release.Round, Signature: hex.EncodeToString(release.Signature)}
f.Prelude = hex.EncodeToString(parts.Prelude)
f.PublicHeader = hex.EncodeToString(parts.Header)
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
f.DateKey = header.DateKey.Compact()
f.CapsuleID = hex.EncodeToString(header.CapsuleID[:])
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
f.AccessPolicy = s.policy.String()
f.Structure = s.policy.String()
f.UnlockAt = unlock.Format(time.RFC3339)
f.HeaderBinding = hex.EncodeToString(ctrl.HeaderBinding[:])
f.OuterStanzas = stanzas(outer)
f.PayloadStanzas = stanzas(payload)
f.AccessKeyFile = dkkFile
f.ControlCBOR = hex.EncodeToString(control)
f.PayloadIdentity = hex.EncodeToString(ctrl.PayloadIdentity[:])
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
f.PayloadLength, f.Padding, f.PaddedLength = ctrl.PayloadLength, int(ctrl.Padding), padded
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
f.PlaintextSHA256 = hex.EncodeToString(psum[:])
// The extensions of the header as it is written: those given, and the
// public note that the writer adds (spec v0.11, §24.1).
f.HeaderExtensions = append(exts(true, header.Critical), exts(false, header.Noncritical)...)
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
f.ControlExt = exts(false, s.controlExt)
if s.signer != nil {
f.Signature = &testkit.FixtureSignature{SecretSeed: hex.EncodeToString(s.signer)}
}
for _, c := range opened.Inspection.Checks {
f.Stages = append(f.Stages, testkit.FixtureStage{Step: c.Step, Name: c.Name, OK: c.OK, Error: c.Error})
}
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
if err := os.WriteFile(filepath.Join(dir, f.File), w.dkc, 0o644); err != nil {
return err
}
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
if err := os.WriteFile(filepath.Join(dir, f.PlaintextFile), plaintext, 0o644); err != nil {
return err
}
if err := testkit.WriteJSON(filepath.Join(dir, s.name+".json"), f); err != nil {
return err
}
if dkkBytes == nil {
return nil
}
Format 3, step 6b: the nine fixtures of format 3 The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
k := w.portable
ksum := sha256.Sum256(dkkBytes)
kf := testkit.DKKFixture{
Description: "portable X25519 .dkk of " + f.File,
Spec: testkit.SpecVersion,
File: dkkFile,
SHA256: hex.EncodeToString(ksum[:]),
CredentialID: hex.EncodeToString(k.CredentialID[:]),
CapsuleID: hex.EncodeToString(k.CapsuleID[:]),
AccessType: k.Type,
Material: hex.EncodeToString(k.Material),
CapsuleDigest: hex.EncodeToString(k.Verification.CapsuleDigest),
Capsule: f.File,
ExpectedResult: "opens INNER_ACCESS_AGE of " + f.File + " and yields its CONTROL_CBOR",
}
if err := os.WriteFile(filepath.Join(dir, dkkFile), dkkBytes, 0o644); err != nil {
return err
}
return testkit.WriteJSON(filepath.Join(dir, s.name+".dkk.json"), kf)
}
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
// opens returns the index of the only stanza of st that id unwraps.
func opens(id age.Identity, st []*age.Stanza) (int, error) {
found := -1
for i, s := range st {
fk, err := id.Unwrap([]*age.Stanza{s})
clear(fk)
if errors.Is(err, age.ErrIncorrectIdentity) {
continue
}
if err != nil {
return 0, err
}
if found >= 0 {
return 0, fmt.Errorf("a credential opens stanzas %d and %d", found, i)
}
found = i
}
if found < 0 {
return 0, errors.New("a credential opens no stanza")
}
return found, nil
}
func decrypt(file []byte, id age.Identity) ([]byte, error) {
r, err := age.Decrypt(bytes.NewReader(file), id)
if err != nil {
return nil, err
}
var b bytes.Buffer
if _, err := b.ReadFrom(r); err != nil {
return nil, err
}
return b.Bytes(), nil
}
func stanzas(in []*age.Stanza) []testkit.FixtureStanza {
out := make([]testkit.FixtureStanza, len(in))
for i, s := range in {
out[i] = testkit.FixtureStanza{Type: s.Type, Args: s.Args}
}
return out
}
func exts(critical bool, in []extension.Extension) []testkit.FixtureExt {
var out []testkit.FixtureExt
for _, e := range in {
out = append(out, testkit.FixtureExt{Critical: critical, ID: e.ID, Version: e.Version, Data: hex.EncodeToString(e.Data)})
}
return out
}

Powered by TurnKey Linux.