Signet Forge 0.1.0
C++20 Parquet library with AI-native extensions
DEMO
Loading...
Searching...
No Matches
hkdf.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// hkdf.hpp -- HMAC-based Key Derivation Function (RFC 5869)
10//
11// Gap C-7/C-8: Proper cryptographic key derivation replaces raw SHA-256
12// for key material derivation. Required by:
13// - NIST SP 800-108 Rev.1: Key Derivation Using Pseudorandom Functions
14// - NIST SP 800-56C Rev.2: Key-Derivation Methods in Key-Establishment
15// - Parquet PME spec: KEK→DEK derivation for key wrapping
16//
17// Components:
18// - HMAC-SHA256: RFC 2104 keyed hash
19// - HKDF-Extract: RFC 5869 §2.2 — PRK = HMAC-Hash(salt, IKM)
20// - HKDF-Expand: RFC 5869 §2.3 — OKM = T(1) || T(2) || ...
21//
22// Reference: https://www.rfc-editor.org/rfc/rfc5869
23// ---------------------------------------------------------------------------
24
25#include "signet/crypto/sha256.hpp" // for detail::sha256
26
27#include <algorithm>
28#include <array>
29#include <cstddef>
30#include <cstdint>
31#include <cstring>
32#include <vector>
33
34namespace signet::forge::crypto {
35
36namespace detail::hkdf {
37
38static constexpr size_t SHA256_BLOCK_SIZE = 64;
39static constexpr size_t SHA256_HASH_SIZE = 32;
40
44inline std::array<uint8_t, 32> hmac_sha256(
45 const uint8_t* key, size_t key_size,
46 const uint8_t* data, size_t data_size) {
47
48 std::array<uint8_t, 32> key_hash{};
49 const uint8_t* actual_key = key;
50 size_t actual_key_size = key_size;
51
52 if (key_size > SHA256_BLOCK_SIZE) {
53 key_hash = detail::sha256::sha256(key, key_size);
54 actual_key = key_hash.data();
55 actual_key_size = SHA256_HASH_SIZE;
56 }
57
58 uint8_t k_pad[SHA256_BLOCK_SIZE] = {};
59 std::memcpy(k_pad, actual_key, actual_key_size);
60
61 // Inner: H((K ^ ipad) || data)
62 uint8_t inner_buf[SHA256_BLOCK_SIZE];
63 for (size_t i = 0; i < SHA256_BLOCK_SIZE; ++i) {
64 inner_buf[i] = k_pad[i] ^ 0x36;
65 }
66
67 std::vector<uint8_t> inner_msg;
68 inner_msg.reserve(SHA256_BLOCK_SIZE + data_size);
69 inner_msg.insert(inner_msg.end(), inner_buf, inner_buf + SHA256_BLOCK_SIZE);
70 inner_msg.insert(inner_msg.end(), data, data + data_size);
71 auto inner_hash = detail::sha256::sha256(inner_msg.data(), inner_msg.size());
72
73 // Outer: H((K ^ opad) || inner_hash)
74 uint8_t outer_buf[SHA256_BLOCK_SIZE];
75 for (size_t i = 0; i < SHA256_BLOCK_SIZE; ++i) {
76 outer_buf[i] = k_pad[i] ^ 0x5c;
77 }
78
79 std::vector<uint8_t> outer_msg;
80 outer_msg.reserve(SHA256_BLOCK_SIZE + SHA256_HASH_SIZE);
81 outer_msg.insert(outer_msg.end(), outer_buf, outer_buf + SHA256_BLOCK_SIZE);
82 outer_msg.insert(outer_msg.end(), inner_hash.begin(), inner_hash.end());
83 auto result = detail::sha256::sha256(outer_msg.data(), outer_msg.size());
84
85 // Zero intermediate key material (CWE-316: cleartext storage in memory)
86 volatile uint8_t* vp = k_pad;
87 for (size_t i = 0; i < SHA256_BLOCK_SIZE; ++i) vp[i] = 0;
88 vp = inner_buf;
89 for (size_t i = 0; i < SHA256_BLOCK_SIZE; ++i) vp[i] = 0;
90 vp = outer_buf;
91 for (size_t i = 0; i < SHA256_BLOCK_SIZE; ++i) vp[i] = 0;
92
93 return result;
94}
95
96} // namespace detail::hkdf
97
107[[nodiscard]] inline std::array<uint8_t, 32> hkdf_extract(
108 const uint8_t* salt, size_t salt_size,
109 const uint8_t* ikm, size_t ikm_size) {
110
111 if (salt == nullptr || salt_size == 0) {
112 uint8_t zero_salt[32] = {};
113 return detail::hkdf::hmac_sha256(zero_salt, 32, ikm, ikm_size);
114 }
115 return detail::hkdf::hmac_sha256(salt, salt_size, ikm, ikm_size);
116}
117
126[[nodiscard]] inline bool hkdf_expand(
127 const std::array<uint8_t, 32>& prk,
128 const uint8_t* info, size_t info_size,
129 uint8_t* output, size_t output_size) {
130
131 static constexpr size_t HASH_LEN = 32;
132 if (output_size > 255 * HASH_LEN) return false;
133
134 size_t n = (output_size + HASH_LEN - 1) / HASH_LEN;
135 std::array<uint8_t, 32> t_prev{};
136 size_t t_prev_size = 0;
137 size_t offset = 0;
138
139 for (size_t i = 1; i <= n; ++i) {
140 std::vector<uint8_t> msg;
141 msg.reserve(t_prev_size + info_size + 1);
142 msg.insert(msg.end(), t_prev.data(), t_prev.data() + t_prev_size);
143 if (info != nullptr && info_size > 0) {
144 msg.insert(msg.end(), info, info + info_size);
145 }
146 msg.push_back(static_cast<uint8_t>(i));
147
148 auto t_i = detail::hkdf::hmac_sha256(prk.data(), HASH_LEN, msg.data(), msg.size());
149
150 size_t copy_len = (std::min)(HASH_LEN, output_size - offset);
151 std::memcpy(output + offset, t_i.data(), copy_len);
152 offset += copy_len;
153
154 t_prev = t_i;
155 t_prev_size = HASH_LEN;
156 }
157
158 return true;
159}
160
162[[nodiscard]] inline bool hkdf(
163 const uint8_t* salt, size_t salt_size,
164 const uint8_t* ikm, size_t ikm_size,
165 const uint8_t* info, size_t info_size,
166 uint8_t* output, size_t output_size) {
167
168 auto prk = hkdf_extract(salt, salt_size, ikm, ikm_size);
169 return hkdf_expand(prk, info, info_size, output, output_size);
170}
171
172} // namespace signet::forge::crypto
std::array< uint8_t, 32 > hmac_sha256(const uint8_t *key, size_t key_size, const uint8_t *data, size_t data_size)
HMAC-SHA256 (RFC 2104): keyed hash for HKDF.
Definition hkdf.hpp:44
std::array< uint8_t, 32 > sha256(const uint8_t *data, size_t size)
Compute SHA-256 hash of arbitrary-length input.
Definition sha256.hpp:165
bool hkdf(const uint8_t *salt, size_t salt_size, const uint8_t *ikm, size_t ikm_size, const uint8_t *info, size_t info_size, uint8_t *output, size_t output_size)
HKDF one-shot (RFC 5869): Extract-then-Expand in one call.
Definition hkdf.hpp:162
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.
Definition hkdf.hpp:107
bool hkdf_expand(const std::array< uint8_t, 32 > &prk, const uint8_t *info, size_t info_size, uint8_t *output, size_t output_size)
HKDF-Expand (RFC 5869 §2.3): Expand PRK to output keying material.
Definition hkdf.hpp:126
SHA-256 hash function (NIST FIPS 180-4).