首次推送

This commit is contained in:
sansen
2026-07-20 19:01:03 +08:00
parent ea01b9cf99
commit ef1bf61f9f
4484 changed files with 937163 additions and 1 deletions
@@ -0,0 +1,242 @@
package handshake
import (
"encoding/binary"
"errors"
"fmt"
"time"
"github.com/honeybbq/teamspeak-go/crypto"
"github.com/oasisprotocol/curve25519-voi/curve"
"github.com/oasisprotocol/curve25519-voi/curve/scalar"
)
var (
errLicenseTooShort = errors.New("license too short")
errUnsupportedLicenseVersion = errors.New("unsupported license version")
errInvalidLicenseTimes = errors.New("license times are invalid")
errIssuerStringNotTerminated = errors.New("non-null-terminated issuer string")
errWrongKeyKindInLicense = errors.New("wrong key kind in license")
errInvalidLicenseBlockType = errors.New("invalid license block type")
)
var licenseRootKey = []byte{
0xcd, 0x0d, 0xe2, 0xae, 0xd4, 0x63, 0x45, 0x50, 0x9a, 0x7e, 0x3c, 0xfd, 0x8f, 0x68, 0xb3, 0xdc, 0x75, 0x55, 0xb2,
0x9d, 0xcc, 0xec, 0x73, 0xcd, 0x18, 0x75, 0x0f, 0x99, 0x38, 0x12, 0x40, 0x8a,
}
type licenseBlockType byte
const (
licenseBlockIntermediate licenseBlockType = 0
licenseBlockServer licenseBlockType = 2
licenseBlockTs5Server licenseBlockType = 8
licenseBlockEphemeral licenseBlockType = 32
)
type licenseBlock struct {
key []byte
hash []byte
properties [][]byte // TS5/TS6 server license properties
issuer string
notValidBefore time.Time
notValidAfter time.Time
blockType licenseBlockType
serverType byte
}
type LicenseChain struct {
Blocks []licenseBlock
}
type blockPayload struct {
read int
issuer string
serverType byte
properties [][]byte
}
func ParseLicenses(data []byte) (*LicenseChain, error) {
if len(data) < 1 {
return nil, errLicenseTooShort
}
if data[0] != 1 {
return nil, errUnsupportedLicenseVersion
}
data = data[1:]
res := &LicenseChain{}
for len(data) > 0 {
block, read, err := parseLicenseBlock(data)
if err != nil {
return nil, err
}
res.Blocks = append(res.Blocks, block)
data = data[read:]
}
return res, nil
}
func (lc *LicenseChain) DeriveKey() ([]byte, error) {
round := make([]byte, len(licenseRootKey))
copy(round, licenseRootKey)
for _, block := range lc.Blocks {
next, err := block.deriveKey(round)
if err != nil {
return nil, err
}
round = next
}
return round, nil
}
func parseLicenseBlock(data []byte) (licenseBlock, int, error) {
const minBlockLen = 42
if len(data) < minBlockLen {
return licenseBlock{}, 0, errLicenseTooShort
}
if data[0] != 0 {
return licenseBlock{}, 0, fmt.Errorf("%w: %d", errWrongKeyKindInLicense, data[0])
}
blockType := licenseBlockType(data[33])
payload, err := parseBlockPayload(blockType, data, minBlockLen)
if err != nil {
return licenseBlock{}, 0, err
}
notValidBefore := unixTimeStart.Add(time.Duration(binary.BigEndian.Uint32(data[34:38])+0x50e22700) * time.Second)
notValidAfter := unixTimeStart.Add(time.Duration(binary.BigEndian.Uint32(data[38:42])+0x50e22700) * time.Second)
if notValidAfter.Before(notValidBefore) {
return licenseBlock{}, 0, errInvalidLicenseTimes
}
key := make([]byte, 32)
copy(key, data[1:33])
allLen := minBlockLen + payload.read
hash := crypto.Hash512(data[1:allLen])
block := licenseBlock{
blockType: blockType,
issuer: payload.issuer,
notValidBefore: notValidBefore,
notValidAfter: notValidAfter,
key: key,
hash: hash[:32],
serverType: payload.serverType,
properties: payload.properties,
}
return block, allLen, nil
}
func parseBlockPayload(blockType licenseBlockType, data []byte, minBlockLen int) (blockPayload, error) {
switch blockType {
case licenseBlockIntermediate:
return parseIntermediatePayload(data)
case licenseBlockServer:
return parseServerPayload(data)
case licenseBlockTs5Server:
return parseTs5ServerPayload(data, minBlockLen)
case licenseBlockEphemeral:
return blockPayload{}, nil
default:
return blockPayload{}, fmt.Errorf("%w: %d", errInvalidLicenseBlockType, blockType)
}
}
func parseIntermediatePayload(data []byte) (blockPayload, error) {
issuer, read, err := readNullString(data[46:])
if err != nil {
return blockPayload{}, err
}
return blockPayload{issuer: issuer, read: 5 + read}, nil
}
func parseServerPayload(data []byte) (blockPayload, error) {
issuer, read, err := readNullString(data[47:])
if err != nil {
return blockPayload{}, err
}
return blockPayload{
issuer: issuer,
read: 6 + read,
serverType: data[42],
}, nil
}
func parseTs5ServerPayload(data []byte, minBlockLen int) (blockPayload, error) {
propertyCount := int(data[43])
pos := 44
properties := make([][]byte, 0, propertyCount)
for range propertyCount {
if pos >= len(data) {
return blockPayload{}, errLicenseTooShort
}
propLen := int(data[pos])
pos++
if pos+propLen > len(data) {
return blockPayload{}, errLicenseTooShort
}
prop := make([]byte, propLen)
copy(prop, data[pos:pos+propLen])
properties = append(properties, prop)
pos += propLen
}
return blockPayload{
read: pos - minBlockLen,
serverType: data[42],
properties: properties,
}, nil
}
func (lb *licenseBlock) deriveKey(parent []byte) ([]byte, error) {
scalarBytes := make([]byte, 32)
copy(scalarBytes, lb.hash)
crypto.ClampScalar(scalarBytes)
sc, err := scalar.NewFromBits(scalarBytes)
if err != nil {
return nil, err
}
pub := curve.NewEdwardsPoint()
err = pub.UnmarshalBinary(lb.key)
if err != nil {
return nil, err
}
pub.Neg(pub)
par := curve.NewEdwardsPoint()
err = par.UnmarshalBinary(parent)
if err != nil {
return nil, err
}
par.Neg(par)
res := curve.NewEdwardsPoint().Mul(pub, sc)
res.Add(res, par)
final, err := res.MarshalBinary()
if err != nil {
return nil, err
}
final[31] ^= 0x80
return final, nil
}
func readNullString(data []byte) (string, int, error) {
for i, b := range data {
if b == 0 {
return string(data[:i]), i, nil
}
}
return "", 0, errIssuerStringNotTerminated
}
var unixTimeStart = time.Unix(0, 0)