mirror of
https://github.com/cwinfo/yggdrasil-go.git
synced 2024-12-23 08:45:39 +00:00
Merge pull request #1038 from yggdrasil-network/neil/multicast
Revise multicast format to include protocol version, discriminator for TLS roots
This commit is contained in:
commit
bcbabff80f
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
|
"crypto/sha1"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
@ -195,6 +196,10 @@ func main() {
|
|||||||
|
|
||||||
n := &node{}
|
n := &node{}
|
||||||
|
|
||||||
|
// Track certificate fingerprints for configured roots, so
|
||||||
|
// that we can match them using the multicast discriminator.
|
||||||
|
fingerprints := map[[20]byte]struct{}{}
|
||||||
|
|
||||||
// Setup the Yggdrasil node itself.
|
// Setup the Yggdrasil node itself.
|
||||||
{
|
{
|
||||||
options := []core.SetupOption{
|
options := []core.SetupOption{
|
||||||
@ -214,6 +219,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
for _, root := range cfg.RootCertificates {
|
for _, root := range cfg.RootCertificates {
|
||||||
options = append(options, core.RootCertificate(*root))
|
options = append(options, core.RootCertificate(*root))
|
||||||
|
fingerprints[sha1.Sum(root.Raw[:])] = struct{}{}
|
||||||
}
|
}
|
||||||
for _, allowed := range cfg.AllowedPublicKeys {
|
for _, allowed := range cfg.AllowedPublicKeys {
|
||||||
k, err := hex.DecodeString(allowed)
|
k, err := hex.DecodeString(allowed)
|
||||||
@ -252,6 +258,29 @@ func main() {
|
|||||||
Priority: uint8(intf.Priority),
|
Priority: uint8(intf.Priority),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if len(fingerprints) > 0 {
|
||||||
|
var matcher multicast.DiscriminatorMatch = func(b []byte) bool {
|
||||||
|
// Break apart the discriminator into 20-byte chunks and
|
||||||
|
// see whether any of them match the configured root CA
|
||||||
|
// fingerprints. If any of them match, we'll return true.
|
||||||
|
var f [20]byte
|
||||||
|
for len(b) >= len(f) {
|
||||||
|
b = b[copy(f[:], b):]
|
||||||
|
if _, ok := fingerprints[f]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Populate our own discriminator with the fingerprints of our
|
||||||
|
// configured root CAs.
|
||||||
|
var discriminator multicast.Discriminator
|
||||||
|
for f := range fingerprints {
|
||||||
|
discriminator = append(discriminator, f[:]...)
|
||||||
|
}
|
||||||
|
options = append(options, matcher)
|
||||||
|
options = append(options, discriminator)
|
||||||
|
}
|
||||||
if n.multicast, err = multicast.New(n.core, logger, options...); err != nil {
|
if n.multicast, err = multicast.New(n.core, logger, options...); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -7,22 +7,33 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type multicastAdvertisement struct {
|
type multicastAdvertisement struct {
|
||||||
PublicKey ed25519.PublicKey
|
MajorVersion uint16
|
||||||
Port uint16
|
MinorVersion uint16
|
||||||
|
PublicKey ed25519.PublicKey
|
||||||
|
Port uint16
|
||||||
|
Discriminator []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *multicastAdvertisement) MarshalBinary() ([]byte, error) {
|
func (m *multicastAdvertisement) MarshalBinary() ([]byte, error) {
|
||||||
b := make([]byte, 0, ed25519.PublicKeySize+2)
|
b := make([]byte, 0, ed25519.PublicKeySize+8+len(m.Discriminator))
|
||||||
|
b = binary.BigEndian.AppendUint16(b, m.MajorVersion)
|
||||||
|
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 = append(b, m.Discriminator...)
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *multicastAdvertisement) UnmarshalBinary(b []byte) error {
|
func (m *multicastAdvertisement) UnmarshalBinary(b []byte) error {
|
||||||
if len(b) < ed25519.PublicKeySize+2 {
|
if len(b) < ed25519.PublicKeySize+8 {
|
||||||
return fmt.Errorf("invalid multicast beacon")
|
return fmt.Errorf("invalid multicast beacon")
|
||||||
}
|
}
|
||||||
m.PublicKey = b[:ed25519.PublicKeySize]
|
m.MajorVersion = binary.BigEndian.Uint16(b[0:2])
|
||||||
m.Port = binary.BigEndian.Uint16(b[ed25519.PublicKeySize:])
|
m.MinorVersion = binary.BigEndian.Uint16(b[2:4])
|
||||||
|
m.PublicKey = append(m.PublicKey[:0], b[4:4+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])
|
||||||
|
m.Discriminator = append(m.Discriminator[:0], b[8+ed25519.PublicKeySize:8+ed25519.PublicKeySize+dl]...)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
38
src/multicast/advertisement_test.go
Normal file
38
src/multicast/advertisement_test.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package multicast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ed25519"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMulticastAdvertisementRoundTrip(t *testing.T) {
|
||||||
|
pk, sk, err := ed25519.GenerateKey(nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
orig := multicastAdvertisement{
|
||||||
|
MajorVersion: 1,
|
||||||
|
MinorVersion: 2,
|
||||||
|
PublicKey: pk,
|
||||||
|
Port: 3,
|
||||||
|
Discriminator: sk, // any bytes will do
|
||||||
|
}
|
||||||
|
|
||||||
|
ob, err := orig.MarshalBinary()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var new multicastAdvertisement
|
||||||
|
if err := new.UnmarshalBinary(ob); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(orig, new) {
|
||||||
|
t.Logf("original: %+v", orig)
|
||||||
|
t.Logf("new: %+v", new)
|
||||||
|
t.Fatalf("differences found after round-trip")
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package multicast
|
package multicast
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -30,8 +31,10 @@ type Multicast struct {
|
|||||||
_interfaces map[string]*interfaceInfo
|
_interfaces map[string]*interfaceInfo
|
||||||
_timer *time.Timer
|
_timer *time.Timer
|
||||||
config struct {
|
config struct {
|
||||||
_groupAddr GroupAddress
|
_discriminator []byte
|
||||||
_interfaces map[MulticastInterface]struct{}
|
_discriminatorMatch func([]byte) bool
|
||||||
|
_groupAddr GroupAddress
|
||||||
|
_interfaces map[MulticastInterface]struct{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,8 +324,11 @@ func (m *Multicast) _announce() {
|
|||||||
}
|
}
|
||||||
addr := linfo.listener.Addr().(*net.TCPAddr)
|
addr := linfo.listener.Addr().(*net.TCPAddr)
|
||||||
adv := multicastAdvertisement{
|
adv := multicastAdvertisement{
|
||||||
PublicKey: m.core.PublicKey(),
|
MajorVersion: core.ProtocolVersionMajor,
|
||||||
Port: uint16(addr.Port),
|
MinorVersion: core.ProtocolVersionMinor,
|
||||||
|
PublicKey: m.core.PublicKey(),
|
||||||
|
Port: uint16(addr.Port),
|
||||||
|
Discriminator: m.config._discriminator,
|
||||||
}
|
}
|
||||||
msg, err := adv.MarshalBinary()
|
msg, err := adv.MarshalBinary()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -373,7 +379,16 @@ func (m *Multicast) listen() {
|
|||||||
if err := adv.UnmarshalBinary(bs[:n]); err != nil {
|
if err := adv.UnmarshalBinary(bs[:n]); err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if adv.PublicKey.Equal(m.core.PublicKey()) {
|
switch {
|
||||||
|
case adv.MajorVersion != core.ProtocolVersionMajor:
|
||||||
|
continue
|
||||||
|
case adv.MinorVersion != core.ProtocolVersionMinor:
|
||||||
|
continue
|
||||||
|
case adv.PublicKey.Equal(m.core.PublicKey()):
|
||||||
|
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
|
continue
|
||||||
}
|
}
|
||||||
from := fromAddr.(*net.UDPAddr)
|
from := fromAddr.(*net.UDPAddr)
|
||||||
|
@ -8,6 +8,10 @@ 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,6 +28,10 @@ type MulticastInterface struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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() {}
|
||||||
|
Loading…
Reference in New Issue
Block a user