diff --git a/node/Buffer.hpp b/attic/Buffer.hpp similarity index 100% rename from node/Buffer.hpp rename to attic/Buffer.hpp diff --git a/node/Packet.cpp b/attic/Packet.cpp similarity index 94% rename from node/Packet.cpp rename to attic/Packet.cpp index 6c1a28506..6508431f1 100644 --- a/node/Packet.cpp +++ b/attic/Packet.cpp @@ -18,7 +18,7 @@ #include "Mutex.hpp" #include "LZ4.hpp" -#if (defined(_MSC_VER) || defined(__GNUC__) || defined(__clang)) && (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64)) +#if defined(__GCC__) && (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64)) #define ZT_PACKET_USE_ATOMIC_INTRINSICS #endif #ifndef ZT_PACKET_USE_ATOMIC_INTRINSICS @@ -137,7 +137,7 @@ static unsigned long long s_packetIdCtr = s_initPacketID(); static std::atomic s_packetIdCtr(s_initPacketID()); #endif -uint64_t Packet::nextPacketId() +uint64_t getPacketId() { #ifdef ZT_PACKET_USE_ATOMIC_INTRINSICS return __sync_add_and_fetch(&s_packetIdCtr,1ULL); diff --git a/node/Packet.hpp b/attic/Packet.hpp similarity index 98% rename from node/Packet.hpp rename to attic/Packet.hpp index 34f444def..5c579a70f 100644 --- a/node/Packet.hpp +++ b/attic/Packet.hpp @@ -234,41 +234,6 @@ #define ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_FLAGS (ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_ADI + 4) #define ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS (ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_FLAGS + 1) -/** - * Signed locator for this node - */ -#define ZT_PROTO_NODE_META_LOCATOR "l" - -/** - * Ephemeral C25519 public key - */ -#define ZT_PROTO_NODE_META_EPHEMERAL_KEY_C25519 "e0" - -/** - * Ephemeral NIST P-384 public key - */ -#define ZT_PROTO_NODE_META_EPHEMERAL_KEY_P384 "e1" - -/** - * Addresses of ZeroTier nodes to whom this node will relay or one entry for 0000000000 if promiscuous. - */ -#define ZT_PROTO_NODE_META_WILL_RELAY_TO "r" - -/** - * X coordinate of your node (sent in OK(HELLO)) - */ -#define ZT_PROTO_NODE_META_LOCATION_X "gX" - -/** - * Y coordinate of your node (sent in OK(HELLO)) - */ -#define ZT_PROTO_NODE_META_LOCATION_Y "gY" - -/** - * Z coordinate of your node (sent in OK(HELLO)) - */ -#define ZT_PROTO_NODE_META_LOCATION_Z "gZ" - // --------------------------------------------------------------------------- namespace ZeroTier { @@ -878,6 +843,9 @@ public: * "dumb" relaying. The latter is faster but secure relaying has roles * where endpoint privacy is desired. Multiply nested ENCAP packets * could allow ZeroTier to act as an onion router. + * + * When encapsulated packets are forwarded they do have their hop count + * field incremented. */ VERB_ENCAP = 0x17 diff --git a/node/AES.cpp b/node/AES.cpp index 0243d860a..390acf8f7 100644 --- a/node/AES.cpp +++ b/node/AES.cpp @@ -149,39 +149,6 @@ void AES::_encryptSW(const uint8_t in[16],uint8_t out[16]) const writeuint32_t(out + 12,(Te2[(t3 >> 24)] & 0xff000000) ^ (Te3[(t0 >> 16) & 0xff] & 0x00ff0000) ^ (Te0[(t1 >> 8) & 0xff] & 0x0000ff00) ^ (Te1[(t2) & 0xff] & 0x000000ff) ^ rk[59]); } -void AES::_ctrSW(const uint8_t iv[16],const void *in,unsigned int len,void *out) const -{ - uint64_t ctr[2],cenc[2]; - memcpy(ctr,iv,16); - uint64_t bctr = Utils::ntoh(ctr[1]); - - const uint8_t *i = (const uint8_t *)in; - uint8_t *o = (uint8_t *)out; - - while (len >= 16) { - _encryptSW((const uint8_t *)ctr,(uint8_t *)cenc); - ctr[1] = Utils::hton(++bctr); -#ifdef ZT_NO_UNALIGNED_ACCESS - for(unsigned int k=0;k<16;++k) - *(o++) = *(i++) ^ ((uint8_t *)cenc)[k]; -#else - *((uint64_t *)o) = *((const uint64_t *)i) ^ cenc[0]; - o += 8; - i += 8; - *((uint64_t *)o) = *((const uint64_t *)i) ^ cenc[1]; - o += 8; - i += 8; -#endif - len -= 16; - } - - if (len) { - _encryptSW((const uint8_t *)ctr,(uint8_t *)cenc); - for(unsigned int k=0;k= 64) { - __m128i ka = _k.ni.k[2]; - __m128i c0 = _mm_xor_si128(ctr0,k0); - __m128i c1 = _mm_xor_si128(ctr1,k0); - __m128i c2 = _mm_xor_si128(ctr2,k0); - __m128i c3 = _mm_xor_si128(ctr3,k0); - ctr0 = _mm_shuffle_epi8(ctr0,swap128); - notctr0msq = ~((uint64_t)_mm_extract_epi64(ctr0,0)); - ctr1 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)(notctr0msq < 5ULL),5LL)),swap128); - ctr2 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)(notctr0msq < 6ULL),6LL)),swap128); - ctr3 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)(notctr0msq < 7ULL),7LL)),swap128); - ctr0 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)(notctr0msq < 4ULL),4LL)),swap128); - __m128i kb = _k.ni.k[3]; - ZT_AES_CTR_AESNI_ROUND(k1); - __m128i kc = _k.ni.k[4]; - ZT_AES_CTR_AESNI_ROUND(ka); - __m128i kd = _k.ni.k[5]; - ZT_AES_CTR_AESNI_ROUND(kb); - ka = _k.ni.k[6]; - ZT_AES_CTR_AESNI_ROUND(kc); - kb = _k.ni.k[7]; - ZT_AES_CTR_AESNI_ROUND(kd); - kc = _k.ni.k[8]; - ZT_AES_CTR_AESNI_ROUND(ka); - kd = _k.ni.k[9]; - ZT_AES_CTR_AESNI_ROUND(kb); - ka = _k.ni.k[10]; - ZT_AES_CTR_AESNI_ROUND(kc); - kb = _k.ni.k[11]; - ZT_AES_CTR_AESNI_ROUND(kd); - kc = _k.ni.k[12]; - ZT_AES_CTR_AESNI_ROUND(ka); - kd = _k.ni.k[13]; - ZT_AES_CTR_AESNI_ROUND(kb); - ka = _k.ni.k[14]; - ZT_AES_CTR_AESNI_ROUND(kc); - ZT_AES_CTR_AESNI_ROUND(kd); - _mm_storeu_si128((__m128i *)out,_mm_xor_si128(_mm_loadu_si128((const __m128i *)in),_mm_aesenclast_si128(c0,ka))); - _mm_storeu_si128((__m128i *)(out + 16),_mm_xor_si128(_mm_loadu_si128((const __m128i *)(in + 16)),_mm_aesenclast_si128(c1,ka))); - _mm_storeu_si128((__m128i *)(out + 32),_mm_xor_si128(_mm_loadu_si128((const __m128i *)(in + 32)),_mm_aesenclast_si128(c2,ka))); - _mm_storeu_si128((__m128i *)(out + 48),_mm_xor_si128(_mm_loadu_si128((const __m128i *)(in + 48)),_mm_aesenclast_si128(c3,ka))); - in += 64; - out += 64; - len -= 64; - } - - __m128i k2 = _k.ni.k[2]; - __m128i k3 = _k.ni.k[3]; - __m128i k4 = _k.ni.k[4]; - __m128i k5 = _k.ni.k[5]; - __m128i k6 = _k.ni.k[6]; - __m128i k7 = _k.ni.k[7]; - - while (len >= 16) { - __m128i c0 = _mm_xor_si128(ctr0,k0); - ctr0 = _mm_shuffle_epi8(ctr0,swap128); - ctr0 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)((~((uint64_t)_mm_extract_epi64(ctr0,0))) < 1ULL),1LL)),swap128); - c0 = _mm_aesenc_si128(c0,k1); - c0 = _mm_aesenc_si128(c0,k2); - c0 = _mm_aesenc_si128(c0,k3); - c0 = _mm_aesenc_si128(c0,k4); - c0 = _mm_aesenc_si128(c0,k5); - c0 = _mm_aesenc_si128(c0,k6); - __m128i ka = _k.ni.k[8]; - c0 = _mm_aesenc_si128(c0,k7); - __m128i kb = _k.ni.k[9]; - c0 = _mm_aesenc_si128(c0,ka); - ka = _k.ni.k[10]; - c0 = _mm_aesenc_si128(c0,kb); - kb = _k.ni.k[11]; - c0 = _mm_aesenc_si128(c0,ka); - ka = _k.ni.k[12]; - c0 = _mm_aesenc_si128(c0,kb); - kb = _k.ni.k[13]; - c0 = _mm_aesenc_si128(c0,ka); - ka = _k.ni.k[14]; - c0 = _mm_aesenc_si128(c0,kb); - _mm_storeu_si128((__m128i *)out,_mm_xor_si128(_mm_loadu_si128((const __m128i *)in),_mm_aesenclast_si128(c0,ka))); - in += 16; - out += 16; - len -= 16; - } - - if (len) { - __m128i c0 = _mm_xor_si128(ctr0,k0); - k0 = _k.ni.k[8]; - c0 = _mm_aesenc_si128(c0,k1); - c0 = _mm_aesenc_si128(c0,k2); - k1 = _k.ni.k[9]; - c0 = _mm_aesenc_si128(c0,k3); - c0 = _mm_aesenc_si128(c0,k4); - k2 = _k.ni.k[10]; - c0 = _mm_aesenc_si128(c0,k5); - c0 = _mm_aesenc_si128(c0,k6); - k3 = _k.ni.k[11]; - c0 = _mm_aesenc_si128(c0,k7); - c0 = _mm_aesenc_si128(c0,k0); - k0 = _k.ni.k[12]; - c0 = _mm_aesenc_si128(c0,k1); - c0 = _mm_aesenc_si128(c0,k2); - k1 = _k.ni.k[13]; - c0 = _mm_aesenc_si128(c0,k3); - c0 = _mm_aesenc_si128(c0,k0); - k2 = _k.ni.k[14]; - c0 = _mm_aesenc_si128(c0,k1); - c0 = _mm_aesenclast_si128(c0,k2); - uint8_t tmp[16]; - _mm_storeu_si128((__m128i *)tmp,c0); - for(unsigned int i=0;iinit(key); } + explicit ZT_ALWAYS_INLINE AES(const uint8_t key[32]) { this->init(key); } ZT_ALWAYS_INLINE ~AES() { Utils::burn(&_k,sizeof(_k)); } /** @@ -71,46 +71,9 @@ public: _encryptSW(in,out); } - /** - * Compute GMAC-AES256 (GCM without ciphertext) - * - * @param iv 96-bit IV - * @param in Input data - * @param len Length of input - * @param out 128-bit authorization tag from GMAC - */ - ZT_ALWAYS_INLINE void gmac(const uint8_t iv[12],const void *in,const unsigned int len,uint8_t out[16]) const + ZT_ALWAYS_INLINE void gcm(const uint8_t iv[12],const void *in,const unsigned int len,uint8_t out[16],uint8_t tag[16]) const { -#ifdef ZT_AES_AESNI - if (likely(Utils::CPUID.aes)) { - _gmac_aesni(iv,(const uint8_t *)in,len,out); - return; - } -#endif - _gmacSW(iv,(const uint8_t *)in,len,out); - } - - /** - * Encrypt or decrypt (they're the same) using AES256-CTR - * - * The counter here is a 128-bit big-endian that starts at the IV. The code only - * increments the least significant 64 bits, making it only safe to use for a - * maximum of 2^64-1 bytes (much larger than we ever do). - * - * @param iv 128-bit CTR IV - * @param in Input plaintext or ciphertext - * @param len Length of input - * @param out Output plaintext or ciphertext - */ - ZT_ALWAYS_INLINE void ctr(const uint8_t iv[16],const void *in,unsigned int len,void *out) const - { -#ifdef ZT_AES_AESNI - if (likely(Utils::CPUID.aes)) { - _ctr_aesni(iv,(const uint8_t *)in,len,(uint8_t *)out); - return; - } -#endif - _ctrSW(iv,in,len,out); + // TODO } private: @@ -122,7 +85,6 @@ private: void _initSW(const uint8_t key[32]); void _encryptSW(const uint8_t in[16],uint8_t out[16]) const; - void _ctrSW(const uint8_t iv[16],const void *in,unsigned int len,void *out) const; void _gmacSW(const uint8_t iv[12],const uint8_t *in,unsigned int len,uint8_t out[16]) const; /**************************************************************************/ @@ -258,7 +220,6 @@ private: } void _gmac_aesni(const uint8_t iv[12],const uint8_t *in,unsigned int len,uint8_t out[16]) const; - void _ctr_aesni(const uint8_t iv[16],const uint8_t *in,unsigned int len,uint8_t *out) const; #endif /* ZT_AES_AESNI ******************************************************/ }; diff --git a/node/Address.hpp b/node/Address.hpp index 6d95aad0f..c0f221992 100644 --- a/node/Address.hpp +++ b/node/Address.hpp @@ -26,7 +26,6 @@ #include "Constants.hpp" #include "Utils.hpp" -#include "Buffer.hpp" namespace ZeroTier { @@ -37,65 +36,31 @@ class Address { public: ZT_ALWAYS_INLINE Address() : _a(0) {} + explicit ZT_ALWAYS_INLINE Address(const uint8_t b[5]) : _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) : _a(a & 0xffffffffffULL) {} - /** - * @param bits Raw address -- 5 bytes, big-endian byte order - * @param len Length of array - */ - ZT_ALWAYS_INLINE Address(const void *bits,unsigned int len) { setTo(bits,len); } - ZT_ALWAYS_INLINE Address &operator=(const uint64_t a) { _a = (a & 0xffffffffffULL); return *this; } /** * @param bits Raw address -- 5 bytes, big-endian byte order * @param len Length of array */ - ZT_ALWAYS_INLINE void setTo(const void *bits,const unsigned int len) + ZT_ALWAYS_INLINE void setTo(const uint8_t b[5]) { - if (len < ZT_ADDRESS_LENGTH) { - _a = 0; - return; - } - const unsigned char *b = (const unsigned char *)bits; - uint64_t a = ((uint64_t)*b++) << 32; - a |= ((uint64_t)*b++) << 24; - a |= ((uint64_t)*b++) << 16; - a |= ((uint64_t)*b++) << 8; - a |= ((uint64_t)*b); - _a = a; + _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]; } /** * @param bits Buffer to hold 5-byte address in big-endian byte order * @param len Length of array */ - ZT_ALWAYS_INLINE void copyTo(void *const bits,const unsigned int len) const + ZT_ALWAYS_INLINE void copyTo(uint8_t b[5]) const { - if (len < ZT_ADDRESS_LENGTH) - return; - unsigned char *b = (unsigned char *)bits; - *(b++) = (unsigned char)((_a >> 32) & 0xff); - *(b++) = (unsigned char)((_a >> 24) & 0xff); - *(b++) = (unsigned char)((_a >> 16) & 0xff); - *(b++) = (unsigned char)((_a >> 8) & 0xff); - *b = (unsigned char)(_a & 0xff); - } - - /** - * Append to a buffer in big-endian byte order - * - * @param b Buffer to append to - */ - template - ZT_ALWAYS_INLINE void appendTo(Buffer &b) const - { - unsigned char *p = (unsigned char *)b.appendField(ZT_ADDRESS_LENGTH); - *(p++) = (unsigned char)((_a >> 32) & 0xff); - *(p++) = (unsigned char)((_a >> 24) & 0xff); - *(p++) = (unsigned char)((_a >> 16) & 0xff); - *(p++) = (unsigned char)((_a >> 8) & 0xff); - *p = (unsigned char)(_a & 0xff); + 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; } /** @@ -131,9 +96,6 @@ public: ZT_ALWAYS_INLINE uint8_t operator[](unsigned int i) const { return (uint8_t)(_a >> (32 - (i * 8))); } ZT_ALWAYS_INLINE operator bool() const { return (_a != 0); } - ZT_ALWAYS_INLINE operator unsigned int() const { return (unsigned int)_a; } - ZT_ALWAYS_INLINE operator unsigned long() const { return (unsigned long)_a; } - ZT_ALWAYS_INLINE operator unsigned long long() const { return (unsigned long long)_a; } ZT_ALWAYS_INLINE void zero() { _a = 0; } diff --git a/node/Buf.hpp b/node/Buf.hpp index 99408751d..650663fa8 100644 --- a/node/Buf.hpp +++ b/node/Buf.hpp @@ -77,6 +77,9 @@ extern std::atomic Buf_pool; * union as 'fields.' This must be a basic plain data type and must be no larger than * ZT_BUF_MEM_SIZE. It's typically a packed struct. * + * Buf instances with different template parameters can freely be cast to one another + * as there is no actual difference in size or layout. + * * @tparam U Type to overlap with data bytes in data union (can't be larger than ZT_BUF_MEM_SIZE) */ template @@ -86,12 +89,10 @@ class Buf private: // Direct construction isn't allowed; use get(). - ZT_ALWAYS_INLINE Buf() - {} + ZT_ALWAYS_INLINE Buf() {} template - ZT_ALWAYS_INLINE Buf(const Buf &b) - { memcpy(data.bytes,b.data.bytes,ZT_BUF_MEM_SIZE); } + ZT_ALWAYS_INLINE Buf(const Buf &b) { memcpy(data.bytes,b.data.bytes,ZT_BUF_MEM_SIZE); } public: static void operator delete(void *ptr,std::size_t sz) @@ -203,8 +204,7 @@ public: * @param ii Iterator to check * @return True if iterator has read past the size of the buffer */ - static ZT_ALWAYS_INLINE bool writeOverflow(const int &ii) - { return ((ii - ZT_BUF_MEM_SIZE) > 0); } + static ZT_ALWAYS_INLINE bool writeOverflow(const int &ii) { return ((ii - ZT_BUF_MEM_SIZE) > 0); } /** * Check for overflow beyond the size of the data that should be in the buffer @@ -216,8 +216,7 @@ public: * @param size Size of data that should be in buffer * @return True if iterator has read past the size of the data */ - static ZT_ALWAYS_INLINE bool readOverflow(const int &ii,const unsigned int size) - { return ((ii - (int)size) > 0); } + static ZT_ALWAYS_INLINE bool readOverflow(const int &ii,const unsigned int size) { return ((ii - (int)size) > 0); } template ZT_ALWAYS_INLINE Buf &operator=(const Buf &b) const @@ -226,6 +225,24 @@ public: return *this; } + /** + * Shortcut to cast between buffers whose data can be viewed through a different struct type + * + * @tparam X A packed struct or other primitive type that should be placed in the data union + * @return Reference to this Buf templated with the supplied parameter + */ + template + ZT_ALWAYS_INLINE Buf &view() { return *reinterpret_cast< Buf * >(this); } + + /** + * Shortcut to cast between buffers whose data can be viewed through a different struct type + * + * @tparam X A packed struct or other primitive type that should be placed in the data union + * @return Reference to this Buf templated with the supplied parameter + */ + template + ZT_ALWAYS_INLINE const Buf &view() const { return *reinterpret_cast< Buf * >(this); } + /** * Zero memory * @@ -241,8 +258,8 @@ public: */ ZT_ALWAYS_INLINE uint8_t rI8(int &ii) const { - const unsigned int s = (unsigned int)ii++; - return data.bytes[s & ZT_BUF_MEM_MASK]; + const int s = ii++; + return data.bytes[(unsigned int)s & ZT_BUF_MEM_MASK]; } /** @@ -432,8 +449,8 @@ public: */ ZT_ALWAYS_INLINE void wI(int &ii,uint8_t n) { - const unsigned int s = (unsigned int)ii++; - data[s & ZT_BUF_MEM_MASK] = n; + const int s = ii++; + data[(unsigned int)s & ZT_BUF_MEM_MASK] = n; } /** @@ -508,7 +525,7 @@ public: template ZT_ALWAYS_INLINE void wO(int &ii,T &t) { - const unsigned int s = (unsigned int)ii; + const int s = ii; if ((s + T::marshalSizeMax()) <= ZT_BUF_MEM_SIZE) { int ms = t.marshal(data.bytes + s); if (ms > 0) @@ -546,7 +563,7 @@ public: */ ZT_ALWAYS_INLINE void wB(int &ii,const void *const bytes,const unsigned int len) { - unsigned int s = (unsigned int)ii; + const int s = ii; if ((ii += (int)len) <= ZT_BUF_MEM_SIZE) memcpy(data.bytes + s,bytes,len); } diff --git a/node/CMakeLists.txt b/node/CMakeLists.txt index f91ef4499..d2e4c9e45 100644 --- a/node/CMakeLists.txt +++ b/node/CMakeLists.txt @@ -6,7 +6,6 @@ set(core_headers Address.hpp AtomicCounter.hpp Buf.hpp - Buffer.hpp C25519.hpp Capability.hpp CertificateOfMembership.hpp @@ -28,10 +27,10 @@ set(core_headers NetworkConfig.hpp Node.hpp OS.hpp - Packet.hpp Path.hpp Peer.hpp Poly1305.hpp + Protocol.hpp RingBuffer.hpp RuntimeEnvironment.hpp Salsa20.hpp @@ -64,10 +63,10 @@ set(core_src Network.cpp NetworkConfig.cpp Node.cpp - Packet.cpp Path.cpp Peer.cpp Poly1305.cpp + Protocol.cpp Salsa20.cpp SelfAwareness.cpp SHA512.cpp diff --git a/node/Capability.cpp b/node/Capability.cpp index 327927035..934c763de 100644 --- a/node/Capability.cpp +++ b/node/Capability.cpp @@ -12,19 +12,21 @@ /****/ #include "Capability.hpp" +#include "Utils.hpp" +#include "Constants.hpp" +#include "MAC.hpp" namespace ZeroTier { bool Capability::sign(const Identity &from,const Address &to) { + uint8_t buf[ZT_CAPABILITY_MARSHAL_SIZE_MAX + 16]; try { for(unsigned int i=0;((i<_maxCustodyChainLength)&&(i tmp; - this->serialize(tmp,true); _custody[i].to = to; _custody[i].from = from.address(); - _custody[i].signatureLength = from.sign(tmp.data(),tmp.size(),_custody[i].signature,sizeof(_custody[i].signature)); + _custody[i].signatureLength = from.sign(buf,(unsigned int)marshal(buf,true),_custody[i].signature,sizeof(_custody[i].signature)); return true; } } @@ -32,4 +34,330 @@ bool Capability::sign(const Identity &from,const Address &to) return false; } +int Capability::marshal(uint8_t data[ZT_CAPABILITY_MARSHAL_SIZE_MAX],const bool forSign) const +{ + int p = 0; + if (forSign) { + for(int k=0;k<8;++k) + data[p++] = 0x7f; + } + Utils::storeBigEndian(data + p,_nwid); p += 8; + Utils::storeBigEndian(data + p,(uint64_t)_ts); p += 8; + Utils::storeBigEndian(data + p,_id); p += 4; + Utils::storeBigEndian(data + p,(uint16_t)_ruleCount); p += 2; + p += Capability::marshalVirtualNetworkRules(data + 22,_rules,_ruleCount); + data[p++] = (uint8_t)_maxCustodyChainLength; + if (!forSign) { + for(unsigned int i=0;;++i) { + if ((i < _maxCustodyChainLength)&&(i < ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH)&&(_custody[i].to)) { + _custody[i].to.copyTo(data + p); p += ZT_ADDRESS_LENGTH; + _custody[i].from.copyTo(data + p); p += ZT_ADDRESS_LENGTH; + data[p++] = 1; + Utils::storeBigEndian(data + p,(uint16_t)_custody[i].signatureLength); p += 2; + for(unsigned int k=0;k<_custody[i].signatureLength;++k) + data[p++] = _custody[i].signature[k]; + } else { + for(int k=0;k(data); + _ts = (int64_t)Utils::loadBigEndian(data + 8); + _id = Utils::loadBigEndian(data + 16); + + const unsigned int rc = Utils::loadBigEndian(data + 20);; + if (rc > ZT_MAX_CAPABILITY_RULES) + return -1; + const int rulesLen = unmarshalVirtualNetworkRules(data + 22,len - 22,_rules,_ruleCount,rc); + if (rulesLen < 0) + return rulesLen; + int p = 22 + rulesLen; + + if (p >= len) + return -1; + _maxCustodyChainLength = data[p++]; + + for(unsigned int i=0;;++i) { + if ((p + ZT_ADDRESS_LENGTH) > len) + return -1; + const Address to(data + p); p += ZT_ADDRESS_LENGTH; + if (!to) break; + if ((i >= _maxCustodyChainLength)||(i >= ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH)) + return -1; + _custody[i].to = to; + if ((p + ZT_ADDRESS_LENGTH) > len) + return -1; + _custody[i].from.setTo(data + p); p += ZT_ADDRESS_LENGTH + 1; + if ((p + 2) > len) + return -1; + const unsigned int sl = Utils::loadBigEndian(data + p); p += 2; + _custody[i].signatureLength = sl; + if ((sl > sizeof(_custody[i].signature))||((p + (int)sl) > len)) + return -1; + memcpy(_custody[i].signature,data + p,sl); p += (int)sl; + } + + if ((p + 2) > len) + return -1; + p += 2 + Utils::loadBigEndian(data + p); + if (p > len) + return -1; + + return p; +} + +int Capability::marshalVirtualNetworkRules(uint8_t data[ZT_VIRTUALNETWORKRULE_MARSHAL_SIZE_MAX],const ZT_VirtualNetworkRule *const rules,const unsigned int ruleCount) +{ + int p = 0; + for(unsigned int i=0;i(data + p,rules[i].v.fwd.address); p += 8; + Utils::storeBigEndian(data + p,rules[i].v.fwd.flags); p += 4; + Utils::storeBigEndian(data + p,rules[i].v.fwd.length); p += 2; + break; + case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS: + case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS: + data[p++] = 5; + Address(rules[i].v.zt).copyTo(data + p); p += ZT_ADDRESS_LENGTH; + break; + case ZT_NETWORK_RULE_MATCH_VLAN_ID: + data[p++] = 2; + Utils::storeBigEndian(data + p,rules[i].v.vlanId); p += 2; + break; + case ZT_NETWORK_RULE_MATCH_VLAN_PCP: + data[p++] = 1; + data[p++] = rules[i].v.vlanPcp; + break; + case ZT_NETWORK_RULE_MATCH_VLAN_DEI: + data[p++] = 1; + data[p++] = rules[i].v.vlanDei; + break; + case ZT_NETWORK_RULE_MATCH_MAC_SOURCE: + case ZT_NETWORK_RULE_MATCH_MAC_DEST: + data[p++] = 6; + MAC(rules[i].v.mac).copyTo(data + p); p += 6; + break; + case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE: + case ZT_NETWORK_RULE_MATCH_IPV4_DEST: + data[p++] = 5; + data[p++] = reinterpret_cast(&(rules[i].v.ipv4.ip))[0]; + data[p++] = reinterpret_cast(&(rules[i].v.ipv4.ip))[1]; + data[p++] = reinterpret_cast(&(rules[i].v.ipv4.ip))[2]; + data[p++] = reinterpret_cast(&(rules[i].v.ipv4.ip))[3]; + data[p++] = rules[i].v.ipv4.mask; + break; + case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE: + case ZT_NETWORK_RULE_MATCH_IPV6_DEST: + data[p++] = 17; + for(int k=0;k<16;++k) + data[p++] = rules[i].v.ipv6.ip[k]; + data[p++] = rules[i].v.ipv6.mask; + break; + case ZT_NETWORK_RULE_MATCH_IP_TOS: + data[p++] = 3; + data[p++] = rules[i].v.ipTos.mask; + data[p++] = rules[i].v.ipTos.value[0]; + data[p++] = rules[i].v.ipTos.value[1]; + break; + case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL: + data[p++] = 1; + data[p++] = rules[i].v.ipProtocol; + break; + case ZT_NETWORK_RULE_MATCH_ETHERTYPE: + data[p++] = 2; + Utils::storeBigEndian(data + p,rules[i].v.etherType); p += 2; + break; + case ZT_NETWORK_RULE_MATCH_ICMP: + data[p++] = 3; + data[p++] = rules[i].v.icmp.type; + data[p++] = rules[i].v.icmp.code; + data[p++] = rules[i].v.icmp.flags; + break; + case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE: + case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE: + data[p++] = 4; + Utils::storeBigEndian(data + p,rules[i].v.port[0]); p += 2; + Utils::storeBigEndian(data + p,rules[i].v.port[1]); p += 2; + break; + case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS: + data[p++] = 8; + Utils::storeBigEndian(data + p,rules[i].v.characteristics); p += 8; + break; + case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE: + data[p++] = 4; + Utils::storeBigEndian(data + p,rules[i].v.frameSize[0]); p += 2; + Utils::storeBigEndian(data + p,rules[i].v.frameSize[1]); p += 2; + break; + case ZT_NETWORK_RULE_MATCH_RANDOM: + data[p++] = 4; + Utils::storeBigEndian(data + p,rules[i].v.randomProbability); p += 4; + break; + case ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE: + case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND: + case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR: + case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR: + case ZT_NETWORK_RULE_MATCH_TAGS_EQUAL: + case ZT_NETWORK_RULE_MATCH_TAG_SENDER: + case ZT_NETWORK_RULE_MATCH_TAG_RECEIVER: + data[p++] = 8; + Utils::storeBigEndian(data + p,rules[i].v.tag.id); p += 4; + Utils::storeBigEndian(data + p,rules[i].v.tag.value); p += 4; + break; + case ZT_NETWORK_RULE_MATCH_INTEGER_RANGE: + data[p++] = 19; + Utils::storeBigEndian(data + p,rules[i].v.intRange.start); p += 8; + Utils::storeBigEndian(data + p,rules[i].v.intRange.start + (uint64_t)rules[i].v.intRange.end); p += 8; + Utils::storeBigEndian(data + p,rules[i].v.intRange.idx); p += 2; + data[p++] = rules[i].v.intRange.format; + break; + } + } + return p; +} + +int Capability::unmarshalVirtualNetworkRules(const uint8_t *const data,const int len,ZT_VirtualNetworkRule *const rules,unsigned int &ruleCount,const unsigned int maxRuleCount) +{ + int p = 0; + unsigned int rc = 0; + while (rc < maxRuleCount) { + if (p >= len) + return -1; + rules[ruleCount].t = data[p++]; + const int fieldLen = (int)data[p++]; + if ((p + fieldLen) > len) + return -1; + switch((ZT_VirtualNetworkRuleType)(rules[ruleCount].t & 0x3f)) { + default: + break; + case ZT_NETWORK_RULE_ACTION_TEE: + case ZT_NETWORK_RULE_ACTION_WATCH: + case ZT_NETWORK_RULE_ACTION_REDIRECT: + if ((p + 14) > len) return -1; + rules[ruleCount].v.fwd.address = Utils::loadBigEndian(data + p); p += 8; + rules[ruleCount].v.fwd.flags = Utils::loadBigEndian(data + p); p += 4; + rules[ruleCount].v.fwd.length = Utils::loadBigEndian(data + p); p += 2; + break; + case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS: + case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS: + if ((p + ZT_ADDRESS_LENGTH) > len) return -1; + rules[ruleCount].v.zt = Address(data + p).toInt(); p += ZT_ADDRESS_LENGTH; + break; + case ZT_NETWORK_RULE_MATCH_VLAN_ID: + if ((p + 2) > len) return -1; + rules[ruleCount].v.vlanId = Utils::loadBigEndian(data + p); p += 2; + break; + case ZT_NETWORK_RULE_MATCH_VLAN_PCP: + if ((p + 1) > len) return -1; + rules[ruleCount].v.vlanPcp = data[p++]; + break; + case ZT_NETWORK_RULE_MATCH_VLAN_DEI: + if ((p + 1) > len) return -1; + rules[ruleCount].v.vlanDei = data[p++]; + break; + case ZT_NETWORK_RULE_MATCH_MAC_SOURCE: + case ZT_NETWORK_RULE_MATCH_MAC_DEST: + if ((p + 6) > len) return -1; + memcpy(rules[ruleCount].v.mac,data + p,6); p += 6; + break; + case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE: + case ZT_NETWORK_RULE_MATCH_IPV4_DEST: + if ((p + 5) > len) return -1; + memcpy(&(rules[ruleCount].v.ipv4.ip),data + p,4); p += 4; + rules[ruleCount].v.ipv4.mask = data[p++]; + break; + case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE: + case ZT_NETWORK_RULE_MATCH_IPV6_DEST: + if ((p + 17) > len) return -1; + memcpy(rules[ruleCount].v.ipv6.ip,data + p,16); p += 16; + rules[ruleCount].v.ipv6.mask = data[p++]; + break; + case ZT_NETWORK_RULE_MATCH_IP_TOS: + if ((p + 3) > len) return -1; + rules[ruleCount].v.ipTos.mask = data[p++]; + rules[ruleCount].v.ipTos.value[0] = data[p++]; + rules[ruleCount].v.ipTos.value[1] = data[p++]; + break; + case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL: + if ((p + 1) > len) return -1; + rules[ruleCount].v.ipProtocol = data[p++]; + break; + case ZT_NETWORK_RULE_MATCH_ETHERTYPE: + if ((p + 2) > len) return -1; + rules[ruleCount].v.etherType = Utils::loadBigEndian(data + p); p += 2; + break; + case ZT_NETWORK_RULE_MATCH_ICMP: + if ((p + 3) > len) return -1; + rules[ruleCount].v.icmp.type = data[p++]; + rules[ruleCount].v.icmp.code = data[p++]; + rules[ruleCount].v.icmp.flags = data[p++]; + break; + case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE: + case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE: + if ((p + 4) > len) return -1; + rules[ruleCount].v.port[0] = Utils::loadBigEndian(data + p); p += 2; + rules[ruleCount].v.port[1] = Utils::loadBigEndian(data + p); p += 2; + break; + case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS: + if ((p + 8) > len) return -1; + rules[ruleCount].v.characteristics = Utils::loadBigEndian(data + p); p += 8; + break; + case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE: + if ((p + 4) > len) return -1; + rules[ruleCount].v.frameSize[0] = Utils::loadBigEndian(data + p); p += 2; + rules[ruleCount].v.frameSize[1] = Utils::loadBigEndian(data + p); p += 2; + break; + case ZT_NETWORK_RULE_MATCH_RANDOM: + if ((p + 4) > len) return -1; + rules[ruleCount].v.randomProbability = Utils::loadBigEndian(data + p); p += 4; + break; + case ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE: + case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND: + case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR: + case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR: + case ZT_NETWORK_RULE_MATCH_TAGS_EQUAL: + case ZT_NETWORK_RULE_MATCH_TAG_SENDER: + case ZT_NETWORK_RULE_MATCH_TAG_RECEIVER: + if ((p + 4) > len) return -1; + rules[ruleCount].v.tag.id = Utils::loadBigEndian(data + p); p += 4; + rules[ruleCount].v.tag.value = Utils::loadBigEndian(data + p); p += 4; + break; + case ZT_NETWORK_RULE_MATCH_INTEGER_RANGE: + if ((p + 19) > len) return -1; + rules[ruleCount].v.intRange.start = Utils::loadBigEndian(data + p); p += 8; + rules[ruleCount].v.intRange.end = (uint32_t)(Utils::loadBigEndian(data + p) - rules[ruleCount].v.intRange.start); p += 8; + rules[ruleCount].v.intRange.idx = Utils::loadBigEndian(data + p); p += 2; + rules[ruleCount].v.intRange.format = data[p++]; + break; + } + p += fieldLen; + ++rc; + } + ruleCount = rc; + return p; +} + } // namespace ZeroTier diff --git a/node/Capability.hpp b/node/Capability.hpp index 55caa403c..eb78b3e97 100644 --- a/node/Capability.hpp +++ b/node/Capability.hpp @@ -23,9 +23,13 @@ #include "Address.hpp" #include "C25519.hpp" #include "Utils.hpp" -#include "Buffer.hpp" #include "Identity.hpp" +#define ZT_VIRTUALNETWORKRULE_MARSHAL_SIZE_MAX 21 + +#define ZT_CAPABILITY__CUSTODY_CHAIN_ITEM_MARSHAL_SIZE_MAX (5 + 5 + 2 + ZT_SIGNATURE_BUFFER_SIZE) +#define ZT_CAPABILITY_MARSHAL_SIZE_MAX (8 + 8 + 4 + 1 + 2 + (ZT_VIRTUALNETWORKRULE_MARSHAL_SIZE_MAX * ZT_MAX_CAPABILITY_RULES) + 2 + (ZT_CAPABILITY__CUSTODY_CHAIN_ITEM_MARSHAL_SIZE_MAX * ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH)) + namespace ZeroTier { class RuntimeEnvironment; @@ -79,7 +83,7 @@ public: * @param rules Network flow rules for this capability * @param ruleCount Number of flow rules */ - ZT_ALWAYS_INLINE Capability(uint32_t id,uint64_t nwid,int64_t ts,unsigned int mccl,const ZT_VirtualNetworkRule *rules,unsigned int ruleCount) : + ZT_ALWAYS_INLINE Capability(const uint32_t id,const uint64_t nwid,const int64_t ts,const unsigned int mccl,const ZT_VirtualNetworkRule *const rules,const unsigned int ruleCount) : _nwid(nwid), _ts(ts), _id(id), @@ -121,7 +125,7 @@ public: ZT_ALWAYS_INLINE Address issuedTo() const { Address i2; - for(unsigned int i=0;i - static inline void serializeRules(Buffer &b,const ZT_VirtualNetworkRule *rules,unsigned int ruleCount) - { - for(unsigned int i=0;i - static inline void deserializeRules(const Buffer &b,unsigned int &p,ZT_VirtualNetworkRule *rules,unsigned int &ruleCount,const unsigned int maxRuleCount) - { - while ((ruleCount < maxRuleCount)&&(p < b.size())) { - rules[ruleCount].t = (uint8_t)b[p++]; - const unsigned int fieldLen = (unsigned int)b[p++]; - switch((ZT_VirtualNetworkRuleType)(rules[ruleCount].t & 0x3f)) { - default: - break; - case ZT_NETWORK_RULE_ACTION_TEE: - case ZT_NETWORK_RULE_ACTION_WATCH: - case ZT_NETWORK_RULE_ACTION_REDIRECT: - rules[ruleCount].v.fwd.address = b.template at(p); - rules[ruleCount].v.fwd.flags = b.template at(p + 8); - rules[ruleCount].v.fwd.length = b.template at(p + 12); - break; - case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS: - case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS: - rules[ruleCount].v.zt = Address(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH).toInt(); - break; - case ZT_NETWORK_RULE_MATCH_VLAN_ID: - rules[ruleCount].v.vlanId = b.template at(p); - break; - case ZT_NETWORK_RULE_MATCH_VLAN_PCP: - rules[ruleCount].v.vlanPcp = (uint8_t)b[p]; - break; - case ZT_NETWORK_RULE_MATCH_VLAN_DEI: - rules[ruleCount].v.vlanDei = (uint8_t)b[p]; - break; - case ZT_NETWORK_RULE_MATCH_MAC_SOURCE: - case ZT_NETWORK_RULE_MATCH_MAC_DEST: - memcpy(rules[ruleCount].v.mac,b.field(p,6),6); - break; - case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE: - case ZT_NETWORK_RULE_MATCH_IPV4_DEST: - memcpy(&(rules[ruleCount].v.ipv4.ip),b.field(p,4),4); - rules[ruleCount].v.ipv4.mask = (uint8_t)b[p + 4]; - break; - case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE: - case ZT_NETWORK_RULE_MATCH_IPV6_DEST: - memcpy(rules[ruleCount].v.ipv6.ip,b.field(p,16),16); - rules[ruleCount].v.ipv6.mask = (uint8_t)b[p + 16]; - break; - case ZT_NETWORK_RULE_MATCH_IP_TOS: - rules[ruleCount].v.ipTos.mask = (uint8_t)b[p]; - rules[ruleCount].v.ipTos.value[0] = (uint8_t)b[p+1]; - rules[ruleCount].v.ipTos.value[1] = (uint8_t)b[p+2]; - break; - case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL: - rules[ruleCount].v.ipProtocol = (uint8_t)b[p]; - break; - case ZT_NETWORK_RULE_MATCH_ETHERTYPE: - rules[ruleCount].v.etherType = b.template at(p); - break; - case ZT_NETWORK_RULE_MATCH_ICMP: - rules[ruleCount].v.icmp.type = (uint8_t)b[p]; - rules[ruleCount].v.icmp.code = (uint8_t)b[p+1]; - rules[ruleCount].v.icmp.flags = (uint8_t)b[p+2]; - break; - case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE: - case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE: - rules[ruleCount].v.port[0] = b.template at(p); - rules[ruleCount].v.port[1] = b.template at(p + 2); - break; - case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS: - rules[ruleCount].v.characteristics = b.template at(p); - break; - case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE: - rules[ruleCount].v.frameSize[0] = b.template at(p); - rules[ruleCount].v.frameSize[1] = b.template at(p + 2); - break; - case ZT_NETWORK_RULE_MATCH_RANDOM: - rules[ruleCount].v.randomProbability = b.template at(p); - break; - case ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE: - case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND: - case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR: - case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR: - case ZT_NETWORK_RULE_MATCH_TAGS_EQUAL: - case ZT_NETWORK_RULE_MATCH_TAG_SENDER: - case ZT_NETWORK_RULE_MATCH_TAG_RECEIVER: - rules[ruleCount].v.tag.id = b.template at(p); - rules[ruleCount].v.tag.value = b.template at(p + 4); - break; - case ZT_NETWORK_RULE_MATCH_INTEGER_RANGE: - rules[ruleCount].v.intRange.start = b.template at(p); - rules[ruleCount].v.intRange.end = (uint32_t)(b.template at(p + 8) - rules[ruleCount].v.intRange.start); - rules[ruleCount].v.intRange.idx = b.template at(p + 16); - rules[ruleCount].v.intRange.format = (uint8_t)b[p + 18]; - break; - } - p += fieldLen; - ++ruleCount; - } - } - - template - inline void serialize(Buffer &b,const bool forSign = false) const - { - if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL); - - // These are the same between Tag and Capability - b.append(_nwid); - b.append(_ts); - b.append(_id); - - b.append((uint16_t)_ruleCount); - serializeRules(b,_rules,_ruleCount); - b.append((uint8_t)_maxCustodyChainLength); - - if (!forSign) { - for(unsigned int i=0;;++i) { - if ((i < _maxCustodyChainLength)&&(i < ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH)&&(_custody[i].to)) { - _custody[i].to.appendTo(b); - _custody[i].from.appendTo(b); - b.append((uint8_t)1); - b.append((uint16_t)_custody[i].signatureLength); - b.append(_custody[i].signature,_custody[i].signatureLength); - } else { - b.append((unsigned char)0,ZT_ADDRESS_LENGTH); // zero 'to' terminates chain - break; - } - } - } - - // This is the size of any additional fields, currently 0. - b.append((uint16_t)0); - - if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL); - } - - template - inline unsigned int deserialize(const Buffer &b,unsigned int startAt = 0) - { - *this = Capability(); - - unsigned int p = startAt; - - _nwid = b.template at(p); p += 8; - _ts = b.template at(p); p += 8; - _id = b.template at(p); p += 4; - - const unsigned int rc = b.template at(p); p += 2; - if (rc > ZT_MAX_CAPABILITY_RULES) - throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW; - deserializeRules(b,p,_rules,_ruleCount,rc); - - _maxCustodyChainLength = (unsigned int)b[p++]; - if ((_maxCustodyChainLength < 1)||(_maxCustodyChainLength > ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH)) - throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW; - - for(unsigned int i=0;;++i) { - const Address to(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH; - if (!to) - break; - if ((i >= _maxCustodyChainLength)||(i >= ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH)) - throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW; - _custody[i].to = to; - _custody[i].from.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH; - if (b[p++] == 1) { - _custody[i].signatureLength = b.template at(p); - if (_custody[i].signatureLength > sizeof(_custody[i].signature)) - throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN; - p += 2; - memcpy(_custody[i].signature,b.field(p,_custody[i].signatureLength),_custody[i].signatureLength); p += _custody[i].signatureLength; - } else { - p += 2 + b.template at(p); - } - } - - p += 2 + b.template at(p); - if (p > b.size()) - throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW; - - return (p - startAt); - } + static int marshalVirtualNetworkRules(uint8_t data[ZT_VIRTUALNETWORKRULE_MARSHAL_SIZE_MAX],const ZT_VirtualNetworkRule *rules,unsigned int ruleCount); + static int unmarshalVirtualNetworkRules(const uint8_t *data,int len,ZT_VirtualNetworkRule *rules,unsigned int &ruleCount,unsigned int maxRuleCount); // Provides natural sort order by ID ZT_ALWAYS_INLINE bool operator<(const Capability &c) const { return (_id < c._id); } diff --git a/node/Constants.hpp b/node/Constants.hpp index 14e10c78a..d03bc1792 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -52,9 +52,9 @@ #define ZT_DEFAULT_MTU 2800 /** - * Maximum number of packet fragments we'll support (protocol limit: 16) + * Maximum number of packet fragments we'll support (11 is the maximum that will fit in a Buf) */ -#define ZT_MAX_PACKET_FRAGMENTS 12 +#define ZT_MAX_PACKET_FRAGMENTS 11 /** * Size of RX queue in packets diff --git a/node/Identity.cpp b/node/Identity.cpp index 6385d9eef..c7b7cb1f0 100644 --- a/node/Identity.cpp +++ b/node/Identity.cpp @@ -94,7 +94,7 @@ void Identity::generate(const Type t) char *const genmem = new char[ZT_IDENTITY_GEN_MEMORY]; do { C25519::generateSatisfying(_Identity_generate_cond(digest,genmem),_pub.c25519,_priv.c25519); - _address.setTo(digest + 59,ZT_ADDRESS_LENGTH); // last 5 bytes are address + _address.setTo(digest + 59); // last 5 bytes are address } while (_address.isReserved()); delete [] genmem; @@ -379,7 +379,7 @@ bool Identity::fromString(const char *str) int Identity::marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX],const bool includePrivate) const { - _address.copyTo(data,ZT_ADDRESS_LENGTH); + _address.copyTo(data); switch(_type) { case C25519: diff --git a/node/MAC.hpp b/node/MAC.hpp index 287c59925..7d969af5f 100644 --- a/node/MAC.hpp +++ b/node/MAC.hpp @@ -32,8 +32,6 @@ class MAC { public: ZT_ALWAYS_INLINE MAC() : _m(0ULL) {} - ZT_ALWAYS_INLINE MAC(const MAC &m) : _m(m._m) {} - ZT_ALWAYS_INLINE MAC(const unsigned char a,const unsigned char b,const unsigned char c,const unsigned char d,const unsigned char e,const unsigned char f) : _m( ((((uint64_t)a) & 0xffULL) << 40U) | ((((uint64_t)b) & 0xffULL) << 32U) | @@ -41,7 +39,7 @@ public: ((((uint64_t)d) & 0xffULL) << 16U) | ((((uint64_t)e) & 0xffULL) << 8U) | (((uint64_t)f) & 0xffULL) ) {} - ZT_ALWAYS_INLINE MAC(const void *bits,unsigned int len) { setTo(bits,len); } + ZT_ALWAYS_INLINE MAC(const uint8_t b[6]) { setTo(b); } ZT_ALWAYS_INLINE MAC(const Address &ztaddr,uint64_t nwid) { fromAddress(ztaddr,nwid); } ZT_ALWAYS_INLINE MAC(const uint64_t m) : _m(m & 0xffffffffffffULL) {} @@ -64,30 +62,17 @@ public: * @param bits Raw MAC in big-endian byte order * @param len Length, must be >= 6 or result is zero */ - ZT_ALWAYS_INLINE void setTo(const void *bits,unsigned int len) + ZT_ALWAYS_INLINE void setTo(const uint8_t b[6]) { - if (len < 6) { - _m = 0ULL; - return; - } - const uint8_t *const b = (const uint8_t *)bits; - _m = (uint64_t)b[0] << 40U; - _m |= (uint64_t)b[1] << 32U; - _m |= (uint64_t)b[2] << 24U; - _m |= (uint64_t)b[3] << 16U; - _m |= (uint64_t)b[4] << 8U; - _m |= (uint64_t)b[5]; + _m = ((uint64_t)b[0] << 40U) | ((uint64_t)b[1] << 32U) | ((uint64_t)b[2] << 24U) | ((uint64_t)b[3] << 16U) | ((uint64_t)b[4] << 8U) | (uint64_t)b[5]; } /** * @param buf Destination buffer for MAC in big-endian byte order * @param len Length of buffer, must be >= 6 or nothing is copied */ - ZT_ALWAYS_INLINE void copyTo(void *buf,unsigned int len) const + ZT_ALWAYS_INLINE void copyTo(uint8_t b[6]) const { - if (len < 6) - return; - uint8_t *const b = (uint8_t *)buf; b[0] = (uint8_t)(_m >> 40U); b[1] = (uint8_t)(_m >> 32U); b[2] = (uint8_t)(_m >> 24U); @@ -96,23 +81,6 @@ public: b[5] = (uint8_t)_m; } - /** - * Append to a buffer in big-endian byte order - * - * @param b Buffer to append to - */ - template - ZT_ALWAYS_INLINE void appendTo(Buffer &b) const - { - uint8_t *p = (uint8_t *)b.appendField(6); - *(p++) = (unsigned char)((_m >> 40) & 0xff); - *(p++) = (unsigned char)((_m >> 32) & 0xff); - *(p++) = (unsigned char)((_m >> 24) & 0xff); - *(p++) = (unsigned char)((_m >> 16) & 0xff); - *(p++) = (unsigned char)((_m >> 8) & 0xff); - *p = (unsigned char)(_m & 0xff); - } - /** * @return True if this is broadcast (all 0xff) */ @@ -131,13 +99,13 @@ public: */ ZT_ALWAYS_INLINE void fromAddress(const Address &ztaddr,uint64_t nwid) { - uint64_t m = ((uint64_t)firstOctetForNetwork(nwid)) << 40; + uint64_t m = ((uint64_t)firstOctetForNetwork(nwid)) << 40U; m |= ztaddr.toInt(); // a is 40 bits - m ^= ((nwid >> 8) & 0xff) << 32; - m ^= ((nwid >> 16) & 0xff) << 24; - m ^= ((nwid >> 24) & 0xff) << 16; - m ^= ((nwid >> 32) & 0xff) << 8; - m ^= (nwid >> 40) & 0xff; + m ^= ((nwid >> 8U) & 0xffU) << 32U; + m ^= ((nwid >> 16U) & 0xffU) << 24U; + m ^= ((nwid >> 24U) & 0xffU) << 16U; + m ^= ((nwid >> 32U) & 0xffU) << 8U; + m ^= (nwid >> 40U) & 0xffU; _m = m; } @@ -151,11 +119,11 @@ public: ZT_ALWAYS_INLINE Address toAddress(uint64_t nwid) const { uint64_t a = _m & 0xffffffffffULL; // least significant 40 bits of MAC are formed from address - a ^= ((nwid >> 8) & 0xff) << 32; // ... XORed with bits 8-48 of the nwid in little-endian byte order, so unmask it - a ^= ((nwid >> 16) & 0xff) << 24; - a ^= ((nwid >> 24) & 0xff) << 16; - a ^= ((nwid >> 32) & 0xff) << 8; - a ^= (nwid >> 40) & 0xff; + a ^= ((nwid >> 8U) & 0xffU) << 32U; // ... XORed with bits 8-48 of the nwid in little-endian byte order, so unmask it + a ^= ((nwid >> 16U) & 0xffU) << 24U; + a ^= ((nwid >> 24U) & 0xffU) << 16U; + a ^= ((nwid >> 32U) & 0xffU) << 8U; + a ^= (nwid >> 40U) & 0xffU; return Address(a); } @@ -165,7 +133,7 @@ public: */ static ZT_ALWAYS_INLINE unsigned char firstOctetForNetwork(uint64_t nwid) { - unsigned char a = ((unsigned char)(nwid & 0xfe) | 0x02); // locally administered, not multicast, from LSB of network ID + const uint8_t a = ((uint8_t)(nwid & 0xfeU) | 0x02U); // locally administered, not multicast, from LSB of network ID return ((a == 0x52) ? 0x32 : a); // blacklist 0x52 since it's used by KVM, libvirt, and other popular virtualization engines... seems de-facto standard on Linux } @@ -205,11 +173,6 @@ public: return buf; } - ZT_ALWAYS_INLINE MAC &operator=(const MAC &m) - { - _m = m._m; - return *this; - } ZT_ALWAYS_INLINE MAC &operator=(const uint64_t m) { _m = m & 0xffffffffffffULL; diff --git a/node/Protocol.cpp b/node/Protocol.cpp new file mode 100644 index 000000000..4b31e8bf4 --- /dev/null +++ b/node/Protocol.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (c)2013-2020 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2024-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2.0 of the Apache License. + */ +/****/ + +#include "Protocol.hpp" +#include "Buf.hpp" +#include "Utils.hpp" + +#if defined(__GCC__) && (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64)) +#define ZT_PACKET_USE_ATOMIC_INTRINSICS +#endif +#ifndef ZT_PACKET_USE_ATOMIC_INTRINSICS +#include +#endif + +namespace ZeroTier { +namespace Protocol { + +namespace { + +const uint8_t ZEROES32[32] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + +/** + * Deterministically mangle a 256-bit crypto key based on packet + * + * This uses extra data from the packet to mangle the secret, giving us an + * effective IV that is somewhat more than 64 bits. This is "free" for + * Salsa20 since it has negligible key setup time so using a different + * key each time is fine. + * + * @param in Input key (32 bytes) + * @param out Output buffer (32 bytes) + */ +ZT_ALWAYS_INLINE void _salsa20MangleKey(const uint8_t *const in,uint8_t *const out,const Buf< Header<> > &packet,const unsigned int packetSize) +{ + // IV and source/destination addresses. Using the addresses divides the + // key space into two halves-- A->B and B->A (since order will change). + for(int i=0;i<18;++i) // 8 + (ZT_ADDRESS_LENGTH * 2) == 18 + out[i] = in[i] ^ packet.data.bytes[i]; + + // Flags, but with hop count masked off. Hop count is altered by forwarding + // nodes. It's one of the only parts of a packet modifiable by people + // without the key. + out[18] = in[18] ^ (packet.data.fields.flags & 0xf8U); + + // Raw packet size in bytes -- thus each packet size defines a new + // key space. + out[19] = in[19] ^ (uint8_t)packetSize; + out[20] = in[20] ^ (uint8_t)(packetSize >> 8U); // little endian + + // Rest of raw key is used unchanged + for(int i=21;i<32;++i) + out[i] = in[i]; +} + +unsigned long long _initPacketID() +{ + unsigned long long tmp = 0; + Utils::getSecureRandom(&tmp,sizeof(tmp)); + tmp >>= 31U; + tmp |= (((uint64_t)time(nullptr)) & 0xffffffffULL) << 33U; + return tmp; +} +#ifdef ZT_PACKET_USE_ATOMIC_INTRINSICS +unsigned long long _packetIdCtr = _initPacketID(); +#else +static std::atomic _packetIdCtr(_initPacketID()); +#endif + +} // anonymous namespace + +void armor(Buf< Header<> > &packet,const unsigned int packetSize,const uint8_t key[ZT_PEER_SECRET_KEY_LENGTH],const uint8_t cipherSuite) +{ + packet.data.fields.flags = (packet.data.fields.flags & 0xc7U) | ((cipherSuite << 3U) & 0x38U); // FFCCCHHH + if (cipherSuite == ZT_PROTO_CIPHER_SUITE__AES_GCM) { + // TODO + } else if (cipherSuite != ZT_PROTO_CIPHER_SUITE__NONE) { + uint8_t mangledKey[ZT_PEER_SECRET_KEY_LENGTH],macKey[ZT_POLY1305_KEY_LEN]; + uint64_t mac[2]; + + _salsa20MangleKey(key,mangledKey,packet,packetSize); + Salsa20 s20(mangledKey,&(packet.data.fields.packetId)); + s20.crypt12(ZEROES32,macKey,sizeof(macKey)); + + uint8_t *payload = packet.data.bytes + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START; + const unsigned int payloadLen = packetSize - ZT_PROTO_PACKET_ENCRYPTED_SECTION_START; + + if (cipherSuite == ZT_PROTO_CIPHER_SUITE__POLY1305_SALSA2012) + s20.crypt12(payload,payload,payloadLen); + + poly1305(mac,payload,payloadLen,macKey); + packet.data.fields.mac = mac[0]; + } +} + +int dearmor(Buf< Header<> > &packet,const unsigned int packetSize,const uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) +{ + const int cipherSuite = (int)(packet.data.fields.flags & 0x38U); + if (cipherSuite == ZT_PROTO_CIPHER_SUITE__AES_GCM) { + // TODO + } else if (cipherSuite != ZT_PROTO_CIPHER_SUITE__NONE) { + uint8_t mangledKey[ZT_PEER_SECRET_KEY_LENGTH],macKey[ZT_POLY1305_KEY_LEN]; + uint64_t mac[2]; + + _salsa20MangleKey(key,mangledKey,packet,packetSize); + Salsa20 s20(mangledKey,&(packet.data.fields.packetId)); + s20.crypt12(ZEROES32,macKey,sizeof(macKey)); + + uint8_t *payload = packet.data.bytes + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START; + const unsigned int payloadLen = packetSize - ZT_PROTO_PACKET_ENCRYPTED_SECTION_START; + + if (cipherSuite == ZT_PROTO_CIPHER_SUITE__POLY1305_SALSA2012) + s20.crypt12(payload,payload,payloadLen); + + poly1305(mac,payload,payloadLen,macKey); + if (packet.data.fields.mac != mac[0]) + return -1; + } + return cipherSuite; +} + +unsigned int compress(Buf< Header<> > &packet,const unsigned int packetSize) +{ + uint8_t tmp[ZT_BUF_MEM_SIZE + 32]; + + if ((packet.data.fields.verb & ZT_PROTO_VERB_FLAG_COMPRESSED) != 0) // sanity check for multiple calls to compress() + return packetSize; + + const unsigned int uncompressedLen = packetSize - ZT_PROTO_PACKET_PAYLOAD_START; + const int compressedLen = LZ4_compress_fast( + reinterpret_cast(packet.data.bytes + ZT_PROTO_PACKET_PAYLOAD_START), + reinterpret_cast(tmp), + (int)uncompressedLen, + sizeof(tmp) - ZT_PROTO_PACKET_PAYLOAD_START, + 2); + if ((compressedLen > 0)&&(compressedLen < uncompressedLen)) { + packet.data.fields.verb |= ZT_PROTO_VERB_FLAG_COMPRESSED; + memcpy(packet.data.bytes + ZT_PROTO_PACKET_PAYLOAD_START,tmp,compressedLen); + return (unsigned int)compressedLen + ZT_PROTO_PACKET_PAYLOAD_START; + } + + return packetSize; +} + +int uncompress(Buf< Header<> > &packet,const unsigned int packetSize) +{ + uint8_t tmp[ZT_BUF_MEM_SIZE]; + + if ((packet.data.fields.verb & ZT_PROTO_VERB_FLAG_COMPRESSED) == 0) + return (int)packetSize; + + const int uncompressedLen = LZ4_decompress_safe( + reinterpret_cast(packet.data.bytes + ZT_PROTO_PACKET_PAYLOAD_START), + reinterpret_cast(tmp), + (int)(packetSize - ZT_PROTO_PACKET_PAYLOAD_START), + sizeof(tmp) - ZT_PROTO_PACKET_PAYLOAD_START); + + if ((uncompressedLen > 0)&&(uncompressedLen <= (sizeof(tmp) - ZT_PROTO_PACKET_PAYLOAD_START))) { + packet.data.fields.verb &= (uint8_t)(~((uint8_t)ZT_PROTO_VERB_FLAG_COMPRESSED)); + memcpy(packet.data.bytes + ZT_PROTO_PACKET_PAYLOAD_START,tmp,uncompressedLen); + return uncompressedLen + ZT_PROTO_PACKET_PAYLOAD_START; + } + return -1; +} + +uint64_t getPacketId() +{ +#ifdef ZT_PACKET_USE_ATOMIC_INTRINSICS + return __sync_add_and_fetch(&_packetIdCtr,1ULL); +#else + return ++_packetIdCtr; +#endif +} + +} // namespace Protocol +} // namespace ZeroTier diff --git a/node/Protocol.hpp b/node/Protocol.hpp new file mode 100644 index 000000000..9cb94b649 --- /dev/null +++ b/node/Protocol.hpp @@ -0,0 +1,928 @@ +/* + * Copyright (c)2013-2020 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2024-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2.0 of the Apache License. + */ +/****/ + +#ifndef ZT_PROTOCOL_HPP +#define ZT_PROTOCOL_HPP + +#include "Constants.hpp" +#include "AES.hpp" +#include "Salsa20.hpp" +#include "Poly1305.hpp" +#include "LZ4.hpp" +#include "Buf.hpp" + +/** + * Protocol version -- incremented only for major changes + * + * 1 - 0.2.0 ... 0.2.5 + * 2 - 0.3.0 ... 0.4.5 + * + Added signature and originating peer to multicast frame + * + Double size of multicast frame bloom filter + * 3 - 0.5.0 ... 0.6.0 + * + Yet another multicast redesign + * + New crypto completely changes key agreement cipher + * 4 - 0.6.0 ... 1.0.6 + * + BREAKING CHANGE: New identity format based on hashcash design + * 5 - 1.1.0 ... 1.1.5 + * + Supports echo + * + Supports in-band world (root server definition) updates + * + Clustering! (Though this will work with protocol v4 clients.) + * + Otherwise backward compatible with protocol v4 + * 6 - 1.1.5 ... 1.1.10 + * + Network configuration format revisions including binary values + * 7 - 1.1.10 ... 1.1.17 + * + Introduce trusted paths for local SDN use + * 8 - 1.1.17 ... 1.2.0 + * + Multipart network configurations for large network configs + * + Tags and Capabilities + * + inline push of CertificateOfMembership deprecated + * 9 - 1.2.0 ... 1.2.14 + * 10 - 1.4.0 ... 1.6.0 + * + Multipath capability and load balancing + * 11 - 2.0.0 ... CURRENT + * + Peer-to-peer multicast replication + * + Old planet/moon stuff is DEAD! + * + AES encryption support + * + NIST P-384 (type 1) identities + * + Ephemeral keys + */ +#define ZT_PROTO_VERSION 11 + +/** + * Packet buffer size (can be changed) + */ +#define ZT_PROTO_MAX_PACKET_LENGTH (ZT_MAX_PACKET_FRAGMENTS * ZT_DEFAULT_PHYSMTU) + +/** + * Minimum viable packet length (a.k.a. header length) + */ +#define ZT_PROTO_MIN_PACKET_LENGTH 28 + +/** + * Index at which the encrypted section of a packet begins + */ +#define ZT_PROTO_PACKET_ENCRYPTED_SECTION_START 27 + + +/** + * Index at which packet payload begins (after verb) + */ +#define ZT_PROTO_PACKET_PAYLOAD_START 28 + +/** + * Maximum hop count allowed by packet structure (3 bits, 0-7) + * + * This is a protocol constant. It's the maximum allowed by the length + * of the hop counter -- three bits. See node/Constants.hpp for the + * pragmatic forwarding limit, which is typically lower. + */ +#define ZT_PROTO_MAX_HOPS 7 + +/** + * NONE/Poly1305 (using Salsa20/12 to generate poly1305 key) + */ +#define ZT_PROTO_CIPHER_SUITE__POLY1305_NONE 0 + +/** + * Salsa2012/Poly1305 + */ +#define ZT_PROTO_CIPHER_SUITE__POLY1305_SALSA2012 1 + +/** + * No encryption or authentication at all + * + * For trusted paths the MAC field is the trusted path ID. + */ +#define ZT_PROTO_CIPHER_SUITE__NONE 2 + +/** + * AES-GCM with AES-256 + */ +#define ZT_PROTO_CIPHER_SUITE__AES_GCM 3 + +/** + * Magic number indicating a fragment + */ +#define ZT_PACKET_FRAGMENT_INDICATOR 0xff + +/** + * Minimum viable fragment length + */ +#define ZT_PROTO_MIN_FRAGMENT_LENGTH 16 + +/** + * Index at which packet fragment payload starts + */ +#define ZT_PROTO_PACKET_FRAGMENT_PAYLOAD_START_AT 16 + +/** + * Header flag indicating that a packet is fragmented and more fragments should be expected + */ +#define ZT_PROTO_FLAG_FRAGMENTED 0x40 + +/** + * Verb flag indicating payload is compressed with LZ4 + */ +#define ZT_PROTO_VERB_FLAG_COMPRESSED 0x80 + +/** + * Signed locator for this node + */ +#define ZT_PROTO_HELLO_NODE_META_LOCATOR "l" + +/** + * Ephemeral C25519 public key + */ +#define ZT_PROTO_HELLO_NODE_META_EPHEMERAL_KEY_C25519 "e0" + +/** + * Ephemeral NIST P-384 public key + */ +#define ZT_PROTO_HELLO_NODE_META_EPHEMERAL_KEY_P384 "e1" + +/** + * Addresses of ZeroTier nodes to whom this node will relay or one entry for 0000000000 if promiscuous. + */ +#define ZT_PROTO_HELLO_NODE_META_WILL_RELAY_TO "r" + +/** + * X coordinate of your node (sent in OK(HELLO)) + */ +#define ZT_PROTO_HELLO_NODE_META_LOCATION_X "gX" + +/** + * Y coordinate of your node (sent in OK(HELLO)) + */ +#define ZT_PROTO_HELLO_NODE_META_LOCATION_Y "gY" + +/** + * Z coordinate of your node (sent in OK(HELLO)) + */ +#define ZT_PROTO_HELLO_NODE_META_LOCATION_Z "gZ" + +/****************************************************************************/ + +/* + * Packet format: + * <[8] 64-bit packet ID / crypto IV> + * <[5] destination ZT address> + * <[5] source ZT address> + * <[1] flags/cipher/hops> + * <[8] 64-bit MAC (or trusted path ID in trusted path mode)> + * [... -- begin encryption envelope -- ...] + * <[1] encrypted flags (MS 3 bits) and verb (LS 5 bits)> + * [... verb-specific payload ...] + * + * Packets smaller than 28 bytes are invalid and silently discarded. + * + * The flags/cipher/hops bit field is: FFCCCHHH where C is a 3-bit cipher + * selection allowing up to 7 cipher suites, F is outside-envelope flags, + * and H is hop count. + * + * The three-bit hop count is the only part of a packet that is mutable in + * transit without invalidating the MAC. All other bits in the packet are + * immutable. This is because intermediate nodes can increment the hop + * count up to 7 (protocol max). + * + * For unencrypted packets, MAC is computed on plaintext. Only HELLO is ever + * sent in the clear, as it's the "here is my public key" message. + * + * Fragments are sent if a packet is larger than UDP MTU. The first fragment + * is sent with its normal header with the fragmented flag set. Remaining + * fragments are sent this way. + * + * The fragmented bit indicates that there is at least one fragment. Fragments + * themselves contain the total, so the receiver must "learn" this from the + * first fragment it receives. + * + * Fragments are sent with the following format: + * <[8] packet ID of packet whose fragment this belongs to> + * <[5] destination ZT address> + * <[1] 0xff, a reserved address, signals that this isn't a normal packet> + * <[1] total fragments (most significant 4 bits), fragment no (LS 4 bits)> + * <[1] ZT hop count (top 5 bits unused and must be zero)> + * <[...] fragment data> + * + * The protocol supports a maximum of 16 fragments. If a fragment is received + * before its main packet header, it should be cached for a brief period of + * time to see if its parent arrives. Loss of any fragment constitutes packet + * loss; there is no retransmission mechanism. The receiver must wait for full + * receipt to authenticate and decrypt; there is no per-fragment MAC. (But if + * fragments are corrupt, the MAC will fail for the whole assembled packet.) + */ + +namespace ZeroTier { +namespace Protocol { + +/** + * Packet verb (message type) + */ +enum Verb +{ + VERB_NOP = 0x00, + + /** + * Announcement of a node's existence and vitals: + * <[1] protocol version> + * <[1] software major version> + * <[1] software minor version> + * <[2] software revision> + * <[8] timestamp for determining latency> + * <[...] binary serialized identity> + * <[...] physical destination address of packet> + * [... begin encrypted region ...] + * <[2] 16-bit reserved (legacy) field, always 0> + * <[2] 16-bit length of meta-data dictionary> + * <[...] meta-data dictionary> + * [... end encrypted region ...] + * <[48] HMAC-SHA384 of all fields to this point (as plaintext)> + * + * HELLO is sent with authentication but without the usual encryption so + * that peers can exchange identities. + * + * Destination address is the actual wire address to which the packet + * was sent. See InetAddress::serialize() for format. + * + * Starting at "begin encrypted section" the reset of the packet is + * encrypted with Salsa20/12. This is not the normal packet encryption + * and is technically not necessary as nothing in HELLO is secret. It + * exists merely to shield meta-data info from passive listeners to + * slightly improve privacy, and for backward compatibility with older + * nodes that required it. + * + * HELLO (and its OK response) ends with a large 384-bit HMAC to allow + * identity exchanges to be authenticated with additional strength beyond + * ordinary packet authentication. + * + * OK payload: + * <[8] HELLO timestamp field echo> + * <[1] protocol version> + * <[1] software major version> + * <[1] software minor version> + * <[2] software revision> + * <[...] physical destination address of packet> + * <[2] 16-bit reserved (legacy) field, always 0> + * <[2] 16-bit length of meta-data dictionary> + * <[...] meta-data dictionary> + * <[48] HMAC-SHA384 of all fields to this point (as plaintext)> + * + * With the exception of the timestamp, the other fields pertain to the + * respondent who is sending OK and are not echoes. + * + * ERROR has no payload. + */ + VERB_HELLO = 0x01, + + /** + * Error response: + * <[1] in-re verb> + * <[8] in-re packet ID> + * <[1] error code> + * <[...] error-dependent payload> + * + * If this is not in response to a single packet then verb can be + * NOP and packet ID can be zero. + */ + VERB_ERROR = 0x02, + + /** + * Success response: + * <[1] in-re verb> + * <[8] in-re packet ID> + * <[...] request-specific payload> + */ + VERB_OK = 0x03, + + /** + * Query an identity by address: + * <[5] address to look up> + * [<[...] additional addresses to look up> + * + * OK response payload: + * <[...] identity> + * <[...] locator> + * [... additional identity/locator pairs] + * + * If the address is not found, no response is generated. The semantics + * of WHOIS is similar to ARP and NDP in that persistent retrying can + * be performed. + * + * It is possible for an identity but a null/empty locator to be returned + * if no locator is known for a node. Older versions will also send no + * locator field at all. + */ + VERB_WHOIS = 0x04, + + /** + * Relay-mediated NAT traversal or firewall punching initiation: + * <[1] flags (unused, currently 0)> + * <[5] ZeroTier address of peer that might be found at this address> + * <[2] 16-bit protocol address port> + * <[1] protocol address length (4 for IPv4, 16 for IPv6)> + * <[...] protocol address (network byte order)> + * + * An upstream node can send this to inform both sides of a relay of + * information they might use to establish a direct connection. + * + * Upon receipt a peer sends HELLO to establish a direct link. + * + * No OK or ERROR is generated. + */ + VERB_RENDEZVOUS = 0x05, + + /** + * ZT-to-ZT unicast ethernet frame (shortened EXT_FRAME): + * <[8] 64-bit network ID> + * <[2] 16-bit ethertype> + * <[...] ethernet payload> + * + * MAC addresses are derived from the packet's source and destination + * ZeroTier addresses. This is a shortened EXT_FRAME that elides full + * Ethernet framing and other optional flags and features when they + * are not necessary. + * + * ERROR may be generated if a membership certificate is needed for a + * closed network. Payload will be network ID. + */ + VERB_FRAME = 0x06, + + /** + * Full Ethernet frame with MAC addressing and optional fields: + * <[8] 64-bit network ID> + * <[1] flags> + * <[6] destination MAC or all zero for destination node> + * <[6] source MAC or all zero for node of origin> + * <[2] 16-bit ethertype> + * <[...] ethernet payload> + * + * Flags: + * 0x01 - Certificate of network membership attached (DEPRECATED) + * 0x02 - Most significant bit of subtype (see below) + * 0x04 - Middle bit of subtype (see below) + * 0x08 - Least significant bit of subtype (see below) + * 0x10 - ACK requested in the form of OK(EXT_FRAME) + * + * Subtypes (0..7): + * 0x0 - Normal frame (bridging can be determined by checking MAC) + * 0x1 - TEEd outbound frame + * 0x2 - REDIRECTed outbound frame + * 0x3 - WATCHed outbound frame (TEE with ACK, ACK bit also set) + * 0x4 - TEEd inbound frame + * 0x5 - REDIRECTed inbound frame + * 0x6 - WATCHed inbound frame + * 0x7 - (reserved for future use) + * + * An extended frame carries full MAC addressing, making it a + * superset of VERB_FRAME. If 0x20 is set then p2p or hub and + * spoke multicast propagation is requested. + * + * OK payload (if ACK flag is set): + * <[8] 64-bit network ID> + * <[1] flags> + * <[6] destination MAC or all zero for destination node> + * <[6] source MAC or all zero for node of origin> + * <[2] 16-bit ethertype> + */ + VERB_EXT_FRAME = 0x07, + + /** + * ECHO request (a.k.a. ping): + * <[...] arbitrary payload> + * + * This generates OK with a copy of the transmitted payload. No ERROR + * is generated. Response to ECHO requests is optional and ECHO may be + * ignored if a node detects a possible flood. + */ + VERB_ECHO = 0x08, + + /** + * Announce interest in multicast group(s): + * <[8] 64-bit network ID> + * <[6] multicast Ethernet address> + * <[4] multicast additional distinguishing information (ADI)> + * [... additional tuples of network/address/adi ...] + * + * LIKEs may be sent to any peer, though a good implementation should + * restrict them to peers on the same network they're for and to network + * controllers and root servers. In the current network, root servers + * will provide the service of final multicast cache. + */ + VERB_MULTICAST_LIKE = 0x09, + + /** + * Network credentials push: + * [<[...] one or more certificates of membership>] + * <[1] 0x00, null byte marking end of COM array> + * <[2] 16-bit number of capabilities> + * <[...] one or more serialized Capability> + * <[2] 16-bit number of tags> + * <[...] one or more serialized Tags> + * <[2] 16-bit number of revocations> + * <[...] one or more serialized Revocations> + * <[2] 16-bit number of certificates of ownership> + * <[...] one or more serialized CertificateOfOwnership> + * + * This can be sent by anyone at any time to push network credentials. + * These will of course only be accepted if they are properly signed. + * Credentials can be for any number of networks. + * + * The use of a zero byte to terminate the COM section is for legacy + * backward compatibility. Newer fields are prefixed with a length. + * + * OK/ERROR are not generated. + */ + VERB_NETWORK_CREDENTIALS = 0x0a, + + /** + * Network configuration request: + * <[8] 64-bit network ID> + * <[2] 16-bit length of request meta-data dictionary> + * <[...] string-serialized request meta-data> + * <[8] 64-bit revision of netconf we currently have> + * <[8] 64-bit timestamp of netconf we currently have> + * + * This message requests network configuration from a node capable of + * providing it. + * + * Responses to this are always whole configs intended for the recipient. + * For patches and other updates a NETWORK_CONFIG is sent instead. + * + * It would be valid and correct as of 1.2.0 to use NETWORK_CONFIG always, + * but OK(NETWORK_CONFIG_REQUEST) should be sent for compatibility. + * + * OK response payload: + * <[8] 64-bit network ID> + * <[2] 16-bit length of network configuration dictionary chunk> + * <[...] network configuration dictionary (may be incomplete)> + * [ ... end of legacy single chunk response ... ] + * <[1] 8-bit flags> + * <[8] 64-bit config update ID (should never be 0)> + * <[4] 32-bit total length of assembled dictionary> + * <[4] 32-bit index of chunk> + * [ ... end signed portion ... ] + * <[1] 8-bit chunk signature type> + * <[2] 16-bit length of chunk signature> + * <[...] chunk signature> + * + * The chunk signature signs the entire payload of the OK response. + * Currently only one signature type is supported: ed25519 (1). + * + * Each config chunk is signed to prevent memory exhaustion or + * traffic crowding DOS attacks against config fragment assembly. + * + * If the packet is from the network controller it is permitted to end + * before the config update ID or other chunking related or signature + * fields. This is to support older controllers that don't include + * these fields and may be removed in the future. + * + * ERROR response payload: + * <[8] 64-bit network ID> + */ + VERB_NETWORK_CONFIG_REQUEST = 0x0b, + + /** + * Network configuration data push: + * <[8] 64-bit network ID> + * <[2] 16-bit length of network configuration dictionary chunk> + * <[...] network configuration dictionary (may be incomplete)> + * <[1] 8-bit flags> + * <[8] 64-bit config update ID (should never be 0)> + * <[4] 32-bit total length of assembled dictionary> + * <[4] 32-bit index of chunk> + * [ ... end signed portion ... ] + * <[1] 8-bit chunk signature type> + * <[2] 16-bit length of chunk signature> + * <[...] chunk signature> + * + * This is a direct push variant for network config updates. It otherwise + * carries the same payload as OK(NETWORK_CONFIG_REQUEST) and has the same + * semantics. + * + * The legacy mode missing the additional chunking fields is not supported + * here. + * + * Flags: + * 0x01 - Use fast propagation + * + * An OK should be sent if the config is successfully received and + * accepted. + * + * OK payload: + * <[8] 64-bit network ID> + * <[8] 64-bit config update ID> + */ + VERB_NETWORK_CONFIG = 0x0c, + + /** + * Request endpoints for multicast distribution: + * <[8] 64-bit network ID> + * <[1] flags> + * <[6] MAC address of multicast group being queried> + * <[4] 32-bit ADI for multicast group being queried> + * <[4] 32-bit requested max number of multicast peers> + * + * This message asks a peer for additional known endpoints that have + * LIKEd a given multicast group. It's sent when the sender wishes + * to send multicast but does not have the desired number of recipient + * peers. + * + * OK response payload: (multiple OKs can be generated) + * <[8] 64-bit network ID> + * <[6] MAC address of multicast group being queried> + * <[4] 32-bit ADI for multicast group being queried> + * <[4] 32-bit total number of known members in this multicast group> + * <[2] 16-bit number of members enumerated in this packet> + * <[...] series of 5-byte ZeroTier addresses of enumerated members> + * + * ERROR is not generated; queries that return no response are dropped. + */ + VERB_MULTICAST_GATHER = 0x0d, + + /** *** DEPRECATED *** + * Multicast frame: + * <[8] 64-bit network ID> + * <[1] flags> + * [<[4] 32-bit implicit gather limit>] + * [<[6] source MAC>] + * <[6] destination MAC (multicast address)> + * <[4] 32-bit multicast ADI (multicast address extension)> + * <[2] 16-bit ethertype> + * <[...] ethernet payload> + * + * Flags: + * 0x01 - Network certificate of membership attached (DEPRECATED) + * 0x02 - Implicit gather limit field is present + * 0x04 - Source MAC is specified -- otherwise it's computed from sender + * 0x08 - Please replicate (sent to multicast replicators) + * + * OK and ERROR responses are optional. OK may be generated if there are + * implicit gather results or if the recipient wants to send its own + * updated certificate of network membership to the sender. ERROR may be + * generated if a certificate is needed or if multicasts to this group + * are no longer wanted (multicast unsubscribe). + * + * OK response payload: + * <[8] 64-bit network ID> + * <[6] MAC address of multicast group> + * <[4] 32-bit ADI for multicast group> + * <[1] flags> + * [<[...] network certificate of membership (DEPRECATED)>] + * [<[...] implicit gather results if flag 0x01 is set>] + * + * OK flags (same bits as request flags): + * 0x01 - OK includes certificate of network membership (DEPRECATED) + * 0x02 - OK includes implicit gather results + * + * ERROR response payload: + * <[8] 64-bit network ID> + * <[6] multicast group MAC> + * <[4] 32-bit multicast group ADI> + */ + VERB_MULTICAST_FRAME_deprecated = 0x0e, + + /** + * Push of potential endpoints for direct communication: + * <[2] 16-bit number of paths> + * <[...] paths> + * + * Path record format: + * <[1] 8-bit path flags (always 0, currently unused)> + * <[2] length of extended path characteristics or 0 for none> + * <[...] extended path characteristics> + * <[1] address type> + * <[1] address length in bytes> + * <[...] address> + * + * The receiver may, upon receiving a push, attempt to establish a + * direct link to one or more of the indicated addresses. It is the + * responsibility of the sender to limit which peers it pushes direct + * paths to to those with whom it has a trust relationship. The receiver + * must obey any restrictions provided such as exclusivity or blacklists. + * OK responses to this message are optional. + * + * Note that a direct path push does not imply that learned paths can't + * be used unless they are blacklisted explicitly or unless flag 0x01 + * is set. + * + * OK and ERROR are not generated. + */ + VERB_PUSH_DIRECT_PATHS = 0x10, + + /** + * A message with arbitrary user-definable content: + * <[8] 64-bit arbitrary message type ID> + * [<[...] message payload>] + * + * This can be used to send arbitrary messages over VL1. It generates no + * OK or ERROR and has no special semantics outside of whatever the user + * (via the ZeroTier core API) chooses to give it. + * + * Message type IDs less than or equal to 65535 are reserved for use by + * ZeroTier, Inc. itself. We recommend making up random ones for your own + * implementations. + */ + VERB_USER_MESSAGE = 0x14, + + /** + * Encapsulate a ZeroTier packet for multicast distribution: + * [... begin signed portion ...] + * <[1] 8-bit flags> + * <[5] 40-bit ZeroTier address of sender> + * <[2] 16-bit length of inner payload> + * <[1] inner payload verb> + * <[...] inner payload data> + * [... end signed portion ...] + * <[2] 16-bit length of signature or 0 if un-signed> + * [<[...] optional signature of multicast>] + * <[...] address (min prefix) list> + */ + VERB_MULTICAST = 0x16, + + /** + * Encapsulate a full ZeroTier packet in another: + * <[...] raw encapsulated packet> + * + * Encapsulation exists to enable secure relaying as opposed to the usual + * "dumb" relaying. The latter is faster but secure relaying has roles + * where endpoint privacy is desired. Multiply nested ENCAP packets + * could allow ZeroTier to act as an onion router. + * + * When encapsulated packets are forwarded they do have their hop count + * field incremented. + */ + VERB_ENCAP = 0x17 + + // protocol max: 0x1f +}; + +/** + * Error codes used in ERROR packets. + */ +enum ErrorCode +{ + /* Invalid request */ + ERROR_INVALID_REQUEST = 0x01, + + /* Bad/unsupported protocol version */ + ERROR_BAD_PROTOCOL_VERSION = 0x02, + + /* Unknown object queried */ + ERROR_OBJ_NOT_FOUND = 0x03, + + /* Verb or use case not supported/enabled by this node */ + ERROR_UNSUPPORTED_OPERATION = 0x05, + + /* Network access denied; updated credentials needed */ + ERROR_NEED_MEMBERSHIP_CERTIFICATE = 0x06, + + /* Tried to join network, but you're not a member */ + ERROR_NETWORK_ACCESS_DENIED_ = 0x07, /* extra _ at end to avoid Windows name conflict */ + + /* Cannot deliver a forwarded ZeroTier packet (e.g. hops exceeded, no routes) */ + ERROR_CANNOT_DELIVER = 0x09 +}; + +/** + * EXT_FRAME subtypes, which are packed into three bits in the flags field. + */ +enum ExtFrameSubtype +{ + EXT_FRAME_SUBTYPE_NORMAL = 0x0, + EXT_FRAME_SUBTYPE_TEE_OUTBOUND = 0x1, + EXT_FRAME_SUBTYPE_REDIRECT_OUTBOUND = 0x2, + EXT_FRAME_SUBTYPE_WATCH_OUTBOUND = 0x3, + EXT_FRAME_SUBTYPE_TEE_INBOUND = 0x4, + EXT_FRAME_SUBTYPE_REDIRECT_INBOUND = 0x5, + EXT_FRAME_SUBTYPE_WATCH_INBOUND = 0x6 +}; + +/** + * EXT_FRAME flags + */ +enum ExtFrameFlag +{ + EXT_FRAME_FLAG_COM_ATTACHED_deprecated = 0x01, + // bits 0x02, 0x04, and 0x08 are occupied by the ExtFrameSubtype + EXT_FRAME_FLAG_ACK_REQUESTED = 0x10 +}; + +/****************************************************************************/ + +/* + * These are bit-packed structures for rapid parsing of packets or at least + * the fixed size headers thereof. Not all packet types have these as some + * are full of variable length fields are are more easily parsed through + * incremental decoding. + * + * All fields larger than one byte are in big-endian byte order on the wire. + */ + +ZT_PACKED_STRUCT(struct HELLO { + uint8_t versionProtocol; + uint8_t versionMajor; + uint8_t versionMinor; + uint16_t versionRev; + uint64_t timestamp; +}); + +ZT_PACKED_STRUCT(struct RENDEZVOUS { + uint8_t flags; + uint8_t peerAddress[5]; + uint16_t port; + uint8_t addressLength; + uint8_t address[16]; +}); + +ZT_PACKED_STRUCT(struct FRAME { + uint64_t networkId; + uint16_t etherType; + uint8_t data[]; +}); + +ZT_PACKED_STRUCT(struct EXT_FRAME { + uint64_t networkId; + uint8_t flags; + uint8_t destMac[6]; + uint8_t sourceMac[6]; + uint16_t etherType; + uint8_t data[]; +}); + +ZT_PACKED_STRUCT(struct MULTICAST_LIKE_Entry { + uint64_t networkId; + uint8_t mac[6]; + uint32_t adi; +}); + +ZT_PACKED_STRUCT(struct MULTICAST_LIKE { + MULTICAST_LIKE_Entry groups[]; +}); + +namespace OK { + +ZT_PACKED_STRUCT(struct HELLO { + uint64_t timestampEcho; + uint8_t versionProtocol; + uint8_t versionMajor; + uint8_t versionMinor; + uint16_t versionRev; +}); + +ZT_PACKED_STRUCT(struct EXT_FRAME { + uint64_t networkId; + uint8_t flags; + uint8_t destMac[6]; + uint8_t sourceMac[6]; + uint16_t etherType; +}); + +/** + * OK response header + * + * The OK header comes after the packet header but before type-specific payloads. + * + * @tparam PT OK payload type (default: uint8_t[]) + */ +template +ZT_PACKED_STRUCT(struct Header { + uint8_t inReVerb; + uint64_t inRePacketId; + PT p; +}); + +} // namespace OK + +namespace ERROR { + +/** + * Error header + * + * The error header comes after the packet header but before type-specific payloads. + * + * @tparam PT Error payload type (default: uint8_t[]) + */ +template +ZT_PACKED_STRUCT(struct Header { + uint8_t inReVerb; + uint64_t inRePacketId; + uint8_t error; + PT p; +}); + +} // namespace ERROR + +/** + * Normal packet header + * + * @tparam PT Packet payload type (default: uint8_t[]) + */ +template +ZT_PACKED_STRUCT(struct Header { + uint64_t packetId; + uint8_t destination[5]; + uint8_t source[5]; + uint8_t flags; + uint64_t mac; + // --- begin encrypted envelope --- + uint8_t verb; + PT p; +}); + +/** + * Packet fragment header + */ +ZT_PACKED_STRUCT(struct FragmentHeader { + uint64_t packetId; + uint8_t destination[5]; + uint8_t fragmentIndicator; // always 0xff for fragments + uint8_t counts; // total: most significant four bits, number: least significant four bits + uint8_t hops; // top 5 bits unused and must be zero + uint8_t p[]; +}); + +/****************************************************************************/ + +/** + * Increment the 3-bit hops field embedded in the packet flags field + * + * @return New hop count (can be greater than allowed if there is an overflow) + */ +template +ZT_ALWAYS_INLINE unsigned int incrementPacketHops(Buf< Header > &packet) +{ + uint8_t f = packet.data.fields.flags; + uint8_t h = f; + f &= 0xf8U; + ++h; + packet.data.fields.flags = f | (h & 0x07U); + return h; +} + +/** + * @return 3-bit hops field embedded in packet flags field + */ +template +ZT_ALWAYS_INLINE unsigned int packetHops(Buf< Header > &packet) const { return (packet.data.fields.flags & 0x07U); } + +/** + * Armor a packet for transport + * + * @param packet Packet to armor + * @param packetSize Size of data in packet (must be at least the minimum packet size) + * @param key 256-bit symmetric key + * @param cipherSuite Cipher suite to apply + */ +void armor(Buf< Header<> > &packet,unsigned int packetSize,const uint8_t key[ZT_PEER_SECRET_KEY_LENGTH],uint8_t cipherSuite); + +/** + * Dearmor a packet and check message authentication code + * + * If the packet is valid and MAC (if indicated) passes, the cipher suite + * is returned. Otherwise -1 is returned to indicate a MAC failure. + * + * @param packet Packet to dearmor + * @param packetSize Size of data in packet (must be at least the minimum packet size) + * @param key 256-bit symmetric key + * @return Cipher suite or -1 if MAC validation failed + */ +int dearmor(Buf< Header<> > &packet,unsigned int packetSize,const uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]); + +/** + * Compress packet payload + * + * @param packet Packet to compress + * @param packetSize Original packet size + * @return New packet size (returns original size of compression didn't help, in which case packet is unmodified) + */ +unsigned int compress(Buf< Header<> > &packet,unsigned int packetSize); + +/** + * Uncompress packet payload (if compressed) + * + * @param packet Packet to uncompress + * @param packetSize Original packet size + * @return New packet size or -1 on decompression error (returns original packet size if packet wasn't compressed) + */ +int uncompress(Buf< Header<> > &packet,unsigned int packetSize); + +/** + * Get a sequential non-repeating packet ID for the next packet (thread-safe) + * + * @return Next packet ID / cryptographic nonce + */ +uint64_t getPacketId(); + +} // namespace Protocol +} // namespace ZeroTier + +#endif diff --git a/node/Utils.cpp b/node/Utils.cpp index 395d0923e..e0638c19b 100644 --- a/node/Utils.cpp +++ b/node/Utils.cpp @@ -165,7 +165,7 @@ void getSecureRandom(void *buf,unsigned int bytes) { static Mutex globalLock; static bool initialized = false; - static uint64_t randomState[4]; + static uint64_t randomState[8]; static uint8_t randomBuf[65536]; static unsigned long randomPtr = sizeof(randomBuf); @@ -175,7 +175,7 @@ void getSecureRandom(void *buf,unsigned int bytes) if (randomPtr >= sizeof(randomBuf)) { randomPtr = 0; - if (unlikely(!initialized)) { + if (!initialized) { initialized = true; #ifdef __WINDOWS__ HCRYPTPROV cryptProvider = NULL; @@ -225,14 +225,18 @@ void getSecureRandom(void *buf,unsigned int bytes) #endif } - for(int k=0;k<4;++k) { if (++randomState[k] != 0) break; } - uint8_t h[48]; - HMACSHA384((const uint8_t *)randomState,randomBuf,sizeof(randomBuf),h); // compute HMAC on random buffer using state as secret key - AES c(h); - c.ctr(h + 32,randomBuf,sizeof(randomBuf),randomBuf); // encrypt random buffer with AES-CTR using HMAC result as key + SHA512(randomState,randomState,sizeof(randomState)); + AES aes(reinterpret_cast(randomState)); + uint64_t ctr[2]; + ctr[0] = randomState[6]; + ctr[1] = randomState[7]; + for(unsigned long i=0;i(ctr),randomBuf + i); + } } - ((uint8_t *)buf)[i] = randomBuf[randomPtr++]; + reinterpret_cast(buf)[i] = randomBuf[randomPtr++]; } }