diff options
author | Saturneric <[email protected]> | 2022-01-03 17:25:59 +0000 |
---|---|---|
committer | Saturneric <[email protected]> | 2022-01-03 17:25:59 +0000 |
commit | c20c7b9bb81a09d54b288517092fe356bb808de4 (patch) | |
tree | 625db0ac06fdf95d5e886ab558e9d240f47730d1 /src/ui/aes/qaesencryption.cpp | |
parent | <fixed, refactor>(ui): Enhanced sending email function. (diff) | |
download | GpgFrontend-c20c7b9bb81a09d54b288517092fe356bb808de4.tar.gz GpgFrontend-c20c7b9bb81a09d54b288517092fe356bb808de4.zip |
<feature, refactor>(core, ui): Key package generate.
1. refactor GpgKeyImportExporter to meet with key package generation.
2. add ExportKeyPackageDialog.
3. add Qt AES ability.
4. refactor Key List to provide menu ability control.
5. add check all and uncheck all ability to key list menu.
Diffstat (limited to 'src/ui/aes/qaesencryption.cpp')
-rw-r--r-- | src/ui/aes/qaesencryption.cpp | 626 |
1 files changed, 626 insertions, 0 deletions
diff --git a/src/ui/aes/qaesencryption.cpp b/src/ui/aes/qaesencryption.cpp new file mode 100644 index 00000000..9103fb8e --- /dev/null +++ b/src/ui/aes/qaesencryption.cpp @@ -0,0 +1,626 @@ +#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, QAESEncryption::Padding padding) +{ + 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, QAESEncryption::Padding padding) +{ + return QAESEncryption(level, mode, padding).decode(rawText, key, iv); +} + +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)))))); +} + +} + +/* + * 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) +{ +#ifdef USE_INTEL_AES_IF_AVAILABLE + m_aesNIAvailable = check_aesni_support(); +#endif + + switch (level) + { + case AES_128: { + AES128 aes; + m_nk = aes.nk; + m_keyLen = aes.keylen; + m_nr = aes.nr; + m_expandedKey = aes.expandedKey; + } + break; + case AES_192: { + AES192 aes; + m_nk = aes.nk; + m_keyLen = aes.keylen; + m_nr = aes.nr; + m_expandedKey = aes.expandedKey; + } + break; + case AES_256: { + AES256 aes; + m_nk = aes.nk; + m_keyLen = aes.keylen; + m_nr = aes.nr; + m_expandedKey = aes.expandedKey; + } + break; + default: { + AES128 aes; + m_nk = aes.nk; + m_keyLen = aes.keylen; + m_nr = aes.nr; + m_expandedKey = aes.expandedKey; + } + break; + } + +} +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. + + // 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) + { + // This function shifts the 4 bytes in a word to the left once. + // [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; + + // 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) +{ + QByteArray::iterator it = m_state->begin(); + 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 +// state matrix with values in an S-box. +void QAESEncryption::subBytes() +{ + QByteArray::iterator it = m_state->begin(); + for(int i = 0; i < 16; i++) + it[i] = getSBoxValue((quint8) it[i]); +} + +// The ShiftRows() function shifts the rows in the state to the left. +// Each row is shifted with different offset. +// Offset = Row number. So the first row is not shifted. +void QAESEncryption::shiftRows() +{ + QByteArray::iterator it = m_state->begin(); + quint8 temp; + //Keep in mind that QByteArray is column-driven!! + + //Shift 1 to left + temp = (quint8)it[1]; + it[1] = (quint8)it[5]; + it[5] = (quint8)it[9]; + it[9] = (quint8)it[13]; + it[13] = (quint8)temp; + + //Shift 2 to left + temp = (quint8)it[2]; + it[2] = (quint8)it[10]; + it[10] = (quint8)temp; + temp = (quint8)it[6]; + it[6] = (quint8)it[14]; + it[14] = (quint8)temp; + + //Shift 3 to left + temp = (quint8)it[3]; + it[3] = (quint8)it[15]; + it[15] = (quint8)it[11]; + it[11] = (quint8)it[7]; + it[7] = (quint8)temp; +} + +// MixColumns function mixes the columns of the state matrix +//optimized!! +void QAESEncryption::mixColumns() +{ + QByteArray::iterator it = m_state->begin(); + quint8 tmp, tm, t; + + 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] ; + + tm = xTime( (quint8)it[i] ^ (quint8)it[i+1] ); + it[i] = (quint8)it[i] ^ (quint8)tm ^ (quint8)tmp; + + tm = xTime( (quint8)it[i+1] ^ (quint8)it[i+2]); + it[i+1] = (quint8)it[i+1] ^ (quint8)tm ^ (quint8)tmp; + + tm = xTime( (quint8)it[i+2] ^ (quint8)it[i+3]); + it[i+2] =(quint8)it[i+2] ^ (quint8)tm ^ (quint8)tmp; + + tm = xTime((quint8)it[i+3] ^ (quint8)t); + it[i+3] =(quint8)it[i+3] ^ (quint8)tm ^ (quint8)tmp; + } +} + +// MixColumns function mixes the columns of the state matrix. +// The method used to multiply may be difficult to understand for the inexperienced. +// Please use the references to gain more information. +void QAESEncryption::invMixColumns() +{ + QByteArray::iterator it = m_state->begin(); + quint8 a,b,c,d; + for(int i = 0; i < 16; i+=4){ + a = (quint8) it[i]; + b = (quint8) it[i+1]; + c = (quint8) it[i+2]; + d = (quint8) it[i+3]; + + it[i] = (quint8) (multiply(a, 0x0e) ^ multiply(b, 0x0b) ^ multiply(c, 0x0d) ^ multiply(d, 0x09)); + it[i+1] = (quint8) (multiply(a, 0x09) ^ multiply(b, 0x0e) ^ multiply(c, 0x0b) ^ multiply(d, 0x0d)); + it[i+2] = (quint8) (multiply(a, 0x0d) ^ multiply(b, 0x09) ^ multiply(c, 0x0e) ^ multiply(d, 0x0b)); + it[i+3] = (quint8) (multiply(a, 0x0b) ^ multiply(b, 0x0d) ^ multiply(c, 0x09) ^ multiply(d, 0x0e)); + } +} + +// The SubBytes Function Substitutes the values in the +// state matrix with values in an S-box. +void QAESEncryption::invSubBytes() +{ + QByteArray::iterator it = m_state->begin(); + for(int i = 0; i < 16; ++i) + it[i] = getSBoxInvert((quint8) it[i]); +} + +void QAESEncryption::invShiftRows() +{ + QByteArray::iterator it = m_state->begin(); + uint8_t temp; + + //Keep in mind that QByteArray is column-driven!! + + //Shift 1 to right + temp = (quint8)it[13]; + it[13] = (quint8)it[9]; + it[9] = (quint8)it[5]; + it[5] = (quint8)it[1]; + it[1] = (quint8)temp; + + //Shift 2 + temp = (quint8)it[10]; + it[10] = (quint8)it[2]; + it[2] = (quint8)temp; + temp = (quint8)it[14]; + it[14] = (quint8)it[6]; + it[6] = (quint8)temp; + + //Shift 3 + temp = (quint8)it[7]; + it[7] = (quint8)it[11]; + it[11] = (quint8)it[15]; + it[15] = (quint8)it[3]; + it[3] = (quint8)temp; +} + +QByteArray QAESEncryption::byteXor(const QByteArray &a, const QByteArray &b) +{ + QByteArray::const_iterator it_a = a.begin(); + QByteArray::const_iterator it_b = b.begin(); + QByteArray ret; + + //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) +{ + + //m_state is the input buffer... + QByteArray output(in); + m_state = &output; + + // Add the First round key to the state before starting the rounds. + addRoundKey(0, expKey); + + // 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){ + subBytes(); + shiftRows(); + mixColumns(); + addRoundKey(round, expKey); + } + + // The last round is given below. + // The MixColumns function is not here in the last round. + subBytes(); + shiftRows(); + addRoundKey(m_nr, expKey); + + return output; +} + +QByteArray QAESEncryption::invCipher(const QByteArray &expKey, const QByteArray &in) +{ + //m_state is the input buffer.... handle it! + QByteArray output(in); + m_state = &output; + + // Add the First round key to the state before starting the rounds. + addRoundKey(m_nr, expKey); + + // 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--){ + invShiftRows(); + invSubBytes(); + addRoundKey(round, expKey); + invMixColumns(); + } + + // The last round is given below. + // The MixColumns function is not here in the last round. + invShiftRows(); + invSubBytes(); + addRoundKey(0, expKey); + + 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.isEmpty() || iv.size() != m_blocklen)) + return QByteArray(); + + QByteArray expandedKey = expandKey(key); + QByteArray alignedText(rawText); + + //Fill array with padding + alignedText.append(getPadding(rawText.size(), m_blocklen)); + + switch(m_mode) + { + 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))); + 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) +{ + if (m_mode >= CBC && (iv.isEmpty() || iv.size() != m_blocklen)) + return QByteArray(); + + QByteArray ret; + QByteArray expandedKey = expandKey(key); + + switch(m_mode) + { + 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); +} |