-
Daniel Lyubomirov authoredDaniel Lyubomirov authored
aes.cc 3.19 KiB
#include <openssl/err.h>
#include <vereign/crypto/aes.hh>
#include <vereign/crypto/rand.hh>
#include <vereign/crypto/errors.hh>
#include <openssl/base.h>
#include <openssl/evp.h>
namespace {
constexpr int gcmIVSizeBytes = 12;
constexpr int gcmTagSizeBytes = 16;
constexpr int aes256BlockSizeBytes = 32;
}
namespace vereign::crypto::aes {
void GCM256Encrypt(
bytes::View src,
bytes::View key,
bytes::Buffer& iv,
bytes::Buffer& tag,
bytes::Buffer& encrypted
) {
iv.Reserve(gcmIVSizeBytes);
crypto::Rand(iv, gcmIVSizeBytes);
encrypted.Reserve(src.Size() + aes256BlockSizeBytes);
bssl::UniquePtr<EVP_CIPHER_CTX> ctx{EVP_CIPHER_CTX_new()};
if (!ctx) {
throw OpenSSLError("evp cipher context cannot be created");
}
auto r = EVP_EncryptInit_ex(ctx.get(), EVP_aes_256_gcm(), nullptr, nullptr, nullptr);
if (r != 1) {
throw OpenSSLError("AES GCM encrypt init failed");
}
r = EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, iv.Size(), nullptr);
if (r != 1) {
throw OpenSSLError("iv size init failed");
}
r = EVP_EncryptInit_ex(ctx.get(), nullptr, nullptr, key.Data(), iv.View().Data());
if (r != 1) {
throw OpenSSLError("key and iv init failed");
}
int bytes_written;
r = EVP_EncryptUpdate(ctx.get(), encrypted.end(), &bytes_written, src.Data(), src.Size());
if (r != 1) {
throw OpenSSLError("encrypt failed");
}
encrypted.IncSize(bytes_written);
r = EVP_EncryptFinal_ex(ctx.get(), encrypted.end(), &bytes_written);
if (r != 1) {
throw OpenSSLError("finalize encryption failed");
}
encrypted.IncSize(bytes_written);
tag.Reserve(gcmTagSizeBytes);
r = EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, gcmTagSizeBytes, tag.end());
if (r != 1) {
throw OpenSSLError("getting GCM tag failed");
}
tag.IncSize(gcmTagSizeBytes);
r = EVP_CIPHER_CTX_cleanup(ctx.get());
if (r != 1) {
ERR_clear_error();
}
}
void GCM256Decrypt(
bytes::View src,
bytes::View key,
bytes::View iv,
bytes::View tag,
bytes::Buffer& decrypted
) {
bssl::UniquePtr<EVP_CIPHER_CTX> ctx{EVP_CIPHER_CTX_new()};
if (!ctx) {
throw OpenSSLError("evp cipher context cannot be created");
}
auto r = EVP_DecryptInit_ex(ctx.get(), EVP_aes_256_gcm(), nullptr, nullptr, nullptr);
if (r != 1) {
throw OpenSSLError("AES GCM decrypt init failed");
}
r = EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, iv.Size(), nullptr);
if (r != 1) {
throw OpenSSLError("iv size init failed");
}
r = EVP_DecryptInit_ex(ctx.get(), nullptr, nullptr, key.Data(), iv.Data());
if (r != 1) {
throw OpenSSLError("key and iv init failed");
}
decrypted.Reserve(src.Size());
int bytes_written;
r = EVP_DecryptUpdate(ctx.get(), decrypted.end(), &bytes_written, src.Data(), src.Size());
if (r != 1) {
throw OpenSSLError("decrypt failed");
}
decrypted.IncSize(bytes_written);
r = EVP_CIPHER_CTX_ctrl(
ctx.get(),
EVP_CTRL_AEAD_SET_TAG,
tag.Size(),
const_cast<uint8_t*>(tag.Data())
);
if (r != 1) {
throw OpenSSLError("setting GCM tag failed");
}
r = EVP_DecryptFinal_ex(ctx.get(), decrypted.end(), &bytes_written);
if (r != 1) {
throw OpenSSLError("verification failed");
}
}
} // vereign::crypto::aes