sym-encryption #1
61
node/cipher-data.go
Normal file
61
node/cipher-data.go
Normal file
@ -0,0 +1,61 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
)
|
||||
|
||||
type dataCipher struct {
|
||||
key []byte
|
||||
aead cipher.AEAD
|
||||
}
|
||||
|
||||
func newDataCipher() *dataCipher {
|
||||
key := make([]byte, 32)
|
||||
if _, err := rand.Read(key); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return newDataCipherFromKey(key)
|
||||
}
|
||||
|
||||
// key must be 32 bytes.
|
||||
func newDataCipherFromKey(key []byte) *dataCipher {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
aead, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &dataCipher{key: key, aead: aead}
|
||||
}
|
||||
|
||||
func (sc *dataCipher) Key() []byte {
|
||||
return sc.key
|
||||
}
|
||||
|
||||
func (sc *dataCipher) Encrypt(h xHeader, data, out []byte) []byte {
|
||||
const s = dataHeaderSize
|
||||
out = out[:s+dataCipherOverhead+len(data)]
|
||||
h.Marshal(dataStreamID, out[:s])
|
||||
sc.aead.Seal(out[s:s], out[:s], data, nil)
|
||||
return out
|
||||
}
|
||||
|
||||
func (sc *dataCipher) Decrypt(encrypted, out []byte) (data []byte, ok bool) {
|
||||
const s = dataHeaderSize
|
||||
if len(encrypted) < s+dataCipherOverhead {
|
||||
ok = false
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
data, err = sc.aead.Open(out[:0], encrypted[:s], encrypted[s:], nil)
|
||||
ok = err == nil
|
||||
return
|
||||
}
|
@ -22,7 +22,7 @@ func TestDataCipher(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, plaintext := range testCases {
|
||||
h1 := dataHeader{
|
||||
h1 := xHeader{
|
||||
Counter: 235153,
|
||||
SourceIP: 4,
|
||||
DestIP: 88,
|
||||
@ -31,11 +31,13 @@ func TestDataCipher(t *testing.T) {
|
||||
encrypted := make([]byte, bufferSize)
|
||||
|
||||
dc1 := newDataCipher()
|
||||
encrypted = dc1.Encrypt(&h1, plaintext, encrypted)
|
||||
encrypted = dc1.Encrypt(h1, plaintext, encrypted)
|
||||
h2 := xHeader{}
|
||||
h2.Parse(encrypted)
|
||||
|
||||
dc2 := newDataCipherFromKey(dc1.Key())
|
||||
|
||||
decrypted, h2, ok := dc2.Decrypt(encrypted, make([]byte, bufferSize-dataHeaderSize))
|
||||
decrypted, ok := dc2.Decrypt(encrypted, make([]byte, bufferSize-dataHeaderSize))
|
||||
if !ok {
|
||||
t.Fatal(ok)
|
||||
}
|
||||
@ -64,7 +66,7 @@ func TestDataCipher_ModifyCiphertext(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, plaintext := range testCases {
|
||||
h1 := dataHeader{
|
||||
h1 := xHeader{
|
||||
Counter: 235153,
|
||||
SourceIP: 4,
|
||||
DestIP: 88,
|
||||
@ -73,14 +75,14 @@ func TestDataCipher_ModifyCiphertext(t *testing.T) {
|
||||
encrypted := make([]byte, bufferSize)
|
||||
|
||||
dc1 := newDataCipher()
|
||||
encrypted = dc1.Encrypt(&h1, plaintext, encrypted)
|
||||
encrypted = dc1.Encrypt(h1, plaintext, encrypted)
|
||||
encrypted[mrand.IntN(len(encrypted))]++
|
||||
|
||||
dc2 := newDataCipherFromKey(dc1.Key())
|
||||
|
||||
_, h2, ok := dc2.Decrypt(encrypted, make([]byte, bufferSize-dataHeaderSize))
|
||||
_, ok := dc2.Decrypt(encrypted, make([]byte, bufferSize-dataHeaderSize))
|
||||
if ok {
|
||||
t.Fatal(ok, h2)
|
||||
t.Fatal(ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -89,14 +91,14 @@ func TestDataCipher_ShortCiphertext(t *testing.T) {
|
||||
dc1 := newDataCipher()
|
||||
shortText := make([]byte, dataHeaderSize+dataCipherOverhead-1)
|
||||
rand.Read(shortText)
|
||||
_, _, ok := dc1.Decrypt(shortText, make([]byte, bufferSize))
|
||||
_, ok := dc1.Decrypt(shortText, make([]byte, bufferSize))
|
||||
if ok {
|
||||
t.Fatal(ok)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDataCipher_Encrypt(b *testing.B) {
|
||||
h1 := dataHeader{
|
||||
h1 := xHeader{
|
||||
Counter: 235153,
|
||||
SourceIP: 4,
|
||||
DestIP: 88,
|
||||
@ -110,12 +112,12 @@ func BenchmarkDataCipher_Encrypt(b *testing.B) {
|
||||
dc1 := newDataCipher()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
encrypted = dc1.Encrypt(&h1, plaintext, encrypted)
|
||||
encrypted = dc1.Encrypt(h1, plaintext, encrypted)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDataCipher_Decrypt(b *testing.B) {
|
||||
h1 := dataHeader{
|
||||
h1 := xHeader{
|
||||
Counter: 235153,
|
||||
SourceIP: 4,
|
||||
DestIP: 88,
|
||||
@ -127,12 +129,12 @@ func BenchmarkDataCipher_Decrypt(b *testing.B) {
|
||||
encrypted := make([]byte, bufferSize)
|
||||
|
||||
dc1 := newDataCipher()
|
||||
encrypted = dc1.Encrypt(&h1, plaintext, encrypted)
|
||||
encrypted = dc1.Encrypt(h1, plaintext, encrypted)
|
||||
|
||||
decrypted := make([]byte, bufferSize)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
decrypted, _, _ = dc1.Decrypt(encrypted, decrypted)
|
||||
decrypted, _ = dc1.Decrypt(encrypted, decrypted)
|
||||
}
|
||||
}
|
26
node/cipher-routing.go
Normal file
26
node/cipher-routing.go
Normal file
@ -0,0 +1,26 @@
|
||||
package node
|
||||
|
||||
import "golang.org/x/crypto/nacl/box"
|
||||
|
||||
type routingCipher struct {
|
||||
sharedKey [32]byte
|
||||
}
|
||||
|
||||
func newRoutingCipher(privKey, pubKey []byte) routingCipher {
|
||||
shared := [32]byte{}
|
||||
box.Precompute(&shared, (*[32]byte)(pubKey), (*[32]byte)(privKey))
|
||||
return routingCipher{shared}
|
||||
}
|
||||
|
||||
func (rc routingCipher) Encrypt(h xHeader, data, out []byte) []byte {
|
||||
const s = routingHeaderSize
|
||||
out = out[:s+routingCipherOverhead+len(data)]
|
||||
h.Marshal(routingStreamID, out[:s])
|
||||
box.SealAfterPrecomputation(out[s:s], data, (*[24]byte)(out[:s]), &rc.sharedKey)
|
||||
return out
|
||||
}
|
||||
|
||||
func (rc routingCipher) Decrypt(encrypted, out []byte) (data []byte, ok bool) {
|
||||
const s = routingHeaderSize
|
||||
return box.OpenAfterPrecomputation(out[:0], encrypted[s:], (*[24]byte)(encrypted[:s]), &rc.sharedKey)
|
||||
}
|
114
node/cipher-routing_test.go
Normal file
114
node/cipher-routing_test.go
Normal file
@ -0,0 +1,114 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/crypto/nacl/box"
|
||||
)
|
||||
|
||||
func newRoutingCipherForTesting() (c1, c2 routingCipher) {
|
||||
pubKey1, privKey1, err := box.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
pubKey2, privKey2, err := box.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return newRoutingCipher(privKey1[:], pubKey2[:]),
|
||||
newRoutingCipher(privKey2[:], pubKey1[:])
|
||||
}
|
||||
|
||||
func TestRoutingCipher(t *testing.T) {
|
||||
c1, c2 := newRoutingCipherForTesting()
|
||||
|
||||
maxSizePlaintext := make([]byte, bufferSize-routingHeaderSize-routingCipherOverhead)
|
||||
rand.Read(maxSizePlaintext)
|
||||
|
||||
testCases := [][]byte{
|
||||
make([]byte, 0),
|
||||
{1},
|
||||
{255},
|
||||
{1, 2, 3, 4, 5},
|
||||
[]byte("Hello world"),
|
||||
maxSizePlaintext,
|
||||
}
|
||||
|
||||
for _, plaintext := range testCases {
|
||||
h1 := xHeader{
|
||||
Counter: 235153,
|
||||
SourceIP: 4,
|
||||
DestIP: 88,
|
||||
}
|
||||
|
||||
encrypted := make([]byte, bufferSize)
|
||||
|
||||
encrypted = c1.Encrypt(h1, plaintext, encrypted)
|
||||
|
||||
decrypted, ok := c2.Decrypt(encrypted, make([]byte, bufferSize))
|
||||
if !ok {
|
||||
t.Fatal(ok)
|
||||
}
|
||||
|
||||
if !bytes.Equal(decrypted, plaintext) {
|
||||
t.Fatal("not equal")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoutingCipher_ShortCiphertext(t *testing.T) {
|
||||
c1, _ := newRoutingCipherForTesting()
|
||||
shortText := make([]byte, routingHeaderSize+routingCipherOverhead-1)
|
||||
rand.Read(shortText)
|
||||
_, ok := c1.Decrypt(shortText, make([]byte, bufferSize))
|
||||
if ok {
|
||||
t.Fatal(ok)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRoutingCipher_Encrypt(b *testing.B) {
|
||||
c1, _ := newRoutingCipherForTesting()
|
||||
h1 := xHeader{
|
||||
Counter: 235153,
|
||||
SourceIP: 4,
|
||||
DestIP: 88,
|
||||
}
|
||||
|
||||
plaintext := make([]byte, bufferSize-routingHeaderSize-routingCipherOverhead)
|
||||
rand.Read(plaintext)
|
||||
|
||||
encrypted := make([]byte, bufferSize)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
encrypted = c1.Encrypt(h1, plaintext, encrypted)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRoutingCipher_Decrypt(b *testing.B) {
|
||||
c1, c2 := newRoutingCipherForTesting()
|
||||
|
||||
h1 := xHeader{
|
||||
Counter: 235153,
|
||||
SourceIP: 4,
|
||||
DestIP: 88,
|
||||
}
|
||||
|
||||
plaintext := make([]byte, bufferSize-routingHeaderSize-routingCipherOverhead)
|
||||
rand.Read(plaintext)
|
||||
|
||||
encrypted := make([]byte, bufferSize)
|
||||
|
||||
encrypted = c1.Encrypt(h1, plaintext, encrypted)
|
||||
|
||||
decrypted := make([]byte, bufferSize)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
decrypted, _ = c2.Decrypt(encrypted, decrypted)
|
||||
}
|
||||
}
|
6
node/cipher.go
Normal file
6
node/cipher.go
Normal file
@ -0,0 +1,6 @@
|
||||
package node
|
||||
|
||||
type packetCipher interface {
|
||||
Encrypt(h xHeader, data, out []byte) []byte
|
||||
Decrypt(encrypted, out []byte) (data []byte, ok bool)
|
||||
}
|
39
node/conn.go
39
node/conn.go
@ -35,6 +35,33 @@ func newConnWriter(conn *net.UDPConn, localIP byte, routing *routingTable) *conn
|
||||
return w
|
||||
}
|
||||
|
||||
/*
|
||||
func (w *connWriter) SendRouting(remoteIP byte, data []byte) {
|
||||
dstPeer := w.routing.Get(remoteIP)
|
||||
if dstPeer == nil {
|
||||
log.Printf("No peer: %d", remoteIP)
|
||||
return
|
||||
}
|
||||
|
||||
var viaPeer *peer
|
||||
|
||||
if dstPeer.Addr == zeroAddrPort {
|
||||
viaPeer = w.routing.Mediator()
|
||||
if viaPeer == nil {
|
||||
log.Printf("No mediator: %d", remoteIP)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
w.sendRouting(dstPeer, viaPeer, data)
|
||||
}
|
||||
*/
|
||||
|
||||
func (w *connWriter) SendData(remoteIP byte, data []byte) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// TODO: deprecated
|
||||
func (w *connWriter) WriteTo(remoteIP, stream byte, data []byte) {
|
||||
dstPeer := w.routing.Get(remoteIP)
|
||||
if dstPeer == nil {
|
||||
@ -50,11 +77,11 @@ func (w *connWriter) WriteTo(remoteIP, stream byte, data []byte) {
|
||||
var viaPeer *peer
|
||||
if dstPeer.Mediated {
|
||||
viaPeer = w.routing.mediator.Load()
|
||||
if viaPeer == nil || viaPeer.Addr == nil {
|
||||
if viaPeer == nil || viaPeer.Addr == zeroAddrPort {
|
||||
log.Printf("Mediator not connected")
|
||||
return
|
||||
}
|
||||
} else if dstPeer.Addr == nil {
|
||||
} else if dstPeer.Addr == zeroAddrPort {
|
||||
log.Printf("Peer doesn't have address: %d", remoteIP)
|
||||
return
|
||||
}
|
||||
@ -62,6 +89,7 @@ func (w *connWriter) WriteTo(remoteIP, stream byte, data []byte) {
|
||||
w.WriteToPeer(dstPeer, viaPeer, stream, data)
|
||||
}
|
||||
|
||||
// TODO: deprecated
|
||||
func (w *connWriter) WriteToPeer(dstPeer, viaPeer *peer, stream byte, data []byte) {
|
||||
w.lock.Lock()
|
||||
|
||||
@ -89,20 +117,21 @@ func (w *connWriter) WriteToPeer(dstPeer, viaPeer *peer, stream byte, data []byt
|
||||
addr = viaPeer.Addr
|
||||
}
|
||||
|
||||
if _, err := w.WriteToUDPAddrPort(buf, *addr); err != nil {
|
||||
if _, err := w.WriteToUDPAddrPort(buf, addr); err != nil {
|
||||
log.Fatalf("Failed to write to UDP port: %v", err)
|
||||
}
|
||||
w.lock.Unlock()
|
||||
}
|
||||
|
||||
// TODO: deprecated
|
||||
func (w *connWriter) Forward(dstIP byte, packet []byte) {
|
||||
dstPeer := w.routing.Get(dstIP)
|
||||
if dstPeer == nil || dstPeer.Addr == nil {
|
||||
if dstPeer == nil || dstPeer.Addr == zeroAddrPort {
|
||||
log.Printf("No peer: %d", dstIP)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := w.WriteToUDPAddrPort(packet, *dstPeer.Addr); err != nil {
|
||||
if _, err := w.WriteToUDPAddrPort(packet, dstPeer.Addr); err != nil {
|
||||
log.Fatalf("Failed to write to UDP port: %v", err)
|
||||
}
|
||||
}
|
||||
|
@ -1,97 +0,0 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const (
|
||||
dataStreamID = 1
|
||||
dataHeaderSize = 12
|
||||
dataCipherOverhead = 16 + 1
|
||||
)
|
||||
|
||||
type dataHeader struct {
|
||||
Counter uint64 // Init with fasttime.Now() << 30 to ensure monotonic.
|
||||
SourceIP byte
|
||||
DestIP byte
|
||||
}
|
||||
|
||||
func (h *dataHeader) Parse(b []byte) {
|
||||
h.Counter = *(*uint64)(unsafe.Pointer(&b[0]))
|
||||
h.SourceIP = b[8]
|
||||
h.DestIP = b[9]
|
||||
}
|
||||
|
||||
func (h *dataHeader) Marshal(buf []byte) {
|
||||
*(*uint64)(unsafe.Pointer(&buf[0])) = h.Counter
|
||||
buf[8] = h.SourceIP
|
||||
buf[9] = h.DestIP
|
||||
buf[10] = 0
|
||||
buf[11] = 0
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type dataCipher struct {
|
||||
key []byte
|
||||
aead cipher.AEAD
|
||||
}
|
||||
|
||||
func newDataCipher() *dataCipher {
|
||||
key := make([]byte, 32)
|
||||
if _, err := rand.Read(key); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return newDataCipherFromKey(key)
|
||||
}
|
||||
|
||||
// key must be 32 bytes.
|
||||
func newDataCipherFromKey(key []byte) *dataCipher {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
aead, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &dataCipher{key: key, aead: aead}
|
||||
}
|
||||
|
||||
func (sc *dataCipher) Key() []byte {
|
||||
return sc.key
|
||||
}
|
||||
|
||||
func (sc *dataCipher) Encrypt(h *dataHeader, data, out []byte) []byte {
|
||||
out = out[:dataHeaderSize+dataCipherOverhead+len(data)]
|
||||
out[0] = dataStreamID
|
||||
|
||||
h.Marshal(out[1:])
|
||||
|
||||
const s = dataHeaderSize
|
||||
sc.aead.Seal(out[1+s:1+s], out[1:1+s], data, nil)
|
||||
return out
|
||||
}
|
||||
|
||||
func (sc *dataCipher) Decrypt(encrypted, out []byte) (data []byte, h dataHeader, ok bool) {
|
||||
const s = dataHeaderSize
|
||||
if len(encrypted) < s+dataCipherOverhead {
|
||||
ok = false
|
||||
return
|
||||
}
|
||||
|
||||
h.Parse(encrypted[1 : 1+s])
|
||||
|
||||
var err error
|
||||
|
||||
data, err = sc.aead.Open(out[:0], encrypted[1:1+s], encrypted[1+s:], nil)
|
||||
ok = err == nil
|
||||
return
|
||||
}
|
@ -2,6 +2,41 @@ package node
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const (
|
||||
routingStreamID = 2
|
||||
routingHeaderSize = 24
|
||||
routingCipherOverhead = 16
|
||||
|
||||
dataStreamID = 1
|
||||
dataHeaderSize = 12
|
||||
dataCipherOverhead = 16
|
||||
)
|
||||
|
||||
// TODO: Rename
|
||||
type xHeader struct {
|
||||
Counter uint64 // Init with fasttime.Now() << 30 to ensure monotonic.
|
||||
SourceIP byte
|
||||
DestIP byte
|
||||
}
|
||||
|
||||
func (h *xHeader) Parse(b []byte) {
|
||||
h.Counter = *(*uint64)(unsafe.Pointer(&b[1]))
|
||||
h.SourceIP = b[9]
|
||||
h.DestIP = b[10]
|
||||
}
|
||||
|
||||
func (h *xHeader) Marshal(streamID byte, buf []byte) {
|
||||
buf[0] = streamID
|
||||
*(*uint64)(unsafe.Pointer(&buf[1])) = h.Counter
|
||||
buf[9] = h.SourceIP
|
||||
buf[10] = h.DestIP
|
||||
buf[11] = 0
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// TODO: Remove this code.
|
||||
const (
|
||||
headerSize = 24
|
||||
streamData = 1
|
||||
|
89
node/packets.go
Normal file
89
node/packets.go
Normal file
@ -0,0 +1,89 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/netip"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var errMalformedPacket = errors.New("malformed packet")
|
||||
|
||||
const (
|
||||
packetTypePing = iota + 1
|
||||
packetTypePong
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type packetWrapper struct {
|
||||
SrcIP byte
|
||||
RemoteAddr netip.AddrPort
|
||||
Packet any
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// A pingPacket is sent from a node acting as a client, to a node acting
|
||||
// as a server. It always contains the shared key the client is expecting
|
||||
// to use for data encryption with the server.
|
||||
type pingPacket struct {
|
||||
SentAt int64 // UnixMilli.
|
||||
SharedKey [32]byte
|
||||
}
|
||||
|
||||
func newPingPacket(sharedKey []byte) (pp pingPacket) {
|
||||
pp.SentAt = time.Now().UnixMilli()
|
||||
copy(pp.SharedKey[:], sharedKey)
|
||||
return
|
||||
}
|
||||
|
||||
func (p pingPacket) Marshal(buf []byte) []byte {
|
||||
buf = buf[:41]
|
||||
buf[0] = packetTypePing
|
||||
*(*uint64)(unsafe.Pointer(&buf[1])) = uint64(p.SentAt)
|
||||
copy(buf[9:41], p.SharedKey[:])
|
||||
return buf
|
||||
}
|
||||
|
||||
func (p *pingPacket) Parse(buf []byte) error {
|
||||
if len(buf) != 41 {
|
||||
return errMalformedPacket
|
||||
}
|
||||
p.SentAt = *(*int64)(unsafe.Pointer(&buf[1]))
|
||||
copy(p.SharedKey[:], buf[9:41])
|
||||
return nil
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// A pongPacket is sent by a node in a server role in response to a pingPacket.
|
||||
type pongPacket struct {
|
||||
SentAt int64 // UnixMilli.
|
||||
RecvdAt int64 // UnixMilli.
|
||||
}
|
||||
|
||||
func newPongPacket(sentAt int64) (pp pongPacket) {
|
||||
pp.SentAt = sentAt
|
||||
pp.RecvdAt = time.Now().UnixMilli()
|
||||
return
|
||||
}
|
||||
|
||||
func (p pongPacket) Marshal(buf []byte) []byte {
|
||||
buf = buf[:17]
|
||||
buf[0] = packetTypePong
|
||||
*(*uint64)(unsafe.Pointer(&buf[1])) = uint64(p.SentAt)
|
||||
*(*uint64)(unsafe.Pointer(&buf[9])) = uint64(p.RecvdAt)
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
func (p *pongPacket) Parse(buf []byte) error {
|
||||
if len(buf) != 17 {
|
||||
return errMalformedPacket
|
||||
}
|
||||
p.SentAt = *(*int64)(unsafe.Pointer(&buf[1]))
|
||||
p.RecvdAt = *(*int64)(unsafe.Pointer(&buf[9]))
|
||||
|
||||
return nil
|
||||
}
|
42
node/packets_test.go
Normal file
42
node/packets_test.go
Normal file
@ -0,0 +1,42 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPacketPing(t *testing.T) {
|
||||
sharedKey := make([]byte, 32)
|
||||
rand.Read(sharedKey)
|
||||
|
||||
buf := make([]byte, bufferSize)
|
||||
|
||||
p := newPingPacket(sharedKey)
|
||||
out := p.Marshal(buf)
|
||||
|
||||
p2 := pingPacket{}
|
||||
if err := p2.Parse(out); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(p, p2) {
|
||||
t.Fatal(p, p2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPacketPong(t *testing.T) {
|
||||
buf := make([]byte, bufferSize)
|
||||
|
||||
p := newPongPacket(123566)
|
||||
out := p.Marshal(buf)
|
||||
|
||||
p2 := pongPacket{}
|
||||
if err := p2.Parse(out); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(p, p2) {
|
||||
t.Fatal(p, p2)
|
||||
}
|
||||
}
|
@ -36,7 +36,7 @@ type peerSupervisor struct {
|
||||
// Peer-related items.
|
||||
version int64 // Ony accessed in HandlePeerUpdate.
|
||||
peer *m.Peer
|
||||
remoteAddrPort *netip.AddrPort
|
||||
remoteAddrPort netip.AddrPort
|
||||
mediated bool
|
||||
sharedKey []byte
|
||||
|
||||
@ -123,9 +123,9 @@ func (s *peerSupervisor) stateInit() stateFunc {
|
||||
addr, ok := netip.AddrFromSlice(s.peer.PublicIP)
|
||||
if ok {
|
||||
addrPort := netip.AddrPortFrom(addr, s.peer.Port)
|
||||
s.remoteAddrPort = &addrPort
|
||||
s.remoteAddrPort = addrPort
|
||||
} else {
|
||||
s.remoteAddrPort = nil
|
||||
s.remoteAddrPort = zeroAddrPort
|
||||
}
|
||||
s.sharedKey = computeSharedKey(s.peer.EncPubKey, s.privKey)
|
||||
|
||||
@ -153,7 +153,7 @@ func (s *peerSupervisor) stateSelectRole() stateFunc {
|
||||
s.logf("STATE: SelectRole")
|
||||
s.updateRoutingTable(false)
|
||||
|
||||
if s.remoteAddrPort != nil {
|
||||
if s.remoteAddrPort != zeroAddrPort {
|
||||
s.mediated = false
|
||||
|
||||
// If both remote and local are public, one side acts as client, and one
|
||||
@ -186,7 +186,7 @@ func (s *peerSupervisor) stateAccept() stateFunc {
|
||||
switch pkt.Type {
|
||||
|
||||
case packetTypePing:
|
||||
s.remoteAddrPort = &pkt.Addr
|
||||
s.remoteAddrPort = pkt.Addr
|
||||
s.updateRoutingTable(true)
|
||||
s.sendPong(pkt.TraceID)
|
||||
return s.stateConnected
|
||||
@ -256,8 +256,8 @@ func (s *peerSupervisor) stateConnected() stateFunc {
|
||||
|
||||
// Server should always follow remote port.
|
||||
if s.localPublic {
|
||||
if pkt.Addr != *s.remoteAddrPort {
|
||||
s.remoteAddrPort = &pkt.Addr
|
||||
if pkt.Addr != s.remoteAddrPort {
|
||||
s.remoteAddrPort = pkt.Addr
|
||||
s.updateRoutingTable(true)
|
||||
}
|
||||
}
|
||||
|
@ -12,12 +12,18 @@ import (
|
||||
"vppn/m"
|
||||
)
|
||||
|
||||
var zeroAddrPort = netip.AddrPort{}
|
||||
|
||||
type peer struct {
|
||||
Up bool // No data will be sent to peers that are down.
|
||||
Mediator bool
|
||||
IP byte // The VPN IP.
|
||||
Up bool // No data will be sent to peers that are down.
|
||||
Addr netip.AddrPort // If we have direct connection, otherwise use mediator.
|
||||
Mediator bool // True if the peer will mediate.
|
||||
RoutingCipher routingCipher
|
||||
DataCipher dataCipher
|
||||
|
||||
// TODO: Deprecated below.
|
||||
Mediated bool
|
||||
IP byte
|
||||
Addr *netip.AddrPort // If we have direct connection, otherwise use mediator.
|
||||
SharedKey []byte
|
||||
}
|
||||
|
||||
@ -48,6 +54,10 @@ func (r *routingTable) Set(ip byte, p *peer) {
|
||||
r.table[ip].Store(p)
|
||||
}
|
||||
|
||||
func (r *routingTable) Mediator() *peer {
|
||||
return r.mediator.Load()
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type router struct {
|
||||
|
@ -1,18 +1,9 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var errMalformedPacket = errors.New("malformed packet")
|
||||
|
||||
const (
|
||||
// Used to maintain connection.
|
||||
packetTypePing = iota + 1
|
||||
packetTypePong
|
||||
)
|
||||
|
||||
type routingPacket struct {
|
||||
Type byte // One of the packetType* constants.
|
||||
TraceID uint64 // For matching requests and responses.
|
||||
|
Loading…
x
Reference in New Issue
Block a user