Skip to content
Snippets Groups Projects
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