mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2024-12-30 09:48:54 +00:00
173 lines
5.0 KiB
C++
173 lines
5.0 KiB
C++
/*
|
|
* ZeroTier One - Network Virtualization Everywhere
|
|
* Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* --
|
|
*
|
|
* You can be released from the requirements of the license by purchasing
|
|
* a commercial license. Buying such a license is mandatory as soon as you
|
|
* develop commercial closed-source software that incorporates or links
|
|
* directly against ZeroTier software without disclosing the source code
|
|
* of your own application.
|
|
*/
|
|
|
|
#ifndef ZT_AES_HPP
|
|
#define ZT_AES_HPP
|
|
|
|
#include "Constants.hpp"
|
|
#include "Utils.hpp"
|
|
|
|
#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64))
|
|
#include <wmmintrin.h>
|
|
#include <emmintrin.h>
|
|
#include <smmintrin.h>
|
|
#define ZT_AES_AESNI 1
|
|
#endif
|
|
|
|
namespace ZeroTier {
|
|
|
|
/**
|
|
* AES-256 and GCM AEAD
|
|
*
|
|
* AES with 128-bit or 192-bit key sizes isn't supported here. This also only
|
|
* supports the encrypt operation since we use AES in GCM mode. For HW acceleration
|
|
* the code is inlined for maximum performance.
|
|
*/
|
|
class AES
|
|
{
|
|
public:
|
|
/**
|
|
* This will be true if your platform's type of AES acceleration is supported on this machine
|
|
*/
|
|
static const bool HW_ACCEL;
|
|
|
|
inline AES() {}
|
|
inline AES(const uint8_t key[32]) { this->init(key); }
|
|
|
|
inline ~AES()
|
|
{
|
|
Utils::burn(&_k,sizeof(_k)); // ensure that expanded key memory is zeroed on object destruction
|
|
}
|
|
|
|
inline void init(const uint8_t key[32])
|
|
{
|
|
if (HW_ACCEL) {
|
|
#ifdef ZT_AES_AESNI
|
|
_init_aesni(key);
|
|
#endif
|
|
} else {
|
|
_initSW(key);
|
|
}
|
|
}
|
|
|
|
inline void encrypt(const uint8_t in[16],uint8_t out[16]) const
|
|
{
|
|
if (HW_ACCEL) {
|
|
#ifdef ZT_AES_AESNI
|
|
_encrypt_aesni(in,out);
|
|
#endif
|
|
} else {
|
|
_encryptSW(in,out);
|
|
}
|
|
}
|
|
|
|
// These are public so the software mode can always be tested in self-test.
|
|
// Normally init(), encrypt(), etc. should be used.
|
|
void _initSW(const uint8_t key[32]);
|
|
void _encryptSW(const uint8_t in[16],uint8_t out[16]) const;
|
|
|
|
private:
|
|
#ifdef ZT_AES_AESNI
|
|
static inline __m128i _init256_1(__m128i a,__m128i b)
|
|
{
|
|
__m128i x,y;
|
|
b = _mm_shuffle_epi32(b,0xff);
|
|
y = _mm_slli_si128(a,0x04);
|
|
x = _mm_xor_si128(a,y);
|
|
y = _mm_slli_si128(y,0x04);
|
|
x = _mm_xor_si128(x,y);
|
|
y = _mm_slli_si128(y,0x04);
|
|
x = _mm_xor_si128(x,y);
|
|
x = _mm_xor_si128(x,b);
|
|
return x;
|
|
}
|
|
static inline __m128i _init256_2(__m128i a,__m128i b)
|
|
{
|
|
__m128i x,y,z;
|
|
y = _mm_aeskeygenassist_si128(a,0x00);
|
|
z = _mm_shuffle_epi32(y,0xaa);
|
|
y = _mm_slli_si128(b,0x04);
|
|
x = _mm_xor_si128(b,y);
|
|
y = _mm_slli_si128(y,0x04);
|
|
x = _mm_xor_si128(x,y);
|
|
y = _mm_slli_si128(y,0x04);
|
|
x = _mm_xor_si128(x,y);
|
|
x = _mm_xor_si128(x,z);
|
|
return x;
|
|
}
|
|
inline void _init_aesni(const uint8_t key[32])
|
|
{
|
|
__m128i t1,t2;
|
|
_k.ni[0] = t1 = _mm_loadu_si128((const __m128i *)key);
|
|
_k.ni[1] = t2 = _mm_loadu_si128((const __m128i *)(key+16));
|
|
_k.ni[2] = t1 = _init256_1(t1,_mm_aeskeygenassist_si128(t2,0x01));
|
|
_k.ni[3] = t2 = _init256_2(t1,t2);
|
|
_k.ni[4] = t1 = _init256_1(t1,_mm_aeskeygenassist_si128(t2,0x02));
|
|
_k.ni[5] = t2 = _init256_2(t1,t2);
|
|
_k.ni[6] = t1 = _init256_1(t1,_mm_aeskeygenassist_si128(t2,0x04));
|
|
_k.ni[7] = t2 = _init256_2(t1,t2);
|
|
_k.ni[8] = t1 = _init256_1(t1,_mm_aeskeygenassist_si128(t2,0x08));
|
|
_k.ni[9] = t2 = _init256_2(t1,t2);
|
|
_k.ni[10] = t1 = _init256_1(t1,_mm_aeskeygenassist_si128(t2,0x10));
|
|
_k.ni[11] = t2 = _init256_2(t1,t2);
|
|
_k.ni[12] = t1 = _init256_1(t1,_mm_aeskeygenassist_si128(t2,0x20));
|
|
_k.ni[13] = t2 = _init256_2(t1,t2);
|
|
_k.ni[14] = _init256_1(t1,_mm_aeskeygenassist_si128(t2,0x40));
|
|
}
|
|
inline void _encrypt_aesni(const void *in,void *out) const
|
|
{
|
|
__m128i tmp;
|
|
tmp = _mm_loadu_si128((const __m128i *)in);
|
|
tmp = _mm_xor_si128(tmp,_k.ni[0]);
|
|
tmp = _mm_aesenc_si128(tmp,_k.ni[1]);
|
|
tmp = _mm_aesenc_si128(tmp,_k.ni[2]);
|
|
tmp = _mm_aesenc_si128(tmp,_k.ni[3]);
|
|
tmp = _mm_aesenc_si128(tmp,_k.ni[4]);
|
|
tmp = _mm_aesenc_si128(tmp,_k.ni[5]);
|
|
tmp = _mm_aesenc_si128(tmp,_k.ni[6]);
|
|
tmp = _mm_aesenc_si128(tmp,_k.ni[7]);
|
|
tmp = _mm_aesenc_si128(tmp,_k.ni[8]);
|
|
tmp = _mm_aesenc_si128(tmp,_k.ni[9]);
|
|
tmp = _mm_aesenc_si128(tmp,_k.ni[10]);
|
|
tmp = _mm_aesenc_si128(tmp,_k.ni[11]);
|
|
tmp = _mm_aesenc_si128(tmp,_k.ni[12]);
|
|
tmp = _mm_aesenc_si128(tmp,_k.ni[13]);
|
|
_mm_storeu_si128((__m128i *)out,_mm_aesenclast_si128(tmp,_k.ni[14]));
|
|
}
|
|
#endif
|
|
|
|
union {
|
|
#ifdef ZT_AES_AESNI
|
|
__m128i ni[15]; // AES-NI expanded key
|
|
#endif
|
|
uint32_t sw[60]; // software mode expanded key
|
|
} _k;
|
|
};
|
|
|
|
} // namespace ZeroTier
|
|
|
|
#endif
|