Multicast passwords

This commit is contained in:
Neil Alexander 2023-10-11 19:28:28 +01:00
parent 45b773eade
commit 2a21241738
No known key found for this signature in database
GPG Key ID: A02A2019A2BB0944
7 changed files with 62 additions and 39 deletions

View File

@ -230,6 +230,7 @@ func main() {
Listen: intf.Listen, Listen: intf.Listen,
Port: intf.Port, Port: intf.Port,
Priority: uint8(intf.Priority), Priority: uint8(intf.Priority),
Password: intf.Password,
}) })
} }
if n.multicast, err = multicast.New(n.core, logger, options...); err != nil { if n.multicast, err = multicast.New(n.core, logger, options...); err != nil {

View File

@ -88,6 +88,7 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error {
Listen: intf.Listen, Listen: intf.Listen,
Port: intf.Port, Port: intf.Port,
Priority: uint8(intf.Priority), Priority: uint8(intf.Priority),
Password: intf.Password,
}) })
} }
m.multicast, err = multicast.New(m.core, m.logger, options...) m.multicast, err = multicast.New(m.core, m.logger, options...)

View File

@ -61,6 +61,7 @@ type MulticastInterfaceConfig struct {
Listen bool Listen bool
Port uint16 Port uint16
Priority uint64 // really uint8, but gobind won't export it Priority uint64 // really uint8, but gobind won't export it
Password string
} }
// Generates default configuration and returns a pointer to the resulting // Generates default configuration and returns a pointer to the resulting

View File

@ -7,21 +7,21 @@ import (
) )
type multicastAdvertisement struct { type multicastAdvertisement struct {
MajorVersion uint16 MajorVersion uint16
MinorVersion uint16 MinorVersion uint16
PublicKey ed25519.PublicKey PublicKey ed25519.PublicKey
Port uint16 Port uint16
Discriminator []byte Hash []byte
} }
func (m *multicastAdvertisement) MarshalBinary() ([]byte, error) { func (m *multicastAdvertisement) MarshalBinary() ([]byte, error) {
b := make([]byte, 0, ed25519.PublicKeySize+8+len(m.Discriminator)) b := make([]byte, 0, ed25519.PublicKeySize+8+len(m.Hash))
b = binary.BigEndian.AppendUint16(b, m.MajorVersion) b = binary.BigEndian.AppendUint16(b, m.MajorVersion)
b = binary.BigEndian.AppendUint16(b, m.MinorVersion) b = binary.BigEndian.AppendUint16(b, m.MinorVersion)
b = append(b, m.PublicKey...) b = append(b, m.PublicKey...)
b = binary.BigEndian.AppendUint16(b, m.Port) b = binary.BigEndian.AppendUint16(b, m.Port)
b = binary.BigEndian.AppendUint16(b, uint16(len(m.Discriminator))) b = binary.BigEndian.AppendUint16(b, uint16(len(m.Hash)))
b = append(b, m.Discriminator...) b = append(b, m.Hash...)
return b, nil return b, nil
} }
@ -34,6 +34,6 @@ func (m *multicastAdvertisement) UnmarshalBinary(b []byte) error {
m.PublicKey = append(m.PublicKey[:0], b[4:4+ed25519.PublicKeySize]...) m.PublicKey = append(m.PublicKey[:0], b[4:4+ed25519.PublicKeySize]...)
m.Port = binary.BigEndian.Uint16(b[4+ed25519.PublicKeySize : 6+ed25519.PublicKeySize]) m.Port = binary.BigEndian.Uint16(b[4+ed25519.PublicKeySize : 6+ed25519.PublicKeySize])
dl := binary.BigEndian.Uint16(b[6+ed25519.PublicKeySize : 8+ed25519.PublicKeySize]) dl := binary.BigEndian.Uint16(b[6+ed25519.PublicKeySize : 8+ed25519.PublicKeySize])
m.Discriminator = append(m.Discriminator[:0], b[8+ed25519.PublicKeySize:8+ed25519.PublicKeySize+dl]...) m.Hash = append(m.Hash[:0], b[8+ed25519.PublicKeySize:8+ed25519.PublicKeySize+dl]...)
return nil return nil
} }

View File

@ -13,11 +13,11 @@ func TestMulticastAdvertisementRoundTrip(t *testing.T) {
} }
orig := multicastAdvertisement{ orig := multicastAdvertisement{
MajorVersion: 1, MajorVersion: 1,
MinorVersion: 2, MinorVersion: 2,
PublicKey: pk, PublicKey: pk,
Port: 3, Port: 3,
Discriminator: sk, // any bytes will do Hash: sk, // any bytes will do
} }
ob, err := orig.MarshalBinary() ob, err := orig.MarshalBinary()

View File

@ -3,6 +3,7 @@ package multicast
import ( import (
"bytes" "bytes"
"context" "context"
"crypto/ed25519"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"math/rand" "math/rand"
@ -14,6 +15,7 @@ import (
"github.com/gologme/log" "github.com/gologme/log"
"github.com/yggdrasil-network/yggdrasil-go/src/core" "github.com/yggdrasil-network/yggdrasil-go/src/core"
"golang.org/x/crypto/blake2b"
"golang.org/x/net/ipv6" "golang.org/x/net/ipv6"
) )
@ -31,10 +33,8 @@ type Multicast struct {
_interfaces map[string]*interfaceInfo _interfaces map[string]*interfaceInfo
_timer *time.Timer _timer *time.Timer
config struct { config struct {
_discriminator []byte _groupAddr GroupAddress
_discriminatorMatch func([]byte) bool _interfaces map[MulticastInterface]struct{}
_groupAddr GroupAddress
_interfaces map[MulticastInterface]struct{}
} }
} }
@ -45,6 +45,8 @@ type interfaceInfo struct {
listen bool listen bool
port uint16 port uint16
priority uint8 priority uint8
password []byte
hash []byte
} }
type listenerInfo struct { type listenerInfo struct {
@ -178,6 +180,7 @@ func (m *Multicast) _getAllowedInterfaces() map[string]*interfaceInfo {
return nil return nil
} }
// Work out which interfaces to announce on // Work out which interfaces to announce on
pk := m.core.PublicKey()
for _, iface := range allifaces { for _, iface := range allifaces {
switch { switch {
case iface.Flags&net.FlagUp == 0: case iface.Flags&net.FlagUp == 0:
@ -196,12 +199,23 @@ func (m *Multicast) _getAllowedInterfaces() map[string]*interfaceInfo {
if !ifcfg.Regex.MatchString(iface.Name) { if !ifcfg.Regex.MatchString(iface.Name) {
continue continue
} }
hasher, err := blake2b.New512([]byte(ifcfg.Password))
if err != nil {
continue
}
if n, err := hasher.Write(pk); err != nil {
continue
} else if n != ed25519.PublicKeySize {
continue
}
interfaces[iface.Name] = &interfaceInfo{ interfaces[iface.Name] = &interfaceInfo{
iface: iface, iface: iface,
beacon: ifcfg.Beacon, beacon: ifcfg.Beacon,
listen: ifcfg.Listen, listen: ifcfg.Listen,
port: ifcfg.Port, port: ifcfg.Port,
priority: ifcfg.Priority, priority: ifcfg.Priority,
password: []byte(ifcfg.Password),
hash: hasher.Sum(nil),
} }
break break
} }
@ -298,10 +312,13 @@ func (m *Multicast) _announce() {
var linfo *listenerInfo var linfo *listenerInfo
if _, ok := m._listeners[iface.Name]; !ok { if _, ok := m._listeners[iface.Name]; !ok {
// No listener was found - let's create one // No listener was found - let's create one
urlString := fmt.Sprintf("tls://[%s]:%d", addrIP, info.port) v := &url.Values{}
u, err := url.Parse(urlString) v.Add("priority", fmt.Sprintf("%d", info.priority))
if err != nil { v.Add("password", string(info.password))
panic(err) u := &url.URL{
Scheme: "tls",
Host: net.JoinHostPort(addrIP.String(), fmt.Sprintf("%d", info.port)),
RawQuery: v.Encode(),
} }
if li, err := m.core.Listen(u, iface.Name); err == nil { if li, err := m.core.Listen(u, iface.Name); err == nil {
m.log.Debugln("Started multicasting on", iface.Name) m.log.Debugln("Started multicasting on", iface.Name)
@ -324,11 +341,11 @@ func (m *Multicast) _announce() {
} }
addr := linfo.listener.Addr().(*net.TCPAddr) addr := linfo.listener.Addr().(*net.TCPAddr)
adv := multicastAdvertisement{ adv := multicastAdvertisement{
MajorVersion: core.ProtocolVersionMajor, MajorVersion: core.ProtocolVersionMajor,
MinorVersion: core.ProtocolVersionMinor, MinorVersion: core.ProtocolVersionMinor,
PublicKey: m.core.PublicKey(), PublicKey: m.core.PublicKey(),
Port: uint16(addr.Port), Port: uint16(addr.Port),
Discriminator: m.config._discriminator, Hash: info.hash,
} }
msg, err := adv.MarshalBinary() msg, err := adv.MarshalBinary()
if err != nil { if err != nil {
@ -356,6 +373,7 @@ func (m *Multicast) listen() {
panic(err) panic(err)
} }
bs := make([]byte, 2048) bs := make([]byte, 2048)
hb := make([]byte, 0, blake2b.Size) // Reused to reduce hash allocations
for { for {
n, rcm, fromAddr, err := m.sock.ReadFrom(bs) n, rcm, fromAddr, err := m.sock.ReadFrom(bs)
if err != nil { if err != nil {
@ -386,10 +404,6 @@ func (m *Multicast) listen() {
continue continue
case adv.PublicKey.Equal(m.core.PublicKey()): case adv.PublicKey.Equal(m.core.PublicKey()):
continue continue
case m.config._discriminatorMatch == nil && !bytes.Equal(adv.Discriminator, m.config._discriminator):
continue
case m.config._discriminatorMatch != nil && !m.config._discriminatorMatch(adv.Discriminator):
continue
} }
from := fromAddr.(*net.UDPAddr) from := fromAddr.(*net.UDPAddr)
from.Port = int(adv.Port) from.Port = int(adv.Port)
@ -398,9 +412,22 @@ func (m *Multicast) listen() {
interfaces = m._interfaces interfaces = m._interfaces
}) })
if info, ok := interfaces[from.Zone]; ok && info.listen { if info, ok := interfaces[from.Zone]; ok && info.listen {
hasher, err := blake2b.New512(info.password)
if err != nil {
continue
}
if n, err := hasher.Write(adv.PublicKey); err != nil {
continue
} else if n != ed25519.PublicKeySize {
continue
}
if !bytes.Equal(hasher.Sum(hb[:0]), adv.Hash) {
continue
}
v := &url.Values{} v := &url.Values{}
v.Add("key", hex.EncodeToString(adv.PublicKey)) v.Add("key", hex.EncodeToString(adv.PublicKey))
v.Add("priority", fmt.Sprintf("%d", info.priority)) v.Add("priority", fmt.Sprintf("%d", info.priority))
v.Add("password", string(info.password))
u := &url.URL{ u := &url.URL{
Scheme: "tls", Scheme: "tls",
Host: from.String(), Host: from.String(),

View File

@ -8,10 +8,6 @@ func (m *Multicast) _applyOption(opt SetupOption) {
m.config._interfaces[v] = struct{}{} m.config._interfaces[v] = struct{}{}
case GroupAddress: case GroupAddress:
m.config._groupAddr = v m.config._groupAddr = v
case Discriminator:
m.config._discriminator = append(m.config._discriminator[:0], v...)
case DiscriminatorMatch:
m.config._discriminatorMatch = v
} }
} }
@ -25,13 +21,10 @@ type MulticastInterface struct {
Listen bool Listen bool
Port uint16 Port uint16
Priority uint8 Priority uint8
Password string
} }
type GroupAddress string type GroupAddress string
type Discriminator []byte
type DiscriminatorMatch func([]byte) bool
func (a MulticastInterface) isSetupOption() {} func (a MulticastInterface) isSetupOption() {}
func (a GroupAddress) isSetupOption() {} func (a GroupAddress) isSetupOption() {}
func (a Discriminator) isSetupOption() {}
func (a DiscriminatorMatch) isSetupOption() {}