working
This commit is contained in:
332
cmd/fakeserver/main.go
Normal file
332
cmd/fakeserver/main.go
Normal file
@@ -0,0 +1,332 @@
|
||||
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))
|
||||
}
|
||||
Reference in New Issue
Block a user