diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index c5f7927b..e325fcfe 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -10,6 +10,10 @@ package yggdrasil // Could be used to DoS (connect, give someone else's keys, spew garbage) // I guess the "peer" part should watch for link packets, disconnect? +// TCP connections start with a metadata exchange. +// It involves exchanging version numbers and crypto keys +// See version.go for version metadata format + import "net" import "time" import "errors" @@ -142,28 +146,39 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { defer sock.Close() // Get our keys myLinkPub, myLinkPriv := newBoxKeys() // ephemeral link keys - keys := []byte{} - keys = append(keys, tcp_key[:]...) - keys = append(keys, iface.core.boxPub[:]...) - keys = append(keys, iface.core.sigPub[:]...) - keys = append(keys, myLinkPub[:]...) - _, err := sock.Write(keys) + meta := version_getBaseMetadata() + meta.box = iface.core.boxPub + meta.sig = iface.core.sigPub + meta.link = *myLinkPub + metaBytes := meta.encode() + _, err := sock.Write(metaBytes) if err != nil { return } timeout := time.Now().Add(6 * time.Second) sock.SetReadDeadline(timeout) - n, err := sock.Read(keys) + _, err = sock.Read(metaBytes) if err != nil { return } - if n < len(keys) { /*panic("Partial key packet?") ;*/ + meta = version_metadata{} // Reset to zero value + if !meta.decode(metaBytes) || !meta.check() { + // Failed to decode and check the metadata + // If it's a version mismatch issue, then print an error message + base := version_getBaseMetadata() + if meta.meta == base.meta { + if meta.ver > base.ver { + iface.core.log.Println("Failed to connect to node:", sock.RemoteAddr().String(), "version:", meta.ver) + } else if meta.ver == base.ver && meta.minorVer > base.minorVer { + iface.core.log.Println("Failed to connect to node:", sock.RemoteAddr().String(), "version:", fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) + } + } + // TODO? Block forever to prevent future connection attempts? suppress future messages about the same node? return } - info := tcpInfo{} // used as a map key, so don't include ephemeral link eys - var theirLinkPub boxPubKey - if !tcp_chop_keys(&info.box, &info.sig, &theirLinkPub, &keys) { /*panic("Invalid key packet?") ;*/ - return + info := tcpInfo{ // used as a map key, so don't include ephemeral link key + box: meta.box, + sig: meta.sig, } // Quit the parent call if this is a connection to ourself equiv := func(k1, k2 []byte) bool { @@ -210,7 +225,7 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { }() // Note that multiple connections to the same node are allowed // E.g. over different interfaces - p := iface.core.peers.newPeer(&info.box, &info.sig, getSharedKey(myLinkPriv, &theirLinkPub)) + p := iface.core.peers.newPeer(&info.box, &info.sig, getSharedKey(myLinkPriv, &meta.link)) p.linkOut = make(chan []byte, 1) in := func(bs []byte) { p.handlePacket(bs) @@ -336,30 +351,8 @@ func (iface *tcpInterface) reader(sock net.Conn, in func([]byte)) { //////////////////////////////////////////////////////////////////////////////// // Magic bytes to check -var tcp_key = [...]byte{'k', 'e', 'y', 's'} var tcp_msg = [...]byte{0xde, 0xad, 0xb1, 0x75} // "dead bits" -func tcp_chop_keys(box *boxPubKey, sig *sigPubKey, link *boxPubKey, bs *[]byte) bool { - // This one is pretty simple: we know how long the message should be - // So don't call this with a message that's too short - if len(*bs) < len(tcp_key)+2*len(*box)+len(*sig) { - return false - } - for idx := range tcp_key { - if (*bs)[idx] != tcp_key[idx] { - return false - } - } - (*bs) = (*bs)[len(tcp_key):] - copy(box[:], *bs) - (*bs) = (*bs)[len(box):] - copy(sig[:], *bs) - (*bs) = (*bs)[len(sig):] - copy(link[:], *bs) - (*bs) = (*bs)[len(sig):] - return true -} - func tcp_chop_msg(bs *[]byte) ([]byte, bool, error) { // Returns msg, ok, err if len(*bs) < len(tcp_msg) { diff --git a/src/yggdrasil/version.go b/src/yggdrasil/version.go new file mode 100644 index 00000000..40a5b965 --- /dev/null +++ b/src/yggdrasil/version.go @@ -0,0 +1,70 @@ +package yggdrasil + +// This file contains the version metadata struct +// Used in the inital connection setup and key exchange +// Some of this could arguably go in wire.go instead + +type version_metadata struct { + meta [4]byte + ver uint64 // 1 byte in this version + // Everything after this point potentially depends on the version number, and is subject to change in future versions + minorVer uint64 // 1 byte in this version + box boxPubKey + sig sigPubKey + link boxPubKey +} + +func version_getBaseMetadata() version_metadata { + return version_metadata{ + meta: [4]byte{'m', 'e', 't', 'a'}, + ver: 0, + minorVer: 2, + } +} + +func version_getMetaLength() (mlen int) { + mlen += 4 // meta + mlen += 1 // ver + mlen += 1 // minorVer + mlen += boxPubKeyLen // box + mlen += sigPubKeyLen // sig + mlen += boxPubKeyLen // link + return +} + +func (m *version_metadata) encode() []byte { + bs := make([]byte, 0, version_getMetaLength()) + bs = append(bs, m.meta[:]...) + bs = append(bs, wire_encode_uint64(m.ver)...) + bs = append(bs, wire_encode_uint64(m.minorVer)...) + bs = append(bs, m.box[:]...) + bs = append(bs, m.sig[:]...) + bs = append(bs, m.link[:]...) + if len(bs) != version_getMetaLength() { + panic("Inconsistent metadata length") + } + return bs +} + +func (m *version_metadata) decode(bs []byte) bool { + switch { + case !wire_chop_slice(m.meta[:], &bs): + return false + case !wire_chop_uint64(&m.ver, &bs): + return false + case !wire_chop_uint64(&m.minorVer, &bs): + return false + case !wire_chop_slice(m.box[:], &bs): + return false + case !wire_chop_slice(m.sig[:], &bs): + return false + case !wire_chop_slice(m.link[:], &bs): + return false + } + return true +} + +func (m *version_metadata) check() bool { + base := version_getBaseMetadata() + return base.meta == m.meta && base.ver == m.ver && base.minorVer == m.minorVer +} diff --git a/src/yggdrasil/wire.go b/src/yggdrasil/wire.go index 3b43143b..23e9ab3d 100644 --- a/src/yggdrasil/wire.go +++ b/src/yggdrasil/wire.go @@ -66,23 +66,14 @@ func wire_decode_uint64(bs []byte) (uint64, int) { } func wire_intToUint(i int64) uint64 { - var u uint64 - if i < 0 { - u = uint64(-i) << 1 - u |= 0x01 // sign bit - } else { - u = uint64(i) << 1 - } - return u + // Non-negative integers mapped to even integers: 0 -> 0, 1 -> 2, etc. + // Negative integres mapped to odd integes: -1 -> 1, -2 -> 3, etc. + // This means the least significant bit is a sign bit. + return ((uint64(-(i+1))<<1)|0x01)*(uint64(i)>>63) + (uint64(i)<<1)*(^uint64(i)>>63) } func wire_intFromUint(u uint64) int64 { - var i int64 - i = int64(u >> 1) - if u&0x01 != 0 { - i *= -1 - } - return i + return int64(u&0x01)*(-int64(u>>1)-1) + int64(^u&0x01)*int64(u>>1) } ////////////////////////////////////////////////////////////////////////////////