Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
|
|
|
// Package testkit builds DateKeys test material: known Quicknet releases,
|
|
|
|
|
// offline release sources, capsules with arbitrary structural defects, and age
|
|
|
|
|
// files with edited headers whose MAC is still valid.
|
|
|
|
|
//
|
|
|
|
|
// It is internal and exists for tests and fixture generation only.
|
|
|
|
|
package testkit
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"encoding/hex"
|
|
|
|
|
"fmt"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
datekeys "g.activething.com/go/DateKeys"
|
|
|
|
|
"g.activething.com/go/DateKeys/profile"
|
|
|
|
|
"g.activething.com/go/DateKeys/provider"
|
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
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Published Quicknet signatures. They are public data obtained from drand
|
|
|
|
|
// relays; tests never trust them blindly but verify them with provider.Verify
|
|
|
|
|
// against the pinned public key.
|
|
|
|
|
var signatures = map[uint64]string{
|
|
|
|
|
1000: "b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39",
|
|
|
|
|
1001: "b33bf3667cbd5a82de3a24b4e0e9fe5513cc1a0e840368c6e31f5fcfa79bea03f73896b25883abf2853d10337fb8fa41",
|
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
|
|
|
1004: "a40658b820c0f8c10207524179a2031ba9537688a0d04e4851b58026be9a341fee3b96fb48ffad28483d84b40a5864aa",
|
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
|
|
|
2000: "b6cb8f482a0b15d45936a4c4ea08e98a087e71787caee3f4d07a8a9843b1bc5423c6b3c22f446488b3137eaca799c77e",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Rounds with a known release, in increasing order.
|
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
|
|
|
var Rounds = []uint64{1000, 1001, 1004, 2000}
|
|
|
|
|
|
|
|
|
|
// XPlusPRound is the first published Quicknet round after 1000 whose
|
|
|
|
|
// signature has an x-coordinate below 2^381 - p, so that x + p still fits in
|
|
|
|
|
// the 381 bits of a compressed G1 encoding: the round of the mutation
|
|
|
|
|
// "release signature re-encoded with x + p" (spec §12.2, §64). The
|
|
|
|
|
// signatures of rounds 1000, 1001 and 2000, those of the fixtures, do not
|
|
|
|
|
// allow it.
|
|
|
|
|
const XPlusPRound = 1004
|
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
|
|
|
|
|
|
|
|
// Release returns the published release of round; it panics for rounds
|
|
|
|
|
// without a known signature.
|
|
|
|
|
func Release(round uint64) provider.Release {
|
|
|
|
|
s, ok := signatures[round]
|
|
|
|
|
if !ok {
|
|
|
|
|
panic(fmt.Sprintf("testkit: no known release for round %d", round))
|
|
|
|
|
}
|
|
|
|
|
b, err := hex.DecodeString(s)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
return provider.Release{Round: round, Signature: b}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Source serves the given releases by round and reports every other round as
|
|
|
|
|
// unavailable. It counts the requests it receives.
|
|
|
|
|
type Source struct {
|
|
|
|
|
Releases map[uint64]provider.Release
|
|
|
|
|
Calls int
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewSource returns a Source with the given releases.
|
|
|
|
|
func NewSource(releases ...provider.Release) *Source {
|
|
|
|
|
s := &Source{Releases: map[uint64]provider.Release{}}
|
|
|
|
|
for _, r := range releases {
|
|
|
|
|
s.Releases[r.Round] = r
|
|
|
|
|
}
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fetch implements provider.ReleaseSource.
|
|
|
|
|
func (s *Source) Fetch(_ context.Context, _ *profile.Profile, c provider.Condition) (provider.Release, error) {
|
|
|
|
|
s.Calls++
|
|
|
|
|
r, ok := s.Releases[c.Round]
|
|
|
|
|
if !ok {
|
|
|
|
|
return provider.Release{}, fmt.Errorf("testkit: round %d: %w", c.Round, datekeys.ErrReleaseUnavailable)
|
|
|
|
|
}
|
|
|
|
|
return r, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fixed returns a clock stopped at t.
|
|
|
|
|
func Fixed(t time.Time) func() time.Time { return func() time.Time { return t } }
|
|
|
|
|
|
|
|
|
|
// Genesis returns the Quicknet genesis instant, a convenient "now" for
|
|
|
|
|
// encrypting to rounds that are already published.
|
|
|
|
|
func Genesis() time.Time { return time.Unix(profile.QuicknetGenesisTime, 0).UTC() }
|
|
|
|
|
|
|
|
|
|
// Registry returns the default registry or panics.
|
|
|
|
|
func Registry() profile.Registry {
|
|
|
|
|
r, err := profile.Default()
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
return r
|
|
|
|
|
}
|