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
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

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
* 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

View File

@ -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;

View File

@ -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);

View File

@ -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();