浏览代码

Cleanup, Address refactoring, tweak V1 identity generation a bit.

Adam Ierymenko 5 年之前
父节点
当前提交
dcb3d49d35
共有 8 个文件被更改,包括 174 次插入134 次删除
  1. 40 40
      node/AES.cpp
  2. 32 27
      node/Address.hpp
  3. 1 1
      node/C25519.hpp
  4. 6 6
      node/Endpoint.cpp
  5. 23 34
      node/Identity.cpp
  6. 72 1
      node/Tests.cpp
  7. 0 16
      node/Utils.cpp
  8. 0 9
      node/Utils.hpp

+ 40 - 40
node/AES.cpp

@@ -1159,50 +1159,50 @@ static ZT_ALWAYS_INLINE __m128i _init256_2_aesni(__m128i a,__m128i b) noexcept
 
 void AES::_init_aesni(const uint8_t key[32]) noexcept
 {
-	__m128i t1,t2;
+	__m128i t1,t2,k1,k2,k3,k4,k5,k6,k7,k8,k9,k10,k11,k12,k13;
 	_k.ni.k[0] = t1 = _mm_loadu_si128((const __m128i *)key);
-	_k.ni.k[1] = t2 = _mm_loadu_si128((const __m128i *)(key+16));
-	_k.ni.k[2] = t1 = _init256_1_aesni(t1,_mm_aeskeygenassist_si128(t2,0x01));
-	_k.ni.k[3] = t2 = _init256_2_aesni(t1,t2);
-	_k.ni.k[4] = t1 = _init256_1_aesni(t1,_mm_aeskeygenassist_si128(t2,0x02));
-	_k.ni.k[5] = t2 = _init256_2_aesni(t1,t2);
-	_k.ni.k[6] = t1 = _init256_1_aesni(t1,_mm_aeskeygenassist_si128(t2,0x04));
-	_k.ni.k[7] = t2 = _init256_2_aesni(t1,t2);
-	_k.ni.k[8] = t1 = _init256_1_aesni(t1,_mm_aeskeygenassist_si128(t2,0x08));
-	_k.ni.k[9] = t2 = _init256_2_aesni(t1,t2);
-	_k.ni.k[10] = t1 = _init256_1_aesni(t1,_mm_aeskeygenassist_si128(t2,0x10));
-	_k.ni.k[11] = t2 = _init256_2_aesni(t1,t2);
-	_k.ni.k[12] = t1 = _init256_1_aesni(t1,_mm_aeskeygenassist_si128(t2,0x20));
-	_k.ni.k[13] = t2 = _init256_2_aesni(t1,t2);
+	_k.ni.k[1] = k1 = t2 = _mm_loadu_si128((const __m128i *)(key + 16));
+	_k.ni.k[2] = k2 = t1 = _init256_1_aesni(t1,_mm_aeskeygenassist_si128(t2,0x01));
+	_k.ni.k[3] = k3 = t2 = _init256_2_aesni(t1,t2);
+	_k.ni.k[4] = k4 = t1 = _init256_1_aesni(t1,_mm_aeskeygenassist_si128(t2,0x02));
+	_k.ni.k[5] = k5 = t2 = _init256_2_aesni(t1,t2);
+	_k.ni.k[6] = k6 = t1 = _init256_1_aesni(t1,_mm_aeskeygenassist_si128(t2,0x04));
+	_k.ni.k[7] = k7 = t2 = _init256_2_aesni(t1,t2);
+	_k.ni.k[8] = k8 = t1 = _init256_1_aesni(t1,_mm_aeskeygenassist_si128(t2,0x08));
+	_k.ni.k[9] = k9 = t2 = _init256_2_aesni(t1,t2);
+	_k.ni.k[10] = k10 = t1 = _init256_1_aesni(t1,_mm_aeskeygenassist_si128(t2,0x10));
+	_k.ni.k[11] = k11 = t2 = _init256_2_aesni(t1,t2);
+	_k.ni.k[12] = k12 = t1 = _init256_1_aesni(t1,_mm_aeskeygenassist_si128(t2,0x20));
+	_k.ni.k[13] = k13 = t2 = _init256_2_aesni(t1,t2);
 	_k.ni.k[14] = _init256_1_aesni(t1,_mm_aeskeygenassist_si128(t2,0x40));
-	_k.ni.k[15] = _mm_aesimc_si128(_k.ni.k[13]);
-	_k.ni.k[16] = _mm_aesimc_si128(_k.ni.k[12]);
-	_k.ni.k[17] = _mm_aesimc_si128(_k.ni.k[11]);
-	_k.ni.k[18] = _mm_aesimc_si128(_k.ni.k[10]);
-	_k.ni.k[19] = _mm_aesimc_si128(_k.ni.k[9]);
-	_k.ni.k[20] = _mm_aesimc_si128(_k.ni.k[8]);
-	_k.ni.k[21] = _mm_aesimc_si128(_k.ni.k[7]);
-	_k.ni.k[22] = _mm_aesimc_si128(_k.ni.k[6]);
-	_k.ni.k[23] = _mm_aesimc_si128(_k.ni.k[5]);
-	_k.ni.k[24] = _mm_aesimc_si128(_k.ni.k[4]);
-	_k.ni.k[25] = _mm_aesimc_si128(_k.ni.k[3]);
-	_k.ni.k[26] = _mm_aesimc_si128(_k.ni.k[2]);
-	_k.ni.k[27] = _mm_aesimc_si128(_k.ni.k[1]);
+	_k.ni.k[15] = _mm_aesimc_si128(k13);
+	_k.ni.k[16] = _mm_aesimc_si128(k12);
+	_k.ni.k[17] = _mm_aesimc_si128(k11);
+	_k.ni.k[18] = _mm_aesimc_si128(k10);
+	_k.ni.k[19] = _mm_aesimc_si128(k9);
+	_k.ni.k[20] = _mm_aesimc_si128(k8);
+	_k.ni.k[21] = _mm_aesimc_si128(k7);
+	_k.ni.k[22] = _mm_aesimc_si128(k6);
+	_k.ni.k[23] = _mm_aesimc_si128(k5);
+	_k.ni.k[24] = _mm_aesimc_si128(k4);
+	_k.ni.k[25] = _mm_aesimc_si128(k3);
+	_k.ni.k[26] = _mm_aesimc_si128(k2);
+	_k.ni.k[27] = _mm_aesimc_si128(k1);
 
 	__m128i h = _k.ni.k[0]; // _mm_xor_si128(_mm_setzero_si128(),_k.ni.k[0]);
-	h = _mm_aesenc_si128(h,_k.ni.k[1]);
-	h = _mm_aesenc_si128(h,_k.ni.k[2]);
-	h = _mm_aesenc_si128(h,_k.ni.k[3]);
-	h = _mm_aesenc_si128(h,_k.ni.k[4]);
-	h = _mm_aesenc_si128(h,_k.ni.k[5]);
-	h = _mm_aesenc_si128(h,_k.ni.k[6]);
-	h = _mm_aesenc_si128(h,_k.ni.k[7]);
-	h = _mm_aesenc_si128(h,_k.ni.k[8]);
-	h = _mm_aesenc_si128(h,_k.ni.k[9]);
-	h = _mm_aesenc_si128(h,_k.ni.k[10]);
-	h = _mm_aesenc_si128(h,_k.ni.k[11]);
-	h = _mm_aesenc_si128(h,_k.ni.k[12]);
-	h = _mm_aesenc_si128(h,_k.ni.k[13]);
+	h = _mm_aesenc_si128(h,k1);
+	h = _mm_aesenc_si128(h,k2);
+	h = _mm_aesenc_si128(h,k3);
+	h = _mm_aesenc_si128(h,k4);
+	h = _mm_aesenc_si128(h,k5);
+	h = _mm_aesenc_si128(h,k6);
+	h = _mm_aesenc_si128(h,k7);
+	h = _mm_aesenc_si128(h,k8);
+	h = _mm_aesenc_si128(h,k9);
+	h = _mm_aesenc_si128(h,k10);
+	h = _mm_aesenc_si128(h,k11);
+	h = _mm_aesenc_si128(h,k12);
+	h = _mm_aesenc_si128(h,k13);
 	h = _mm_aesenclast_si128(h,_k.ni.k[14]);
 	const __m128i shuf = s_shuf;
 	__m128i hswap = _mm_shuffle_epi8(h,shuf);

+ 32 - 27
node/Address.hpp

@@ -29,10 +29,10 @@ class Address : public TriviallyCopyable
 {
 public:
 	ZT_ALWAYS_INLINE Address() noexcept : _a(0) {}
+	explicit ZT_ALWAYS_INLINE Address(const uint64_t a) noexcept : _a(a) {}
 	explicit ZT_ALWAYS_INLINE Address(const uint8_t b[5]) noexcept : _a(((uint64_t)b[0] << 32U) | ((uint64_t)b[1] << 24U) | ((uint64_t)b[2] << 16U) | ((uint64_t)b[3] << 8U) | (uint64_t)b[4]) {}
-	explicit ZT_ALWAYS_INLINE Address(const uint64_t a) noexcept : _a(a & 0xffffffffffULL) {}
 
-	ZT_ALWAYS_INLINE Address &operator=(const uint64_t a) noexcept { _a = (a & 0xffffffffffULL); return *this; }
+	ZT_ALWAYS_INLINE Address &operator=(const uint64_t a) noexcept { _a = a; return *this; }
 
 	/**
 	 * @param bits Raw address -- 5 bytes, big-endian byte order
@@ -49,11 +49,12 @@ public:
 	 */
 	ZT_ALWAYS_INLINE void copyTo(uint8_t b[5]) const noexcept
 	{
-		b[0] = (uint8_t)(_a >> 32U);
-		b[1] = (uint8_t)(_a >> 24U);
-		b[2] = (uint8_t)(_a >> 16U);
-		b[3] = (uint8_t)(_a >> 8U);
-		b[4] = (uint8_t)_a;
+		const uint64_t a = _a;
+		b[0] = (uint8_t)(a >> 32U);
+		b[1] = (uint8_t)(a >> 24U);
+		b[2] = (uint8_t)(a >> 16U);
+		b[3] = (uint8_t)(a >> 8U);
+		b[4] = (uint8_t)a;
 	}
 
 	/**
@@ -72,9 +73,26 @@ public:
 	ZT_ALWAYS_INLINE unsigned long hashCode() const noexcept { return (unsigned long)_a; }
 
 	/**
+	 * @param s String with at least 11 characters of space available (10 + terminating NULL)
 	 * @return Hexadecimal string
 	 */
-	ZT_ALWAYS_INLINE char *toString(char buf[ZT_ADDRESS_STRING_SIZE_MAX]) const noexcept { return Utils::hex10(_a,buf); }
+	ZT_ALWAYS_INLINE char *toString(char s[ZT_ADDRESS_STRING_SIZE_MAX]) const noexcept
+	{
+		const uint64_t a = _a;
+		const unsigned int m = 0xf;
+		s[0] = Utils::HEXCHARS[(unsigned int)(a >> 36U) & m];
+		s[1] = Utils::HEXCHARS[(unsigned int)(a >> 32U) & m];
+		s[2] = Utils::HEXCHARS[(unsigned int)(a >> 28U) & m];
+		s[3] = Utils::HEXCHARS[(unsigned int)(a >> 24U) & m];
+		s[4] = Utils::HEXCHARS[(unsigned int)(a >> 20U) & m];
+		s[5] = Utils::HEXCHARS[(unsigned int)(a >> 16U) & m];
+		s[6] = Utils::HEXCHARS[(unsigned int)(a >> 12U) & m];
+		s[7] = Utils::HEXCHARS[(unsigned int)(a >> 8U) & m];
+		s[8] = Utils::HEXCHARS[(unsigned int)(a >> 4U) & m];
+		s[9] = Utils::HEXCHARS[(unsigned int)a & m];
+		s[10] = 0;
+		return s;
+	}
 
 	/**
 	 * Check if this address is reserved
@@ -87,27 +105,14 @@ public:
 	 */
 	ZT_ALWAYS_INLINE bool isReserved() const noexcept { return ((!_a)||((_a >> 32U) == ZT_ADDRESS_RESERVED_PREFIX)); }
 
-	/**
-	 * @param i Value from 0 to 4 (inclusive)
-	 * @return Byte at said position (address interpreted in big-endian order)
-	 */
-	ZT_ALWAYS_INLINE uint8_t operator[](unsigned int i) const noexcept { return (uint8_t)(_a >> (32 - (i * 8))); }
-
 	ZT_ALWAYS_INLINE operator bool() const noexcept { return (_a != 0); }
 
-	ZT_ALWAYS_INLINE bool operator==(const uint64_t &a) const noexcept { return (_a == (a & 0xffffffffffULL)); }
-	ZT_ALWAYS_INLINE bool operator!=(const uint64_t &a) const noexcept { return (_a != (a & 0xffffffffffULL)); }
-	ZT_ALWAYS_INLINE bool operator>(const uint64_t &a) const noexcept { return (_a > (a & 0xffffffffffULL)); }
-	ZT_ALWAYS_INLINE bool operator<(const uint64_t &a) const noexcept { return (_a < (a & 0xffffffffffULL)); }
-	ZT_ALWAYS_INLINE bool operator>=(const uint64_t &a) const noexcept { return (_a >= (a & 0xffffffffffULL)); }
-	ZT_ALWAYS_INLINE bool operator<=(const uint64_t &a) const noexcept { return (_a <= (a & 0xffffffffffULL)); }
-
-	ZT_ALWAYS_INLINE bool operator==(const Address &a) const noexcept { return (_a == a._a); }
-	ZT_ALWAYS_INLINE bool operator!=(const Address &a) const noexcept { return (_a != a._a); }
-	ZT_ALWAYS_INLINE bool operator>(const Address &a) const noexcept { return (_a > a._a); }
-	ZT_ALWAYS_INLINE bool operator<(const Address &a) const noexcept { return (_a < a._a); }
-	ZT_ALWAYS_INLINE bool operator>=(const Address &a) const noexcept { return (_a >= a._a); }
-	ZT_ALWAYS_INLINE bool operator<=(const Address &a) const noexcept { return (_a <= a._a); }
+	ZT_ALWAYS_INLINE bool operator==(const Address &a) const noexcept { return _a == a._a; }
+	ZT_ALWAYS_INLINE bool operator!=(const Address &a) const noexcept { return _a != a._a; }
+	ZT_ALWAYS_INLINE bool operator>(const Address &a) const noexcept { return _a > a._a; }
+	ZT_ALWAYS_INLINE bool operator<(const Address &a) const noexcept { return _a < a._a; }
+	ZT_ALWAYS_INLINE bool operator>=(const Address &a) const noexcept { return _a >= a._a; }
+	ZT_ALWAYS_INLINE bool operator<=(const Address &a) const noexcept { return _a <= a._a; }
 
 #if 0
 	/**

+ 1 - 1
node/C25519.hpp

@@ -73,7 +73,7 @@ public:
 	 * @param their Their public key
 	 * @param rawkey Buffer to receive raw (not hashed) agreed upon key
 	 */
-	static void agree(const uint8_t mine[ZT_C25519_PRIVATE_KEY_LEN],const uint8_t their[ZT_C25519_PUBLIC_KEY_LEN],uint8_t rawkey[32]);
+	static void agree(const uint8_t mine[ZT_C25519_PRIVATE_KEY_LEN],const uint8_t their[ZT_C25519_PUBLIC_KEY_LEN],uint8_t rawkey[ZT_C25519_SHARED_KEY_LEN]);
 
 	/**
 	 * Sign a message with a sender's key pair

+ 6 - 6
node/Endpoint.cpp

@@ -56,9 +56,9 @@ int Endpoint::marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX]) const noexcept
 {
 	int p;
 	data[0] = (uint8_t)_t;
-	Utils::storeBigEndian(data + 1,(int16_t)_l[0]);
-	Utils::storeBigEndian(data + 3,(int16_t)_l[1]);
-	Utils::storeBigEndian(data + 5,(int16_t)_l[2]);
+	Utils::storeBigEndian(data + 1,(uint16_t)_l[0]);
+	Utils::storeBigEndian(data + 3,(uint16_t)_l[1]);
+	Utils::storeBigEndian(data + 5,(uint16_t)_l[2]);
 	switch(_t) {
 		case TYPE_ZEROTIER:
 			data[7] = (uint8_t)(_v.zt.a >> 32U);
@@ -116,9 +116,9 @@ int Endpoint::unmarshal(const uint8_t *restrict data,const int len) noexcept
 		return -1;
 	int p;
 	_t = (Type)data[0];
-	_l[0] = Utils::loadBigEndian<int16_t>(data + 1);
-	_l[1] = Utils::loadBigEndian<int16_t>(data + 3);
-	_l[2] = Utils::loadBigEndian<int16_t>(data + 5);
+	_l[0] = (int)Utils::loadBigEndian<uint16_t>(data + 1);
+	_l[1] = (int)Utils::loadBigEndian<uint16_t>(data + 3);
+	_l[2] = (int)Utils::loadBigEndian<uint16_t>(data + 5);
   switch(_t) {
 		case TYPE_NIL:
 			return 7;

+ 23 - 34
node/Identity.cpp

@@ -26,13 +26,8 @@ namespace ZeroTier {
 
 namespace {
 
-// --------------------------------------------------------------------------------------------------------------------
-
-// This is the memory-intensive hash function used to compute v0 identities
-// from v0 public keys.
-
+// This is the memory-intensive hash function used to compute v0 identities from v0 public keys.
 #define ZT_V0_IDENTITY_GEN_MEMORY 2097152
-
 static void _computeMemoryHardHash(const void *const publicKey,unsigned int publicKeyBytes,void *const digest,void *const genmem) noexcept
 {
 	// Digest publicKey[] to obtain initial digest
@@ -67,7 +62,6 @@ static void _computeMemoryHardHash(const void *const publicKey,unsigned int publ
 		s20.crypt20(digest,digest,64);
 	}
 }
-
 struct _v0_identity_generate_cond
 {
 	ZT_ALWAYS_INLINE _v0_identity_generate_cond() noexcept {}
@@ -81,7 +75,17 @@ struct _v0_identity_generate_cond
 	char *genmem;
 };
 
-// --------------------------------------------------------------------------------------------------------------------
+ZT_ALWAYS_INLINE void _v1_hash(uint8_t *const digest,const void *const in,const unsigned int len) noexcept
+{
+	SHA384(digest,in,len);
+	Utils::storeBigEndian(digest,Utils::loadBigEndian<uint64_t>(digest)           % 18446744073709549811ULL); // these are primes close to uint64_max
+	Utils::storeBigEndian(digest + 8,Utils::loadBigEndian<uint64_t>(digest + 8)   % 18446744073709549757ULL);
+	Utils::storeBigEndian(digest + 16,Utils::loadBigEndian<uint64_t>(digest + 16) % 18446744073709549733ULL);
+	Utils::storeBigEndian(digest + 24,Utils::loadBigEndian<uint64_t>(digest + 24) % 18446744073709549667ULL);
+	Utils::storeBigEndian(digest + 32,Utils::loadBigEndian<uint64_t>(digest + 32) % 18446744073709549613ULL);
+	Utils::storeBigEndian(digest + 40,Utils::loadBigEndian<uint64_t>(digest + 40) % 18446744073709549583ULL);
+	SHA384(digest,in,len,digest,48);
+}
 
 } // anonymous namespace
 
@@ -89,7 +93,7 @@ const Identity Identity::NIL;
 
 bool Identity::generate(const Type t)
 {
-	uint8_t digest[128];
+	uint8_t digest[64];
 
 	_type = t;
 	_hasPrivate = true;
@@ -107,23 +111,16 @@ bool Identity::generate(const Type t)
 
 		case P384: {
 			AES c;
-			do {
+			for(;;) {
 				C25519::generate(_pub.c25519,_priv.c25519);
 				ECC384GenerateKey(_pub.p384,_priv.p384);
-
-				SHA384(digest,&_pub,sizeof(_pub));
-				c.init(digest);
-				c.encrypt(digest,digest + 48);
-				c.encrypt(digest + 16,digest + 64);
-				c.encrypt(digest + 32,digest + 80);
-				SHA384(digest,digest,96);
-
-				if (digest[47] != 0)
-					continue;
-
-				_address.setTo(digest);
-			} while (_address.isReserved());
-
+				_v1_hash(digest,&_pub,sizeof(_pub));
+				if (((digest[46] & 1U)|digest[47]) == 0) { // right-most 9 bits must be zero
+					_address.setTo(digest);
+					if (!_address.isReserved())
+						break;
+				}
+			}
 			_hash.set(digest); // P384 uses the same hash for hash() and address generation
 		} break;
 
@@ -151,7 +148,7 @@ bool Identity::locallyValidate() const
 			return false;
 
 		case P384:
-			return ((_hash[47] == 0)&&(Address(_hash.data()) == _address));
+			return ( (Address(_hash.data()) == _address) && (((_hash[46] & 1U)|_hash[47]) == 0) );
 
 	}
 	return false;
@@ -524,15 +521,7 @@ void Identity::_computeHash()
 			break;
 
 		case P384:
-			if (!_hash) {
-				uint8_t *const digest = _hash.data();
-				SHA384(digest,&_pub,sizeof(_pub));
-				AES c(digest);
-				c.encrypt(digest,digest + 48);
-				c.encrypt(digest + 16,digest + 64);
-				c.encrypt(digest + 32,digest + 80);
-				SHA384(digest,digest,96);
-			}
+			_v1_hash(_hash.data(),&_pub,sizeof(_pub));
 			break;
 	}
 }

+ 72 - 1
node/Tests.cpp

@@ -178,6 +178,9 @@ static const C25519TestVector C25519_TEST_VECTORS[ZT_NUM_C25519_TEST_VECTORS] =
 #define IDENTITY_V0_KNOWN_GOOD_0 "8e4df28b72:0:ac3d46abe0c21f3cfe7a6c8d6a85cfcffcb82fbd55af6a4d6350657c68200843fa2e16f9418bbd9702cae365f2af5fb4c420908b803a681d4daef6114d78a2d7:bd8dd6e4ce7022d2f812797a80c6ee8ad180dc4ebf301dec8b06d1be08832bddd63a2f1cfa7b2c504474c75bdc8898ba476ef92e8e2d0509f8441985171ff16e"
 #define IDENTITY_V0_KNOWN_BAD_0 "9e4df28b72:0:ac3d46abe0c21f3cfe7a6c8d6a85cfcffcb82fbd55af6a4d6350657c68200843fa2e16f9418bbd9702cae365f2af5fb4c420908b803a681d4daef6114d78a2d7:bd8dd6e4ce7022d2f812797a80c6ee8ad180dc4ebf301dec8b06d1be08832bddd63a2f1cfa7b2c504474c75bdc8898ba476ef92e8e2d0509f8441985171ff16e"
 
+#define IDENTITY_V1_KNOWN_GOOD_0 "bc72fb58e4:1:fya26hekqeromqdtpzq3mzj26zecwf7pkjahictpreapv4sw5vjcdkf6tbwaajzw6cq2ro6usrtzerccr37n52hiydogi2boaxk4tjidnhctgsbk4i4g34madrxihraurflyoe3xgeqkbpj2zrlsivscvbygzd3zfqs3qihoi6e24xy2jridq:tqaxnh3pucstd2xuwylgjfapyug7zdxorfwv37ted66qic6fu5g3pveodg7so4vt7cil7ptoht6msn6m2tsrfyd52a5f3b3g5wbd5ljjds2sftrjjw3qcb645eg4iizbqv5mlphgpa2uznonoo77qblbx6fdjh2nbt3ksooebj377rgu6qmq"
+#define IDENTITY_V1_KNOWN_BAD_0 "bc82fb58e4:1:fya26hekqeromqdtpzq3mzj26zecwf7pkjahictpreapv4sw5vjcdkf6tbwaajzw6cq2ro6usrtzerccr37n52hiydogi2boaxk4tjidnhctgsbk4i4g34madrxihraurflyoe3xgeqkbpj2zrlsivscvbygzd3zfqs3qihoi6e24xy2jridq:tqaxnh3pucstd2xuwylgjfapyug7zdxorfwv37ted66qic6fu5g3pveodg7so4vt7cil7ptoht6msn6m2tsrfyd52a5f3b3g5wbd5ljjds2sftrjjw3qcb645eg4iizbqv5mlphgpa2uznonoo77qblbx6fdjh2nbt3ksooebj377rgu6qmq"
+
 // --------------------------------------------------------------------------------------------------------------------
 
 #if __BYTE_ORDER == __LITTLE_ENDIAN
@@ -481,7 +484,7 @@ extern "C" const char *ZTT_general()
 				FCV<Buf::Slice,16> ref;
 
 				int frags = 1 + (int)(Utils::random() % 16);
-				int skip = ((k & 3) == 1) ? -1 : (int)(Utils::random() % frags);
+				int skip = ((k & 3U) == 1) ? -1 : (int)(Utils::random() % frags);
 				bool complete = false;
 				message.resize(frags);
 				ref.resize(frags);
@@ -555,6 +558,54 @@ extern "C" const char *ZTT_general()
 			Buf::freePool();
 			ZT_T_PRINTF("OK (cache remaining: %u)" ZT_EOL_S,defrag.cacheSize());
 		}
+
+		{
+			ZT_T_PRINTF("[general] Testing Identity type 0 (C25519)... ");
+			Identity id;
+
+			if (!id.fromString(IDENTITY_V0_KNOWN_GOOD_0)) {
+				ZT_T_PRINTF("FAILED (error parsing test identity #1)" ZT_EOL_S);
+				return "Identity test failed: parse error";
+			}
+			if (!id.locallyValidate()) {
+				ZT_T_PRINTF("FAILED (validation of known-good identity failed)" ZT_EOL_S);
+				return "Identity test failed: validation of known-good identity";
+			}
+			if (!id.fromString(IDENTITY_V0_KNOWN_BAD_0)) {
+				ZT_T_PRINTF("FAILED (error parsing test identity #2)" ZT_EOL_S);
+				return "Identity test failed: parse error";
+			}
+			if (id.locallyValidate()) {
+				ZT_T_PRINTF("FAILED (validation of known-bad identity returned ok)" ZT_EOL_S);
+				return "Identity test failed: validation of known-bad identity";
+			}
+
+			ZT_T_PRINTF("OK" ZT_EOL_S "[general] Testing Identity type 1 (P384)... ");
+
+			//id.generate(Identity::P384);
+			//char tmp[1024];
+			//id.toString(true,tmp);
+			//ZT_T_PRINTF("\n%s\n",tmp);
+
+			if (!id.fromString(IDENTITY_V1_KNOWN_GOOD_0)) {
+				ZT_T_PRINTF("FAILED (error parsing test identity #1)" ZT_EOL_S);
+				return "Identity test failed: parse error";
+			}
+			if (!id.locallyValidate()) {
+				ZT_T_PRINTF("FAILED (validation of known-good identity failed)" ZT_EOL_S);
+				return "Identity test failed: validation of known-good identity";
+			}
+			if (!id.fromString(IDENTITY_V1_KNOWN_BAD_0)) {
+				ZT_T_PRINTF("FAILED (error parsing test identity #2)" ZT_EOL_S);
+				return "Identity test failed: parse error";
+			}
+			if (id.locallyValidate()) {
+				ZT_T_PRINTF("FAILED (validation of known-bad identity returned ok)" ZT_EOL_S);
+				return "Identity test failed: validation of known-bad identity";
+			}
+
+			ZT_T_PRINTF("OK" ZT_EOL_S);
+		}
 	} catch (std::exception &e) {
 		ZT_T_PRINTF(ZT_EOL_S "[general] Unexpected exception: %s" ZT_EOL_S,e.what());
 		return e.what();
@@ -921,6 +972,26 @@ extern "C" const char *ZTT_benchmarkCrypto()
 			end = now();
 			ZT_T_PRINTF("%.4f μs/verify" ZT_EOL_S,((double)(end - start) * 1000.0) / (double)(500 * ZT_NUM_C25519_TEST_VECTORS));
 		}
+
+		{
+			ZT_T_PRINTF("[crypto] Benchmarking V0 Identity generation... ");
+			Identity id;
+			int64_t start = now();
+			for(long i=0;i<5;++i) {
+				id.generate(Identity::C25519);
+				foo = (uint8_t)id.address().toInt();
+			}
+			int64_t end = now();
+			ZT_T_PRINTF("%.4f ms/generation" ZT_EOL_S,(double)(end - start) / 5.0);
+			ZT_T_PRINTF("[crypto] Benchmarking V1 Identity generation... ");
+			start = now();
+			for(long i=0;i<5;++i) {
+				id.generate(Identity::P384);
+				foo = (uint8_t)id.address().toInt();
+			}
+			end = now();
+			ZT_T_PRINTF("%.4f ms/generation" ZT_EOL_S,(double)(end - start) / 5.0);
+		}
 	} catch (std::exception &e) {
 		ZT_T_PRINTF(ZT_EOL_S "[crypto] Unexpected exception: %s" ZT_EOL_S,e.what());
 		return e.what();

+ 0 - 16
node/Utils.cpp

@@ -193,22 +193,6 @@ uint64_t unhex(const char *s) noexcept
 	return n;
 }
 
-char *hex10(uint64_t i,char s[11]) noexcept
-{
-	s[0] = HEXCHARS[(i >> 36U) & 0xfU];
-	s[1] = HEXCHARS[(i >> 32U) & 0xfU];
-	s[2] = HEXCHARS[(i >> 28U) & 0xfU];
-	s[3] = HEXCHARS[(i >> 24U) & 0xfU];
-	s[4] = HEXCHARS[(i >> 20U) & 0xfU];
-	s[5] = HEXCHARS[(i >> 16U) & 0xfU];
-	s[6] = HEXCHARS[(i >> 12U) & 0xfU];
-	s[7] = HEXCHARS[(i >> 8U) & 0xfU];
-	s[8] = HEXCHARS[(i >> 4U) & 0xfU];
-	s[9] = HEXCHARS[i & 0xfU];
-	s[10] = (char)0;
-	return s;
-}
-
 char *hex(const void *d,unsigned int l,char *s) noexcept
 {
 	char *const save = s;

+ 0 - 9
node/Utils.hpp

@@ -115,15 +115,6 @@ char *hex(uint64_t i,char s[17]) noexcept;
  */
 uint64_t unhex(const char *s) noexcept;
 
-/**
- * Convert the least significant 40 bits of a uint64_t to hex
- *
- * @param i Unsigned 64-bit int
- * @param s Buffer of size [11] to receive 10 hex characters
- * @return Pointer to buffer
- */
-char *hex10(uint64_t i,char s[11]) noexcept;
-
 /**
  * Convert a byte array into hex
  *