Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
|
|
|
package testkit
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"regexp"
|
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// FixtureStanza is the visible part of an age stanza in a fixture.
|
|
|
|
|
type FixtureStanza struct {
|
|
|
|
|
Type string `json:"type"`
|
|
|
|
|
Args []string `json:"args"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FixtureRelease is the release a fixture opens with.
|
|
|
|
|
type FixtureRelease struct {
|
|
|
|
|
Round uint64 `json:"round"`
|
|
|
|
|
Signature string `json:"signature"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FixtureStage is the expected result of one step of spec §63.
|
|
|
|
|
type FixtureStage struct {
|
|
|
|
|
Step int `json:"step"`
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
OK bool `json:"ok"`
|
|
|
|
|
Error string `json:"error,omitempty"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DKCFixture holds the expected values of an official .dkc fixture (spec §67).
|
|
|
|
|
type DKCFixture struct {
|
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
|
|
|
Description string `json:"description"`
|
|
|
|
|
Spec string `json:"spec"`
|
|
|
|
|
// Format is the capsule format, the VERSION of the PRELUDE (spec §22).
|
|
|
|
|
Format int `json:"format"`
|
|
|
|
|
File string `json:"file"`
|
|
|
|
|
SHA256 string `json:"sha256"`
|
|
|
|
|
Release FixtureRelease `json:"release"`
|
|
|
|
|
Prelude string `json:"prelude"`
|
|
|
|
|
PublicHeader string `json:"public_header"`
|
|
|
|
|
DateKey string `json:"datekey"`
|
|
|
|
|
CapsuleID string `json:"capsule_id"`
|
|
|
|
|
AccessPolicy string `json:"access_policy"`
|
|
|
|
|
Structure string `json:"structure"`
|
|
|
|
|
UnlockAt string `json:"unlock_at"`
|
|
|
|
|
HeaderBinding string `json:"header_binding"`
|
|
|
|
|
OuterStanzas []FixtureStanza `json:"outer_stanzas"`
|
|
|
|
|
PayloadStanzas []FixtureStanza `json:"payload_stanzas"`
|
|
|
|
|
InnerStanzas []FixtureStanza `json:"inner_stanzas,omitempty"`
|
|
|
|
|
// AccessKeyStanza and IdentityStanzas are, in a format 2 time_and_key
|
|
|
|
|
// fixture, the index in InnerStanzas of the stanza each credential
|
|
|
|
|
// opens: the .dkk, and each identity of Identities in order. The stanzas
|
|
|
|
|
// no credential opens are dummies. Official vectors are the only place
|
|
|
|
|
// where this is recorded (spec §39, §67).
|
|
|
|
|
AccessKeyStanza *int `json:"access_key_stanza,omitempty"`
|
|
|
|
|
IdentityStanzas []int `json:"identity_stanzas,omitempty"`
|
|
|
|
|
AccessKeyFile string `json:"access_key_file,omitempty"`
|
|
|
|
|
Identities []string `json:"identities,omitempty"`
|
|
|
|
|
ControlCBOR string `json:"control_cbor"`
|
|
|
|
|
PayloadIdentity string `json:"payload_identity"`
|
|
|
|
|
// PayloadLength is L, the length of the content, the plaintext the
|
|
|
|
|
// reader delivers. In format 2 the plaintext of PAYLOAD_AGE is
|
|
|
|
|
// PaddedLength bytes, P = rule(L), the rule being Padding (spec §29.1).
|
|
|
|
|
PayloadLength uint64 `json:"payload_length"`
|
|
|
|
|
Padding int `json:"padding,omitempty"`
|
|
|
|
|
PaddedLength uint64 `json:"padded_length,omitempty"`
|
|
|
|
|
PlaintextFile string `json:"plaintext_file"`
|
|
|
|
|
PlaintextSHA256 string `json:"plaintext_sha256"`
|
|
|
|
|
HeaderExtensions []FixtureExt `json:"header_extensions,omitempty"`
|
|
|
|
|
ControlExt []FixtureExt `json:"control_extensions,omitempty"`
|
|
|
|
|
Stages []FixtureStage `json:"stages"`
|
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FixtureExt is an extension in a fixture.
|
|
|
|
|
type FixtureExt struct {
|
|
|
|
|
Critical bool `json:"critical"`
|
|
|
|
|
ID string `json:"id"`
|
|
|
|
|
Version uint64 `json:"version"`
|
|
|
|
|
Data string `json:"data,omitempty"` // hex of the exact data bytes; absent without data
|
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DKKFixture holds the expected values of an official .dkk fixture (spec §68).
|
|
|
|
|
type DKKFixture struct {
|
|
|
|
|
Description string `json:"description"`
|
|
|
|
|
Spec string `json:"spec"`
|
|
|
|
|
File string `json:"file"`
|
|
|
|
|
SHA256 string `json:"sha256"`
|
|
|
|
|
CredentialID string `json:"credential_id"`
|
|
|
|
|
CapsuleID string `json:"capsule_id"`
|
|
|
|
|
AccessType string `json:"access_type"`
|
|
|
|
|
Material string `json:"access_material"`
|
|
|
|
|
CapsuleDigest string `json:"capsule_digest,omitempty"`
|
|
|
|
|
Extensions []FixtureExt `json:"extensions,omitempty"`
|
|
|
|
|
Capsule string `json:"capsule"`
|
|
|
|
|
ExpectedResult string `json:"expected_result"`
|
|
|
|
|
Stages []FixtureStage `json:"stages,omitempty"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ReadJSON decodes a JSON file.
|
|
|
|
|
func ReadJSON(path string, v any) error {
|
|
|
|
|
b, err := os.ReadFile(path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return json.Unmarshal(b, v)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// WriteJSON writes v as indented JSON with a trailing newline.
|
|
|
|
|
func WriteJSON(path string, v any) error {
|
|
|
|
|
b, err := json.MarshalIndent(v, "", " ")
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return writeFile(path, append(b, '\n'))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// editPattern is an Edit as json.MarshalIndent spreads it over five lines.
|
|
|
|
|
var editPattern = regexp.MustCompile(`\[\n\s*(\d+),\n\s*(\d+),\n\s*("[0-9a-f]*")\n\s*\]`)
|
|
|
|
|
|
|
|
|
|
// WriteJSONEdits is WriteJSON with every Edit, [at, delete, "hex"], on one
|
|
|
|
|
// line.
|
|
|
|
|
func WriteJSONEdits(path string, v any) error {
|
|
|
|
|
b, err := json.MarshalIndent(v, "", " ")
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return writeFile(path, append(editPattern.ReplaceAll(b, []byte("[$1, $2, $3]")), '\n'))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func writeFile(path string, b []byte) error {
|
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
|
|
|
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return os.WriteFile(path, b, 0o644)
|
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
|
|
|
}
|