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 datekeys is the reference Go implementation of the DateKeys Protocol
|
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
|
|
|
// Specification v0.9 (spec/DateKeys_Protocol_Specification_v0.9.md).
|
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
|
|
|
//
|
|
|
|
|
// The protocol objects live in subpackages:
|
|
|
|
|
//
|
|
|
|
|
// - datekey: DateKey resolution and the canonical dk1_ form (spec §14-§19).
|
|
|
|
|
// - profile: Provider Profiles and the pinned Quicknet profile (spec §10-§13).
|
|
|
|
|
// - provider, provider/drand: release sources and local BLS verification (spec §45-§52).
|
|
|
|
|
// - capsule: the DateKeyCap .dkc container (spec §20-§39, §61-§63).
|
|
|
|
|
// - accesskey: the DateKeys Access Key .dkk credential (spec §40-§44).
|
|
|
|
|
// - extension: the generic extension mechanism (spec §54).
|
|
|
|
|
//
|
|
|
|
|
// This package holds the normative error catalogue of spec §69. Every protocol
|
|
|
|
|
// failure returned by this module wraps exactly one of these sentinels, so
|
|
|
|
|
// callers can match them with [errors.Is] and extract the code with [Code].
|
|
|
|
|
package datekeys
|
|
|
|
|
|
|
|
|
|
import "errors"
|
|
|
|
|
|
|
|
|
|
// Error is a normative DateKeys error (spec §69). Values are compared by
|
|
|
|
|
// identity; use [errors.Is] against the exported sentinels.
|
|
|
|
|
type Error struct {
|
|
|
|
|
code string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Error returns the normative code, for example "ERR_INVALID_MAGIC".
|
|
|
|
|
func (e *Error) Error() string { return e.code }
|
|
|
|
|
|
|
|
|
|
// Code returns the normative code, for example "ERR_INVALID_MAGIC".
|
|
|
|
|
func (e *Error) Code() string { return e.code }
|
|
|
|
|
|
|
|
|
|
// Normative errors, spec §69.
|
|
|
|
|
var (
|
|
|
|
|
// ErrInvalidMagic: the object does not start with DKC1 or DKK1 (spec §22, §40).
|
|
|
|
|
ErrInvalidMagic = &Error{"ERR_INVALID_MAGIC"}
|
|
|
|
|
// ErrUnsupportedVersion: an unknown framing or schema version (spec §22, §70).
|
|
|
|
|
ErrUnsupportedVersion = &Error{"ERR_UNSUPPORTED_VERSION"}
|
|
|
|
|
// ErrInvalidFlags: FLAGS or RESERVED are not zero (spec §22, §40).
|
|
|
|
|
ErrInvalidFlags = &Error{"ERR_INVALID_FLAGS"}
|
|
|
|
|
// ErrNonCanonicalCBOR: the bytes are not the unique deterministic CBOR
|
|
|
|
|
// encoding of a valid instance of the normative schema (spec §58, §58.1).
|
|
|
|
|
ErrNonCanonicalCBOR = &Error{"ERR_NON_CANONICAL_CBOR"}
|
|
|
|
|
// ErrUnknownProfile: the DateKey names a profile that is not pinned locally (spec §13).
|
|
|
|
|
ErrUnknownProfile = &Error{"ERR_UNKNOWN_PROFILE"}
|
|
|
|
|
// ErrProfileMismatch: a chain hash or profile does not match the pinned profile (spec §35, §63).
|
|
|
|
|
ErrProfileMismatch = &Error{"ERR_PROFILE_MISMATCH"}
|
|
|
|
|
// ErrDateKeyInvalid: a DateKey that cannot be decoded or validated (spec §18, §19).
|
|
|
|
|
ErrDateKeyInvalid = &Error{"ERR_DATEKEY_INVALID"}
|
|
|
|
|
// ErrDateKeyNonCanonical: a valid DateKey in a non-canonical encoding (spec §19).
|
|
|
|
|
ErrDateKeyNonCanonical = &Error{"ERR_DATEKEY_NON_CANONICAL"}
|
|
|
|
|
// ErrRoundMismatch: a round that differs from the locally resolved one (spec §17, §63).
|
|
|
|
|
ErrRoundMismatch = &Error{"ERR_ROUND_MISMATCH"}
|
|
|
|
|
// ErrReleaseUnavailable: the release is not published yet or no source delivered it (spec §45-§50).
|
|
|
|
|
ErrReleaseUnavailable = &Error{"ERR_RELEASE_UNAVAILABLE"}
|
|
|
|
|
// ErrReleaseInvalid: a release that fails local verification (spec §51).
|
|
|
|
|
ErrReleaseInvalid = &Error{"ERR_RELEASE_INVALID"}
|
|
|
|
|
// ErrAccessRequired: the policy requires an access credential and none was supplied (spec §33).
|
|
|
|
|
ErrAccessRequired = &Error{"ERR_ACCESS_REQUIRED"}
|
|
|
|
|
// ErrAccessInvalid: the supplied credentials do not open this capsule (spec §33, §38).
|
|
|
|
|
ErrAccessInvalid = &Error{"ERR_ACCESS_INVALID"}
|
|
|
|
|
// ErrPolicyStructureMismatch: the cryptographic structure does not match the
|
|
|
|
|
// declared access policy or the stanza rules of V1 (spec §25, §29, §32, §33, §36).
|
|
|
|
|
ErrPolicyStructureMismatch = &Error{"ERR_POLICY_STRUCTURE_MISMATCH"}
|
|
|
|
|
// ErrHeaderBinding: header_binding does not match PRELUDE || PUBLIC_HEADER (spec §26).
|
|
|
|
|
ErrHeaderBinding = &Error{"ERR_HEADER_BINDING"}
|
|
|
|
|
// ErrIntegrity: truncation, corruption or failed authentication of framing or age data (spec §4, §55).
|
|
|
|
|
ErrIntegrity = &Error{"ERR_INTEGRITY"}
|
|
|
|
|
// ErrExtensionCriticalUnknown: a critical extension this implementation does not know (spec §54).
|
|
|
|
|
ErrExtensionCriticalUnknown = &Error{"ERR_EXTENSION_CRITICAL_UNKNOWN"}
|
|
|
|
|
// ErrExtensionDataInvalid: a known extension whose data does not follow its
|
|
|
|
|
// registered schema. It rejects the object only for a critical extension; a
|
|
|
|
|
// noncritical one is reported as unusable (spec §54, §72).
|
|
|
|
|
ErrExtensionDataInvalid = &Error{"ERR_EXTENSION_DATA_INVALID"}
|
Format 3, step 2: the codec of BODY, security and the head
- Format3 and the control of schema version 3, with the keys of
version 2 (spec 31). The PRELUDE still rejects VERSION 3 until the
reader opens format 3, in step 3, with the test data that expect it.
- The frame of BODY (spec 29.2): AREA_LEN, SECURITY_LEN and HEAD_LEN,
their limits against L and the zeros of the area, all ERR_INTEGRITY.
- security (spec 29.3, 29.7): the outer map, with the signature and
the seal as separately encoded byte strings, and its verdicts X, F0,
F1, S0, S1 and S2, which never fail. The first row that holds
decides, so a seal that breaks its schema is S2 before its type is
read. Writers of this version write it empty, 22 bytes.
- The head (spec 29.4): layer 2 with its type tag and version 1;
layer 3 with the CDDL, R1 and R8; layer 4 in key order, the comment
and the declared author, each file with R2 to R6c, R10 and its
layout, R7 and R9 over the tree, all ERR_HEAD_INVALID, and then the
critical extensions of the new extension.Head object.
- ERR_HEAD_INVALID is declared; All lists it once SpecVersion moves to
0.10 with the test data, in step 6.
- The control tests take format 4 as the caller error that format 3
was, as section 76 of the spec anticipated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
// ErrHeadInvalid: the head of a format 3 capsule is well encoded but one
|
|
|
|
|
// of its fields breaks its rules: a path, the comment, the declared author
|
Format 3, step 6a: specification 0.10 and ERR_HEAD_INVALID
- SpecVersion is 0.10: the version command, the catalogue test and the
spec field of every test data file name spec v0.10. The regenerated
test data change in that field only.
- All lists ERR_HEAD_INVALID, last, as section 69 of the spec does.
- The tests of the path rules and of format 3 held literal invisible
and combining characters (ZWJ, VS16, U+202E, soft hyphen, the Kelvin
sign and others), which an editor could normalize or hide; they are
Go escapes now, with the same values.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
// or the layout of the files (spec §29.4 to §29.6). New in v0.10.
|
Format 3, step 2: the codec of BODY, security and the head
- Format3 and the control of schema version 3, with the keys of
version 2 (spec 31). The PRELUDE still rejects VERSION 3 until the
reader opens format 3, in step 3, with the test data that expect it.
- The frame of BODY (spec 29.2): AREA_LEN, SECURITY_LEN and HEAD_LEN,
their limits against L and the zeros of the area, all ERR_INTEGRITY.
- security (spec 29.3, 29.7): the outer map, with the signature and
the seal as separately encoded byte strings, and its verdicts X, F0,
F1, S0, S1 and S2, which never fail. The first row that holds
decides, so a seal that breaks its schema is S2 before its type is
read. Writers of this version write it empty, 22 bytes.
- The head (spec 29.4): layer 2 with its type tag and version 1;
layer 3 with the CDDL, R1 and R8; layer 4 in key order, the comment
and the declared author, each file with R2 to R6c, R10 and its
layout, R7 and R9 over the tree, all ERR_HEAD_INVALID, and then the
critical extensions of the new extension.Head object.
- ERR_HEAD_INVALID is declared; All lists it once SpecVersion moves to
0.10 with the test data, in step 6.
- The control tests take format 4 as the caller error that format 3
was, as section 76 of the spec anticipated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
ErrHeadInvalid = &Error{"ERR_HEAD_INVALID"}
|
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
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// All returns every normative error in the order of spec §69.
|
|
|
|
|
func All() []*Error {
|
|
|
|
|
return []*Error{
|
|
|
|
|
ErrInvalidMagic, ErrUnsupportedVersion, ErrInvalidFlags, ErrNonCanonicalCBOR,
|
|
|
|
|
ErrUnknownProfile, ErrProfileMismatch, ErrDateKeyInvalid, ErrDateKeyNonCanonical,
|
|
|
|
|
ErrRoundMismatch, ErrReleaseUnavailable, ErrReleaseInvalid, ErrAccessRequired,
|
|
|
|
|
ErrAccessInvalid, ErrPolicyStructureMismatch, ErrHeaderBinding, ErrIntegrity,
|
Format 3, step 6a: specification 0.10 and ERR_HEAD_INVALID
- SpecVersion is 0.10: the version command, the catalogue test and the
spec field of every test data file name spec v0.10. The regenerated
test data change in that field only.
- All lists ERR_HEAD_INVALID, last, as section 69 of the spec does.
- The tests of the path rules and of format 3 held literal invisible
and combining characters (ZWJ, VS16, U+202E, soft hyphen, the Kelvin
sign and others), which an editor could normalize or hide; they are
Go escapes now, with the same values.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
ErrExtensionCriticalUnknown, ErrExtensionDataInvalid, ErrHeadInvalid,
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Code returns the normative code of the first DateKeys error in err's tree,
|
|
|
|
|
// or "" if err does not wrap one.
|
|
|
|
|
func Code(err error) string {
|
|
|
|
|
var e *Error
|
|
|
|
|
if errors.As(err, &e) {
|
|
|
|
|
return e.code
|
|
|
|
|
}
|
|
|
|
|
return ""
|
|
|
|
|
}
|