6#if !defined(SIGNET_ENABLE_COMMERCIAL) || !SIGNET_ENABLE_COMMERCIAL
7#error "signet/crypto/hsm_client_stub.hpp requires SIGNET_ENABLE_COMMERCIAL=ON (AGPL-3.0 commercial tier). See LICENSE_COMMERCIAL."
43#include <unordered_map>
51namespace detail::aes_key_wrap {
54static constexpr std::array<uint8_t, 8> DEFAULT_IV = {
55 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6
64 const std::array<uint8_t, 32>& kek,
65 const std::vector<uint8_t>& plaintext)
67 if (plaintext.size() < 16 || (plaintext.size() % 8) != 0) {
69 "AES Key Wrap: plaintext must be >= 16 bytes and a multiple of 8"};
72 const size_t n = plaintext.size() / 8;
76 std::array<uint8_t, 8> A{};
77 std::memcpy(A.data(), DEFAULT_IV.data(), 8);
79 std::vector<uint8_t> R(plaintext.begin(), plaintext.end());
82 for (
size_t j = 0; j < 6; ++j) {
83 for (
size_t i = 0; i < n; ++i) {
85 std::array<uint8_t, 16> block{};
86 std::memcpy(block.data(), A.data(), 8);
87 std::memcpy(block.data() + 8, R.data() + i * 8, 8);
90 const auto& encrypted = block;
93 uint64_t t = n * j + i + 1;
94 std::memcpy(A.data(), encrypted.data(), 8);
96 for (
int k = 7; k >= 0 && t > 0; --k) {
97 A[
static_cast<size_t>(k)] ^=
static_cast<uint8_t
>(t & 0xFF);
102 std::memcpy(R.data() + i * 8, encrypted.data() + 8, 8);
107 std::vector<uint8_t> result(8 + R.size());
108 std::memcpy(result.data(), A.data(), 8);
109 std::memcpy(result.data() + 8, R.data(), R.size());
119 const std::array<uint8_t, 32>& kek,
120 const std::vector<uint8_t>& ciphertext)
122 if (ciphertext.size() < 24 || (ciphertext.size() % 8) != 0) {
124 "AES Key Unwrap: ciphertext must be >= 24 bytes and a multiple of 8"};
127 const size_t n = (ciphertext.size() / 8) - 1;
128 Aes256 cipher(kek.data());
131 std::array<uint8_t, 8> A{};
132 std::memcpy(A.data(), ciphertext.data(), 8);
134 std::vector<uint8_t> R(ciphertext.begin() + 8, ciphertext.end());
137 for (
int j = 5; j >= 0; --j) {
138 for (
int ii =
static_cast<int>(n) - 1; ii >= 0; --ii) {
139 size_t i =
static_cast<size_t>(ii);
142 uint64_t t = n *
static_cast<size_t>(j) + i + 1;
143 std::array<uint8_t, 8> A_xor = A;
144 for (
int k = 7; k >= 0 && t > 0; --k) {
145 A_xor[
static_cast<size_t>(k)] ^=
static_cast<uint8_t
>(t & 0xFF);
150 std::array<uint8_t, 16> block{};
151 std::memcpy(block.data(), A_xor.data(), 8);
152 std::memcpy(block.data() + 8, R.data() + i * 8, 8);
157 std::memcpy(A.data(), block.data(), 8);
159 std::memcpy(R.data() + i * 8, block.data() + 8, 8);
164 if (std::memcmp(A.data(), DEFAULT_IV.data(), 8) != 0) {
166 "AES Key Unwrap: integrity check failed — wrong KEK or corrupted data"};
196 const std::string& key_id,
197 const std::vector<uint8_t>& kek)
199 if (kek.size() != 32) {
201 "HSM stub: KEK must be exactly 32 bytes (AES-256)"};
203 std::array<uint8_t, 32> key{};
204 std::memcpy(key.data(), kek.data(), 32);
211 const std::array<uint8_t, 32>& kek) {
216 [[nodiscard]]
bool has_kek(
const std::string& key_id)
const {
217 return keks_.find(key_id) != keks_.end();
221 [[nodiscard]]
size_t kek_count()
const {
return keks_.size(); }
226 const std::vector<uint8_t>& dek,
227 const std::string& master_key_id)
const override
229 auto it = keks_.find(master_key_id);
230 if (it == keks_.end()) {
232 "HSM stub: KEK not found: " + master_key_id};
237 if (dek.size() < 16 || (dek.size() % 8) != 0) {
239 "HSM stub: DEK must be >= 16 bytes and a multiple of 8"};
246 const std::vector<uint8_t>& wrapped_dek,
247 const std::string& master_key_id)
const override
249 auto it = keks_.find(master_key_id);
250 if (it == keks_.end()) {
252 "HSM stub: KEK not found: " + master_key_id};
259 std::unordered_map<std::string, std::array<uint8_t, 32>> keks_;
AES-256 block cipher implementation (FIPS-197).
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).
void decrypt_block(uint8_t block[BLOCK_SIZE]) const
Decrypt a single 16-byte block in-place (FIPS-197 Section 5.3).
Test stub implementing IKmsClient using software AES Key Wrap.
expected< void > register_kek(const std::string &key_id, const std::vector< uint8_t > &kek)
Register a KEK by ID. The key must be exactly 32 bytes (AES-256).
bool has_kek(const std::string &key_id) const
Check if a KEK is registered.
expected< std::vector< uint8_t > > unwrap_key(const std::vector< uint8_t > &wrapped_dek, const std::string &master_key_id) const override
Unwrap (decrypt) a wrapped DEK using the KEK identified by master_key_id.
size_t kek_count() const
Number of registered KEKs.
void register_kek(const std::string &key_id, const std::array< uint8_t, 32 > &kek)
Register a KEK from a raw 32-byte array.
expected< std::vector< uint8_t > > wrap_key(const std::vector< uint8_t > &dek, const std::string &master_key_id) const override
Wrap (encrypt) a DEK under the KEK identified by master_key_id.
Abstract KMS client interface for DEK/KEK key wrapping.
A lightweight result type that holds either a success value of type T or an Error.
expected< std::vector< uint8_t > > wrap(const std::array< uint8_t, 32 > &kek, const std::vector< uint8_t > &plaintext)
AES Key Wrap — wraps plaintext key material under a KEK.
expected< std::vector< uint8_t > > unwrap(const std::array< uint8_t, 32 > &kek, const std::vector< uint8_t > &ciphertext)
AES Key Unwrap — recovers plaintext key material from wrapped form.
@ ENCRYPTION_ERROR
An encryption or decryption operation failed (bad key, tampered ciphertext, PME error).
@ INVALID_ARGUMENT
A caller-supplied argument is outside the valid range or violates a precondition.
Lightweight error value carrying an ErrorCode and a human-readable message.