Browse Source

Bunch of new-gen serialization work

Adam Ierymenko 5 years ago
parent
commit
3b94ef99ae
4 changed files with 335 additions and 69 deletions
  1. 55 28
      node/Endpoint.hpp
  2. 88 2
      node/Identity.hpp
  3. 30 30
      node/InetAddress.hpp
  4. 162 9
      node/Locator.hpp

+ 55 - 28
node/Endpoint.hpp

@@ -37,34 +37,37 @@ public:
 	enum Type
 	{
 		NIL =      0, // NIL value
-		SOCKADDR = 1, // InetAddress
+		INETADDR = 1, // InetAddress (v4 or v6)
 		DNSNAME =  2, // DNS name and port that resolves to InetAddress
 		ZEROTIER = 3, // ZeroTier Address (for relaying and meshy behavior)
-		URL =      4  // URL for http/https/ws/etc. (not implemented yet)
+		URL =      4, // URL for http/https/ws/etc. (not implemented yet)
+		ETHERNET = 5  // 48-bit LAN-local Ethernet address
 	};
 
 	inline Endpoint() { memset(reinterpret_cast<void *>(this),0,sizeof(Endpoint)); }
 
-	inline Endpoint(const InetAddress &sa) : _t(SOCKADDR) { _v.sa = sa; }
+	inline Endpoint(const InetAddress &sa) : _t(INETADDR) { _v.sa = sa; }
 	inline Endpoint(const Address &zt,const uint8_t identityHash[ZT_IDENTITY_HASH_SIZE]) : _t(ZEROTIER) { _v.zt.a = zt.toInt(); memcpy(_v.zt.idh,identityHash,ZT_IDENTITY_HASH_SIZE); }
 	inline Endpoint(const char *name,const int port) : _t(DNSNAME) { Utils::scopy(_v.dns.name,sizeof(_v.dns.name),name); _v.dns.port = port; }
 	inline Endpoint(const char *url) : _t(URL) { Utils::scopy(_v.url,sizeof(_v.url),url); }
 
-	inline const InetAddress *sockaddr() const { return (_t == SOCKADDR) ? reinterpret_cast<const InetAddress *>(&_v.sa) : nullptr; }
+	inline const InetAddress *sockaddr() const { return (_t == INETADDR) ? reinterpret_cast<const InetAddress *>(&_v.sa) : nullptr; }
 	inline const char *dnsName() const { return (_t == DNSNAME) ? _v.dns.name : nullptr; }
 	inline const int dnsPort() const { return (_t == DNSNAME) ? _v.dns.port : -1; }
 	inline Address ztAddress() const { return (_t == ZEROTIER) ? Address(_v.zt.a) : Address(); }
 	inline const uint8_t *ztIdentityHash() const { return (_t == ZEROTIER) ? _v.zt.idh : nullptr; }
 	inline const char *url() const { return (_t == URL) ? _v.url : nullptr; }
+	inline MAC ethernet() const { return (_t == ETHERNET) ? MAC(_v.eth) : MAC(); }
 
 	inline Type type() const { return _t; }
 
-	inline unsigned int marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX])
+	static inline int marshalSizeMax() { return ZT_ENDPOINT_MARSHAL_SIZE_MAX; }
+	inline int marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX])
 	{
-		unsigned int p;
+		int p;
 		switch(_t) {
-			case SOCKADDR:
-				data[0] = (uint8_t)SOCKADDR;
+			case INETADDR:
+				data[0] = (uint8_t)INETADDR;
 				return 1 + reinterpret_cast<const InetAddress *>(&_v.sa)->marshal(data+1);
 			case DNSNAME:
 				data[0] = (uint8_t)DNSNAME;
@@ -74,7 +77,7 @@ public:
 						break;
 					++p;
 					if (p == (ZT_ENDPOINT_MAX_NAME_SIZE+1))
-						return 0;
+						return -1;
 				}
 				data[p++] = (uint8_t)((_v.dns.port >> 8) & 0xff);
 				data[p++] = (uint8_t)(_v.dns.port & 0xff);
@@ -96,45 +99,55 @@ public:
 						break;
 					++p;
 					if (p == (ZT_ENDPOINT_MAX_NAME_SIZE+1))
-						return 0;
+						return -1;
 				}
 				return p;
+			case ETHERNET:
+				data[0] = (uint8_t)ETHERNET;
+				data[1] = (uint8_t)((_v.eth >> 40) & 0xff);
+				data[2] = (uint8_t)((_v.eth >> 32) & 0xff);
+				data[3] = (uint8_t)((_v.eth >> 24) & 0xff);
+				data[4] = (uint8_t)((_v.eth >> 16) & 0xff);
+				data[5] = (uint8_t)((_v.eth >> 8) & 0xff);
+				data[6] = (uint8_t)(_v.eth & 0xff);
+				return 7;
 			default:
 				data[0] = (uint8_t)NIL;
 				return 1;
 		}
 	}
-
-	inline bool unmarshal(const uint8_t *restrict data,const unsigned int len)
+	inline int unmarshal(const uint8_t *restrict data,const int len)
 	{
-		if (len == 0)
-			return false;
-		unsigned int p;
+		if (len <= 0)
+			return -1;
+		int p;
 		switch((Type)data[0]) {
 			case NIL:
 				_t = NIL;
-				return true;
-			case SOCKADDR:
-				_t = SOCKADDR;
+				return 1;
+			case INETADDR:
+				_t = INETADDR;
 				return reinterpret_cast<InetAddress *>(&_v.sa)->unmarshal(data+1,len-1);
 			case DNSNAME:
 				if (len < 4)
-					return false;
+					return -1;
 				_t = DNSNAME;
 				p = 1;
 				for (;;) {
-					if ((_v.dns.name[p-1] = (char)data[p]) == 0)
+					if ((_v.dns.name[p-1] = (char)data[p]) == 0) {
+						++p;
 						break;
+					}
 					++p;
 					if ((p >= (ZT_ENDPOINT_MAX_NAME_SIZE+1))||(p >= (len-2)))
 						return;
 				}
 				_v.dns.port = ((int)data[p++]) << 8;
 				_v.dns.port |= (int)data[p++];
-				return true;
+				return p;
 			case ZEROTIER:
-				if (len != (ZT_IDENTITY_HASH_SIZE + 6))
-					return false;
+				if (len < (ZT_IDENTITY_HASH_SIZE + 6))
+					return -1;
 				_t = ZEROTIER;
 				_v.zt.a = ((uint64_t)data[1]) << 32;
 				_v.zt.a |= ((uint64_t)data[2]) << 24;
@@ -142,20 +155,33 @@ public:
 				_v.zt.a |= ((uint64_t)data[4]) << 8;
 				_v.zt.a |= (uint64_t)data[5];
 				memcpy(_v.zt.idh,data + 6,ZT_IDENTITY_HASH_SIZE);
-				return true;
+				return (ZT_IDENTITY_HASH_SIZE + 6);
 			case URL:
 				if (len < 2)
-					return false;
+					return -1;
 				_t = URL;
 				p = 1;
 				for (;;) {
-					if ((_v.url[p-1] = (char)data[p]) == 0)
+					if ((_v.url[p-1] = (char)data[p]) == 0) {
+						++p;
 						break;
+					}
 					++p;
 					if ((p >= (ZT_ENDPOINT_MAX_NAME_SIZE+1))||(p >= len))
-						return;
+						return -1;
 				}
-				return true;
+				return p;
+			case ETHERNET:
+				if (len < 7)
+					return -1;
+				_t = ZEROTIER;
+				_v.eth = ((uint64_t)data[1]) << 40;
+				_v.eth |= ((uint64_t)data[2]) << 32;
+				_v.eth |= ((uint64_t)data[3]) << 24;
+				_v.eth |= ((uint64_t)data[4]) << 16;
+				_v.eth |= ((uint64_t)data[5]) << 8;
+				_v.eth |= (uint64_t)data[6];
+				return 7;
 		}
 		return false;
 	}
@@ -173,6 +199,7 @@ private:
 			uint8_t idh[ZT_IDENTITY_HASH_SIZE];
 		} zt;
 		char url[ZT_ENDPOINT_MAX_NAME_SIZE];
+		uint64_t eth;
 	} _v;
 };
 

+ 88 - 2
node/Identity.hpp

@@ -27,6 +27,11 @@
 
 #define ZT_IDENTITY_STRING_BUFFER_LENGTH 1024
 
+#define ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE (ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE + ZT_C25519_SIGNATURE_LEN + ZT_ECC384_SIGNATURE_SIZE)
+#define ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE (ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE)
+
+#define ZT_IDENTITY_MARSHAL_SIZE_MAX (ZT_ADDRESS_LENGTH + 4 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE)
+
 namespace ZeroTier {
 
 /**
@@ -241,6 +246,87 @@ public:
 	 */
 	inline const Address &address() const { return _address; }
 
+	static inline int marshalSizeMax() { return ZT_IDENTITY_MARSHAL_SIZE_MAX; }
+	inline int marshal(uint8_t restrict data[ZT_IDENTITY_MARSHAL_SIZE_MAX],const bool includePrivate = false) const
+	{
+		_address.copyTo(data,ZT_ADDRESS_LENGTH);
+		switch(_type) {
+
+			case C25519:
+				data[ZT_ADDRESS_LENGTH] = (uint8_t)C25519;
+				memcpy(data + ZT_ADDRESS_LENGTH + 1,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
+				if ((includePrivate)&&(_hasPrivate)) {
+					data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN] = ZT_C25519_PRIVATE_KEY_LEN;
+					memcpy(data + ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN);
+					return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN);
+				}
+				data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN] = 0;
+				return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1);
+
+			case P384:
+				data[ZT_ADDRESS_LENGTH] = (uint8_t)P384;
+				memcpy(data + ZT_ADDRESS_LENGTH + 1,&_pub,ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE);
+				if ((includePrivate)&&(_hasPrivate)) {
+					data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE;
+					memcpy(data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1,&_priv,ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE);
+					data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE] = 0;
+					return (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1);
+				}
+				data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = 0;
+				data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1] = 0;
+				return (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 2);
+
+		}
+		return -1;
+	}
+	inline int unmarshal(const uint8_t *restrict data,const int len)
+	{
+		if (len < (ZT_ADDRESS_LENGTH + 1))
+			return -1;
+		unsigned int privlen;
+		switch((_type = (Type)data[ZT_ADDRESS_LENGTH])) {
+
+			case C25519:
+				if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1))
+					return -1;
+				memcpy(_pub.c25519,data + ZT_ADDRESS_LENGTH + 1,ZT_C25519_PUBLIC_KEY_LEN);
+				privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN];
+				if (privlen == ZT_C25519_PRIVATE_KEY_LEN) {
+					if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN))
+						return -1;
+					_hasPrivate = true;
+					memcpy(_priv.c25519,data + ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1,ZT_C25519_PRIVATE_KEY_LEN);
+					return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN);
+				} else if (privlen == 0) {
+					_hasPrivate = false;
+					return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1);
+				}
+				break;
+
+			case P384:
+				if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 2))
+					return -1;
+				memcpy(&_pub,data + ZT_ADDRESS_LENGTH + 1,ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE);
+				privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE];
+				if (privlen == ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE) {
+					if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1))
+						return -1;
+					_hasPrivate = true;
+					memcpy(&_priv,data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1,ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE);
+					privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE];
+					if (len < (privlen + (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1)))
+						return -1;
+					return (privlen + (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1));
+				} else if (privlen == 0) {
+					_hasPrivate = false;
+					return (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 2);
+				}
+				break;
+
+		}
+		return -1;
+	}
+
 	/**
 	 * Serialize this identity (binary)
 	 *
@@ -274,7 +360,7 @@ public:
 				} else {
 					b.append((uint8_t)0);
 				}
-				b.append((uint16_t)0); // size of additional fields (should have included such a thing in v0!)
+				b.append((uint8_t)0); // size of additional fields (should have included such a thing in v0!)
 				break;
 
 		}
@@ -332,7 +418,7 @@ public:
 				} else {
 					_hasPrivate = false;
 				}
-				p += b.template at<uint16_t>(p) + 2;
+				p += b.template at<uint8_t>(p) + 2;
 				break;
 
 			default:

+ 30 - 30
node/InetAddress.hpp

@@ -489,7 +489,8 @@ struct InetAddress : public sockaddr_storage
 	 */
 	inline operator bool() const { return (ss_family != 0); }
 
-	inline unsigned int marshal(uint8_t restrict data[20]) const
+	static inline int marshalSizeMax() { return 19; }
+	inline int marshal(uint8_t restrict data[19]) const
 	{
 		switch(ss_family) {
 			case AF_INET:
@@ -515,37 +516,36 @@ struct InetAddress : public sockaddr_storage
 				return 1;
 		}
 	}
-
-	inline bool unmarshal(const uint8_t *restrict data,const unsigned int len)
+	inline int unmarshal(const uint8_t *restrict data,const int len)
 	{
-		if (len) {
-			memset(this,0,sizeof(InetAddress));
-			switch(data[0]) {
-				case 0:
-					return true;
-				case 4:
-					if (len != 7)
-						return false;
-					reinterpret_cast<sockaddr_in *>(this)->sin_family = AF_INET;
-					reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[0] = data[1];
-					reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[1] = data[2];
-					reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[2] = data[3];
-					reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[3] = data[4];
-					reinterpret_cast<sockaddr_in *>(this)->sin_port = Utils::hton((((uint16_t)data[5]) << 8) | (uint16_t)data[6]);
-					return true;
-				case 6:
-					if (len != 19)
-						return false;
-					reinterpret_cast<sockaddr_in6 *>(this)->sin6_family = AF_INET6;
-					for(int i=0;i<16;i++)
-						(reinterpret_cast<sockaddr_in6 *>(this)->sin6_addr.s6_addr)[i] = data[i+1];
-					reinterpret_cast<sockaddr_in6 *>(this)->sin6_port = Utils::hton((((uint16_t)data[17]) << 8) | (uint16_t)data[18]);
-					return true;
-				default:
-					return false;
-			}
+		if (len <= 0)
+			return -1;
+		switch(data[0]) {
+			case 0:
+				return 1;
+			case 4:
+				if (len < 7)
+					return -1;
+				memset(this,0,sizeof(InetAddress));
+				reinterpret_cast<sockaddr_in *>(this)->sin_family = AF_INET;
+				reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[0] = data[1];
+				reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[1] = data[2];
+				reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[2] = data[3];
+				reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[3] = data[4];
+				reinterpret_cast<sockaddr_in *>(this)->sin_port = Utils::hton((((uint16_t)data[5]) << 8) | (uint16_t)data[6]);
+				return 7;
+			case 6:
+				if (len < 19)
+					return -1;
+				memset(this,0,sizeof(InetAddress));
+				reinterpret_cast<sockaddr_in6 *>(this)->sin6_family = AF_INET6;
+				for(int i=0;i<16;i++)
+					(reinterpret_cast<sockaddr_in6 *>(this)->sin6_addr.s6_addr)[i] = data[i+1];
+				reinterpret_cast<sockaddr_in6 *>(this)->sin6_port = Utils::hton((((uint16_t)data[17]) << 8) | (uint16_t)data[18]);
+				return 19;
+			default:
+				return -1;
 		}
-		return false;
 	}
 
 	template<unsigned int C>

+ 162 - 9
node/Locator.hpp

@@ -19,6 +19,7 @@
 
 #include "Constants.hpp"
 #include "Endpoint.hpp"
+#include "Identity.hpp"
 
 #define ZT_LOCATOR_MAX_ENDPOINTS 8
 
@@ -29,26 +30,178 @@ namespace ZeroTier {
 /**
  * Signed information about a node's location on the network
  *
- * A locator is a signed record that contains information about where a node
- * may be found. It can contain static physical addresses or virtual ZeroTier
- * addresses of nodes that can forward to the target node. Locator records
- * can be stored in signed DNS TXT record sets, in LF by roots, in caches,
- * etc.
+ * A locator contains long-lived endpoints for a node such as IP/port pairs,
+ * URLs, or other nodes, and is signed by the node it describes.
  */
 class Locator
 {
-	friend class SharedPtr<Locator>;
-
 public:
-	inline Locator() : _ts(0),_signatureLength(0) {}
+	inline Locator() : _ts(0),_at(nullptr),_signatureLength(0) {}
+	inline ~Locator() { delete [] _at; }
+
+	inline Locator(const Locator &l) :
+		_ts(l._ts),
+		_id(l._id),
+		_at((l._endpointCount > 0) ? new Endpoint[l._endpointCount] : nullptr),
+		_endpointCount(l._endpointCount),
+		_signatureLength(l._signatureLength)
+	{
+		for(unsigned int i=0;i<_endpointCount;++i)
+			_at[i] = l._at[i];
+		memcpy(_signature,l._signature,_signatureLength);
+	}
+
+	inline Locator &operator=(const Locator &l)
+	{
+		_ts = l._ts;
+		_id = l._id;
+		delete [] _at;
+		_at = (l._endpointCount > 0) ? new Endpoint[l._endpointCount] : nullptr;
+		for(unsigned int i=0;i<l._endpointCount;++i)
+			_at[i] = l._at[i];
+		_endpointCount = l._endpointCount;
+		_signatureLength = l._signatureLength;
+		memcpy(_signature,l._signature,_signatureLength);
+		return *this;
+	}
 
 	inline int64_t timestamp() const { return _ts; }
 	inline const Identity &id() const { return _id; }
 
+	/**
+	 * Create and sign a Locator
+	 *
+	 * @param ts Timestamp
+	 * @param id Identity (must include secret to allow signing)
+	 * @param at Array of Endpoint objects specifying where this peer might be found
+	 * @param endpointCount Number of endpoints (max: ZT_LOCATOR_MAX_ENDPOINTS)
+	 * @return True if init and sign were successful
+	 */
+	inline bool create(const int64_t ts,const Identity &id,const Endpoint *restrict at,const unsigned int endpointCount)
+	{
+		if ((endpointCount > ZT_LOCATOR_MAX_ENDPOINTS)||(!id.hasPrivate()))
+			return false;
+		_ts = ts;
+		_id = id;
+		if (_at)
+			delete [] _at;
+		_at = new Endpoint[endpointCount];
+		for(unsigned int i=0;i<endpointCount;++i)
+			_at[i] = at[i];
+		_endpointCount = endpointCount;
+
+		uint8_t signData[ZT_LOCATOR_MARSHAL_SIZE_MAX];
+		const unsigned int signLen = marshal(signData,true);
+		if (signLen == 0)
+			return false;
+		if ((_signatureLength = id.sign(signData,signLen,_signature,sizeof(_signature))) == 0)
+			return false;
+
+		return true;
+	}
+
+	/**
+	 * Verify this Locator's validity and signature
+	 *
+	 * @return True if valid and signature checks out
+	 */
+	inline bool verify() const
+	{
+		if ((_ts == 0)||(_endpointCount > ZT_LOCATOR_MAX_ENDPOINTS)||(_signatureLength > ZT_SIGNATURE_BUFFER_SIZE))
+			return false;
+		uint8_t signData[ZT_LOCATOR_MARSHAL_SIZE_MAX];
+		const unsigned int signLen = marshal(signData,true);
+		return _id.verify(signData,signLen,_signature,_signatureLength);
+	}
+
 	inline operator bool() const { return (_ts != 0); }
 
-	inline bool create(const int64_t ts,const Identity &id,const Endpoint *restrict at,const unsigned int endpointCount)
+	static inline int marshalSizeMax() { return ZT_LOCATOR_MARSHAL_SIZE_MAX; }
+	inline int marshal(uint8_t restrict data[ZT_LOCATOR_MARSHAL_SIZE_MAX],const bool excludeSignature = false) const
+	{
+		if ((_endpointCount > ZT_LOCATOR_MAX_ENDPOINTS)||(_signatureLength > ZT_SIGNATURE_BUFFER_SIZE))
+			return -1;
+
+		data[0] = (uint8_t)((uint64_t)_ts >> 56);
+		data[1] = (uint8_t)((uint64_t)_ts >> 48);
+		data[2] = (uint8_t)((uint64_t)_ts >> 40);
+		data[3] = (uint8_t)((uint64_t)_ts >> 32);
+		data[4] = (uint8_t)((uint64_t)_ts >> 24);
+		data[5] = (uint8_t)((uint64_t)_ts >> 16);
+		data[6] = (uint8_t)((uint64_t)_ts >> 8);
+		data[7] = (uint8_t)((uint64_t)_ts);
+
+		int p = _id.marshal(data + 8,false);
+		if (p <= 0)
+			return -1;
+		p += 8;
+
+		data[p++] = (uint8_t)_endpointCount;
+		for(unsigned int i=0;i<_endpointCount;++i) {
+			int tmp = _at[i].marshal(data + p);
+			if (tmp < 0)
+				return -1;
+			p += tmp;
+		}
+
+		if (!excludeSignature) {
+			data[p++] = (uint8_t)(_signatureLength >> 8);
+			data[p++] = (uint8_t)_signatureLength;
+			memcpy(data + p,_signature,_signatureLength);
+			p += _signatureLength;
+		}
+
+		return p;
+	}
+	inline int unmarshal(const uint8_t *restrict data,const int len)
 	{
+		if (len <= 8)
+			return -1;
+
+		uint64_t ts = ((uint64_t)data[0] << 56);
+		ts |= ((uint64_t)data[1] << 48);
+		ts |= ((uint64_t)data[2] << 40);
+		ts |= ((uint64_t)data[3] << 32);
+		ts |= ((uint64_t)data[4] << 24);
+		ts |= ((uint64_t)data[5] << 16);
+		ts |= ((uint64_t)data[6] << 8);
+		ts |= (uint64_t)data[7];
+		_ts = (int64_t)ts;
+
+		int p = _id.unmarshal(data + 8,len - 8);
+		if (p <= 0)
+			return -1;
+		p += 8;
+
+		if (p >= len)
+			return -1;
+		unsigned int ec = (int)data[p++];
+		if (ec > ZT_LOCATOR_MAX_ENDPOINTS)
+			return -1;
+		if (_at)
+			delete [] _at;
+		_at = new Endpoint[ec];
+		for(int i=0;i<ec;++i) {
+			int tmp = _at[i].unmarshal(data + p,len - p);
+			if (tmp < 0)
+				return -1;
+			p += tmp;
+		}
+
+		if ((p + 2) > len)
+			return -1;
+		unsigned int sl = data[p++];
+		sl <<= 8;
+		sl |= data[p++];
+		if (sl > ZT_SIGNATURE_BUFFER_SIZE)
+			return -1;
+		_signatureLength = sl;
+		if ((p + sl) > len)
+			return -1;
+		memcpy(_signature,data + p,sl);
+		p += (int)sl;
+
+		return p;
 	}
 
 private: