153 lines
3.9 KiB
Go
153 lines
3.9 KiB
Go
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
|
|
}
|