Files
go-ts/pkg/protocol/license.go

153 lines
3.9 KiB
Go
Raw Normal View History

2026-01-15 16:49:16 +01:00
package protocol
import (
"bytes"
"crypto/sha512"
"errors"
"fmt"
"filippo.io/edwards25519"
)
// Root Key (compressed Edwards25519 point)
var LicenseRootKeyBytes = [32]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,
}
// ParseLicense and derive final Public Key
func ParseLicenseAndDeriveKey(licenseData []byte) ([]byte, error) {
// Header
if len(licenseData) < 1 || licenseData[0] != 0x01 {
return nil, errors.New("invalid license version")
}
reader := bytes.NewReader(licenseData[1:])
// Initialize current key with Root
currentPoint, err := new(edwards25519.Point).SetBytes(LicenseRootKeyBytes[:])
if err != nil {
return nil, fmt.Errorf("failed to load root key: %v", err)
}
// Iterate blocks
for reader.Len() > 0 {
// Calculate Start Position
bytesConsumed := (len(licenseData) - 1) - reader.Len()
blockStartAbs := 1 + bytesConsumed
// Read KeyType
_, err = reader.ReadByte()
if err != nil {
return nil, fmt.Errorf("read keyType failed: %v", err)
}
// Read Public Key
var pubKeyBytes [32]byte
if _, err := reader.Read(pubKeyBytes[:]); err != nil {
return nil, fmt.Errorf("read pubKey failed: %v", err)
}
blockPubKey, err := new(edwards25519.Point).SetBytes(pubKeyBytes[:])
if err != nil {
return nil, fmt.Errorf("invalid block public key: %v", err)
}
// Read Block Type
blockType, err := reader.ReadByte()
if err != nil {
return nil, fmt.Errorf("read blockType failed: %v", err)
}
// Read Dates
dates := make([]byte, 8)
if _, err := reader.Read(dates); err != nil {
return nil, fmt.Errorf("read dates failed: %v", err)
}
// Parse Content
switch blockType {
case 0x00: // Intermediate
// 4 bytes int
tmp := make([]byte, 4)
if _, err := reader.Read(tmp); err != nil {
return nil, fmt.Errorf("read intermediate int failed: %v", err)
}
// String
if _, err := readNullTerminatedString(reader); err != nil {
return nil, fmt.Errorf("read intermediate string failed: %v", err)
}
case 0x01, 0x03: // Website / Code
if _, err := readNullTerminatedString(reader); err != nil {
return nil, err
}
case 0x02: // Server
// 5 bytes
tmp := make([]byte, 5)
if _, err := reader.Read(tmp); err != nil {
return nil, fmt.Errorf("read server data failed: %v", err)
}
if _, err := readNullTerminatedString(reader); err != nil {
return nil, fmt.Errorf("read server string failed: %v", err)
}
case 0x20: // Box
// Ephemeral blocks might be empty content
if reader.Len() > 0 {
if _, err := readNullTerminatedString(reader); err != nil {
return nil, err
}
}
default:
// Fallback
}
// Calculate End for Hashing
bytesConsumedAfter := (len(licenseData) - 1) - reader.Len()
blockEndAbs := 1 + bytesConsumedAfter
hashStart := blockStartAbs + 1
if blockEndAbs <= hashStart {
return nil, errors.New("block too short for hashing")
}
hashableData := licenseData[hashStart:blockEndAbs]
// Calculate SHA512 Hash
hash := sha512.Sum512(hashableData)
var scalarBytes [32]byte
copy(scalarBytes[:], hash[:32]) // Take first 32 bytes
// Clamp the hash
scalarBytes[0] &= 248
scalarBytes[31] &= 127
scalarBytes[31] |= 64
scalar, err := new(edwards25519.Scalar).SetBytesWithClamping(scalarBytes[:])
if err != nil {
return nil, fmt.Errorf("scalar creation failed: %v", err)
}
// Derive: current = current + (blockKey * hash)
term := new(edwards25519.Point).ScalarMult(scalar, blockPubKey)
currentPoint.Add(currentPoint, term)
}
return currentPoint.Bytes(), nil
}
func readNullTerminatedString(r *bytes.Reader) (string, error) {
var data []byte
for {
b, err := r.ReadByte()
if err != nil {
return "", err
}
if b == 0x00 {
break
}
data = append(data, b)
}
return string(data), nil
}