You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
DateKeys/internal/testkit/testkit.go

95 lines
3.2 KiB

// 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"
)
// 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",
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
// 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
}

Powered by TurnKey Linux.