Signet Forge 0.1.0
C++20 Parquet library with AI-native extensions
DEMO
Loading...
Searching...
No Matches
sha256.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
14
15#include <array>
16#include <cstddef>
17#include <cstdint>
18#include <cstring>
19#include <vector>
20
21namespace signet::forge::crypto {
22namespace detail::sha256 {
23
24// ===========================================================================
25// SHA-256 (FIPS 180-4)
26// ===========================================================================
27
31static constexpr uint32_t H0[8] = {
32 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
33 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
34};
35
39static constexpr uint32_t K[64] = {
40 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
41 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
42 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
43 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
44 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
45 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
46 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
47 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
48 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
49 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
50 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
51 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
52 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
53 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
54 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
55 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
56};
57
59inline constexpr uint32_t rotr(uint32_t x, int n) {
60 return (x >> n) | (x << (32 - n));
61}
62
64inline constexpr uint32_t Ch(uint32_t x, uint32_t y, uint32_t z) {
65 return (x & y) ^ (~x & z);
66}
67
68inline constexpr uint32_t Maj(uint32_t x, uint32_t y, uint32_t z) {
69 return (x & y) ^ (x & z) ^ (y & z);
70}
71
72inline constexpr uint32_t Sigma0(uint32_t x) {
73 return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22);
74}
75
76inline constexpr uint32_t Sigma1(uint32_t x) {
77 return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25);
78}
79
80inline constexpr uint32_t sigma0(uint32_t x) {
81 return rotr(x, 7) ^ rotr(x, 18) ^ (x >> 3);
82}
83
84inline constexpr uint32_t sigma1(uint32_t x) {
85 return rotr(x, 17) ^ rotr(x, 19) ^ (x >> 10);
86}
87
89inline uint32_t load_be32(const uint8_t* p) {
90 return (static_cast<uint32_t>(p[0]) << 24)
91 | (static_cast<uint32_t>(p[1]) << 16)
92 | (static_cast<uint32_t>(p[2]) << 8)
93 | (static_cast<uint32_t>(p[3]));
94}
95
97inline void store_be32(uint8_t* p, uint32_t v) {
98 p[0] = static_cast<uint8_t>(v >> 24);
99 p[1] = static_cast<uint8_t>(v >> 16);
100 p[2] = static_cast<uint8_t>(v >> 8);
101 p[3] = static_cast<uint8_t>(v);
102}
103
105inline void store_be64(uint8_t* p, uint64_t v) {
106 p[0] = static_cast<uint8_t>(v >> 56);
107 p[1] = static_cast<uint8_t>(v >> 48);
108 p[2] = static_cast<uint8_t>(v >> 40);
109 p[3] = static_cast<uint8_t>(v >> 32);
110 p[4] = static_cast<uint8_t>(v >> 24);
111 p[5] = static_cast<uint8_t>(v >> 16);
112 p[6] = static_cast<uint8_t>(v >> 8);
113 p[7] = static_cast<uint8_t>(v);
114}
115
120inline void compress(uint32_t h[8], const uint8_t block[64]) {
121 // Step 1: Prepare the message schedule W[0..63]
122 uint32_t W[64];
123
124 // W[0..15] = the sixteen 32-bit words from the message block
125 for (int t = 0; t < 16; ++t) {
126 W[t] = load_be32(block + t * 4);
127 }
128
129 // W[16..63] = derived from earlier words
130 for (int t = 16; t < 64; ++t) {
131 W[t] = sigma1(W[t - 2]) + W[t - 7] + sigma0(W[t - 15]) + W[t - 16];
132 }
133
134 // Step 2: Initialize working variables
135 uint32_t a = h[0], b = h[1], c = h[2], d = h[3];
136 uint32_t e = h[4], f = h[5], g = h[6], hh = h[7];
137
138 // Step 3: 64 rounds of compression
139 for (int t = 0; t < 64; ++t) {
140 uint32_t T1 = hh + Sigma1(e) + Ch(e, f, g) + K[t] + W[t];
141 uint32_t T2 = Sigma0(a) + Maj(a, b, c);
142 hh = g;
143 g = f;
144 f = e;
145 e = d + T1;
146 d = c;
147 c = b;
148 b = a;
149 a = T1 + T2;
150 }
151
152 // Step 4: Update hash state
153 h[0] += a; h[1] += b; h[2] += c; h[3] += d;
154 h[4] += e; h[5] += f; h[6] += g; h[7] += hh;
155}
156
165inline std::array<uint8_t, 32> sha256(const uint8_t* data, size_t size) {
166 // Initialize hash state
167 uint32_t h[8];
168 std::memcpy(h, H0, sizeof(h));
169
170 // Process complete 64-byte blocks
171 size_t full_blocks = size / 64;
172 for (size_t i = 0; i < full_blocks; ++i) {
173 compress(h, data + i * 64);
174 }
175
176 // Pad the final block(s)
177 // Padding format: message || 1-bit || 0*-bits || 64-bit big-endian length
178 size_t remainder = size % 64;
179 uint8_t final_block[128] = {}; // At most 2 blocks needed
180
181 // Copy remaining bytes
182 if (remainder > 0)
183 std::memcpy(final_block, data + full_blocks * 64, remainder);
184
185 // Append the 1-bit (0x80 byte)
186 final_block[remainder] = 0x80;
187
188 // Determine how many final blocks we need
189 size_t final_blocks;
190 if (remainder < 56) {
191 // Length fits in the same block (bytes 56..63)
192 final_blocks = 1;
193 store_be64(final_block + 56, static_cast<uint64_t>(size) * 8);
194 } else {
195 // Need a second block for the length
196 final_blocks = 2;
197 store_be64(final_block + 120, static_cast<uint64_t>(size) * 8);
198 }
199
200 // Process final block(s)
201 for (size_t i = 0; i < final_blocks; ++i) {
202 compress(h, final_block + i * 64);
203 }
204
205 // Output as big-endian bytes
206 std::array<uint8_t, 32> digest;
207 for (int i = 0; i < 8; ++i) {
208 store_be32(digest.data() + i * 4, h[i]);
209 }
210
211 return digest;
212}
213
215inline std::array<uint8_t, 32> sha256(const std::vector<uint8_t>& data) {
216 return sha256(data.data(), data.size());
217}
218
226inline std::array<uint8_t, 32> sha256_concat(
227 const uint8_t* a, size_t a_size,
228 const uint8_t* b, size_t b_size) {
229
230 static constexpr uint8_t LABEL[] = "signet-forge-hybrid-kem-v1";
231 std::vector<uint8_t> combined;
232 combined.reserve(sizeof(LABEL) - 1 + a_size + b_size);
233 combined.insert(combined.end(), LABEL, LABEL + sizeof(LABEL) - 1);
234 combined.insert(combined.end(), a, a + a_size);
235 combined.insert(combined.end(), b, b + b_size);
236 return sha256(combined.data(), combined.size());
237}
238
239} // namespace detail::sha256
240} // namespace signet::forge::crypto
void compress(uint32_t h[8], const uint8_t block[64])
Process a single 64-byte (512-bit) message block.
Definition sha256.hpp:120
constexpr uint32_t Maj(uint32_t x, uint32_t y, uint32_t z)
Definition sha256.hpp:68
constexpr uint32_t sigma1(uint32_t x)
Definition sha256.hpp:84
constexpr uint32_t Ch(uint32_t x, uint32_t y, uint32_t z)
SHA-256 logical functions (FIPS 180-4 Section 4.1.2)
Definition sha256.hpp:64
void store_be32(uint8_t *p, uint32_t v)
Store a uint32 as 4 big-endian bytes.
Definition sha256.hpp:97
constexpr uint32_t Sigma0(uint32_t x)
Definition sha256.hpp:72
void store_be64(uint8_t *p, uint64_t v)
Store a uint64 as 8 big-endian bytes.
Definition sha256.hpp:105
std::array< uint8_t, 32 > sha256_concat(const uint8_t *a, size_t a_size, const uint8_t *b, size_t b_size)
Hash concatenation of two byte spans with domain separation: SHA-256(label || a || b).
Definition sha256.hpp:226
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
constexpr uint32_t rotr(uint32_t x, int n)
Right-rotate a 32-bit word by n bits.
Definition sha256.hpp:59
constexpr uint32_t sigma0(uint32_t x)
Definition sha256.hpp:80
uint32_t load_be32(const uint8_t *p)
Load a big-endian uint32 from 4 bytes.
Definition sha256.hpp:89
constexpr uint32_t Sigma1(uint32_t x)
Definition sha256.hpp:76