Lightweight 0.20260303.0
Loading...
Searching...
No Matches
Sha256.hpp
1// SPDX-License-Identifier: Apache-2.0
2// Simple SHA-256 implementation for checksum verification
3#pragma once
4
5#include <array>
6#include <cstdint>
7#include <cstring>
8#include <iomanip>
9#include <span>
10#include <sstream>
11#include <string>
12
13namespace Lightweight::SqlBackup
14{
15
16/// Simple SHA-256 implementation for backup integrity verification.
17class Sha256
18{
19 public:
20 /// The size of the SHA-256 digest in bytes.
21 static constexpr size_t DigestSize = 32;
22 /// The block size used by the SHA-256 algorithm in bytes.
23 static constexpr size_t BlockSize = 64;
24
25 Sha256()
26 {
27 Reset();
28 }
29
30 /// Resets the hash state to initial values.
31 void Reset()
32 {
33 _state[0] = 0x6a09e667;
34 _state[1] = 0xbb67ae85;
35 _state[2] = 0x3c6ef372;
36 _state[3] = 0xa54ff53a;
37 _state[4] = 0x510e527f;
38 _state[5] = 0x9b05688c;
39 _state[6] = 0x1f83d9ab;
40 _state[7] = 0x5be0cd19;
41 _count = 0;
42 _bufferLen = 0;
43 }
44
45 /// Updates the hash with the given data.
46 void Update(void const* data, size_t len)
47 {
48 auto const* bytes = static_cast<uint8_t const*>(data);
49 _count += len;
50
51 if (_bufferLen > 0)
52 {
53 size_t const toCopy = std::min(BlockSize - _bufferLen, len);
54 std::memcpy(_buffer.data() + _bufferLen, bytes, toCopy);
55 _bufferLen += toCopy;
56 bytes += toCopy;
57 len -= toCopy;
58
59 if (_bufferLen == BlockSize)
60 {
61 ProcessBlock(_buffer.data());
62 _bufferLen = 0;
63 }
64 }
65
66 while (len >= BlockSize)
67 {
68 ProcessBlock(bytes);
69 bytes += BlockSize;
70 len -= BlockSize;
71 }
72
73 if (len > 0)
74 {
75 std::memcpy(_buffer.data(), bytes, len);
76 _bufferLen = len;
77 }
78 }
79
80 /// Updates the hash with the given data span.
81 void Update(std::span<uint8_t const> data)
82 {
83 Update(data.data(), data.size());
84 }
85
86 /// Updates the hash with the given string data.
87 void Update(std::string_view data)
88 {
89 Update(data.data(), data.size());
90 }
91
92 /// Finalizes the hash computation and returns the digest.
93 std::array<uint8_t, DigestSize> Finalize()
94 {
95 uint64_t const bitCount = _count * 8;
96
97 // Padding
98 uint8_t const pad = 0x80;
99 Update(&pad, 1);
100
101 while (_bufferLen != 56)
102 {
103 uint8_t const zero = 0;
104 Update(&zero, 1);
105 }
106
107 // Append bit count (big-endian)
108 std::array<uint8_t, 8> countBytes {};
109 for (size_t i = 0; i < 8; ++i)
110 countBytes[i] = static_cast<uint8_t>(bitCount >> (56 - i * 8));
111 Update(countBytes.data(), 8);
112
113 // Output hash
114 std::array<uint8_t, DigestSize> digest {};
115 for (size_t i = 0; i < 8; ++i)
116 {
117 digest[(i * 4) + 0] = static_cast<uint8_t>(_state[i] >> 24);
118 digest[(i * 4) + 1] = static_cast<uint8_t>(_state[i] >> 16);
119 digest[(i * 4) + 2] = static_cast<uint8_t>(_state[i] >> 8);
120 digest[(i * 4) + 3] = static_cast<uint8_t>(_state[i]);
121 }
122 return digest;
123 }
124
125 /// Converts a digest to its hexadecimal string representation.
126 static std::string ToHex(std::array<uint8_t, DigestSize> const& digest)
127 {
128 std::ostringstream oss;
129 for (auto b: digest)
130 oss << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(b);
131 return oss.str();
132 }
133
134 /// Computes the SHA-256 hash of the given data and returns it as a hex string.
135 static std::string Hash(void const* data, size_t len)
136 {
137 Sha256 hasher;
138 hasher.Update(data, len);
139 return ToHex(hasher.Finalize());
140 }
141
142 /// Computes the SHA-256 hash of the given string and returns it as a hex string.
143 static std::string Hash(std::string_view data)
144 {
145 return Hash(data.data(), data.size());
146 }
147
148 private:
149 static constexpr std::array<uint32_t, 64> K = {
150 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
151 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
152 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
153 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
154 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
155 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
156 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
157 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
158 };
159
160 static uint32_t RotateRight(uint32_t x, int n)
161 {
162 return (x >> n) | (x << (32 - n));
163 }
164
165 static uint32_t Ch(uint32_t x, uint32_t y, uint32_t z)
166 {
167 return (x & y) ^ (~x & z);
168 }
169
170 static uint32_t Maj(uint32_t x, uint32_t y, uint32_t z)
171 {
172 return (x & y) ^ (x & z) ^ (y & z);
173 }
174
175 static uint32_t Sigma0(uint32_t x)
176 {
177 return RotateRight(x, 2) ^ RotateRight(x, 13) ^ RotateRight(x, 22);
178 }
179
180 static uint32_t Sigma1(uint32_t x)
181 {
182 return RotateRight(x, 6) ^ RotateRight(x, 11) ^ RotateRight(x, 25);
183 }
184
185 static uint32_t LowerSigma0(uint32_t x)
186 {
187 return RotateRight(x, 7) ^ RotateRight(x, 18) ^ (x >> 3);
188 }
189
190 static uint32_t LowerSigma1(uint32_t x)
191 {
192 return RotateRight(x, 17) ^ RotateRight(x, 19) ^ (x >> 10);
193 }
194
195 void ProcessBlock(uint8_t const* block)
196 {
197 std::array<uint32_t, 64> W {};
198
199 // Prepare message schedule
200 for (size_t i = 0; i < 16; ++i)
201 {
202 W[i] = (static_cast<uint32_t>(block[i * 4]) << 24) | (static_cast<uint32_t>(block[(i * 4) + 1]) << 16)
203 | (static_cast<uint32_t>(block[(i * 4) + 2]) << 8) | static_cast<uint32_t>(block[(i * 4) + 3]);
204 }
205 for (size_t i = 16; i < 64; ++i)
206 W[i] = LowerSigma1(W[i - 2]) + W[i - 7] + LowerSigma0(W[i - 15]) + W[i - 16];
207
208 // Working variables
209 uint32_t a = _state[0];
210 uint32_t b = _state[1];
211 uint32_t c = _state[2];
212 uint32_t d = _state[3];
213 uint32_t e = _state[4];
214 uint32_t f = _state[5];
215 uint32_t g = _state[6];
216 uint32_t h = _state[7];
217
218 // Compression
219 for (size_t i = 0; i < 64; ++i)
220 {
221 uint32_t const T1 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i];
222 uint32_t const T2 = Sigma0(a) + Maj(a, b, c);
223 h = g;
224 g = f;
225 f = e;
226 e = d + T1;
227 d = c;
228 c = b;
229 b = a;
230 a = T1 + T2;
231 }
232
233 _state[0] += a;
234 _state[1] += b;
235 _state[2] += c;
236 _state[3] += d;
237 _state[4] += e;
238 _state[5] += f;
239 _state[6] += g;
240 _state[7] += h;
241 }
242
243 std::array<uint32_t, 8> _state {};
244 std::array<uint8_t, BlockSize> _buffer {};
245 size_t _bufferLen = 0;
246 uint64_t _count = 0;
247};
248
249} // namespace Lightweight::SqlBackup
Simple SHA-256 implementation for backup integrity verification.
Definition Sha256.hpp:18
void Update(std::string_view data)
Updates the hash with the given string data.
Definition Sha256.hpp:87
static constexpr size_t BlockSize
The block size used by the SHA-256 algorithm in bytes.
Definition Sha256.hpp:23
static std::string Hash(std::string_view data)
Computes the SHA-256 hash of the given string and returns it as a hex string.
Definition Sha256.hpp:143
std::array< uint8_t, DigestSize > Finalize()
Finalizes the hash computation and returns the digest.
Definition Sha256.hpp:93
static std::string Hash(void const *data, size_t len)
Computes the SHA-256 hash of the given data and returns it as a hex string.
Definition Sha256.hpp:135
void Update(void const *data, size_t len)
Updates the hash with the given data.
Definition Sha256.hpp:46
static std::string ToHex(std::array< uint8_t, DigestSize > const &digest)
Converts a digest to its hexadecimal string representation.
Definition Sha256.hpp:126
static constexpr size_t DigestSize
The size of the SHA-256 digest in bytes.
Definition Sha256.hpp:21
void Reset()
Resets the hash state to initial values.
Definition Sha256.hpp:31
void Update(std::span< uint8_t const > data)
Updates the hash with the given data span.
Definition Sha256.hpp:81