actually authenticate messages with challenges and HMACs
This commit is contained in:
101
server/main.go
101
server/main.go
@@ -3,11 +3,14 @@
|
||||
// TODO: somehow persist IPs
|
||||
// TODO: key rotation
|
||||
// TODO: dont trust claimed IP at all
|
||||
// TODO: replay attacks are still a thing
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/hpke"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"log"
|
||||
@@ -21,14 +24,16 @@ type Peer struct {
|
||||
RealAddr *net.UDPAddr
|
||||
InternalIP string
|
||||
HexPublicKey string
|
||||
AuthSecret []byte
|
||||
}
|
||||
|
||||
var (
|
||||
peers = shared.NewTMap[string, *Peer]()
|
||||
peersByInternal = shared.NewTMap[string, *Peer]()
|
||||
peersByPubKey = shared.NewTMap[string, *Peer]()
|
||||
conn *net.UDPConn
|
||||
multicastKey = make([]byte, 32)
|
||||
peers = shared.NewTMap[string, *Peer]()
|
||||
peersByInternal = shared.NewTMap[string, *Peer]()
|
||||
peersByPubKey = shared.NewTMap[string, *Peer]()
|
||||
conn *net.UDPConn
|
||||
multicastKey = make([]byte, 32)
|
||||
authSecretsByPubKey = shared.NewTMap[string, []byte]()
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -93,7 +98,7 @@ func handleReq(req []byte, addr *net.UDPAddr) {
|
||||
pktType := binary.LittleEndian.Uint16(req[2:])
|
||||
|
||||
switch pktType {
|
||||
case shared.REQ_REGISTER:
|
||||
case shared.REQ_GET_CHALLENGE:
|
||||
pubKey, err := hpke.MLKEM768X25519().NewPublicKey(req[4:])
|
||||
if err != nil {
|
||||
log.Println("invalid pubkey")
|
||||
@@ -102,10 +107,48 @@ func handleReq(req []byte, addr *net.UDPAddr) {
|
||||
|
||||
hexPubKey := hex.EncodeToString(pubKey.Bytes())
|
||||
|
||||
authSecret := make([]byte, 32)
|
||||
if _, err = rand.Read(authSecret); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
authSecretsByPubKey.Set(hexPubKey, authSecret)
|
||||
|
||||
ciphertext, err := hpke.Seal(pubKey, hpke.HKDFSHA256(), hpke.ChaCha20Poly1305(), []byte("baalvpn-challenge"), authSecret)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sendTo(addr, shared.BuildPkt(shared.RESP_GET_CHALLENGE, ciphertext))
|
||||
case shared.REQ_REGISTER:
|
||||
auth := req[4:36]
|
||||
|
||||
pubKey, err := hpke.MLKEM768X25519().NewPublicKey(req[36:])
|
||||
if err != nil {
|
||||
log.Println("invalid pubkey")
|
||||
return
|
||||
}
|
||||
|
||||
hexPubKey := hex.EncodeToString(pubKey.Bytes())
|
||||
|
||||
authSecret, ok := authSecretsByPubKey.GetOK(hexPubKey)
|
||||
if !ok {
|
||||
log.Println("unknown pubkey tried to REGISTER")
|
||||
return
|
||||
}
|
||||
defer authSecretsByPubKey.Delete(hexPubKey)
|
||||
|
||||
mac := hmac.New(sha256.New, authSecret)
|
||||
mac.Write(pubKey.Bytes())
|
||||
if !hmac.Equal(mac.Sum(nil), auth) {
|
||||
log.Println("failed to authenticate REGISTER")
|
||||
return
|
||||
}
|
||||
|
||||
var internalIP string
|
||||
// TODO: authenticating only by PUBLIC key is obviously a bad idea
|
||||
if peer, ok := peersByPubKey.GetOK(hexPubKey); ok {
|
||||
internalIP = peer.InternalIP
|
||||
peer.AuthSecret = authSecret
|
||||
peers.Delete(peer.RealAddr.String())
|
||||
peer.RealAddr = addr
|
||||
peers.Set(addr.String(), peer)
|
||||
@@ -115,28 +158,35 @@ func handleReq(req []byte, addr *net.UDPAddr) {
|
||||
RealAddr: addr,
|
||||
InternalIP: internalIP,
|
||||
HexPublicKey: hexPubKey,
|
||||
AuthSecret: authSecret,
|
||||
}
|
||||
peers.Set(addr.String(), peer)
|
||||
peersByInternal.Set(internalIP, peer)
|
||||
peersByPubKey.Set(hexPubKey, peer)
|
||||
}
|
||||
|
||||
ciphertext, err := hpke.Seal(pubKey, hpke.HKDFSHA256(), hpke.ChaCha20Poly1305(), []byte("baalvpn"), multicastKey)
|
||||
ciphertext, err := hpke.Seal(pubKey, hpke.HKDFSHA256(), hpke.ChaCha20Poly1305(), []byte("baalvpn-register"), multicastKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
resp := shared.BuildPkt(shared.RESP_REGISTER, append(net.ParseIP(internalIP), ciphertext...))
|
||||
sendTo(addr, resp)
|
||||
sendTo(addr, shared.BuildPkt(shared.RESP_REGISTER, net.ParseIP(internalIP), ciphertext))
|
||||
case shared.ENC_PKT:
|
||||
peer := peers.Get(addr.String())
|
||||
if peer == nil {
|
||||
log.Println("data from unregistered peer:", addr.String())
|
||||
log.Println("ENC_PKT from unregistered peer:", addr.String())
|
||||
return
|
||||
}
|
||||
|
||||
destIP := net.IP(req[4:20]).String()
|
||||
srcIP := net.IP(req[20:36]).String()
|
||||
mac := hmac.New(sha256.New, peer.AuthSecret)
|
||||
mac.Write(req[36:])
|
||||
if !hmac.Equal(mac.Sum(nil), req[4:36]) {
|
||||
log.Println("failed to authenticate ENC_PKT")
|
||||
return
|
||||
}
|
||||
|
||||
destIP := net.IP(req[36:52]).String()
|
||||
srcIP := net.IP(req[52:68]).String()
|
||||
if srcIP != peer.InternalIP {
|
||||
log.Println("rejected spoofed srcIP in ENC_PKT")
|
||||
return
|
||||
@@ -151,7 +201,14 @@ func handleReq(req []byte, addr *net.UDPAddr) {
|
||||
case shared.BROADCAST_PKT:
|
||||
peer := peers.Get(addr.String())
|
||||
if peer == nil {
|
||||
log.Println("data from unregistered peer:", addr.String())
|
||||
log.Println("BROADCAST_PKT from unregistered peer:", addr.String())
|
||||
return
|
||||
}
|
||||
|
||||
mac := hmac.New(sha256.New, peer.AuthSecret)
|
||||
mac.Write(req[36:])
|
||||
if !hmac.Equal(mac.Sum(nil), req[4:36]) {
|
||||
log.Println("failed to authenticate BROADCAST_PKT")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -165,23 +222,29 @@ func handleReq(req []byte, addr *net.UDPAddr) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
resp := shared.BuildPkt(shared.RESP_GET_PUBKEY, append(net.ParseIP(peerIP), pubKey...))
|
||||
sendTo(addr, resp)
|
||||
sendTo(addr, shared.BuildPkt(shared.RESP_GET_PUBKEY, net.ParseIP(peerIP), pubKey))
|
||||
}
|
||||
case shared.REQ_ESTABLISH, shared.RESP_ESTABLISH:
|
||||
peer := peers.Get(addr.String())
|
||||
if peer == nil {
|
||||
log.Println("unregistered peer tried to ESTABLISH")
|
||||
log.Println("ESTABLISH from unregistered peer:", addr.String())
|
||||
return
|
||||
}
|
||||
|
||||
srcIP := net.IP(req[20:36]).String()
|
||||
mac := hmac.New(sha256.New, peer.AuthSecret)
|
||||
mac.Write(req[36:])
|
||||
if !hmac.Equal(mac.Sum(nil), req[4:36]) {
|
||||
log.Println("failed to authenticate ESTABLISH")
|
||||
return
|
||||
}
|
||||
|
||||
srcIP := net.IP(req[52:68]).String()
|
||||
if srcIP != peer.InternalIP {
|
||||
log.Println("rejected spoofed srcIP in ESTABLISH")
|
||||
return
|
||||
}
|
||||
|
||||
dstIP := net.IP(req[4:20]).String()
|
||||
dstIP := net.IP(req[36:52]).String()
|
||||
dstPeer := peersByInternal.Get(dstIP)
|
||||
if dstPeer == nil {
|
||||
log.Println("tried to ESTABLISH with an unknown peer")
|
||||
|
||||
Reference in New Issue
Block a user