Browse Source

It builds, and basic stuff and crypto passes tests.

Adam Ierymenko 5 years ago
parent
commit
87890565f3
9 changed files with 126 additions and 61 deletions
  1. 1 1
      CMakeLists.txt
  2. 3 0
      Makefile
  3. 12 21
      node/FCV.hpp
  4. 8 0
      node/OS.hpp
  5. 4 4
      node/Poly1305.cpp
  6. 6 6
      node/Protocol.hpp
  7. 26 25
      node/Tests.cpp
  8. 4 4
      node/Utils.cpp
  9. 62 0
      node/Utils.hpp

+ 1 - 1
CMakeLists.txt

@@ -44,7 +44,7 @@ if (BUILD_CENTRAL_CONTROLLER)
 endif(BUILD_CENTRAL_CONTROLLER)
 
 if(CMAKE_BUILD_TYPE STREQUAL "Debug")
-	add_definitions(-DZT_TRACE)
+	add_definitions(-DZT_DEBUG)
 endif(CMAKE_BUILD_TYPE STREQUAL "Debug")
 
 if(WIN32)

+ 3 - 0
Makefile

@@ -10,6 +10,9 @@ all:	setup
 setup:
 	mkdir -p ${BUILDDIR} && cd ${BUILDDIR} && cmake .. -DCMAKE_BUILD_TYPE=Release
 
+setup-debug:
+	mkdir -p ${BUILDDIR} && cd ${BUILDDIR} && cmake .. -DCMAKE_BUILD_TYPE=Debug
+
 debug:
 	mkdir -p ${BUILDDIR} && cd ${BUILDDIR} && cmake .. -DCMAKE_BUILD_TYPE=Debug && $(MAKE)
 

+ 12 - 21
node/FCV.hpp

@@ -44,20 +44,17 @@ public:
 	typedef const T * const_iterator;
 
 	ZT_ALWAYS_INLINE FCV() noexcept : _s(0) {}
-
-	template<unsigned int C2>
-	ZT_ALWAYS_INLINE FCV(const FCV<T,C2> &v) : _s(0) { *this = v; }
+	ZT_ALWAYS_INLINE FCV(const FCV &v) : _s(0) { *this = v; }
 
 	ZT_ALWAYS_INLINE ~FCV() { this->clear(); }
 
-	template<unsigned int C2>
-	ZT_ALWAYS_INLINE FCV &operator=(const FCV<T,C2> &v)
+	ZT_ALWAYS_INLINE FCV &operator=(const FCV &v)
 	{
-		if ((C != C2)||(&v != this)) {
+		if (&v != this) {
 			this->clear();
-			const unsigned int vs = ((C2 > C) && (v._s > C)) ? C : v._s;
-			_s = vs;
-			for (unsigned int i = 0; i < vs; ++i)
+			const unsigned int s = v._s;
+			_s = s;
+			for (unsigned int i=0;i<s;++i)
 				new(reinterpret_cast<T *>(_m) + i) T(*(reinterpret_cast<const T *>(v._m) + i));
 		}
 		return *this;
@@ -245,8 +242,7 @@ public:
 		}
 	}
 
-	template<unsigned int C2>
-	ZT_ALWAYS_INLINE bool operator==(const FCV<T,C2> &v) const
+	ZT_ALWAYS_INLINE bool operator==(const FCV &v) const
 	{
 		if (_s == v._s) {
 			for(unsigned int i=0;i<_s;++i) {
@@ -257,16 +253,11 @@ public:
 		}
 		return false;
 	}
-	template<unsigned int C2>
-	ZT_ALWAYS_INLINE bool operator!=(const FCV<T,C2> &v) const { return (!(*this == v)); }
-	template<unsigned int C2>
-	ZT_ALWAYS_INLINE bool operator<(const FCV<T,C2> &v) const { return std::lexicographical_compare(begin(),end(),v.begin(),v.end()); }
-	template<unsigned int C2>
-	ZT_ALWAYS_INLINE bool operator>(const FCV<T,C2> &v) const { return (v < *this); }
-	template<unsigned int C2>
-	ZT_ALWAYS_INLINE bool operator<=(const FCV<T,C2> &v) const { return !(v < *this); }
-	template<unsigned int C2>
-	ZT_ALWAYS_INLINE bool operator>=(const FCV<T,C2> &v) const { return !(*this < v); }
+	ZT_ALWAYS_INLINE bool operator!=(const FCV &v) const { return (!(*this == v)); }
+	ZT_ALWAYS_INLINE bool operator<(const FCV &v) const { return std::lexicographical_compare(begin(),end(),v.begin(),v.end()); }
+	ZT_ALWAYS_INLINE bool operator>(const FCV &v) const { return (v < *this); }
+	ZT_ALWAYS_INLINE bool operator<=(const FCV &v) const { return !(v < *this); }
+	ZT_ALWAYS_INLINE bool operator>=(const FCV &v) const { return !(*this < v); }
 
 private:
 	unsigned int _s;

+ 8 - 0
node/OS.hpp

@@ -130,7 +130,11 @@ typedef unsigned uint128_t __attribute__((mode(TI)));
 #endif
 
 #if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__)
+#ifdef ZT_DEBUG
+#define ZT_ALWAYS_INLINE
+#else
 #define ZT_ALWAYS_INLINE __attribute__((always_inline)) inline
+#endif
 #ifndef restrict
 #define restrict __restrict__
 #endif
@@ -190,8 +194,12 @@ typedef unsigned uint128_t __attribute__((mode(TI)));
 #endif
 
 #ifndef ZT_ALWAYS_INLINE
+#ifdef ZT_DEBUG
+#define ZT_ALWAYS_INLINE
+#else
 #define ZT_ALWAYS_INLINE inline
 #endif
+#endif
 
 // Macro to avoid calling hton() on values known at compile time.
 #if __BYTE_ORDER == __LITTLE_ENDIAN

+ 4 - 4
node/Poly1305.cpp

@@ -16,10 +16,10 @@ Public domain.
 #pragma warning(disable: 4146)
 #endif
 
-#define U8TO64(p) Utils::loadBigEndian<uint64_t>(p)
-#define U64TO8(p,v) Utils::storeBigEndian<uint64_t>(p,v)
-#define U8TO32(p) Utils::loadBigEndian<uint32_t>(p)
-#define U32TO8(p,v) Utils::storeBigEndian<uint32_t>(p,v)
+#define U8TO64(p) Utils::loadLittleEndian<uint64_t>(p)
+#define U64TO8(p,v) Utils::storeLittleEndian<uint64_t>(p,v)
+#define U8TO32(p) Utils::loadLittleEndian<uint32_t>(p)
+#define U32TO8(p,v) Utils::storeLittleEndian<uint32_t>(p,v)
 
 namespace ZeroTier {
 

+ 6 - 6
node/Protocol.hpp

@@ -969,31 +969,31 @@ ZT_PACKED_STRUCT(struct UNSUPPORTED_OPERATION__NETWORK_CONFIG_REQUEST
  * @param packetSize Packet's actual size in bytes
  * @return Packet ID or 0 if packet size is less than 8
  */
-ZT_ALWAYS_INLINE uint64_t packetId(const Buf &pkt,const unsigned int packetSize) noexcept { return (packetSize >= 8) ? Utils::loadBigEndian<uint64_t>(pkt.unsafeData) : 0ULL; }
+static ZT_ALWAYS_INLINE uint64_t packetId(const Buf &pkt,const unsigned int packetSize) noexcept { return (packetSize >= 8) ? Utils::loadBigEndian<uint64_t>(pkt.unsafeData) : 0ULL; }
 
 /**
  * @param Packet to extract hops from
  * @param packetSize Packet's actual size in bytes
  * @return 3-bit hops field embedded in packet flags field
  */
-ZT_ALWAYS_INLINE uint8_t packetHops(const Buf &pkt,const unsigned int packetSize) noexcept { return (packetSize >= ZT_PROTO_PACKET_FLAGS_INDEX) ? (pkt.unsafeData[ZT_PROTO_PACKET_FLAGS_INDEX] & ZT_PROTO_FLAG_FIELD_HOPS_MASK) : 0; }
+static ZT_ALWAYS_INLINE uint8_t packetHops(const Buf &pkt,const unsigned int packetSize) noexcept { return (packetSize >= ZT_PROTO_PACKET_FLAGS_INDEX) ? (pkt.unsafeData[ZT_PROTO_PACKET_FLAGS_INDEX] & ZT_PROTO_FLAG_FIELD_HOPS_MASK) : 0; }
 
 /**
  * @param Packet to extract cipher ID from
  * @param packetSize Packet's actual size in bytes
  * @return 3-bit cipher field embedded in packet flags field
  */
-ZT_ALWAYS_INLINE uint8_t packetCipher(const Buf &pkt,const unsigned int packetSize) noexcept { return (packetSize >= ZT_PROTO_PACKET_FLAGS_INDEX) ? ((pkt.unsafeData[ZT_PROTO_PACKET_FLAGS_INDEX] >> 3U) & 0x07U) : 0; }
+static ZT_ALWAYS_INLINE uint8_t packetCipher(const Buf &pkt,const unsigned int packetSize) noexcept { return (packetSize >= ZT_PROTO_PACKET_FLAGS_INDEX) ? ((pkt.unsafeData[ZT_PROTO_PACKET_FLAGS_INDEX] >> 3U) & 0x07U) : 0; }
 
 /**
  * @return 3-bit hops field embedded in packet flags field
  */
-ZT_ALWAYS_INLINE uint8_t packetHops(const Header &ph) noexcept { return (ph.flags & 0x07U); }
+static ZT_ALWAYS_INLINE uint8_t packetHops(const Header &ph) noexcept { return (ph.flags & 0x07U); }
 
 /**
  * @return 3-bit cipher field embedded in packet flags field
  */
-ZT_ALWAYS_INLINE uint8_t packetCipher(const Header &ph) noexcept { return ((ph.flags >> 3U) & 0x07U); }
+static ZT_ALWAYS_INLINE uint8_t packetCipher(const Header &ph) noexcept { return ((ph.flags >> 3U) & 0x07U); }
 
 /**
  * Deterministically mangle a 256-bit crypto key based on packet characteristics
@@ -1005,7 +1005,7 @@ ZT_ALWAYS_INLINE uint8_t packetCipher(const Header &ph) noexcept { return ((ph.f
  * @param in Input key (32 bytes)
  * @param out Output buffer (32 bytes)
  */
-ZT_ALWAYS_INLINE void salsa2012DeriveKey(const uint8_t *const in,uint8_t *const out,const Buf &packet,const unsigned int packetSize) noexcept
+static ZT_ALWAYS_INLINE void salsa2012DeriveKey(const uint8_t *const in,uint8_t *const out,const Buf &packet,const unsigned int packetSize) noexcept
 {
 	// IV and source/destination addresses. Using the addresses divides the
 	// key space into two halves-- A->B and B->A (since order will change).

+ 26 - 25
node/Tests.cpp

@@ -50,7 +50,7 @@ using namespace ZeroTier;
 
 static const uint8_t ECC384_TV0_PUBLIC[49] = { 0x02,0xed,0xbc,0xbb,0x1f,0x23,0x9b,0xbd,0x9d,0x3d,0x7c,0xef,0x6b,0x37,0xa3,0x26,0x69,0xe9,0x4d,0xf4,0x26,0x64,0xfb,0xac,0x76,0x40,0xc2,0x22,0x21,0xa6,0xa3,0xdf,0x8c,0x96,0x81,0x76,0x0f,0x0e,0x67,0xab,0xd4,0x51,0x58,0xb3,0x15,0x63,0xfb,0x49,0x71 };
 static const uint8_t ECC384_TV0_PRIVATE[48]  = { 0x62,0x93,0x9b,0x4a,0x29,0x3c,0xc6,0x86,0x98,0xc3,0xd0,0x7f,0xb7,0xff,0x97,0xa2,0xfb,0xc9,0x36,0x8a,0x1d,0xa5,0x40,0x8e,0x49,0x13,0xd4,0x15,0x46,0xcb,0xb4,0x08,0xfa,0x8c,0xb2,0x7f,0xcc,0x3f,0x72,0xf8,0x0d,0x16,0x7b,0xf0,0xa4,0xc3,0x29,0xd3 };
-static const uint8_t ECC384_TV0_DH_SELF_AGREE[48] = { 0xf6,0x96,0xbd,0x1b,0xda,0x5e,0x52,0x8c,0x1d,0x56,0xa3,0x6e,0xd9,0xba,0xd7,0x84,0xdd,0x20,0x1b,0x50,0xc9,0xd8,0x68,0xb9,0x52,0x93,0x27,0xab,0x17,0xed,0xc6,0xae,0x89,0x5e,0x7f,0xd9,0x46,0x15,0x87,0xf4,0xc8,0x47,0x2e,0xf7,0x86,0xf5,0x87 };
+static const uint8_t ECC384_TV0_DH_SELF_AGREE[48] = { 0xf6,0x96,0xbd,0x1b,0xda,0x5e,0x52,0x8c,0x1d,0x56,0xa3,0x6e,0xd9,0xba,0xd7,0x84,0xdd,0x20,0x1b,0x50,0xc9,0xd8,0x68,0xb9,0x52,0x93,0x27,0xab,0x17,0xed,0xc6,0xae,0x89,0x5e,0x7f,0xd9,0x46,0x15,0x87,0xf4,0xc8,0x47,0x2e,0xf7,0x86,0xf5,0x87,0x0b };
 static const uint8_t ECC384_TV0_SIG[96] = { 0x98,0x93,0x5f,0x0a,0x05,0x2c,0xba,0x3a,0xd7,0xd2,0x08,0xde,0x64,0xe7,0x77,0x2c,0xbd,0xe6,0xd9,0x16,0x11,0xd2,0xef,0x03,0xba,0x12,0x9f,0x14,0x98,0x49,0x8c,0x2d,0x36,0x50,0xd9,0xcf,0xbb,0x2b,0xea,0xcb,0x28,0xe7,0x0b,0x90,0x43,0x9e,0x01,0x8b,0x52,0xdb,0x46,0xec,0xc7,0xf6,0xa9,0x56,0x88,0x00,0x3c,0xdb,0x4f,0xfe,0x04,0xa1,0xc7,0x4c,0x3f,0xfc,0xb8,0xc8,0x70,0x42,0x12,0xf4,0x37,0xfa,0xcd,0xb9,0x17,0x2f,0x60,0x8c,0xb6,0x05,0xc6,0xce,0x37,0xd6,0xc9,0xf0,0x0b,0x23,0x39,0x10,0x29,0x0d };
 
 static const uint8_t SALSA20_TV0_KEY[32] = { 0x0f,0x62,0xb5,0x08,0x5b,0xae,0x01,0x54,0xa7,0xfa,0x4d,0xa0,0xf3,0x46,0x99,0xec,0x3f,0x92,0xe5,0x38,0x8b,0xde,0x31,0x84,0xd7,0x2a,0x7d,0xd0,0x23,0x76,0xc9,0x1c };
@@ -173,7 +173,7 @@ public:
 	ZT_ALWAYS_INLINE LifeCycleTracker(const LifeCycleTracker &ltc) :
 		cnt(ltc.cnt)
 	{
-		if (*cnt) ++*cnt;
+		if (cnt) ++*cnt;
 	}
 	explicit ZT_ALWAYS_INLINE LifeCycleTracker(long &c) :
 		cnt(&c)
@@ -187,9 +187,9 @@ public:
 	ZT_ALWAYS_INLINE LifeCycleTracker &operator=(const LifeCycleTracker &ltc)
 	{
 		if (&ltc != this) {
-			if (*cnt) --*cnt;
+			if (cnt) --*cnt;
 			cnt = ltc.cnt;
-			if (*cnt) ++*cnt;
+			if (cnt) ++*cnt;
 		}
 		return *this;
 	}
@@ -202,14 +202,14 @@ extern "C" const char *ZTT_general()
 {
 	try {
 		volatile uint64_t endian = 0;
-		reinterpret_cast<volatile uint8_t *>(endian)[0] = 1;
-		reinterpret_cast<volatile uint8_t *>(endian)[1] = 2;
-		reinterpret_cast<volatile uint8_t *>(endian)[2] = 3;
-		reinterpret_cast<volatile uint8_t *>(endian)[3] = 4;
-		reinterpret_cast<volatile uint8_t *>(endian)[4] = 5;
-		reinterpret_cast<volatile uint8_t *>(endian)[5] = 6;
-		reinterpret_cast<volatile uint8_t *>(endian)[6] = 7;
-		reinterpret_cast<volatile uint8_t *>(endian)[7] = 8;
+		reinterpret_cast<volatile uint8_t *>(&endian)[0] = 1;
+		reinterpret_cast<volatile uint8_t *>(&endian)[1] = 2;
+		reinterpret_cast<volatile uint8_t *>(&endian)[2] = 3;
+		reinterpret_cast<volatile uint8_t *>(&endian)[3] = 4;
+		reinterpret_cast<volatile uint8_t *>(&endian)[4] = 5;
+		reinterpret_cast<volatile uint8_t *>(&endian)[5] = 6;
+		reinterpret_cast<volatile uint8_t *>(&endian)[6] = 7;
+		reinterpret_cast<volatile uint8_t *>(&endian)[7] = 8;
 #if __BYTE_ORDER == __LITTLE_ENDIAN
 		if (*(&endian) != 0x0807060504030201ULL) {
 			ZT_T_PRINTF("[general] Error: __BYTE_ORDER == __LITTLE_ENDIAN but byte order is actually %.16llx" ZT_EOL_S,endian);
@@ -293,7 +293,9 @@ extern "C" const char *ZTT_general()
 			ZT_T_PRINTF("OK" ZT_EOL_S);
 
 			ZT_T_PRINTF("[general] Utils::random() samples: %.16llx %.16llx" ZT_EOL_S,c,d);
+
 			uint8_t secrand[64];
+			Utils::getSecureRandom(secrand,sizeof(secrand));
 			char secrands[256];
 			Utils::hex(secrand,sizeof(secrand),secrands);
 			ZT_T_PRINTF("[general] Utils::getSecureRandom() sample: %s" ZT_EOL_S,secrands);
@@ -417,7 +419,6 @@ extern "C" const char *ZTT_crypto()
 				ZT_T_PRINTF("FAILED (SHA512)" ZT_EOL_S);
 				return "SHA512 test vector failed";
 			}
-			ZT_T_PRINTF("OK" ZT_EOL_S);
 			SHA384(h,SHA512_TV0_INPUT,strlen(SHA512_TV0_INPUT));
 			if (memcmp(h,SHA512_TV0_SHA384_DIGEST,48) != 0) {
 				ZT_T_PRINTF("FAILED (SHA384)" ZT_EOL_S);
@@ -442,12 +443,12 @@ extern "C" const char *ZTT_crypto()
 					return "Curve25519 key agreement test failed (does not match expected value)";
 				}
 				C25519::sign(C25519_TEST_VECTORS[t].priv1,C25519_TEST_VECTORS[t].pub1,kh,64,sig);
-				if (memcmp(sig,C25519_TEST_VECTORS[t].agreementSignedBy1,96) != 0) {
+				if (memcmp(sig,C25519_TEST_VECTORS[t].agreementSignedBy1,ZT_C25519_SIGNATURE_LEN) != 0) {
 					ZT_T_PRINTF("FAILED (signature of agreement by key 1 does not match test vector %d)" ZT_EOL_S,t);
 					return "Curve25519 signature test failed (signature by key 1 does not match expected value)";
 				}
 				C25519::sign(C25519_TEST_VECTORS[t].priv2,C25519_TEST_VECTORS[t].pub2,kh,64,sig);
-				if (memcmp(sig,C25519_TEST_VECTORS[t].agreementSignedBy1,96) != 0) {
+				if (memcmp(sig,C25519_TEST_VECTORS[t].agreementSignedBy2,ZT_C25519_SIGNATURE_LEN) != 0) {
 					ZT_T_PRINTF("FAILED (signature of agreement by key 2 does not match test vector %d)" ZT_EOL_S,t);
 					return "Curve25519 signature test failed (signature by key 2 does not match expected value)";
 				}
@@ -456,10 +457,10 @@ extern "C" const char *ZTT_crypto()
 		}
 
 		{
-			uint8_t key[48];
+			uint8_t key[ZT_ECC384_SHARED_SECRET_SIZE];
 			ZT_T_PRINTF("[crypto] Testing ECC384 (NIST P-384)... ");
 			ECC384ECDH(ECC384_TV0_PUBLIC,ECC384_TV0_PRIVATE,key);
-			if (memcmp(key,ECC384_TV0_DH_SELF_AGREE,48) != 0) {
+			if (memcmp(key,ECC384_TV0_DH_SELF_AGREE,ZT_ECC384_SHARED_SECRET_SIZE) != 0) {
 				ZT_T_PRINTF("FAILED (test vector 0, self-agree)" ZT_EOL_S);
 				return "ECC384 test vector 0 self-agree failed";
 			}
@@ -496,12 +497,12 @@ extern "C" const char *ZTT_crypto()
 			ZT_T_PRINTF("[crypto] Testing Poly1305... ");
 			poly1305(tag,POLY1305_TV0_INPUT,sizeof(POLY1305_TV0_INPUT),POLY1305_TV0_KEY);
 			if (memcmp(tag,POLY1305_TV0_TAG,16) != 0) {
-				ZT_T_PRINTF("FAILED (test vector 0)");
+				ZT_T_PRINTF("FAILED (test vector 0)" ZT_EOL_S);
 				return "poly1305 test vector 0 failed";
 			}
 			poly1305(tag,POLY1305_TV1_INPUT,sizeof(POLY1305_TV1_INPUT),POLY1305_TV1_KEY);
 			if (memcmp(tag,POLY1305_TV1_TAG,16) != 0) {
-				ZT_T_PRINTF("FAILED (test vector 1)");
+				ZT_T_PRINTF("FAILED (test vector 1)" ZT_EOL_S);
 				return "poly1305 test vector 1 failed";
 			}
 			ZT_T_PRINTF("OK" ZT_EOL_S);
@@ -513,12 +514,12 @@ extern "C" const char *ZTT_crypto()
 			AES aes(AES_TEST_VECTOR_0_KEY);
 			aes.encrypt(AES_TEST_VECTOR_0_IN,out);
 			if (memcmp(AES_TEST_VECTOR_0_OUT,out,16) != 0) {
-				ZT_T_PRINTF("FAILED (test vector 0)");
+				ZT_T_PRINTF("FAILED (test vector 0) ZT_EOL_S");
 				return "AES test vector 0 failed";
 			}
 			aes.decrypt(out,out);
 			if (memcmp(AES_TEST_VECTOR_0_IN,out,16) != 0) {
-				ZT_T_PRINTF("FAILED (test vector 0 decrypt)");
+				ZT_T_PRINTF("FAILED (test vector 0 decrypt) ZT_EOL_S");
 				return "AES test vector 0 decrypt failed";
 			}
 			ZT_T_PRINTF("OK" ZT_EOL_S);
@@ -534,7 +535,7 @@ extern "C" const char *ZTT_crypto()
 				gmac.update(AES_GMAC_VECTOR_0_IN,sizeof(AES_GMAC_VECTOR_0_IN));
 				gmac.finish(tag);
 				if (memcmp(tag,AES_GMAC_VECTOR_0_OUT,16) != 0) {
-					ZT_T_PRINTF("FAILED (test vector 0)");
+					ZT_T_PRINTF("FAILED (test vector 0)" ZT_EOL_S);
 					return "AES-GMAC test vector 0 failed";
 				}
 			}
@@ -545,7 +546,7 @@ extern "C" const char *ZTT_crypto()
 				gmac.update(AES_GMAC_VECTOR_1_IN,sizeof(AES_GMAC_VECTOR_1_IN));
 				gmac.finish(tag);
 				if (memcmp(tag,AES_GMAC_VECTOR_1_OUT,16) != 0) {
-					ZT_T_PRINTF("FAILED (test vector 1)");
+					ZT_T_PRINTF("FAILED (test vector 1)" ZT_EOL_S);
 					return "AES-GMAC test vector 1 failed";
 				}
 			}
@@ -556,7 +557,7 @@ extern "C" const char *ZTT_crypto()
 				gmac.update(AES_GMAC_VECTOR_2_IN,sizeof(AES_GMAC_VECTOR_2_IN));
 				gmac.finish(tag);
 				if (memcmp(tag,AES_GMAC_VECTOR_2_OUT,16) != 0) {
-					ZT_T_PRINTF("FAILED (test vector 2)");
+					ZT_T_PRINTF("FAILED (test vector 2)" ZT_EOL_S);
 					return "AES-GMAC test vector 2 failed";
 				}
 			}
@@ -568,7 +569,7 @@ extern "C" const char *ZTT_crypto()
 				gmac.update(AES_GMAC_VECTOR_2_IN + (sizeof(AES_GMAC_VECTOR_2_IN) - 117),117);
 				gmac.finish(tag);
 				if (memcmp(tag,AES_GMAC_VECTOR_2_OUT,16) != 0) {
-					ZT_T_PRINTF("FAILED (test vector 2, two fragments)");
+					ZT_T_PRINTF("FAILED (test vector 2, two fragments)" ZT_EOL_S);
 					return "AES-GMAC test vector (in two fragments) 2 failed";
 				}
 			}

+ 4 - 4
node/Utils.cpp

@@ -325,9 +325,9 @@ void getSecureRandom(void *buf,unsigned int bytes) noexcept
 #if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64))
 				if (CPUID.rdrand) {
 					uint64_t tmp = 0;
-					for(int i=0;i<16;++i) {
+					for(int k=0;k<16;++k) {
 						_rdrand64_step((unsigned long long *)&tmp);
-						randomState[i] ^= tmp;
+						randomState[k] ^= tmp;
 					}
 				}
 #endif
@@ -342,16 +342,16 @@ void getSecureRandom(void *buf,unsigned int bytes) noexcept
 
 			++randomState[15];
 			SHA384(randomState,randomState,sizeof(randomState));
+
 			AES aes(randomState);
 			uint64_t ctr[2],tmp[2];
 			ctr[0] = randomState[4];
 			ctr[1] = randomState[5]; // AES key + CTR/nonce = part replaced each time by SHA384
-			for(int k=0;k<8192;) {
+			for(int k=0;k<8192;k+=2) {
 				++ctr[0];
 				aes.encrypt(ctr,tmp);
 				randomBuf[k] ^= tmp[0];
 				randomBuf[k+1] ^= tmp[1];
-				k += 2;
 			}
 		}
 

+ 62 - 0
node/Utils.hpp

@@ -464,6 +464,68 @@ static ZT_ALWAYS_INLINE void storeBigEndian(void *const p,const I i) noexcept
 #endif
 }
 
+/**
+ * Decode a little-endian value from a byte stream
+ *
+ * @tparam I Type to decode (should be unsigned e.g. uint32_t or uint64_t)
+ * @param p Byte stream, must be at least sizeof(I) in size
+ * @return Decoded integer
+ */
+template<typename I>
+static ZT_ALWAYS_INLINE I loadLittleEndian(const void *const p) noexcept
+{
+#ifdef ZT_NO_UNALIGNED_ACCESS
+	I x = (I)0;
+	for(unsigned int k=0;k<sizeof(I);++k) {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+		reinterpret_cast<uint8_t *>(&x)[k] = reinterpret_cast<const uint8_t *>(p)[k];
+#else
+		reinterpret_cast<uint8_t *>(&x)[k] = reinterpret_cast<const uint8_t *>(p)[(sizeof(I)-1)-k];
+#endif
+	}
+	return x;
+#else
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	return *reinterpret_cast<const I *>(p);
+#else
+	I x = (I)0;
+	for(unsigned int k=0;k<sizeof(I);++k) {
+		reinterpret_cast<uint8_t *>(&x)[k] = reinterpret_cast<const uint8_t *>(p)[(sizeof(I)-1)-k];
+	}
+	return x;
+#endif
+#endif
+}
+
+/**
+ * Save an integer in little-endian format
+ *
+ * @tparam I Integer type to store (usually inferred)
+ * @param p Byte stream to write (must be at least sizeof(I))
+ * #param i Integer to write
+ */
+template<typename I>
+static ZT_ALWAYS_INLINE void storeLittleEndian(void *const p,const I i) noexcept
+{
+#ifdef ZT_NO_UNALIGNED_ACCESS
+	for(unsigned int k=0;k<sizeof(I);++k) {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+		reinterpret_cast<uint8_t *>(p)[k] = reinterpret_cast<const uint8_t *>(&i)[k];
+#else
+		reinterpret_cast<uint8_t *>(p)[k] = reinterpret_cast<const uint8_t *>(&i)[(sizeof(I)-1)-k];
+#endif
+	}
+#else
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	*reinterpret_cast<I *>(p) = i;
+#else
+	for(unsigned int k=0;k<sizeof(I);++k) {
+		reinterpret_cast<uint8_t *>(p)[k] = reinterpret_cast<const uint8_t *>(&i)[(sizeof(I)-1)-k];
+	}
+#endif
+#endif
+}
+
 /**
  * Copy bits from memory into an integer type without modifying their order
  *