Compare commits

...

68 Commits

Author SHA1 Message Date
86811dd24f added exports symbols 2021-04-29 21:42:14 +03:00
ffeda3b4a4 addded export symbols 2021-04-29 21:32:08 +03:00
6e94df14d5
Merge pull request #1 from QuasarApp/update
Update
2021-04-16 09:11:24 +03:00
d14c463d28 Merge remote-tracking branch 'base/master' into HEAD 2020-12-12 11:55:27 +03:00
Oleg Derevenetz
0ddf76d59a
Minor improvements (#33)
* Pass expKey parameter of addRoundKey() by const reference to avoid unnecessary copy.

* Use C++11 nullptr instead of NULL, make it clear that default value of iv parameter in encode() and decode() is empty QByteArray instead of implicit conversion from null pointer via QByteArray(const char *, int = -1) constructor.

* Change parameter names in declarations of cipher(), invCipher() and byteXor() to match definitions.

* Convert AES-NI-related files to headers, place functions with internal linkage to anonymous namespace to avoid exporting them, don't use inline specifier (inline keyword have different meaning in C++ rather than in C).

* Use char literals instead of implementation-defined int-to-signed-char conversions where possible.

* Set default value for padding argument in static RemovePadding() to match sample in README.
2020-09-09 12:39:11 -07:00
Ilya Chesalin
a22e7bd5f9
Fixed invShiftRows in Shift 3 (#30)
This must be broken. The indices of iterable inside shift 3 seem random and do not correspond to the AES algorighm. More than that, it should be reversed from shiftRows methods, and shifts 1 and 2 seem normal.
2020-05-15 17:28:30 -07:00
Matteo Brichese
74643b2570 updated readme after aesni merge 2020-05-15 16:58:38 -07:00
Matteo B
78efdb4f3f
Adding ECB and CBC modes via AES-NI (#32)
Added ECB and CBC modes via AES-NI, only is enabled and supported
2020-05-15 16:45:35 -07:00
60e07bbb94 update Copyright 2020-01-06 14:12:22 +03:00
2a35a79597 fix build on qt 5.9 2019-10-07 13:01:29 +03:00
61876c7e06 fix win 2019-08-22 18:06:26 +03:00
18ae6b5306 added support of ccache 2019-08-22 17:49:39 +03:00
Matteo B
f5ef736a7f
Merge pull request #29 from mcmule/retrocomp-qbytearray-qt-lts
adding qt version check to keep retrocomp with qt pre 5.10 qbytearray
2019-07-09 10:44:11 -07:00
8c590ddd34 added export marker for class 2019-07-09 14:09:42 +03:00
Marc Muller
57b0cdf743 adding qt version check to keep retrocomp with qt pre 5.10 qbytearray 2019-06-28 11:40:31 +02:00
5ecbf59fca fix git ignore 2019-06-07 09:31:13 +03:00
1b3ddbd2d2 fix tests pro file 2019-06-04 18:13:13 +03:00
b296606aab fix git ignore 2019-05-30 15:27:05 +03:00
Matteo Brichese
4074157f0d cleanup 2019-03-27 21:34:19 -07:00
Matteo B
62d159430b
Merge pull request #25 from bricke/fix_PKCS7_blocklen
Fix PKCS7 when text size is a multiple of block size
2019-03-27 21:12:59 -07:00
Matteo Brichese
9537c6e5ad fixed size for blocklen 2019-03-27 21:09:59 -07:00
Matteo Brichese
4a06e8728a fix PKCS7 when text size is a multiple of block size 2019-03-27 21:04:58 -07:00
Matteo Brichese
3d4c882683 fix types and new added new test 2018-12-06 19:43:42 -08:00
Matteo B
39bc5e170e
Merge pull request #19 from tomgey/master
Fix ISO removePadding
2018-12-06 11:38:58 -08:00
Thomas Geymayer
3f2b2bcdce
Fix ISO removePadding
Fix removing of ISO padding when no padding was added.
2018-11-14 12:10:55 +01:00
Matteo B
a2ef03872b
Update README.md 2018-10-18 08:21:05 -07:00
Matteo Brichese
935b3d9c14
Update README.md 2018-04-09 16:07:33 -07:00
Matteo Brichese
d040dc73dc Merge branch 'master' of https://github.com/bricke/Qt-AES 2018-04-05 16:23:11 -07:00
Matteo Brichese
6096400737 adding OFB test 2018-04-05 16:23:01 -07:00
Matteo Brichese
c0cf26380d
Update README.md 2018-04-05 16:16:11 -07:00
Matteo Brichese
d96ee60dee
Update README.md 2018-04-05 16:15:32 -07:00
Matteo Brichese
e0d35f2fde
updated readme 2018-04-05 16:13:03 -07:00
Matteo Brichese
e5f942d924
Merge pull request #9 from bricke/OFB_dev
Ofb dev
2018-04-05 16:09:41 -07:00
Matteo Brichese
278565d661 adding OFB with basic test 2018-04-05 16:07:22 -07:00
Matteo Brichese
95104a6ef9 removing insert to fix compatibility with Qt5.6 2018-04-04 11:41:41 -07:00
Matteo Brichese
4ce12493f6 first try on OFB - not tested 2018-04-03 17:42:03 -07:00
Matteo Brichese
6fc1d88485 Merge branch 'master' of https://github.com/bricke/Qt-AES 2018-04-03 16:52:56 -07:00
Matteo Brichese
b376ee9723 added long text test 2018-04-03 16:52:39 -07:00
Matteo Brichese
a74a46e08e
Update README.md 2018-03-28 18:02:21 -07:00
Matteo Brichese
839c544555
updated readme 2018-03-28 18:01:14 -07:00
Matteo Brichese
80574a11a9 cleanup plus static/instance methods for padding removal 2018-03-28 17:59:11 -07:00
Matteo Brichese
b616caf7c1
Merge pull request #7 from bricke/test_padding
no conflicts
2018-03-28 17:51:38 -07:00
Matteo Brichese
56c2554df5 added padding stuff 2018-03-28 17:50:41 -07:00
Matteo Brichese
f94a6339de padding tests 2018-03-28 17:47:20 -07:00
Matteo Brichese
1809de722f working on padding feature 2018-03-28 17:42:09 -07:00
Matteo Brichese
dcb49d1fd1 adding padding standards 2018-03-28 16:43:13 -07:00
Matteo Brichese
c44296051f
fix title 2018-03-28 16:03:51 -07:00
Matteo Brichese
b7f75cbd2e
adding padding disclaimer 2018-03-28 16:03:30 -07:00
Matteo Brichese
8be7a0851d
Merge pull request #5 from BeardedBeaver/feature/Feature-ByteArrayByReference
QByteArrays now passed by reference to improve performance
2018-01-29 11:27:51 -08:00
Matteo Brichese
d69745c43e fixing IV in examples 2018-01-29 11:16:58 -08:00
linev
8b9bcdd933 QByteArrays now passed by reference to improve performance 2018-01-13 15:47:49 +03:00
Matteo Brichese
8b58ac9a3b
Merge pull request #3 from fetzerch/qt5.5
Fix compatibility with Qt 5.5
2017-11-06 14:28:27 -08:00
Christian Fetzer
dc64718fd5 Fix compatibility with Qt 5.5
QByteArray &QByteArray::append(int count, char ch) was introduced in Qt
5.7. This patch allows the project to be used with for example with the
current Ubuntu LTS version (16.04 Xenial).
2017-11-05 20:08:14 +01:00
Matteo Brichese
355230f397 security disclaimer 2017-07-13 09:07:19 -07:00
Matteo Brichese
3350cf1760 Merge pull request #1 from bricke/CFB-mode
CFB mode
2017-07-10 14:12:55 -07:00
Matteo Brichese
666e9dd004 updated readme 2017-07-10 14:12:09 -07:00
Matteo Brichese
e05e387b5c adding CFB-mode 2017-07-10 14:03:57 -07:00
Matteo Brichese
f651250939 first attempt at CFB mode 2017-07-10 13:10:38 -07:00
Matteo Brichese
8470ae108a Update README.md 2017-07-10 10:40:59 -07:00
Matteo Brichese
e40e4ae8d6 Update README.md 2017-07-10 10:40:43 -07:00
Matteo Brichese
ae940ecbaf Update README.md 2017-07-07 15:42:28 -07:00
Matteo Brichese
4e150f10fb optimizing Rcon table 2017-07-07 15:26:03 -07:00
Matteo Brichese
3b8b68cddc relocating var declaration 2017-07-07 14:20:52 -07:00
Matteo Brichese
ff02304e23 updating readme with method calls 2017-07-07 14:20:06 -07:00
Matteo Brichese
15d10586cc formatted following guidelines 2017-07-07 14:10:45 -07:00
Matteo Brichese
8f569d3fcb Update CONTRIBUTING.md 2017-07-07 13:57:21 -07:00
Matteo Brichese
0ed02f4e24 Create CONTRIBUTING.md 2017-07-07 13:56:39 -07:00
Matteo Brichese
e5c51ed205 Update README.md 2017-07-07 13:24:57 -07:00
16 changed files with 1727 additions and 245 deletions

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
*.user
*moc_*
*qrc_res*
*.swp
*.o
build/*
build/release/QAESEncryption
*.qaesencryption

5
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,5 @@
# Qt Conding Style
See [Qt Coding Style Doc](http://wiki.qt.io/Qt_Coding_Style)
# Qt Conding Conventions
See [Qt Coding Conventions Doc](http://wiki.qt.io/Coding_Conventions)

119
README.md
View File

@ -1,18 +1,64 @@
# Qt-AES
Small and portable AES encryption class for Qt.
Supports all key sizes - 128/192/256 bits - ECB and CBC 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
### Usage via instance
Example for 128bit ECB
### Available Methods
```
// 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 = 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 = QByteArray());
// Key expansion in Rijndael schedule
// return the new expanded key as byte array
QByteArray expandKey(const QByteArray key);
```
The same methods are available as static calls
```
QAESEncryption::Crypt => encode(...)
QAESEncryption::Decrypt => decode(...)
QAESEncryption::ExpandKey => expandKey(...)
```
#### AES Levels
The class supports all AES key lenghts
* AES_128
* AES_192
* AES_256
#### Modes
The class supports the following operating modes
* ECB
* CBC
* CFB
* OFB
#### Padding
By default the padding method is `ISO`, however, the class supports:
* ZERO
* PKCS7
* ISO
### Example
Sample code using a 128bit key in ECB mode
```
#include "qaesencryption.h"
QAESEncryption encryption(QAESEncryption::AES_128, QAESEncryption::ECB);
QByteArray encodedText = encryption.encode(plainText, key);
QAESEncryption encryption(QAESEncryption::AES_128, QAESEncryption::ECB);
QByteArray encodedText = encryption.encode(plainText, key);
QByteArray decodedText = encryption.decode(encodedText, key);
QByteArray decodedText = encryption.decode(encodedText, key);
```
Example for 256bit CBC using QString
@ -20,47 +66,70 @@ Example for 256bit CBC using QString
#include <QCryptographicHash>
#include "qaesencryption.h"
QAESEncryption encryption(QAESEncryption::AES_256, QAESEncryption::CBC);
QAESEncryption encryption(QAESEncryption::AES_256, QAESEncryption::CBC);
QString inputStr("The Advanced Encryption Standard (AES), also known by its original name Rijndael "
QString inputStr("The Advanced Encryption Standard (AES), also known by its original name Rijndael "
"is a specification for the encryption of electronic data established by the U.S. "
"National Institute of Standards and Technology (NIST) in 2001");
QString key("your-string-key");
QString iv("your-IV-vector");
QString key("your-string-key");
QString iv("your-IV-vector");
QByteArray hashKey = QCryptographicHash::hash(key.toLocal8Bit(), QCryptographicHash::Sha256);
QByteArray hashIV = QCryptographicHash::hash(iv.toLocal8Bit(), QCryptographicHash::Sha256);
QByteArray hashKey = QCryptographicHash::hash(key.toLocal8Bit(), QCryptographicHash::Sha256);
QByteArray hashIV = QCryptographicHash::hash(iv.toLocal8Bit(), QCryptographicHash::Md5);
QByteArray encodeText = encryption.encode(inputStr.toLocal8Bit(), hashKey, hashIV);
QByteArray decodeText = encryption.decode(encodeText, hashKey, hashIV);
QByteArray encodeText = encryption.encode(inputStr.toLocal8Bit(), hashKey, hashIV);
QByteArray decodeText = encryption.decode(encodeText, hashKey, hashIV);
QString decodedString = QString(encryption.removePadding(decodeText));
//decodedString == inputStr !!
```
### Usage via static invocation
Example of static invocation without creating instances
### Example via static invocation
Static invocation without creating instances, 256 bit key, ECB mode, starting from *QString* text/key
```
#include <QCryptographicHash>
#include "qaesencryption.h"
QString inputStr("The Advanced Encryption Standard (AES), also known by its original name Rijndael "
QString inputStr("The Advanced Encryption Standard (AES), also known by its original name Rijndael "
"is a specification for the encryption of electronic data established by the U.S. "
"National Institute of Standards and Technology (NIST) in 2001");
QString key("your-string-key");
QString iv("your-IV-vector");
QString key("your-string-key");
QString iv("your-IV-vector");
QByteArray hashKey = QCryptographicHash::hash(key.toLocal8Bit(), QCryptographicHash::Sha256);
QByteArray hashIV = QCryptographicHash::hash(iv.toLocal8Bit(), QCryptographicHash::Sha256);
QByteArray hashKey = QCryptographicHash::hash(key.toLocal8Bit(), QCryptographicHash::Sha256);
QByteArray hashIV = QCryptographicHash::hash(iv.toLocal8Bit(), QCryptographicHash::Md5);
//Static invocation
QAESEncryption::Crypt(QAESEncryption::AES_256, QAESEncryption::CBC, inputStr.toLocal8Bit(), hashKey, hashIV);
//Static invocation
QByteArray encrypted = QAESEncryption::Crypt(QAESEncryption::AES_256, QAESEncryption::CBC,
inputStr.toLocal8Bit(), hashKey, hashIV);
//...
// Removal of Padding via Static function
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 are available at [NIST-Recommendation for Block Cipher Modes of Operation](http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf)
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)
Please note that this code is not audited or AES-certified by any competent authority, use it at your own risk.
## Dependencies
* qtcore
No OpenSSL required.
## Contact
Question or suggestions are welcome!
Please use the GitHub issue tracking to report suggestions or issues.
## Licence
## License
This software is provided under the [UNLICENSE](http://unlicense.org/)
## Known Issues
Please take a look at the list of currently open issues

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

23
ccache.pri Executable file
View File

@ -0,0 +1,23 @@
#
# Copyright (C) 2018-2020 QuasarApp.
# Distributed under the lgplv3 software license, see the accompanying
# Everyone is permitted to copy and distribute verbatim copies
# of this license document, but changing it is not allowed.
#
!isEmpty(CCACHE_INCLUDE):error("ccache.pri already included")
CCACHE_INCLUDE = 1
contains(QMAKE_HOST.os, Linux):{
BIN = $$system(which ccache)
!isEmpty(BIN) {
message(ccache detected in $$BIN)
QMAKE_CXX='$$BIN $$QMAKE_CXX'
}
}

View File

@ -1,14 +1,15 @@
#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,35 +1,113 @@
#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
* */
QByteArray QAESEncryption::Crypt(QAESEncryption::AES level, QAESEncryption::MODE mode, const QByteArray rawText, const QByteArray key, const QByteArray iv)
QByteArray QAESEncryption::Crypt(QAESEncryption::Aes level, QAESEncryption::Mode mode, const QByteArray &rawText,
const QByteArray &key, const QByteArray &iv, QAESEncryption::Padding padding)
{
return QAESEncryption(level, mode).encode(rawText, key, iv);
return QAESEncryption(level, mode, padding).encode(rawText, key, iv);
}
QByteArray QAESEncryption::Decrypt(QAESEncryption::AES level, QAESEncryption::MODE mode, const QByteArray rawText, const QByteArray key, const QByteArray iv)
QByteArray QAESEncryption::Decrypt(QAESEncryption::Aes level, QAESEncryption::Mode mode, const QByteArray &rawText,
const QByteArray &key, const QByteArray &iv, QAESEncryption::Padding padding)
{
return QAESEncryption(level, mode).decode(rawText, key, iv);
return QAESEncryption(level, mode, padding).decode(rawText, key, iv);
}
QByteArray QAESEncryption::ExpandKey(QAESEncryption::AES level, QAESEncryption::MODE mode, const QByteArray key)
QByteArray QAESEncryption::ExpandKey(QAESEncryption::Aes level, QAESEncryption::Mode mode, const QByteArray &key)
{
return QAESEncryption(level, mode).expandKey(key);
}
QByteArray QAESEncryption::RemovePadding(const QByteArray &rawText, QAESEncryption::Padding padding)
{
if (rawText.isEmpty())
return rawText;
QByteArray ret(rawText);
switch (padding)
{
case Padding::ZERO:
//Works only if the last byte of the decoded array is not zero
while (ret.at(ret.length()-1) == 0x00)
ret.remove(ret.length()-1, 1);
break;
case Padding::PKCS7:
#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:
{
// Find the last byte which is not zero
int marker_index = ret.length() - 1;
for (; marker_index >= 0; --marker_index)
{
if (ret.at(marker_index) != 0x00)
{
break;
}
}
// And check if it's the byte for marking padding
if (ret.at(marker_index) == '\x80')
{
ret.truncate(marker_index);
}
break;
}
default:
//do nothing
break;
}
return ret;
}
/*
* End Static function declarations
* */
/*
* Local Functions
* */
namespace {
quint8 xTime(quint8 x)
{
return ((x<<1) ^ (((x>>7) & 1) * 0x1b));
}
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))))));
}
}
/*
* Constructor
* End Local functions
* */
QAESEncryption::QAESEncryption(QAESEncryption::AES level, QAESEncryption::MODE mode) : m_nb(4), m_blocklen(16), m_level(level), m_mode(mode)
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)
{
@ -63,32 +141,93 @@ QAESEncryption::QAESEncryption(QAESEncryption::AES level, QAESEncryption::MODE m
m_keyLen = aes.keylen;
m_nr = aes.nr;
m_expandedKey = aes.expandedKey;
qDebug() << "Defaulting to AES128";
}
break;
}
}
QByteArray QAESEncryption::expandKey(const QByteArray key)
QByteArray QAESEncryption::getPadding(int currSize, int alignment)
{
int size = (alignment - currSize % alignment) % alignment;
switch(m_padding)
{
case Padding::ZERO:
return QByteArray(size, 0x00);
break;
case Padding::PKCS7:
if (size == 0)
size = alignment;
return QByteArray(size, size);
break;
case Padding::ISO:
if (size > 0)
return QByteArray (size - 1, 0x00).prepend('\x80');
break;
default:
return QByteArray(size, 0x00);
break;
}
return QByteArray();
}
QByteArray QAESEncryption::expandKey(const QByteArray &key)
{
#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());
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
{
int i, k;
quint8 tempa[4]; // Used for the column/row operations
QByteArray roundKey(key);
// The first round key is the key itself.
// ...
QByteArray roundKey(key); // The first round key is the key itself.
// 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);
}
if (i % m_nk == 0)
{
@ -96,53 +235,45 @@ QByteArray QAESEncryption::expandKey(const QByteArray key)
// [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
// Function RotWord()
{
k = tempa[0];
tempa[0] = tempa[1];
tempa[1] = tempa[2];
tempa[2] = tempa[3];
tempa[3] = k;
}
// SubWord() is a function that takes a four-byte input word and
// applies the S-box to each of the four bytes to produce an output word.
// 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;
}
}
// 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) {
for(int i=0; i < 16; ++i)
it[i] = (quint8) it[i] ^ (quint8) expKey.at(round * m_nb * 4 + (i/4) * m_nb + (i%4));
}
}
// The SubBytes Function Substitutes the values in the
@ -193,8 +324,7 @@ void QAESEncryption::mixColumns()
QByteArray::iterator it = m_state->begin();
quint8 tmp, tm, t;
for(int i = 0; i < 16; i += 4)
{
for(int i = 0; i < 16; i += 4){
t = (quint8)it[i];
tmp = (quint8)it[i] ^ (quint8)it[i+1] ^ (quint8)it[i+2] ^ (quint8)it[i+3] ;
@ -219,8 +349,7 @@ void QAESEncryption::invMixColumns()
{
QByteArray::iterator it = m_state->begin();
quint8 a,b,c,d;
for(int i = 0; i < 16; i+=4)
{
for(int i = 0; i < 16; i+=4){
a = (quint8) it[i];
b = (quint8) it[i+1];
c = (quint8) it[i+2];
@ -265,27 +394,28 @@ 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::ivXor(const QByteArray in, const QByteArray iv)
QByteArray QAESEncryption::byteXor(const QByteArray &a, const QByteArray &b)
{
QByteArray::const_iterator it = in.begin();
QByteArray::const_iterator it_iv = iv.begin();
QByteArray::const_iterator it_a = a.begin();
QByteArray::const_iterator it_b = b.begin();
QByteArray ret;
for(int i = 0; i < m_blocklen; i++)
ret.insert(i,it[i] ^ it_iv[i]);
//for(int i = 0; i < m_blocklen; i++)
for(int i = 0; i < std::min(a.size(), b.size()); i++)
ret.insert(i,it_a[i] ^ it_b[i]);
return ret;
}
// Cipher is the main function that encrypts the PlainText.
QByteArray QAESEncryption::cipher(const QByteArray expKey, const QByteArray in)
QByteArray QAESEncryption::cipher(const QByteArray &expKey, const QByteArray &in)
{
//m_state is the input buffer...
@ -298,8 +428,7 @@ QByteArray QAESEncryption::cipher(const QByteArray expKey, const QByteArray in)
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr-1 rounds are executed in the loop below.
for(quint8 round = 1; round < m_nr; ++round)
{
for(quint8 round = 1; round < m_nr; ++round){
subBytes();
shiftRows();
mixColumns();
@ -315,7 +444,7 @@ QByteArray QAESEncryption::cipher(const QByteArray expKey, const QByteArray in)
return output;
}
QByteArray QAESEncryption::invCipher(const QByteArray expKey, const QByteArray in)
QByteArray QAESEncryption::invCipher(const QByteArray &expKey, const QByteArray &in)
{
//m_state is the input buffer.... handle it!
QByteArray output(in);
@ -327,8 +456,7 @@ QByteArray QAESEncryption::invCipher(const QByteArray expKey, const QByteArray i
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr-1 rounds are executed in the loop below.
for(quint8 round=m_nr-1; round>0 ; round--)
{
for(quint8 round=m_nr-1; round>0 ; round--){
invShiftRows();
invSubBytes();
addRoundKey(round, expKey);
@ -344,52 +472,155 @@ QByteArray QAESEncryption::invCipher(const QByteArray expKey, const QByteArray i
return output;
}
QByteArray QAESEncryption::encode(const QByteArray rawText, const QByteArray key, const QByteArray iv)
QByteArray QAESEncryption::printArray(uchar* arr, int size)
{
if (m_mode == CBC && (iv.isNull() || iv.size() != m_blocklen))
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.isEmpty() || iv.size() != m_blocklen))
return QByteArray();
QByteArray ret;
QByteArray expandedKey = expandKey(key);
QByteArray alignedText(rawText);
QByteArray ivTemp(iv);
//Fill array with padding
alignedText.append(getPadding(rawText.size(), m_blocklen));
alignedText.append(getPadding(rawText.size(), m_blocklen), 0); //filling the array with zeros
for(int i=0; i < alignedText.size(); i+= m_blocklen)
switch(m_mode)
{
if (m_mode == CBC)
alignedText.replace(i, m_blocklen, ivXor(alignedText.mid(i, m_blocklen),ivTemp));
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)));
return ret;
}
break;
case CBC: {
#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());
}
#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)));
if (m_mode == CBC)
ivTemp = ret.mid(i, m_blocklen);
}
return ret;
}
break;
case CFB: {
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))));
}
return ret;
}
break;
case OFB: {
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)));
}
ret.append(byteXor(alignedText, ofbTemp));
return ret;
}
break;
default: break;
}
return QByteArray();
}
QByteArray QAESEncryption::decode(const QByteArray rawText, const QByteArray key, const QByteArray iv)
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;
QByteArray expandedKey = expandKey(key);
QByteArray alignedText(rawText);
QByteArray ivTemp(iv);
for(int i=0; i < alignedText.size(); i+= m_blocklen)
switch(m_mode)
{
ret.append(invCipher(expandedKey, alignedText.mid(i, m_blocklen)));
if (m_mode == CBC) {
ret.replace(i, m_blocklen, ivXor(ret.mid(i, m_blocklen),ivTemp));
ivTemp = alignedText.mid(i, m_blocklen);
case ECB:
for(int i=0; i < rawText.size(); i+= m_blocklen)
ret.append(invCipher(expandedKey, rawText.mid(i, m_blocklen)));
break;
case CBC: {
QByteArray ivTemp(iv);
for(int i=0; i < rawText.size(); i+= m_blocklen){
ret.append(invCipher(expandedKey, rawText.mid(i, m_blocklen)));
ret.replace(i, m_blocklen, byteXor(ret.mid(i, m_blocklen),ivTemp));
ivTemp = rawText.mid(i, m_blocklen);
}
}
break;
case CFB: {
ret.append(byteXor(rawText.mid(0, m_blocklen), cipher(expandedKey, iv)));
for(int i=0; i < rawText.size(); i+= m_blocklen){
if (i+m_blocklen < rawText.size()) {
ret.append(byteXor(rawText.mid(i+m_blocklen, m_blocklen),
cipher(expandedKey, rawText.mid(i, m_blocklen))));
}
}
}
break;
case OFB: {
QByteArray ofbTemp;
ofbTemp.append(cipher(expandedKey, iv));
for (int i=m_blocklen; i < rawText.size(); i += m_blocklen){
ofbTemp.append(cipher(expandedKey, ofbTemp.right(m_blocklen)));
}
ret.append(byteXor(rawText, ofbTemp));
}
break;
default:
//do nothing
break;
}
return ret;
}
QByteArray QAESEncryption::removePadding(const QByteArray &rawText)
{
return RemovePadding(rawText, (Padding) m_padding);
}

View File

@ -4,77 +4,104 @@
#include <QObject>
#include <QByteArray>
class QAESEncryption : public QObject
#ifdef __linux__
#ifndef __LP64__
#define do_rdtsc _do_rdtsc
#endif
#endif
#include "./../qtsecret_global.h"
class Qt_SECRETSHARED_EXPORT QAESEncryption : public QObject
{
Q_OBJECT
public:
typedef enum {
enum Aes {
AES_128,
AES_192,
AES_256
} AES;
};
typedef enum {
enum Mode {
ECB,
CBC
} MODE;
CBC,
CFB,
OFB
};
static QByteArray Crypt(QAESEncryption::AES level, QAESEncryption::MODE mode, const QByteArray rawText, const QByteArray key, const QByteArray iv = NULL);
static QByteArray Decrypt(QAESEncryption::AES level, QAESEncryption::MODE mode, const QByteArray rawText, const QByteArray key, const QByteArray iv = NULL);
static QByteArray ExpandKey(QAESEncryption::AES level, QAESEncryption::MODE mode, const QByteArray key);
enum Padding {
ZERO,
PKCS7,
ISO
};
QAESEncryption(QAESEncryption::AES level, QAESEncryption::MODE mode);
static QByteArray Crypt(QAESEncryption::Aes level, QAESEncryption::Mode mode, const QByteArray &rawText, const QByteArray &key,
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 = 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 = 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 expandKey(const QByteArray key);
QAESEncryption(QAESEncryption::Aes level, QAESEncryption::Mode mode,
QAESEncryption::Padding padding = QAESEncryption::ISO);
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:
private:
//typedef uint8_t state[4][4];
int m_nb, m_blocklen, m_level, m_mode;
int m_nb;
int m_blocklen;
int m_level;
int m_mode;
int m_nk;
int m_keyLen;
int m_nr;
int m_expandedKey;
int m_padding;
bool m_aesNIAvailable;
QByteArray* m_state;
int m_nk, m_keyLen, m_nr, m_expandedKey;
typedef struct{
struct AES256{
int nk = 8;
int keylen = 32;
int nr = 14;
int expandedKey = 240;
} AES256;
};
typedef struct{
struct AES192{
int nk = 6;
int keylen = 24;
int nr = 12;
int expandedKey = 209;
} AES192;
};
typedef struct{
struct AES128{
int nk = 4;
int keylen = 16;
int nr = 10;
int expandedKey = 176;
} AES128;
};
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();
void invMixColumns();
void invSubBytes();
void invShiftRows();
QByteArray cipher(const QByteArray expKey, const QByteArray plainText);
QByteArray invCipher(const QByteArray expKey, const QByteArray plainText);
QByteArray ivXor(const QByteArray in, const QByteArray iv);
QByteArray getPadding(int currSize, int alignment);
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
@ -95,8 +122,8 @@ private:
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
const quint8 rsbox[256] =
{ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
const quint8 rsbox[256] = {
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
@ -115,35 +142,9 @@ private:
// The round constant word array, Rcon[i], contains the values given by
// x to th e power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
const quint8 Rcon[256] = {
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d };
inline 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) * xTime(xTime(xTime(x)))) ^ ((y>>4 & 1) * xTime(xTime(xTime(xTime(x))))));
}
inline int getPadding(int currSize, int alignment) {
return (alignment - currSize % alignment) % alignment;
}
// Only the first 14 elements are needed
const quint8 Rcon[14] = {
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab};
};
#endif // QAESENCRYPTION_H

View File

@ -9,16 +9,21 @@ 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 {
DESTDIR="$$PWD/build/debug"
}
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
@ -26,5 +31,19 @@ DEFINES += QT_DEPRECATED_WARNINGS
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
RESOURCES += \
res.qrc
include(ccache.pri)

12
qtsecret_global.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef QTSECRET_GLOBAL_H
#define QTSECRET_GLOBAL_H
#include <QtCore/qglobal.h>
#if defined(Qt_SECRET_LIBRARY)
# define Qt_SECRETSHARED_EXPORT Q_DECL_EXPORT
#else
# define Qt_SECRETSHARED_EXPORT Q_DECL_IMPORT
#endif
#endif // QTSECRET_GLOBAL_H

5
res.qrc Normal file
View File

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
<file>unit_test/longText.txt</file>
</qresource>
</RCC>

View File

@ -1,11 +1,16 @@
#include "aestest.h"
#include <QDebug>
#include <QByteArray>
#include <QCryptographicHash>
#include <QFile>
#include "qaesencryption.h"
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]);
@ -28,12 +33,14 @@ void AesTest::initTestCase()
quint8 out_text[16] = { 0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, 0xa8, 0x9e, 0xca, 0xf3, 0x24, 0x66, 0xef, 0x97 };
quint8 out_text_2[16] = { 0xbd, 0x33, 0x4f, 0x1d, 0x6e, 0x45, 0xf2, 0x5f, 0xf7, 0x12, 0xa2, 0x14, 0x57, 0x1f, 0xa5, 0xcc };
quint8 out_text_3[16] = { 0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8 };
quint8 out_text_4[16] = { 0x3b, 0x3f, 0xd9, 0x2e, 0xb7, 0x2d, 0xad, 0x20, 0x33, 0x34, 0x49, 0xf8, 0xe8, 0x3c, 0xfb, 0x4a };
for (int i=0; i<16; i++){
in.append(in_text[i]);
outECB128.append(out_text[i]);
outECB192.append(out_text_2[i]);
outECB256.append(out_text_3[i]);
outOFB128.append(out_text_4[i]);
}
quint8 text_cbc[64] = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
@ -50,6 +57,7 @@ void AesTest::initTestCase()
inCBC128.append(text_cbc[i]);
outCBC128.append(output_cbc[i]);
}
}
@ -59,13 +67,11 @@ void AesTest::ECB128Crypt()
{
QByteArray hexText, outputHex;
QAESEncryption encryption(QAESEncryption::AES_128, QAESEncryption::ECB);
QCOMPARE(encryption.encode(in, key16), outECB128);
}
void AesTest::ECB128Decrypt()
{
QByteArray hexText, outputHex;
QAESEncryption encryption(QAESEncryption::AES_128, QAESEncryption::ECB);
QCOMPARE(encryption.decode(outECB128, key16), in);
@ -103,7 +109,7 @@ void AesTest::ECB256Decrypt()
void AesTest::ECB256String()
{
QAESEncryption encryption(QAESEncryption::AES_256, QAESEncryption::ECB);
QAESEncryption encryption(QAESEncryption::AES_256, QAESEncryption::ECB, QAESEncryption::Padding::ISO);
QString inputStr("The Advanced Encryption Standard (AES), also known by its original name Rijndael "
"is a specification for the encryption of electronic data established by the U.S. "
@ -113,12 +119,13 @@ void AesTest::ECB256String()
QByteArray hashKey = QCryptographicHash::hash(key.toLocal8Bit(), QCryptographicHash::Sha256);
QByteArray encodeText = encryption.encode(inputStr.toLocal8Bit(), hashKey);
QByteArray decodedText = encryption.removePadding(encryption.decode(encodeText, hashKey));
QCOMPARE(QString(encryption.decode(encodeText, hashKey)), inputStr);
QCOMPARE(QString(decodedText), inputStr);
}
//==================CBC TESTING=========================
////==================CBC TESTING=========================
void AesTest::CBC128Crypt()
{
@ -133,3 +140,98 @@ void AesTest::CBC128Decrypt()
QCOMPARE(encryption.decode(outCBC128, key16, iv), inCBC128);
}
//=================== CFB TESTING ============================
void AesTest::CFB256String()
{
QAESEncryption encryption(QAESEncryption::AES_256, QAESEncryption::CFB, QAESEncryption::PKCS7);
QString inputStr("The Advanced Encryption Standard (AES), also known by its original name Rijndael "
"is a specification for the encryption of electronic data established by the U.S. "
"National Institute of Standards and Technology (NIST) in 2001");
QString key("123456789123");
QByteArray hashKey = QCryptographicHash::hash(key.toLocal8Bit(), QCryptographicHash::Sha256);
QByteArray encodeText = encryption.encode(inputStr.toLocal8Bit(), hashKey, iv);
QByteArray decodedText = encryption.removePadding(encryption.decode(encodeText, hashKey, iv));
QCOMPARE(QString(decodedText), inputStr);
}
void AesTest::CFB256LongText()
{
QAESEncryption encryption(QAESEncryption::AES_256, QAESEncryption::CFB);
QFile textFile(":/unit_test/longText.txt");
QByteArray input;
if (textFile.open(QFile::ReadOnly))
input = textFile.readAll();
else
QFAIL("File longText.txt not found!");
QString key("123456789123");
QByteArray hashKey = QCryptographicHash::hash(key.toLocal8Bit(), QCryptographicHash::Sha256);
QByteArray encodeText = encryption.encode(input, hashKey, iv);
QByteArray decodedText = encryption.removePadding(encryption.decode(encodeText, hashKey, iv));
QCOMPARE(decodedText, input);
}
void AesTest::OFB128Crypt()
{
QAESEncryption encryption(QAESEncryption::AES_128, QAESEncryption::OFB);
QCOMPARE(encryption.encode(in, key16, iv), outOFB128);
}
void AesTest::OFB256String()
{
QAESEncryption encryption(QAESEncryption::AES_128, QAESEncryption::OFB, QAESEncryption::PKCS7);
QString inputStr("The Advanced Encryption Standard (AES), also known by its original name Rijndael "
"is a specification for the encryption of electronic data established by the U.S. "
"National Institute of Standards and Technology (NIST) in 2001");
QString key("123456789123");
QByteArray hashKey = QCryptographicHash::hash(key.toLocal8Bit(), QCryptographicHash::Sha256);
QByteArray encodeText = encryption.encode(inputStr.toLocal8Bit(), hashKey, iv);
QByteArray decodedText = encryption.removePadding(encryption.decode(encodeText, hashKey, iv));
QCOMPARE(inputStr, QString(decodedText));
}
void AesTest::CBC256StringEvenISO()
{
QAESEncryption encryption(QAESEncryption::AES_256, QAESEncryption::CBC);
//16 byte string
QString inputStr("1234567890123456");
QString key("123456789123");
QByteArray hashKey = QCryptographicHash::hash(key.toLocal8Bit(), QCryptographicHash::Sha256);
QByteArray encodeText = encryption.encode(inputStr.toLocal8Bit(), hashKey, iv);
QByteArray decodeText = encryption.decode(encodeText, hashKey, iv);
QString decodedString = QString(encryption.removePadding(decodeText));
QCOMPARE(QString(decodeText), decodedString);
}
void AesTest::CBC256StringEvenPKCS7()
{
QAESEncryption encryption(QAESEncryption::AES_256, QAESEncryption::CBC, QAESEncryption::PKCS7);
//16 byte string
QString inputStr("1234567890123456");
int blockLen = 16;
QString key("123456789123");
QByteArray hashKey = QCryptographicHash::hash(key.toLocal8Bit(), QCryptographicHash::Sha256);
QByteArray encodeText = encryption.encode(inputStr.toLocal8Bit(), hashKey, iv);
QByteArray decodeText = encryption.decode(encodeText, hashKey, iv);
QByteArray padding = decodeText.remove(0, encryption.removePadding(decodeText).length());
QCOMPARE(padding.size(), blockLen);
}

View File

@ -25,13 +25,30 @@ private slots:
void CBC128Crypt();
void CBC128Decrypt();
void CFB256String();
void CFB256LongText();
void OFB128Crypt();
void OFB256String();
void CBC256StringEvenISO();
void CBC256StringEvenPKCS7();
void cleanupTestCase(){}
private:
QByteArray key16, key24, key32;
QByteArray key16;
QByteArray key24;
QByteArray key32;
QByteArray iv;
QByteArray in, outECB128, outECB192, outECB256;
QByteArray inCBC128, outCBC128;
QByteArray in;
QByteArray outECB128;
QByteArray outECB192;
QByteArray outECB256;
QByteArray inCBC128;
QByteArray outCBC128;
QByteArray outOFB128;
};
#endif // AESTEST_H

665
unit_test/longText.txt Normal file
View File

@ -0,0 +1,665 @@
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
rFNmuB2892Bb53Y3aIkONNC2Sh1uUua9JxUDjThJSl1eKWX2VTeXjJrS+xrMK5dnP6CK3reuFocI
nVlTGucuHzQJPVylSFjIgDJLp2SbhMkDuTNpklMXIBxZJZcklXIS5LCKQIpKtsk2NyayQyJLkCuS
ylyZ0+5LSzyaxdScurkyZ1k6kkn2DiScqp8litMincmUfJanlySBJDfJE//EAC4QAAICAgEDAwQB
BAMBAQAAAAABAhEDEiEEEBMgIjEUMDJBBSMzQEIVNFBEgP/aAAgBAQABBQL/APUNf+tXejXukamp
qaigKNE/kfAnZkySWT62acOtgyM4T/8AMoUSho0FEkjQUDUrtqa9mZMkmYYPKZNavFeTHk8mfFT5
TjOUXg6y/wDHr/ErvXevs5eU8S1hDQkrXgh05LH1OchHqMUeoxqEu3RZr9FFFd9Ga0UV6qKKOPs6
mpRTNWaM0ZozQ0NSiyyzY2NmbM2Zsy2bG5ubGw/y7M6lSlCXXMn1eSZ1C0x9oS0lH3RortVjVGol
6KKNTQ1RwV2oo17UUUaigartRRX2aKKNSjUo1ZqzU1Gu1WRjzL8+2WekY9VvkyrHMThiMno6GW+C
mc9kxyNjY2ZbOTntZsWWX6qK7WWWWbFlllmxZfoor7qJfJKSSy52RdPIsmSWsqeI8EiWKce38Yv6
IzU0NDQ0NTU1K7UUUV34+xwWi/s3/hTZPNb0nOM/psJLrUiXU5Zjb7qc0b2dD1ePEoyUl6LLLLLL
7X6OSiiimUyjU1NTQ0NTU1NTU1NTU1NTUr7+TIscJZ8WUl1eGBm63NMdsoooo4OCk1/GYVknFKC9
NFFFFf8Alc9v5Ny8GHWMck9RzkzZlnJbLKRwM/i8tL/Bv/Gv7zfH1EpkOjxzOu6R4EM+CzTgss/X
8fx1H+JZZZZZf+Y+Vzghgfs6vF5sHji1KLR8ijbkiUeVE4qMbP47++WiyzaJaNl6bLRsjdd79G8R
5IHmieWJ5IjzRPNE8sR5Uec8shzZ5GeVnmZ5kec855yyyyyyyyyyyyyzYljjkIe3tPocM31OCWOf
iOh6NZX1H8fOCo1QooR0eRwyvJN9qkOxd9meWZ5ZjySZbL77SNpnuLmbZDllGpqao9v2q78Fm6N0
bo3N0bm5ubm5ubm7NmLJUU1aY5UdTm4/J4cn0+DN/IRlGU43ZEbOlxpQq/Xx3ooopd7G79Nll9qR
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
UjQ1aWFhEJeiJeFywdwdhbFsDU6iILDNuC1wbiE/QnuG3pjehhd0hNR4gHGmNuQMWwmkxiEFGqMu
ixIWVE4WEh3R7SY3NlqqwlNmu0JTo29jij2JP2Pej+CeCxkklEMKSMaaceBIhhlyFWiJaYmJzg/D
ZRLcD9GQlEIFGmNMOtCNvgiDIqL8I8LDlK6YWNl8FMc1ICkHBYfoFHaJpRKnDpJwY40UbR8BtNj+
GX78EIuH4oQhqMon9N0zqWCbuJETQ0NooqUXCoaT6QniuFKP/B7RBtSEHgUE+0LSITYyQWyQmxDw
9/IuZeGP/AWfOfs4PYZ0PHIuY//EACgQAQACAgICAgICAwEBAQAAAAEAESExQVFhcRCBkbGhwdHh
8PEgMP/aAAgBAQABPxCpUqJKlSpUqVKlXKlSpUqVKlSuZWJUqVKlSpUqVKlSpUqVKldypUqVKh8V
KlYlSpVzcqVKZUqVKlSpUqVK+FSpUqVKlSsSsSpUqVK6lSpUqVKlcysypUqVKgSpUqVKlSpUqVzK
lcSuIEqVKlSoEqVKlSqgSpUqVKlYmJUruUSpUqVKlSpUqVKlSpUqV8KlSupUqVMJUqVKlSpUqVKl
fFSpUqV8V1KlSpUqVKlVKlSpWK+KiSrlSpUqVKzK5iSvipUCVKlSpUqV3KldypUr4VKlSokqVKlE
qBKlSpUqVKiSpUqUSpXUSVK+AlQJUrHxVSpUqZfFROJUqV8VKlSpRKlSpUqVKlSpUqVKlSupUr4V
iVKlcSpUqVKlSpUqVKlSpVypUqVAlSpTKlSpUqVKlSpUqVKlSpWJUqVK+KlMqVKlSpUqVKlSpVSp
UqVKlSpUqVKlSpXEqVKlSpUqVKlSpUqV8VKlSmVKlSpUqVAlSpTKZUqVKudJUqUyuZXyVKifAIkL
S8t/8gIYIo3K/wDgVKlSpUqVK+FfCpUqBK+FRipUqVKohDGEqVKlcSvhUqVKlSpUqVKlSpUYq4JL
CCSoiomQT4CiOICskc9TJLGqm0Jggj1MsUpcqLNyvhidNcTTrc2KbHwwOqaIXXiVwy7MkqNPDmVK
lSpUqVKlSpUqVE4lcSu5UqVKlSpUqVKlSpUqVKlSpUqVKlSqlSpRFxUXD2QsDGobdQGllhIjcA1M
2cISxmLygYlW6B9QRxfPMa6LgbfcQ3OCziVHmtLUSuYW4L8XLr04sV9/AAUJcqoGBrgGvuFAch+F
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