mirror of
https://github.com/cwinfo/yggdrasil-go.git
synced 2024-12-22 23:25: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
|
||||
|
||||
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/cheggaaa/pb/v3 v3.0.8
|
||||
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-20221115123222-ec61cea2f439/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk=
|
||||
github.com/Arceliar/ironwood v0.0.0-20230318003210-65aa386cab13 h1:z0PVz7aDDW5c+JVEW7b00N2JMGAfV6BHtTcOJ8zHKcU=
|
||||
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/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI=
|
||||
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
|
||||
info.Key = d.Key
|
||||
info.Port = d.Port
|
||||
info.Rest = d.Rest
|
||||
//info.Rest = d.Rest
|
||||
dhts = append(dhts, info)
|
||||
}
|
||||
return dhts
|
||||
@ -110,7 +110,7 @@ func (c *Core) GetPaths() []PathEntryInfo {
|
||||
for _, p := range ps {
|
||||
var info PathEntryInfo
|
||||
info.Key = p.Key
|
||||
info.Path = p.Path
|
||||
//info.Path = p.Path
|
||||
paths = append(paths, info)
|
||||
}
|
||||
return paths
|
||||
|
@ -278,7 +278,7 @@ func (intf *link) handler(dial *linkDial) error {
|
||||
})
|
||||
|
||||
meta := version_getBaseMetadata()
|
||||
meta.key = intf.links.core.public
|
||||
meta.publicKey = intf.links.core.public
|
||||
metaBytes := meta.encode()
|
||||
if err := intf.conn.SetDeadline(time.Now().Add(time.Second * 6)); err != nil {
|
||||
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)",
|
||||
connectError,
|
||||
intf.lname,
|
||||
fmt.Sprintf("%d.%d", base.ver, base.minorVer),
|
||||
fmt.Sprintf("%d.%d", meta.ver, meta.minorVer),
|
||||
fmt.Sprintf("%d.%d", base.majorVer, base.minorVer),
|
||||
fmt.Sprintf("%d.%d", meta.majorVer, meta.minorVer),
|
||||
)
|
||||
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.
|
||||
if pinned := intf.options.pinnedEd25519Keys; len(pinned) > 0 {
|
||||
var key keyArray
|
||||
copy(key[:], meta.key)
|
||||
copy(key[:], meta.publicKey)
|
||||
if _, allowed := pinned[key]; !allowed {
|
||||
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
|
||||
isallowed := len(allowed) == 0
|
||||
for k := range allowed {
|
||||
if bytes.Equal(k[:], meta.key) {
|
||||
if bytes.Equal(k[:], meta.publicKey) {
|
||||
isallowed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if intf.incoming && !intf.force && !isallowed {
|
||||
_ = 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() {
|
||||
@ -347,13 +347,13 @@ func (intf *link) handler(dial *linkDial) error {
|
||||
if intf.incoming {
|
||||
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)
|
||||
localStr := intf.conn.LocalAddr()
|
||||
intf.links.core.log.Infof("Connected %s %s: %s, source %s",
|
||||
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 {
|
||||
case io.EOF, net.ErrClosed, nil:
|
||||
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
|
||||
// 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.
|
||||
// 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.
|
||||
type version_metadata struct {
|
||||
meta [4]byte
|
||||
ver uint8 // 1 byte in this version
|
||||
// Everything after this point potentially depends on the version number, and is subject to change in future versions
|
||||
minorVer uint8 // 1 byte in this version
|
||||
key ed25519.PublicKey
|
||||
majorVer uint16
|
||||
minorVer uint16
|
||||
publicKey ed25519.PublicKey
|
||||
priority uint8
|
||||
}
|
||||
|
||||
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.
|
||||
func version_getBaseMetadata() version_metadata {
|
||||
return version_metadata{
|
||||
meta: [4]byte{'m', 'e', 't', 'a'},
|
||||
ver: 0,
|
||||
minorVer: 4,
|
||||
majorVer: ProtocolVersionMajor,
|
||||
minorVer: ProtocolVersionMinor,
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (m *version_metadata) encode() []byte {
|
||||
bs := make([]byte, 0, version_getMetaLength())
|
||||
bs = append(bs, m.meta[:]...)
|
||||
bs = append(bs, m.ver)
|
||||
bs = append(bs, m.minorVer)
|
||||
bs = append(bs, m.key[:]...)
|
||||
if len(bs) != version_getMetaLength() {
|
||||
panic("Inconsistent metadata length")
|
||||
}
|
||||
bs := make([]byte, 0, 64)
|
||||
bs = append(bs, 'm', 'e', 't', 'a')
|
||||
|
||||
bs = binary.BigEndian.AppendUint16(bs, metaVersionMajor)
|
||||
bs = binary.BigEndian.AppendUint16(bs, 2)
|
||||
bs = binary.BigEndian.AppendUint16(bs, m.majorVer)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// Decodes version metadata from its wire format into the struct.
|
||||
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
|
||||
}
|
||||
offset := 0
|
||||
offset += copy(m.meta[:], bs[offset:])
|
||||
m.ver, offset = bs[offset], offset+1
|
||||
m.minorVer, offset = bs[offset], offset+1
|
||||
m.key = append([]byte(nil), bs[offset:]...)
|
||||
for bs = bs[4:]; len(bs) >= 4; {
|
||||
op := binary.BigEndian.Uint16(bs[:2])
|
||||
oplen := binary.BigEndian.Uint16(bs[2:4])
|
||||
if bs = bs[4:]; len(bs) < int(oplen) {
|
||||
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
|
||||
}
|
||||
|
||||
// Checks that the "meta" bytes and the version numbers are the expected values.
|
||||
func (m *version_metadata) check() bool {
|
||||
base := version_getBaseMetadata()
|
||||
return base.meta == m.meta && base.ver == m.ver && base.minorVer == m.minorVer
|
||||
switch {
|
||||
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