diff --git a/README.md b/README.md index 1e24099..0a6d05a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Qt-AES Small and portable AES encryption class for Qt. -Supports all key sizes - 128/192/256 bits - ECB, CBC, CFB and OFB modes +Native support for all key sizes - 128/192/256 bits - ECB, CBC, CFB and OFB modes +AES-NI support for all key sizes - ECB, CBC modes ## Usage @@ -9,12 +10,12 @@ Supports all key sizes - 128/192/256 bits - ECB, CBC, CFB and OFB modes // Encode of rawText with key // iv is used in CBC mode // return the encrypted byte array -QByteArray encode(const QByteArray rawText, const QByteArray key, const QByteArray iv = NULL); +QByteArray encode(const QByteArray rawText, const QByteArray key, const QByteArray iv = QByteArray()); // Decode of rawText with key // iv is used in CBC mode // return the decrypted byte array -QByteArray decode(const QByteArray rawText, const QByteArray key, const QByteArray iv = NULL); +QByteArray decode(const QByteArray rawText, const QByteArray key, const QByteArray iv = QByteArray()); // Key expansion in Rijndael schedule // return the new expanded key as byte array @@ -108,6 +109,11 @@ QString decodedString = QString(QAESEncryption::RemovePadding(decodeText)); ``` +## AES New Instructions Set +To use the hardware acceleration provided by the AES New Instructions Set, define USE_INTEL_AES_IF_AVAILABLE +If the CPU supports it, the code will switch to use AESNI automatically. +The feature is enabled by default + ## Unit Testing The unit testing vectors used are included in [NIST-Recommendation for Block Cipher Modes of Operation](http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf) diff --git a/aesni/aesni-enc-cbc.h b/aesni/aesni-enc-cbc.h new file mode 100644 index 0000000..4328c66 --- /dev/null +++ b/aesni/aesni-enc-cbc.h @@ -0,0 +1,63 @@ +#ifndef AESNIENCCBC_H +#define AESNIENCCBC_H + +#include + +namespace { + +void AES_CBC_encrypt(const unsigned char *in, + unsigned char *out, + unsigned char ivec[16], + unsigned long length, + const char *key, + int number_of_rounds) +{ + __m128i feedback,data; + unsigned long i; + int j; + if (length%16) + length = length/16+1; + else length /=16; + feedback=_mm_loadu_si128 ((__m128i*)ivec); + for(i=0; i < length; i++) { + data = _mm_loadu_si128 (&((__m128i*)in)[i]); + feedback = _mm_xor_si128 (data,feedback); + feedback = _mm_xor_si128 (feedback,((__m128i*)key)[0]); + for(j=1; j + +namespace { + +/* Note – the length of the output buffer is assumed to be a multiple of 16 bytes */ +void AES_ECB_encrypt(const unsigned char *in, //pointer to the PLAINTEXT + unsigned char *out, //pointer to the CIPHERTEXT buffer + unsigned long length, //text length in bytes + const char *key, //pointer to the expanded key schedule + int number_of_rounds) //number of AES rounds 10,12 or 14 +{ + __m128i tmp; + unsigned long i; + int j; + if(length%16) + length = length/16+1; + else + length = length/16; + for(i = 0; i < length; i++) { + tmp = _mm_loadu_si128 (&((__m128i*)in)[i]); + tmp = _mm_xor_si128 (tmp,((__m128i*)key)[0]); + for(j=1; j + +#define cpuid(func, ax, bx, cx, dx)\ + __asm__ __volatile__("cpuid": "=a" (ax), "=b" (bx), "=c" (cx), "=d" (dx) : "a" (func)); + +namespace { + +bool check_aesni_support() +{ + unsigned int a,b,c,d; + cpuid(1, a,b,c,d); + return (c & 0x2000000); +} + +__m128i AES_128_ASSIST (__m128i temp1, __m128i temp2) +{ + __m128i temp3; + temp2 = _mm_shuffle_epi32 (temp2 ,0xff); + temp3 = _mm_slli_si128 (temp1, 0x4); + temp1 = _mm_xor_si128 (temp1, temp3); + temp3 = _mm_slli_si128 (temp3, 0x4); + temp1 = _mm_xor_si128 (temp1, temp3); + temp3 = _mm_slli_si128 (temp3, 0x4); + temp1 = _mm_xor_si128 (temp1, temp3); + temp1 = _mm_xor_si128 (temp1, temp2); + return temp1; +} + +void AES_128_Key_Expansion (const unsigned char *userkey, + unsigned char *key) +{ + __m128i temp1, temp2; + __m128i *Key_Schedule = (__m128i*)key; + temp1 = _mm_loadu_si128((__m128i*)userkey); + Key_Schedule[0] = temp1; + temp2 = _mm_aeskeygenassist_si128 (temp1 ,0x1); + temp1 = AES_128_ASSIST(temp1, temp2); + Key_Schedule[1] = temp1; + temp2 = _mm_aeskeygenassist_si128 (temp1,0x2); + temp1 = AES_128_ASSIST(temp1, temp2); + Key_Schedule[2] = temp1; + temp2 = _mm_aeskeygenassist_si128 (temp1,0x4); + temp1 = AES_128_ASSIST(temp1, temp2); + Key_Schedule[3] = temp1; + temp2 = _mm_aeskeygenassist_si128 (temp1,0x8); + temp1 = AES_128_ASSIST(temp1, temp2); + Key_Schedule[4] = temp1; + temp2 = _mm_aeskeygenassist_si128 (temp1,0x10); + temp1 = AES_128_ASSIST(temp1, temp2); + Key_Schedule[5] = temp1; + temp2 = _mm_aeskeygenassist_si128 (temp1,0x20); + temp1 = AES_128_ASSIST(temp1, temp2); + Key_Schedule[6] = temp1; + temp2 = _mm_aeskeygenassist_si128 (temp1,0x40); + temp1 = AES_128_ASSIST(temp1, temp2); + Key_Schedule[7] = temp1; + temp2 = _mm_aeskeygenassist_si128 (temp1,0x80); + temp1 = AES_128_ASSIST(temp1, temp2); + Key_Schedule[8] = temp1; + temp2 = _mm_aeskeygenassist_si128 (temp1,0x1b); + temp1 = AES_128_ASSIST(temp1, temp2); + Key_Schedule[9] = temp1; + temp2 = _mm_aeskeygenassist_si128 (temp1,0x36); + temp1 = AES_128_ASSIST(temp1, temp2); + Key_Schedule[10] = temp1; +} + +void KEY_192_ASSIST(__m128i* temp1, __m128i * temp2, __m128i * temp3) +{ + __m128i temp4; + *temp2 = _mm_shuffle_epi32 (*temp2, 0x55); + temp4 = _mm_slli_si128 (*temp1, 0x4); + *temp1 = _mm_xor_si128 (*temp1, temp4); + temp4 = _mm_slli_si128 (temp4, 0x4); + *temp1 = _mm_xor_si128 (*temp1, temp4); + temp4 = _mm_slli_si128 (temp4, 0x4); + *temp1 = _mm_xor_si128 (*temp1, temp4); + *temp1 = _mm_xor_si128 (*temp1, *temp2); + *temp2 = _mm_shuffle_epi32(*temp1, 0xff); + temp4 = _mm_slli_si128 (*temp3, 0x4); + *temp3 = _mm_xor_si128 (*temp3, temp4); + *temp3 = _mm_xor_si128 (*temp3, *temp2); +} + +void AES_192_Key_Expansion (const unsigned char *userkey, unsigned char *key) +{ + __m128i temp1, temp2, temp3; + __m128i *Key_Schedule = (__m128i*)key; + temp1 = _mm_loadu_si128((__m128i*)userkey); + temp3 = _mm_loadu_si128((__m128i*)(userkey+16)); + Key_Schedule[0]=temp1; Key_Schedule[1]=temp3; + temp2=_mm_aeskeygenassist_si128 (temp3,0x1); + KEY_192_ASSIST(&temp1, &temp2, &temp3); + Key_Schedule[1] = (__m128i)_mm_shuffle_pd((__m128d)Key_Schedule[1], (__m128d)temp1,0); + Key_Schedule[2] = (__m128i)_mm_shuffle_pd((__m128d)temp1,(__m128d)temp3,1); + temp2=_mm_aeskeygenassist_si128 (temp3,0x2); + KEY_192_ASSIST(&temp1, &temp2, &temp3); + Key_Schedule[3]=temp1; + Key_Schedule[4]=temp3; + temp2=_mm_aeskeygenassist_si128 (temp3,0x4); + KEY_192_ASSIST(&temp1, &temp2, &temp3); + Key_Schedule[4] = (__m128i)_mm_shuffle_pd((__m128d)Key_Schedule[4], (__m128d)temp1,0); + Key_Schedule[5] = (__m128i)_mm_shuffle_pd((__m128d)temp1,(__m128d)temp3,1); + temp2=_mm_aeskeygenassist_si128 (temp3,0x8); + KEY_192_ASSIST(&temp1, &temp2, &temp3); + Key_Schedule[6]=temp1; + Key_Schedule[7]=temp3; + temp2=_mm_aeskeygenassist_si128 (temp3,0x10); + KEY_192_ASSIST(&temp1, &temp2, &temp3); + Key_Schedule[7] = (__m128i)_mm_shuffle_pd((__m128d)Key_Schedule[7], (__m128d)temp1,0); + Key_Schedule[8] = (__m128i)_mm_shuffle_pd((__m128d)temp1,(__m128d)temp3,1); + temp2=_mm_aeskeygenassist_si128 (temp3,0x20); + KEY_192_ASSIST(&temp1, &temp2, &temp3); + Key_Schedule[9]=temp1; + Key_Schedule[10]=temp3; + temp2=_mm_aeskeygenassist_si128 (temp3,0x40); + KEY_192_ASSIST(&temp1, &temp2, &temp3); + Key_Schedule[10] = (__m128i)_mm_shuffle_pd((__m128d)Key_Schedule[10], (__m128d)temp1,0); + Key_Schedule[11] = (__m128i)_mm_shuffle_pd((__m128d)temp1,(__m128d)temp3,1); + temp2=_mm_aeskeygenassist_si128 (temp3,0x80); + KEY_192_ASSIST(&temp1, &temp2, &temp3); + Key_Schedule[12]=temp1; +} + +void KEY_256_ASSIST_1(__m128i* temp1, __m128i * temp2) +{ + __m128i temp4; + *temp2 = _mm_shuffle_epi32(*temp2, 0xff); + temp4 = _mm_slli_si128 (*temp1, 0x4); + *temp1 = _mm_xor_si128 (*temp1, temp4); + temp4 = _mm_slli_si128 (temp4, 0x4); + *temp1 = _mm_xor_si128 (*temp1, temp4); + temp4 = _mm_slli_si128 (temp4, 0x4); + *temp1 = _mm_xor_si128 (*temp1, temp4); + *temp1 = _mm_xor_si128 (*temp1, *temp2); +} + +void KEY_256_ASSIST_2(__m128i* temp1, __m128i * temp3) +{ + __m128i temp2,temp4; + temp4 = _mm_aeskeygenassist_si128 (*temp1, 0x0); + temp2 = _mm_shuffle_epi32(temp4, 0xaa); + temp4 = _mm_slli_si128 (*temp3, 0x4); + *temp3 = _mm_xor_si128 (*temp3, temp4); + temp4 = _mm_slli_si128 (temp4, 0x4); + *temp3 = _mm_xor_si128 (*temp3, temp4); + temp4 = _mm_slli_si128 (temp4, 0x4); + *temp3 = _mm_xor_si128 (*temp3, temp4); + *temp3 = _mm_xor_si128 (*temp3, temp2); +} + +void AES_256_Key_Expansion (const unsigned char *userkey, unsigned char *key) +{ + __m128i temp1, temp2, temp3; + __m128i *Key_Schedule = (__m128i*)key; + temp1 = _mm_loadu_si128((__m128i*)userkey); + temp3 = _mm_loadu_si128((__m128i*)(userkey+16)); + Key_Schedule[0] = temp1; Key_Schedule[1] = temp3; + temp2 = _mm_aeskeygenassist_si128 (temp3,0x01); + KEY_256_ASSIST_1(&temp1, &temp2); + Key_Schedule[2]=temp1; + KEY_256_ASSIST_2(&temp1, &temp3); + Key_Schedule[3]=temp3; + temp2 = _mm_aeskeygenassist_si128 (temp3,0x02); + KEY_256_ASSIST_1(&temp1, &temp2); + Key_Schedule[4]=temp1; + KEY_256_ASSIST_2(&temp1, &temp3); + Key_Schedule[5]=temp3; + temp2 = _mm_aeskeygenassist_si128 (temp3,0x04); + KEY_256_ASSIST_1(&temp1, &temp2); + Key_Schedule[6]=temp1; + KEY_256_ASSIST_2(&temp1, &temp3); + Key_Schedule[7]=temp3; + temp2 = _mm_aeskeygenassist_si128 (temp3,0x08); + KEY_256_ASSIST_1(&temp1, &temp2); + Key_Schedule[8]=temp1; + KEY_256_ASSIST_2(&temp1, &temp3); + Key_Schedule[9]=temp3; + temp2 = _mm_aeskeygenassist_si128 (temp3,0x10); + KEY_256_ASSIST_1(&temp1, &temp2); + Key_Schedule[10]=temp1; + KEY_256_ASSIST_2(&temp1, &temp3); + Key_Schedule[11]=temp3; + temp2 = _mm_aeskeygenassist_si128 (temp3,0x20); + KEY_256_ASSIST_1(&temp1, &temp2); + Key_Schedule[12]=temp1; + KEY_256_ASSIST_2(&temp1, &temp3); + Key_Schedule[13]=temp3; + temp2 = _mm_aeskeygenassist_si128 (temp3,0x40); + KEY_256_ASSIST_1(&temp1, &temp2); + Key_Schedule[14]=temp1; +} + +} + +#endif // AESNIKEYEXP_H diff --git a/main.cpp b/main.cpp index 40e661c..ef3b7c2 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,13 @@ #include #include + +#ifdef __cplusplus #include "unit_test/aestest.h" +#endif int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); - AesTest test1; QTest::qExec(&test1); return 0; diff --git a/qaesencryption.cpp b/qaesencryption.cpp index 87a9a12..9103fb8 100644 --- a/qaesencryption.cpp +++ b/qaesencryption.cpp @@ -1,4 +1,12 @@ #include "qaesencryption.h" +#include +#include + +#ifdef USE_INTEL_AES_IF_AVAILABLE +#include "aesni/aesni-key-exp.h" +#include "aesni/aesni-enc-ecb.h" +#include "aesni/aesni-enc-cbc.h" +#endif /* * Static Functions @@ -34,7 +42,11 @@ QByteArray QAESEncryption::RemovePadding(const QByteArray &rawText, QAESEncrypti ret.remove(ret.length()-1, 1); break; case Padding::PKCS7: - ret.remove(ret.length() - *ret.rbegin(), *ret.rbegin()); +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + ret.remove(ret.length() - ret.back(), ret.back()); +#else + ret.remove(ret.length() - ret.at(ret.length() - 1), ret.at(ret.length() - 1)); +#endif break; case Padding::ISO: { @@ -49,7 +61,7 @@ QByteArray QAESEncryption::RemovePadding(const QByteArray &rawText, QAESEncrypti } // And check if it's the byte for marking padding - if (ret.at(marker_index) == static_cast(0x80)) + if (ret.at(marker_index) == '\x80') { ret.truncate(marker_index); } @@ -66,28 +78,36 @@ QByteArray QAESEncryption::RemovePadding(const QByteArray &rawText, QAESEncrypti * */ /* - * Inline Functions + * Local Functions * */ -inline quint8 xTime(quint8 x){ - return ((x<<1) ^ (((x>>7) & 1) * 0x1b)); +namespace { + +quint8 xTime(quint8 x) +{ + return ((x<<1) ^ (((x>>7) & 1) * 0x1b)); } -inline quint8 multiply(quint8 x, quint8 y){ - return (((y & 1) * x) ^ ((y>>1 & 1) * xTime(x)) ^ ((y>>2 & 1) * xTime(xTime(x))) ^ ((y>>3 & 1) +quint8 multiply(quint8 x, quint8 y) +{ + return (((y & 1) * x) ^ ((y>>1 & 1) * xTime(x)) ^ ((y>>2 & 1) * xTime(xTime(x))) ^ ((y>>3 & 1) * xTime(xTime(xTime(x)))) ^ ((y>>4 & 1) * xTime(xTime(xTime(xTime(x)))))); } -/* - * End Inline functions - * */ +} +/* + * End Local functions + * */ QAESEncryption::QAESEncryption(Aes level, Mode mode, Padding padding) : m_nb(4), m_blocklen(16), m_level(level), m_mode(mode), m_padding(padding) + , m_aesNIAvailable(false), m_state(nullptr) { - m_state = NULL; +#ifdef USE_INTEL_AES_IF_AVAILABLE + m_aesNIAvailable = check_aesni_support(); +#endif switch (level) { @@ -141,7 +161,7 @@ QByteArray QAESEncryption::getPadding(int currSize, int alignment) break; case Padding::ISO: if (size > 0) - return QByteArray (size - 1, 0x00).prepend(0x80); + return QByteArray (size - 1, 0x00).prepend('\x80'); break; default: return QByteArray(size, 0x00); @@ -152,61 +172,104 @@ QByteArray QAESEncryption::getPadding(int currSize, int alignment) QByteArray QAESEncryption::expandKey(const QByteArray &key) { - int i, k; - quint8 tempa[4]; // Used for the column/row operations - QByteArray roundKey(key); - // The first round key is the key itself. - // ... +#ifdef USE_INTEL_AES_IF_AVAILABLE + if (true){ + switch(m_level) { + case AES_128: { + AES128 aes128; + quint8 ret[aes128.expandedKey]; + memset(ret, 0x00, sizeof(ret)); + quint8 uchar_key[key.size()]; + memcpy(uchar_key, key.data(), key.size()); + AES_128_Key_Expansion(uchar_key, ret); + return QByteArray((char*) ret, aes128.expandedKey); + } + break; + case AES_192: { + AES192 aes192; + quint8 ret[aes192.expandedKey]; + memset(ret, 0x00, sizeof(ret)); + quint8 uchar_key[key.size()]; + memcpy(uchar_key, key.data(), key.size()); - // All other round keys are found from the previous round keys. - //i == Nk - for(i = m_nk; i < m_nb * (m_nr + 1); i++) + AES_192_Key_Expansion(uchar_key, ret); + return QByteArray((char*) ret, aes192.expandedKey); + } + break; + case AES_256: { + AES256 aes256; + quint8 ret[aes256.expandedKey]; + memset(ret, 0x00, sizeof(ret)); + quint8 uchar_key[key.size()]; + memcpy(uchar_key, key.data(), key.size()); + + AES_256_Key_Expansion(uchar_key, ret); + return QByteArray((char*) ret, aes256.expandedKey); + } + break; + default: + return QByteArray(); + break; + } + } else +#endif { - tempa[0] = (quint8) roundKey.at((i-1) * 4 + 0); - tempa[1] = (quint8) roundKey.at((i-1) * 4 + 1); - tempa[2] = (quint8) roundKey.at((i-1) * 4 + 2); - tempa[3] = (quint8) roundKey.at((i-1) * 4 + 3); - if (i % m_nk == 0) - { - // This function shifts the 4 bytes in a word to the left once. - // [a0,a1,a2,a3] becomes [a1,a2,a3,a0] + int i, k; + quint8 tempa[4]; // Used for the column/row operations + QByteArray roundKey(key); // The first round key is the key itself. - // Function RotWord() - k = tempa[0]; - tempa[0] = tempa[1]; - tempa[1] = tempa[2]; - tempa[2] = tempa[3]; - tempa[3] = k; + // All other round keys are found from the previous round keys. + //i == Nk + for(i = m_nk; i < m_nb * (m_nr + 1); i++) + { + tempa[0] = (quint8) roundKey.at((i-1) * 4 + 0); + tempa[1] = (quint8) roundKey.at((i-1) * 4 + 1); + tempa[2] = (quint8) roundKey.at((i-1) * 4 + 2); + tempa[3] = (quint8) roundKey.at((i-1) * 4 + 3); - // Function Subword() - tempa[0] = getSBoxValue(tempa[0]); - tempa[1] = getSBoxValue(tempa[1]); - tempa[2] = getSBoxValue(tempa[2]); - tempa[3] = getSBoxValue(tempa[3]); + if (i % m_nk == 0) + { + // This function shifts the 4 bytes in a word to the left once. + // [a0,a1,a2,a3] becomes [a1,a2,a3,a0] - tempa[0] = tempa[0] ^ Rcon[i/m_nk]; - } - if (m_level == AES_256 && i % m_nk == 4) - { - // Function Subword() - tempa[0] = getSBoxValue(tempa[0]); - tempa[1] = getSBoxValue(tempa[1]); - tempa[2] = getSBoxValue(tempa[2]); - tempa[3] = getSBoxValue(tempa[3]); - } - roundKey.insert(i * 4 + 0, (quint8) roundKey.at((i - m_nk) * 4 + 0) ^ tempa[0]); - roundKey.insert(i * 4 + 1, (quint8) roundKey.at((i - m_nk) * 4 + 1) ^ tempa[1]); - roundKey.insert(i * 4 + 2, (quint8) roundKey.at((i - m_nk) * 4 + 2) ^ tempa[2]); - roundKey.insert(i * 4 + 3, (quint8) roundKey.at((i - m_nk) * 4 + 3) ^ tempa[3]); + // Function RotWord() + k = tempa[0]; + tempa[0] = tempa[1]; + tempa[1] = tempa[2]; + tempa[2] = tempa[3]; + tempa[3] = k; + + // Function Subword() + tempa[0] = getSBoxValue(tempa[0]); + tempa[1] = getSBoxValue(tempa[1]); + tempa[2] = getSBoxValue(tempa[2]); + tempa[3] = getSBoxValue(tempa[3]); + + tempa[0] = tempa[0] ^ Rcon[i/m_nk]; + } + + if (m_level == AES_256 && i % m_nk == 4) + { + // Function Subword() + tempa[0] = getSBoxValue(tempa[0]); + tempa[1] = getSBoxValue(tempa[1]); + tempa[2] = getSBoxValue(tempa[2]); + tempa[3] = getSBoxValue(tempa[3]); + } + roundKey.insert(i * 4 + 0, (quint8) roundKey.at((i - m_nk) * 4 + 0) ^ tempa[0]); + roundKey.insert(i * 4 + 1, (quint8) roundKey.at((i - m_nk) * 4 + 1) ^ tempa[1]); + roundKey.insert(i * 4 + 2, (quint8) roundKey.at((i - m_nk) * 4 + 2) ^ tempa[2]); + roundKey.insert(i * 4 + 3, (quint8) roundKey.at((i - m_nk) * 4 + 3) ^ tempa[3]); + } + return roundKey; } - return roundKey; } // This function adds the round key to state. // The round key is added to the state by an XOR function. -void QAESEncryption::addRoundKey(const quint8 round, const QByteArray expKey) +void QAESEncryption::addRoundKey(const quint8 round, const QByteArray &expKey) { QByteArray::iterator it = m_state->begin(); for(int i=0; i < 16; ++i) @@ -331,11 +394,11 @@ void QAESEncryption::invShiftRows() it[6] = (quint8)temp; //Shift 3 - temp = (quint8)it[15]; - it[15] = (quint8)it[3]; - it[3] = (quint8)it[7]; + temp = (quint8)it[7]; it[7] = (quint8)it[11]; - it[11] = (quint8)temp; + it[11] = (quint8)it[15]; + it[15] = (quint8)it[3]; + it[3] = (quint8)temp; } QByteArray QAESEncryption::byteXor(const QByteArray &a, const QByteArray &b) @@ -409,12 +472,20 @@ QByteArray QAESEncryption::invCipher(const QByteArray &expKey, const QByteArray return output; } +QByteArray QAESEncryption::printArray(uchar* arr, int size) +{ + QByteArray print(""); + for(int i=0; i= CBC && (iv.isNull() || iv.size() != m_blocklen)) + if (m_mode >= CBC && (iv.isEmpty() || iv.size() != m_blocklen)) return QByteArray(); - QByteArray ret; QByteArray expandedKey = expandKey(key); QByteArray alignedText(rawText); @@ -423,45 +494,86 @@ QByteArray QAESEncryption::encode(const QByteArray &rawText, const QByteArray &k switch(m_mode) { - case ECB: + case ECB: { +#ifdef USE_INTEL_AES_IF_AVAILABLE + if (m_aesNIAvailable){ + unsigned char in[alignedText.size()]; + memcpy(in, alignedText.data(), alignedText.size()); + unsigned char out[alignedText.size()]; + memcpy(out, alignedText.data(), alignedText.size()); + char expKey[expandedKey.size()]; + memcpy(expKey, expandedKey.data(), expandedKey.size()); + AES_ECB_encrypt(in, out, alignedText.size(), + expKey, m_nr); + return QByteArray((char*)out, alignedText.size()); + } +#endif + QByteArray ret; for(int i=0; i < alignedText.size(); i+= m_blocklen) ret.append(cipher(expandedKey, alignedText.mid(i, m_blocklen))); - break; + return ret; + } + break; case CBC: { - QByteArray ivTemp(iv); - for(int i=0; i < alignedText.size(); i+= m_blocklen) { - alignedText.replace(i, m_blocklen, byteXor(alignedText.mid(i, m_blocklen),ivTemp)); - ret.append(cipher(expandedKey, alignedText.mid(i, m_blocklen))); - ivTemp = ret.mid(i, m_blocklen); - } +#ifdef USE_INTEL_AES_IF_AVAILABLE + if (m_aesNIAvailable){ + quint8 in[alignedText.size()]; + memcpy(in, alignedText.constData(), alignedText.size()); + quint8 ivec[iv.size()]; + memcpy(ivec, iv.data(), iv.size()); + char out[alignedText.size()]; + memset(out, 0x00, alignedText.size()); + char expKey[expandedKey.size()]; + memcpy(expKey, expandedKey.data(), expandedKey.size()); + AES_CBC_encrypt(in, + (unsigned char*) out, + ivec, + alignedText.size(), + expKey, + m_nr); + return QByteArray(out, alignedText.size()); } - break; +#endif + QByteArray ret; + QByteArray ivTemp(iv); + for(int i=0; i < alignedText.size(); i+= m_blocklen) { + alignedText.replace(i, m_blocklen, byteXor(alignedText.mid(i, m_blocklen),ivTemp)); + ret.append(cipher(expandedKey, alignedText.mid(i, m_blocklen))); + ivTemp = ret.mid(i, m_blocklen); + } + return ret; + } + break; case CFB: { - ret.append(byteXor(alignedText.left(m_blocklen), cipher(expandedKey, iv))); - for(int i=0; i < alignedText.size(); i+= m_blocklen) { - if (i+m_blocklen < alignedText.size()) - ret.append(byteXor(alignedText.mid(i+m_blocklen, m_blocklen), - cipher(expandedKey, ret.mid(i, m_blocklen)))); - } + QByteArray ret; + ret.append(byteXor(alignedText.left(m_blocklen), cipher(expandedKey, iv))); + for(int i=0; i < alignedText.size(); i+= m_blocklen) { + if (i+m_blocklen < alignedText.size()) + ret.append(byteXor(alignedText.mid(i+m_blocklen, m_blocklen), + cipher(expandedKey, ret.mid(i, m_blocklen)))); } - break; + return ret; + } + break; case OFB: { - QByteArray ofbTemp; - ofbTemp.append(cipher(expandedKey, iv)); - for (int i=m_blocklen; i < alignedText.size(); i += m_blocklen){ - ofbTemp.append(cipher(expandedKey, ofbTemp.right(m_blocklen))); - } - ret.append(byteXor(alignedText, ofbTemp)); + QByteArray ret; + QByteArray ofbTemp; + ofbTemp.append(cipher(expandedKey, iv)); + for (int i=m_blocklen; i < alignedText.size(); i += m_blocklen){ + ofbTemp.append(cipher(expandedKey, ofbTemp.right(m_blocklen))); } - break; + ret.append(byteXor(alignedText, ofbTemp)); + return ret; + } + break; default: break; } - return ret; + return QByteArray(); } QByteArray QAESEncryption::decode(const QByteArray &rawText, const QByteArray &key, const QByteArray &iv) { - if (m_mode >= CBC && (iv.isNull() || iv.size() != m_blocklen)) + if (m_mode >= CBC && (iv.isEmpty() || iv.size() != m_blocklen)) return QByteArray(); QByteArray ret; diff --git a/qaesencryption.h b/qaesencryption.h index 79e0824..e19f862 100644 --- a/qaesencryption.h +++ b/qaesencryption.h @@ -4,6 +4,12 @@ #include #include +#ifdef __linux__ +#ifndef __LP64__ +#define do_rdtsc _do_rdtsc +#endif +#endif + class Q_DECL_EXPORT QAESEncryption : public QObject { Q_OBJECT @@ -28,20 +34,21 @@ public: }; static QByteArray Crypt(QAESEncryption::Aes level, QAESEncryption::Mode mode, const QByteArray &rawText, const QByteArray &key, - const QByteArray &iv = NULL, QAESEncryption::Padding padding = QAESEncryption::ISO); + const QByteArray &iv = QByteArray(), QAESEncryption::Padding padding = QAESEncryption::ISO); static QByteArray Decrypt(QAESEncryption::Aes level, QAESEncryption::Mode mode, const QByteArray &rawText, const QByteArray &key, - const QByteArray &iv = NULL, QAESEncryption::Padding padding = QAESEncryption::ISO); + const QByteArray &iv = QByteArray(), QAESEncryption::Padding padding = QAESEncryption::ISO); static QByteArray ExpandKey(QAESEncryption::Aes level, QAESEncryption::Mode mode, const QByteArray &key); - static QByteArray RemovePadding(const QByteArray &rawText, QAESEncryption::Padding padding); + static QByteArray RemovePadding(const QByteArray &rawText, QAESEncryption::Padding padding = QAESEncryption::ISO); QAESEncryption(QAESEncryption::Aes level, QAESEncryption::Mode mode, QAESEncryption::Padding padding = QAESEncryption::ISO); - QByteArray encode(const QByteArray &rawText, const QByteArray &key, const QByteArray &iv = NULL); - QByteArray decode(const QByteArray &rawText, const QByteArray &key, const QByteArray &iv = NULL); + QByteArray encode(const QByteArray &rawText, const QByteArray &key, const QByteArray &iv = QByteArray()); + QByteArray decode(const QByteArray &rawText, const QByteArray &key, const QByteArray &iv = QByteArray()); QByteArray removePadding(const QByteArray &rawText); QByteArray expandKey(const QByteArray &key); + QByteArray printArray(uchar *arr, int size); signals: public slots: @@ -56,6 +63,7 @@ private: int m_nr; int m_expandedKey; int m_padding; + bool m_aesNIAvailable; QByteArray* m_state; struct AES256{ @@ -82,7 +90,7 @@ private: quint8 getSBoxValue(quint8 num){return sbox[num];} quint8 getSBoxInvert(quint8 num){return rsbox[num];} - void addRoundKey(const quint8 round, const QByteArray expKey); + void addRoundKey(const quint8 round, const QByteArray &expKey); void subBytes(); void shiftRows(); void mixColumns(); @@ -90,9 +98,9 @@ private: void invSubBytes(); void invShiftRows(); QByteArray getPadding(int currSize, int alignment); - QByteArray cipher(const QByteArray &expKey, const QByteArray &plainText); - QByteArray invCipher(const QByteArray &expKey, const QByteArray &plainText); - QByteArray byteXor(const QByteArray &in, const QByteArray &iv); + QByteArray cipher(const QByteArray &expKey, const QByteArray &in); + QByteArray invCipher(const QByteArray &expKey, const QByteArray &in); + QByteArray byteXor(const QByteArray &a, const QByteArray &b); const quint8 sbox[256] = { //0 1 2 3 4 5 6 7 8 9 A B C D E F diff --git a/qaesencryption.pro b/qaesencryption.pro index dc462c5..dc5bee6 100644 --- a/qaesencryption.pro +++ b/qaesencryption.pro @@ -9,16 +9,15 @@ CONFIG -= app_bundle TEMPLATE = app -SOURCES += main.cpp \ - qaesencryption.cpp \ - unit_test/aestest.cpp - # The following define makes your compiler emit warnings if you use # any feature of Qt which as been marked deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS +DEFINES += USE_INTEL_AES_IF_AVAILABLE +QMAKE_CXXFLAGS += -maes + CONFIG(release, debug|release): { DESTDIR="$$PWD/build/release" } else { @@ -32,8 +31,15 @@ CONFIG(release, debug|release): { HEADERS += \ qaesencryption.h \ + aesni/aesni-key-exp.h \ + aesni/aesni-enc-ecb.h \ + aesni/aesni-enc-cbc.h \ unit_test/aestest.h +SOURCES += main.cpp \ + qaesencryption.cpp \ + unit_test/aestest.cpp + DISTFILES += \ unit_test/longText.txt diff --git a/unit_test/aestest.cpp b/unit_test/aestest.cpp index 39080ca..e083e00 100644 --- a/unit_test/aestest.cpp +++ b/unit_test/aestest.cpp @@ -8,6 +8,9 @@ void AesTest::initTestCase() { +#ifdef USE_INTEL_AES_IF_AVAILABLE + qDebug() << "AESNI Enabled"; +#endif quint8 key_16[16] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}; for (int i=0; i<16; i++) key16.append(key_16[i]); @@ -54,6 +57,7 @@ void AesTest::initTestCase() inCBC128.append(text_cbc[i]); outCBC128.append(output_cbc[i]); } + }