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