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/profile/point_test.go

113 lines
4.2 KiB

package profile_test
import (
"bytes"
"errors"
"testing"
"github.com/drand/drand/v2/crypto"
datekeys "g.activething.com/go/DateKeys"
"g.activething.com/go/DateKeys/internal/testkit"
"g.activething.com/go/DateKeys/profile"
)
// Spec §12.2: the decoders of drand, through which the reference reads public
// keys, release signatures and the U of a tlock stanza, accept exactly the
// canonical encodings. The reference relies on them for the rule, so this
// test fails if a dependency update makes them lenient.
func TestDrandPointDecodersAreCanonical(t *testing.T) {
s, err := crypto.SchemeFromName(crypto.SigsOnG1ID)
if err != nil {
t.Fatal(err)
}
g1 := func(b []byte) error { return s.SigGroup.Point().UnmarshalBinary(b) }
g2 := func(b []byte) error { return s.KeyGroup.Point().UnmarshalBinary(b) }
must := func(b []byte, err error) []byte {
t.Helper()
if err != nil {
t.Fatal(err)
}
return b
}
cleared := func(b []byte, flags byte) []byte {
c := bytes.Clone(b)
c[0] &^= flags
return c
}
key, sig := profile.Quicknet().PublicKey, testkit.Release(1000).Signature
for _, tc := range []struct {
name string
decode func([]byte) error
in []byte
ok bool
}{
{"G1: a release signature", g1, sig, true},
{"G1: the point at infinity", g1, testkit.Infinity(48), true},
{"G2: the Quicknet public key", g2, key, true},
{"G2: the point at infinity", g2, testkit.Infinity(96), true},
{"G1: x + p", g1, must(testkit.AddModulus(testkit.Release(testkit.XPlusPRound).Signature, 0)), false},
{"G2: c1 + p", g2, must(testkit.AddModulus(key, 0)), false},
{"G2: c0 + p", g2, must(testkit.AddModulus(key, testkit.CoordinateLen)), false},
{"G1: infinity flag and a payload", g1, testkit.InfinityWithPayload(sig), false},
{"G2: infinity flag and a payload", g2, testkit.InfinityWithPayload(key), false},
{"G1: the point at infinity with the sort flag", g1, testkit.Negated(testkit.Infinity(48)), false},
{"G2: the point at infinity with the sort flag", g2, testkit.Negated(testkit.Infinity(96)), false},
{"G1: the point at infinity without the compression flag", g1, cleared(testkit.Infinity(48), testkit.FlagCompressed), false},
{"G1: compression flag cleared", g1, cleared(sig, testkit.FlagCompressed), false},
{"G2: compression flag cleared", g2, cleared(key, testkit.FlagCompressed), false},
{"G1: 96 bytes, the length of the uncompressed form", g1, append(bytes.Clone(sig), sig...), false},
{"G2: 192 bytes, the length of the uncompressed form", g2, append(bytes.Clone(key), key...), false},
{"G1: 47 bytes", g1, sig[:47], false},
{"G2: 97 bytes", g2, append(bytes.Clone(key), 0), false},
{"G1: a point of the curve outside the prime-order subgroup", g1, offSubgroupG1(t), false},
} {
if err := tc.decode(tc.in); (err == nil) != tc.ok {
t.Errorf("%s: decoded %v, want %v (%v)", tc.name, err == nil, tc.ok, err)
}
}
}
// Spec §12.1, §12.2: the public key of a profile is the canonical encoding of
// a point of the key group other than the point at infinity. Each profile
// below is otherwise valid, its chain hash computed over the exact bytes of
// its key, and is refused whether it is validated or decoded.
func TestPublicKeyEncodingIsCanonical(t *testing.T) {
key := profile.Quicknet().PublicKey
c1PlusP, err := testkit.AddModulus(key, 0)
if err != nil {
t.Fatal(err)
}
c0PlusP, err := testkit.AddModulus(key, testkit.CoordinateLen)
if err != nil {
t.Fatal(err)
}
uncompressed := bytes.Clone(key)
uncompressed[0] &^= testkit.FlagCompressed
for _, tc := range []struct {
name string
key []byte
}{
{"c1 + p", c1PlusP},
{"c0 + p", c0PlusP},
{"the point at infinity", testkit.Infinity(96)},
{"infinity flag and a payload", testkit.InfinityWithPayload(key)},
{"compression flag cleared", uncompressed},
{"192 bytes, the length of the uncompressed form", append(bytes.Clone(key), key...)},
} {
p := profile.Quicknet()
p.PublicKey = tc.key
copy(p.ChainHash[:], chainHashFormula(p))
if err := p.Validate(); !errors.Is(err, datekeys.ErrUnknownProfile) {
t.Errorf("%s: Validate: %v", tc.name, err)
}
b, err := p.CanonicalCBOR()
if err != nil {
t.Fatal(err)
}
if _, err := profile.Decode(b); !errors.Is(err, datekeys.ErrUnknownProfile) {
t.Errorf("%s: Decode: %v", tc.name, err)
}
}
}

Powered by TurnKey Linux.