diff --git a/debian/control b/debian/control index 52f8daea4..1eb147e09 100644 --- a/debian/control +++ b/debian/control @@ -11,7 +11,7 @@ Homepage: https://www.zerotier.com/ Package: zerotier-one Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, iproute2, adduser, libstdc++6 -apt-caHomepage: https://www.zerotier.com/ +Homepage: https://www.zerotier.com/ Description: ZeroTier network virtualization service ZeroTier One lets you join ZeroTier virtual networks and have them appear as tun/tap ports on your system. See diff --git a/node/AES.hpp b/node/AES.hpp index d37158187..930a4aff8 100644 --- a/node/AES.hpp +++ b/node/AES.hpp @@ -150,37 +150,56 @@ public: } /** - * Perform AES-256-GMAC-CTR encryption + * Perform AES-GMAC-CTR encryption * - * This mode combines the two standard modes AES256-GMAC and AES256-CTR to - * yield a mode similar to AES256-GCM-SIV that is resistant to accidental - * message IV duplication. This is good because ZeroTier is stateless and - * uses a small (64-bit) IV to reduce bandwidth overhead. + * This is an AES mode built from GMAC and AES-CTR that is similar to the + * various SIV (synthetic IV) modes for AES and is resistant to nonce + * re-use. It's specifically tweaked for ZeroTier's packet structure with + * a 64-bit IV (extended to 96 bits by including packet size and other info) + * and a 64-bit auth tag. * + * The use of separate keys for MAC and encrypt is precautionary. It + * ensures that the CTR IV (and CTR output) are always secrets regardless + * of what an attacker might do with accumulated IVs and auth tags. + * + * @param k1 MAC key + * @param k2 Encryption key * @param iv 96-bit message IV * @param in Message plaintext * @param len Length of plaintext * @param out Output buffer to receive ciphertext * @param tag Output buffer to receive 64-bit authentication tag */ - inline void ztGmacCtrEncrypt(const uint8_t iv[12],const void *in,unsigned int len,void *out,uint8_t tag[8]) const + static inline void ztGmacCtrEncrypt(const AES &k1,const AES &k2,const uint8_t iv[12],const void *in,unsigned int len,void *out,uint8_t tag[8]) { uint8_t ctrIv[16]; - gmac(iv,in,len,ctrIv); - encrypt(ctrIv,ctrIv); + // Compute AES[k1](GMAC[k1](iv,plaintext)) + k1.gmac(iv,in,len,ctrIv); + k1.encrypt(ctrIv,ctrIv); // ECB mode encrypt step is because GMAC is not a PRF + // Auth tag for packet is first 64 bits of AES(GMAC) (rest is discarded) +#ifdef ZT_NO_TYPE_PUNNING for(unsigned int i=0;i<8;++i) tag[i] = ctrIv[i]; +#else + *((uint64_t *)tag) = *((uint64_t *)ctrIv); +#endif + // Synthetic CTR IV is AES[k2](AES[k1]( tag[0..4] | tag[4..8]^iv[0..4] | iv[4..12] )) for(unsigned int i=4;i<8;++i) ctrIv[i] ^= iv[i - 4]; for(unsigned int i=8;i<16;++i) ctrIv[i] = iv[i - 4]; - encrypt(ctrIv,ctrIv); - ctr(ctrIv,in,len,out); + k1.encrypt(ctrIv,ctrIv); + k2.encrypt(ctrIv,ctrIv); // ECB mode encrypt here makes CTR IV itself a secret and mixes bits + + // Encrypt with AES[k2]-CTR + k2.ctr(ctrIv,in,len,out); } /** - * Decrypt a message encrypted with AES-256-GMAC-CTR and check its authenticity + * Decrypt a message encrypted with AES-GMAC-CTR and check its authenticity * + * @param k1 MAC key + * @param k2 Encryption key * @param iv 96-bit message IV * @param in Message ciphertext * @param len Length of ciphertext @@ -188,20 +207,25 @@ public: * @param tag Authentication tag supplied with message * @return True if authentication tags match and message appears authentic */ - inline bool ztGmacCtrDecrypt(const uint8_t iv[12],const void *in,unsigned int len,void *out,const uint8_t tag[8]) const + static inline bool ztGmacCtrDecrypt(const AES &k1,const AES &k2,const uint8_t iv[12],const void *in,unsigned int len,void *out,const uint8_t tag[8]) { uint8_t ctrIv[16],gmacOut[16]; - for(unsigned int i=0;i<8;++i) ctrIv[i] = tag[i]; - - for(unsigned int i=4;i<8;++i) ctrIv[i] ^= iv[i - 4]; + // Recover synthetic and secret CTR IV from auth tag and packet IV + for(unsigned int i=0;i<4;++i) ctrIv[i] = tag[i]; + for(unsigned int i=4;i<8;++i) ctrIv[i] = tag[i] ^ iv[i - 4]; for(unsigned int i=8;i<16;++i) ctrIv[i] = iv[i - 4]; - encrypt(ctrIv,ctrIv); - ctr(ctrIv,in,len,out); + k1.encrypt(ctrIv,ctrIv); + k2.encrypt(ctrIv,ctrIv); - gmac(iv,out,len,gmacOut); - encrypt(gmacOut,gmacOut); + // Decrypt with AES[k2]-CTR + k2.ctr(ctrIv,in,len,out); + // Compute AES[k1](GMAC[k1](iv,plaintext)) + k1.gmac(iv,out,len,gmacOut); + k1.encrypt(gmacOut,gmacOut); + + // Check that packet's auth tag matches first 64 bits of AES(GMAC) #ifdef ZT_NO_TYPE_PUNNING return Utils::secureEq(gmacOut,tag,8); #else diff --git a/node/Packet.cpp b/node/Packet.cpp index 5e5357ce1..21357db8f 100644 --- a/node/Packet.cpp +++ b/node/Packet.cpp @@ -935,11 +935,19 @@ bool Packet::uncompress() uint64_t Packet::nextPacketId() { + // The packet ID which is also the packet's nonce/IV can be sequential but + // it should never repeat. This scheme minimizes the chance of nonce + // repetition if (as will usually be the case) the clock is relatively + // accurate. + static uint64_t ctr = 0; static Mutex lock; lock.lock(); - while (ctr == 0) + while (ctr == 0) { Utils::getSecureRandom(&ctr,sizeof(ctr)); + ctr <<= 32; + ctr |= ((uint64_t)time(nullptr)) & 0x00000000ffffffffULL; + } const uint64_t i = ctr++; lock.unlock(); return i; diff --git a/root/root.cpp b/root/root.cpp index 56c85d272..f8171d418 100644 --- a/root/root.cpp +++ b/root/root.cpp @@ -316,8 +316,9 @@ static void handlePacket(const int v4s,const int v6s,const InetAddress *const ip switch(pkt.verb()) { case Packet::VERB_HELLO: try { - if ((now - peer->lastHello) > 1000) { + if ((now - peer->lastHello) > 500) { peer->lastHello = now; + peer->vProto = (int)pkt[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION]; peer->vMajor = (int)pkt[ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION]; peer->vMinor = (int)pkt[ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION]; @@ -354,7 +355,7 @@ static void handlePacket(const int v4s,const int v6s,const InetAddress *const ip case Packet::VERB_ECHO: try { - if ((now - peer->lastEcho) > 1000) { + if ((now - peer->lastEcho) > 500) { peer->lastEcho = now; Packet outp(source,s_self.address(),Packet::VERB_OK); diff --git a/selftest.cpp b/selftest.cpp index 2d25b9a80..c5eb6ac73 100644 --- a/selftest.cpp +++ b/selftest.cpp @@ -224,7 +224,7 @@ static int testCrypto() std::cout << " AES-256-GMAC-CTR (benchmark): "; std::cout.flush(); start = OSUtils::now(); for(unsigned long i=0;i<200000;++i) { - tv.ztGmacCtrEncrypt((const uint8_t *)hexbuf,buf1,sizeof(buf1),buf2,(uint8_t *)(hexbuf + 8)); + AES::ztGmacCtrEncrypt(tv,tv,(const uint8_t *)hexbuf,buf1,sizeof(buf1),buf2,(uint8_t *)(hexbuf + 8)); hexbuf[0] = buf2[0]; } end = OSUtils::now();