Signet Forge 0.1.0
C++20 Parquet library with AI-native extensions
DEMO
Loading...
Searching...
No Matches
aes_ctr.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright 2026 Johnson Ogundeji
3#pragma once
4
7
8// ---------------------------------------------------------------------------
9// aes_ctr.hpp -- Bundled, zero-dependency, header-only AES-256-CTR
10//
11// Implements AES-256 in Counter Mode (CTR) as specified in:
12// NIST SP 800-38A: Recommendation for Block Cipher Modes of Operation
13// https://csrc.nist.gov/publications/detail/sp/800-38a/final
14//
15// CTR mode converts AES into a stream cipher: for each 16-byte block, the
16// counter block is encrypted with AES, and the result is XORed with the data.
17// Since XOR is its own inverse, encryption and decryption are identical.
18//
19// This mode is used for Parquet column data encryption where authentication
20// is not needed (column integrity is verified by page checksums).
21//
22// Parameters:
23// Key size: 32 bytes (256 bits, AES-256)
24// IV size: 16 bytes (full 128-bit IV/counter initial value)
25// Counter: Last 4 bytes of the 16-byte block, incremented big-endian
26//
27// NIST SP 800-38A test vector (F.5.5 -- CTR-AES256.Encrypt):
28// Key: 603deb1015ca71be2b73aef0857d7781
29// 1f352c073b6108d72d9810a30914dff4
30// IV/CTR: f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff
31// Plaintext: 6bc1bee22e409f96e93d7e117393172a
32// ae2d8a571e03ac9c9eb76fac45af8e51
33// 30c81c46a35ce411e5fbc1191a0a52ef
34// f69f2445df4f9b17ad2b417be66c3710
35// Ciphertext:601ec313775789a5b7a7f504bbf3d228
36// f443e3ca4d62b59aca84e990cacaf5c5
37// 2b0930daa23de94ce87017ba2d84988d
38// dfc9c58db67aada613c2dd08457941a6
39// ---------------------------------------------------------------------------
40
42#include "signet/error.hpp"
43
44#include <cstddef>
45#include <cstdint>
46#include <cstring>
47#include <vector>
48
49namespace signet::forge::crypto {
50
51// ===========================================================================
52// AesCtr -- AES-256-CTR stream cipher (NIST SP 800-38A)
53// ===========================================================================
54
68class AesCtr {
69public:
70 static constexpr size_t KEY_SIZE = 32;
71 static constexpr size_t IV_SIZE = 16;
72
74 explicit AesCtr(const uint8_t key[KEY_SIZE])
75 : cipher_(key) {}
76
77 AesCtr(const AesCtr&) = delete;
78 AesCtr& operator=(const AesCtr&) = delete;
79
80 // -----------------------------------------------------------------------
81 // process -- Encrypt or decrypt data using AES-CTR
82 //
83 // CTR mode is symmetric: encrypt(plaintext) == decrypt(ciphertext)
84 // because the keystream is generated independently and simply XORed.
85 //
86 // Algorithm:
87 // For block i (0-indexed):
88 // 1. counter_block = IV with last 4 bytes = (initial_ctr + i) in BE
89 // 2. keystream_block = AES_K(counter_block)
90 // 3. output_block = input_block ^ keystream_block
91 //
92 // The last block may be partial -- only the needed keystream bytes
93 // are used.
94 //
95 // Counter increment: The last 4 bytes of the 16-byte IV are treated
96 // as a big-endian uint32 and incremented by 1 for each block. The
97 // first 12 bytes (nonce portion) remain constant.
98 // -----------------------------------------------------------------------
109 const uint8_t* data, size_t size,
110 const uint8_t iv[IV_SIZE]) const {
111
112 std::vector<uint8_t> output(size);
113 if (size == 0) return output;
114
115 // Counter overflow guard: extract initial 32-bit counter from IV's
116 // last 4 bytes (big-endian) and compute actual remaining blocks.
117 uint32_t initial_counter =
118 (static_cast<uint32_t>(iv[12]) << 24) |
119 (static_cast<uint32_t>(iv[13]) << 16) |
120 (static_cast<uint32_t>(iv[14]) << 8) |
121 (static_cast<uint32_t>(iv[15]));
122 uint64_t max_blocks = 0xFFFFFFFFULL - static_cast<uint64_t>(initial_counter) + 1;
123 uint64_t max_bytes = max_blocks * 16;
124 if (static_cast<uint64_t>(size) > max_bytes)
126 "AES-CTR: data size exceeds 32-bit counter space for this IV"};
127
128 // Initialize counter block from IV
129 uint8_t counter[16];
130 std::memcpy(counter, iv, 16);
131
132 size_t offset = 0;
133
134 while (offset < size) {
135 // Encrypt the counter block to produce keystream
136 uint8_t keystream[16];
137 std::memcpy(keystream, counter, 16);
138 cipher_.encrypt_block(keystream);
139
140 // XOR keystream with data (handle partial last block)
141 size_t block_len = size - offset;
142 if (block_len > 16) block_len = 16;
143
144 for (size_t j = 0; j < block_len; ++j) {
145 output[offset + j] = data[offset + j] ^ keystream[j];
146 }
147
148 offset += block_len;
149
150 // Increment counter (last 4 bytes, big-endian)
151 inc_counter(counter);
152 }
153
154 return output;
155 }
156
163 const uint8_t* data, size_t size,
164 const uint8_t iv[IV_SIZE]) const {
165 return process(data, size, iv);
166 }
167
174 const uint8_t* data, size_t size,
175 const uint8_t iv[IV_SIZE]) const {
176 return process(data, size, iv);
177 }
178
179private:
180 Aes256 cipher_;
181
183 static void inc_counter(uint8_t counter[16]) {
184 for (int i = 15; i >= 12; --i) {
185 if (++counter[i] != 0) break;
186 }
187 }
188};
189
190} // namespace signet::forge::crypto
AES-256 block cipher implementation (FIPS-197).
AES-256 block cipher (FIPS-197).
Definition aes_core.hpp:253
void encrypt_block(uint8_t block[BLOCK_SIZE]) const
Encrypt a single 16-byte block in-place (FIPS-197 Section 5.1).
Definition aes_core.hpp:280
AES-256 in Counter Mode (CTR) as specified in NIST SP 800-38A.
Definition aes_ctr.hpp:68
AesCtr & operator=(const AesCtr &)=delete
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.
Definition aes_ctr.hpp:162
expected< std::vector< uint8_t > > decrypt(const uint8_t *data, size_t size, const uint8_t iv[IV_SIZE]) const
Convenience alias for process() – decrypt data with AES-CTR.
Definition aes_ctr.hpp:173
static constexpr size_t KEY_SIZE
AES-256 key size in bytes.
Definition aes_ctr.hpp:70
AesCtr(const AesCtr &)=delete
AesCtr(const uint8_t key[KEY_SIZE])
Initialize with a 32-byte key.
Definition aes_ctr.hpp:74
expected< std::vector< uint8_t > > process(const uint8_t *data, size_t size, const uint8_t iv[IV_SIZE]) const
Encrypt or decrypt data using AES-CTR.
Definition aes_ctr.hpp:108
static constexpr size_t IV_SIZE
Full 128-bit IV/counter size in bytes.
Definition aes_ctr.hpp:71
A lightweight result type that holds either a success value of type T or an Error.
Definition error.hpp:145
@ 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.
Definition error.hpp:101