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/locator/fuzz_test.go

143 lines
4.3 KiB

package locator_test
import (
"bytes"
"encoding/hex"
"net/netip"
"testing"
"g.activething.com/go/DateKeys/extension"
"g.activething.com/go/DateKeys/internal/testkit"
"g.activething.com/go/DateKeys/locator"
)
// FuzzUnmarshal reads the plaintext of a locator: it never panics, and what
// it accepts is canonical, so that encoding it again gives the same bytes.
func FuzzUnmarshal(f *testing.F) {
var v testkit.LocatorVectorFile
if err := testkit.ReadJSON("../testdata/vectors/locator.json", &v); err != nil {
f.Fatal(err)
}
for _, s := range []string{v.Plaintext, v.Mixed.Plaintext} {
if b, err := hex.DecodeString(s); err == nil {
f.Add(b)
}
}
for _, c := range v.PlaintextCases {
if b, err := hex.DecodeString(c.Plaintext); err == nil {
f.Add(b)
}
}
f.Add([]byte{0xa0})
f.Fuzz(func(t *testing.T, b []byte) {
l, err := locator.Unmarshal(b)
if err != nil {
return
}
for _, a := range l.Usable() {
if locator.CheckURI(a.URI) != nil || a.Host() == "" {
t.Fatalf("a usable address %q that the rules refuse", a.URI)
}
}
})
}
// FuzzParseInfo reads the data of datekeys.capsule: it never panics, and its
// only normative code is ERR_EXTENSION_DATA_INVALID (spec §54, §57).
func FuzzParseInfo(f *testing.F) {
var v testkit.LocatorVectorFile
if err := testkit.ReadJSON("../testdata/vectors/locator.json", &v); err != nil {
f.Fatal(err)
}
if b, err := hex.DecodeString(v.Extension); err == nil {
f.Add(b)
}
for _, c := range v.ExtensionCases {
if b, err := hex.DecodeString(c.Data); err == nil {
f.Add(b)
}
}
f.Fuzz(func(t *testing.T, b []byte) {
_, err := locator.ParseInfo(extension.Extension{ID: extension.CapsuleID, Version: 1, Data: b})
if err != nil && !bytes.Contains([]byte(err.Error()), []byte("ERR_EXTENSION_DATA_INVALID")) {
t.Fatalf("an error without its code: %v", err)
}
})
}
// FuzzCheckURI checks an address: it never panics, and a host that it shows
// is in the address as it is written, with no decoding.
func FuzzCheckURI(f *testing.F) {
var v testkit.LocatorVectorFile
if err := testkit.ReadJSON("../testdata/vectors/locator.json", &v); err != nil {
f.Fatal(err)
}
for _, c := range v.URICases {
f.Add(c.URI)
}
f.Fuzz(func(t *testing.T, uri string) {
if locator.CheckURI(uri) != nil {
return
}
if h := (locator.Address{URI: uri}).Host(); h == "" || !bytes.Contains([]byte(uri), []byte(h)) {
t.Fatalf("%q: host %q", uri, h)
}
})
}
// FuzzCheckResolvedIP checks the address a name resolves to, with a NAT64
// prefix of the network or none (spec v0.13, §44.1): it never panics, and an
// address it accepts is public, or holds a public IPv4 address at the
// positions of RFC 6052 in 64:ff9b::/96 or in the prefix, with bits 64 to
// 71 zero. No prefix ever lets an address that holds a private IPv4 address
// through.
func FuzzCheckResolvedIP(f *testing.F) {
f.Add(net16("64:ff9b::cb00:7205"), net16("64:ff9b:1::"), uint8(48), false)
f.Add(net16("64:ff9b:1:c0a8:1:a00::"), net16("64:ff9b:1::"), uint8(48), true)
f.Add(net16("2a01:4f8:c0:64::c0a8:10a"), net16("2a01:4f8:c0:64::"), uint8(96), true)
f.Add(net16("2a01:4f8::1"), net16("::"), uint8(0), false)
f.Fuzz(func(t *testing.T, ipb, pb []byte, bits uint8, withPrefix bool) {
if len(ipb) != 16 || len(pb) != 16 {
return
}
ip := netip.AddrFrom16([16]byte(ipb))
var p netip.Prefix
if withPrefix {
p = netip.PrefixFrom(netip.AddrFrom16([16]byte(pb)), int(bits))
}
if locator.CheckResolvedIP(ip, p) != nil {
return
}
public := locator.CheckResolvedIP(ip, netip.Prefix{}) == nil
for _, q := range []netip.Prefix{netip.MustParsePrefix("64:ff9b::/96"), p} {
if !q.IsValid() || !q.Contains(ip) {
continue
}
b := ip.As16()
if q.Bits() < 96 && b[8] != 0 {
t.Fatalf("%s accepted with %v: bits 64 to 71 set", ip, p)
}
var v4 [4]byte
n := 0
for i := q.Bits() / 8; n < 4; i++ {
if i != 8 {
v4[n] = b[i]
n++
}
}
if locator.CheckResolvedIP(netip.AddrFrom4(v4), netip.Prefix{}) != nil {
t.Fatalf("%s accepted with %v: it holds %v, which is not public", ip, p, netip.AddrFrom4(v4))
}
return
}
if !public {
t.Fatalf("%s accepted with %v, outside every NAT64 prefix, and not public", ip, p)
}
})
}
func net16(s string) []byte {
b := netip.MustParseAddr(s).As16()
return b[:]
}

Powered by TurnKey Linux.