166 lines
No EOL
5.1 KiB
C++
166 lines
No EOL
5.1 KiB
C++
#pragma once
|
|
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <random>
|
|
#include <openssl/evp.h>
|
|
|
|
|
|
namespace drp::util::crypto {
|
|
|
|
|
|
/**
|
|
* Represent an AES key.
|
|
* Allow for encrypting and decrypting data.
|
|
* @tparam keySize the size of the key (in bytes)
|
|
* @tparam ivSize the size of the initialisation vector (in bytes)
|
|
*/
|
|
template<std::size_t keySize, std::size_t ivSize, const EVP_CIPHER*(cipherFunction)()>
|
|
class AesKey {
|
|
public:
|
|
/**
|
|
* Create a random AES key.
|
|
*/
|
|
AesKey() {
|
|
// generate a random key
|
|
for (auto& byte : this->_data)
|
|
byte = randomDistribution(randomGenerator);
|
|
}
|
|
|
|
explicit AesKey(const std::array<std::uint8_t, keySize>& data) {
|
|
this->_data = data;
|
|
}
|
|
|
|
/**
|
|
* Encrypt data with this key.
|
|
* @param plainData the data to encrypt
|
|
* @return the encrypted data. It will always be longer than the original data.
|
|
*/
|
|
[[nodiscard]] std::vector<std::uint8_t> encrypt(const std::vector<std::uint8_t>& plainData) const {
|
|
// create an initialization vector
|
|
std::array<std::uint8_t, ivSize> iv {};
|
|
for (auto& byte : iv)
|
|
byte = randomDistribution(randomGenerator);
|
|
|
|
// create the cipher context
|
|
const auto context = std::unique_ptr<EVP_CIPHER_CTX, decltype(&EVP_CIPHER_CTX_free)>(
|
|
EVP_CIPHER_CTX_new(),
|
|
EVP_CIPHER_CTX_free
|
|
);
|
|
|
|
if (context == nullptr)
|
|
throw std::runtime_error("Error creating EVP_CIPHER_CTX");
|
|
|
|
// initialize the encryptor
|
|
if (EVP_EncryptInit_ex(
|
|
context.get(),
|
|
cipherFunction(),
|
|
nullptr,
|
|
this->_data.data(),
|
|
iv.data()
|
|
) != 1)
|
|
throw std::runtime_error("Error initializing encryption");
|
|
|
|
std::vector<std::uint8_t> encryptedData(ivSize + plainData.size() + EVP_CIPHER_block_size(EVP_aes_256_cbc()));
|
|
std::copy(iv.begin(), iv.end(), encryptedData.begin());
|
|
int length;
|
|
|
|
// encrypt the data
|
|
if (EVP_EncryptUpdate(
|
|
context.get(),
|
|
encryptedData.data() + ivSize,
|
|
&length,
|
|
plainData.data(),
|
|
static_cast<int>(plainData.size())
|
|
) != 1)
|
|
throw std::runtime_error("Error encrypting data");
|
|
int encryptedDataLength = length;
|
|
|
|
// finalize the encryption
|
|
if (EVP_EncryptFinal_ex(
|
|
context.get(),
|
|
encryptedData.data() + ivSize + encryptedDataLength,
|
|
&length
|
|
) != 1)
|
|
throw std::runtime_error("Error finalizing encryption");
|
|
encryptedDataLength += length;
|
|
encryptedData.resize(ivSize + encryptedDataLength);
|
|
|
|
return encryptedData;
|
|
}
|
|
|
|
/**
|
|
* Decrypt data with this key.
|
|
* @param rawEncryptedData the encrypted data to decrypt.
|
|
* @return the decrypted data.
|
|
*/
|
|
[[nodiscard]] std::vector<std::uint8_t> decrypt(const std::vector<std::uint8_t>& rawEncryptedData) const {
|
|
// create a cipher context
|
|
const auto context = std::unique_ptr<EVP_CIPHER_CTX, decltype(&EVP_CIPHER_CTX_free)>(
|
|
EVP_CIPHER_CTX_new(),
|
|
EVP_CIPHER_CTX_free
|
|
);
|
|
|
|
std::array<std::uint8_t, ivSize> iv;
|
|
std::copy(rawEncryptedData.begin(), rawEncryptedData.begin() + ivSize, iv.data());
|
|
const std::vector encryptedData(rawEncryptedData.begin() + ivSize, rawEncryptedData.end());
|
|
|
|
// initialize the decryptor
|
|
if (EVP_DecryptInit_ex(
|
|
context.get(),
|
|
cipherFunction(),
|
|
nullptr,
|
|
this->_data.data(),
|
|
iv.data()
|
|
) != 1)
|
|
throw std::runtime_error("Error initializing decryptor");
|
|
|
|
std::vector<std::uint8_t> plainData(encryptedData.size());
|
|
int length;
|
|
|
|
// decrypt the data
|
|
if (EVP_DecryptUpdate(
|
|
context.get(),
|
|
plainData.data(),
|
|
&length,
|
|
encryptedData.data(),
|
|
static_cast<int>(encryptedData.size())
|
|
) != 1)
|
|
throw std::runtime_error("Error decrypting data");
|
|
|
|
// finalize the decryption
|
|
if (EVP_DecryptFinal_ex(
|
|
context.get(),
|
|
plainData.data(),
|
|
&length
|
|
) != 1)
|
|
throw std::runtime_error("Error finalizing decryptor");
|
|
plainData.resize(length);
|
|
|
|
return plainData;
|
|
}
|
|
|
|
private:
|
|
static std::mt19937 randomGenerator;
|
|
static std::uniform_int_distribution<std::uint8_t> randomDistribution;
|
|
|
|
std::array<std::uint8_t, keySize> _data;
|
|
};
|
|
|
|
|
|
template<std::size_t keySize, std::size_t ivSize, const EVP_CIPHER*(cipherFunction)()>
|
|
std::mt19937 AesKey<keySize, ivSize, cipherFunction>::randomGenerator = std::mt19937(std::random_device{}());
|
|
|
|
template<std::size_t keySize, std::size_t ivSize, const EVP_CIPHER*(cipherFunction)()>
|
|
std::uniform_int_distribution<std::uint8_t> AesKey<keySize, ivSize, cipherFunction>::randomDistribution = std::uniform_int_distribution(
|
|
std::numeric_limits<std::uint8_t>::min(),
|
|
std::numeric_limits<std::uint8_t>::max()
|
|
);
|
|
|
|
|
|
using AesKey256 = AesKey<256/8, 16, EVP_aes_256_cbc>;
|
|
|
|
|
|
} |