From 15897003a058a3c97360f11579deb9dcd81b376b Mon Sep 17 00:00:00 2001 From: mykola2312 <49044616+mykola2312@users.noreply.github.com> Date: Mon, 13 Jan 2025 08:49:28 +0200 Subject: [PATCH] implement nonce per-route check --- net/lux_noncelist.go | 4 ++++ net/lux_packet.go | 12 +++++++++++- net/lux_router.go | 33 +++++++++++++++++++++++++++------ 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/net/lux_noncelist.go b/net/lux_noncelist.go index 48556c5..4c0386e 100644 --- a/net/lux_noncelist.go +++ b/net/lux_noncelist.go @@ -41,6 +41,10 @@ func (list *LuxNonceList) RotateOrFail(newNonce uint64) bool { return true } +func (list *LuxNonceList) Flush() { + list.nonces = make([]uint64, 0) +} + func (list *LuxNonceList) Get(idx int) uint64 { return list.nonces[idx] } diff --git a/net/lux_packet.go b/net/lux_packet.go index edfdecb..b10a1a6 100644 --- a/net/lux_packet.go +++ b/net/lux_packet.go @@ -18,7 +18,7 @@ const ( LuxPacketTypeSync = 1 ) -const LUX_PROTO_PACKET_HDRLEN = proto.LUX_PROTO_ID_SIZE + 2 +const LUX_PROTO_PACKET_HDRLEN = 8 + proto.LUX_PROTO_ID_SIZE + 2 // Target, Type are decoded from header // Buffer contains LuxBuffer with stripped headers @@ -28,6 +28,7 @@ const LUX_PROTO_PACKET_HDRLEN = proto.LUX_PROTO_ID_SIZE + 2 // Target (ID) is rather confusing. What it actually describes is // associated key with route. type LuxPacket struct { + Nonce uint64 Target proto.LuxID Type LuxPacketType Buffer proto.LuxBuffer @@ -52,6 +53,13 @@ func DecryptLuxPacket(dgram LuxDatagram, key crypto.LuxKey) (LuxPacket, error) { decrypter.CryptBlocks(payload, dgram.Payload) packet.Buffer = proto.FromSlice(payload) + + if nonce, err := packet.Buffer.ReadUint64(); err == nil { + packet.Nonce = nonce + } else { + return packet, err + } + if err := packet.Target.Read(&packet.Buffer); err != nil { return packet, err @@ -99,6 +107,8 @@ func EncryptLuxPacket(packet LuxPacket, key crypto.LuxKey, target *net.UDPAddr) } wd := proto.AllocLuxBuffer(packetLen + paddingLen) + + wd.WriteUint64(packet.Nonce) key.Id.Write(&wd) wd.WriteUint16(uint16(packet.Type)) wd.WriteBytes(packet.Buffer.AllBytes()) diff --git a/net/lux_router.go b/net/lux_router.go index fed3383..3ecc262 100644 --- a/net/lux_router.go +++ b/net/lux_router.go @@ -14,6 +14,7 @@ type LuxRoute struct { Key crypto.LuxKey Destination *net.UDPAddr Associated *LuxChannel + Nonces LuxNonceList } type LuxRouter struct { @@ -83,6 +84,7 @@ func (r *LuxRouter) CreateOutboundRoute(id proto.LuxID, chType LuxChannelType, u Key: key, Destination: channel.Address, Associated: r.addOutboundChannel(channel), + Nonces: NewLuxNonceList(), }) return nil } @@ -97,6 +99,7 @@ func (r *LuxRouter) CreateInboundChannel(chType LuxChannelType, udpAddr string) Key: r.thisKey, Destination: channel.Address, Associated: r.addInboundChannel(channel), + Nonces: NewLuxNonceList(), }) return nil } @@ -216,6 +219,11 @@ func (r *LuxRouter) Recv() (LuxPacket, error) { var err error var packet LuxPacket + /* BUG: + * nodes share same UUID. When updating routes by UUID, it will overwrite others' nodes routes. + * this is current limitation of protocol + */ + // first we look key from routes if route, ok := r.GetRoute(dgram.Target); ok { packet, err = DecryptLuxPacket(dgram, route.Key) @@ -230,11 +238,11 @@ func (r *LuxRouter) Recv() (LuxPacket, error) { r.DeleteRoute(&route) return packet, errors.New("bogus packet from established route") /* NOTE: - * there may be rare situation where multiple clients behind NAT/CGNAT - * reusing same IP:Port of established route, confusing routing table. - * This is not critical, since hosts and nodes are expected to send - * data again after interval of time, and packet loss is expected and - * tolerated at protocol design level. + * there may be rare situation where multiple clients behind NAT/CGNAT + * reusing same IP:Port of established route, confusing routing table. + * This is not critical, since hosts and nodes are expected to send + * data again after interval of time, and packet loss is expected and + * tolerated at protocol design level. */ } @@ -250,16 +258,27 @@ func (r *LuxRouter) Recv() (LuxPacket, error) { if bytes.Equal(packet.Target.UUID[:], key.Id.UUID[:]) { // key UUID and decrypted UUID matching - create OR update route and return packet + var route *LuxRoute if _, idx := r.getRouteIndexByID(packet.Target); idx != -1 { - route := &r.routes[idx] + route = &r.routes[idx] route.Destination = dgram.Target route.Associated = dgram.Channel + // since packet arrived from different transport, we flush nonces + route.Nonces.Flush() } else { r.routes = append(r.routes, LuxRoute{ Key: key, Destination: dgram.Target, Associated: dgram.Channel, + Nonces: NewLuxNonceList(), }) + route = &r.routes[len(r.routes)-1] + } + + // rotate nonce + if !route.Nonces.RotateOrFail(packet.Nonce) { + // failed nonce, discard packet + return packet, fmt.Errorf("packet failed nonce check") } packet.ChannelType = dgram.Channel.Type @@ -277,6 +296,7 @@ func (r *LuxRouter) Send(packet LuxPacket) error { return errors.New("no route to peer") } + packet.Nonce = GenerateLuxNonce() dgram, err := EncryptLuxPacket(packet, route.Key, route.Destination) if err != nil { return err @@ -289,6 +309,7 @@ func (r *LuxRouter) Send(packet LuxPacket) error { func (r *LuxRouter) Multicast(packet LuxPacket) error { for _, route := range r.routes { if bytes.Equal(route.Key.Id.UUID[:], packet.Target.UUID[:]) { + packet.Nonce = GenerateLuxNonce() dgram, err := EncryptLuxPacket(packet, route.Key, route.Destination) if err != nil { return err