New handshake, use softcrdt upstream

This commit is contained in:
Neil Alexander 2023-03-18 12:14:32 +00:00
parent 1420ea5662
commit 83c1a810b5
No known key found for this signature in database
GPG Key ID: A02A2019A2BB0944
6 changed files with 123 additions and 47 deletions

2
go.mod
View File

@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go
go 1.17 go 1.17
require ( require (
github.com/Arceliar/ironwood v0.0.0-20221115123222-ec61cea2f439 github.com/Arceliar/ironwood v0.0.0-20230318003210-65aa386cab13
github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979
github.com/cheggaaa/pb/v3 v3.0.8 github.com/cheggaaa/pb/v3 v3.0.8
github.com/gologme/log v1.2.0 github.com/gologme/log v1.2.0

4
go.sum
View File

@ -1,5 +1,5 @@
github.com/Arceliar/ironwood v0.0.0-20221115123222-ec61cea2f439 h1:eOW6/XIs06TnUn9GPCnfv71CQZw8edP3u3mH3lZt6iM= github.com/Arceliar/ironwood v0.0.0-20230318003210-65aa386cab13 h1:z0PVz7aDDW5c+JVEW7b00N2JMGAfV6BHtTcOJ8zHKcU=
github.com/Arceliar/ironwood v0.0.0-20221115123222-ec61cea2f439/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= github.com/Arceliar/ironwood v0.0.0-20230318003210-65aa386cab13/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk=
github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3ugUxx2EnnWmgba1kCqPkd4Gk1yQ= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3ugUxx2EnnWmgba1kCqPkd4Gk1yQ=
github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=

View File

@ -98,7 +98,7 @@ func (c *Core) GetDHT() []DHTEntryInfo {
var info DHTEntryInfo var info DHTEntryInfo
info.Key = d.Key info.Key = d.Key
info.Port = d.Port info.Port = d.Port
info.Rest = d.Rest //info.Rest = d.Rest
dhts = append(dhts, info) dhts = append(dhts, info)
} }
return dhts return dhts
@ -110,7 +110,7 @@ func (c *Core) GetPaths() []PathEntryInfo {
for _, p := range ps { for _, p := range ps {
var info PathEntryInfo var info PathEntryInfo
info.Key = p.Key info.Key = p.Key
info.Path = p.Path //info.Path = p.Path
paths = append(paths, info) paths = append(paths, info)
} }
return paths return paths

View File

@ -278,7 +278,7 @@ func (intf *link) handler(dial *linkDial) error {
}) })
meta := version_getBaseMetadata() meta := version_getBaseMetadata()
meta.key = intf.links.core.public meta.publicKey = intf.links.core.public
metaBytes := meta.encode() metaBytes := meta.encode()
if err := intf.conn.SetDeadline(time.Now().Add(time.Second * 6)); err != nil { if err := intf.conn.SetDeadline(time.Now().Add(time.Second * 6)); err != nil {
return fmt.Errorf("failed to set handshake deadline: %w", err) return fmt.Errorf("failed to set handshake deadline: %w", err)
@ -311,8 +311,8 @@ func (intf *link) handler(dial *linkDial) error {
intf.links.core.log.Debugf("%s: %s is incompatible version (local %s, remote %s)", intf.links.core.log.Debugf("%s: %s is incompatible version (local %s, remote %s)",
connectError, connectError,
intf.lname, intf.lname,
fmt.Sprintf("%d.%d", base.ver, base.minorVer), fmt.Sprintf("%d.%d", base.majorVer, base.minorVer),
fmt.Sprintf("%d.%d", meta.ver, meta.minorVer), fmt.Sprintf("%d.%d", meta.majorVer, meta.minorVer),
) )
return errors.New("remote node is incompatible version") return errors.New("remote node is incompatible version")
} }
@ -320,7 +320,7 @@ func (intf *link) handler(dial *linkDial) error {
// check - in future versions we really should check a signature or something like that. // check - in future versions we really should check a signature or something like that.
if pinned := intf.options.pinnedEd25519Keys; len(pinned) > 0 { if pinned := intf.options.pinnedEd25519Keys; len(pinned) > 0 {
var key keyArray var key keyArray
copy(key[:], meta.key) copy(key[:], meta.publicKey)
if _, allowed := pinned[key]; !allowed { if _, allowed := pinned[key]; !allowed {
return fmt.Errorf("node public key that does not match pinned keys") return fmt.Errorf("node public key that does not match pinned keys")
} }
@ -329,14 +329,14 @@ func (intf *link) handler(dial *linkDial) error {
allowed := intf.links.core.config._allowedPublicKeys allowed := intf.links.core.config._allowedPublicKeys
isallowed := len(allowed) == 0 isallowed := len(allowed) == 0
for k := range allowed { for k := range allowed {
if bytes.Equal(k[:], meta.key) { if bytes.Equal(k[:], meta.publicKey) {
isallowed = true isallowed = true
break break
} }
} }
if intf.incoming && !intf.force && !isallowed { if intf.incoming && !intf.force && !isallowed {
_ = intf.close() _ = intf.close()
return fmt.Errorf("node public key %q is not in AllowedPublicKeys", hex.EncodeToString(meta.key)) return fmt.Errorf("node public key %q is not in AllowedPublicKeys", hex.EncodeToString(meta.publicKey))
} }
phony.Block(intf.links, func() { phony.Block(intf.links, func() {
@ -347,13 +347,13 @@ func (intf *link) handler(dial *linkDial) error {
if intf.incoming { if intf.incoming {
dir = "inbound" dir = "inbound"
} }
remoteAddr := net.IP(address.AddrForKey(meta.key)[:]).String() remoteAddr := net.IP(address.AddrForKey(meta.publicKey)[:]).String()
remoteStr := fmt.Sprintf("%s@%s", remoteAddr, intf.info.remote) remoteStr := fmt.Sprintf("%s@%s", remoteAddr, intf.info.remote)
localStr := intf.conn.LocalAddr() localStr := intf.conn.LocalAddr()
intf.links.core.log.Infof("Connected %s %s: %s, source %s", intf.links.core.log.Infof("Connected %s %s: %s, source %s",
dir, strings.ToUpper(intf.info.linkType), remoteStr, localStr) dir, strings.ToUpper(intf.info.linkType), remoteStr, localStr)
err = intf.links.core.HandleConn(meta.key, intf.conn, intf.options.priority) err = intf.links.core.HandleConn(meta.publicKey, intf.conn, intf.options.priority)
switch err { switch err {
case io.EOF, net.ErrClosed, nil: case io.EOF, net.ErrClosed, nil:
intf.links.core.log.Infof("Disconnected %s %s: %s, source %s", intf.links.core.log.Infof("Disconnected %s %s: %s, source %s",

View File

@ -4,65 +4,107 @@ package core
// Used in the initial connection setup and key exchange // Used in the initial connection setup and key exchange
// Some of this could arguably go in wire.go instead // Some of this could arguably go in wire.go instead
import "crypto/ed25519" import (
"bytes"
"crypto/ed25519"
"encoding/binary"
)
// This is the version-specific metadata exchanged at the start of a connection. // This is the version-specific metadata exchanged at the start of a connection.
// It must always begin with the 4 bytes "meta" and a wire formatted uint64 major version number. // It must always begin with the 4 bytes "meta" and a wire formatted uint64 major version number.
// The current version also includes a minor version number, and the box/sig/link keys that need to be exchanged to open a connection. // The current version also includes a minor version number, and the box/sig/link keys that need to be exchanged to open a connection.
type version_metadata struct { type version_metadata struct {
meta [4]byte majorVer uint16
ver uint8 // 1 byte in this version minorVer uint16
// Everything after this point potentially depends on the version number, and is subject to change in future versions publicKey ed25519.PublicKey
minorVer uint8 // 1 byte in this version priority uint8
key ed25519.PublicKey
} }
const (
ProtocolVersionMajor uint16 = 0
ProtocolVersionMinor uint16 = 5
)
const (
metaVersionMajor uint16 = iota // uint16
metaVersionMinor // uint16
metaPublicKey // [32]byte
metaPriority // uint8
)
// Gets a base metadata with no keys set, but with the correct version numbers. // Gets a base metadata with no keys set, but with the correct version numbers.
func version_getBaseMetadata() version_metadata { func version_getBaseMetadata() version_metadata {
return version_metadata{ return version_metadata{
meta: [4]byte{'m', 'e', 't', 'a'}, majorVer: ProtocolVersionMajor,
ver: 0, minorVer: ProtocolVersionMinor,
minorVer: 4,
} }
} }
// Gets the length of the metadata for this version, used to know how many bytes to read from the start of a connection.
func version_getMetaLength() (mlen int) {
mlen += 4 // meta
mlen++ // ver, as long as it's < 127, which it is in this version
mlen++ // minorVer, as long as it's < 127, which it is in this version
mlen += ed25519.PublicKeySize // key
return
}
// Encodes version metadata into its wire format. // Encodes version metadata into its wire format.
func (m *version_metadata) encode() []byte { func (m *version_metadata) encode() []byte {
bs := make([]byte, 0, version_getMetaLength()) bs := make([]byte, 0, 64)
bs = append(bs, m.meta[:]...) bs = append(bs, 'm', 'e', 't', 'a')
bs = append(bs, m.ver)
bs = append(bs, m.minorVer) bs = binary.BigEndian.AppendUint16(bs, metaVersionMajor)
bs = append(bs, m.key[:]...) bs = binary.BigEndian.AppendUint16(bs, 2)
if len(bs) != version_getMetaLength() { bs = binary.BigEndian.AppendUint16(bs, m.majorVer)
panic("Inconsistent metadata length")
} bs = binary.BigEndian.AppendUint16(bs, metaVersionMinor)
bs = binary.BigEndian.AppendUint16(bs, 2)
bs = binary.BigEndian.AppendUint16(bs, m.minorVer)
bs = binary.BigEndian.AppendUint16(bs, metaPublicKey)
bs = binary.BigEndian.AppendUint16(bs, ed25519.PublicKeySize)
bs = append(bs, m.publicKey[:]...)
bs = binary.BigEndian.AppendUint16(bs, metaPriority)
bs = binary.BigEndian.AppendUint16(bs, 1)
bs = append(bs, m.priority)
return bs return bs
} }
// Decodes version metadata from its wire format into the struct. // Decodes version metadata from its wire format into the struct.
func (m *version_metadata) decode(bs []byte) bool { func (m *version_metadata) decode(bs []byte) bool {
if len(bs) != version_getMetaLength() { meta := [4]byte{'m', 'e', 't', 'a'}
if !bytes.Equal(bs[:4], meta[:]) {
return false return false
} }
offset := 0 for bs = bs[4:]; len(bs) >= 4; {
offset += copy(m.meta[:], bs[offset:]) op := binary.BigEndian.Uint16(bs[:2])
m.ver, offset = bs[offset], offset+1 oplen := binary.BigEndian.Uint16(bs[2:4])
m.minorVer, offset = bs[offset], offset+1 if bs = bs[4:]; len(bs) < int(oplen) {
m.key = append([]byte(nil), bs[offset:]...) break
}
switch op {
case metaVersionMajor:
m.majorVer = binary.BigEndian.Uint16(bs[:2])
case metaVersionMinor:
m.minorVer = binary.BigEndian.Uint16(bs[:2])
case metaPublicKey:
m.publicKey = make(ed25519.PublicKey, ed25519.PublicKeySize)
copy(m.publicKey, bs[:ed25519.PublicKeySize])
case metaPriority:
m.priority = bs[0]
}
bs = bs[oplen:]
}
return true return true
} }
// Checks that the "meta" bytes and the version numbers are the expected values. // Checks that the "meta" bytes and the version numbers are the expected values.
func (m *version_metadata) check() bool { func (m *version_metadata) check() bool {
base := version_getBaseMetadata() switch {
return base.meta == m.meta && base.ver == m.ver && base.minorVer == m.minorVer case m.majorVer != ProtocolVersionMajor:
return false
case m.minorVer != ProtocolVersionMinor:
return false
case len(m.publicKey) != ed25519.PublicKeySize:
return false
default:
return true
}
} }

34
src/core/version_test.go Normal file
View File

@ -0,0 +1,34 @@
package core
import (
"crypto/ed25519"
"math/rand"
"reflect"
"testing"
)
func TestVersionRoundtrip(t *testing.T) {
for _, test := range []*version_metadata{
{majorVer: 1},
{majorVer: 256},
{majorVer: 2, minorVer: 4},
{majorVer: 2, minorVer: 257},
{majorVer: 258, minorVer: 259},
{majorVer: 3, minorVer: 5, priority: 6},
{majorVer: 260, minorVer: 261, priority: 7},
} {
// Generate a random public key for each time, since it is
// a required field.
test.publicKey = make(ed25519.PublicKey, ed25519.PublicKeySize)
rand.Read(test.publicKey)
encoded := test.encode()
decoded := &version_metadata{}
if !decoded.decode(encoded) {
t.Fatalf("failed to decode")
}
if !reflect.DeepEqual(test, decoded) {
t.Fatalf("round-trip failed\nwant: %+v\n got: %+v", test, decoded)
}
}
}