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 capsule implements the DateKeyCap .dkc container (spec §20-§39):
|
|
|
|
|
// framing, PUBLIC_HEADER, CONTROL_CBOR, header_binding, the time_only and
|
|
|
|
|
// time_and_key constructions, and the encryption (spec §61, §62) and
|
|
|
|
|
// decryption (spec §63) flows.
|
|
|
|
|
//
|
|
|
|
|
// A .dkc is PRELUDE || PUBLIC_HEADER || SEALED_CONTROL || PAYLOAD_AGE, where
|
|
|
|
|
// SEALED_CONTROL and PAYLOAD_AGE are complete standard age files and the
|
|
|
|
|
// payload runs to EOF (spec §22, §28-§34).
|
|
|
|
|
package capsule
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"crypto/sha256"
|
|
|
|
|
"encoding/binary"
|
|
|
|
|
"encoding/hex"
|
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
|
|
|
"errors"
|
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
|
|
|
"fmt"
|
|
|
|
|
|
|
|
|
|
datekeys "g.activething.com/go/DateKeys"
|
|
|
|
|
"g.activething.com/go/DateKeys/codec"
|
|
|
|
|
"g.activething.com/go/DateKeys/datekey"
|
|
|
|
|
"g.activething.com/go/DateKeys/extension"
|
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
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Framing constants (spec §22, §23) and parser limits (spec §57).
|
|
|
|
|
const (
|
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
|
|
|
Magic = "DKC1"
|
|
|
|
|
PreludeSize = 16
|
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
|
|
|
|
|
|
|
|
MaxPublicHeaderLen = 1 << 20 // 1 MiB
|
|
|
|
|
MaxSealedControlLen = 64 << 20 // 64 MiB
|
|
|
|
|
|
|
|
|
|
HeaderTypeTag = "datekeycap"
|
|
|
|
|
HeaderVersion = 1
|
|
|
|
|
ControlTypeTag = "datekeys-control"
|
|
|
|
|
|
|
|
|
|
CapsuleIDSize = 16
|
|
|
|
|
)
|
|
|
|
|
|
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 is the format of a capsule: the VERSION byte of its PRELUDE. It
|
|
|
|
|
// also fixes the schema version of its CONTROL_CBOR, the number of stanzas of
|
|
|
|
|
// its INNER_ACCESS_AGE and whether its payload is padded (spec §22).
|
|
|
|
|
type Format uint8
|
|
|
|
|
|
|
|
|
|
// Formats of V1 (spec §22). A reader opens both.
|
|
|
|
|
const (
|
|
|
|
|
// Format1 is the format of spec v0.8.2: one or more stanzas in
|
|
|
|
|
// INNER_ACCESS_AGE and a payload without padding. Only a generator of
|
|
|
|
|
// test vectors may write it (spec §62.1, §70): Encrypt never does.
|
|
|
|
|
Format1 Format = 1
|
|
|
|
|
// Format2 is the format of spec v0.9, the one Encrypt writes: exactly 16
|
|
|
|
|
// stanzas in INNER_ACCESS_AGE and a padded payload (spec §29.1, §39).
|
|
|
|
|
Format2 Format = 2
|
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
|
|
|
// Format3 is the format of spec v0.10: the padded plaintext of
|
|
|
|
|
// PAYLOAD_AGE is BODY, with the security area, the head and several
|
|
|
|
|
// files (spec §29.2 to §29.7).
|
|
|
|
|
Format3 Format = 3
|
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 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
|
|
|
func (f Format) valid() bool { return f >= Format1 && f <= Format3 }
|
|
|
|
|
|
|
|
|
|
// padded reports whether the payload of format f is padded, with L and the
|
|
|
|
|
// padding code in keys 6 and 7 of its control (spec §29.1, §31).
|
|
|
|
|
func (f Format) padded() bool { return f == Format2 || f == Format3 }
|
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
|
|
|
|
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
|
|
|
// Policy is the declared access policy of PUBLIC_HEADER (spec §25).
|
|
|
|
|
type Policy uint8
|
|
|
|
|
|
|
|
|
|
// Access policies of V1.
|
|
|
|
|
const (
|
|
|
|
|
TimeOnly Policy = 0
|
|
|
|
|
TimeAndKey Policy = 1
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func (p Policy) String() string {
|
|
|
|
|
switch p {
|
|
|
|
|
case TimeOnly:
|
|
|
|
|
return "time_only"
|
|
|
|
|
case TimeAndKey:
|
|
|
|
|
return "time_and_key"
|
|
|
|
|
}
|
|
|
|
|
return fmt.Sprintf("policy(%d)", uint8(p))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ParsePolicy parses "time_only" or "time_and_key".
|
|
|
|
|
func ParsePolicy(s string) (Policy, error) {
|
|
|
|
|
switch s {
|
|
|
|
|
case "time_only":
|
|
|
|
|
return TimeOnly, nil
|
|
|
|
|
case "time_and_key":
|
|
|
|
|
return TimeAndKey, nil
|
|
|
|
|
}
|
|
|
|
|
return 0, fmt.Errorf("capsule: unknown access policy %q", s)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p Policy) valid() bool { return p == TimeOnly || p == TimeAndKey }
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// PRELUDE
|
|
|
|
|
|
|
|
|
|
// Prelude is the fixed 16-byte PRELUDE (spec §22, §23).
|
|
|
|
|
type Prelude 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
|
|
|
Format Format // VERSION
|
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
|
|
|
PublicHeaderLen uint32
|
|
|
|
|
SealedControlLen uint32
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Bytes returns the exact 16 prelude bytes, the ones covered by
|
|
|
|
|
// header_binding.
|
|
|
|
|
func (p Prelude) Bytes() [PreludeSize]byte {
|
|
|
|
|
var b [PreludeSize]byte
|
|
|
|
|
copy(b[0:4], Magic)
|
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
|
|
|
b[4] = byte(p.Format)
|
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
|
|
|
// FLAGS (b[5]) and RESERVED (b[6:8]) are zero in V1.
|
|
|
|
|
binary.BigEndian.PutUint32(b[8:12], p.PublicHeaderLen)
|
|
|
|
|
binary.BigEndian.PutUint32(b[12:16], p.SealedControlLen)
|
|
|
|
|
return b
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PayloadOffset is where PAYLOAD_AGE starts:
|
|
|
|
|
// 16 + PUBLIC_HEADER_LEN + SEALED_CONTROL_LEN (spec §63).
|
|
|
|
|
func (p Prelude) PayloadOffset() int64 {
|
|
|
|
|
return PreludeSize + int64(p.PublicHeaderLen) + int64(p.SealedControlLen)
|
|
|
|
|
}
|
|
|
|
|
|
Spec v0.8.2 refinements: error precedence, trust model, strict order
Approved refinements, each recorded with its reproducible case in the
§76 v0.8.2 subsection:
- §69.1: layered error model with normative precedence (frame, type tag
and version, CBOR profile and CDDL, then fields with their own code in
ascending key order; across steps the §63 order decides), with a scope
paragraph for the optional steps 5, 6 and 8.
- §55.1: normative trust table per section (who can write it, from which
step it is bound, what it never proves); §72: security-relevant claims
go in CONTROL_CBOR or under a signature, .dkk data is advisory.
- §31/§54: extension arrays in strictly ascending unsigned byte order of
extension_id (one rule for order and uniqueness).
- Gaps a second implementation needed: §28.1 malformed age headers,
§15/§19 latest unlock time and dk1_ reading rules, §22/§23/§57 length
lower bounds, §63 step 8 tlock argument comparison and step 9 order,
§12.1 profile validation with the drand chain-hash formula, §74 table
of implementation limits.
Reference alignment: .dkk errors only at step 9.a (new
OpenOptions.AccessKeyFile, used by the CLI), CR/LF in dk1_ is
ERR_DATEKEY_INVALID, BODY_LEN 0 is ERR_INTEGRITY, nil identities are not
credentials, and AccessIdentity tries every identity on every stanza so
its verdict does not depend on their order. dk1.json gains three
vectors; every other testdata file is byte-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
// ParsePrelude validates the prelude (spec §22, §23, §63 steps 1 and 2), in
|
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 order of spec §23: magic, a complete prelude, version (the format, 1 or
|
|
|
|
|
// 2), FLAGS == 0 and RESERVED == 0, and lengths from 1 up to the limits of
|
|
|
|
|
// spec §57.
|
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
|
|
|
func ParsePrelude(b []byte) (Prelude, error) {
|
|
|
|
|
if len(b) < 4 || string(b[0:4]) != Magic {
|
|
|
|
|
return Prelude{}, fmt.Errorf("capsule: %w", datekeys.ErrInvalidMagic)
|
|
|
|
|
}
|
|
|
|
|
if len(b) < PreludeSize {
|
|
|
|
|
return Prelude{}, fmt.Errorf("capsule: truncated prelude: %w", datekeys.ErrIntegrity)
|
|
|
|
|
}
|
Format 3, step 3: the reader
Open reads format 3 (spec 29.2 to 29.7, 63 steps 17 and 18): the
PRELUDE accepts VERSION 3, and the files go to a Sink.
- Sink: Begin with the validated head, Create for each file in the
order of the head, and Commit only after every check of step 17;
after any failure that follows a successful Begin, Abort, once. A
format 3 capsule without a Sink fails right after step 2 with
ErrSinkRequired, a caller error with no code, no failed step and no
request; a capsule of format 1 or 2 without dst fails there too.
- Step 17 in its substeps: the frame and the area, security and its
verdicts, which never fail, the head, the files filling CONTENT, the
SHA-256 of each file and the padding. A failure of age or a
plaintext whose length is not P prevails; otherwise the first
substep that fails decides, and a code other than ERR_INTEGRITY is
reported only after reading PAYLOAD_AGE to its end.
- The reads of BODY grow with the bytes received, never with AREA_LEN,
HEAD_LEN or a declared size (spec 57); a test measures it.
- A failure of the Sink is the caller's own error with ERR_INTEGRITY,
as one of dst is in formats 1 and 2.
- Opened gains Head, Verdicts, AreaLen and UnusableHeadExtensions.
- Test data: "version changed" sets VERSION 4, and the format 2 list
gains "format 2 time_only relabeled format 3", which fails at step
14, as section 64 of spec v0.10 lists: 126 cases, 89 of the spec.
The randomly built capsules keep their recorded bytes.
- testkit: Build writes format 3 and can edit the padded plaintext;
Head3, Body3, DiscardSink and MemorySink build and open BODY.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
if !Format(b[4]).valid() {
|
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
|
|
|
return Prelude{}, fmt.Errorf("capsule: framing version %d: %w", b[4], datekeys.ErrUnsupportedVersion)
|
|
|
|
|
}
|
|
|
|
|
if b[5] != 0 || b[6] != 0 || b[7] != 0 {
|
|
|
|
|
return Prelude{}, fmt.Errorf("capsule: flags %#x, reserved %#02x%02x: %w", b[5], b[6], b[7], datekeys.ErrInvalidFlags)
|
|
|
|
|
}
|
|
|
|
|
p := Prelude{
|
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: Format(b[4]),
|
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
|
|
|
PublicHeaderLen: binary.BigEndian.Uint32(b[8:12]),
|
|
|
|
|
SealedControlLen: binary.BigEndian.Uint32(b[12:16]),
|
|
|
|
|
}
|
|
|
|
|
if p.PublicHeaderLen == 0 || p.PublicHeaderLen > MaxPublicHeaderLen {
|
|
|
|
|
return Prelude{}, fmt.Errorf("capsule: PUBLIC_HEADER_LEN %d outside 1..%d: %w", p.PublicHeaderLen, MaxPublicHeaderLen, datekeys.ErrIntegrity)
|
|
|
|
|
}
|
|
|
|
|
if p.SealedControlLen == 0 || p.SealedControlLen > MaxSealedControlLen {
|
|
|
|
|
return Prelude{}, fmt.Errorf("capsule: SEALED_CONTROL_LEN %d outside 1..%d: %w", p.SealedControlLen, MaxSealedControlLen, datekeys.ErrIntegrity)
|
|
|
|
|
}
|
|
|
|
|
return p, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// HeaderBinding returns SHA-256(PRELUDE || PUBLIC_HEADER_BYTES) over the exact
|
|
|
|
|
// stored bytes; the header is never re-serialized for it (spec §26).
|
|
|
|
|
func HeaderBinding(prelude [PreludeSize]byte, publicHeader []byte) [32]byte {
|
|
|
|
|
h := sha256.New()
|
|
|
|
|
h.Write(prelude[:])
|
|
|
|
|
h.Write(publicHeader)
|
|
|
|
|
var out [32]byte
|
|
|
|
|
h.Sum(out[:0])
|
|
|
|
|
return out
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// PUBLIC_HEADER
|
|
|
|
|
|
|
|
|
|
// Header is PUBLIC_HEADER (spec §24). There is no separate profile_id: the
|
|
|
|
|
// profile comes from the DateKey, the single source of truth.
|
|
|
|
|
type Header struct {
|
|
|
|
|
CapsuleID [CapsuleIDSize]byte // key 2
|
|
|
|
|
DateKey datekey.DateKey // key 3, canonical dk1_
|
|
|
|
|
Policy Policy // key 4, access_policy
|
|
|
|
|
Critical []extension.Extension // key 5
|
|
|
|
|
Noncritical []extension.Extension // key 6
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// headerWire is PUBLIC_HEADER as it is encoded: keys 2 to 6, keys 0 and 1
|
|
|
|
|
// being the constants HeaderTypeTag and HeaderVersion.
|
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
|
|
|
type headerWire struct {
|
|
|
|
|
CapsuleID []byte // key 2
|
|
|
|
|
DateKey string // key 3
|
|
|
|
|
Policy uint64 // key 4
|
|
|
|
|
Critical []extension.Extension // key 5, omitted when empty
|
|
|
|
|
Noncritical []extension.Extension // key 6, omitted when empty
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (w *headerWire) encode(e *codec.Encoder) {
|
|
|
|
|
e.Map(5 + nonEmpty(w.Critical) + nonEmpty(w.Noncritical))
|
|
|
|
|
e.Uint(0)
|
|
|
|
|
e.Text(HeaderTypeTag)
|
|
|
|
|
e.Uint(1)
|
|
|
|
|
e.Uint(HeaderVersion)
|
|
|
|
|
e.Uint(2)
|
|
|
|
|
e.Bstr(w.CapsuleID)
|
|
|
|
|
e.Uint(3)
|
|
|
|
|
e.Text(w.DateKey)
|
|
|
|
|
e.Uint(4)
|
|
|
|
|
e.Uint(w.Policy)
|
|
|
|
|
encodeExtensions(e, 5, w.Critical, w.Noncritical)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// decode reads PUBLIC_HEADER with every CDDL rule whose violation is
|
|
|
|
|
// ErrNonCanonicalCBOR, including the extension arrays; the DateKey, which
|
|
|
|
|
// has codes of its own (spec §57), is parsed afterwards.
|
|
|
|
|
func (w *headerWire) decode(d *codec.Decoder) error {
|
|
|
|
|
pairs, err := d.Map(7)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
var seen uint
|
|
|
|
|
for range pairs {
|
|
|
|
|
k, err := d.Key()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
switch k {
|
|
|
|
|
case 0:
|
|
|
|
|
_, err = d.Text(len(HeaderTypeTag))
|
|
|
|
|
case 1:
|
|
|
|
|
_, err = d.Uint(HeaderVersion)
|
|
|
|
|
case 2:
|
|
|
|
|
w.CapsuleID, err = d.Bstr(CapsuleIDSize, CapsuleIDSize)
|
|
|
|
|
case 3:
|
|
|
|
|
w.DateKey, err = d.Text(MaxPublicHeaderLen)
|
|
|
|
|
case 4:
|
|
|
|
|
// Compared as read, before any narrowing to Policy, which would
|
|
|
|
|
// let 256, 257, 2^32 and the like pass as a V1 policy.
|
|
|
|
|
if w.Policy, err = d.Uint(codec.MaxSafeUint); err == nil && w.Policy > uint64(TimeAndKey) {
|
|
|
|
|
err = fmt.Errorf("access_policy %d is not defined in V1: %w", w.Policy, datekeys.ErrNonCanonicalCBOR)
|
|
|
|
|
}
|
|
|
|
|
case 5:
|
|
|
|
|
w.Critical, err = extension.DecodeArray(d)
|
|
|
|
|
case 6:
|
|
|
|
|
w.Noncritical, err = extension.DecodeArray(d)
|
|
|
|
|
default:
|
|
|
|
|
return fmt.Errorf("key %d is not defined: %w", k, datekeys.ErrNonCanonicalCBOR)
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("key %d: %w", k, err)
|
|
|
|
|
}
|
|
|
|
|
seen |= 1 << k
|
|
|
|
|
}
|
|
|
|
|
if err := required(seen, 5); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return d.EndMap()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// nonEmpty is 1 for an extension array that is written and 0 for one that is
|
|
|
|
|
// omitted (spec §58.1).
|
|
|
|
|
func nonEmpty(exts []extension.Extension) int {
|
|
|
|
|
if len(exts) == 0 {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// encodeExtensions writes the critical and noncritical arrays at keys key and
|
|
|
|
|
// key+1, each only when it is not empty.
|
|
|
|
|
func encodeExtensions(e *codec.Encoder, key uint64, critical, noncritical []extension.Extension) {
|
|
|
|
|
for i, exts := range [][]extension.Extension{critical, noncritical} {
|
|
|
|
|
if len(exts) > 0 {
|
|
|
|
|
e.Uint(key + uint64(i))
|
|
|
|
|
extension.EncodeArray(e, exts)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// required checks that seen holds the keys 0 to n-1, which are required.
|
|
|
|
|
func required(seen uint, n int) error {
|
|
|
|
|
for k := range n {
|
|
|
|
|
if seen&(1<<k) == 0 {
|
|
|
|
|
return fmt.Errorf("key %d is missing: %w", k, datekeys.ErrNonCanonicalCBOR)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CapsuleIDHex returns the capsule_id in hexadecimal.
|
|
|
|
|
func (h *Header) CapsuleIDHex() string { return hex.EncodeToString(h.CapsuleID[:]) }
|
|
|
|
|
|
|
|
|
|
// EncodeHeader returns the Deterministic CBOR bytes of h, at most
|
|
|
|
|
// MaxPublicHeaderLen of them (spec §57).
|
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
|
|
|
func EncodeHeader(h *Header) ([]byte, error) {
|
|
|
|
|
compact := h.DateKey.Compact()
|
|
|
|
|
if compact == "" {
|
|
|
|
|
return nil, fmt.Errorf("capsule: invalid DateKey: %w", datekeys.ErrDateKeyInvalid)
|
|
|
|
|
}
|
|
|
|
|
if !h.Policy.valid() {
|
|
|
|
|
return nil, fmt.Errorf("capsule: unknown access policy %d", h.Policy)
|
|
|
|
|
}
|
|
|
|
|
w := headerWire{
|
|
|
|
|
CapsuleID: h.CapsuleID[:],
|
|
|
|
|
DateKey: compact,
|
|
|
|
|
Policy: uint64(h.Policy),
|
|
|
|
|
}
|
|
|
|
|
var err error
|
|
|
|
|
if w.Critical, err = extension.Canonical(h.Critical); err != nil {
|
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
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if w.Noncritical, err = extension.Canonical(h.Noncritical); err != nil {
|
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
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if err := extension.CheckDisjoint(w.Critical, w.Noncritical); err != nil {
|
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
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
var e codec.Encoder
|
|
|
|
|
w.encode(&e)
|
|
|
|
|
b, err := e.Out()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if len(b) > MaxPublicHeaderLen {
|
|
|
|
|
return nil, fmt.Errorf("capsule: PUBLIC_HEADER of %d bytes exceeds %d: %w", len(b), MaxPublicHeaderLen, datekeys.ErrIntegrity)
|
|
|
|
|
}
|
|
|
|
|
return b, nil
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DecodeHeader validates and decodes PUBLIC_HEADER bytes (spec §24, §27,
|
|
|
|
|
// §63 step 4): the §57 limit, the schema version, canonical CBOR, the schema
|
|
|
|
|
// with a 16-byte capsule_id, a V1 access policy and well-formed extension
|
|
|
|
|
// arrays, and then a canonical DateKey, so that a header that also breaks the
|
|
|
|
|
// CDDL reports ErrNonCanonicalCBOR. Whether the profile is pinned and the
|
|
|
|
|
// critical extensions known is decided by the caller.
|
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
|
|
|
func DecodeHeader(b []byte) (*Header, error) {
|
|
|
|
|
if len(b) > MaxPublicHeaderLen {
|
|
|
|
|
return nil, fmt.Errorf("capsule: PUBLIC_HEADER of %d bytes exceeds %d: %w", len(b), MaxPublicHeaderLen, datekeys.ErrIntegrity)
|
|
|
|
|
}
|
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 := codec.CheckSchema(b, HeaderTypeTag, HeaderVersion); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("capsule: PUBLIC_HEADER: %w", err)
|
|
|
|
|
}
|
|
|
|
|
var w headerWire
|
|
|
|
|
if err := codec.Unmarshal(b, w.decode, w.encode); err != nil {
|
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
|
|
|
return nil, fmt.Errorf("capsule: PUBLIC_HEADER: %w", err)
|
|
|
|
|
}
|
|
|
|
|
if err := extension.CheckDisjoint(w.Critical, w.Noncritical); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("capsule: PUBLIC_HEADER: %w", err)
|
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
|
|
|
}
|
|
|
|
|
dk, err := datekey.Parse(w.DateKey)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("capsule: PUBLIC_HEADER: %w", err)
|
|
|
|
|
}
|
|
|
|
|
// decode bounds w.Policy to 0 or 1, so the conversion is exact.
|
|
|
|
|
h := &Header{DateKey: dk, Policy: Policy(w.Policy), Critical: w.Critical, Noncritical: w.Noncritical}
|
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
|
|
|
copy(h.CapsuleID[:], w.CapsuleID)
|
|
|
|
|
return h, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// CONTROL_CBOR
|
|
|
|
|
|
|
|
|
|
// Control is CONTROL_CBOR (spec §31). PayloadIdentity is I_PAYLOAD, a secret.
|
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
|
|
|
// Its schema version is the format of its capsule, which is not part of
|
|
|
|
|
// Control: it is given to EncodeControl and DecodeControl.
|
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
|
|
|
type Control struct {
|
|
|
|
|
HeaderBinding [32]byte // key 2
|
|
|
|
|
PayloadIdentity [32]byte // key 3, raw X25519 identity bytes. SECRET.
|
|
|
|
|
Critical []extension.Extension // key 4
|
|
|
|
|
Noncritical []extension.Extension // key 5
|
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
|
|
|
// PayloadLength is L, the length of the content (key 6), and Padding
|
|
|
|
|
// the padding rule of PAYLOAD_AGE (key 7). Both exist in format 2 only
|
|
|
|
|
// (spec §29.1, §31); in format 1 they are zero.
|
|
|
|
|
PayloadLength uint64
|
|
|
|
|
Padding Padding
|
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
|
|
|
}
|
|
|
|
|
|
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
|
|
|
// payloadLengthSize is the fixed size of payload_length, so that the length
|
|
|
|
|
// of CONTROL_CBOR never depends on L (spec §31, §55.2).
|
|
|
|
|
const payloadLengthSize = 8
|
|
|
|
|
|
|
|
|
|
// controlWire is CONTROL_CBOR as it is encoded: keys 2 to 7, keys 0 and 1
|
|
|
|
|
// being the constants ControlTypeTag and the format.
|
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
|
|
|
type controlWire 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
|
|
|
Format Format // key 1, the schema version
|
|
|
|
|
HeaderBinding []byte // key 2
|
|
|
|
|
PayloadIdentity []byte // key 3, SECRET
|
|
|
|
|
Critical []extension.Extension // key 4, omitted when empty
|
|
|
|
|
Noncritical []extension.Extension // key 5, omitted when empty
|
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
|
|
|
PayloadLength []byte // key 6, format 2 only: 8 bytes, big-endian
|
|
|
|
|
Padding uint64 // key 7, format 2 only
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (w *controlWire) encode(e *codec.Encoder) {
|
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
|
|
|
n := 4 + nonEmpty(w.Critical) + nonEmpty(w.Noncritical)
|
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
|
|
|
if w.Format.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
|
|
|
n += 2
|
|
|
|
|
}
|
|
|
|
|
e.Map(n)
|
|
|
|
|
e.Uint(0)
|
|
|
|
|
e.Text(ControlTypeTag)
|
|
|
|
|
e.Uint(1)
|
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
|
|
|
e.Uint(uint64(w.Format))
|
|
|
|
|
e.Uint(2)
|
|
|
|
|
e.Bstr(w.HeaderBinding)
|
|
|
|
|
e.Uint(3)
|
|
|
|
|
e.Bstr(w.PayloadIdentity)
|
|
|
|
|
encodeExtensions(e, 4, w.Critical, w.Noncritical)
|
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
|
|
|
if w.Format.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
|
|
|
e.Uint(6)
|
|
|
|
|
e.Bstr(w.PayloadLength)
|
|
|
|
|
e.Uint(7)
|
|
|
|
|
e.Uint(w.Padding)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
// decode reads CONTROL_CBOR with every CDDL rule of the schema version of
|
|
|
|
|
// w.Format: keys 6 and 7 are required in version 2 and not defined in
|
|
|
|
|
// version 1. The caller wipes PayloadIdentity, whatever the result.
|
|
|
|
|
func (w *controlWire) decode(d *codec.Decoder) error {
|
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
|
|
|
maxPairs := 6
|
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
|
|
|
if w.Format.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
|
|
|
maxPairs = 8
|
|
|
|
|
}
|
|
|
|
|
pairs, err := d.Map(maxPairs)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
var seen uint
|
|
|
|
|
for range pairs {
|
|
|
|
|
k, err := d.Key()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
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
|
|
|
if (k == 6 || k == 7) && !w.Format.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
|
|
|
return fmt.Errorf("key %d is not defined: %w", k, datekeys.ErrNonCanonicalCBOR)
|
|
|
|
|
}
|
|
|
|
|
switch k {
|
|
|
|
|
case 0:
|
|
|
|
|
_, err = d.Text(len(ControlTypeTag))
|
|
|
|
|
case 1:
|
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
|
|
|
_, err = d.Uint(uint64(w.Format))
|
|
|
|
|
case 2:
|
|
|
|
|
w.HeaderBinding, err = d.Bstr(32, 32)
|
|
|
|
|
case 3:
|
|
|
|
|
w.PayloadIdentity, err = d.Bstr(32, 32)
|
|
|
|
|
case 4:
|
|
|
|
|
w.Critical, err = extension.DecodeArray(d)
|
|
|
|
|
case 5:
|
|
|
|
|
w.Noncritical, err = extension.DecodeArray(d)
|
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
|
|
|
case 6:
|
|
|
|
|
if w.PayloadLength, err = d.Bstr(payloadLengthSize, payloadLengthSize); err == nil {
|
|
|
|
|
if l := binary.BigEndian.Uint64(w.PayloadLength); l > MaxPayloadLength {
|
|
|
|
|
err = fmt.Errorf("payload_length %d exceeds L_MAX = %d: %w", l, uint64(MaxPayloadLength), datekeys.ErrNonCanonicalCBOR)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case 7:
|
|
|
|
|
// Compared as read, before any narrowing to Padding, which would
|
|
|
|
|
// let 257 and the like pass as a defined code.
|
|
|
|
|
if w.Padding, err = d.Uint(codec.MaxSafeUint); err == nil && w.Padding != uint64(Bloque256) && w.Padding != uint64(Reforzado) {
|
|
|
|
|
err = fmt.Errorf("padding code %d is not defined: %w", w.Padding, datekeys.ErrNonCanonicalCBOR)
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
return fmt.Errorf("key %d is not defined: %w", k, datekeys.ErrNonCanonicalCBOR)
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("key %d: %w", k, err)
|
|
|
|
|
}
|
|
|
|
|
seen |= 1 << k
|
|
|
|
|
}
|
|
|
|
|
if err := required(seen, 4); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
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
|
|
|
if w.Format.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
|
|
|
for _, k := range []uint{6, 7} {
|
|
|
|
|
if seen&(1<<k) == 0 {
|
|
|
|
|
return fmt.Errorf("key %d is missing: %w", k, datekeys.ErrNonCanonicalCBOR)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return d.EndMap()
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// String describes c without I_PAYLOAD.
|
|
|
|
|
func (c Control) String() 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
|
|
|
return fmt.Sprintf("Control{header_binding=%x payload_identity=REDACTED payload_length=%d padding=%d}", c.HeaderBinding, c.PayloadLength, c.Padding)
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GoString describes c without I_PAYLOAD.
|
|
|
|
|
func (c Control) GoString() string { return c.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
|
|
|
// EncodeControl returns the Deterministic CBOR bytes of c as CONTROL_CBOR of
|
|
|
|
|
// a capsule of format f: schema version 1 without keys 6 and 7, or schema
|
|
|
|
|
// version 2 with them (spec §31). The caller must wipe the result: it
|
|
|
|
|
// contains I_PAYLOAD.
|
|
|
|
|
func EncodeControl(c *Control, f Format) ([]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
|
|
|
w := controlWire{
|
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: f,
|
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
|
|
|
HeaderBinding: c.HeaderBinding[:],
|
|
|
|
|
PayloadIdentity: c.PayloadIdentity[:],
|
|
|
|
|
}
|
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
|
|
|
switch f {
|
|
|
|
|
case Format1:
|
|
|
|
|
if c.PayloadLength != 0 || c.Padding != 0 {
|
|
|
|
|
return nil, errors.New("capsule: CONTROL_CBOR of format 1 has no payload_length and no padding")
|
|
|
|
|
}
|
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
|
|
|
case Format2, Format3:
|
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 !c.Padding.valid() {
|
|
|
|
|
return nil, fmt.Errorf("capsule: padding code %d is not defined", uint8(c.Padding))
|
|
|
|
|
}
|
|
|
|
|
if c.PayloadLength > MaxPayloadLength {
|
|
|
|
|
return nil, fmt.Errorf("capsule: payload_length %d exceeds L_MAX = %d", c.PayloadLength, uint64(MaxPayloadLength))
|
|
|
|
|
}
|
|
|
|
|
w.PayloadLength = binary.BigEndian.AppendUint64(nil, c.PayloadLength)
|
|
|
|
|
w.Padding = uint64(c.Padding)
|
|
|
|
|
default:
|
|
|
|
|
return nil, fmt.Errorf("capsule: format %d is not defined", uint8(f))
|
|
|
|
|
}
|
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
|
|
|
var err error
|
|
|
|
|
if w.Critical, err = extension.Canonical(c.Critical); err != nil {
|
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
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if w.Noncritical, err = extension.Canonical(c.Noncritical); err != nil {
|
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
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if err := extension.CheckDisjoint(w.Critical, w.Noncritical); err != nil {
|
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
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
var e codec.Encoder
|
|
|
|
|
w.encode(&e)
|
|
|
|
|
return e.Out()
|
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
|
|
|
}
|
|
|
|
|
|
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
|
|
|
// DecodeControl validates and decodes the CONTROL_CBOR of a capsule of format
|
|
|
|
|
// f (spec §31, §63 step 14). Its schema version must be f, and another is
|
|
|
|
|
// ErrUnsupportedVersion whatever follows (spec §69.1, layer 2); in version 2,
|
|
|
|
|
// payload_length is 8 bytes of at most MaxPayloadLength and padding is 1 or
|
|
|
|
|
// 2. A non-canonical encoding is rejected even though CONTROL_CBOR is not
|
|
|
|
|
// hashed.
|
|
|
|
|
func DecodeControl(b []byte, f Format) (*Control, error) {
|
|
|
|
|
if !f.valid() {
|
|
|
|
|
return nil, fmt.Errorf("capsule: format %d is not defined", uint8(f))
|
|
|
|
|
}
|
|
|
|
|
if err := codec.CheckSchema(b, ControlTypeTag, uint64(f)); err != nil {
|
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
|
|
|
return nil, fmt.Errorf("capsule: CONTROL_CBOR: %w", 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
|
|
|
w := controlWire{Format: f}
|
|
|
|
|
defer func() { clear(w.PayloadIdentity) }()
|
|
|
|
|
if err := codec.Unmarshal(b, w.decode, w.encode); err != nil {
|
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
|
|
|
return nil, fmt.Errorf("capsule: CONTROL_CBOR: %w", err)
|
|
|
|
|
}
|
|
|
|
|
if err := extension.CheckDisjoint(w.Critical, w.Noncritical); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("capsule: CONTROL_CBOR: %w", err)
|
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
|
|
|
}
|
|
|
|
|
c := &Control{Critical: w.Critical, Noncritical: w.Noncritical}
|
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
|
|
|
copy(c.HeaderBinding[:], w.HeaderBinding)
|
|
|
|
|
copy(c.PayloadIdentity[:], w.PayloadIdentity)
|
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
|
|
|
if f.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
|
|
|
// decode bounds both values, so the conversions are exact.
|
|
|
|
|
c.PayloadLength = binary.BigEndian.Uint64(w.PayloadLength)
|
|
|
|
|
c.Padding = Padding(w.Padding)
|
|
|
|
|
}
|
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
|
|
|
return c, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// looksLikeAge reports whether b starts with the age v1 intro line.
|
|
|
|
|
func looksLikeAge(b []byte) bool {
|
|
|
|
|
return bytes.HasPrefix(b, []byte("age-encryption.org/v1\n"))
|
|
|
|
|
}
|