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 provider_test
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"errors"
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
datekeys "g.activething.com/go/DateKeys"
|
|
|
|
|
"g.activething.com/go/DateKeys/internal/testkit"
|
|
|
|
|
"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
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestVerifyPublishedReleases(t *testing.T) {
|
|
|
|
|
p := profile.Quicknet()
|
|
|
|
|
for _, round := range testkit.Rounds {
|
|
|
|
|
if err := provider.Verify(p, provider.Condition{Round: round}, testkit.Release(round)); err != nil {
|
|
|
|
|
t.Fatalf("round %d: %v", round, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestVerifyRejects(t *testing.T) {
|
|
|
|
|
p := profile.Quicknet()
|
|
|
|
|
r1000, r1001 := testkit.Release(1000), testkit.Release(1001)
|
|
|
|
|
for _, tc := range []struct {
|
|
|
|
|
name string
|
|
|
|
|
cond uint64
|
|
|
|
|
rel provider.Release
|
|
|
|
|
want error
|
|
|
|
|
}{
|
|
|
|
|
// Spec §17: a valid signature of another round is not enough.
|
|
|
|
|
{"valid release of another round", 1000, r1001, datekeys.ErrRoundMismatch},
|
|
|
|
|
// A signature of round 1001 relabelled as round 1000.
|
|
|
|
|
{"signature of another round relabelled", 1000, provider.Release{Round: 1000, Signature: r1001.Signature}, datekeys.ErrReleaseInvalid},
|
|
|
|
|
{"all-zero signature", 1000, provider.Release{Round: 1000, Signature: make([]byte, 48)}, datekeys.ErrReleaseInvalid},
|
|
|
|
|
{"flipped bit", 1000, provider.Release{Round: 1000, Signature: flip(r1000.Signature)}, datekeys.ErrReleaseInvalid},
|
|
|
|
|
{"short signature", 1000, provider.Release{Round: 1000, Signature: r1000.Signature[:47]}, datekeys.ErrReleaseInvalid},
|
|
|
|
|
{"G2-sized signature", 1000, provider.Release{Round: 1000, Signature: bytes.Repeat(r1000.Signature, 2)}, datekeys.ErrReleaseInvalid},
|
|
|
|
|
{"round zero", 0, provider.Release{Round: 0, Signature: r1000.Signature}, datekeys.ErrDateKeyInvalid},
|
|
|
|
|
{"round beyond the profile", p.MaxRound() + 1, provider.Release{Round: p.MaxRound() + 1, Signature: r1000.Signature}, datekeys.ErrDateKeyInvalid},
|
|
|
|
|
} {
|
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
|
if err := provider.Verify(p, provider.Condition{Round: tc.cond}, tc.rel); !errors.Is(err, tc.want) {
|
|
|
|
|
t.Fatalf("got %v, want %v", err, tc.want)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestVerifyUsesThePinnedKeyOnly(t *testing.T) {
|
|
|
|
|
// A profile with another public key rejects the genuine signature.
|
|
|
|
|
p := profile.Quicknet()
|
|
|
|
|
p.PublicKey = append([]byte(nil), p.PublicKey...)
|
|
|
|
|
p.PublicKey[len(p.PublicKey)-1] ^= 1
|
|
|
|
|
err := provider.Verify(p, provider.Condition{Round: 1000}, testkit.Release(1000))
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("verified under a different key")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func flip(b []byte) []byte {
|
|
|
|
|
c := append([]byte(nil), b...)
|
|
|
|
|
c[10] ^= 0x01
|
|
|
|
|
return c
|
|
|
|
|
}
|