mirror of
https://github.com/cwinfo/yggdrasil-go.git
synced 2024-12-23 02:55:39 +00:00
New handshake, use softcrdt
upstream
This commit is contained in:
parent
1420ea5662
commit
83c1a810b5
2
go.mod
2
go.mod
@ -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
4
go.sum
@ -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=
|
||||||
|
@ -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
|
||||||
|
@ -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",
|
||||||
|
@ -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
34
src/core/version_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user