package yggdrasil // Wire formatting tools // These are all ugly and probably not very secure // Packet types, as an Encode_uint64 at the start of each packet // TODO? make things still work after reordering (after things stabilize more?) // Type safety would also be nice, `type wire_type uint64`, rewrite as needed? const ( 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 wire_SwitchAnnounce // TODO put inside protocol traffic header wire_SwitchHopRequest // TODO put inside protocol traffic header wire_SwitchHop // TODO put inside protocol traffic header wire_SessionPing // inside protocol traffic header wire_SessionPong // inside protocol traffic header wire_DHTLookupRequest // inside protocol traffic header wire_DHTLookupResponse // inside protocol traffic header wire_SearchRequest // inside protocol traffic header wire_SearchResponse // inside protocol traffic header //wire_Keys // udp key packet (boxPub, sigPub) ) // Encode uint64 using a variable length scheme // Similar to binary.Uvarint, but big-endian func wire_encode_uint64(elem uint64) []byte { return wire_put_uint64(elem, nil) } // Occasionally useful for appending to an existing slice (if there's room) func wire_put_uint64(elem uint64, out []byte) []byte { bs := make([]byte, 0, 10) bs = append(bs, byte(elem&0x7f)) for e := elem >> 7; e > 0; e >>= 7 { bs = append(bs, byte(e|0x80)) } // Now reverse bytes, because we set them in the wrong order // TODO just put them in the right place the first time... last := len(bs) - 1 for idx := 0; idx < len(bs)/2; idx++ { bs[idx], bs[last-idx] = bs[last-idx], bs[idx] } return append(out, bs...) } // Decode uint64 from a []byte slice // Returns the decoded uint64 and the number of bytes used func wire_decode_uint64(bs []byte) (uint64, int) { length := 0 elem := uint64(0) for _, b := range bs { elem <<= 7 elem |= uint64(b & 0x7f) length++ if b&0x80 == 0 { break } } return elem, length } 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 } func wire_intFromUint(u uint64) int64 { var i int64 i = int64(u >> 1) if u&0x01 != 0 { i *= -1 } return i } //////////////////////////////////////////////////////////////////////////////// // Takes coords, returns coords prefixed with encoded coord length 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 } func wire_put_coords(coords []byte, bs []byte) []byte { bs = wire_put_uint64(uint64(len(coords)), bs) bs = append(bs, coords...) return bs } // Takes a packet that begins with coords (starting with coord length) // Returns a slice of coords and the number of bytes read func wire_decode_coords(packet []byte) ([]byte, int) { coordLen, coordBegin := wire_decode_uint64(packet) coordEnd := coordBegin + int(coordLen) //if coordBegin == 0 { panic("No coords found") } // Testing //if coordEnd > len(packet) { panic("Packet too short") } // Testing if coordBegin == 0 || coordEnd > len(packet) { return nil, 0 } return packet[coordBegin:coordEnd], coordEnd } //////////////////////////////////////////////////////////////////////////////// // TODO move this msg stuff somewhere else, use encode() and decode() methods // Announces that we can send parts of a Message with a particular seq type msgAnnounce struct { root sigPubKey tstamp int64 seq uint64 len uint64 //Deg uint64 //RSeq uint64 } func (m *msgAnnounce) encode() []byte { bs := wire_encode_uint64(wire_SwitchAnnounce) bs = append(bs, m.root[:]...) bs = append(bs, wire_encode_uint64(wire_intToUint(m.tstamp))...) bs = append(bs, wire_encode_uint64(m.seq)...) bs = append(bs, wire_encode_uint64(m.len)...) //bs = append(bs, wire_encode_uint64(m.Deg)...) //bs = append(bs, wire_encode_uint64(m.RSeq)...) return bs } func (m *msgAnnounce) decode(bs []byte) bool { var pType uint64 var tstamp uint64 switch { case !wire_chop_uint64(&pType, &bs): return false case pType != wire_SwitchAnnounce: return false case !wire_chop_slice(m.root[:], &bs): return false case !wire_chop_uint64(&tstamp, &bs): return false case !wire_chop_uint64(&m.seq, &bs): return false case !wire_chop_uint64(&m.len, &bs): return false //case !wire_chop_uint64(&m.Deg, &bs): return false //case !wire_chop_uint64(&m.RSeq, &bs): return false } m.tstamp = wire_intFromUint(tstamp) return true } type msgHopReq struct { root sigPubKey tstamp int64 seq uint64 hop uint64 } func (m *msgHopReq) encode() []byte { bs := wire_encode_uint64(wire_SwitchHopRequest) bs = append(bs, m.root[:]...) bs = append(bs, wire_encode_uint64(wire_intToUint(m.tstamp))...) bs = append(bs, wire_encode_uint64(m.seq)...) bs = append(bs, wire_encode_uint64(m.hop)...) return bs } func (m *msgHopReq) decode(bs []byte) bool { var pType uint64 var tstamp uint64 switch { case !wire_chop_uint64(&pType, &bs): return false case pType != wire_SwitchHopRequest: return false case !wire_chop_slice(m.root[:], &bs): return false case !wire_chop_uint64(&tstamp, &bs): return false case !wire_chop_uint64(&m.seq, &bs): return false case !wire_chop_uint64(&m.hop, &bs): return false } m.tstamp = wire_intFromUint(tstamp) return true } type msgHop struct { root sigPubKey tstamp int64 seq uint64 hop uint64 port switchPort next sigPubKey sig sigBytes } func (m *msgHop) encode() []byte { bs := wire_encode_uint64(wire_SwitchHop) bs = append(bs, m.root[:]...) bs = append(bs, wire_encode_uint64(wire_intToUint(m.tstamp))...) bs = append(bs, wire_encode_uint64(m.seq)...) bs = append(bs, wire_encode_uint64(m.hop)...) bs = append(bs, wire_encode_uint64(uint64(m.port))...) bs = append(bs, m.next[:]...) bs = append(bs, m.sig[:]...) return bs } func (m *msgHop) decode(bs []byte) bool { var pType uint64 var tstamp uint64 switch { case !wire_chop_uint64(&pType, &bs): return false case pType != wire_SwitchHop: return false case !wire_chop_slice(m.root[:], &bs): return false case !wire_chop_uint64(&tstamp, &bs): return false case !wire_chop_uint64(&m.seq, &bs): return false case !wire_chop_uint64(&m.hop, &bs): return false case !wire_chop_uint64((*uint64)(&m.port), &bs): return false case !wire_chop_slice(m.next[:], &bs): return false case !wire_chop_slice(m.sig[:], &bs): return false } m.tstamp = wire_intFromUint(tstamp) return true } // Format used to check signatures only, so no need to also support decoding func wire_encode_locator(loc *switchLocator) []byte { coords := wire_encode_coords(loc.getCoords()) var bs []byte bs = append(bs, loc.root[:]...) bs = append(bs, wire_encode_uint64(wire_intToUint(loc.tstamp))...) bs = append(bs, coords...) return bs } func wire_chop_slice(toSlice []byte, fromSlice *[]byte) bool { if len(*fromSlice) < len(toSlice) { return false } copy(toSlice, *fromSlice) *fromSlice = (*fromSlice)[len(toSlice):] return true } func wire_chop_coords(toCoords *[]byte, fromSlice *[]byte) bool { coords, coordLen := wire_decode_coords(*fromSlice) if coordLen == 0 { return false } *toCoords = append((*toCoords)[:0], coords...) *fromSlice = (*fromSlice)[coordLen:] return true } func wire_chop_uint64(toUInt64 *uint64, fromSlice *[]byte) bool { dec, decLen := wire_decode_uint64(*fromSlice) if decLen == 0 { return false } *toUInt64 = dec *fromSlice = (*fromSlice)[decLen:] return true } //////////////////////////////////////////////////////////////////////////////// // Wire traffic packets type wire_trafficPacket struct { ttl uint64 // TODO? hide this as a wire format detail, not set by user coords []byte handle handle nonce boxNonce payload []byte } // This is basically MarshalBinary, but decode doesn't allow that... func (p *wire_trafficPacket) encode() []byte { bs := util_getBytes() bs = wire_put_uint64(wire_Traffic, bs) bs = wire_put_uint64(p.ttl, bs) bs = wire_put_coords(p.coords, bs) bs = append(bs, p.handle[:]...) bs = append(bs, p.nonce[:]...) bs = append(bs, p.payload...) return bs } // Not just UnmarshalBinary becuase the original slice isn't always copied from func (p *wire_trafficPacket) decode(bs []byte) bool { var pType uint64 switch { case !wire_chop_uint64(&pType, &bs): return false case pType != wire_Traffic: return false case !wire_chop_uint64(&p.ttl, &bs): return false case !wire_chop_coords(&p.coords, &bs): return false case !wire_chop_slice(p.handle[:], &bs): return false case !wire_chop_slice(p.nonce[:], &bs): return false } p.payload = append(util_getBytes(), bs...) return true } type wire_protoTrafficPacket struct { ttl uint64 // TODO? hide this as a wire format detail, not set by user coords []byte toKey boxPubKey fromKey boxPubKey nonce boxNonce payload []byte } func (p *wire_protoTrafficPacket) encode() []byte { coords := wire_encode_coords(p.coords) bs := wire_encode_uint64(wire_ProtocolTraffic) bs = append(bs, wire_encode_uint64(p.ttl)...) bs = append(bs, coords...) bs = append(bs, p.toKey[:]...) bs = append(bs, p.fromKey[:]...) bs = append(bs, p.nonce[:]...) bs = append(bs, p.payload...) return bs } 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 case !wire_chop_uint64(&p.ttl, &bs): return false case !wire_chop_coords(&p.coords, &bs): return false case !wire_chop_slice(p.toKey[:], &bs): return false case !wire_chop_slice(p.fromKey[:], &bs): return false case !wire_chop_slice(p.nonce[:], &bs): return false } p.payload = bs return true } type wire_linkProtoTrafficPacket struct { toKey boxPubKey fromKey boxPubKey nonce boxNonce payload []byte } func (p *wire_linkProtoTrafficPacket) encode() []byte { bs := wire_encode_uint64(wire_LinkProtocolTraffic) bs = append(bs, p.toKey[:]...) bs = append(bs, p.fromKey[:]...) bs = append(bs, p.nonce[:]...) bs = append(bs, p.payload...) return bs } 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 case !wire_chop_slice(p.toKey[:], &bs): return false case !wire_chop_slice(p.fromKey[:], &bs): return false case !wire_chop_slice(p.nonce[:], &bs): return false } p.payload = bs return true } //////////////////////////////////////////////////////////////////////////////// func (p *sessionPing) encode() []byte { var pTypeVal uint64 if p.isPong { pTypeVal = wire_SessionPong } else { pTypeVal = wire_SessionPing } bs := wire_encode_uint64(pTypeVal) //p.sendPermPub used in top level (crypto), so skipped here 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) bs = append(bs, coords...) return bs } func (p *sessionPing) decode(bs []byte) bool { var pType uint64 var tstamp uint64 switch { case !wire_chop_uint64(&pType, &bs): return false case pType != wire_SessionPing && pType != wire_SessionPong: return false //p.sendPermPub used in top level (crypto), so skipped here case !wire_chop_slice(p.handle[:], &bs): return false case !wire_chop_slice(p.sendSesPub[:], &bs): return false case !wire_chop_uint64(&tstamp, &bs): return false case !wire_chop_coords(&p.coords, &bs): return false } p.tstamp = wire_intFromUint(tstamp) if pType == wire_SessionPong { p.isPong = true } return true } //////////////////////////////////////////////////////////////////////////////// func (r *dhtReq) encode() []byte { coords := wire_encode_coords(r.coords) bs := wire_encode_uint64(wire_DHTLookupRequest) bs = append(bs, r.key[:]...) bs = append(bs, coords...) bs = append(bs, r.dest[:]...) return bs } func (r *dhtReq) decode(bs []byte) bool { var pType uint64 switch { case !wire_chop_uint64(&pType, &bs): return false case pType != wire_DHTLookupRequest: return false case !wire_chop_slice(r.key[:], &bs): return false case !wire_chop_coords(&r.coords, &bs): return false case !wire_chop_slice(r.dest[:], &bs): return false default: return true } } func (r *dhtRes) encode() []byte { coords := wire_encode_coords(r.coords) bs := wire_encode_uint64(wire_DHTLookupResponse) bs = append(bs, r.key[:]...) bs = append(bs, coords...) bs = append(bs, r.dest[:]...) for _, info := range r.infos { coords = wire_encode_coords(info.coords) bs = append(bs, info.key[:]...) bs = append(bs, coords...) } return bs } func (r *dhtRes) decode(bs []byte) bool { var pType uint64 switch { case !wire_chop_uint64(&pType, &bs): return false case pType != wire_DHTLookupResponse: return false case !wire_chop_slice(r.key[:], &bs): return false case !wire_chop_coords(&r.coords, &bs): return false case !wire_chop_slice(r.dest[:], &bs): 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 } r.infos = append(r.infos, &info) } return true } //////////////////////////////////////////////////////////////////////////////// func (r *searchReq) encode() []byte { coords := wire_encode_coords(r.coords) bs := wire_encode_uint64(wire_SearchRequest) bs = append(bs, r.key[:]...) bs = append(bs, coords...) bs = append(bs, r.dest[:]...) return bs } func (r *searchReq) decode(bs []byte) bool { var pType uint64 switch { case !wire_chop_uint64(&pType, &bs): return false case pType != wire_SearchRequest: return false case !wire_chop_slice(r.key[:], &bs): return false case !wire_chop_coords(&r.coords, &bs): return false case !wire_chop_slice(r.dest[:], &bs): return false default: return true } } func (r *searchRes) encode() []byte { coords := wire_encode_coords(r.coords) bs := wire_encode_uint64(wire_SearchResponse) bs = append(bs, r.key[:]...) bs = append(bs, coords...) bs = append(bs, r.dest[:]...) return bs } func (r *searchRes) decode(bs []byte) bool { var pType uint64 switch { case !wire_chop_uint64(&pType, &bs): return false case pType != wire_SearchResponse: return false case !wire_chop_slice(r.key[:], &bs): return false case !wire_chop_coords(&r.coords, &bs): return false case !wire_chop_slice(r.dest[:], &bs): return false default: return true } }