2017-12-29 04:16:20 +00:00
package yggdrasil
// Wire formatting tools
// These are all ugly and probably not very secure
2018-01-26 23:30:51 +00:00
// TODO clean up unused/commented code, and add better comments to whatever is left
2018-06-10 23:03:28 +00:00
// Packet types, as wire_encode_uint64(type) at the start of each packet
2018-12-15 02:49:18 +00:00
import (
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
"github.com/yggdrasil-network/yggdrasil-go/src/util"
)
2017-12-29 04:16:20 +00:00
const (
2018-01-04 22:37:51 +00:00
wire_Traffic = iota // data being routed somewhere, handle for crypto
wire_ProtocolTraffic // protocol traffic, pub keys for crypto
wire_LinkProtocolTraffic // link proto traffic, pub keys for crypto
2018-06-07 02:11:10 +00:00
wire_SwitchMsg // inside link protocol traffic header
2018-01-04 22:37:51 +00:00
wire_SessionPing // inside protocol traffic header
wire_SessionPong // inside protocol traffic header
wire_DHTLookupRequest // inside protocol traffic header
wire_DHTLookupResponse // inside protocol traffic header
2017-12-29 04:16:20 +00:00
)
2018-06-10 23:03:28 +00:00
// Calls wire_put_uint64 on a nil slice.
2017-12-29 04:16:20 +00:00
func wire_encode_uint64 ( elem uint64 ) [ ] byte {
2018-01-04 22:37:51 +00:00
return wire_put_uint64 ( elem , nil )
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Encode uint64 using a variable length scheme.
// Similar to binary.Uvarint, but big-endian.
2018-07-30 01:58:52 +00:00
func wire_put_uint64 ( e uint64 , out [ ] byte ) [ ] byte {
var b [ 10 ] byte
i := len ( b ) - 1
b [ i ] = byte ( e & 0x7f )
for e >>= 7 ; e != 0 ; e >>= 7 {
i --
b [ i ] = byte ( e | 0x80 )
2018-01-04 22:37:51 +00:00
}
2018-07-30 01:58:52 +00:00
return append ( out , b [ i : ] ... )
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Returns the length of a wire encoded uint64 of this value.
2018-01-13 13:26:26 +00:00
func wire_uint64_len ( elem uint64 ) int {
l := 1
for e := elem >> 7 ; e > 0 ; e >>= 7 {
l ++
}
return l
}
2018-06-10 23:03:28 +00:00
// Decode uint64 from a []byte slice.
// Returns the decoded uint64 and the number of bytes used.
2017-12-29 04:16:20 +00:00
func wire_decode_uint64 ( bs [ ] byte ) ( uint64 , int ) {
2018-01-04 22:37:51 +00:00
length := 0
elem := uint64 ( 0 )
for _ , b := range bs {
elem <<= 7
elem |= uint64 ( b & 0x7f )
length ++
if b & 0x80 == 0 {
break
}
}
return elem , length
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Converts an int64 into uint64 so it can be written to the wire.
// Non-negative integers are mapped to even integers: 0 -> 0, 1 -> 2, etc.
// Negative integres are mapped to odd integes: -1 -> 1, -2 -> 3, etc.
// This means the least significant bit is a sign bit.
2017-12-29 04:16:20 +00:00
func wire_intToUint ( i int64 ) uint64 {
2018-06-09 21:36:13 +00:00
return ( ( uint64 ( - ( i + 1 ) ) << 1 ) | 0x01 ) * ( uint64 ( i ) >> 63 ) + ( uint64 ( i ) << 1 ) * ( ^ uint64 ( i ) >> 63 )
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Converts uint64 back to int64, genreally when being read from the wire.
2017-12-29 04:16:20 +00:00
func wire_intFromUint ( u uint64 ) int64 {
2018-06-09 21:36:13 +00:00
return int64 ( u & 0x01 ) * ( - int64 ( u >> 1 ) - 1 ) + int64 ( ^ u & 0x01 ) * int64 ( u >> 1 )
2017-12-29 04:16:20 +00:00
}
////////////////////////////////////////////////////////////////////////////////
2018-06-10 23:03:28 +00:00
// Takes coords, returns coords prefixed with encoded coord length.
2018-01-04 22:37:51 +00:00
func wire_encode_coords ( coords [ ] byte ) [ ] byte {
coordLen := wire_encode_uint64 ( uint64 ( len ( coords ) ) )
bs := make ( [ ] byte , 0 , len ( coordLen ) + len ( coords ) )
bs = append ( bs , coordLen ... )
bs = append ( bs , coords ... )
return bs
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Puts a length prefix and the coords into bs, returns the wire formatted coords.
// Useful in hot loops where we don't want to allocate and we know the rest of the later parts of the slice are safe to overwrite.
2018-01-04 22:37:51 +00:00
func wire_put_coords ( coords [ ] byte , bs [ ] byte ) [ ] byte {
bs = wire_put_uint64 ( uint64 ( len ( coords ) ) , bs )
bs = append ( bs , coords ... )
return bs
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Takes a slice that begins with coords (starting with coord length).
// Returns a slice of coords and the number of bytes read.
// Used as part of various decode() functions for structs.
2017-12-29 04:16:20 +00:00
func wire_decode_coords ( packet [ ] byte ) ( [ ] byte , int ) {
2018-01-04 22:37:51 +00:00
coordLen , coordBegin := wire_decode_uint64 ( packet )
coordEnd := coordBegin + int ( coordLen )
if coordBegin == 0 || coordEnd > len ( packet ) {
return nil , 0
}
return packet [ coordBegin : coordEnd ] , coordEnd
2017-12-29 04:16:20 +00:00
}
////////////////////////////////////////////////////////////////////////////////
2018-06-10 23:03:28 +00:00
// Encodes a swtichMsg into its wire format.
2018-06-07 02:11:10 +00:00
func ( m * switchMsg ) encode ( ) [ ] byte {
bs := wire_encode_uint64 ( wire_SwitchMsg )
bs = append ( bs , m . Root [ : ] ... )
bs = append ( bs , wire_encode_uint64 ( wire_intToUint ( m . TStamp ) ) ... )
for _ , hop := range m . Hops {
bs = append ( bs , wire_encode_uint64 ( uint64 ( hop . Port ) ) ... )
bs = append ( bs , hop . Next [ : ] ... )
bs = append ( bs , hop . Sig [ : ] ... )
}
return bs
}
2018-06-10 23:03:28 +00:00
// Decodes a wire formatted switchMsg into the struct, returns true if successful.
2018-06-07 02:11:10 +00:00
func ( m * switchMsg ) decode ( bs [ ] byte ) bool {
var pType uint64
var tstamp uint64
switch {
case ! wire_chop_uint64 ( & pType , & bs ) :
return false
case pType != wire_SwitchMsg :
return false
case ! wire_chop_slice ( m . Root [ : ] , & bs ) :
return false
case ! wire_chop_uint64 ( & tstamp , & bs ) :
return false
}
m . TStamp = wire_intFromUint ( tstamp )
for len ( bs ) > 0 {
var hop switchMsgHop
switch {
case ! wire_chop_uint64 ( ( * uint64 ) ( & hop . Port ) , & bs ) :
return false
case ! wire_chop_slice ( hop . Next [ : ] , & bs ) :
return false
case ! wire_chop_slice ( hop . Sig [ : ] , & bs ) :
return false
}
m . Hops = append ( m . Hops , hop )
}
return true
}
////////////////////////////////////////////////////////////////////////////////
2018-06-10 23:03:28 +00:00
// A utility function used to copy bytes into a slice and advance the beginning of the source slice, returns true if successful.
2017-12-29 04:16:20 +00:00
func wire_chop_slice ( toSlice [ ] byte , fromSlice * [ ] byte ) bool {
2018-01-04 22:37:51 +00:00
if len ( * fromSlice ) < len ( toSlice ) {
return false
}
copy ( toSlice , * fromSlice )
* fromSlice = ( * fromSlice ) [ len ( toSlice ) : ]
return true
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// A utility function to extract coords from a slice and advance the source slices, returning true if successful.
2017-12-29 04:16:20 +00:00
func wire_chop_coords ( toCoords * [ ] byte , fromSlice * [ ] byte ) bool {
2018-01-04 22:37:51 +00:00
coords , coordLen := wire_decode_coords ( * fromSlice )
if coordLen == 0 {
return false
}
* toCoords = append ( ( * toCoords ) [ : 0 ] , coords ... )
* fromSlice = ( * fromSlice ) [ coordLen : ]
return true
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// A utility function to extract a wire encoded uint64 into the provided pointer while advancing the start of the source slice, returning true if successful.
2017-12-29 04:16:20 +00:00
func wire_chop_uint64 ( toUInt64 * uint64 , fromSlice * [ ] byte ) bool {
2018-01-04 22:37:51 +00:00
dec , decLen := wire_decode_uint64 ( * fromSlice )
if decLen == 0 {
return false
}
* toUInt64 = dec
* fromSlice = ( * fromSlice ) [ decLen : ]
return true
2017-12-29 04:16:20 +00:00
}
////////////////////////////////////////////////////////////////////////////////
// Wire traffic packets
2018-06-10 23:03:28 +00:00
// The wire format for ordinary IPv6 traffic encapsulated by the network.
2017-12-29 04:16:20 +00:00
type wire_trafficPacket struct {
2018-06-02 20:21:05 +00:00
Coords [ ] byte
2018-12-15 02:49:18 +00:00
Handle crypto . Handle
Nonce crypto . BoxNonce
2018-06-02 20:21:05 +00:00
Payload [ ] byte
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Encodes a wire_trafficPacket into its wire format.
2017-12-29 04:16:20 +00:00
func ( p * wire_trafficPacket ) encode ( ) [ ] byte {
2018-12-15 02:49:18 +00:00
bs := util . GetBytes ( )
2018-01-04 22:37:51 +00:00
bs = wire_put_uint64 ( wire_Traffic , bs )
2018-06-02 20:21:05 +00:00
bs = wire_put_coords ( p . Coords , bs )
bs = append ( bs , p . Handle [ : ] ... )
bs = append ( bs , p . Nonce [ : ] ... )
bs = append ( bs , p . Payload ... )
2018-01-04 22:37:51 +00:00
return bs
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Decodes an encoded wire_trafficPacket into the struct, returning true if successful.
2017-12-29 04:16:20 +00:00
func ( p * wire_trafficPacket ) decode ( bs [ ] byte ) bool {
2018-01-04 22:37:51 +00:00
var pType uint64
switch {
case ! wire_chop_uint64 ( & pType , & bs ) :
return false
case pType != wire_Traffic :
return false
2018-06-02 20:21:05 +00:00
case ! wire_chop_coords ( & p . Coords , & bs ) :
2018-01-04 22:37:51 +00:00
return false
2018-06-02 20:21:05 +00:00
case ! wire_chop_slice ( p . Handle [ : ] , & bs ) :
2018-01-04 22:37:51 +00:00
return false
2018-06-02 20:21:05 +00:00
case ! wire_chop_slice ( p . Nonce [ : ] , & bs ) :
2018-01-04 22:37:51 +00:00
return false
}
2018-12-15 02:49:18 +00:00
p . Payload = append ( util . GetBytes ( ) , bs ... )
2018-01-04 22:37:51 +00:00
return true
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// The wire format for protocol traffic, such as dht req/res or session ping/pong packets.
2017-12-29 04:16:20 +00:00
type wire_protoTrafficPacket struct {
2018-06-02 20:21:05 +00:00
Coords [ ] byte
2018-12-15 02:49:18 +00:00
ToKey crypto . BoxPubKey
FromKey crypto . BoxPubKey
Nonce crypto . BoxNonce
2018-06-02 20:21:05 +00:00
Payload [ ] byte
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Encodes a wire_protoTrafficPacket into its wire format.
2017-12-29 04:16:20 +00:00
func ( p * wire_protoTrafficPacket ) encode ( ) [ ] byte {
2018-06-02 20:21:05 +00:00
coords := wire_encode_coords ( p . Coords )
2018-01-04 22:37:51 +00:00
bs := wire_encode_uint64 ( wire_ProtocolTraffic )
bs = append ( bs , coords ... )
2018-06-02 20:21:05 +00:00
bs = append ( bs , p . ToKey [ : ] ... )
bs = append ( bs , p . FromKey [ : ] ... )
bs = append ( bs , p . Nonce [ : ] ... )
bs = append ( bs , p . Payload ... )
2018-01-04 22:37:51 +00:00
return bs
}
2018-06-10 23:03:28 +00:00
// Decodes an encoded wire_protoTrafficPacket into the struct, returning true if successful.
2018-01-04 22:37:51 +00:00
func ( p * wire_protoTrafficPacket ) decode ( bs [ ] byte ) bool {
var pType uint64
switch {
case ! wire_chop_uint64 ( & pType , & bs ) :
return false
case pType != wire_ProtocolTraffic :
return false
2018-06-02 20:21:05 +00:00
case ! wire_chop_coords ( & p . Coords , & bs ) :
2018-01-04 22:37:51 +00:00
return false
2018-06-02 20:21:05 +00:00
case ! wire_chop_slice ( p . ToKey [ : ] , & bs ) :
2018-01-04 22:37:51 +00:00
return false
2018-06-02 20:21:05 +00:00
case ! wire_chop_slice ( p . FromKey [ : ] , & bs ) :
2018-01-04 22:37:51 +00:00
return false
2018-06-02 20:21:05 +00:00
case ! wire_chop_slice ( p . Nonce [ : ] , & bs ) :
2018-01-04 22:37:51 +00:00
return false
}
2018-06-02 20:21:05 +00:00
p . Payload = bs
2018-01-04 22:37:51 +00:00
return true
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// The wire format for link protocol traffic, namely switchMsg.
// There's really two layers of this, with the outer layer using permanent keys, and the inner layer using ephemeral keys.
// The keys themselves are exchanged as part of the connection setup, and then omitted from the packets.
// The two layer logic is handled in peers.go, but it's kind of ugly.
2017-12-29 04:16:20 +00:00
type wire_linkProtoTrafficPacket struct {
2018-12-15 02:49:18 +00:00
Nonce crypto . BoxNonce
2018-06-02 20:21:05 +00:00
Payload [ ] byte
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Encodes a wire_linkProtoTrafficPacket into its wire format.
2017-12-29 04:16:20 +00:00
func ( p * wire_linkProtoTrafficPacket ) encode ( ) [ ] byte {
2018-01-04 22:37:51 +00:00
bs := wire_encode_uint64 ( wire_LinkProtocolTraffic )
2018-06-02 20:21:05 +00:00
bs = append ( bs , p . Nonce [ : ] ... )
bs = append ( bs , p . Payload ... )
2018-01-04 22:37:51 +00:00
return bs
}
2018-06-10 23:03:28 +00:00
// Decodes an encoded wire_linkProtoTrafficPacket into the struct, returning true if successful.
2018-01-04 22:37:51 +00:00
func ( p * wire_linkProtoTrafficPacket ) decode ( bs [ ] byte ) bool {
var pType uint64
switch {
case ! wire_chop_uint64 ( & pType , & bs ) :
return false
case pType != wire_LinkProtocolTraffic :
return false
2018-06-02 20:21:05 +00:00
case ! wire_chop_slice ( p . Nonce [ : ] , & bs ) :
2018-01-04 22:37:51 +00:00
return false
}
2018-06-02 20:21:05 +00:00
p . Payload = bs
2018-01-04 22:37:51 +00:00
return true
2017-12-29 04:16:20 +00:00
}
////////////////////////////////////////////////////////////////////////////////
2018-06-10 23:03:28 +00:00
// Encodes a sessionPing into its wire format.
2017-12-29 04:16:20 +00:00
func ( p * sessionPing ) encode ( ) [ ] byte {
2018-01-04 22:37:51 +00:00
var pTypeVal uint64
2018-06-02 21:19:42 +00:00
if p . IsPong {
2018-01-04 22:37:51 +00:00
pTypeVal = wire_SessionPong
} else {
pTypeVal = wire_SessionPing
}
bs := wire_encode_uint64 ( pTypeVal )
//p.sendPermPub used in top level (crypto), so skipped here
2018-06-02 21:19:42 +00:00
bs = append ( bs , p . Handle [ : ] ... )
bs = append ( bs , p . SendSesPub [ : ] ... )
bs = append ( bs , wire_encode_uint64 ( wire_intToUint ( p . Tstamp ) ) ... )
coords := wire_encode_coords ( p . Coords )
2018-01-04 22:37:51 +00:00
bs = append ( bs , coords ... )
2018-06-02 21:19:42 +00:00
bs = append ( bs , wire_encode_uint64 ( uint64 ( p . MTU ) ) ... )
2018-01-04 22:37:51 +00:00
return bs
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Decodes an encoded sessionPing into the struct, returning true if successful.
2017-12-29 04:16:20 +00:00
func ( p * sessionPing ) decode ( bs [ ] byte ) bool {
2018-01-04 22:37:51 +00:00
var pType uint64
var tstamp uint64
2018-02-11 23:58:30 +00:00
var mtu uint64
2018-01-04 22:37:51 +00:00
switch {
case ! wire_chop_uint64 ( & pType , & bs ) :
return false
case pType != wire_SessionPing && pType != wire_SessionPong :
return false
2018-06-02 21:30:05 +00:00
//p.sendPermPub used in top level (crypto), so skipped here
case ! wire_chop_slice ( p . Handle [ : ] , & bs ) :
2018-01-04 22:37:51 +00:00
return false
2018-06-02 21:19:42 +00:00
case ! wire_chop_slice ( p . SendSesPub [ : ] , & bs ) :
2018-01-04 22:37:51 +00:00
return false
case ! wire_chop_uint64 ( & tstamp , & bs ) :
return false
2018-06-02 21:19:42 +00:00
case ! wire_chop_coords ( & p . Coords , & bs ) :
2018-01-04 22:37:51 +00:00
return false
2018-02-11 23:58:30 +00:00
case ! wire_chop_uint64 ( & mtu , & bs ) :
mtu = 1280
2018-01-04 22:37:51 +00:00
}
2018-06-02 21:19:42 +00:00
p . Tstamp = wire_intFromUint ( tstamp )
2018-01-04 22:37:51 +00:00
if pType == wire_SessionPong {
2018-06-02 21:19:42 +00:00
p . IsPong = true
2018-01-04 22:37:51 +00:00
}
2018-06-02 21:19:42 +00:00
p . MTU = uint16 ( mtu )
2018-01-04 22:37:51 +00:00
return true
2017-12-29 04:16:20 +00:00
}
////////////////////////////////////////////////////////////////////////////////
2018-06-10 23:03:28 +00:00
// Encodes a dhtReq into its wire format.
2017-12-29 04:16:20 +00:00
func ( r * dhtReq ) encode ( ) [ ] byte {
2018-06-02 21:19:42 +00:00
coords := wire_encode_coords ( r . Coords )
2018-01-04 22:37:51 +00:00
bs := wire_encode_uint64 ( wire_DHTLookupRequest )
bs = append ( bs , coords ... )
2018-06-02 21:19:42 +00:00
bs = append ( bs , r . Dest [ : ] ... )
2018-01-04 22:37:51 +00:00
return bs
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Decodes an encoded dhtReq into the struct, returning true if successful.
2017-12-29 04:16:20 +00:00
func ( r * dhtReq ) decode ( bs [ ] byte ) bool {
2018-01-04 22:37:51 +00:00
var pType uint64
switch {
case ! wire_chop_uint64 ( & pType , & bs ) :
return false
case pType != wire_DHTLookupRequest :
return false
2018-06-02 21:19:42 +00:00
case ! wire_chop_coords ( & r . Coords , & bs ) :
2018-01-04 22:37:51 +00:00
return false
2018-06-02 21:19:42 +00:00
case ! wire_chop_slice ( r . Dest [ : ] , & bs ) :
2018-01-04 22:37:51 +00:00
return false
default :
return true
}
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Encodes a dhtRes into its wire format.
2017-12-29 04:16:20 +00:00
func ( r * dhtRes ) encode ( ) [ ] byte {
2018-06-02 21:19:42 +00:00
coords := wire_encode_coords ( r . Coords )
2018-01-04 22:37:51 +00:00
bs := wire_encode_uint64 ( wire_DHTLookupResponse )
bs = append ( bs , coords ... )
2018-06-02 21:19:42 +00:00
bs = append ( bs , r . Dest [ : ] ... )
for _ , info := range r . Infos {
2018-01-04 22:37:51 +00:00
coords = wire_encode_coords ( info . coords )
bs = append ( bs , info . key [ : ] ... )
bs = append ( bs , coords ... )
}
return bs
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Decodes an encoded dhtRes into the struct, returning true if successful.
2017-12-29 04:16:20 +00:00
func ( r * dhtRes ) decode ( bs [ ] byte ) bool {
2018-01-04 22:37:51 +00:00
var pType uint64
switch {
case ! wire_chop_uint64 ( & pType , & bs ) :
return false
case pType != wire_DHTLookupResponse :
return false
2018-06-02 21:19:42 +00:00
case ! wire_chop_coords ( & r . Coords , & bs ) :
2018-01-04 22:37:51 +00:00
return false
2018-06-02 21:19:42 +00:00
case ! wire_chop_slice ( r . Dest [ : ] , & bs ) :
2018-01-04 22:37:51 +00:00
return false
}
for len ( bs ) > 0 {
info := dhtInfo { }
switch {
case ! wire_chop_slice ( info . key [ : ] , & bs ) :
return false
case ! wire_chop_coords ( & info . coords , & bs ) :
return false
}
2018-06-02 21:19:42 +00:00
r . Infos = append ( r . Infos , & info )
2018-01-04 22:37:51 +00:00
}
return true
2017-12-29 04:16:20 +00:00
}