Merge remote-tracking branch 'base/master' into HEAD

This commit is contained in:
Andrei Yankovich 2020-12-12 11:55:27 +03:00
commit d14c463d28
9 changed files with 564 additions and 103 deletions

View File

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

63
aesni/aesni-enc-cbc.h Normal file
View File

@ -0,0 +1,63 @@
#ifndef AESNIENCCBC_H
#define AESNIENCCBC_H
#include <wmmintrin.h>
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 <number_of_rounds; j++)
feedback = _mm_aesenc_si128 (feedback,((__m128i*)key)[j]);
feedback = _mm_aesenclast_si128 (feedback,((__m128i*)key)[j]); _mm_storeu_si128 (&((__m128i*)out)[i],feedback);
}
}
#if 0
void AES_CBC_decrypt(const unsigned char *in,
unsigned char *out,
unsigned char ivec[16],
unsigned long length,
const char *key,
int number_of_rounds)
{
__m128i data,feedback,last_in;
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++) {
last_in=_mm_loadu_si128 (&((__m128i*)in)[i]);
data = _mm_xor_si128 (last_in,((__m128i*)key)[0]);
for(j=1; j <number_of_rounds; j++) {
data = _mm_aesdec_si128 (data,((__m128i*)key)[j]);
}
data = _mm_aesdeclast_si128 (data,((__m128i*)key)[j]);
data = _mm_xor_si128 (data,feedback);
_mm_storeu_si128 (&((__m128i*)out)[i],data);
feedback=last_in;
}
}
#endif
}
#endif // AESNIENCCBC_H

61
aesni/aesni-enc-ecb.h Normal file
View File

@ -0,0 +1,61 @@
#ifndef AESNIENCECB_H
#define AESNIENCECB_H
#include <wmmintrin.h>
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 <number_of_rounds; j++) {
tmp = _mm_aesenc_si128 (tmp, ((__m128i*)key)[j]);
}
tmp = _mm_aesenclast_si128 (tmp,((__m128i*)key)[j]);
_mm_storeu_si128 (&((__m128i*)out)[i],tmp);
}
}
#if 0
void AES_ECB_decrypt(const unsigned char *in, //pointer to the CIPHERTEXT
unsigned char *out, //pointer to the DECRYPTED TEXT 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 < number_of_rounds; j++) {
tmp = _mm_aesdec_si128 (tmp,((__m128i*)key)[j]);
}
tmp = _mm_aesdeclast_si128 (tmp,((__m128i*)key)[j]);
_mm_storeu_si128 (&((__m128i*)out)[i],tmp);
}
}
#endif
}
#endif // AESNIENCECB_H

199
aesni/aesni-key-exp.h Normal file
View File

@ -0,0 +1,199 @@
#ifndef AESNIKEYEXP_H
#define AESNIKEYEXP_H
#include <wmmintrin.h>
#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

View File

@ -1,11 +1,13 @@
#include <QCoreApplication>
#include <QTest>
#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;

View File

@ -1,4 +1,12 @@
#include "qaesencryption.h"
#include <QDebug>
#include <QVector>
#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<char>(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<size; i++)
print.append(arr[i]);
return print.toHex();
}
QByteArray QAESEncryption::encode(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;
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;

View File

@ -4,6 +4,12 @@
#include <QObject>
#include <QByteArray>
#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

View File

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

View File

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