Загрузка данных
package main
import (
"bytes"
"crypto/rand"
"encoding/binary"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"sync"
"time"
"golang.org/x/crypto/argon2"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/curve25519"
"golang.org/x/crypto/mlkem"
"lukechampine.com/blake3"
)
const (
Magic0 = 0xAB
Magic1 = 0xCD
Version = 0x02
FixedPacketSize = 1280
RekeyPackets = 1 << 20
RekeySeconds = 600
ServerPort = "443"
MaxFailures = 3
SwitchInterval = 5 * time.Second
CmdNewIP = 0x03
CmdNewSNI = 0x04
CmdRekey = 0x05
)
var (
RussianSNI = []string{
"gosuslugi.ru",
"vk.com",
"ya.ru",
"mail.ru",
"sberbank.ru",
"yandex.ru",
}
ServerIPPool = []string{
"127.0.0.1",
}
ClientIPPool = []string{
"127.0.0.1",
}
BridgeIP = "127.0.0.1"
ExitIP = "127.0.0.1"
)
type HandshakeKeys struct {
X25519Priv []byte
X25519Pub []byte
MLKEMPriv *mlkem.DecapsulationKey768
MLKEMPub []byte
}
func GenerateHandshakeKeys() (*HandshakeKeys, error) {
xPriv := make([]byte, 32)
if _, err := rand.Read(xPriv); err != nil {
return nil, err
}
xPub, err := curve25519.X25519(xPriv, curve25519.Basepoint)
if err != nil {
return nil, err
}
mPriv, err := mlkem.GenerateKey768()
if err != nil {
return nil, err
}
return &HandshakeKeys{
X25519Priv: xPriv,
X25519Pub: xPub,
MLKEMPriv: mPriv,
MLKEMPub: mPriv.EncapsulationKey().Bytes(),
}, nil
}
func DeriveKeyBLAKE3(shared, salt []byte, info string) []byte {
h := blake3.New(32, nil)
h.Write(shared)
h.Write(salt)
h.Write([]byte(info))
return h.Sum(nil)
}
func DeriveKeyArgon2(password, salt []byte) []byte {
return argon2.IDKey(password, salt, 2, 19*1024, 1, 32)
}
func RotateKeyBLAKE3(current []byte, counter uint32) []byte {
salt := make([]byte, 4)
binary.BigEndian.PutUint32(salt, counter)
return DeriveKeyBLAKE3(current, salt, "XChangeNon-rotate")
}
func PadPayload(pt []byte) []byte {
padded := make([]byte, FixedPacketSize)
binary.BigEndian.PutUint16(padded[0:2], uint16(len(pt)))
copy(padded[2:], pt)
if _, err := rand.Read(padded[2+len(pt):]); err != nil {
panic(err)
}
return padded
}
func UnpadPayload(p []byte) ([]byte, error) {
if len(p) < 2 {
return nil, fmt.Errorf("short")
}
n := binary.BigEndian.Uint16(p[0:2])
if int(n) > len(p)-2 {
return nil, fmt.Errorf("bad len")
}
return p[2 : 2+n], nil
}
func RandomSNI() string {
n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(RussianSNI))))
return RussianSNI[n.Int64()]
}
func RandomServerIP() string {
n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(ServerIPPool))))
return ServerIPPool[n.Int64()]
}
func Jitter(maxMs int) {
n, _ := rand.Int(rand.Reader, big.NewInt(int64(maxMs)))
time.Sleep(time.Duration(n.Int64()) * time.Millisecond)
}
type ReplayWindow struct {
last uint64
seen map[uint64]bool
win uint64
mu sync.Mutex
}
func NewReplayWindow() *ReplayWindow {
return &ReplayWindow{seen: make(map[uint64]bool), win: 1024}
}
func (rw *ReplayWindow) Check(c uint64) bool {
rw.mu.Lock()
defer rw.mu.Unlock()
if rw.last > rw.win && c <= rw.last-rw.win {
return false
}
if rw.seen[c] {
return false
}
rw.seen[c] = true
if c > rw.last {
rw.last = c
}
for k := range rw.seen {
if k <= rw.last-rw.win {
delete(rw.seen, k)
}
}
return true
}
type DeadHand struct {
mu sync.Mutex
probes map[string]int
lastProbe map[string]time.Time
threshold int
window time.Duration
onTrigger func(string)
}
func NewDeadHand(threshold int, window time.Duration, onTrigger func(string)) *DeadHand {
return &DeadHand{
probes: make(map[string]int),
lastProbe: make(map[string]time.Time),
threshold: threshold,
window: window,
onTrigger: onTrigger,
}
}
func (dh *DeadHand) RecordProbe(ip string) {
dh.mu.Lock()
defer dh.mu.Unlock()
now := time.Now()
if last, ok := dh.lastProbe[ip]; ok && now.Sub(last) > dh.window {
dh.probes[ip] = 0
}
dh.lastProbe[ip] = now
dh.probes[ip]++
if dh.probes[ip] >= dh.threshold {
go dh.onTrigger(ip)
dh.probes[ip] = 0
}
}
func IsProbe(packet []byte) bool {
if len(packet) < 44 {
return true
}
if packet[0] != Magic0 || packet[1] != Magic1 {
return true
}
return false
}
type AdaptiveRekey struct {
mu sync.Mutex
packetCount uint64
suspicionLevel int
lastRekey time.Time
}
func NewAdaptiveRekey() *AdaptiveRekey {
return &AdaptiveRekey{lastRekey: time.Now()}
}
func (ar *AdaptiveRekey) IncrementPacket() {
ar.mu.Lock()
defer ar.mu.Unlock()
ar.packetCount++
}
func (ar *AdaptiveRekey) MarkSuspicious() {
ar.mu.Lock()
defer ar.mu.Unlock()
ar.suspicionLevel++
}
func (ar *AdaptiveRekey) ShouldRekey() bool {
ar.mu.Lock()
defer ar.mu.Unlock()
threshold := uint64(RekeyPackets)
interval := time.Duration(RekeySeconds) * time.Second
if ar.suspicionLevel > 0 {
threshold = 1000
interval = 30 * time.Second
}
if ar.packetCount >= threshold {
return true
}
if time.Since(ar.lastRekey) >= interval {
return true
}
return false
}
func (ar *AdaptiveRekey) OnRekey() {
ar.mu.Lock()
defer ar.mu.Unlock()
ar.packetCount = 0
ar.lastRekey = time.Now()
if ar.suspicionLevel > 0 {
ar.suspicionLevel--
}
}
type XChangeNonState struct {
mu sync.Mutex
Key []byte
SessionID [8]byte
SendCounter uint64
RekeyCount uint32
CurrentIP string
CurrentSNI string
DeadHand *DeadHand
Rekeyer *AdaptiveRekey
Replay *ReplayWindow
CreatedAt time.Time
Conn *net.UDPConn
Addr *net.UDPAddr
}
func NewXChangeNonState(key []byte, sid [8]byte, dh *DeadHand, ar *AdaptiveRekey) *XChangeNonState {
return &XChangeNonState{
Key: key,
SessionID: sid,
CurrentIP: RandomServerIP(),
CurrentSNI: RandomSNI(),
DeadHand: dh,
Rekeyer: ar,
Replay: NewReplayWindow(),
CreatedAt: time.Now(),
}
}
func (s *XChangeNonState) RotateAll() {
s.mu.Lock()
defer s.mu.Unlock()
s.RekeyCount++
s.Key = RotateKeyBLAKE3(s.Key, s.RekeyCount)
s.SendCounter = 0
s.CurrentIP = RandomServerIP()
s.CurrentSNI = RandomSNI()
s.Replay = NewReplayWindow()
s.CreatedAt = time.Now()
s.Rekeyer.OnRekey()
}
func (s *XChangeNonState) Tick() {
if s.Rekeyer.ShouldRekey() {
s.RotateAll()
}
}
func (s *XChangeNonState) Encrypt(pt []byte) ([]byte, error) {
s.mu.Lock()
defer s.mu.Unlock()
aead, err := chacha20poly1305.NewX(s.Key)
if err != nil {
return nil, err
}
padded := PadPayload(pt)
nonce := make([]byte, aead.NonceSize())
if _, err := rand.Read(nonce); err != nil {
return nil, err
}
aad := make([]byte, 0, 20)
aad = append(aad, Magic0, Magic1, Version, 0x00)
aad = append(aad, s.SessionID[:]...)
cb := make([]byte, 8)
binary.BigEndian.PutUint64(cb, s.SendCounter)
aad = append(aad, cb...)
ct := aead.Seal(nil, nonce, padded, aad)
pkt := make([]byte, 0, 44+24+len(ct))
pkt = append(pkt, Magic0, Magic1, Version, 0x00)
pkt = append(pkt, s.SessionID[:]...)
pkt = append(pkt, cb...)
pkt = append(pkt, nonce...)
pkt = append(pkt, ct...)
s.SendCounter++
s.Rekeyer.IncrementPacket()
return pkt, nil
}
func (s *XChangeNonState) Decrypt(pkt []byte) ([]byte, error) {
if len(pkt) < 68 {
return nil, fmt.Errorf("short")
}
if pkt[0] != Magic0 || pkt[1] != Magic1 {
return nil, fmt.Errorf("bad magic")
}
if pkt[2] != Version {
return nil, fmt.Errorf("bad ver")
}
sid := pkt[4:12]
c := binary.BigEndian.Uint64(pkt[12:20])
nonce := pkt[20:44]
ct := pkt[44:]
if !s.Replay.Check(c) {
return nil, fmt.Errorf("replay")
}
aead, err := chacha20poly1305.NewX(s.Key)
if err != nil {
return nil, err
}
aad := make([]byte, 0, 20)
aad = append(aad, Magic0, Magic1, Version, 0x00)
aad = append(aad, sid...)
cb := make([]byte, 8)
binary.BigEndian.PutUint64(cb, c)
aad = append(aad, cb...)
padded, err := aead.Open(nil, nonce, ct, aad)
if err != nil {
return nil, err
}
return UnpadPayload(padded)
}
func (s *XChangeNonState) NotifyClient(conn *net.UDPConn, addr *net.UDPAddr, cmd byte, data string) {
payload := make([]byte, 0, 1+len(data))
payload = append(payload, cmd)
payload = append(payload, []byte(data)...)
pkt, err := s.Encrypt(payload)
if err != nil {
return
}
Jitter(50)
conn.WriteToUDP(pkt, addr)
}
func ServerHandshake(server *HandshakeKeys, clientXPub []byte) ([]byte, []byte, error) {
xShared, err := curve25519.X25519(server.X25519Priv, clientXPub)
if err != nil {
return nil, nil, err
}
mShared, mCT := server.MLKEMPriv.EncapsulationKey().Encapsulate()
return append(xShared, mShared...), mCT, nil
}
func ClientHandshake(client *HandshakeKeys, serverXPub, mlkemCT []byte) ([]byte, error) {
xShared, err := curve25519.X25519(client.X25519Priv, serverXPub)
if err != nil {
return nil, err
}
mShared, err := client.MLKEMPriv.Decapsulate(mlkemCT)
if err != nil {
return nil, err
}
return append(xShared, mShared...), nil
}
type HostingAPI struct {
Provider string
Token string
BaseURL string
}
type APIResponse struct {
IP string `json:"ip"`
}
func (api *HostingAPI) RequestNewIP() (string, error) {
reqBody, _ := json.Marshal(map[string]string{"action": "allocate_ip"})
req, err := http.NewRequest("POST", api.BaseURL, bytes.NewBuffer(reqBody))
if err != nil {
return "", err
}
req.Header.Set("Authorization", "Bearer "+api.Token)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var apiResp APIResponse
if err := json.Unmarshal(body, &apiResp); err != nil {
return "", err
}
return apiResp.IP, nil
}
func (api *HostingAPI) AddIPToPool(ip string) {
ServerIPPool = append(ServerIPPool, ip)
ClientIPPool = append(ClientIPPool, ip)
fmt.Printf("[API] new IP added to pool: %s\n", ip)
}
type Chain struct {
BridgeIP string
ExitIP string
Enabled bool
}
func (c *Chain) Forward(packet []byte) ([]byte, error) {
if !c.Enabled {
return packet, nil
}
exitAddr, err := net.ResolveUDPAddr("udp", c.ExitIP+":"+ServerPort)
if err != nil {
return nil, err
}
conn, err := net.DialUDP("udp", nil, exitAddr)
if err != nil {
return nil, err
}
defer conn.Close()
if _, err := conn.Write(packet); err != nil {
return nil, err
}
buf := make([]byte, 4096)
conn.SetReadDeadline(time.Now().Add(10 * time.Second))
n, err := conn.Read(buf)
if err != nil {
return nil, err
}
return buf[:n], nil
}
func RunServer(server *HandshakeKeys, listenAddr string, api *HostingAPI, chain *Chain) error {
addr, err := net.ResolveUDPAddr("udp", listenAddr)
if err != nil {
return err
}
conn, err := net.ListenUDP("udp", addr)
if err != nil {
return err
}
defer conn.Close()
fmt.Printf("[server] XChangeNon v0.2 listening on %s\n", listenAddr)
dh := NewDeadHand(5, 10*time.Second, func(ip string) {
fmt.Printf("[DEAD HAND] probe from %s — rotating\n", ip)
if api != nil {
newIP, err := api.RequestNewIP()
if err == nil {
api.AddIPToPool(newIP)
}
}
})
buf := make([]byte, 4096)
for {
n, clientAddr, err := conn.ReadFromUDP(buf)
if err != nil {
continue
}
if IsProbe(buf[:n]) {
dh.RecordProbe(clientAddr.IP.String())
continue
}
if n < 32 {
continue
}
clientXPub := buf[:32]
shared, mlkemCT, err := ServerHandshake(server, clientXPub)
if err != nil {
continue
}
salt := make([]byte, 32)
rand.Read(salt)
key := DeriveKeyBLAKE3(shared, salt, "XChangeNon-session")
sid := [8]byte{}
copy(sid[:], salt[:8])
resp := make([]byte, 0, 32+len(mlkemCT)+32)
resp = append(resp, server.X25519Pub...)
resp = append(resp, mlkemCT...)
resp = append(resp, salt...)
Jitter(50)
conn.WriteToUDP(resp, clientAddr)
ar := NewAdaptiveRekey()
state := NewXChangeNonState(key, sid, dh, ar)
state.Conn = conn
state.Addr = clientAddr
go handleClient(state, chain)
}
}
func handleClient(state *XChangeNonState, chain *Chain) {
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
go func() {
for range ticker.C {
state.Tick()
}
}()
buf := make([]byte, 4096)
for {
state.Conn.SetReadDeadline(time.Now().Add(5 * time.Minute))
n, _, err := state.Conn.ReadFromUDP(buf)
if err != nil {
return
}
pt, err := state.Decrypt(buf[:n])
if err != nil {
continue
}
if chain != nil && chain.Enabled {
resp, err := chain.Forward(pt)
if err != nil {
continue
}
pt = resp
}
reply := append([]byte("echo: "), pt...)
pkt, err := state.Encrypt(reply)
if err != nil {
continue
}
Jitter(50)
state.Conn.WriteToUDP(pkt, state.Addr)
}
}
type Client struct {
mu sync.Mutex
CurrentIP string
CurrentIndex int
Failures int
Conn *net.UDPConn
State *XChangeNonState
}
func NewClient() *Client {
return &Client{
CurrentIndex: 0,
CurrentIP: ClientIPPool[0],
}
}
func (c *Client) SwitchToIP(ip string) error {
c.mu.Lock()
defer c.mu.Unlock()
if c.Conn != nil {
c.Conn.Close()
}
c.CurrentIP = ip
fmt.Printf("[client] switching to IP: %s\n", ip)
addr, err := net.ResolveUDPAddr("udp", ip+":"+ServerPort)
if err != nil {
return err
}
conn, err := net.DialUDP("udp", nil, addr)
if err != nil {
return err
}
c.Conn = conn
c.Failures = 0
return nil
}
func (c *Client) SwitchToNextIP() error {
c.mu.Lock()
c.CurrentIndex = (c.CurrentIndex + 1) % len(ClientIPPool)
nextIP := ClientIPPool[c.CurrentIndex]
c.mu.Unlock()
return c.SwitchToIP(nextIP)
}
func (c *Client) MarkFailure() {
c.mu.Lock()
defer c.mu.Unlock()
c.Failures++
}
func (c *Client) ShouldSwitch() bool {
c.mu.Lock()
defer c.mu.Unlock()
return c.Failures >= MaxFailures
}
func (c *Client) HandleCommand(pt []byte) {
if len(pt) < 3 {
return
}
if pt[0] != Magic0 || pt[1] != Magic1 {
return
}
cmd := pt[2]
data := string(pt[3:])
switch cmd {
case CmdNewIP:
c.SwitchToIP(data)
case CmdNewSNI:
fmt.Printf("[client] new SNI: %s\n", data)
case CmdRekey:
fmt.Printf("[client] rekey requested\n")
}
}
func (c *Client) Monitor() {
ticker := time.NewTicker(SwitchInterval)
defer ticker.Stop()
for range ticker.C {
if c.ShouldSwitch() {
c.SwitchToNextIP()
}
}
}
func RunClient(serverAddr string) error {
client, err := GenerateHandshakeKeys()
if err != nil {
return err
}
c := NewClient()
if err := c.SwitchToIP(ClientIPPool[0]); err != nil {
return err
}
defer c.Conn.Close()
go c.Monitor()
Jitter(50)
if _, err := c.Conn.Write(client.X25519Pub); err != nil {
return err
}
buf := make([]byte, 4096)
c.Conn.SetReadDeadline(time.Now().Add(10 * time.Second))
n, err := c.Conn.Read(buf)
if err != nil {
c.MarkFailure()
return err
}
serverXPub := buf[:32]
mlkemCT := buf[32 : 32+1088]
salt := buf[32+1088 : 32+1088+32]
shared, err := ClientHandshake(client, serverXPub, mlkemCT)
if err != nil {
return err
}
key := DeriveKeyBLAKE3(shared, salt, "XChangeNon-session")
sid := [8]byte{}
copy(sid[:], salt[:8])
dh := NewDeadHand(5, 10*time.Second, func(string) {})
ar := NewAdaptiveRekey()
state := NewXChangeNonState(key, sid, dh, ar)
c.State = state
pkt, err := state.Encrypt([]byte("hello from client"))
if err != nil {
return err
}
Jitter(50)
if _, err := c.Conn.Write(pkt); err != nil {
c.MarkFailure()
return err
}
c.Conn.SetReadDeadline(time.Now().Add(10 * time.Second))
n, err = c.Conn.Read(buf)
if err != nil {
c.MarkFailure()
return err
}
reply, err := state.Decrypt(buf[:n])
if err != nil {
return err
}
c.HandleCommand(reply)
fmt.Printf("[client] reply: %s\n", reply)
return nil
}
func main() {
_ = DeriveKeyArgon2
api := &HostingAPI{
Provider: "selectel",
Token: "YOUR_API_TOKEN",
BaseURL: "https://api.selectel.ru/vpc/resell/v2/ips",
}
chain := &Chain{
BridgeIP: BridgeIP,
ExitIP: ExitIP,
Enabled: false,
}
mode := "server"
if mode == "server" {
server, _ := GenerateHandshakeKeys()
fmt.Printf("[server] ML-KEM pub: %d bytes\n", len(server.MLKEMPub))
RunServer(server, ":"+ServerPort, api, chain)
} else {
RunClient("127.0.0.1:" + ServerPort)
}
}