Files
ts-mobile-go/go/_patches/github.com/honeybbq/teamspeak-go/handshake/license_test.go
T
2026-07-20 19:01:03 +08:00

223 lines
5.7 KiB
Go

package handshake_test
import (
"encoding/base64"
"encoding/binary"
"encoding/hex"
"testing"
"github.com/honeybbq/teamspeak-go/handshake"
)
// Real-world TeamSpeak anonymous license captured from a live server handshake.
const testLicenseBase64 = "AQBgjAAqtcBUrw5futTtkl3+EM3OW4Lal6OTPlwuv4xV/gIRFlEAG0Nl" +
"AAcAAAAgQW5vbnltb3VzAACWSZf+Mjl5RT5mu4rvf8nhAZp9TjXO10XfGHQ9HQPtHiAYiqjtGItRrQ=="
func decodeTestLicense(t *testing.T) []byte {
t.Helper()
data, err := base64.StdEncoding.DecodeString(testLicenseBase64)
if err != nil {
t.Fatalf("base64 decode failed: %v", err)
}
return data
}
func TestParseLicensesValid(t *testing.T) {
data := decodeTestLicense(t)
chain, err := handshake.ParseLicenses(data)
if err != nil {
t.Fatalf("ParseLicenses failed: %v", err)
}
if len(chain.Blocks) != 2 {
t.Errorf("expected 2 blocks, got %d", len(chain.Blocks))
}
}
func TestParseLicensesEmptyInput(t *testing.T) {
_, err := handshake.ParseLicenses([]byte{})
if err == nil {
t.Error("expected error for empty input")
}
}
func TestParseLicensesWrongVersion(t *testing.T) {
// Version byte at index 0 must be 1
_, err := handshake.ParseLicenses([]byte{0x02, 0x00})
if err == nil {
t.Error("expected error for unsupported version")
}
}
func TestParseLicensesTooShortBlock(t *testing.T) {
// Version OK but block data too short (< 42 bytes)
data := make([]byte, 10)
data[0] = 0x01 // valid version
// remaining 9 bytes are not enough for a license block (needs 42)
_, err := handshake.ParseLicenses(data)
if err == nil {
t.Error("expected error for truncated block")
}
}
func TestDeriveKeyLength(t *testing.T) {
data := decodeTestLicense(t)
chain, err := handshake.ParseLicenses(data)
if err != nil {
t.Fatal(err)
}
key, err := chain.DeriveKey()
if err != nil {
t.Fatalf("DeriveKey failed: %v", err)
}
if len(key) != 32 {
t.Errorf("expected 32-byte key, got %d bytes", len(key))
}
}
func TestDeriveKeyDeterministic(t *testing.T) {
data := decodeTestLicense(t)
chain, err := handshake.ParseLicenses(data)
if err != nil {
t.Fatal(err)
}
key1, err := chain.DeriveKey()
if err != nil {
t.Fatal(err)
}
key2, err := chain.DeriveKey()
if err != nil {
t.Fatal(err)
}
if hex.EncodeToString(key1) != hex.EncodeToString(key2) {
t.Error("DeriveKey is not deterministic")
}
}
func TestDeriveKeyNonZero(t *testing.T) {
data := decodeTestLicense(t)
chain, err := handshake.ParseLicenses(data)
if err != nil {
t.Fatal(err)
}
key, err := chain.DeriveKey()
if err != nil {
t.Fatal(err)
}
allZero := true
for _, b := range key {
if b != 0 {
allZero = false
break
}
}
if allZero {
t.Error("derived key should not be all zeros")
}
}
func TestParseLicensesExpectedKeyKnownValue(t *testing.T) {
// Known expected key derived from this specific anonymous license.
const expectedKeyHex = "82a168e11f9f3e3496fbf8479cd3e17d9b0945e224a71fb371af619a256b8446"
data := decodeTestLicense(t)
chain, err := handshake.ParseLicenses(data)
if err != nil {
t.Fatal(err)
}
key, err := chain.DeriveKey()
if err != nil {
t.Fatal(err)
}
if got := hex.EncodeToString(key); got != expectedKeyHex {
t.Errorf("DeriveKey = %s, want %s", got, expectedKeyHex)
}
}
// TS5/TS6 server license block (type 8) tests
// buildTs5LicenseBlob constructs a synthetic version-1 license containing a
// single Ts5Server block (type 8) with the given properties.
func buildTs5LicenseBlob(props [][]byte) []byte {
// Block layout:
// [0] key kind = 0
// [1:33] 32-byte Ed25519 public key (identity point)
// [33] block type = 8
// [34:38] not valid before (BE uint32)
// [38:42] not valid after (BE uint32)
// [42] server license type
// [43] property count
// [44+] length-prefixed properties
const headerSize = 44
totalPropsSize := 0
for _, p := range props {
totalPropsSize += 1 + len(p)
}
block := make([]byte, headerSize, headerSize+totalPropsSize)
block[0] = 0x00
block[1] = 0x01 // Ed25519 identity point (0,1)
block[33] = 0x08
binary.BigEndian.PutUint32(block[34:38], 0x00000000)
binary.BigEndian.PutUint32(block[38:42], 0x7FFFFFFF)
block[42] = 7
block[43] = byte(len(props))
for _, p := range props {
block = append(block, byte(len(p)))
block = append(block, p...)
}
return append([]byte{0x01}, block...) // version prefix
}
func TestParseTs5ServerBlock(t *testing.T) {
data := buildTs5LicenseBlob([][]byte{
[]byte("issuer.example.com"),
{0xDE, 0xAD},
})
chain, err := handshake.ParseLicenses(data)
if err != nil {
t.Fatalf("ParseLicenses failed: %v", err)
}
if len(chain.Blocks) != 1 {
t.Errorf("expected 1 block, got %d", len(chain.Blocks))
}
key, err := chain.DeriveKey()
if err != nil {
t.Fatalf("DeriveKey failed: %v", err)
}
if len(key) != 32 {
t.Errorf("expected 32-byte key, got %d", len(key))
}
}
func TestParseTs5ServerBlockZeroProperties(t *testing.T) {
data := buildTs5LicenseBlob(nil)
chain, err := handshake.ParseLicenses(data)
if err != nil {
t.Fatalf("ParseLicenses failed: %v", err)
}
if len(chain.Blocks) != 1 {
t.Errorf("expected 1 block, got %d", len(chain.Blocks))
}
}
func TestParseTs5ServerBlockTruncatedPropertyData(t *testing.T) {
data := buildTs5LicenseBlob([][]byte{{0x01, 0x02, 0x03}})
// Chop off last byte so the property data is incomplete.
data = data[:len(data)-1]
_, err := handshake.ParseLicenses(data)
if err == nil {
t.Error("expected error for truncated property data")
}
}
func TestParseTs5ServerBlockTruncatedPropertyLength(t *testing.T) {
// Claim 2 properties but only provide 1.
data := buildTs5LicenseBlob([][]byte{{0xAA}})
data[1+43] = 2 // override property count to 2
_, err := handshake.ParseLicenses(data)
if err == nil {
t.Error("expected error when property count exceeds available data")
}
}