Browse Source

type 1 (P-384) identities

Adam Ierymenko 6 năm trước cách đây
mục cha
commit
83d723eb79
15 tập tin đã thay đổi với 467 bổ sung591 xóa
  1. 14 24
      node/C25519.cpp
  2. 15 63
      node/C25519.hpp
  3. 104 104
      node/ECC384.cpp
  4. 70 80
      node/Identity.cpp
  5. 81 79
      node/Identity.hpp
  6. 1 1
      node/IncomingPacket.cpp
  7. 2 0
      node/InetAddress.hpp
  8. 2 3
      node/NetworkConfig.hpp
  9. 1 1
      node/Peer.cpp
  10. 44 25
      node/Root.hpp
  11. 0 46
      node/SHA512.cpp
  12. 49 0
      node/SHA512.hpp
  13. 1 1
      node/Trace.hpp
  14. 33 41
      node/Utils.cpp
  15. 50 123
      selftest.cpp

+ 14 - 24
node/C25519.cpp

@@ -2395,23 +2395,12 @@ static inline void get_hram(unsigned char *hram, const unsigned char *sm, const
 
 namespace ZeroTier {
 
-void C25519::agree(const C25519::Private &mine,const C25519::Public &their,void *keybuf,unsigned int keylen)
+void C25519::agree(const uint8_t mine[ZT_C25519_PRIVATE_KEY_LEN],const uint8_t their[ZT_C25519_PUBLIC_KEY_LEN],uint8_t rawkey[32])
 {
-	unsigned char rawkey[32];
-	unsigned char digest[64];
-
-	crypto_scalarmult(rawkey,mine.data,their.data);
-	SHA512(digest,rawkey,32);
-	for(unsigned int i=0,k=0;i<keylen;) {
-		if (k == 64) {
-			k = 0;
-			SHA512(digest,digest,64);
-		}
-		((unsigned char *)keybuf)[i++] = digest[k++];
-	}
+	crypto_scalarmult(rawkey,mine,their);
 }
 
-void C25519::sign(const C25519::Private &myPrivate,const C25519::Public &myPublic,const void *msg,unsigned int len,void *signature)
+void C25519::sign(const uint8_t myPrivate[ZT_C25519_PRIVATE_KEY_LEN],const uint8_t myPublic[ZT_C25519_PUBLIC_KEY_LEN],const void *msg,unsigned int len,void *signature)
 {
 	unsigned char digest[64]; // we sign the first 32 bytes of SHA-512(msg)
 	SHA512(digest,msg,len);
@@ -2425,7 +2414,7 @@ void C25519::sign(const C25519::Private &myPrivate,const C25519::Public &myPubli
 	unsigned char hram[crypto_hash_sha512_BYTES];
 	unsigned char *sig = (unsigned char *)signature;
 
-	SHA512(extsk,myPrivate.data + 32,32);
+	SHA512(extsk,myPrivate + 32,32);
 	extsk[0] &= 248;
 	extsk[31] &= 127;
 	extsk[31] |= 64;
@@ -2446,7 +2435,7 @@ void C25519::sign(const C25519::Private &myPrivate,const C25519::Public &myPubli
 	for(unsigned int i=0;i<32;i++)
 		sig[i] = r[i];
 
-	get_hram(hram,sig,myPublic.data + 32,sig,96);
+	get_hram(hram,sig,myPublic + 32,sig,96);
 
 	sc25519_from64bytes(&scs, hram);
 	sc25519_from32bytes(&scsk, extsk);
@@ -2459,7 +2448,7 @@ void C25519::sign(const C25519::Private &myPrivate,const C25519::Public &myPubli
 		sig[32 + i] = s[i];
 }
 
-bool C25519::verify(const C25519::Public &their,const void *msg,unsigned int len,const void *signature,const unsigned int siglen)
+bool C25519::verify(const uint8_t their[ZT_C25519_PUBLIC_KEY_LEN],const void *msg,unsigned int len,const void *signature,const unsigned int siglen)
 {
 	if (siglen < 64) return false;
 
@@ -2482,10 +2471,10 @@ bool C25519::verify(const C25519::Public &their,const void *msg,unsigned int len
 	unsigned char hram[crypto_hash_sha512_BYTES];
 	unsigned char m[96];
 
-	if (ge25519_unpackneg_vartime(&get1,their.data + 32))
+	if (ge25519_unpackneg_vartime(&get1,their + 32))
 		return false;
 
-	get_hram(hram,sig,their.data + 32,m,96);
+	get_hram(hram,sig,their + 32,m,96);
 
 	sc25519_from64bytes(&schram, hram);
 
@@ -2497,14 +2486,14 @@ bool C25519::verify(const C25519::Public &their,const void *msg,unsigned int len
 	return Utils::secureEq(sig,t2,32);
 }
 
-void C25519::_calcPubDH(C25519::Pair &kp)
+void C25519::_calcPubDH(uint8_t pub[ZT_C25519_PUBLIC_KEY_LEN],const uint8_t priv[ZT_C25519_PRIVATE_KEY_LEN])
 {
 	// First 32 bytes of pub and priv are the keys for ECDH key
 	// agreement. This generates the public portion from the private.
-	crypto_scalarmult_base(kp.pub.data,kp.priv.data);
+	crypto_scalarmult_base(pub,priv);
 }
 
-void C25519::_calcPubED(C25519::Pair &kp)
+void C25519::_calcPubED(uint8_t pub[ZT_C25519_PUBLIC_KEY_LEN],const uint8_t priv[ZT_C25519_PRIVATE_KEY_LEN])
 {
 	unsigned char extsk[64];
 	sc25519 scsk;
@@ -2512,13 +2501,14 @@ void C25519::_calcPubED(C25519::Pair &kp)
 
 	// Second 32 bytes of pub and priv are the keys for ed25519
 	// signing and verification.
-	SHA512(extsk,kp.priv.data + 32,32);
+	SHA512(extsk,priv + 32,32);
 	extsk[0] &= 248;
 	extsk[31] &= 127;
 	extsk[31] |= 64;
 	sc25519_from32bytes(&scsk,extsk);
 	ge25519_scalarmult_base(&gepk,&scsk);
-	ge25519_pack(kp.pub.data + 32,&gepk);
+	ge25519_pack(pub + 32,&gepk);
+
 	// In NaCl, the public key is crammed into the next 32 bytes
 	// of the private key for signing since both keys are required
 	// to sign. In this version we just get it from kp.pub, so we

+ 15 - 63
node/C25519.hpp

@@ -34,6 +34,7 @@ namespace ZeroTier {
 #define ZT_C25519_PUBLIC_KEY_LEN 64
 #define ZT_C25519_PRIVATE_KEY_LEN 64
 #define ZT_C25519_SIGNATURE_LEN 96
+#define ZT_C25519_SHARED_KEY_LEN 32
 
 /**
  * A combined Curve25519 ECDH and Ed25519 signature engine
@@ -41,21 +42,14 @@ namespace ZeroTier {
 class C25519
 {
 public:
-	struct Public { uint8_t data[ZT_C25519_PUBLIC_KEY_LEN]; };
-	struct Private { uint8_t data[ZT_C25519_PRIVATE_KEY_LEN]; };
-	struct Signature { uint8_t data[ZT_C25519_SIGNATURE_LEN]; };
-	struct Pair { Public pub; Private priv; };
-
 	/**
 	 * Generate a C25519 elliptic curve key pair
 	 */
-	static inline Pair generate()
+	static inline void generate(uint8_t pub[ZT_C25519_PUBLIC_KEY_LEN],uint8_t priv[ZT_C25519_PRIVATE_KEY_LEN])
 	{
-		Pair kp;
-		Utils::getSecureRandom(kp.priv.data,ZT_C25519_PRIVATE_KEY_LEN);
-		_calcPubDH(kp);
-		_calcPubED(kp);
-		return kp;
+		Utils::getSecureRandom(priv,ZT_C25519_PRIVATE_KEY_LEN);
+		_calcPubDH(pub,priv);
+		_calcPubED(pub,priv);
 	}
 
 	/**
@@ -72,18 +66,15 @@ public:
 	 * @tparam F Type of 'cond'
 	 */
 	template<typename F>
-	static inline Pair generateSatisfying(F cond)
+	static inline void generateSatisfying(F cond,uint8_t pub[ZT_C25519_PUBLIC_KEY_LEN],uint8_t priv[ZT_C25519_PRIVATE_KEY_LEN])
 	{
-		Pair kp;
-		void *const priv = (void *)kp.priv.data;
 		Utils::getSecureRandom(priv,ZT_C25519_PRIVATE_KEY_LEN);
-		_calcPubED(kp); // do Ed25519 key -- bytes 32-63 of pub and priv
+		_calcPubED(pub,priv); // do Ed25519 key -- bytes 32-63 of pub and priv
 		do {
 			++(((uint64_t *)priv)[1]);
 			--(((uint64_t *)priv)[2]);
-			_calcPubDH(kp); // keep regenerating bytes 0-31 until satisfied
-		} while (!cond(kp));
-		return kp;
+			_calcPubDH(pub,priv); // keep regenerating bytes 0-31 until satisfied
+		} while (!cond(pub));
 	}
 
 	/**
@@ -94,10 +85,9 @@ public:
 	 *
 	 * @param mine My private key
 	 * @param their Their public key
-	 * @param keybuf Buffer to fill
-	 * @param keylen Number of key bytes to generate
+	 * @param rawkey Buffer to receive raw (not hashed) agreed upon key
 	 */
-	static void agree(const Private &mine,const Public &their,void *keybuf,unsigned int keylen);
+	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]);
 
 	/**
 	 * Sign a message with a sender's key pair
@@ -118,34 +108,7 @@ public:
 	 * @param len Length of message in bytes
 	 * @param signature Buffer to fill with signature -- MUST be 96 bytes in length
 	 */
-	static void sign(const Private &myPrivate,const Public &myPublic,const void *msg,unsigned int len,void *signature);
-
-	/**
-	 * Sign a message with a sender's key pair
-	 *
-	 * Note that this generates a 96-byte signature that contains an extra 32 bytes
-	 * of hash data. This data is included for historical reasons and is optional. The
-	 * verify function here will take the first 64 bytes only (normal ed25519 signature)
-	 * or a 96-byte length signature with the extra input hash data.
-	 * 
-	 * @param myPrivate My private key
-	 * @param myPublic My public key
-	 * @param msg Message to sign
-	 * @param len Length of message in bytes
-	 * @return Signature
-	 */
-	static inline Signature sign(const Private &myPrivate,const Public &myPublic,const void *msg,unsigned int len)
-	{
-		Signature sig;
-		sign(myPrivate,myPublic,msg,len,sig.data);
-		return sig;
-	}
-	static inline Signature sign(const Pair &mine,const void *msg,unsigned int len)
-	{
-		Signature sig;
-		sign(mine.priv,mine.pub,msg,len,sig.data);
-		return sig;
-	}
+	static void sign(const uint8_t myPrivate[ZT_C25519_PRIVATE_KEY_LEN],const uint8_t myPublic[ZT_C25519_PUBLIC_KEY_LEN],const void *msg,unsigned int len,void *signature);
 
 	/**
 	 * Verify a message's signature
@@ -157,27 +120,16 @@ public:
 	 * @param siglen Length of signature in bytes
 	 * @return True if signature is valid and the message is authentic and unmodified
 	 */
-	static bool verify(const Public &their,const void *msg,unsigned int len,const void *signature,const unsigned int siglen);
-
-	/**
-	 * Verify a message's signature
-	 *
-	 * @param their Public key to verify against
-	 * @param msg Message to verify signature integrity against
-	 * @param len Length of message in bytes
-	 * @param signature 96-byte signature
-	 * @return True if signature is valid and the message is authentic and unmodified
-	 */
-	static inline bool verify(const Public &their,const void *msg,unsigned int len,const Signature &signature) { return verify(their,msg,len,signature.data,96); }
+	static bool verify(const uint8_t their[ZT_C25519_PUBLIC_KEY_LEN],const void *msg,unsigned int len,const void *signature,const unsigned int siglen);
 
 private:
 	// derive first 32 bytes of kp.pub from first 32 bytes of kp.priv
 	// this is the ECDH key
-	static void _calcPubDH(Pair &kp);
+	static void _calcPubDH(uint8_t pub[ZT_C25519_PUBLIC_KEY_LEN],const uint8_t priv[ZT_C25519_PRIVATE_KEY_LEN]);
 
 	// derive 2nd 32 bytes of kp.pub from 2nd 32 bytes of kp.priv
 	// this is the Ed25519 sign/verify key
-	static void _calcPubED(Pair &kp);
+	static void _calcPubED(uint8_t pub[ZT_C25519_PUBLIC_KEY_LEN],const uint8_t priv[ZT_C25519_PRIVATE_KEY_LEN]);
 };
 
 } // namespace ZeroTier

+ 104 - 104
node/ECC384.cpp

@@ -116,7 +116,7 @@ typedef struct EccPoint
 #define Curve_G_24 { \
 	{0xF4FF0AFD82FF1012ull, 0x7CBF20EB43A18800ull, 0x188DA80EB03090F6ull}, \
 	{0x73F977A11E794811ull, 0x631011ED6B24CDD5ull, 0x07192B95FFC8DA78ull}}
-	
+
 #define Curve_G_32 { \
 	{0xF4A13945D898C296ull, 0x77037D812DEB33A0ull, 0xF8BCE6E563A440F2ull, 0x6B17D1F2E12C4247ull}, \
 	{0xCBB6406837BF51F5ull, 0x2BCE33576B315ECEull, 0x8EE7EB4A7C0F9E16ull, 0x4FE342E2FE1A7F9Bull}}
@@ -189,7 +189,7 @@ static inline uint vli_numBits(uint64_t *p_vli)
 {
 	uint i;
 	uint64_t l_digit;
-	
+
 	uint l_numDigits = vli_numDigits(p_vli);
 	if(l_numDigits == 0)
 	{
@@ -201,7 +201,7 @@ static inline uint vli_numBits(uint64_t *p_vli)
 	{
 		l_digit >>= 1;
 	}
-	
+
 	return ((l_numDigits - 1) * 64 + i);
 }
 
@@ -244,7 +244,7 @@ static inline uint64_t vli_lshift(uint64_t *p_result, uint64_t *p_in, uint p_shi
 		p_result[i] = (l_temp << p_shift) | l_carry;
 		l_carry = l_temp >> (64 - p_shift);
 	}
-	
+
 	return l_carry;
 }
 
@@ -253,7 +253,7 @@ static inline void vli_rshift1(uint64_t *p_vli)
 {
 	uint64_t *l_end = p_vli;
 	uint64_t l_carry = 0;
-	
+
 	p_vli += NUM_ECC_DIGITS;
 	while(p_vli-- > l_end)
 	{
@@ -304,9 +304,9 @@ static inline void vli_mult(uint64_t *p_result, uint64_t *p_left, uint64_t *p_ri
 {
 	uint128_t r01 = 0;
 	uint64_t r2 = 0;
-	
+
 	uint i, k;
-	
+
 	/* Compute each digit of p_result in sequence, maintaining the carries. */
 	for(k=0; k < NUM_ECC_DIGITS*2 - 1; ++k)
 	{
@@ -321,7 +321,7 @@ static inline void vli_mult(uint64_t *p_result, uint64_t *p_left, uint64_t *p_ri
 		r01 = (r01 >> 64) | (((uint128_t)r2) << 64);
 		r2 = 0;
 	}
-	
+
 	p_result[NUM_ECC_DIGITS*2 - 1] = (uint64_t)r01;
 }
 
@@ -330,7 +330,7 @@ static inline void vli_square(uint64_t *p_result, uint64_t *p_left)
 {
 	uint128_t r01 = 0;
 	uint64_t r2 = 0;
-	
+
 	uint i, k;
 	for(k=0; k < NUM_ECC_DIGITS*2 - 1; ++k)
 	{
@@ -350,7 +350,7 @@ static inline void vli_square(uint64_t *p_result, uint64_t *p_left)
 		r01 = (r01 >> 64) | (((uint128_t)r2) << 64);
 		r2 = 0;
 	}
-	
+
 	p_result[NUM_ECC_DIGITS*2 - 1] = (uint64_t)r01;
 }
 
@@ -359,27 +359,27 @@ static inline void vli_square(uint64_t *p_result, uint64_t *p_left)
 static inline uint128_t mul_64_64(uint64_t p_left, uint64_t p_right)
 {
 	uint128_t l_result;
-	
+
 	uint64_t a0 = p_left & 0xffffffffull;
 	uint64_t a1 = p_left >> 32;
 	uint64_t b0 = p_right & 0xffffffffull;
 	uint64_t b1 = p_right >> 32;
-	
+
 	uint64_t m0 = a0 * b0;
 	uint64_t m1 = a0 * b1;
 	uint64_t m2 = a1 * b0;
 	uint64_t m3 = a1 * b1;
-	
+
 	m2 += (m0 >> 32);
 	m2 += m1;
 	if(m2 < m1)
 	{ // overflow
 		m3 += 0x100000000ull;
 	}
-	
+
 	l_result.m_low = (m0 & 0xffffffffull) | (m2 << 32);
 	l_result.m_high = m3 + (m2 >> 32);
-	
+
 	return l_result;
 }
 
@@ -395,9 +395,9 @@ static inline void vli_mult(uint64_t *p_result, uint64_t *p_left, uint64_t *p_ri
 {
 	uint128_t r01 = {0, 0};
 	uint64_t r2 = 0;
-	
+
 	uint i, k;
-	
+
 	/* Compute each digit of p_result in sequence, maintaining the carries. */
 	for(k=0; k < NUM_ECC_DIGITS*2 - 1; ++k)
 	{
@@ -413,7 +413,7 @@ static inline void vli_mult(uint64_t *p_result, uint64_t *p_left, uint64_t *p_ri
 		r01.m_high = r2;
 		r2 = 0;
 	}
-	
+
 	p_result[NUM_ECC_DIGITS*2 - 1] = r01.m_low;
 }
 
@@ -421,7 +421,7 @@ static inline void vli_square(uint64_t *p_result, uint64_t *p_left)
 {
 	uint128_t r01 = {0, 0};
 	uint64_t r2 = 0;
-	
+
 	uint i, k;
 	for(k=0; k < NUM_ECC_DIGITS*2 - 1; ++k)
 	{
@@ -443,7 +443,7 @@ static inline void vli_square(uint64_t *p_result, uint64_t *p_left)
 		r01.m_high = r2;
 		r2 = 0;
 	}
-	
+
 	p_result[NUM_ECC_DIGITS*2 - 1] = r01.m_low;
 }
 
@@ -480,33 +480,33 @@ static void vli_mmod_fast(uint64_t *p_result, uint64_t *p_product)
 {
 	uint64_t l_tmp[NUM_ECC_DIGITS];
 	int l_carry;
-	
+
 	vli_set(p_result, p_product);
-	
+
 	l_tmp[0] = p_product[2];
 	l_tmp[1] = (p_product[3] & 0x1FFFFFFFFull) | (p_product[2] << 33);
 	l_carry = vli_add(p_result, p_result, l_tmp);
-	
+
 	l_tmp[0] = (p_product[2] >> 31) | (p_product[3] << 33);
 	l_tmp[1] = (p_product[3] >> 31) | ((p_product[2] & 0xFFFFFFFF80000000ull) << 2);
 	l_carry += vli_add(p_result, p_result, l_tmp);
-	
+
 	l_tmp[0] = (p_product[2] >> 62) | (p_product[3] << 2);
 	l_tmp[1] = (p_product[3] >> 62) | ((p_product[2] & 0xC000000000000000ull) >> 29) | (p_product[3] << 35);
 	l_carry += vli_add(p_result, p_result, l_tmp);
-	
+
 	l_tmp[0] = (p_product[3] >> 29);
 	l_tmp[1] = ((p_product[3] & 0xFFFFFFFFE0000000ull) << 4);
 	l_carry += vli_add(p_result, p_result, l_tmp);
-	
+
 	l_tmp[0] = (p_product[3] >> 60);
 	l_tmp[1] = (p_product[3] & 0xFFFFFFFE00000000ull);
 	l_carry += vli_add(p_result, p_result, l_tmp);
-	
+
 	l_tmp[0] = 0;
 	l_tmp[1] = ((p_product[3] & 0xF000000000000000ull) >> 27);
 	l_carry += vli_add(p_result, p_result, l_tmp);
-	
+
 	while(l_carry || vli_cmp(curve_p, p_result) != 1)
 	{
 		l_carry -= vli_sub(p_result, p_result, curve_p);
@@ -521,21 +521,21 @@ static void vli_mmod_fast(uint64_t *p_result, uint64_t *p_product)
 {
 	uint64_t l_tmp[NUM_ECC_DIGITS];
 	int l_carry;
-	
+
 	vli_set(p_result, p_product);
-	
+
 	vli_set(l_tmp, &p_product[3]);
 	l_carry = vli_add(p_result, p_result, l_tmp);
-	
+
 	l_tmp[0] = 0;
 	l_tmp[1] = p_product[3];
 	l_tmp[2] = p_product[4];
 	l_carry += vli_add(p_result, p_result, l_tmp);
-	
+
 	l_tmp[0] = l_tmp[1] = p_product[5];
 	l_tmp[2] = 0;
 	l_carry += vli_add(p_result, p_result, l_tmp);
-	
+
 	while(l_carry || vli_cmp(curve_p, p_result) != 1)
 	{
 		l_carry -= vli_sub(p_result, p_result, curve_p);
@@ -550,10 +550,10 @@ static void vli_mmod_fast(uint64_t *p_result, uint64_t *p_product)
 {
 	uint64_t l_tmp[NUM_ECC_DIGITS];
 	int l_carry;
-	
+
 	/* t */
 	vli_set(p_result, p_product);
-	
+
 	/* s1 */
 	l_tmp[0] = 0;
 	l_tmp[1] = p_product[5] & 0xffffffff00000000ull;
@@ -561,56 +561,56 @@ static void vli_mmod_fast(uint64_t *p_result, uint64_t *p_product)
 	l_tmp[3] = p_product[7];
 	l_carry = vli_lshift(l_tmp, l_tmp, 1);
 	l_carry += vli_add(p_result, p_result, l_tmp);
-	
+
 	/* s2 */
 	l_tmp[1] = p_product[6] << 32;
 	l_tmp[2] = (p_product[6] >> 32) | (p_product[7] << 32);
 	l_tmp[3] = p_product[7] >> 32;
 	l_carry += vli_lshift(l_tmp, l_tmp, 1);
 	l_carry += vli_add(p_result, p_result, l_tmp);
-	
+
 	/* s3 */
 	l_tmp[0] = p_product[4];
 	l_tmp[1] = p_product[5] & 0xffffffff;
 	l_tmp[2] = 0;
 	l_tmp[3] = p_product[7];
 	l_carry += vli_add(p_result, p_result, l_tmp);
-	
+
 	/* s4 */
 	l_tmp[0] = (p_product[4] >> 32) | (p_product[5] << 32);
 	l_tmp[1] = (p_product[5] >> 32) | (p_product[6] & 0xffffffff00000000ull);
 	l_tmp[2] = p_product[7];
 	l_tmp[3] = (p_product[6] >> 32) | (p_product[4] << 32);
 	l_carry += vli_add(p_result, p_result, l_tmp);
-	
+
 	/* d1 */
 	l_tmp[0] = (p_product[5] >> 32) | (p_product[6] << 32);
 	l_tmp[1] = (p_product[6] >> 32);
 	l_tmp[2] = 0;
 	l_tmp[3] = (p_product[4] & 0xffffffff) | (p_product[5] << 32);
 	l_carry -= vli_sub(p_result, p_result, l_tmp);
-	
+
 	/* d2 */
 	l_tmp[0] = p_product[6];
 	l_tmp[1] = p_product[7];
 	l_tmp[2] = 0;
 	l_tmp[3] = (p_product[4] >> 32) | (p_product[5] & 0xffffffff00000000ull);
 	l_carry -= vli_sub(p_result, p_result, l_tmp);
-	
+
 	/* d3 */
 	l_tmp[0] = (p_product[6] >> 32) | (p_product[7] << 32);
 	l_tmp[1] = (p_product[7] >> 32) | (p_product[4] << 32);
 	l_tmp[2] = (p_product[4] >> 32) | (p_product[5] << 32);
 	l_tmp[3] = (p_product[6] << 32);
 	l_carry -= vli_sub(p_result, p_result, l_tmp);
-	
+
 	/* d4 */
 	l_tmp[0] = p_product[7];
 	l_tmp[1] = p_product[4] & 0xffffffff00000000ull;
 	l_tmp[2] = p_product[5];
 	l_tmp[3] = p_product[6] & 0xffffffff00000000ull;
 	l_carry -= vli_sub(p_result, p_result, l_tmp);
-	
+
 	if(l_carry < 0)
 	{
 		do
@@ -633,7 +633,7 @@ static inline void omega_mult(uint64_t *p_result, uint64_t *p_right)
 {
 	uint64_t l_tmp[NUM_ECC_DIGITS];
 	uint64_t l_carry, l_diff;
-	
+
 	/* Multiply by (2^128 + 2^96 - 2^32 + 1). */
 	vli_set(p_result, p_right); /* 1 */
 	l_carry = vli_lshift(l_tmp, p_right, 32);
@@ -662,17 +662,17 @@ static inline void omega_mult(uint64_t *p_result, uint64_t *p_right)
 static inline void vli_mmod_fast(uint64_t *p_result, uint64_t *p_product)
 {
 	uint64_t l_tmp[2*NUM_ECC_DIGITS];
-	 
+
 	while(!vli_isZero(p_product + NUM_ECC_DIGITS)) /* While c1 != 0 */
 	{
 		uint64_t l_carry = 0;
 		uint i;
-		
+
 		vli_clear(l_tmp);
 		vli_clear(l_tmp + NUM_ECC_DIGITS);
 		omega_mult(l_tmp, p_product + NUM_ECC_DIGITS); /* tmp = w * c1 */
 		vli_clear(p_product + NUM_ECC_DIGITS); /* p = c0 */
-		
+
 		/* (c1, c0) = c0 + w * c1 */
 		for(i=0; i<NUM_ECC_DIGITS+3; ++i)
 		{
@@ -684,7 +684,7 @@ static inline void vli_mmod_fast(uint64_t *p_result, uint64_t *p_product)
 			p_product[i] = l_sum;
 		}
 	}
-	
+
 	while(vli_cmp(p_product, curve_p) > 0)
 	{
 		vli_sub(p_product, p_product, curve_p);
@@ -719,7 +719,7 @@ static inline void vli_modInv(uint64_t *p_result, uint64_t *p_input, uint64_t *p
 	uint64_t a[NUM_ECC_DIGITS], b[NUM_ECC_DIGITS], u[NUM_ECC_DIGITS], v[NUM_ECC_DIGITS];
 	uint64_t l_carry;
 	int l_cmpResult;
-	
+
 	if(vli_isZero(p_input))
 	{
 		vli_clear(p_result);
@@ -731,7 +731,7 @@ static inline void vli_modInv(uint64_t *p_result, uint64_t *p_input, uint64_t *p
 	vli_clear(u);
 	u[0] = 1;
 	vli_clear(v);
-	
+
 	while((l_cmpResult = vli_cmp(a, b)) != 0)
 	{
 		l_carry = 0;
@@ -800,7 +800,7 @@ static inline void vli_modInv(uint64_t *p_result, uint64_t *p_input, uint64_t *p
 			}
 		}
 	}
-	
+
 	vli_set(p_result, u);
 }
 
@@ -822,23 +822,23 @@ static inline void EccPoint_double_jacobian(uint64_t *X1, uint64_t *Y1, uint64_t
 	/* t1 = X, t2 = Y, t3 = Z */
 	uint64_t t4[NUM_ECC_DIGITS];
 	uint64_t t5[NUM_ECC_DIGITS];
-	
+
 	if(vli_isZero(Z1))
 	{
 		return;
 	}
-	
+
 	vli_modSquare_fast(t4, Y1);   /* t4 = y1^2 */
 	vli_modMult_fast(t5, X1, t4); /* t5 = x1*y1^2 = A */
 	vli_modSquare_fast(t4, t4);   /* t4 = y1^4 */
 	vli_modMult_fast(Y1, Y1, Z1); /* t2 = y1*z1 = z3 */
 	vli_modSquare_fast(Z1, Z1);   /* t3 = z1^2 */
-	
+
 	vli_modAdd(X1, X1, Z1, curve_p); /* t1 = x1 + z1^2 */
 	vli_modAdd(Z1, Z1, Z1, curve_p); /* t3 = 2*z1^2 */
 	vli_modSub(Z1, X1, Z1, curve_p); /* t3 = x1 - z1^2 */
 	vli_modMult_fast(X1, X1, Z1);	/* t1 = x1^2 - z1^4 */
-	
+
 	vli_modAdd(Z1, X1, X1, curve_p); /* t3 = 2*(x1^2 - z1^4) */
 	vli_modAdd(X1, X1, Z1, curve_p); /* t1 = 3*(x1^2 - z1^4) */
 	if(vli_testBit(X1, 0))
@@ -852,14 +852,14 @@ static inline void EccPoint_double_jacobian(uint64_t *X1, uint64_t *Y1, uint64_t
 		vli_rshift1(X1);
 	}
 	/* t1 = 3/2*(x1^2 - z1^4) = B */
-	
+
 	vli_modSquare_fast(Z1, X1);	  /* t3 = B^2 */
 	vli_modSub(Z1, Z1, t5, curve_p); /* t3 = B^2 - A */
 	vli_modSub(Z1, Z1, t5, curve_p); /* t3 = B^2 - 2A = x3 */
 	vli_modSub(t5, t5, Z1, curve_p); /* t5 = A - x3 */
 	vli_modMult_fast(X1, X1, t5);	/* t1 = B * (A - x3) */
 	vli_modSub(t4, X1, t4, curve_p); /* t4 = B * (A - x3) - y1^4 = y3 */
-	
+
 	vli_set(X1, Z1);
 	vli_set(Z1, Y1);
 	vli_set(Y1, t4);
@@ -880,10 +880,10 @@ static inline void apply_z(uint64_t *X1, uint64_t *Y1, uint64_t *Z)
 static inline void XYcZ_initial_double(uint64_t *X1, uint64_t *Y1, uint64_t *X2, uint64_t *Y2, uint64_t *p_initialZ)
 {
 	uint64_t z[NUM_ECC_DIGITS];
-	
+
 	vli_set(X2, X1);
 	vli_set(Y2, Y1);
-	
+
 	vli_clear(z);
 	z[0] = 1;
 	if(p_initialZ)
@@ -892,9 +892,9 @@ static inline void XYcZ_initial_double(uint64_t *X1, uint64_t *Y1, uint64_t *X2,
 	}
 
 	apply_z(X1, Y1, z);
-	
+
 	EccPoint_double_jacobian(X1, Y1, z);
-	
+
 	apply_z(X2, Y2, z);
 }
 
@@ -906,14 +906,14 @@ static inline void XYcZ_add(uint64_t *X1, uint64_t *Y1, uint64_t *X2, uint64_t *
 {
 	/* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */
 	uint64_t t5[NUM_ECC_DIGITS];
-	
+
 	vli_modSub(t5, X2, X1, curve_p); /* t5 = x2 - x1 */
 	vli_modSquare_fast(t5, t5);	  /* t5 = (x2 - x1)^2 = A */
 	vli_modMult_fast(X1, X1, t5);	/* t1 = x1*A = B */
 	vli_modMult_fast(X2, X2, t5);	/* t3 = x2*A = C */
 	vli_modSub(Y2, Y2, Y1, curve_p); /* t4 = y2 - y1 */
 	vli_modSquare_fast(t5, Y2);	  /* t5 = (y2 - y1)^2 = D */
-	
+
 	vli_modSub(t5, t5, X1, curve_p); /* t5 = D - B */
 	vli_modSub(t5, t5, X2, curve_p); /* t5 = D - B - C = x3 */
 	vli_modSub(X2, X2, X1, curve_p); /* t3 = C - B */
@@ -921,7 +921,7 @@ static inline void XYcZ_add(uint64_t *X1, uint64_t *Y1, uint64_t *X2, uint64_t *
 	vli_modSub(X2, X1, t5, curve_p); /* t3 = B - x3 */
 	vli_modMult_fast(Y2, Y2, X2);	/* t4 = (y2 - y1)*(B - x3) */
 	vli_modSub(Y2, Y2, Y1, curve_p); /* t4 = y3 */
-	
+
 	vli_set(X2, t5);
 }
 
@@ -935,7 +935,7 @@ static inline void XYcZ_addC(uint64_t *X1, uint64_t *Y1, uint64_t *X2, uint64_t
 	uint64_t t5[NUM_ECC_DIGITS];
 	uint64_t t6[NUM_ECC_DIGITS];
 	uint64_t t7[NUM_ECC_DIGITS];
-	
+
 	vli_modSub(t5, X2, X1, curve_p); /* t5 = x2 - x1 */
 	vli_modSquare_fast(t5, t5);	  /* t5 = (x2 - x1)^2 = A */
 	vli_modMult_fast(X1, X1, t5);	/* t1 = x1*A = B */
@@ -948,17 +948,17 @@ static inline void XYcZ_addC(uint64_t *X1, uint64_t *Y1, uint64_t *X2, uint64_t
 	vli_modAdd(t6, X1, X2, curve_p); /* t6 = B + C */
 	vli_modSquare_fast(X2, Y2);	  /* t3 = (y2 - y1)^2 */
 	vli_modSub(X2, X2, t6, curve_p); /* t3 = x3 */
-	
+
 	vli_modSub(t7, X1, X2, curve_p); /* t7 = B - x3 */
 	vli_modMult_fast(Y2, Y2, t7);	/* t4 = (y2 - y1)*(B - x3) */
 	vli_modSub(Y2, Y2, Y1, curve_p); /* t4 = y3 */
-	
+
 	vli_modSquare_fast(t7, t5);	  /* t7 = (y2 + y1)^2 = F */
 	vli_modSub(t7, t7, t6, curve_p); /* t7 = x3' */
 	vli_modSub(t6, t7, X1, curve_p); /* t6 = x3' - B */
 	vli_modMult_fast(t6, t6, t5);	/* t6 = (y2 + y1)*(x3' - B) */
 	vli_modSub(Y1, t6, Y1, curve_p); /* t2 = y3' */
-	
+
 	vli_set(X1, t7);
 }
 
@@ -968,9 +968,9 @@ static inline void EccPoint_mult(EccPoint *p_result, EccPoint *p_point, uint64_t
 	uint64_t Rx[2][NUM_ECC_DIGITS];
 	uint64_t Ry[2][NUM_ECC_DIGITS];
 	uint64_t z[NUM_ECC_DIGITS];
-	
+
 	int i, nb;
-	
+
 	vli_set(Rx[1], p_point->x);
 	vli_set(Ry[1], p_point->y);
 
@@ -985,7 +985,7 @@ static inline void EccPoint_mult(EccPoint *p_result, EccPoint *p_point, uint64_t
 
 	nb = !vli_testBit(p_scalar, 0);
 	XYcZ_addC(Rx[1-nb], Ry[1-nb], Rx[nb], Ry[nb]);
-	
+
 	/* Find final 1/Z value. */
 	vli_modSub(z, Rx[1], Rx[0], curve_p); /* X1 - X0 */
 	vli_modMult_fast(z, z, Ry[1-nb]);	 /* Yb * (X1 - X0) */
@@ -996,9 +996,9 @@ static inline void EccPoint_mult(EccPoint *p_result, EccPoint *p_point, uint64_t
 	/* End 1/Z calculation */
 
 	XYcZ_add(Rx[nb], Ry[nb], Rx[1-nb], Ry[1-nb]);
-	
+
 	apply_z(Rx[0], Ry[0], z);
-	
+
 	vli_set(p_result->x, Rx[0]);
 	vli_set(p_result->y, Ry[0]);
 }
@@ -1037,7 +1037,7 @@ static inline void mod_sqrt(uint64_t a[NUM_ECC_DIGITS])
 	unsigned i;
 	uint64_t p1[NUM_ECC_DIGITS] = {1};
 	uint64_t l_result[NUM_ECC_DIGITS] = {1};
-	
+
 	/* Since curve_p == 3 (mod 4) for all supported curves, we can
 	   compute sqrt(a) = a^((curve_p + 1) / 4) (mod curve_p). */
 	vli_add(p1, curve_p, p1); /* p1 = curve_p + 1 */
@@ -1056,14 +1056,14 @@ static inline void ecc_point_decompress(EccPoint *p_point, const uint8_t p_compr
 {
 	uint64_t _3[NUM_ECC_DIGITS] = {3}; /* -a = 3 */
 	ecc_bytes2native(p_point->x, p_compressed+1);
-	
+
 	vli_modSquare_fast(p_point->y, p_point->x); /* y = x^2 */
 	vli_modSub(p_point->y, p_point->y, _3, curve_p); /* y = x^2 - 3 */
 	vli_modMult_fast(p_point->y, p_point->y, p_point->x); /* y = x^3 - 3x */
 	vli_modAdd(p_point->y, p_point->y, curve_b, curve_p); /* y = x^3 - 3x + b */
-	
+
 	mod_sqrt(p_point->y);
-	
+
 	if((p_point->y[0] & 0x01) != (p_compressed[0] & 0x01))
 	{
 		vli_sub(p_point->y, curve_p, p_point->y);
@@ -1075,7 +1075,7 @@ static inline int ecc_make_key(uint8_t p_publicKey[ECC_BYTES+1], uint8_t p_priva
 	uint64_t l_private[NUM_ECC_DIGITS];
 	EccPoint l_public;
 	unsigned l_tries = 0;
-	
+
 	do
 	{
 		if(!getRandomNumber(l_private) || (l_tries++ >= MAX_TRIES))
@@ -1086,7 +1086,7 @@ static inline int ecc_make_key(uint8_t p_publicKey[ECC_BYTES+1], uint8_t p_priva
 		{
 			continue;
 		}
-	
+
 		/* Make sure the private key is in the range [1, n-1].
 		   For the supported curves, n is always large enough that we only need to subtract once at most. */
 		if(vli_cmp(curve_n, l_private) != 1)
@@ -1096,7 +1096,7 @@ static inline int ecc_make_key(uint8_t p_publicKey[ECC_BYTES+1], uint8_t p_priva
 
 		EccPoint_mult(&l_public, &curve_G, l_private, NULL);
 	} while(EccPoint_isZero(&l_public));
-	
+
 	ecc_native2bytes(p_privateKey, l_private);
 	ecc_native2bytes(p_publicKey + 1, l_public.x);
 	p_publicKey[0] = 2 + (l_public.y[0] & 0x01);
@@ -1108,20 +1108,20 @@ static inline int ecdh_shared_secret(const uint8_t p_publicKey[ECC_BYTES+1], con
 	EccPoint l_public;
 	uint64_t l_private[NUM_ECC_DIGITS];
 	uint64_t l_random[NUM_ECC_DIGITS];
-	
+
 	if(!getRandomNumber(l_random))
 	{
 		return 0;
 	}
-	
+
 	ecc_point_decompress(&l_public, p_publicKey);
 	ecc_bytes2native(l_private, p_privateKey);
-	
+
 	EccPoint l_product;
 	EccPoint_mult(&l_product, &l_public, l_private, l_random);
-	
+
 	ecc_native2bytes(p_secret, l_product.x);
-	
+
 	return !EccPoint_isZero(&l_product);
 }
 
@@ -1135,7 +1135,7 @@ static inline void vli_modMult(uint64_t *p_result, uint64_t *p_left, uint64_t *p
 	uint l_digitShift, l_bitShift;
 	uint l_productBits;
 	uint l_modBits = vli_numBits(p_mod);
-	
+
 	vli_mult(l_product, p_left, p_right);
 	l_productBits = vli_numBits(l_product + NUM_ECC_DIGITS);
 	if(l_productBits)
@@ -1146,13 +1146,13 @@ static inline void vli_modMult(uint64_t *p_result, uint64_t *p_left, uint64_t *p
 	{
 		l_productBits = vli_numBits(l_product);
 	}
-	
+
 	if(l_productBits < l_modBits)
 	{ /* l_product < p_mod. */
 		vli_set(p_result, l_product);
 		return;
 	}
-	
+
 	/* Shift p_mod by (l_leftBits - l_modBits). This multiplies p_mod by the largest
 	   power of two possible while still resulting in a number less than p_left. */
 	vli_clear(l_modMultiple);
@@ -1186,7 +1186,7 @@ static inline void vli_modMult(uint64_t *p_result, uint64_t *p_left, uint64_t *p
 		vli_rshift1(l_modMultiple + NUM_ECC_DIGITS);
 		vli_rshift1(l_modMultiple);
 		l_modMultiple[NUM_ECC_DIGITS-1] |= l_carry;
-		
+
 		--l_productBits;
 	}
 	vli_set(p_result, l_product);
@@ -1204,7 +1204,7 @@ static inline int ecdsa_sign(const uint8_t p_privateKey[ECC_BYTES], const uint8_
 	uint64_t l_s[NUM_ECC_DIGITS];
 	EccPoint p;
 	unsigned l_tries = 0;
-	
+
 	do
 	{
 		if(!getRandomNumber(k) || (l_tries++ >= MAX_TRIES))
@@ -1215,15 +1215,15 @@ static inline int ecdsa_sign(const uint8_t p_privateKey[ECC_BYTES], const uint8_
 		{
 			continue;
 		}
-	
+
 		if(vli_cmp(curve_n, k) != 1)
 		{
 			vli_sub(k, k, curve_n);
 		}
-	
+
 		/* tmp = k * G */
 		EccPoint_mult(&p, &curve_G, k, NULL);
-	
+
 		/* r = x1 (mod n) */
 		if(vli_cmp(curve_n, p.x) != 1)
 		{
@@ -1232,7 +1232,7 @@ static inline int ecdsa_sign(const uint8_t p_privateKey[ECC_BYTES], const uint8_
 	} while(vli_isZero(p.x));
 
 	ecc_native2bytes(p_signature, p.x);
-	
+
 	ecc_bytes2native(l_tmp, p_privateKey);
 	vli_modMult(l_s, p.x, l_tmp, curve_n); /* s = r*d */
 	ecc_bytes2native(l_tmp, p_hash);
@@ -1240,7 +1240,7 @@ static inline int ecdsa_sign(const uint8_t p_privateKey[ECC_BYTES], const uint8_
 	vli_modInv(k, k, curve_n); /* k = 1 / k */
 	vli_modMult(l_s, l_s, k, curve_n); /* s = (e + r*d) / k */
 	ecc_native2bytes(p_signature + ECC_BYTES, l_s);
-	
+
 	return 1;
 }
 
@@ -1254,18 +1254,18 @@ static inline int ecdsa_verify(const uint8_t p_publicKey[ECC_BYTES+1], const uin
 	uint64_t tx[NUM_ECC_DIGITS];
 	uint64_t ty[NUM_ECC_DIGITS];
 	uint64_t tz[NUM_ECC_DIGITS];
-	
+
 	uint64_t l_r[NUM_ECC_DIGITS], l_s[NUM_ECC_DIGITS];
-	
+
 	ecc_point_decompress(&l_public, p_publicKey);
 	ecc_bytes2native(l_r, p_signature);
 	ecc_bytes2native(l_s, p_signature + ECC_BYTES);
-	
+
 	if(vli_isZero(l_r) || vli_isZero(l_s))
 	{ /* r, s must not be 0. */
 		return 0;
 	}
-	
+
 	if(vli_cmp(curve_n, l_r) != 1 || vli_cmp(curve_n, l_s) != 1)
 	{ /* r, s must be < n. */
 		return 0;
@@ -1276,7 +1276,7 @@ static inline int ecdsa_verify(const uint8_t p_publicKey[ECC_BYTES+1], const uin
 	ecc_bytes2native(u1, p_hash);
 	vli_modMult(u1, u1, z, curve_n); /* u1 = e/s */
 	vli_modMult(u2, l_r, z, curve_n); /* u2 = r/s */
-	
+
 	/* Calculate l_sum = G + Q. */
 	vli_set(l_sum.x, l_public.x);
 	vli_set(l_sum.y, l_public.y);
@@ -1286,11 +1286,11 @@ static inline int ecdsa_verify(const uint8_t p_publicKey[ECC_BYTES+1], const uin
 	XYcZ_add(tx, ty, l_sum.x, l_sum.y);
 	vli_modInv(z, z, curve_p); /* Z = 1/Z */
 	apply_z(l_sum.x, l_sum.y, z);
-	
+
 	/* Use Shamir's trick to calculate u1*G + u2*Q */
 	EccPoint *l_points[4] = {NULL, &curve_G, &l_public, &l_sum};
 	uint l_numBits = umax(vli_numBits(u1), vli_numBits(u2));
-	
+
 	EccPoint *l_point = l_points[(!!vli_testBit(u1, l_numBits-1)) | ((!!vli_testBit(u2, l_numBits-1)) << 1)];
 	vli_set(rx, l_point->x);
 	vli_set(ry, l_point->y);
@@ -1301,7 +1301,7 @@ static inline int ecdsa_verify(const uint8_t p_publicKey[ECC_BYTES+1], const uin
 	for(i = l_numBits - 2; i >= 0; --i)
 	{
 		EccPoint_double_jacobian(rx, ry, z);
-		
+
 		int l_index = (!!vli_testBit(u1, i)) | ((!!vli_testBit(u2, i)) << 1);
 		EccPoint *l_point = l_points[l_index];
 		if(l_point)
@@ -1317,7 +1317,7 @@ static inline int ecdsa_verify(const uint8_t p_publicKey[ECC_BYTES+1], const uin
 
 	vli_modInv(z, z, curve_p); /* Z = 1/Z */
 	apply_z(rx, ry, z);
-	
+
 	/* v = x1 (mod n) */
 	if(vli_cmp(curve_n, rx) != 1)
 	{

+ 70 - 80
node/Identity.cpp

@@ -40,7 +40,8 @@ namespace ZeroTier {
 namespace {
 
 // These can't be changed without a new identity type. They define the
-// parameters of the hashcash hashing/searching algorithm.
+// parameters of the hashcash hashing/searching algorithm for type 0
+// identities.
 #define ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN 17
 #define ZT_IDENTITY_GEN_MEMORY 2097152
 
@@ -86,9 +87,9 @@ struct _Identity_generate_cond
 {
 	inline _Identity_generate_cond() {}
 	inline _Identity_generate_cond(unsigned char *sb,char *gm) : digest(sb),genmem(gm) {}
-	inline bool operator()(const C25519::Pair &kp) const
+	inline bool operator()(const uint8_t pub[ZT_C25519_PUBLIC_KEY_LEN]) const
 	{
-		_computeMemoryHardHash(kp.pub.data,ZT_C25519_PUBLIC_KEY_LEN,digest,genmem);
+		_computeMemoryHardHash(pub,ZT_C25519_PUBLIC_KEY_LEN,digest,genmem);
 		return (digest[0] < ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN);
 	}
 	unsigned char *digest;
@@ -100,71 +101,44 @@ struct _Identity_generate_cond
 void Identity::generate(const Type t)
 {
 	uint8_t digest[64];
+
+	_type = t;
+	_hasPrivate = true;
+
 	char *const genmem = new char[ZT_IDENTITY_GEN_MEMORY];
-	switch(t) {
-		case C25519: {
-			C25519::Pair kp;
-			do {
-				kp = C25519::generateSatisfying(_Identity_generate_cond(digest,genmem));
-				_address.setTo(digest + 59,ZT_ADDRESS_LENGTH); // last 5 bytes are address
-			} while (_address.isReserved());
-			memcpy(_k.t0.pub.data,kp.pub.data,ZT_C25519_PUBLIC_KEY_LEN);
-			memcpy(_k.t0.priv.data,kp.priv.data,ZT_C25519_PRIVATE_KEY_LEN);
-			_type = C25519;
-			_hasPrivate = true;
-		}	break;
-		case P384: {
-			do {
-				ECC384GenerateKey(_k.t1.pub,_k.t1.priv);
-				_computeMemoryHardHash(_k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE,digest,genmem);
-				if (digest[0] >= ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN)
-					continue;
-				_address.setTo(digest + 59,ZT_ADDRESS_LENGTH);
-			} while (_address.isReserved());
-			_type = P384;
-			_hasPrivate = true;
-		}	break;
-	}
+	do {
+		C25519::generateSatisfying(_Identity_generate_cond(digest,genmem),_pub.c25519,_priv.c25519);
+		_address.setTo(digest + 59,ZT_ADDRESS_LENGTH); // last 5 bytes are address
+	} while (_address.isReserved());
 	delete [] genmem;
+
+	if (t == P384)
+		ECC384GenerateKey(_pub.p384,_priv.p384);
 }
 
 bool Identity::locallyValidate() const
 {
 	if (_address.isReserved())
 		return false;
-	uint8_t digest[64];
+
 	char *genmem = nullptr;
 	try {
+		uint8_t digest[64];
 		genmem = new char[ZT_IDENTITY_GEN_MEMORY];
-		switch(_type) {
-			case C25519:
-				_computeMemoryHardHash(_k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN,digest,genmem);
-				break;
-			case P384:
-				_computeMemoryHardHash(_k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE,digest,genmem);
-				break;
-			default:
-				return false;
-		}
+		_computeMemoryHardHash(_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN,digest,genmem);
 		delete [] genmem;
-		unsigned char addrb[5];
-		_address.copyTo(addrb,5);
-		return (
-			(digest[0] < ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN)&&
-			(digest[59] == addrb[0])&&
-			(digest[60] == addrb[1])&&
-			(digest[61] == addrb[2])&&
-			(digest[62] == addrb[3])&&
-			(digest[63] == addrb[4]));
+		return ((_address == Address(digest + 59,ZT_ADDRESS_LENGTH))&&(!_address.isReserved())&&(digest[0] < ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN));
 	} catch ( ... ) {
 		if (genmem) delete [] genmem;
-		return false;
 	}
+
+	return false;
 }
 
 char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const
 {
 	switch(_type) {
+
 		case C25519: {
 			char *p = buf;
 			Utils::hex10(_address.toInt(),p);
@@ -172,16 +146,17 @@ char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_
 			*(p++) = ':';
 			*(p++) = '0';
 			*(p++) = ':';
-			Utils::hex(_k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN,p);
+			Utils::hex(_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN,p);
 			p += ZT_C25519_PUBLIC_KEY_LEN * 2;
 			if ((_hasPrivate)&&(includePrivate)) {
 				*(p++) = ':';
-				Utils::hex(_k.t0.priv.data,ZT_C25519_PRIVATE_KEY_LEN,p);
+				Utils::hex(_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN,p);
 				p += ZT_C25519_PRIVATE_KEY_LEN * 2;
 			}
 			*p = (char)0;
 			return buf;
 		} break;
+
 		case P384: {
 			char *p = buf;
 			Utils::hex10(_address.toInt(),p);
@@ -189,38 +164,43 @@ char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_
 			*(p++) = ':';
 			*(p++) = '1';
 			*(p++) = ':';
-			Utils::hex(_k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE,p);
-			p += ZT_ECC384_PUBLIC_KEY_SIZE * 2;
+			int el = Utils::b32e((const uint8_t *)(&_pub),ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE,p,(unsigned int)(ZT_IDENTITY_STRING_BUFFER_LENGTH - (uintptr_t)(p - buf)));
+			if (el <= 0) return nullptr;
+			p += el;
 			if ((_hasPrivate)&&(includePrivate)) {
 				*(p++) = ':';
-				Utils::hex(_k.t1.priv,ZT_ECC384_PRIVATE_KEY_SIZE,p);
-				p += ZT_ECC384_PRIVATE_KEY_SIZE * 2;
+				el = Utils::b32e((const uint8_t *)(&_pub),ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE,p,(unsigned int)(ZT_IDENTITY_STRING_BUFFER_LENGTH - (uintptr_t)(p - buf)));
+				if (el <= 0) return nullptr;
+				p += el;
 			}
 			*p = (char)0;
 			return buf;
 		}	break;
+
 	}
 	return nullptr;
 }
 
 bool Identity::fromString(const char *str)
 {
+	_hasPrivate = false;
+
 	if (!str) {
 		_address.zero();
 		return false;
 	}
+
 	char tmp[ZT_IDENTITY_STRING_BUFFER_LENGTH];
 	if (!Utils::scopy(tmp,sizeof(tmp),str)) {
 		_address.zero();
 		return false;
 	}
 
-	_hasPrivate = false;
-
 	int fno = 0;
 	char *saveptr = (char *)0;
-	for(char *f=Utils::stok(tmp,":",&saveptr);(f);f=Utils::stok((char *)0,":",&saveptr)) {
+	for(char *f=Utils::stok(tmp,":",&saveptr);((f)&&(fno < 4));f=Utils::stok((char *)0,":",&saveptr)) {
 		switch(fno++) {
+
 			case 0:
 				_address = Address(Utils::hexStrToU64(f));
 				if (_address.isReserved()) {
@@ -228,6 +208,7 @@ bool Identity::fromString(const char *str)
 					return false;
 				}
 				break;
+
 			case 1:
 				if ((f[0] == '0')&&(!f[1])) {
 					_type = C25519;
@@ -238,47 +219,56 @@ bool Identity::fromString(const char *str)
 					return false;
 				}
 				break;
+
 			case 2:
 				switch(_type) {
+
 					case C25519:
-						if (Utils::unhex(f,_k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN) != ZT_C25519_PUBLIC_KEY_LEN) {
+						if (Utils::unhex(f,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) != ZT_C25519_PUBLIC_KEY_LEN) {
 							_address.zero();
 							return false;
 						}
 						break;
+
 					case P384:
-						if (Utils::unhex(f,_k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE) != ZT_ECC384_PUBLIC_KEY_SIZE) {
+						if (Utils::b32d(f,(uint8_t *)(&_pub),ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE) != (ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE)) {
 							_address.zero();
 							return false;
 						}
 						break;
+
 				}
 				break;
+
 			case 3:
-				switch(_type) {
-					case C25519:
-						if (Utils::unhex(f,_k.t0.priv.data,ZT_C25519_PRIVATE_KEY_LEN) != ZT_C25519_PRIVATE_KEY_LEN) {
-							_address.zero();
-							return false;
-						} else {
-							_hasPrivate = true;
-						}
-						break;
-					case P384:
-						if (Utils::unhex(f,_k.t1.priv,ZT_ECC384_PRIVATE_KEY_SIZE) != ZT_ECC384_PRIVATE_KEY_SIZE) {
-							_address.zero();
-							return false;
-						} else {
-							_hasPrivate = true;
-						}
-						break;
+				if (strlen(f) > 1) {
+					switch(_type) {
+
+						case C25519:
+							if (Utils::unhex(f,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN) != ZT_C25519_PRIVATE_KEY_LEN) {
+								_address.zero();
+								return false;
+							} else {
+								_hasPrivate = true;
+							}
+							break;
+
+						case P384:
+							if (Utils::b32d(f,(uint8_t *)(&_priv),ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE) != (ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE)) {
+								_address.zero();
+								return false;
+							} else {
+								_hasPrivate = true;
+							}
+							break;
+
+					}
+					break;
 				}
-				break;
-			default:
-				_address.zero();
-				return false;
+
 		}
 	}
+
 	if (fno < 3) {
 		_address.zero();
 		return false;

+ 81 - 79
node/Identity.hpp

@@ -38,7 +38,7 @@
 #include "SHA512.hpp"
 #include "ECC384.hpp"
 
-#define ZT_IDENTITY_STRING_BUFFER_LENGTH 384
+#define ZT_IDENTITY_STRING_BUFFER_LENGTH 512
 
 namespace ZeroTier {
 
@@ -60,8 +60,8 @@ public:
 	 */
 	enum Type
 	{
-		C25519 = ZT_CRYPTO_ALG_C25519, // Type 0 -- Curve25519 and Ed25519 (1.0 and 2.0, default)
-		P384 = ZT_CRYPTO_ALG_P384      // Type 1 -- NIST P-384 ECDH and ECDSA (2.0+ only)
+		C25519 = ZT_CRYPTO_ALG_C25519, // Type 0 -- Curve25519 and Ed25519 (1.x and 2.x, default)
+		P384 = ZT_CRYPTO_ALG_P384      // Type 1 -- NIST P-384 with linked Curve25519 and Ed25519 secondaries (2.x+)
 	};
 
 	inline Identity() { memset(reinterpret_cast<void *>(this),0,sizeof(Identity)); }
@@ -78,6 +78,9 @@ public:
 
 	inline ~Identity() { Utils::burn(reinterpret_cast<void *>(this),sizeof(Identity)); }
 
+	/**
+	 * Set identity to NIL value (all zero)
+	 */
 	inline void zero() { Utils::burn(reinterpret_cast<void *>(this),sizeof(Identity)); }
 
 	inline Identity &operator=(const Identity &id)
@@ -95,7 +98,7 @@ public:
 	 * Generate a new identity (address, key pair)
 	 *
 	 * This is a time consuming operation.
-	 * 
+	 *
 	 * @param t Type of identity to generate
 	 */
 	void generate(const Type t);
@@ -123,31 +126,10 @@ public:
 		if (_hasPrivate) {
 			switch(_type) {
 				case C25519:
-					SHA512(sha,_k.t0.priv.data,ZT_C25519_PRIVATE_KEY_LEN);
+					SHA512(sha,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN);
 					return true;
 				case P384:
-					SHA512(sha,_k.t1.priv,ZT_ECC384_PRIVATE_KEY_SIZE);
-					return true;
-			}
-		}
-		return false;
-	}
-
-	/**
-	 * Compute the SHA512 hash of our public key
-	 * 
-	 * @param sha Buffer to receive hash bytes
-	 * @return True on success, false if identity is empty or invalid
-	 */
-	inline bool sha512PublicKey(void *sha) const
-	{
-		if (_hasPrivate) {
-			switch(_type) {
-				case C25519:
-					SHA512(sha,_k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN);
-					return true;
-				case P384:
-					SHA512(sha,_k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE);
+					SHA512(sha,&_priv,ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE);
 					return true;
 			}
 		}
@@ -159,7 +141,7 @@ public:
 	 *
 	 * The signature buffer should be large enough for the largest
 	 * signature, which is currently 96 bytes.
-	 * 
+	 *
 	 * @param data Data to sign
 	 * @param len Length of data
 	 * @param sig Buffer to receive signature
@@ -168,25 +150,33 @@ public:
 	 */
 	inline unsigned int sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const
 	{
-		uint8_t h[48];
+		uint8_t h[48 + ZT_C25519_PUBLIC_KEY_LEN];
 		if (!_hasPrivate)
 			return 0;
 		switch(_type) {
+
 			case C25519:
 				if (siglen < ZT_C25519_SIGNATURE_LEN)
 					return 0;
-				C25519::sign(_k.t0.priv,_k.t0.pub,data,len,sig);
+				C25519::sign(_priv.c25519,_pub.c25519,data,len,sig);
 				return ZT_C25519_SIGNATURE_LEN;
+
 			case P384:
 				if (siglen < ZT_ECC384_SIGNATURE_SIZE)
 					return 0;
+				// Include C25519 public key in input for P-384 signature so the two keys are "bound
+				// together" and cannot be decoupled in the same identity. An identity can have the
+				// same C25519 key but a different P-384 key and have the same address, but this
+				// means its signatures and key agreements will be different.
 				SHA384(h,data,len);
-				ECC384ECDSASign(_k.t1.priv,h,(uint8_t *)sig);
+				memcpy(h + 48,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
+				SHA384(h,h,48 + ZT_C25519_PUBLIC_KEY_LEN);
+				ECC384ECDSASign(_priv.p384,h,(uint8_t *)sig);
 				return ZT_ECC384_SIGNATURE_SIZE;
 		}
 		return 0;
 	}
-	
+
 	/**
 	 * Verify a message signature against this identity
 	 *
@@ -200,18 +190,20 @@ public:
 	{
 		switch(_type) {
 			case C25519:
-				return C25519::verify(_k.t0.pub,data,len,sig,siglen);
+				return C25519::verify(_pub.c25519,data,len,sig,siglen);
 			case P384:
 				if (siglen == ZT_ECC384_SIGNATURE_SIZE) {
-					uint8_t h[48];
+					uint8_t h[48 + ZT_C25519_PUBLIC_KEY_LEN];
 					SHA384(h,data,len);
-					return ECC384ECDSAVerify(_k.t1.pub,h,(const uint8_t *)sig);
+					memcpy(h + 48,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
+					SHA384(h,h,48 + ZT_C25519_PUBLIC_KEY_LEN);
+					return ECC384ECDSAVerify(_pub.p384,h,(const uint8_t *)sig);
 				}
 				break;
 		}
 		return false;
 	}
-	
+
 	/**
 	 * Shortcut method to perform key agreement with another identity
 	 *
@@ -219,34 +211,43 @@ public:
 	 *
 	 * @param id Identity to agree with
 	 * @param key Result parameter to fill with key bytes
-	 * @param klen Length of key in bytes
 	 * @return Was agreement successful?
 	 */
-	inline bool agree(const Identity &id,void *key,unsigned int klen) const
+	inline bool agree(const Identity &id,uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) const
 	{
-		uint8_t ecc384RawSecret[ZT_ECC384_SHARED_SECRET_SIZE];
-		uint8_t h[48];
+		uint8_t rawkey[128];
+		uint8_t h[64];
 		if (_hasPrivate) {
 			switch(_type) {
+
 				case C25519:
-					C25519::agree(_k.t0.priv,id._k.t0.pub,key,klen);
+					C25519::agree(_priv.c25519,id._pub.c25519,rawkey);
+					SHA512(h,rawkey,ZT_C25519_SHARED_KEY_LEN);
+					memcpy(key,h,32);
 					return true;
+
 				case P384:
-					ECC384ECDH(id._k.t1.pub,_k.t1.priv,ecc384RawSecret);
-					SHA384(h,ecc384RawSecret,sizeof(ecc384RawSecret));
-					for(unsigned int i=0,hi=0;i<klen;++i) {
-						if (hi == 48) {
-							hi = 0;
-							SHA384(h,h,48);
-						}
-						((uint8_t *)key)[i] = h[hi++];
+					if (id._type == P384) {
+						// Perform key agreement over both curves for the same reason that C25519 public
+						// keys are included in P-384 signature inputs: to bind the keys together so
+						// that a type 1 identity with the same C25519 public key (and therefore address)
+						// but a different P-384 key will not work.
+						C25519::agree(_priv.c25519,id._pub.c25519,rawkey);
+						ECC384ECDH(id._pub.p384,_priv.p384,rawkey + ZT_C25519_SHARED_KEY_LEN);
+						SHA384(h,rawkey,ZT_C25519_SHARED_KEY_LEN + ZT_ECC384_SHARED_SECRET_SIZE);
+						for(unsigned int i=0;i<32;++i)
+							key[i] = h[i];
+						for(unsigned int i=0;i<16;++i)
+							key[i] ^= h[32+i];
+						return true;
 					}
-					return true;
+					return false;
+
 			}
 		}
 		return false;
 	}
-	
+
 	/**
 	 * @return This identity's address
 	 */
@@ -267,10 +268,10 @@ public:
 
 			case C25519:
 				b.append((uint8_t)C25519);
-				b.append(_k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN);
+				b.append(_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
 				if ((_hasPrivate)&&(includePrivate)) {
 					b.append((uint8_t)ZT_C25519_PRIVATE_KEY_LEN);
-					b.append(_k.t0.priv.data,ZT_C25519_PRIVATE_KEY_LEN);
+					b.append(_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN);
 				} else {
 					b.append((uint8_t)0);
 				}
@@ -278,10 +279,12 @@ public:
 
 			case P384:
 				b.append((uint8_t)P384);
-				b.append(_k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE);
+				b.append(_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
+				b.append(_pub.p384,ZT_ECC384_PUBLIC_KEY_SIZE);
 				if ((_hasPrivate)&&(includePrivate)) {
-					b.append((uint8_t)ZT_ECC384_PRIVATE_KEY_SIZE);
-					b.append(_k.t1.priv,ZT_ECC384_PRIVATE_KEY_SIZE);
+					b.append((uint8_t)(ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE));
+					b.append(_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN);
+					b.append(_priv.p384,ZT_ECC384_PRIVATE_KEY_SIZE);
 				} else {
 					b.append((uint8_t)0);
 				}
@@ -312,37 +315,38 @@ public:
 		_address.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
 		p += ZT_ADDRESS_LENGTH;
 
-		_type = (Type)b[p++];
-		switch(_type) {
+		switch((_type = (Type)b[p++])) {
 
 			case C25519:
-				memcpy(_k.t0.pub.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN);
+				memcpy(_pub.c25519,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN);
 				p += ZT_C25519_PUBLIC_KEY_LEN;
 				pkl = (unsigned int)b[p++];
 				if (pkl) {
 					if (pkl != ZT_C25519_PRIVATE_KEY_LEN)
 						throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
 					_hasPrivate = true;
-					memcpy(_k.t0.priv.data,b.field(p,ZT_C25519_PRIVATE_KEY_LEN),ZT_C25519_PRIVATE_KEY_LEN);
+					memcpy(_priv.c25519,b.field(p,ZT_C25519_PRIVATE_KEY_LEN),ZT_C25519_PRIVATE_KEY_LEN);
 					p += ZT_C25519_PRIVATE_KEY_LEN;
 				} else {
-					memset(_k.t0.priv.data,0,ZT_C25519_PRIVATE_KEY_LEN);
 					_hasPrivate = false;
 				}
 				break;
 
 			case P384:
-				memcpy(_k.t0.pub.data,b.field(p,ZT_ECC384_PUBLIC_KEY_SIZE),ZT_ECC384_PUBLIC_KEY_SIZE);
+				memcpy(_pub.c25519,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN);
+				p += ZT_C25519_PUBLIC_KEY_LEN;
+				memcpy(_pub.p384,b.field(p,ZT_ECC384_PUBLIC_KEY_SIZE),ZT_ECC384_PUBLIC_KEY_SIZE);
 				p += ZT_ECC384_PUBLIC_KEY_SIZE;
 				pkl = (unsigned int)b[p++];
 				if (pkl) {
-					if (pkl != ZT_ECC384_PRIVATE_KEY_SIZE)
+					if (pkl != (ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE))
 						throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
 					_hasPrivate = true;
-					memcpy(_k.t1.priv,b.field(p,ZT_ECC384_PRIVATE_KEY_SIZE),ZT_ECC384_PRIVATE_KEY_SIZE);
+					memcpy(_priv.c25519,b.field(p,ZT_C25519_PRIVATE_KEY_LEN),ZT_C25519_PRIVATE_KEY_LEN);
+					p += ZT_C25519_PRIVATE_KEY_LEN;
+					memcpy(_priv.p384,b.field(p,ZT_ECC384_PRIVATE_KEY_SIZE),ZT_ECC384_PRIVATE_KEY_SIZE);
 					p += ZT_ECC384_PRIVATE_KEY_SIZE;
 				} else {
-					memset(_k.t1.priv,0,ZT_ECC384_PRIVATE_KEY_SIZE);
 					_hasPrivate = false;
 				}
 				break;
@@ -385,9 +389,9 @@ public:
 		if ((_address == id._address)&&(_type == id._type)) {
 			switch(_type) {
 				case C25519:
-					return (memcmp(_k.t0.pub.data,id._k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN) == 0);
+					return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) == 0);
 				case P384:
-					return (memcmp(_k.t1.pub,id._k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE) == 0);
+					return (memcmp(&_pub,&id._pub,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE) == 0);
 				default:
 					return false;
 			}
@@ -404,9 +408,9 @@ public:
 			if (_type == id._type) {
 				switch(_type) {
 					case C25519:
-						return (memcmp(_k.t0.pub.data,id._k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN) < 0);
+						return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) < 0);
 					case P384:
-						return (memcmp(_k.t1.pub,id._k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE) < 0);
+						return (memcmp(&_pub,&id._pub,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE) < 0);
 				}
 			}
 		}
@@ -421,18 +425,16 @@ public:
 
 private:
 	Address _address;
-	union {
-		struct {
-			C25519::Public pub;
-			C25519::Private priv;
-		} t0;
-		struct {
-			uint8_t pub[ZT_ECC384_PUBLIC_KEY_SIZE];
-			uint8_t priv[ZT_ECC384_PRIVATE_KEY_SIZE];
-		} t1;
-	} _k;
 	Type _type;
 	bool _hasPrivate;
+	ZT_PACKED_STRUCT(struct { // don't re-order these
+		uint8_t c25519[ZT_C25519_PRIVATE_KEY_LEN];
+		uint8_t p384[ZT_ECC384_PRIVATE_KEY_SIZE];
+	}) _priv;
+	ZT_PACKED_STRUCT(struct { // don't re-order these
+		uint8_t c25519[ZT_C25519_PUBLIC_KEY_LEN];
+		uint8_t p384[ZT_ECC384_PUBLIC_KEY_SIZE];
+	}) _pub;
 };
 
 } // namespace ZeroTier

+ 1 - 1
node/IncomingPacket.cpp

@@ -295,7 +295,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool
 					return true;
 
 				uint8_t key[ZT_PEER_SECRET_KEY_LENGTH];
-				if (RR->identity.agree(id,key,ZT_PEER_SECRET_KEY_LENGTH)) {
+				if (RR->identity.agree(id,key)) {
 					if (dearmor(key)) { // ensure packet is authentic, otherwise drop
 						RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"address collision");
 						Packet outp(id.address(),RR->identity.address(),Packet::VERB_ERROR);

+ 2 - 0
node/InetAddress.hpp

@@ -110,6 +110,8 @@ struct InetAddress : public sockaddr_storage
 	inline InetAddress(const uint32_t ipv4,unsigned int port) { this->set(&ipv4,4,port); }
 	inline InetAddress(const char *ipSlashPort) { this->fromString(ipSlashPort); }
 
+	inline void clear() { memset(this,0,sizeof(InetAddress)); }
+
 	inline InetAddress &operator=(const InetAddress &a)
 	{
 		if (&a != this)

+ 2 - 3
node/NetworkConfig.hpp

@@ -213,9 +213,8 @@ namespace ZeroTier {
  * This is a memcpy()'able structure and is safe (in a crash sense) to modify
  * without locks.
  */
-class NetworkConfig
+struct NetworkConfig
 {
-public:
 	inline NetworkConfig() :
 		networkId(0),
 		timestamp(0),
@@ -278,7 +277,7 @@ public:
 		interference with lwIP's TCP congestion algorithm. Compression is also disabled
 		for some NAS builds due to the usage of low-performance processors in certain
 		older and budget models. */
-		return false;
+		return true;
 #endif
 	}
 

+ 1 - 1
node/Peer.cpp

@@ -74,7 +74,7 @@ Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Ident
 	_lastAggregateStatsReport(0),
 	_lastAggregateAllocation(0)
 {
-	if (!myIdentity.agree(peerIdentity,_key,ZT_PEER_SECRET_KEY_LENGTH))
+	if (!myIdentity.agree(peerIdentity,_key))
 		throw ZT_EXCEPTION_INVALID_ARGUMENT;
 }
 

+ 44 - 25
node/Root.hpp

@@ -40,18 +40,18 @@ namespace ZeroTier {
 
 /**
  * A root entry pointing to a node capable of global identity lookup and indirect transit
- * 
+ *
  * Root entries point to DNS records that contain TXT entries that decode to Locator objects
  * pointing to actual root nodes. A default root identity and static addresses can also be
  * provided as fallback if DNS is not available.
- * 
+ *
  * Note that root identities can change if DNS returns a different result, but that DNS entries
  * are authenticated using their own signature scheme. This allows a root DNS name to serve
  * up different roots based on factors like location or relative load of different roots.
- * 
+ *
  * It's also possible to create a root with no DNS and no DNS validator public key. This root
  * will be a static entry pointing to a single root identity and set of physical addresses.
- * 
+ *
  * This object is thread-safe and may be concurrently accessed and updated.
  */
 class Root
@@ -62,7 +62,7 @@ public:
 
 	/**
 	 * Create a new root entry
-	 * 
+	 *
 	 * @param dn DNS name
 	 * @param dnspk DNS public key for record validation
 	 * @param dnspksize Size of DNS public key (currently always the size of a NIST P-384 point compressed public key)
@@ -129,7 +129,7 @@ public:
 	}
 
 	/**
-	 * @return DNS name for this root (or empty string if none)
+	 * @return DNS name for this root or empty string if static entry with no DNS
 	 */
 	inline const Str dnsName() const
 	{
@@ -138,7 +138,7 @@ public:
 	}
 
 	/**
-	 * @return Latest locator
+	 * @return Latest locator or NIL locator object if none
 	 */
 	inline Locator locator() const
 	{
@@ -147,7 +147,7 @@ public:
 	}
 
 	/**
-	 * @return Timestamp of latest retrieved locator
+	 * @return Timestamp of latest retrieved locator or 0 if none
 	 */
 	inline int64_t locatorTimestamp() const
 	{
@@ -155,22 +155,6 @@ public:
 		return _lastFetchedLocator.timestamp();
 	}
 
-	/**
-	 * Pick a random physical address
-	 * 
-	 * @return Physical address or InetAddress::NIL if none are available
-	 */
-	inline const InetAddress randomPhysicalAddress() const
-	{
-		Mutex::Lock l(_lock);
-		if (_lastFetchedLocator.phy().empty()) {
-			if (_defaultAddresses.empty())
-				return InetAddress::NIL;
-			return _defaultAddresses[(unsigned long)Utils::random() % (unsigned long)_defaultAddresses.size()];
-		}
-		return _lastFetchedLocator.phy()[(unsigned long)Utils::random() % (unsigned long)_lastFetchedLocator.phy().size()];
-	}
-
 	/**
 	 * Update locator, returning true if new locator is valid and newer than existing
 	 */
@@ -197,7 +181,7 @@ public:
 			if (_dnsPublicKeySize != ZT_ECC384_PUBLIC_KEY_SIZE)
 				return false;
 			Locator loc;
-			if (!loc.decodeTxtRecords(start,end,_dnsPublicKey))
+			if (!loc.decodeTxtRecords(start,end,_dnsPublicKey)) // also does verify()
 				return false;
 			if ((loc.phy().size() > 0)&&(loc.timestamp() > _lastFetchedLocator.timestamp())) {
 				_lastFetchedLocator = loc;
@@ -208,6 +192,41 @@ public:
 		return false;
 	}
 
+	/**
+	 * Pick random IPv4 and IPv6 addresses for this root
+	 *
+	 * @param v4 Filled with V4 address or NIL if none found
+	 * @param v6 Filled with V6 address or NIL if none found
+	 */
+	inline void pickPhysical(InetAddress &v4,InetAddress &v6) const
+	{
+		v4.clear();
+		v6.clear();
+		std::vector<const InetAddress *> v4a,v6a;
+		Mutex::Lock l(_lock);
+		const std::vector<InetAddress> *const av = (_lastFetchedLocator) ? &(_lastFetchedLocator.phy()) : &_defaultAddresses;
+		for(std::vector<InetAddress>::const_iterator i(av->begin());i!=av->end();++i) {
+			switch(i->ss_family) {
+				case AF_INET:
+					v4a.push_back(&(*i));
+					break;
+				case AF_INET6:
+					v6a.push_back(&(*i));
+					break;
+			}
+		}
+		if (v4a.size() == 1) {
+			v4 = *v4a[0];
+		} else if (v4a.size() > 1) {
+			v4 = *v4a[(unsigned long)Utils::random() % (unsigned long)v4a.size()];
+		}
+		if (v6a.size() == 1) {
+			v6 = *v6a[0];
+		} else if (v6a.size() > 1) {
+			v6 = *v6a[(unsigned long)Utils::random() % (unsigned long)v6a.size()];
+		}
+	}
+
 private:
 	Identity _defaultIdentity;
 	std::vector<InetAddress> _defaultAddresses;

+ 0 - 46
node/SHA512.cpp

@@ -14,49 +14,6 @@ Public domain.
 #include "SHA512.hpp"
 #include "Utils.hpp"
 
-#ifdef __APPLE__
-#include <CommonCrypto/CommonDigest.h>
-#define ZT_HAVE_NATIVE_SHA512
-namespace ZeroTier {
-void SHA512(void *digest,const void *data,unsigned int len)
-{
-	CC_SHA512_CTX ctx;
-	CC_SHA512_Init(&ctx);
-	CC_SHA512_Update(&ctx,data,len);
-	CC_SHA512_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
-}
-void SHA384(void *digest,const void *data,unsigned int len)
-{
-	CC_SHA512_CTX ctx;
-	CC_SHA384_Init(&ctx);
-	CC_SHA384_Update(&ctx,data,len);
-	CC_SHA384_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
-}
-}
-#endif
-
-#ifdef ZT_USE_LIBCRYPTO
-#include <openssl/sha.h>
-#define ZT_HAVE_NATIVE_SHA512
-namespace ZeroTier {
-void SHA512(void *digest,const void *data,unsigned int len)
-{
-	SHA512_CTX ctx;
-	SHA512_Init(&ctx);
-	SHA512_Update(&ctx,data,len);
-	SHA512_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
-}
-void SHA384(void *digest,const void *data,unsigned int len)
-{
-	SHA512_CTX ctx;
-	SHA384_Init(&ctx);
-	SHA384_Update(&ctx,data,len);
-	SHA384_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
-}
-}
-#endif
-
-// If a platform-native SHA512 isn't available we use this 64-bit C version.
 #ifndef ZT_HAVE_NATIVE_SHA512
 
 namespace ZeroTier {
@@ -255,6 +212,3 @@ void SHA384(void *digest,const void *data,unsigned int len)
 } // namespace ZeroTier
 
 #endif // !ZT_HAVE_NATIVE_SHA512
-
-extern "C" void ZT_sha512internal(void *digest,const void *data,unsigned int len) { ZeroTier::SHA512(digest,data,len); }
-extern "C" void ZT_sha384internal(void *digest,const void *data,unsigned int len) { ZeroTier::SHA384(digest,data,len); }

+ 49 - 0
node/SHA512.hpp

@@ -27,12 +27,61 @@
 #ifndef ZT_SHA512_HPP
 #define ZT_SHA512_HPP
 
+#include "Constants.hpp"
+
+#ifdef __APPLE__
+#include <CommonCrypto/CommonDigest.h>
+#endif
+
+#ifdef ZT_USE_LIBCRYPTO
+#include <openssl/sha.h>
+#endif
+
 #define ZT_SHA512_DIGEST_LEN 64
+#define ZT_SHA384_DIGEST_LEN 48
 
 namespace ZeroTier {
 
+#ifdef __APPLE__
+#define ZT_HAVE_NATIVE_SHA512 1
+static inline void SHA512(void *digest,const void *data,unsigned int len)
+{
+	CC_SHA512_CTX ctx;
+	CC_SHA512_Init(&ctx);
+	CC_SHA512_Update(&ctx,data,len);
+	CC_SHA512_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
+}
+static inline void SHA384(void *digest,const void *data,unsigned int len)
+{
+	CC_SHA512_CTX ctx;
+	CC_SHA384_Init(&ctx);
+	CC_SHA384_Update(&ctx,data,len);
+	CC_SHA384_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
+}
+#endif
+
+#ifdef ZT_USE_LIBCRYPTO
+#define ZT_HAVE_NATIVE_SHA512 1
+static inline void SHA512(void *digest,const void *data,unsigned int len)
+{
+	SHA512_CTX ctx;
+	SHA512_Init(&ctx);
+	SHA512_Update(&ctx,data,len);
+	SHA512_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
+}
+static inline void SHA384(void *digest,const void *data,unsigned int len)
+{
+	SHA512_CTX ctx;
+	SHA384_Init(&ctx);
+	SHA384_Update(&ctx,data,len);
+	SHA384_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
+}
+#endif
+
+#ifndef ZT_HAVE_NATIVE_SHA512
 void SHA512(void *digest,const void *data,unsigned int len);
 void SHA384(void *digest,const void *data,unsigned int len);
+#endif
 
 } // namespace ZeroTier
 

+ 1 - 1
node/Trace.hpp

@@ -51,13 +51,13 @@ class Identity;
 class Peer;
 class Path;
 class Network;
-class NetworkConfig;
 class MAC;
 class CertificateOfMembership;
 class CertificateOfOwnership;
 class Revocation;
 class Tag;
 class Capability;
+struct NetworkConfig;
 
 /**
  * Remote tracing and trace logging handler

+ 33 - 41
node/Utils.cpp

@@ -154,9 +154,14 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
 {
 	static Mutex globalLock;
 	static Salsa20 s20;
-	static bool s20Initialized = false;
-	static uint8_t randomBuf[65536];
+	static bool initialized = false;
+	static uint8_t randomBuf[131072];
 	static unsigned int randomPtr = sizeof(randomBuf);
+#ifdef __WINDOWS__
+	static HCRYPTPROV cryptProvider = NULL;
+#else
+	static int devURandomFd = -1;
+#endif
 
 	Mutex::Lock _l(globalLock);
 
@@ -167,8 +172,9 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
 	 * a bit of extra entropy and further randomizing the result,and comes
 	 * at almost no cost and with no real downside if the random source is
 	 * good. */
-	if (!s20Initialized) {
-		s20Initialized = true;
+	if (unlikely(!initialized)) {
+		initialized = true;
+
 		uint64_t s20Key[4];
 		s20Key[0] = (uint64_t)time(nullptr);
 #ifdef __WINDOWS__
@@ -179,60 +185,46 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
 		s20Key[2] = (uint64_t)s20Key; // address of s20Key[]
 		s20Key[3] = (uint64_t)&s20; // address of s20
 		s20.init(s20Key,s20Key);
-	}
 
 #ifdef __WINDOWS__
+		if (!CryptAcquireContextA(&cryptProvider,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) {
+			fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to obtain WinCrypt context!\r\n");
+			exit(1);
+		}
+#else
+		devURandomFd = ::open("/dev/urandom",O_RDONLY);
+		if (devURandomFd < 0) {
+			fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\n");
+			exit(1);
+		}
+#endif
+	}
 
-	static HCRYPTPROV cryptProvider = NULL;
+#ifdef __WINDOWS__
 
 	for(unsigned int i=0;i<bytes;++i) {
-		if (randomPtr >= sizeof(randomBuf)) {
-			if (cryptProvider == NULL) {
-				if (!CryptAcquireContextA(&cryptProvider,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) {
-					fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to obtain WinCrypt context!\r\n");
-					exit(1);
-				}
-			}
+		if (unlikely(randomPtr >= sizeof(randomBuf))) {
+			randomPtr = 0;
 			if (!CryptGenRandom(cryptProvider,(DWORD)sizeof(randomBuf),(BYTE *)randomBuf)) {
 				fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() CryptGenRandom failed!\r\n");
 				exit(1);
 			}
-			randomPtr = 0;
 			s20.crypt12(randomBuf,randomBuf,sizeof(randomBuf));
-			s20.init(randomBuf,randomBuf);
 		}
 		((uint8_t *)buf)[i] = randomBuf[randomPtr++];
 	}
 
 #else // not __WINDOWS__
 
-	static int devURandomFd = -1;
-
-	if (devURandomFd < 0) {
-		devURandomFd = ::open("/dev/urandom",O_RDONLY);
-		if (devURandomFd < 0) {
-			fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\n");
-			exit(1);
-			return;
-		}
-	}
-
 	for(unsigned int i=0;i<bytes;++i) {
-		if (randomPtr >= sizeof(randomBuf)) {
-			for(;;) {
-				if ((int)::read(devURandomFd,randomBuf,sizeof(randomBuf)) != (int)sizeof(randomBuf)) {
-					::close(devURandomFd);
-					devURandomFd = ::open("/dev/urandom",O_RDONLY);
-					if (devURandomFd < 0) {
-						fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\n");
-						exit(1);
-						return;
-					}
-				} else break;
-			}
+		if (unlikely(randomPtr >= sizeof(randomBuf))) {
 			randomPtr = 0;
+			if ((int)::read(devURandomFd,randomBuf,sizeof(randomBuf)) != (int)sizeof(randomBuf)) {
+				::close(devURandomFd);
+				fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to read from /dev/urandom\n");
+				exit(1);
+			}
 			s20.crypt12(randomBuf,randomBuf,sizeof(randomBuf));
-			s20.init(randomBuf,randomBuf);
 		}
 		((uint8_t *)buf)[i] = randomBuf[randomPtr++];
 	}
@@ -265,7 +257,7 @@ int Utils::b32e(const uint8_t *data,int length,char *result,int bufSize)
       }
       int index = 0x1F & (buffer >> (bitsLeft - 5));
       bitsLeft -= 5;
-      result[count++] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"[index];
+      result[count++] = "abcdefghijklmnopqrstuvwxyZ234567"[index];
     }
   }
   if (count < bufSize) {
@@ -379,7 +371,7 @@ unsigned int Utils::b64d(const char *in,unsigned char *out,unsigned int outlen)
 					break;
 				case 1:
 					out[j++] |= (c >> 4) & 0x3;
-					out[j] = (c & 0xf) << 4; 
+					out[j] = (c & 0xf) << 4;
 					break;
 				case 2:
 					out[j++] |= (c >> 2) & 0xf;

+ 50 - 123
selftest.cpp

@@ -386,7 +386,41 @@ static int testCrypto()
 		::free((void *)bb);
 	}
 
-	std::cout << "[crypto] Testing ECC384 (NIST P-384)..." ZT_EOL_S;
+	std::cout << "[crypto] Testing C25519 and Ed25519... "; std::cout.flush();
+	for(int k=0;k<ZT_NUM_C25519_TEST_VECTORS;++k) {
+		uint8_t pub1[ZT_C25519_PUBLIC_KEY_LEN],pub2[ZT_C25519_PUBLIC_KEY_LEN],priv1[ZT_C25519_PRIVATE_KEY_LEN],priv2[ZT_C25519_PRIVATE_KEY_LEN];
+		memcpy(pub1,C25519_TEST_VECTORS[k].pub1,ZT_C25519_PUBLIC_KEY_LEN);
+		memcpy(priv1,C25519_TEST_VECTORS[k].priv1,ZT_C25519_PRIVATE_KEY_LEN);
+		memcpy(pub2,C25519_TEST_VECTORS[k].pub2,ZT_C25519_PUBLIC_KEY_LEN);
+		memcpy(priv2,C25519_TEST_VECTORS[k].priv2,ZT_C25519_PRIVATE_KEY_LEN);
+		uint8_t ag1[32],ag2[32];
+		C25519::agree(priv1,pub2,ag1);
+		C25519::agree(priv2,pub1,ag2);
+		if (memcmp(ag1,ag2,32) != 0) {
+			std::cout << "FAIL (1)" ZT_EOL_S;
+			return -1;
+		}
+		uint8_t ag1h[64];
+		SHA512(ag1h,ag1,32);
+		if (memcmp(ag1h,C25519_TEST_VECTORS[k].agreement,64) != 0) {
+			std::cout << "FAIL (2)" ZT_EOL_S;
+			return -1;
+		}
+		uint8_t sig1[ZT_C25519_SIGNATURE_LEN],sig2[ZT_C25519_SIGNATURE_LEN];
+		C25519::sign(priv1,pub1,ag1h,64,sig1);
+		C25519::sign(priv2,pub2,ag1h,64,sig2);
+		if (memcmp(sig1,C25519_TEST_VECTORS[k].agreementSignedBy1,64) != 0) {
+			std::cout << "FAIL (3)" ZT_EOL_S;
+			return -1;
+		}
+		if (memcmp(sig2,C25519_TEST_VECTORS[k].agreementSignedBy2,64) != 0) {
+			std::cout << "FAIL (4)" ZT_EOL_S;
+			return -1;
+		}
+	}
+	std::cout << "PASS" ZT_EOL_S;
+
+	std::cout << "[crypto] Testing NIST P-384..." ZT_EOL_S;
 	{
 		uint8_t p384pub[ZT_ECC384_PUBLIC_KEY_SIZE],p384priv[ZT_ECC384_PRIVATE_KEY_SIZE],p384sig[ZT_ECC384_SIGNATURE_SIZE],p384hash[ZT_ECC384_SIGNATURE_HASH_SIZE];
 		char p384hex[256];
@@ -432,116 +466,6 @@ static int testCrypto()
 		std::cout << "[crypto]   ECDSA Test Vector: PASS" ZT_EOL_S;
 	}
 
-	std::cout << "[crypto] Testing C25519 and Ed25519 against test vectors... "; std::cout.flush();
-	for(int k=0;k<ZT_NUM_C25519_TEST_VECTORS;++k) {
-		C25519::Pair p1,p2;
-		memcpy(p1.pub.data,C25519_TEST_VECTORS[k].pub1,ZT_C25519_PUBLIC_KEY_LEN);
-		memcpy(p1.priv.data,C25519_TEST_VECTORS[k].priv1,ZT_C25519_PRIVATE_KEY_LEN);
-		memcpy(p2.pub.data,C25519_TEST_VECTORS[k].pub2,ZT_C25519_PUBLIC_KEY_LEN);
-		memcpy(p2.priv.data,C25519_TEST_VECTORS[k].priv2,ZT_C25519_PRIVATE_KEY_LEN);
-		C25519::agree(p1.priv,p2.pub,buf1,64);
-		C25519::agree(p2.priv,p1.pub,buf2,64);
-		if (memcmp(buf1,buf2,64)) {
-			std::cout << "FAIL (1)" ZT_EOL_S;
-			return -1;
-		}
-		if (memcmp(buf1,C25519_TEST_VECTORS[k].agreement,64)) {
-			std::cout << "FAIL (2)" ZT_EOL_S;
-			return -1;
-		}
-		C25519::Signature sig1 = C25519::sign(p1,buf1,64);
-		if (memcmp(sig1.data,C25519_TEST_VECTORS[k].agreementSignedBy1,64)) {
-			std::cout << "FAIL (3)" ZT_EOL_S;
-			return -1;
-		}
-		C25519::Signature sig2 = C25519::sign(p2,buf1,64);
-		if (memcmp(sig2.data,C25519_TEST_VECTORS[k].agreementSignedBy2,64)) {
-			std::cout << "FAIL (4)" ZT_EOL_S;
-			return -1;
-		}
-	}
-	std::cout << "PASS" ZT_EOL_S;
-
-	std::cout << "[crypto] Testing C25519 ECC key agreement... "; std::cout.flush();
-	for(unsigned int i=0;i<100;++i) {
-		memset(buf1,64,sizeof(buf1));
-		memset(buf2,64,sizeof(buf2));
-		memset(buf3,64,sizeof(buf3));
-		C25519::Pair p1 = C25519::generate();
-		C25519::Pair p2 = C25519::generate();
-		C25519::Pair p3 = C25519::generate();
-		C25519::agree(p1.priv,p2.pub,buf1,64);
-		C25519::agree(p2.priv,p1.pub,buf2,64);
-		C25519::agree(p3.priv,p1.pub,buf3,64);
-		// p1<>p2 should equal p1<>p2
-		if (memcmp(buf1,buf2,64)) {
-			std::cout << "FAIL (1)" ZT_EOL_S;
-			return -1;
-		}
-		// p2<>p1 should not equal p3<>p1
-		if (!memcmp(buf2,buf3,64)) {
-			std::cout << "FAIL (2)" ZT_EOL_S;
-			return -1;
-		}
-	}
-	std::cout << "PASS" ZT_EOL_S;
-
-	std::cout << "[crypto] Benchmarking C25519 ECC key agreement... "; std::cout.flush();
-	C25519::Pair bp[8];
-	for(int k=0;k<8;++k)
-		bp[k] = C25519::generate();
-	uint64_t st = OSUtils::now();
-	for(unsigned int k=0;k<50;++k) {
-		C25519::agree(bp[~k & 7].priv,bp[k & 7].pub,buf1,64);
-	}
-	uint64_t et = OSUtils::now();
-	std::cout << ((double)(et - st) / 50.0) << "ms per agreement." ZT_EOL_S;
-
-	std::cout << "[crypto] Testing Ed25519 ECC signatures... "; std::cout.flush();
-	C25519::Pair didntSign = C25519::generate();
-	for(unsigned int i=0;i<10;++i) {
-		C25519::Pair p1 = C25519::generate();
-		for(unsigned int k=0;k<sizeof(buf1);++k)
-			buf1[k] = (unsigned char)rand();
-		C25519::Signature sig = C25519::sign(p1,buf1,sizeof(buf1));
-		if (!C25519::verify(p1.pub,buf1,sizeof(buf1),sig)) {
-			std::cout << "FAIL (1)" ZT_EOL_S;
-			return -1;
-		}
-		++buf1[17];
-		if (C25519::verify(p1.pub,buf1,sizeof(buf1),sig)) {
-			std::cout << "FAIL (2)" ZT_EOL_S;
-			return -1;
-		}
-		--buf1[17];
-		if (!C25519::verify(p1.pub,buf1,sizeof(buf1),sig)) {
-			std::cout << "FAIL (3)" ZT_EOL_S;
-			return -1;
-		}
-		if (C25519::verify(didntSign.pub,buf1,sizeof(buf1),sig)) {
-			std::cout << "FAIL (2)" ZT_EOL_S;
-			return -1;
-		}
-		for(unsigned int k=0;k<64;++k) {
-			C25519::Signature sig2(sig);
-			sig2.data[rand() % ZT_C25519_SIGNATURE_LEN] ^= (unsigned char)(1 << (rand() & 7));
-			if (C25519::verify(p1.pub,buf1,sizeof(buf1),sig2)) {
-				std::cout << "FAIL (5)" ZT_EOL_S;
-				return -1;
-			}
-		}
-	}
-	std::cout << "PASS" ZT_EOL_S;
-
-	std::cout << "[crypto] Benchmarking Ed25519 ECC signatures... "; std::cout.flush();
-	st = OSUtils::now();
-	for(int k=0;k<1000;++k) {
-		C25519::Signature sig;
-		C25519::sign(didntSign.priv,didntSign.pub,buf1,sizeof(buf1),sig.data);
-	}
-	et = OSUtils::now();
-	std::cout << ((double)(et - st) / 50.0) << "ms per signature." ZT_EOL_S;
-
 	return 0;
 }
 
@@ -577,18 +501,21 @@ static int testIdentity()
 	}
 	std::cout << "PASS (i.e. it failed)" ZT_EOL_S;
 
-	for(unsigned int k=0;k<4;++k) {
-		std::cout << "[identity] Generate identity... "; std::cout.flush();
-		uint64_t genstart = OSUtils::now();
-		id.generate(Identity::C25519);
-		uint64_t genend = OSUtils::now();
-		std::cout << "(took " << (genend - genstart) << "ms): " << id.toString(true,buf2) << ZT_EOL_S;
-		std::cout << "[identity] Locally validate identity: ";
-		if (id.locallyValidate()) {
-			std::cout << "PASS" ZT_EOL_S;
-		} else {
-			std::cout << "FAIL" ZT_EOL_S;
-			return -1;
+	static const Identity::Type idtypes[2] = { Identity::C25519,Identity::P384 };
+	for(unsigned int ti=0;ti<2;++ti) {
+		for(unsigned int k=0;k<2;++k) {
+			std::cout << "[identity] Generate identity (type " << (int)idtypes[ti] << ")... "; std::cout.flush();
+			uint64_t genstart = OSUtils::now();
+			id.generate(idtypes[ti]);
+			uint64_t genend = OSUtils::now();
+			std::cout << "(took " << (genend - genstart) << "ms): " << id.toString(true,buf2) << ZT_EOL_S;
+			std::cout << "[identity] Locally validate identity: ";
+			if (id.locallyValidate()) {
+				std::cout << "PASS" ZT_EOL_S;
+			} else {
+				std::cout << "FAIL" ZT_EOL_S;
+				return -1;
+			}
 		}
 	}