//go:build phoboscref
/* SPDX-License-Identifier: MIT
*
* Phobos
*/
package phobos
import (
"bytes"
"encoding/binary"
"hash/crc32"
"math/rand"
"net/netip"
"testing"
"golang.zx2c4.com/wireguard/windows/phobos/cref"
)
var referenceAddrs = []netip.AddrPort{
netip.MustParseAddrPort("127.0.0.1:51820"),
netip.MustParseAddrPort("10.0.0.7:1"),
netip.MustParseAddrPort("203.0.113.42:65535"),
}
func TestStunBindingSuccessMatchesReference(t *testing.T) {
rng := rand.New(rand.NewSource(6))
for _, addr := range referenceAddrs {
txid := make([]byte, 12)
rng.Read(txid)
want := cref.StunBindingSuccess(txid, addr)
got := make([]byte, 128)
n := stunBuildBindingSuccess(got, txid, addr)
if n < 0 || !bytes.Equal(got[:n], want) {
t.Fatalf("binding success mismatch for %v:\n got %x\nwant %x", addr, got[:max(n, 0)], want)
}
}
}
func TestStunBindingRequestMatchesReference(t *testing.T) {
var rng rng32 = 12345
buf := make([]byte, 128)
n := stunBuildBindingRequest(buf, &rng)
reference := cref.StunBindingRequest()
if n != len(reference) {
t.Fatalf("binding request size mismatch: got %d want %d", n, len(reference))
}
if !bytes.Equal(buf[:8], reference[:8]) {
t.Fatalf("header mismatch: got %x want %x", buf[:8], reference[:8])
}
if !bytes.Equal(buf[20:24], reference[20:24]) {
t.Fatalf("fingerprint attribute header mismatch: got %x want %x", buf[20:24], reference[20:24])
}
fingerprinted := bytes.Clone(buf[:20])
fingerprinted[2], fingerprinted[3] = 0, 0
want := cref.CRC32(fingerprinted) ^ stunFingerprintXOR
if got := binary.BigEndian.Uint32(buf[24:]); got != want {
t.Fatalf("fingerprint mismatch: got %08x want %08x", got, want)
}
}
func TestCRC32MatchesReference(t *testing.T) {
rng := rand.New(rand.NewSource(11))
for _, length := range []int{0, 1, 20, 32, 1000} {
data := make([]byte, length)
rng.Read(data)
if got, want := crc32.ChecksumIEEE(data), cref.CRC32(data); got != want {
t.Fatalf("crc32 mismatch at length %d: got %08x want %08x", length, got, want)
}
}
}
func TestStunFramingInteropWithReference(t *testing.T) {
rng := rand.New(rand.NewSource(7))
var maskerRNG rng32 = 999
for _, length := range []int{1, 4, 148, 1024, 1420} {
payload := make([]byte, length)
rng.Read(payload)
buf := make([]byte, BufferSize)
copy(buf, payload)
n := stunWrapDataIndication(buf, length, &maskerRNG)
if n < 0 {
t.Fatalf("go wrap failed at length %d", length)
}
if !bytes.Equal(cref.StunUnwrap(buf[:n]), payload) {
t.Fatalf("C cannot unwrap Go STUN frame at length %d", length)
}
frame := cref.StunWrap(payload, BufferSize)
buf = make([]byte, BufferSize)
copy(buf, frame)
n = stunUnwrapDataIndication(buf, len(frame))
if n < 0 || !bytes.Equal(buf[:n], payload) {
t.Fatalf("Go cannot unwrap C STUN frame at length %d", length)
}
}
}
func TestMediaFramingInteropWithReference(t *testing.T) {
rng := rand.New(rand.NewSource(8))
params := MediaParams{PayloadType: 102, SSRC: 0xDEADBEEF, TimestampStep: 3000}
for _, length := range []int{4, 148, 1024, 1420} {
payload := make([]byte, length)
rng.Read(payload)
masker := NewMasker(MaskingMEDIA, params)
buf := make([]byte, BufferSize)
copy(buf, payload)
n := masker.OnDataWrap(buf, length)
if n < 0 {
t.Fatalf("go wrap failed at length %d", length)
}
if !bytes.Equal(cref.MediaUnwrap(buf[:n], params.PayloadType, params.SSRC), payload) {
t.Fatalf("C cannot unwrap Go RTP frame at length %d", length)
}
frame := cref.MediaWrap(payload, params.PayloadType, params.SSRC, params.TimestampStep, BufferSize)
buf = make([]byte, BufferSize)
copy(buf, frame)
n = NewMasker(MaskingMEDIA, params).OnDataUnwrap(buf, len(frame), referenceAddrs[0], nil)
if n < 0 || !bytes.Equal(buf[:n], payload) {
t.Fatalf("Go cannot unwrap C RTP frame at length %d", length)
}
}
}
func TestMediaHeaderMatchesReferenceLayout(t *testing.T) {
params := MediaParams{PayloadType: 111, SSRC: 0x01020304, TimestampStep: 1500}
payload := bytes.Repeat([]byte{0x33}, 64)
buf := make([]byte, BufferSize)
copy(buf, payload)
n := NewMasker(MaskingMEDIA, params).OnDataWrap(buf, len(payload))
reference := cref.MediaWrap(payload, params.PayloadType, params.SSRC, params.TimestampStep, BufferSize)
if n != len(reference) {
t.Fatalf("frame length mismatch: got %d want %d", n, len(reference))
}
if buf[0] != reference[0] || buf[1] != reference[1] {
t.Fatalf("RTP flags/payload type mismatch: got %x want %x", buf[:2], reference[:2])
}
if !bytes.Equal(buf[8:12], reference[8:12]) {
t.Fatalf("SSRC mismatch: got %x want %x", buf[8:12], reference[8:12])
}
if !bytes.Equal(buf[rtpHeaderSize:n], reference[rtpHeaderSize:]) {
t.Fatal("payload mismatch")
}
}
func TestMediaPresetsMatchReference(t *testing.T) {
if len(mediaPresets) != 50 {
t.Fatalf("preset table size drifted: %d", len(mediaPresets))
}
seen := map[mediaPreset]bool{}
for _, preset := range mediaPresets {
if preset.payloadType < 96 || preset.payloadType > 127 {
t.Fatalf("payload type %d outside the dynamic RTP range", preset.payloadType)
}
seen[preset] = true
}
for range 4000 {
var payloadType uint8
step := cref.PickMediaPreset(&payloadType)
if !seen[mediaPreset{payloadType, step}] {
t.Fatalf("reference produced preset {%d,%d} missing from the Go table", payloadType, step)
}
}
}