/** * Copyright 2021 BrutalWizard (https://github.com/bru74lw1z4rd). All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution **/ #include "include/QBlockCipher.h" QSimpleCrypto::QBlockCipher::QBlockCipher() { } /// /// \brief QSimpleCrypto::QBlockCipher::generateRandomBytes - Function generates random bytes by size. /// \param size - Size of generated bytes. /// \return Returns random bytes. /// QByteArray QSimpleCrypto::QBlockCipher::generateRandomBytes(const int& size) { unsigned char arr[sizeof(size)]; RAND_bytes(arr, sizeof(size)); QByteArray buffer = QByteArray(reinterpret_cast(arr), size); return buffer; } QByteArray QSimpleCrypto::QBlockCipher::generateSecureRandomBytes(const int &size) { unsigned char arr[sizeof(size)]; RAND_priv_bytes(arr, sizeof(size)); QByteArray buffer = QByteArray(reinterpret_cast(arr), size); return buffer; } /// /// \brief QSimpleCrypto::QBlockCipher::encryptAesBlockCipher - Function encrypts data with Aes Block Cipher algorithm. /// \param data - Data that will be encrypted. /// \param key - AES key. /// \param iv - Initialization vector. /// \param password - Encryption password. /// \param salt - Random delta. /// \param rounds - Transformation rounds. /// \param chiper - Can be used with OpenSSL EVP_CIPHER (ecb, cbc, cfb, ofb, ctr) - 128, 192, 256. Example: EVP_aes_256_cbc(). /// \param md - Hash algroitm (OpenSSL EVP_MD). Example: EVP_sha512(). /// \return Returns decrypted data or "", if error happened. /// QByteArray QSimpleCrypto::QBlockCipher::encryptAesBlockCipher(QByteArray data, QByteArray key, QByteArray iv, const int& rounds, const EVP_CIPHER* cipher, const EVP_MD* md) { try { /* Initialize EVP_CIPHER_CTX */ std::unique_ptr encryptionCipher { EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free }; if (encryptionCipher == nullptr) { throw std::runtime_error("Couldn't initialize \'encryptionCipher\'. EVP_CIPHER_CTX_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); } /* Reinterpret values for multi use */ unsigned char* m_key = reinterpret_cast(key.data()); unsigned char* m_iv = reinterpret_cast(iv.data()); /* Set data length */ int cipherTextLength(data.size() + AES_BLOCK_SIZE); int finalLength = 0; /* Initialize cipcherText. Here encrypted data will be stored */ std::unique_ptr cipherText { new unsigned char[cipherTextLength]() }; if (cipherText == nullptr) { throw std::runtime_error("Couldn't allocate memory for 'cipherText'."); } // Bug here // /* Start encryption with password based encryption routine */ // if (!EVP_BytesToKey(cipher, md, reinterpret_cast(salt.data()), reinterpret_cast(password.data()), password.length(), rounds, m_key, m_iv)) { // throw std::runtime_error("Couldn't start encryption routine. EVP_BytesToKey(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); // } /* Initialize encryption operation. */ if (!EVP_EncryptInit_ex(encryptionCipher.get(), cipher, nullptr, m_key, m_iv)) { throw std::runtime_error("Couldn't initialize encryption operation. EVP_EncryptInit_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); } /* * Provide the message to be encrypted, and obtain the encrypted output. * EVP_EncryptUpdate can be called multiple times if necessary */ if (!EVP_EncryptUpdate(encryptionCipher.get(), cipherText.get(), &cipherTextLength, reinterpret_cast(data.data()), data.size())) { throw std::runtime_error("Couldn't provide message to be encrypted. EVP_EncryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); } /* Finalize the encryption. Normally ciphertext bytes may be written at this stage */ if (!EVP_EncryptFinal(encryptionCipher.get(), cipherText.get() + cipherTextLength, &finalLength)) { throw std::runtime_error("Couldn't finalize encryption. EVP_EncryptFinal(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); } /* Finilize data to be readable with qt */ QByteArray encryptedData = QByteArray(reinterpret_cast(cipherText.get()), cipherTextLength + finalLength); return encryptedData; } catch (std::exception& exception) { QSimpleCrypto::QBlockCipher::error.setError(1, exception.what()); return QByteArray(); } catch (...) { QSimpleCrypto::QBlockCipher::error.setError(2, "Unknown error!"); return QByteArray(); } return QByteArray(); } /// /// \brief QSimpleCrypto::QBlockCipher::encryptAesBlockCipher - Function decrypts data with Aes Block Cipher algorithm. /// \param data - Data that will be decrypted. /// \param key - AES key. /// \param iv - Initialization vector. /// \param password - Decryption password. /// \param salt - Random delta. /// \param rounds - Transformation rounds. /// \param chiper - Can be used with OpenSSL EVP_CIPHER (ecb, cbc, cfb, ofb, ctr) - 128, 192, 256. Example: EVP_aes_256_cbc(). /// \param md - Hash algroitm (OpenSSL EVP_MD). Example: EVP_sha512(). /// \return Returns decrypted data or "", if error happened. /// QByteArray QSimpleCrypto::QBlockCipher::decryptAesBlockCipher(QByteArray data, QByteArray key, QByteArray iv, const int& rounds, const EVP_CIPHER* cipher, const EVP_MD* md) { try { /* Initialize EVP_CIPHER_CTX */ std::unique_ptr decryptionCipher { EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free }; if (decryptionCipher == nullptr) { throw std::runtime_error("Couldn't initialize \'decryptionCipher\'. EVP_CIPHER_CTX_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); } /* Reinterpret values for multi use */ unsigned char* m_key = reinterpret_cast(key.data()); unsigned char* m_iv = reinterpret_cast(iv.data()); /* Set data length */ int plainTextLength(data.size()); int finalLength = 0; /* Initialize plainText. Here decrypted data will be stored */ std::unique_ptr plainText { new unsigned char[plainTextLength + AES_BLOCK_SIZE]() }; if (plainText == nullptr) { throw std::runtime_error("Couldn't allocate memory for \'plainText\'. EVP_CIPHER_CTX_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); } // Bug here // /* Start encryption with password based encryption routine */ // if (!EVP_BytesToKey(cipher, md, reinterpret_cast(salt.data()), reinterpret_cast(password.data()), password.length(), rounds, m_key, m_iv)) { // throw std::runtime_error("Couldn't start decryption routine. EVP_BytesToKey(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); // } /* Initialize decryption operation. */ if (!EVP_DecryptInit_ex(decryptionCipher.get(), cipher, nullptr, m_key, m_iv)) { throw std::runtime_error("Couldn't initialize decryption operation. EVP_DecryptInit_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); } /* * Provide the message to be decrypted, and obtain the plaintext output. * EVP_DecryptUpdate can be called multiple times if necessary */ if (!EVP_DecryptUpdate(decryptionCipher.get(), plainText.get(), &plainTextLength, reinterpret_cast(data.data()), data.size())) { throw std::runtime_error("Couldn't provide message to be decrypted. EVP_DecryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); } /* * Finalize the decryption. A positive return value indicates success, * anything else is a failure - the plaintext is not trustworthy. */ if (!EVP_DecryptFinal(decryptionCipher.get(), plainText.get() + plainTextLength, &finalLength)) { throw std::runtime_error("Couldn't finalize decryption. EVP_DecryptFinal. Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr))); } /* Finilize data to be readable with qt */ QByteArray decryptedData = QByteArray(reinterpret_cast(plainText.get()), plainTextLength + finalLength); return decryptedData; } catch (std::exception& exception) { QSimpleCrypto::QBlockCipher::error.setError(1, exception.what()); return QByteArray(exception.what()); } catch (...) { QSimpleCrypto::QBlockCipher::error.setError(2, "Unknown error!"); return QByteArray(); } return QByteArray(); }