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_test
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"context"
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
"g.activething.com/go/DateKeys/capsule"
|
|
|
|
|
"g.activething.com/go/DateKeys/extension"
|
|
|
|
|
"g.activething.com/go/DateKeys/internal/testkit"
|
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
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const mutationsFile = "../testdata/vectors/mutations.json"
|
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
|
|
|
|
|
|
|
|
// build returns a capsule made by testkit.Build and the options to open it.
|
|
|
|
|
func build(t *testing.T, b testkit.Build) ([]byte, capsule.OpenOptions) {
|
|
|
|
|
t.Helper()
|
|
|
|
|
if b.Plaintext == nil {
|
|
|
|
|
b.Plaintext = []byte("malicious creator")
|
|
|
|
|
}
|
|
|
|
|
out, err := b.Make()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
return out.DKC, capsule.OpenOptions{Registry: testkit.Registry(), Source: testkit.NewSource(testkit.Release(1000)), Now: testkit.Fixed(testkit.Genesis().AddDate(1, 0, 0))}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Spec §64: every mutation of the corpus (testkit.Mutations), built afresh,
|
|
|
|
|
// fails with its exact normative error at its exact step, and a failure
|
|
|
|
|
// before the release request causes no request.
|
|
|
|
|
func TestMutationCorpus(t *testing.T) {
|
|
|
|
|
e, err := testkit.NewMutationEnv(fixtureDir)
|
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 != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
n := 0
|
|
|
|
|
for _, m := range testkit.Mutations() {
|
|
|
|
|
if m.Spec {
|
|
|
|
|
n++
|
|
|
|
|
}
|
|
|
|
|
t.Run(m.Name, func(t *testing.T) {
|
|
|
|
|
in, err := m.Make(e)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if err := m.Check(in); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
Spec v0.8.2 amendment: canonical point encoding; no library error text
Amendment of the unreleased v0.8.2, recorded in §76 with its case: the
second implementation's phase-2 research found that tlock-js over
@noble/curves 1.9.7 accepts U re-encoded as c0 + p and a signature
x + p and returns the same file key, while the reference rejects both
(noble 1.9.7 differed from kilic on 5,615 of 41,686 encodings), and the
spec did not say which encodings are valid.
- §12.2 defines the canonical encoding of a BLS12-381 point (drand's
compressed ZCash form) and requires decoders to reject every other
byte string; §12.1 applies it to public_key.
- §63 step 10 applies it to the release signature (ERR_RELEASE_INVALID)
and step 11 defines the tlock stanza body U || V || W (96 + 16 + 16
bytes for Quicknet) with a canonical, non-infinity U (ERR_INTEGRITY).
- §64 gains ten mutations, exported to mutations.json (65 cases). The
signature x + p case uses published Quicknet round 1004, the first
after 1000 whose x allows x + p < 2^381. The reference already gave
every stated code and step.
Errors no longer copy text from tlock, kyber, age, drand or
kyber-bls12381. kyber's IBE error carried the candidate plaintext and r,
and with one bit of W flipped the message disclosed the real tlock file
key with that bit flipped. Every such place now uses a fixed reason with
its normative sentinel; TestTlockFailureDiagnosticsCarryNoSecrets fails
with the old wrapping.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
if n != 33 {
|
|
|
|
|
t.Fatalf("spec §64 lists 33 mutations, the corpus has %d", n)
|
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The exported corpus, testdata/vectors/mutations.json, holds every mutation
|
|
|
|
|
// of the corpus in order, and replaying each case from the file alone gives
|
|
|
|
|
// exactly the recorded error and step. The capsules that are not built with
|
|
|
|
|
// randomness are the ones the corpus derives from the fixtures.
|
|
|
|
|
func TestExportedMutationCorpus(t *testing.T) {
|
|
|
|
|
var f testkit.MutationFile
|
|
|
|
|
if err := testkit.ReadJSON(mutationsFile, &f); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
e, err := testkit.NewMutationEnv(fixtureDir)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
muts := testkit.Mutations()
|
|
|
|
|
if len(f.Cases) != len(muts) {
|
|
|
|
|
t.Fatalf("%d exported cases, the corpus has %d mutations", len(f.Cases), len(muts))
|
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
|
|
|
}
|
|
|
|
|
for i, c := range f.Cases {
|
|
|
|
|
m := muts[i]
|
|
|
|
|
t.Run(c.Name, func(t *testing.T) {
|
|
|
|
|
if c.Name != m.Name || c.Spec != m.Spec || c.Error != m.Want.Code() || c.Step != m.Step || c.Network != m.Network || c.Frozen != m.Random {
|
|
|
|
|
t.Fatalf("exported case %+v does not match mutation %q", c, m.Name)
|
|
|
|
|
}
|
|
|
|
|
if err := c.Check(fixtureDir); 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
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if m.Random {
|
|
|
|
|
if c.DKC.Base != "" {
|
|
|
|
|
t.Fatal("a frozen capsule must not depend on a fixture")
|
|
|
|
|
}
|
|
|
|
|
return
|
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
|
|
|
}
|
|
|
|
|
want, err := m.Make(e)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(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
|
|
|
}
|
|
|
|
|
got, err := c.Input(fixtureDir)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(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
|
|
|
}
|
|
|
|
|
if !bytes.Equal(got.DKC, want.DKC) || !bytes.Equal(got.DKK, want.DKK) {
|
|
|
|
|
t.Fatal("the exported capsule or .dkk differs from the one the mutation derives")
|
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
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Known critical extensions are accepted when the application declares them.
|
|
|
|
|
func TestKnownCriticalExtensions(t *testing.T) {
|
|
|
|
|
crit := []extension.Extension{{ID: "org.example.must-understand", Version: 1}}
|
|
|
|
|
for _, b := range []testkit.Build{{HeaderCritical: crit}, {ControlCritical: crit}} {
|
|
|
|
|
dkc, o := build(t, b)
|
|
|
|
|
o.Extensions = extension.Set{"org.example.must-understand": {1}}
|
|
|
|
|
var out bytes.Buffer
|
|
|
|
|
if _, err := capsule.Open(context.Background(), &out, bytes.NewReader(dkc), o); err != nil || out.String() != "malicious creator" {
|
|
|
|
|
t.Fatalf("known critical extension rejected: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|