wacky encryption

This commit is contained in:
2026-07-22 13:53:30 +02:00
parent c09c5c4fd0
commit c9c4cc0af1
6 changed files with 204 additions and 67 deletions

View File

@@ -1,4 +1,4 @@
package shared package main
import ( import (
"crypto/rand" "crypto/rand"

View File

@@ -1,23 +1,24 @@
package main package main
import ( import (
"encoding/base64" "crypto/sha256"
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
"log" "log"
"net" "net"
"os" "os"
"os/exec" "os/exec"
"time"
"baalvpn/shared" "baalvpn/shared"
"filippo.io/mlkem768/xwing"
"github.com/songgao/water" "github.com/songgao/water"
) )
const ( const (
SERVER_IP = "172.20.12.47" SERVER_IP = "172.20.12.47"
IFACE_NAME = "baalvpn" IFACE_NAME = "baalvpn"
PSK = "rj5FvWB0n342C6UGrAS0gI3GX5ya+lDmC7/XmgFHF+s="
) )
var ( var (
@@ -25,7 +26,14 @@ var (
serverAddr *net.UDPAddr serverAddr *net.UDPAddr
iface *water.Interface iface *water.Interface
conn *net.UDPConn conn *net.UDPConn
pskKey []byte privKey *xwing.DecapsulationKey
// TODO: this should probably have a mutex
peerPubKeys = map[string][]byte{}
// TODO: this should probably have a mutex
peerSessionKeys = map[string][]byte{}
// TODO: this should probably have a mutex
peerEstablishAcks = map[string]bool{}
) )
func main() { func main() {
@@ -33,12 +41,6 @@ func main() {
panic("root permissions needed") panic("root permissions needed")
} }
var err error
pskKey, err = base64.StdEncoding.DecodeString(PSK)
if err != nil {
panic(err)
}
listenAddr, err := net.ResolveUDPAddr("udp", ":38000") listenAddr, err := net.ResolveUDPAddr("udp", ":38000")
if err != nil { if err != nil {
panic(err) panic(err)
@@ -61,12 +63,63 @@ func main() {
} }
func register() { func register() {
req := shared.BuildPkt(shared.REQ_REGISTER, []byte{}) var err error
privKey, err = xwing.GenerateKey()
if err != nil {
panic(err)
}
pubKey := privKey.EncapsulationKey()
req := shared.BuildPkt(shared.REQ_REGISTER, pubKey)
if _, err := conn.WriteToUDP(req, serverAddr); err != nil { if _, err := conn.WriteToUDP(req, serverAddr); err != nil {
panic(err) panic(err)
} }
} }
func getOrEstablishSessionKey(ip string) []byte {
if key, ok := peerSessionKeys[ip]; ok {
return key
} else {
log.Println("requesting pubkey of " + ip)
req := shared.BuildPkt(shared.REQ_GET_PUBKEY, net.ParseIP(ip))
if _, err := conn.WriteToUDP(req, serverAddr); err != nil {
panic(err)
}
for {
// TODO: eww
time.Sleep(50 * time.Millisecond)
if peerPubKey, ok := peerPubKeys[ip]; ok {
log.Println("got pubkey of " + ip)
ciphertext, sharedSecret, err := xwing.Encapsulate(peerPubKey)
if err != nil {
panic(err)
}
reqData := append(net.ParseIP(ip), append(internalIP, ciphertext...)...)
req := shared.BuildPkt(shared.REQ_ESTABLISH, reqData)
if _, err := conn.WriteToUDP(req, serverAddr); err != nil {
panic(err)
}
for {
// TODO: eww
time.Sleep(50 * time.Millisecond)
if peerEstablishAcks[ip] {
delete(peerEstablishAcks, ip)
break
}
}
sessionKey := sha256.Sum256(sharedSecret)
peerSessionKeys[ip] = sessionKey[:]
return sessionKey[:]
}
}
}
}
func receivePackets() { func receivePackets() {
for { for {
buffer := make([]byte, 50000) buffer := make([]byte, 50000)
@@ -106,8 +159,12 @@ func receivePacket(pkt []byte) {
log.Println("got assigned IP by the server:", internalIP.String()) log.Println("got assigned IP by the server:", internalIP.String())
setupInterface() setupInterface()
go sendPackets() go sendPackets()
case shared.RESP_DATA: case shared.ENC_PKT:
decryptedPkt, err := shared.DecryptSym(pskKey, pkt[4:]) srcIP := net.IP(pkt[20:36]).String()
sessionKey := getOrEstablishSessionKey(srcIP)
decryptedPkt, err := DecryptSym(sessionKey, pkt[36:])
if err != nil { if err != nil {
panic(err) panic(err)
} }
@@ -116,6 +173,37 @@ func receivePacket(pkt []byte) {
if _, err := iface.Write(decryptedPkt); err != nil { if _, err := iface.Write(decryptedPkt); err != nil {
log.Println(err) log.Println(err)
} }
case shared.BROADCAST_PKT:
log.Println("received pkt:", hex.EncodeToString(pkt[4:]))
if _, err := iface.Write(pkt[4:]); err != nil {
log.Println(err)
}
case shared.RESP_GET_PUBKEY:
respIP := net.IP(pkt[4:20]).String()
pubKey := pkt[20:]
peerPubKeys[respIP] = pubKey
case shared.REQ_ESTABLISH:
srcIP := net.IP(pkt[20:36]).String()
ciphertext := pkt[36:]
sharedSecret, err := xwing.Decapsulate(privKey, ciphertext)
if err != nil {
panic(err)
}
sessionKey := sha256.Sum256(sharedSecret)
peerSessionKeys[srcIP] = sessionKey[:]
log.Println("received session key from", srcIP)
reqData := append(net.ParseIP(srcIP), internalIP...)
req := shared.BuildPkt(shared.RESP_ESTABLISH, reqData)
if _, err := conn.WriteToUDP(req, serverAddr); err != nil {
panic(err)
}
case shared.RESP_ESTABLISH:
srcIP := net.IP(pkt[20:36]).String()
log.Println("established session key with", srcIP)
peerEstablishAcks[srcIP] = true
default: default:
log.Println("unknown packet type") log.Println("unknown packet type")
} }
@@ -150,14 +238,33 @@ func sendPackets() {
} }
pkt = pkt[:n] pkt = pkt[:n]
encryptedPkt, err := shared.EncryptSym(pskKey, pkt) etherType := binary.BigEndian.Uint16(pkt[12:14])
payload := pkt[14:]
if etherType != 0x86dd { // IPv6
continue
}
destIP := net.IP(payload[24:40]).String()
if payload[24] == 0xff { // multicast
req := shared.BuildPkt(shared.BROADCAST_PKT, pkt)
if _, err := conn.WriteToUDP(req, serverAddr); err != nil {
log.Println(err)
}
} else {
destSessionKey := getOrEstablishSessionKey(destIP)
encryptedPkt, err := EncryptSym(destSessionKey, pkt)
if err != nil { if err != nil {
panic(err) panic(err)
} }
req := shared.BuildPkt(shared.REQ_DATA, encryptedPkt) reqBody := append(net.ParseIP(destIP), append(internalIP, encryptedPkt...)...)
req := shared.BuildPkt(shared.ENC_PKT, reqBody)
if _, err := conn.WriteToUDP(req, serverAddr); err != nil { if _, err := conn.WriteToUDP(req, serverAddr); err != nil {
log.Println(err) log.Println(err)
} }
} }
}
} }

5
go.mod
View File

@@ -3,10 +3,9 @@ module baalvpn
go 1.26.3 go 1.26.3
require ( require (
filippo.io/mlkem768 v0.0.0-20260214141301-2e7bebc7d88d
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
golang.org/x/crypto v0.54.0 golang.org/x/crypto v0.54.0
) )
require ( require golang.org/x/sys v0.47.0 // indirect
golang.org/x/sys v0.47.0 // indirect
)

2
go.sum
View File

@@ -1,3 +1,5 @@
filippo.io/mlkem768 v0.0.0-20260214141301-2e7bebc7d88d h1:YyLyABjdrdt2l/E6JAnku4BjhEDXhxQD2bPOnOvy8/M=
filippo.io/mlkem768 v0.0.0-20260214141301-2e7bebc7d88d/go.mod h1:ym4egWKLpazdho3bHx0xuQlCq02ttP+vhxxKO8LgO9c=
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8= github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8=
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E= github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E=
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=

View File

@@ -2,11 +2,13 @@
// TODO: authentication // TODO: authentication
// TODO: somehow persist IPs // TODO: somehow persist IPs
// TODO: handle multiple peers behind one NAT // TODO: handle multiple peers behind one NAT
// TODO: key rotation
// TODO: sha256 -> HKDF
// TODO: recovery
package main package main
import ( import (
"crypto/rand" "crypto/rand"
"encoding/base64"
"encoding/binary" "encoding/binary"
"log" "log"
"net" "net"
@@ -19,16 +21,13 @@ import (
type Peer struct { type Peer struct {
RealAddr *net.UDPAddr RealAddr *net.UDPAddr
InternalIP string InternalIP string
PublicKey []byte
} }
// TODO: replace with key exchange between peers
const PSK = "rj5FvWB0n342C6UGrAS0gI3GX5ya+lDmC7/XmgFHF+s="
var ( var (
peers = map[string]*Peer{} peers = map[string]*Peer{}
peersMutex sync.Mutex peersMutex sync.Mutex
conn *net.UDPConn conn *net.UDPConn
pskKey []byte
) )
func main() { func main() {
@@ -36,12 +35,6 @@ func main() {
panic("root permissions needed") panic("root permissions needed")
} }
var err error
pskKey, err = base64.StdEncoding.DecodeString(PSK)
if err != nil {
panic(err)
}
receivePackets() receivePackets()
} }
@@ -91,6 +84,8 @@ func handleReq(req []byte, addr *net.UDPAddr) {
peersMutex.Lock() peersMutex.Lock()
defer peersMutex.Unlock() defer peersMutex.Unlock()
pubKey := req[4:]
internalIP := randomIP().String() internalIP := randomIP().String()
if destPeer, ok := peers[addr.IP.String()]; ok { if destPeer, ok := peers[addr.IP.String()]; ok {
@@ -99,12 +94,13 @@ func handleReq(req []byte, addr *net.UDPAddr) {
peers[addr.IP.String()] = &Peer{ peers[addr.IP.String()] = &Peer{
RealAddr: addr, RealAddr: addr,
InternalIP: internalIP, InternalIP: internalIP,
PublicKey: pubKey,
} }
} }
resp := shared.BuildPkt(shared.RESP_REGISTER, net.ParseIP(internalIP)) resp := shared.BuildPkt(shared.RESP_REGISTER, net.ParseIP(internalIP))
sendTo(addr, resp) sendTo(addr, resp)
case shared.REQ_DATA: case shared.ENC_PKT:
peersMutex.Lock() peersMutex.Lock()
peer := peers[addr.IP.String()] peer := peers[addr.IP.String()]
peersMutex.Unlock() peersMutex.Unlock()
@@ -113,38 +109,55 @@ func handleReq(req []byte, addr *net.UDPAddr) {
return return
} }
pkt, err := shared.DecryptSym(pskKey, req[4:]) destIP := net.IP(req[4:20]).String()
if err != nil { srcIP := net.IP(req[20:36]).String()
log.Printf("failed to decrypt packet from %s: %s", peer.RealAddr, err) if srcIP != peer.InternalIP {
log.Println("rejected spoofed srcIP in ENC_PKT")
return return
} }
etherType := binary.BigEndian.Uint16(pkt[12:14]) if destPeer := getPeerByInternalIP(destIP); destPeer != nil {
payload := pkt[14:] log.Println(srcIP + " -> " + destIP)
sendTo(destPeer.RealAddr, req)
switch etherType {
case 0x86dd: // IPv6
rawDestIP := payload[24:40]
destIP := net.IP(rawDestIP).String()
encryptedPkt, err := shared.EncryptSym(pskKey, pkt)
if err != nil {
panic(err)
}
resp := shared.BuildPkt(shared.RESP_DATA, encryptedPkt)
if rawDestIP[0] == 0xff { // multicast
log.Println(peer.InternalIP + " -> " + destIP + " (*)")
broadcast(resp, peer.InternalIP)
} else if destPeer := getPeerByInternalIP(destIP); destPeer != nil {
log.Println(peer.InternalIP + " -> " + destIP)
sendTo(destPeer.RealAddr, resp)
} else { } else {
log.Println(peer.InternalIP + " -/> " + destIP + " (unrecognized IP)") log.Println(srcIP + " -/> " + destIP + " (unrecognized IP)")
} }
default: case shared.BROADCAST_PKT:
log.Printf("unhandled EtherType: 0x%x\n", etherType) peersMutex.Lock()
peer := peers[addr.IP.String()]
peersMutex.Unlock()
if peer == nil {
log.Println("data from unregistered peer:", addr.String())
return
} }
destIP := net.IP(req[42:58]).String()
log.Println(peer.InternalIP + " -> " + destIP)
broadcast(req, peer.InternalIP)
case shared.REQ_GET_PUBKEY:
peerIP := net.IP(req[4:20]).String()
peer := getPeerByInternalIP(peerIP)
if peer == nil {
log.Println(addr.String(), "tried to get pubkey of unknown peer", peerIP)
return
}
resp := shared.BuildPkt(shared.RESP_GET_PUBKEY, append(net.ParseIP(peer.InternalIP), peer.PublicKey...))
sendTo(addr, resp)
case shared.REQ_ESTABLISH, shared.RESP_ESTABLISH:
peersMutex.Lock()
peer := peers[addr.IP.String()]
peersMutex.Unlock()
srcIP := net.IP(req[20:36]).String()
if srcIP != peer.InternalIP {
log.Println("rejected spoofed srcIP in ESTABLISH")
return
}
dstIP := net.IP(req[4:20]).String()
dstPeer := getPeerByInternalIP(dstIP)
sendTo(dstPeer.RealAddr, req)
default: default:
log.Println("unknown packet type") log.Println("unknown packet type")
} }

View File

@@ -6,14 +6,30 @@ const PROTO_VERSION uint16 = 1
const ( const (
unused uint16 = iota unused uint16 = iota
// - empty - // (client -> server) requests an IP
// [ pubkey - 1216 bytes ]
REQ_REGISTER REQ_REGISTER
// (server -> client) assigns an IP
// [ ip - 16 bytes ] // [ ip - 16 bytes ]
RESP_REGISTER RESP_REGISTER
// [ encrypted pkt ] // (client -> server -> client2) relays an encrypted packet to a specified peer
REQ_DATA // [ destIP - 16 bytes ] [ srcIP - 16 bytes ] [ encrypted pkt ]
// [ encrypted pkt ] ENC_PKT
RESP_DATA // (client -> server -> *) broadcasts an unsecured packet
// [ plaintext pkt ]
BROADCAST_PKT
// (client -> server) requests peer's pubkey from the server for encapsulation
// [ ip - 16 bytes ]
REQ_GET_PUBKEY
// (server -> client) provides requested pubkey
// [ ip - 16 bytes ] [ pubkey - 1216 bytes ]
RESP_GET_PUBKEY
// (client -> server -> client2) establishes a session key with another peer
// [ destIP - 16 bytes ] [ srcIP - 16 bytes ] [ ciphertext - 1120 bytes ]
REQ_ESTABLISH
// (client2 -> server -> client) acknowledges the session key was established
// [ destIP - 16 bytes ] [ srcIP - 16 bytes ]
RESP_ESTABLISH
) )
func BuildPkt(pktType uint16, data []byte) []byte { func BuildPkt(pktType uint16, data []byte) []byte {