333 lines
8.2 KiB
Go
333 lines
8.2 KiB
Go
|
|
package main
|
||
|
|
|
||
|
|
import (
|
||
|
|
"bytes"
|
||
|
|
"crypto/ecdsa"
|
||
|
|
"crypto/elliptic"
|
||
|
|
"crypto/rand"
|
||
|
|
"crypto/sha1"
|
||
|
|
"crypto/sha256"
|
||
|
|
"crypto/sha512"
|
||
|
|
"encoding/asn1"
|
||
|
|
"encoding/base64"
|
||
|
|
"encoding/binary"
|
||
|
|
"encoding/hex"
|
||
|
|
"fmt"
|
||
|
|
"log"
|
||
|
|
"math/big"
|
||
|
|
"net"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"go-ts/pkg/protocol"
|
||
|
|
|
||
|
|
"filippo.io/edwards25519"
|
||
|
|
)
|
||
|
|
|
||
|
|
type ServerState struct {
|
||
|
|
Step int
|
||
|
|
A1 [16]byte
|
||
|
|
A2 [100]byte
|
||
|
|
|
||
|
|
Identity *ecdsa.PrivateKey
|
||
|
|
|
||
|
|
LicensePriv *edwards25519.Scalar
|
||
|
|
LicensePub *edwards25519.Point
|
||
|
|
LicenseBlock []byte
|
||
|
|
|
||
|
|
Alpha []byte
|
||
|
|
Beta []byte
|
||
|
|
SharedSecret []byte
|
||
|
|
SharedIV []byte
|
||
|
|
SharedMac [8]byte
|
||
|
|
}
|
||
|
|
|
||
|
|
func main() {
|
||
|
|
addr, _ := net.ResolveUDPAddr("udp", ":9988")
|
||
|
|
conn, _ := net.ListenUDP("udp", addr)
|
||
|
|
log.Println("FakeServer listening on :9988")
|
||
|
|
|
||
|
|
// 1. Generate Server Identity (P-256)
|
||
|
|
privKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||
|
|
|
||
|
|
// 2. Generate Transport/License Key (Ed25519)
|
||
|
|
var seed [32]byte
|
||
|
|
rand.Read(seed[:])
|
||
|
|
lPriv, _ := new(edwards25519.Scalar).SetBytesWithClamping(seed[:])
|
||
|
|
lPub := new(edwards25519.Point).ScalarBaseMult(lPriv)
|
||
|
|
|
||
|
|
// 3. Create 'l' (License Block) - 32 random bytes for now
|
||
|
|
lData := make([]byte, 32)
|
||
|
|
rand.Read(lData)
|
||
|
|
|
||
|
|
state := &ServerState{
|
||
|
|
Step: 0,
|
||
|
|
Identity: privKey,
|
||
|
|
LicensePriv: lPriv,
|
||
|
|
LicensePub: lPub,
|
||
|
|
LicenseBlock: lData,
|
||
|
|
Alpha: make([]byte, 10),
|
||
|
|
Beta: make([]byte, 54),
|
||
|
|
}
|
||
|
|
rand.Read(state.Alpha)
|
||
|
|
rand.Read(state.Beta)
|
||
|
|
rand.Read(state.A2[:])
|
||
|
|
|
||
|
|
buf := make([]byte, 4096)
|
||
|
|
for {
|
||
|
|
n, rAddr, err := conn.ReadFromUDP(buf)
|
||
|
|
if err != nil {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
data := make([]byte, n)
|
||
|
|
copy(data, buf[:n])
|
||
|
|
handlePacket(conn, rAddr, data, state)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func handlePacket(conn *net.UDPConn, addr *net.UDPAddr, data []byte, s *ServerState) {
|
||
|
|
pkt, err := protocol.Decode(data, true)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if pkt.Header.PacketType() == protocol.PacketTypeInit1 {
|
||
|
|
if len(pkt.Data) > 5 && pkt.Data[4] == 0x00 { // Step 0
|
||
|
|
log.Println("Recv Step 0, Sending Step 1")
|
||
|
|
sendStep1(conn, addr, s)
|
||
|
|
} else if len(pkt.Data) > 5 && pkt.Data[4] == 0x02 { // Step 2
|
||
|
|
log.Println("Recv Step 2, Sending Step 3")
|
||
|
|
sendStep3(conn, addr, s)
|
||
|
|
}
|
||
|
|
} else if pkt.Header.PacketType() == protocol.PacketTypeCommand {
|
||
|
|
decrypted, err := decryptHandshake(pkt)
|
||
|
|
if err == nil {
|
||
|
|
sStr := string(decrypted)
|
||
|
|
if len(sStr) > 8 && sStr[0:8] == "clientek" {
|
||
|
|
log.Printf("Recv clientek (Decrypted): %s", sStr)
|
||
|
|
if err := s.processClientEk(decrypted); err != nil {
|
||
|
|
log.Printf("Error processing clientek: %v", err)
|
||
|
|
} else {
|
||
|
|
log.Println("Shared Secret Derived. Waiting for clientinit.")
|
||
|
|
}
|
||
|
|
sendAck(conn, addr, pkt.Header.PacketID)
|
||
|
|
return
|
||
|
|
} else if len(sStr) > 12 && sStr[0:12] == "clientinitiv" {
|
||
|
|
log.Println("Recv clientinitiv. Sending initivexpand2...")
|
||
|
|
sendInitivexpand2(conn, addr, s)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if len(s.SharedSecret) > 0 {
|
||
|
|
s.decryptClientInit(pkt)
|
||
|
|
} else if err != nil && pkt.Header.FlagUnencrypted() == false {
|
||
|
|
// log.Printf("Decrypt failed: %v", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func decryptHandshake(pkt *protocol.Packet) ([]byte, error) {
|
||
|
|
key := protocol.HandshakeKey
|
||
|
|
nonce := protocol.HandshakeNonce
|
||
|
|
|
||
|
|
meta := make([]byte, 5)
|
||
|
|
binary.BigEndian.PutUint16(meta[0:2], pkt.Header.PacketID)
|
||
|
|
binary.BigEndian.PutUint16(meta[2:4], pkt.Header.ClientID)
|
||
|
|
meta[4] = pkt.Header.Type
|
||
|
|
|
||
|
|
return protocol.DecryptEAX(key, nonce, meta, pkt.Data, pkt.Header.MAC[:])
|
||
|
|
}
|
||
|
|
|
||
|
|
func sendStep1(conn *net.UDPConn, addr *net.UDPAddr, s *ServerState) {
|
||
|
|
buf := new(bytes.Buffer)
|
||
|
|
binary.Write(buf, binary.BigEndian, int32(time.Now().Unix()))
|
||
|
|
buf.WriteByte(0x01)
|
||
|
|
rand.Read(s.A1[:])
|
||
|
|
buf.Write(s.A1[:])
|
||
|
|
|
||
|
|
pkt := protocol.NewPacket(protocol.PacketTypeInit1, buf.Bytes())
|
||
|
|
pkt.Header.PacketID = 1
|
||
|
|
copy(pkt.Header.MAC[:], []byte("TS3INIT1"))
|
||
|
|
|
||
|
|
encoded, _ := pkt.Encode(false)
|
||
|
|
conn.WriteToUDP(encoded, addr)
|
||
|
|
}
|
||
|
|
|
||
|
|
func sendStep3(conn *net.UDPConn, addr *net.UDPAddr, s *ServerState) {
|
||
|
|
buf := new(bytes.Buffer)
|
||
|
|
binary.Write(buf, binary.BigEndian, int32(time.Now().Unix()))
|
||
|
|
buf.WriteByte(0x03)
|
||
|
|
|
||
|
|
x := make([]byte, 64)
|
||
|
|
rand.Read(x)
|
||
|
|
n := make([]byte, 64)
|
||
|
|
rand.Read(n)
|
||
|
|
buf.Write(x)
|
||
|
|
buf.Write(n)
|
||
|
|
binary.Write(buf, binary.BigEndian, uint32(0))
|
||
|
|
buf.Write(s.A2[:])
|
||
|
|
|
||
|
|
pkt := protocol.NewPacket(protocol.PacketTypeInit1, buf.Bytes())
|
||
|
|
pkt.Header.PacketID = 2
|
||
|
|
copy(pkt.Header.MAC[:], []byte("TS3INIT1"))
|
||
|
|
|
||
|
|
encoded, _ := pkt.Encode(false)
|
||
|
|
conn.WriteToUDP(encoded, addr)
|
||
|
|
}
|
||
|
|
|
||
|
|
func sendInitivexpand2(conn *net.UDPConn, addr *net.UDPAddr, s *ServerState) {
|
||
|
|
lStr := base64.StdEncoding.EncodeToString(s.LicenseBlock)
|
||
|
|
base64.StdEncoding.EncodeToString(s.Beta) // unused?
|
||
|
|
betaStr := base64.StdEncoding.EncodeToString(s.Beta)
|
||
|
|
|
||
|
|
// Encode Omega (P-256 Public Key)
|
||
|
|
pub := s.Identity.PublicKey
|
||
|
|
omegaBytes := elliptic.Marshal(pub.Curve, pub.X, pub.Y)
|
||
|
|
omegaStr := base64.StdEncoding.EncodeToString(omegaBytes)
|
||
|
|
|
||
|
|
// Create Proof: Sign(SHA256(lBytes)) with Identity
|
||
|
|
hash := sha256.Sum256(s.LicenseBlock)
|
||
|
|
r, sb, err := ecdsa.Sign(rand.Reader, s.Identity, hash[:])
|
||
|
|
if err != nil {
|
||
|
|
log.Printf("Signing failed: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
type ECDSASignature struct {
|
||
|
|
R, S *big.Int
|
||
|
|
}
|
||
|
|
sig := ECDSASignature{R: r, S: sb}
|
||
|
|
proofBytes, _ := asn1.Marshal(sig)
|
||
|
|
proofStr := base64.StdEncoding.EncodeToString(proofBytes)
|
||
|
|
|
||
|
|
// Send command
|
||
|
|
cmd := fmt.Sprintf("initivexpand2 l=%s beta=%s omega=%s ot=1 proof=%s tvd=C",
|
||
|
|
lStr, betaStr, omegaStr, proofStr)
|
||
|
|
|
||
|
|
pkt := protocol.NewPacket(protocol.PacketTypeCommand, []byte(cmd))
|
||
|
|
pkt.Header.PacketID = 3
|
||
|
|
|
||
|
|
// Encrypt with HandshakeKey
|
||
|
|
key := protocol.HandshakeKey
|
||
|
|
nonce := protocol.HandshakeNonce
|
||
|
|
|
||
|
|
// Meta S->C (3 bytes: PID(2)+Type(1))
|
||
|
|
meta := make([]byte, 3)
|
||
|
|
binary.BigEndian.PutUint16(meta[0:2], pkt.Header.PacketID)
|
||
|
|
meta[2] = pkt.Header.Type
|
||
|
|
|
||
|
|
encData, mac, _ := protocol.EncryptEAX(key, nonce, meta, pkt.Data)
|
||
|
|
pkt.Data = encData
|
||
|
|
copy(pkt.Header.MAC[:], mac)
|
||
|
|
|
||
|
|
encoded, _ := pkt.Encode(false)
|
||
|
|
conn.WriteToUDP(encoded, addr)
|
||
|
|
}
|
||
|
|
|
||
|
|
func sendAck(conn *net.UDPConn, addr *net.UDPAddr, pid uint16) {
|
||
|
|
pkt := protocol.NewPacket(protocol.PacketTypeAck, nil)
|
||
|
|
pkt.Header.PacketID = pid
|
||
|
|
|
||
|
|
// Encrypt ACK
|
||
|
|
key := protocol.HandshakeKey
|
||
|
|
nonce := protocol.HandshakeNonce
|
||
|
|
meta := make([]byte, 3)
|
||
|
|
binary.BigEndian.PutUint16(meta[0:2], pid)
|
||
|
|
meta[2] = pkt.Header.Type
|
||
|
|
|
||
|
|
encData, mac, _ := protocol.EncryptEAX(key, nonce, meta, pkt.Data)
|
||
|
|
pkt.Data = encData
|
||
|
|
copy(pkt.Header.MAC[:], mac)
|
||
|
|
|
||
|
|
encoded, _ := pkt.Encode(false)
|
||
|
|
conn.WriteToUDP(encoded, addr)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *ServerState) deriveSharedSecret(clientEkPub []byte) error {
|
||
|
|
clientPoint, err := new(edwards25519.Point).SetBytes(clientEkPub)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("invalid client point: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Server negates client point
|
||
|
|
clientPoint.Negate(clientPoint)
|
||
|
|
|
||
|
|
sharedPoint := new(edwards25519.Point).ScalarMult(s.LicensePriv, clientPoint)
|
||
|
|
sharedBytes := sharedPoint.Bytes()
|
||
|
|
|
||
|
|
sharedBytes[31] ^= 0x80
|
||
|
|
|
||
|
|
hash := sha512.Sum512(sharedBytes)
|
||
|
|
s.SharedSecret = hash[:]
|
||
|
|
|
||
|
|
s.SharedIV = make([]byte, 64)
|
||
|
|
copy(s.SharedIV, s.SharedSecret)
|
||
|
|
for i := 0; i < 10; i++ {
|
||
|
|
s.SharedIV[i] ^= s.Alpha[i]
|
||
|
|
}
|
||
|
|
if len(s.Beta) >= 54 {
|
||
|
|
for i := 0; i < 54; i++ {
|
||
|
|
s.SharedIV[10+i] ^= s.Beta[i]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
macHash := sha1.Sum(s.SharedIV)
|
||
|
|
copy(s.SharedMac[:], macHash[0:8])
|
||
|
|
|
||
|
|
log.Printf("Shared Secret Derived! IV: %s", hex.EncodeToString(s.SharedIV))
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *ServerState) processClientEk(data []byte) error {
|
||
|
|
str := string(data)
|
||
|
|
start := "ek="
|
||
|
|
idx := 0
|
||
|
|
for i := 0; i < len(str)-len(start); i++ {
|
||
|
|
if str[i:i+3] == start {
|
||
|
|
idx = i + 3
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if idx == 0 {
|
||
|
|
return fmt.Errorf("no ek found")
|
||
|
|
}
|
||
|
|
|
||
|
|
end := idx
|
||
|
|
for i := idx; i < len(str); i++ {
|
||
|
|
if str[i] == ' ' {
|
||
|
|
end = i
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
ekStr := str[idx:end]
|
||
|
|
ekBytes, err := base64.StdEncoding.DecodeString(ekStr)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
return s.deriveSharedSecret(ekBytes)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *ServerState) decryptClientInit(pkt *protocol.Packet) {
|
||
|
|
crypto := &protocol.CryptoState{
|
||
|
|
SharedIV: s.SharedIV,
|
||
|
|
SharedMac: s.SharedMac[:],
|
||
|
|
GenerationID: 0,
|
||
|
|
}
|
||
|
|
|
||
|
|
key, nonce := crypto.GenerateKeyNonce(&pkt.Header, true) // isClientToServer
|
||
|
|
|
||
|
|
meta := make([]byte, 5)
|
||
|
|
binary.BigEndian.PutUint16(meta[0:2], pkt.Header.PacketID)
|
||
|
|
binary.BigEndian.PutUint16(meta[2:4], pkt.Header.ClientID)
|
||
|
|
meta[4] = pkt.Header.Type
|
||
|
|
|
||
|
|
dec, err := protocol.DecryptEAX(key, nonce, meta, pkt.Data, pkt.Header.MAC[:])
|
||
|
|
if err != nil {
|
||
|
|
log.Printf("Decryption Failed: %v", err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
log.Printf(">>> DECRYPTED CLIENTINIT <<<\n%s\n", string(dec))
|
||
|
|
}
|