AES-GMAC-CTR tweaks, self test tweaks, debian typo fix.

This commit is contained in:
Adam Ierymenko 2019-09-04 08:19:12 -07:00
parent e29c2d0260
commit 185e90c40f
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
5 changed files with 57 additions and 24 deletions

2
debian/control vendored
View file

@ -11,7 +11,7 @@ Homepage: https://www.zerotier.com/
Package: zerotier-one Package: zerotier-one
Architecture: any Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, iproute2, adduser, libstdc++6 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 Description: ZeroTier network virtualization service
ZeroTier One lets you join ZeroTier virtual networks and ZeroTier One lets you join ZeroTier virtual networks and
have them appear as tun/tap ports on your system. See have them appear as tun/tap ports on your system. See

View file

@ -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 * This is an AES mode built from GMAC and AES-CTR that is similar to the
* yield a mode similar to AES256-GCM-SIV that is resistant to accidental * various SIV (synthetic IV) modes for AES and is resistant to nonce
* message IV duplication. This is good because ZeroTier is stateless and * re-use. It's specifically tweaked for ZeroTier's packet structure with
* uses a small (64-bit) IV to reduce bandwidth overhead. * 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 iv 96-bit message IV
* @param in Message plaintext * @param in Message plaintext
* @param len Length of plaintext * @param len Length of plaintext
* @param out Output buffer to receive ciphertext * @param out Output buffer to receive ciphertext
* @param tag Output buffer to receive 64-bit authentication tag * @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]; uint8_t ctrIv[16];
gmac(iv,in,len,ctrIv); // Compute AES[k1](GMAC[k1](iv,plaintext))
encrypt(ctrIv,ctrIv); 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]; 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=4;i<8;++i) ctrIv[i] ^= iv[i - 4];
for(unsigned int i=8;i<16;++i) ctrIv[i] = iv[i - 4]; for(unsigned int i=8;i<16;++i) ctrIv[i] = iv[i - 4];
encrypt(ctrIv,ctrIv); k1.encrypt(ctrIv,ctrIv);
ctr(ctrIv,in,len,out); 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 iv 96-bit message IV
* @param in Message ciphertext * @param in Message ciphertext
* @param len Length of ciphertext * @param len Length of ciphertext
@ -188,20 +207,25 @@ public:
* @param tag Authentication tag supplied with message * @param tag Authentication tag supplied with message
* @return True if authentication tags match and message appears authentic * @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]; uint8_t ctrIv[16],gmacOut[16];
for(unsigned int i=0;i<8;++i) ctrIv[i] = tag[i]; // 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] ^= iv[i - 4]; 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]; for(unsigned int i=8;i<16;++i) ctrIv[i] = iv[i - 4];
encrypt(ctrIv,ctrIv); k1.encrypt(ctrIv,ctrIv);
ctr(ctrIv,in,len,out); k2.encrypt(ctrIv,ctrIv);
gmac(iv,out,len,gmacOut); // Decrypt with AES[k2]-CTR
encrypt(gmacOut,gmacOut); 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 #ifdef ZT_NO_TYPE_PUNNING
return Utils::secureEq(gmacOut,tag,8); return Utils::secureEq(gmacOut,tag,8);
#else #else

View file

@ -935,11 +935,19 @@ bool Packet::uncompress()
uint64_t Packet::nextPacketId() 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 uint64_t ctr = 0;
static Mutex lock; static Mutex lock;
lock.lock(); lock.lock();
while (ctr == 0) while (ctr == 0) {
Utils::getSecureRandom(&ctr,sizeof(ctr)); Utils::getSecureRandom(&ctr,sizeof(ctr));
ctr <<= 32;
ctr |= ((uint64_t)time(nullptr)) & 0x00000000ffffffffULL;
}
const uint64_t i = ctr++; const uint64_t i = ctr++;
lock.unlock(); lock.unlock();
return i; return i;

View file

@ -316,8 +316,9 @@ static void handlePacket(const int v4s,const int v6s,const InetAddress *const ip
switch(pkt.verb()) { switch(pkt.verb()) {
case Packet::VERB_HELLO: case Packet::VERB_HELLO:
try { try {
if ((now - peer->lastHello) > 1000) { if ((now - peer->lastHello) > 500) {
peer->lastHello = now; peer->lastHello = now;
peer->vProto = (int)pkt[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION]; peer->vProto = (int)pkt[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION];
peer->vMajor = (int)pkt[ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION]; peer->vMajor = (int)pkt[ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION];
peer->vMinor = (int)pkt[ZT_PROTO_VERB_HELLO_IDX_MINOR_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: case Packet::VERB_ECHO:
try { try {
if ((now - peer->lastEcho) > 1000) { if ((now - peer->lastEcho) > 500) {
peer->lastEcho = now; peer->lastEcho = now;
Packet outp(source,s_self.address(),Packet::VERB_OK); Packet outp(source,s_self.address(),Packet::VERB_OK);

View file

@ -224,7 +224,7 @@ static int testCrypto()
std::cout << " AES-256-GMAC-CTR (benchmark): "; std::cout.flush(); std::cout << " AES-256-GMAC-CTR (benchmark): "; std::cout.flush();
start = OSUtils::now(); start = OSUtils::now();
for(unsigned long i=0;i<200000;++i) { 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]; hexbuf[0] = buf2[0];
} }
end = OSUtils::now(); end = OSUtils::now();