add a fuzz target for the token generator

This commit is contained in:
Marten Seemann 2020-08-23 17:03:46 +07:00
parent 556bf18dbf
commit 8b96db3a84

161
fuzzing/tokens/fuzz.go Normal file
View file

@ -0,0 +1,161 @@
package tokens
import (
"encoding/binary"
"fmt"
"math/rand"
"net"
"time"
"github.com/lucas-clemente/quic-go/internal/handshake"
"github.com/lucas-clemente/quic-go/internal/protocol"
)
func Fuzz(data []byte) int {
if len(data) < 8 {
return -1
}
seed := binary.BigEndian.Uint64(data[:8])
data = data[8:]
tg, err := handshake.NewTokenGenerator(rand.New(rand.NewSource(int64(seed))))
if err != nil {
panic(err)
}
if len(data) < 1 {
return -1
}
s := data[0] % 3
data = data[1:]
switch s {
case 0:
tg.DecodeToken(data)
return 1
case 1:
return newToken(tg, data)
case 2:
return newRetryToken(tg, data)
}
return -1
}
func newToken(tg *handshake.TokenGenerator, data []byte) int {
if len(data) < 1 {
return -1
}
usesUDPAddr := data[0]%2 == 0
data = data[1:]
if len(data) != 18 {
return -1
}
var addr net.Addr
if usesUDPAddr {
addr = &net.UDPAddr{
Port: int(binary.BigEndian.Uint16(data[:2])),
IP: net.IP(data[2:]),
}
} else {
addr = &net.TCPAddr{
Port: int(binary.BigEndian.Uint16(data[:2])),
IP: net.IP(data[2:]),
}
}
start := time.Now()
encrypted, err := tg.NewToken(addr)
if err != nil {
panic(err)
}
token, err := tg.DecodeToken(encrypted)
if err != nil {
panic(err)
}
if token.IsRetryToken {
panic("didn't encode a Retry token")
}
if token.SentTime.Before(start) || token.SentTime.After(time.Now()) {
panic("incorrect send time")
}
if token.OriginalDestConnectionID != nil || token.RetrySrcConnectionID != nil {
panic("didn't expect connection IDs")
}
checkAddr(token.RemoteAddr, addr)
return 1
}
func newRetryToken(tg *handshake.TokenGenerator, data []byte) int {
if len(data) < 2 {
return -1
}
origDestConnIDLen := int(data[0] % 21)
retrySrcConnIDLen := int(data[1] % 21)
data = data[2:]
if len(data) < origDestConnIDLen {
return -1
}
origDestConnID := protocol.ConnectionID(data[:origDestConnIDLen])
data = data[origDestConnIDLen:]
if len(data) < retrySrcConnIDLen {
return -1
}
retrySrcConnID := protocol.ConnectionID(data[:retrySrcConnIDLen])
data = data[retrySrcConnIDLen:]
if len(data) < 1 {
return -1
}
usesUDPAddr := data[0]%2 == 0
data = data[1:]
if len(data) != 18 {
return -1
}
start := time.Now()
var addr net.Addr
if usesUDPAddr {
addr = &net.UDPAddr{
Port: int(binary.BigEndian.Uint16(data[:2])),
IP: net.IP(data[2:]),
}
} else {
addr = &net.TCPAddr{
Port: int(binary.BigEndian.Uint16(data[:2])),
IP: net.IP(data[2:]),
}
}
encrypted, err := tg.NewRetryToken(addr, origDestConnID, retrySrcConnID)
if err != nil {
panic(err)
}
token, err := tg.DecodeToken(encrypted)
if err != nil {
panic(err)
}
if !token.IsRetryToken {
panic("expected a Retry token")
}
if token.SentTime.Before(start) || token.SentTime.After(time.Now()) {
panic("incorrect send time")
}
if !token.OriginalDestConnectionID.Equal(origDestConnID) {
panic("orig dest conn ID doesn't match")
}
if !token.RetrySrcConnectionID.Equal(retrySrcConnID) {
panic("retry src conn ID doesn't match")
}
checkAddr(token.RemoteAddr, addr)
return 1
}
func checkAddr(tokenAddr string, addr net.Addr) {
if udpAddr, ok := addr.(*net.UDPAddr); ok {
// For UDP addresses, we encode only the IP (not the port).
if ip := udpAddr.IP.String(); tokenAddr != ip {
fmt.Printf("%s vs %s", tokenAddr, ip)
panic("wrong remote address for a net.UDPAddr")
}
return
}
if tokenAddr != addr.String() {
fmt.Printf("%s vs %s", tokenAddr, addr.String())
panic("wrong remote address")
}
}