41#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
44#elif defined(__linux__)
45# include <sys/random.h>
72 const uint8_t* data,
size_t size,
73 const std::string& aad =
"")
const = 0;
77 const uint8_t* data,
size_t size,
78 const std::string& aad =
"")
const = 0;
84 [[nodiscard]] virtual
size_t key_size() const noexcept = 0;
101namespace detail::cipher {
109 if (size == 0)
return;
110#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
111 arc4random_buf(buf, size);
112#elif defined(__linux__)
115 while (written < size) {
116 ssize_t ret = getrandom(buf + written, size - written, 0);
118 if (errno == EINTR)
continue;
120 volatile unsigned char* p = buf;
121 for (
size_t i = 0; i < size; ++i) p[i] = 0;
122 throw std::runtime_error(
"signet: getrandom() failed");
124 written +=
static_cast<size_t>(ret);
127 if (size >
static_cast<size_t>((std::numeric_limits<ULONG>::max)())) {
128 throw std::runtime_error(
"csprng_fill: size exceeds ULONG max");
130 NTSTATUS status = BCryptGenRandom(NULL, buf,
static_cast<ULONG
>(size),
131 BCRYPT_USE_SYSTEM_PREFERRED_RNG);
133 throw std::runtime_error(
"BCryptGenRandom failed");
136 throw std::runtime_error(
"signet: no secure RNG available on this platform");
144 std::vector<uint8_t> iv(iv_size);
153inline std::vector<uint8_t>
prepend_iv(
const std::vector<uint8_t>& iv,
154 const std::vector<uint8_t>& ciphertext) {
155 std::vector<uint8_t> out;
156 out.reserve(1 + iv.size() + ciphertext.size());
157 out.push_back(
static_cast<uint8_t
>(iv.size()));
158 out.insert(out.end(), iv.begin(), iv.end());
159 out.insert(out.end(), ciphertext.begin(), ciphertext.end());
177 "cipher: encrypted data too short (no IV size byte)"};
179 uint8_t iv_size = data[0];
180 if (iv_size == 0 || iv_size > 16) {
182 "cipher: invalid IV size " + std::to_string(iv_size)};
184 size_t header_len = 1 +
static_cast<size_t>(iv_size);
185 if (size < header_len) {
187 "cipher: encrypted data too short for IV"};
189 return IvParsed{data + 1, data + header_len, size - header_len};
210namespace detail::crngt {
214 uint8_t prev[32] = {};
215 bool initialized =
false;
228 uint8_t* buf,
size_t size) {
229 detail::cipher::fill_random_bytes(buf, size);
233 while (offset + 32 <= size) {
235 if (std::memcmp(buf + offset, state.
prev, 32) == 0) {
236 throw std::runtime_error(
237 "CRNGT failure: consecutive RNG outputs are identical "
238 "(FIPS 140-3 §4.9.2)");
241 std::memcpy(state.
prev, buf + offset, 32);
249 size_t remaining = size - offset;
250 if (remaining > 0 && std::memcmp(buf + offset, state.
prev, remaining) == 0) {
251 throw std::runtime_error(
"CRNGT: consecutive identical output blocks detected (partial)");
269namespace detail::secure_mem {
274 if (!ptr || size == 0)
return false;
276 return VirtualLock(ptr, size) != 0;
277#elif defined(__EMSCRIPTEN__)
278 (void)ptr; (void)size;
280#elif defined(__unix__) || defined(__APPLE__)
281 return ::mlock(ptr, size) == 0;
283 (void)ptr; (void)size;
290 if (!ptr || size == 0)
return;
292 VirtualUnlock(ptr, size);
293#elif defined(__EMSCRIPTEN__)
294 (void)ptr; (void)size;
295#elif defined(__unix__) || defined(__APPLE__)
296 ::munlock(ptr, size);
298 (void)ptr; (void)size;
303inline void secure_zero(
void* ptr,
size_t size) {
304 if (!ptr || size == 0)
return;
306 SecureZeroMemory(ptr, size);
308 volatile unsigned char* p =
static_cast<volatile unsigned char*
>(ptr);
309 for (
size_t i = 0; i < size; ++i) p[i] = 0;
325 detail::secure_mem::lock_memory(data_.data(), data_.size());
330 : data_(ptr, ptr + size) {
331 detail::secure_mem::lock_memory(data_.data(), data_.size());
336 detail::cipher::fill_random_bytes(data_.data(), size);
337 detail::secure_mem::lock_memory(data_.data(), data_.size());
341 detail::secure_mem::secure_zero(data_.data(), data_.size());
342 detail::secure_mem::unlock_memory(data_.data(), data_.size());
348 detail::secure_mem::lock_memory(data_.data(), data_.size());
351 if (
this != &other) {
352 detail::secure_mem::secure_zero(data_.data(), data_.size());
353 detail::secure_mem::unlock_memory(data_.data(), data_.size());
354 data_ = std::move(other.data_);
361 [[nodiscard]]
const uint8_t*
data()
const {
return data_.data(); }
362 [[nodiscard]] uint8_t*
data() {
return data_.data(); }
363 [[nodiscard]]
size_t size()
const {
return data_.size(); }
364 [[nodiscard]]
bool empty()
const {
return data_.empty(); }
367 std::vector<uint8_t> data_;
392 static constexpr uint64_t MAX_INVOCATIONS = UINT64_C(0xFFFFFFFF);
395 static constexpr uint64_t DEFAULT_ROTATION_THRESHOLD =
396 static_cast<uint64_t
>(MAX_INVOCATIONS * 0.75);
405 : key_{}, gcm_(nullptr) {
406 std::memcpy(key_.data(), key.data(), (std::min)(key.size(), key_.size()));
407 gcm_ = std::make_unique<AesGcm>(key_.data());
412 : key_{}, gcm_(nullptr) {
413 std::memcpy(key_.data(), key, (std::min)(key_len, key_.size()));
414 gcm_ = std::make_unique<AesGcm>(key_.data());
423 uint64_t threshold = DEFAULT_ROTATION_THRESHOLD) {
424 rotation_callback_ = std::move(cb);
425 rotation_threshold_ = threshold;
430 return invocation_count_.load(std::memory_order_relaxed);
434 const uint8_t* data,
size_t size,
435 const std::string& aad =
"")
const override {
437 if (key_.size() != AesGcm::KEY_SIZE) {
438 return Error{ErrorCode::ENCRYPTION_ERROR,
439 "AesGcmCipher: key must be 32 bytes"};
445 uint64_t count = invocation_count_.fetch_add(1, std::memory_order_relaxed);
446 if (count >= MAX_INVOCATIONS) {
447 return Error{ErrorCode::ENCRYPTION_ERROR,
448 "AES-GCM: key invocation limit reached (2^32). "
449 "NIST SP 800-38D §8.2 requires key rotation."};
453 if (rotation_callback_ && count == rotation_threshold_) {
454 rotation_callback_(count);
457 auto iv = detail::cipher::generate_iv(AesGcm::IV_SIZE);
459 auto result = aad.empty()
460 ? gcm_->encrypt(data, size, iv.data())
461 : gcm_->encrypt(data, size, iv.data(),
462 reinterpret_cast<const uint8_t*
>(aad.data()),
465 if (!result)
return result.error();
466 return detail::cipher::prepend_iv(iv, *result);
470 const uint8_t* data,
size_t size,
471 const std::string& aad =
"")
const override {
473 if (key_.size() != AesGcm::KEY_SIZE) {
474 return Error{ErrorCode::ENCRYPTION_ERROR,
475 "AesGcmCipher: key must be 32 bytes"};
478 auto iv_result = detail::cipher::parse_iv_header(data, size);
479 if (!iv_result)
return iv_result.error();
480 const auto& [iv, ciphertext, ct_size] = *iv_result;
483 return gcm_->decrypt(ciphertext, ct_size, iv,
484 reinterpret_cast<const uint8_t*
>(aad.data()),
487 return gcm_->decrypt(ciphertext, ct_size, iv);
494 volatile uint8_t* p = key_.data();
495 for (
size_t i = 0; i < key_.size(); ++i) p[i] = 0;
496#if defined(__GNUC__) || defined(__clang__)
497 __asm__ __volatile__(
"" :::
"memory");
502 [[nodiscard]]
size_t key_size() const noexcept
override {
return AesGcm::KEY_SIZE; }
504 return "AES-256-GCM";
508 std::array<uint8_t, 32> key_{};
509 std::unique_ptr<AesGcm> gcm_;
510 mutable std::atomic<uint64_t> invocation_count_{0};
511 RotationCallback rotation_callback_;
512 uint64_t rotation_threshold_{DEFAULT_ROTATION_THRESHOLD};
528 static constexpr uint8_t AAD_TAGGED_IV_FLAG = 0x80u;
529 static constexpr size_t AAD_TAG_SIZE = 32u;
533 : key_{}, ctr_(nullptr) {
534 std::memcpy(key_.data(), key.data(), (std::min)(key.size(), key_.size()));
535 ctr_ = std::make_unique<AesCtr>(key_.data());
540 : key_{}, ctr_(nullptr) {
541 std::memcpy(key_.data(), key, (std::min)(key_len, key_.size()));
542 ctr_ = std::make_unique<AesCtr>(key_.data());
546 const uint8_t* data,
size_t size,
547 const std::string& aad =
"")
const override {
549 if (key_.size() != AesCtr::KEY_SIZE) {
550 return Error{ErrorCode::ENCRYPTION_ERROR,
551 "AesCtrCipher: key must be 32 bytes"};
554 auto iv = detail::cipher::generate_iv(AesCtr::IV_SIZE);
555 auto ct_result = ctr_->encrypt(data, size, iv.data());
556 if (!ct_result)
return ct_result.error();
559 return detail::cipher::prepend_iv(iv, *ct_result);
562 auto mac_key = derive_aad_mac_key();
563 auto tag = compute_aad_tag(mac_key, aad, iv.data(), iv.size(),
564 ct_result->data(), ct_result->size());
566 std::vector<uint8_t> out;
567 out.reserve(1 + iv.size() + ct_result->size() + tag.size());
568 out.push_back(
static_cast<uint8_t
>(iv.size()) | AAD_TAGGED_IV_FLAG);
569 out.insert(out.end(), iv.begin(), iv.end());
570 out.insert(out.end(), ct_result->begin(), ct_result->end());
571 out.insert(out.end(), tag.begin(), tag.end());
576 const uint8_t* data,
size_t size,
577 const std::string& aad =
"")
const override {
579 if (key_.size() != AesCtr::KEY_SIZE) {
580 return Error{ErrorCode::ENCRYPTION_ERROR,
581 "AesCtrCipher: key must be 32 bytes"};
584 return Error{ErrorCode::ENCRYPTION_ERROR,
585 "AesCtrCipher: encrypted data too short"};
588 const uint8_t iv_flag = data[0];
589 const bool tagged = (iv_flag & AAD_TAGGED_IV_FLAG) != 0;
591 const size_t iv_size =
static_cast<size_t>(iv_flag & ~AAD_TAGGED_IV_FLAG);
592 const size_t header_size = 1 + iv_size;
593 if (iv_size == 0 || iv_size > AesCtr::IV_SIZE || size < header_size + AAD_TAG_SIZE) {
594 return Error{ErrorCode::ENCRYPTION_ERROR,
595 "AesCtrCipher: invalid tagged CTR payload"};
598 return Error{ErrorCode::ENCRYPTION_ERROR,
599 "AesCtrCipher: tagged CTR payload requires AAD"};
602 const uint8_t* iv = data + 1;
603 const size_t ct_size = size - header_size - AAD_TAG_SIZE;
604 const uint8_t* ciphertext = data + header_size;
605 const uint8_t* stored_tag = ciphertext + ct_size;
607 auto mac_key = derive_aad_mac_key();
608 auto expected_tag = compute_aad_tag(mac_key, aad, iv, iv_size, ciphertext, ct_size);
610 for (
size_t i = 0; i < expected_tag.size(); ++i) {
611 diff |=
static_cast<uint8_t
>(stored_tag[i] ^ expected_tag[i]);
614 return Error{ErrorCode::ENCRYPTION_ERROR,
615 "AesCtrCipher: AAD authentication failed"};
618 auto pt_result = ctr_->decrypt(ciphertext, ct_size, iv);
619 if (!pt_result)
return pt_result.error();
620 return std::move(*pt_result);
623 auto iv_result = detail::cipher::parse_iv_header(data, size);
624 if (!iv_result)
return iv_result.error();
625 const auto& [iv, ciphertext, ct_size] = *iv_result;
627 auto pt_result = ctr_->decrypt(ciphertext, ct_size, iv);
628 if (!pt_result)
return pt_result.error();
629 return std::move(*pt_result);
635 volatile uint8_t* p = key_.data();
636 for (
size_t i = 0; i < key_.size(); ++i) p[i] = 0;
637#if defined(__GNUC__) || defined(__clang__)
638 __asm__ __volatile__(
"" :::
"memory");
645 [[nodiscard]]
size_t key_size() const noexcept
override {
return AesCtr::KEY_SIZE; }
648 return "AES-256-CTR";
652 [[nodiscard]] std::array<uint8_t, 32> derive_aad_mac_key()
const {
653 static constexpr uint8_t kInfo[] =
"signet-aes-ctr-aad-mac-v1";
654 auto prk =
hkdf_extract(
nullptr, 0, key_.data(), key_.size());
655 std::array<uint8_t, 32> mac_key{};
656 (void)hkdf_expand(prk, kInfo,
sizeof(kInfo) - 1, mac_key.data(), mac_key.size());
660 [[nodiscard]]
static std::array<uint8_t, AAD_TAG_SIZE> compute_aad_tag(
661 const std::array<uint8_t, 32>& mac_key,
662 const std::string& aad,
663 const uint8_t* iv,
size_t iv_size,
664 const uint8_t* ciphertext,
size_t ct_size) {
666 std::vector<uint8_t> mac_input;
667 mac_input.reserve(aad.size() + iv_size + ct_size);
668 mac_input.insert(mac_input.end(), aad.begin(), aad.end());
669 mac_input.insert(mac_input.end(), iv, iv + iv_size);
670 mac_input.insert(mac_input.end(), ciphertext, ciphertext + ct_size);
671 return detail::hkdf::hmac_sha256(mac_key.data(), mac_key.size(),
672 mac_input.data(), mac_input.size());
675 std::array<uint8_t, 32> key_{};
676 std::unique_ptr<AesCtr> ctr_;
690 return std::make_unique<AesGcmCipher>(key);
696 if (algo == EncryptionAlgorithm::AES_GCM_V1) {
697 return std::make_unique<AesGcmCipher>(key);
700 return std::make_unique<AesCtrCipher>(key);
707 return std::make_unique<AesGcmCipher>(key);
728namespace detail::kat {
732 std::vector<uint8_t> out;
734 auto nibble = [](
char c) -> uint8_t {
735 if (c >=
'0' && c <=
'9')
return static_cast<uint8_t
>(c -
'0');
736 if (c >=
'a' && c <=
'f')
return static_cast<uint8_t
>(c -
'a' + 10);
737 if (c >=
'A' && c <=
'F')
return static_cast<uint8_t
>(c -
'A' + 10);
740 uint8_t hi = nibble(*hex++);
742 uint8_t lo = nibble(*hex++);
743 out.push_back(
static_cast<uint8_t
>((hi << 4) | lo));
757 using namespace detail::kat;
761 auto key = hex_decode(
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
762 auto pt = hex_decode(
"00112233445566778899aabbccddeeff");
763 auto exp = hex_decode(
"8ea2b7ca516745bfeafc49904b496089");
764 if (key.size() != 32 || pt.size() != 16)
return false;
766 Aes256 cipher(key.data());
768 std::memcpy(block, pt.data(), 16);
770 if (std::memcmp(block, exp.data(), 16) != 0)
return false;
777 auto key = hex_decode(
"feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308");
778 auto iv = hex_decode(
"cafebabefacedbaddecaf888");
779 auto aad = hex_decode(
"feedfacedeadbeeffeedfacedeadbeefabaddad2");
780 auto pt = hex_decode(
"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39");
781 auto exp_ct = hex_decode(
"522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662");
784 auto result = gcm.
encrypt(pt.data(), pt.size(), iv.data(), aad.data(), aad.size());
785 if (!result.has_value())
return false;
786 if (result->size() != pt.size() + 16)
return false;
788 if (std::memcmp(result->data(), exp_ct.data(), pt.size()) != 0)
return false;
790 auto dec = gcm.
decrypt(result->data(), result->size(), iv.data(), aad.data(), aad.size());
791 if (!dec.has_value())
return false;
792 if (dec->size() != pt.size())
return false;
793 if (std::memcmp(dec->data(), pt.data(), pt.size()) != 0)
return false;
798 auto key = hex_decode(
"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");
799 auto iv = hex_decode(
"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
800 auto pt = hex_decode(
"6bc1bee22e409f96e93d7e117393172a");
801 auto exp = hex_decode(
"601ec313775789a5b7a7f504bbf3d228");
804 auto ct_result = ctr.
encrypt(pt.data(), pt.size(), iv.data());
805 if (!ct_result.has_value())
return false;
806 auto& ct = *ct_result;
807 if (ct.size() != pt.size())
return false;
808 if (std::memcmp(ct.data(), exp.data(), pt.size()) != 0)
return false;
AES-256-CTR stream cipher implementation (NIST SP 800-38A).
AES-256-GCM authenticated encryption (NIST SP 800-38D).
AES-256 block cipher (FIPS-197).
void encrypt_block(uint8_t block[BLOCK_SIZE]) const
Encrypt a single 16-byte block in-place (FIPS-197 Section 5.1).
AES-256-CTR adapter – wraps the low-level AesCtr class behind ICipher.
size_t key_size() const noexcept override
expected< std::vector< uint8_t > > decrypt(const uint8_t *data, size_t size, const std::string &aad="") const override
Decrypt data produced by encrypt().
std::string_view algorithm_name() const noexcept override
AesCtrCipher(const uint8_t *key, size_t key_len)
Construct from a raw key pointer and length.
expected< std::vector< uint8_t > > encrypt(const uint8_t *data, size_t size, const std::string &aad="") const override
Encrypt data.
bool is_authenticated() const noexcept override
AesCtrCipher(const std::vector< uint8_t > &key)
Construct from a key vector (must be 32 bytes for AES-256).
~AesCtrCipher() override
Destructor: securely zeroes key material (CWE-244: heap inspection).
AES-256 in Counter Mode (CTR) as specified in NIST SP 800-38A.
expected< std::vector< uint8_t > > encrypt(const uint8_t *data, size_t size, const uint8_t iv[IV_SIZE]) const
Convenience alias for process() – encrypt data with AES-CTR.
AES-256-GCM adapter – wraps the low-level AesGcm class behind ICipher.
~AesGcmCipher() override
Destructor: securely zeroes key material (CWE-244: heap inspection).
std::function< void(uint64_t invocation_count)> RotationCallback
Callback type for key rotation notification.
bool is_authenticated() const noexcept override
Whether this cipher provides authentication (GCM=true, CTR=false).
size_t key_size() const noexcept override
Key size in bytes (32 for AES-256).
expected< std::vector< uint8_t > > encrypt(const uint8_t *data, size_t size, const std::string &aad="") const override
Encrypt data.
expected< std::vector< uint8_t > > decrypt(const uint8_t *data, size_t size, const std::string &aad="") const override
Decrypt data produced by encrypt().
uint64_t invocation_count() const noexcept
Get the current number of encrypt() invocations on this key.
AesGcmCipher(const uint8_t *key, size_t key_len)
Construct from a raw key pointer and length.
AesGcmCipher(const std::vector< uint8_t > &key)
Construct from a key vector (must be 32 bytes for AES-256).
std::string_view algorithm_name() const noexcept override
Human-readable algorithm name.
void set_rotation_callback(RotationCallback cb, uint64_t threshold=DEFAULT_ROTATION_THRESHOLD)
Register a callback invoked when the key approaches its invocation limit.
AES-256 in Galois/Counter Mode (GCM) as specified in NIST SP 800-38D.
expected< std::vector< uint8_t > > encrypt(const uint8_t *plaintext, size_t plaintext_size, const uint8_t iv[IV_SIZE], const uint8_t *aad=nullptr, size_t aad_size=0) const
Authenticated encryption with additional data (AEAD).
expected< std::vector< uint8_t > > decrypt(const uint8_t *ciphertext_with_tag, size_t total_size, const uint8_t iv[IV_SIZE], const uint8_t *aad=nullptr, size_t aad_size=0) const
Authenticated decryption and verification (NIST SP 800-38D Section 7.2).
Abstract cipher interface — unified API for authenticated (GCM) and unauthenticated (CTR) encryption.
virtual size_t key_size() const noexcept=0
Key size in bytes (32 for AES-256).
virtual std::string_view algorithm_name() const noexcept=0
Human-readable algorithm name.
virtual expected< std::vector< uint8_t > > decrypt(const uint8_t *data, size_t size, const std::string &aad="") const =0
Decrypt data produced by encrypt().
virtual expected< std::vector< uint8_t > > encrypt(const uint8_t *data, size_t size, const std::string &aad="") const =0
Encrypt data.
virtual bool is_authenticated() const noexcept=0
Whether this cipher provides authentication (GCM=true, CTR=false).
virtual ~ICipher()=default
RAII container for sensitive key material with mlock and secure zeroization.
SecureKeyBuffer(SecureKeyBuffer &&other) noexcept
SecureKeyBuffer(const SecureKeyBuffer &)=delete
const uint8_t * data() const
SecureKeyBuffer(const std::vector< uint8_t > &key)
Construct from existing key bytes (copies and locks).
SecureKeyBuffer & operator=(const SecureKeyBuffer &)=delete
SecureKeyBuffer & operator=(SecureKeyBuffer &&other) noexcept
SecureKeyBuffer(const uint8_t *ptr, size_t size)
Construct from raw bytes (copies and locks).
SecureKeyBuffer(size_t size)
Construct with a specified size of random key material.
A lightweight result type that holds either a success value of type T or an Error.
HKDF key derivation (RFC 5869) using HMAC-SHA256.
std::vector< uint8_t > generate_iv(size_t iv_size)
Generate a random initialization vector of the specified size.
void fill_random_bytes(uint8_t *buf, size_t size)
Fill a buffer with cryptographically random bytes using the best available OS-level CSPRNG (CWE-338: ...
expected< IvParsed > parse_iv_header(const uint8_t *data, size_t size)
Parse the IV header from encrypted data: [1 byte: iv_size] [iv] [ciphertext].
std::vector< uint8_t > prepend_iv(const std::vector< uint8_t > &iv, const std::vector< uint8_t > &ciphertext)
Prepend an IV header to ciphertext: [1 byte: iv.size()] [iv bytes] [ciphertext].
void fill_random_bytes_tested(CrngtState &state, uint8_t *buf, size_t size)
Generate random bytes with FIPS 140-3 §4.9.2 continuous test.
std::vector< uint8_t > hex_decode(const char *hex)
Decode a hex string to bytes (internal helper for KAT vectors).
bool lock_memory(void *ptr, size_t size)
Lock a memory region so it is not paged to swap.
void unlock_memory(void *ptr, size_t size)
Unlock a previously locked memory region.
std::array< uint8_t, 32 > hkdf_extract(const uint8_t *salt, size_t salt_size, const uint8_t *ikm, size_t ikm_size)
HKDF-Extract (RFC 5869 §2.2): Extract a pseudorandom key from input keying material.
bool crypto_self_test()
Run power-on self-tests (Known Answer Tests) for all crypto primitives.
EncryptionAlgorithm
Encryption algorithm identifier.
@ ENCRYPTION_ERROR
An encryption or decryption operation failed (bad key, tampered ciphertext, PME error).
Lightweight error value carrying an ErrorCode and a human-readable message.
Factory for creating cipher instances from algorithm enum + raw key.
static std::unique_ptr< ICipher > create_footer_cipher(EncryptionAlgorithm, const std::vector< uint8_t > &key)
Create a footer cipher (always authenticated = GCM).
static std::unique_ptr< ICipher > create_metadata_cipher(EncryptionAlgorithm, const std::vector< uint8_t > &key)
Create a metadata cipher (always authenticated = GCM).
static std::unique_ptr< ICipher > create_column_cipher(EncryptionAlgorithm algo, const std::vector< uint8_t > &key)
Create a column data cipher (GCM or CTR based on algorithm).
Result of parsing an IV header from encrypted data.
const uint8_t * ciphertext
Pointer to the ciphertext after the IV.
const uint8_t * iv
Pointer to the IV bytes within the input buffer.
size_t ct_size
Ciphertext length (may include GCM auth tag).
CRNGT state — stores the previous 32-byte RNG output for comparison.