Some minor security tightening stuff and AES fixes.

This commit is contained in:
Adam Ierymenko 2020-03-26 13:54:15 -07:00
parent d151d731a6
commit bedf63e257
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
6 changed files with 87 additions and 24 deletions

View file

@ -507,8 +507,7 @@ void AES::CTR::crypt(const void *const input,unsigned int len) noexcept
--len; --len;
out[totalLen++] = *(in++); out[totalLen++] = *(in++);
if (!(totalLen & 15U)) { if (!(totalLen & 15U)) {
__m128i d0 = _mm_set_epi64x((long long)Utils::hton(c1),(long long)c0); __m128i d0 = _mm_set_epi64x((long long)Utils::hton(c1++),(long long)c0);
if (unlikely(++c1 == 0ULL)) c0 = Utils::hton(Utils::ntoh(c0) + 1ULL);
d0 = _mm_xor_si128(d0,k0); d0 = _mm_xor_si128(d0,k0);
d0 = _mm_aesenc_si128(d0,k1); d0 = _mm_aesenc_si128(d0,k1);
d0 = _mm_aesenc_si128(d0,k2); d0 = _mm_aesenc_si128(d0,k2);
@ -656,7 +655,6 @@ void AES::CTR::crypt(const void *const input,unsigned int len) noexcept
d0 = _mm_aesenc_si128(d0,k13); d0 = _mm_aesenc_si128(d0,k13);
d0 = _mm_aesenclast_si128(d0,k14); d0 = _mm_aesenclast_si128(d0,k14);
_mm_storeu_si128(reinterpret_cast<__m128i *>(out),_mm_xor_si128(d0,_mm_loadu_si128(reinterpret_cast<const __m128i *>(in)))); _mm_storeu_si128(reinterpret_cast<__m128i *>(out),_mm_xor_si128(d0,_mm_loadu_si128(reinterpret_cast<const __m128i *>(in))));
in += 16; in += 16;
len -= 16; len -= 16;
out += 16; out += 16;
@ -678,6 +676,7 @@ void AES::CTR::crypt(const void *const input,unsigned int len) noexcept
#endif #endif
uint64_t keyStream[2]; uint64_t keyStream[2];
uint32_t ctr = Utils::ntoh(reinterpret_cast<uint32_t *>(_ctr)[3]);
unsigned int totalLen = _len; unsigned int totalLen = _len;
if ((totalLen & 15U)) { if ((totalLen & 15U)) {
@ -690,10 +689,10 @@ void AES::CTR::crypt(const void *const input,unsigned int len) noexcept
out[totalLen++] = *(in++); out[totalLen++] = *(in++);
if (!(totalLen & 15U)) { if (!(totalLen & 15U)) {
_aes._encryptSW(reinterpret_cast<const uint8_t *>(_ctr),reinterpret_cast<uint8_t *>(keyStream)); _aes._encryptSW(reinterpret_cast<const uint8_t *>(_ctr),reinterpret_cast<uint8_t *>(keyStream));
reinterpret_cast<uint32_t *>(_ctr)[3] = Utils::hton(++ctr);
uint8_t *outblk = out + (totalLen - 16); uint8_t *outblk = out + (totalLen - 16);
for(int i=0;i<16;++i) for(int i=0;i<16;++i)
outblk[i] ^= reinterpret_cast<uint8_t *>(keyStream)[i]; outblk[i] ^= reinterpret_cast<uint8_t *>(keyStream)[i];
_ctr[1] = Utils::hton(Utils::ntoh(_ctr[1]) + 1ULL);
break; break;
} }
} }
@ -707,23 +706,23 @@ void AES::CTR::crypt(const void *const input,unsigned int len) noexcept
#endif #endif
while (len >= 16) { while (len >= 16) {
_aes._encryptSW(reinterpret_cast<const uint8_t *>(_ctr),reinterpret_cast<uint8_t *>(keyStream)); _aes._encryptSW(reinterpret_cast<const uint8_t *>(_ctr),reinterpret_cast<uint8_t *>(keyStream));
reinterpret_cast<uint32_t *>(_ctr)[3] = Utils::hton(++ctr);
reinterpret_cast<uint64_t *>(out)[0] = reinterpret_cast<const uint64_t *>(in)[0] ^ keyStream[0]; reinterpret_cast<uint64_t *>(out)[0] = reinterpret_cast<const uint64_t *>(in)[0] ^ keyStream[0];
reinterpret_cast<uint64_t *>(out)[1] = reinterpret_cast<const uint64_t *>(in)[1] ^ keyStream[1]; reinterpret_cast<uint64_t *>(out)[1] = reinterpret_cast<const uint64_t *>(in)[1] ^ keyStream[1];
out += 16; out += 16;
len -= 16; len -= 16;
in += 16; in += 16;
_ctr[1] = Utils::hton(Utils::ntoh(_ctr[1]) + 1ULL);
} }
#ifdef ZT_NO_UNALIGNED_ACCESS #ifdef ZT_NO_UNALIGNED_ACCESS
} else { } else {
while (len >= 16) { while (len >= 16) {
_aes._encryptSW(reinterpret_cast<const uint8_t *>(_ctr),reinterpret_cast<uint8_t *>(keyStream)); _aes._encryptSW(reinterpret_cast<const uint8_t *>(_ctr),reinterpret_cast<uint8_t *>(keyStream));
reinterpret_cast<uint32_t *>(_ctr)[3] = Utils::hton(++ctr);
for (int i = 0;i < 16;++i) for (int i = 0;i < 16;++i)
out[i] = in[i] ^ reinterpret_cast<uint8_t *>(keyStream)[i]; out[i] = in[i] ^ reinterpret_cast<uint8_t *>(keyStream)[i];
out += 16; out += 16;
len -= 16; len -= 16;
in += 16; in += 16;
_ctr[1] = Utils::hton(Utils::ntoh(_ctr[1]) + 1ULL);
} }
} }
#endif #endif

View file

@ -57,16 +57,27 @@ public:
/** /**
* Create an un-initialized AES instance (must call init() before use) * Create an un-initialized AES instance (must call init() before use)
*/ */
ZT_INLINE AES() noexcept {} ZT_INLINE AES() noexcept
{
Utils::memoryLock(this,sizeof(AES));
}
/** /**
* Create an AES instance with the given key * Create an AES instance with the given key
* *
* @param key 256-bit key * @param key 256-bit key
*/ */
explicit ZT_INLINE AES(const void *const key) noexcept { this->init(key); } explicit ZT_INLINE AES(const void *const key) noexcept :
AES()
{
this->init(key);
}
ZT_INLINE ~AES() { Utils::burn(&_k,sizeof(_k)); } ZT_INLINE ~AES()
{
Utils::burn(&_k,sizeof(_k));
Utils::memoryUnlock(this,sizeof(AES));
}
/** /**
* Set (or re-set) this AES256 cipher's key * Set (or re-set) this AES256 cipher's key
@ -191,6 +202,9 @@ public:
/** /**
* Streaming AES-CTR encrypt/decrypt * Streaming AES-CTR encrypt/decrypt
*
* NOTE: this doesn't support overflow of the counter in the least significant 32 bits.
* AES-GMAC-CTR doesn't need this, so we don't support it as an optimization.
*/ */
class CTR class CTR
{ {
@ -202,7 +216,7 @@ public:
/** /**
* Initialize this CTR instance to encrypt a new stream * Initialize this CTR instance to encrypt a new stream
* *
* @param iv Unique initialization vector * @param iv Unique initialization vector and initial 32-bit counter (least significant 32 bits, big-endian)
* @param output Buffer to which to store output (MUST be large enough for total bytes processed!) * @param output Buffer to which to store output (MUST be large enough for total bytes processed!)
*/ */
ZT_INLINE void init(const uint8_t iv[16],void *const output) noexcept ZT_INLINE void init(const uint8_t iv[16],void *const output) noexcept
@ -312,15 +326,16 @@ public:
_iv[1] = gmacTag[0]; _iv[1] = gmacTag[0];
_ctr._aes.encrypt(_iv,_iv); _ctr._aes.encrypt(_iv,_iv);
// Bit 31 of the CTR IV is masked to (1) allow us to optimize by forgetting // For CTR we need to mask the least significant 32 bits to zero for a typical
// about integer overflow for less than 2^31 bytes (which is far less than // 96-bit nonce, since this guarantees compatiblity with the most CTR implementations
// this system's max message size), and (2) ensure interoperability with any // and avoids having to do add with carry.
// future FIPS-compliant or other cryptographic libraries that may or may not
// handle 32-bit integer overflow of the least significant 32 bits in the
// counter in the expected way.
uint64_t ctrIv[2]; uint64_t ctrIv[2];
ctrIv[0] = _iv[0]; ctrIv[0] = _iv[0];
ctrIv[1] = _iv[1] & ZT_CONST_TO_BE_UINT64(0xffffffff7fffffffULL); #if __BYTE_ORDER == __BIG_ENDIAN
ctrIv[1] = _iv[1] & 0xffffffff00000000ULL;
#else
ctrIv[1] = _iv[1] & 0x00000000ffffffffULL;
#endif
_ctr.init(reinterpret_cast<const uint8_t *>(ctrIv),_output); _ctr.init(reinterpret_cast<const uint8_t *>(ctrIv),_output);
} }

View file

@ -63,8 +63,11 @@ public:
*/ */
static const Identity NIL; static const Identity NIL;
ZT_INLINE Identity() noexcept { memoryZero(this); } ZT_INLINE Identity() noexcept
ZT_INLINE ~Identity() { Utils::burn(reinterpret_cast<void *>(&this->_priv),sizeof(this->_priv)); } {
Utils::memoryLock(this,sizeof(Identity));
memoryZero(this);
}
/** /**
* Construct identity from string * Construct identity from string
@ -74,7 +77,17 @@ public:
* *
* @param str Identity in canonical string format * @param str Identity in canonical string format
*/ */
explicit ZT_INLINE Identity(const char *str) { fromString(str); } explicit ZT_INLINE Identity(const char *str)
{
Utils::memoryLock(this,sizeof(Identity));
fromString(str);
}
ZT_INLINE ~Identity()
{
Utils::memoryUnlock(this,sizeof(Identity));
Utils::burn(reinterpret_cast<void *>(&this->_priv),sizeof(this->_priv));
}
/** /**
* Set identity to NIL value (all zero) * Set identity to NIL value (all zero)

View file

@ -52,6 +52,7 @@ Peer::Peer(const RuntimeEnvironment *renv) :
_vMinor(0), _vMinor(0),
_vRevision(0) _vRevision(0)
{ {
Utils::memoryLock(_key,sizeof(_key));
} }
bool Peer::init(const Identity &peerIdentity) bool Peer::init(const Identity &peerIdentity)

View file

@ -47,9 +47,6 @@ class Peer
friend class SharedPtr<Peer>; friend class SharedPtr<Peer>;
friend class Topology; friend class Topology;
private:
ZT_INLINE Peer() {}
public: public:
/** /**
* Create an uninitialized peer * Create an uninitialized peer
@ -61,7 +58,11 @@ public:
*/ */
explicit Peer(const RuntimeEnvironment *renv); explicit Peer(const RuntimeEnvironment *renv);
ZT_INLINE ~Peer() { Utils::burn(_key,sizeof(_key)); } ZT_INLINE ~Peer()
{
Utils::memoryUnlock(_key,sizeof(_key));
Utils::burn(_key,sizeof(_key));
}
/** /**
* Initialize peer with an identity * Initialize peer with an identity

View file

@ -20,6 +20,10 @@ namespace ZeroTier {
namespace Utils { namespace Utils {
#ifndef __WINDOWS__
#include <sys/mman.h>
#endif
// Macros to convert endian-ness at compile time for constants. // Macros to convert endian-ness at compile time for constants.
#if __BYTE_ORDER == __LITTLE_ENDIAN #if __BYTE_ORDER == __LITTLE_ENDIAN
#define ZT_CONST_TO_BE_UINT16(x) ((uint16_t)((uint16_t)((uint16_t)(x) << 8U) | (uint16_t)((uint16_t)(x) >> 8U))) #define ZT_CONST_TO_BE_UINT16(x) ((uint16_t)((uint16_t)((uint16_t)(x) << 8U) | (uint16_t)((uint16_t)(x) >> 8U)))
@ -58,6 +62,36 @@ extern const uint64_t ZERO256[4];
*/ */
extern const char HEXCHARS[16]; extern const char HEXCHARS[16];
/**
* Lock memory to prevent swapping out to secondary storage (if possible)
*
* This is used to attempt to prevent the swapping out of long-term stored secure
* credentials like secret keys. It isn't supported on all platforms and may not
* be absolutely guaranteed to work, but it's a countermeasure.
*
* @param p Memory to lock
* @param l Size of memory
*/
static ZT_INLINE void memoryLock(const void *const p,const unsigned int l) noexcept
{
#ifndef __WINDOWS__
mlock(p,l);
#endif
}
/**
* Unlock memory locked with memoryLock()
*
* @param p Memory to unlock
* @param l Size of memory
*/
static ZT_INLINE void memoryUnlock(const void *const p,const unsigned int l) noexcept
{
#ifndef __WINDOWS__
munlock(p,l);
#endif
}
/** /**
* Perform a time-invariant binary comparison * Perform a time-invariant binary comparison
* *