TheAlgorithms/C++ 1.0.0
All the algorithms implemented in C++
Loading...
Searching...
No Matches
sha256.cpp
Go to the documentation of this file.
1
15#include <array>
16#include <cassert>
17#include <cstdint>
18#include <iomanip>
19#include <iostream>
20#include <sstream>
21#include <utility>
22#include <vector>
23
28namespace hashing {
34namespace sha256 {
40class Hash {
41 // Initialize array of hash values with first 32 bits of the fractional
42 // parts of the square roots of the first 8 primes 2..19
43 std::array<uint32_t, 8> hash = {0x6A09E667, 0xBB67AE85, 0x3C6EF372,
44 0xA54FF53A, 0x510E527F, 0x9B05688C,
45 0x1F83D9AB, 0x5BE0CD19};
46
47 public:
48 void update(const std::array<uint32_t, 64> &blocks);
49 std::string to_string() const;
50};
51
58uint32_t right_rotate(uint32_t n, size_t rotate) {
59 return (n >> rotate) | (n << (32 - rotate));
60}
61
67void Hash::update(const std::array<uint32_t, 64> &blocks) {
68 // Initialize array of round constants with first 32 bits of the fractional
69 // parts of the cube roots of the first 64 primes 2..311
70 const std::array<uint32_t, 64> round_constants = {
71 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1,
72 0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
73 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 0xE49B69C1, 0xEFBE4786,
74 0x0FC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
75 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147,
76 0x06CA6351, 0x14292967, 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
77 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 0xA2BFE8A1, 0xA81A664B,
78 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
79 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A,
80 0x5B9CCA4F, 0x682E6FF3, 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
81 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2};
82
83 // Initialize working variables
84 auto a = hash[0];
85 auto b = hash[1];
86 auto c = hash[2];
87 auto d = hash[3];
88 auto e = hash[4];
89 auto f = hash[5];
90 auto g = hash[6];
91 auto h = hash[7];
92
93 // Compression function main loop
94 for (size_t block_num = 0; block_num < 64; ++block_num) {
95 const auto s1 =
96 right_rotate(e, 6) ^ right_rotate(e, 11) ^ right_rotate(e, 25);
97 const auto ch = (e & f) ^ (~e & g);
98 const auto temp1 =
99 h + s1 + ch + round_constants[block_num] + blocks[block_num];
100 const auto s0 =
101 right_rotate(a, 2) ^ right_rotate(a, 13) ^ right_rotate(a, 22);
102 const auto maj = (a & b) ^ (a & c) ^ (b & c);
103 const auto temp2 = s0 + maj;
104
105 h = g;
106 g = f;
107 f = e;
108 e = d + temp1;
109 d = c;
110 c = b;
111 b = a;
112 a = temp1 + temp2;
113 }
114
115 // Update hash values
116 hash[0] += a;
117 hash[1] += b;
118 hash[2] += c;
119 hash[3] += d;
120 hash[4] += e;
121 hash[5] += f;
122 hash[6] += g;
123 hash[7] += h;
124}
125
130std::string Hash::to_string() const {
131 std::stringstream ss;
132 for (size_t i = 0; i < 8; ++i) {
133 ss << std::hex << std::setfill('0') << std::setw(8) << hash[i];
134 }
135 return ss.str();
136}
137
143std::size_t compute_padded_size(const std::size_t input_size) {
144 if (input_size % 64 < 56) {
145 return input_size + 64 - (input_size % 64);
146 }
147 return input_size + 128 - (input_size % 64);
148}
149
156template <typename T>
157uint8_t extract_byte(const T in_value, const std::size_t byte_num) {
158 if (sizeof(in_value) <= byte_num) {
159 throw std::out_of_range("Byte at index byte_num does not exist");
160 }
161 return (in_value >> (byte_num * 8)) & 0xFF;
162}
163
170char get_char(const std::string &input, std::size_t pos) {
171 const auto input_size = input.length();
172 if (pos < input_size) {
173 return input[pos];
174 }
175 if (pos == input_size) {
176 return '\x80';
177 }
178 const auto padded_input_size = compute_padded_size(input_size);
179 if (pos < padded_input_size - 8) {
180 return '\x00';
181 }
182 if (padded_input_size <= pos) {
183 throw std::out_of_range("pos is out of range");
184 }
185 return static_cast<char>(
186 extract_byte<size_t>(input_size * 8, padded_input_size - pos - 1));
187}
188
195std::array<uint32_t, 64> create_message_schedule_array(const std::string &input,
196 const size_t byte_num) {
197 std::array<uint32_t, 64> blocks{};
198
199 // Copy chunk into first 16 words of the message schedule array
200 for (size_t block_num = 0; block_num < 16; ++block_num) {
201 blocks[block_num] =
202 (static_cast<uint8_t>(get_char(input, byte_num + block_num * 4))
203 << 24) |
204 (static_cast<uint8_t>(get_char(input, byte_num + block_num * 4 + 1))
205 << 16) |
206 (static_cast<uint8_t>(get_char(input, byte_num + block_num * 4 + 2))
207 << 8) |
208 static_cast<uint8_t>(get_char(input, byte_num + block_num * 4 + 3));
209 }
210
211 // Extend the first 16 words into remaining 48 words of the message schedule
212 // array
213 for (size_t block_num = 16; block_num < 64; ++block_num) {
214 const auto s0 = right_rotate(blocks[block_num - 15], 7) ^
215 right_rotate(blocks[block_num - 15], 18) ^
216 (blocks[block_num - 15] >> 3);
217 const auto s1 = right_rotate(blocks[block_num - 2], 17) ^
218 right_rotate(blocks[block_num - 2], 19) ^
219 (blocks[block_num - 2] >> 10);
220 blocks[block_num] =
221 blocks[block_num - 16] + s0 + blocks[block_num - 7] + s1;
222 }
223
224 return blocks;
225}
226
232std::string sha256(const std::string &input) {
233 Hash h;
234 // Process message in successive 512-bit (64-byte) chunks
235 for (size_t byte_num = 0; byte_num < compute_padded_size(input.length());
236 byte_num += 64) {
237 h.update(create_message_schedule_array(input, byte_num));
238 }
239 return h.to_string();
240}
241} // namespace sha256
242} // namespace hashing
243
249 assert(hashing::sha256::compute_padded_size(55) == 64);
250 assert(hashing::sha256::compute_padded_size(56) == 128);
251 assert(hashing::sha256::compute_padded_size(130) == 192);
252}
253
254static void test_extract_byte() {
255 assert(hashing::sha256::extract_byte<uint32_t>(512, 0) == 0);
256 assert(hashing::sha256::extract_byte<uint32_t>(512, 1) == 2);
257 bool exception = false;
258 try {
259 hashing::sha256::extract_byte<uint32_t>(512, 5);
260 } catch (const std::out_of_range &) {
261 exception = true;
262 }
263 assert(exception);
264}
265
266static void test_get_char() {
267 assert(hashing::sha256::get_char("test", 3) == 't');
268 assert(hashing::sha256::get_char("test", 4) == '\x80');
269 assert(hashing::sha256::get_char("test", 5) == '\x00');
270 assert(hashing::sha256::get_char("test", 63) == 32);
271 bool exception = false;
272 try {
273 hashing::sha256::get_char("test", 64);
274 } catch (const std::out_of_range &) {
275 exception = true;
276 }
277 assert(exception);
278}
279
280static void test_right_rotate() {
281 assert(hashing::sha256::right_rotate(128, 3) == 16);
282 assert(hashing::sha256::right_rotate(1, 30) == 4);
283 assert(hashing::sha256::right_rotate(6, 30) == 24);
284}
285
286static void test_sha256() {
287 struct TestCase {
288 const std::string input;
289 const std::string expected_hash;
290 TestCase(std::string input, std::string expected_hash)
291 : input(std::move(input)),
292 expected_hash(std::move(expected_hash)) {}
293 };
294 const std::vector<TestCase> test_cases{
295 TestCase(
296 "",
297 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"),
298 TestCase(
299 "test",
300 "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"),
301 TestCase(
302 "Hello World",
303 "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e"),
304 TestCase("Hello World!",
305 "7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9"
306 "069")};
307 for (const auto &tc : test_cases) {
308 assert(hashing::sha256::sha256(tc.input) == tc.expected_hash);
309 }
310}
311
312static void test() {
314 test_extract_byte();
315 test_get_char();
316 test_right_rotate();
317 test_sha256();
318
319 std::cout << "All tests have successfully passed!\n";
320}
321
326int main() {
327 test(); // Run self-test implementations
328 return 0;
329}
void test()
Contains hash array and functions to update it and convert it to a hexadecimal string.
Definition sha256.cpp:40
void update(const std::array< uint32_t, 64 > &blocks)
Updates the hash array.
Definition sha256.cpp:67
std::string to_string() const
Convert the hash to a hexadecimal string.
Definition sha256.cpp:130
int h(int key)
Used for assert.
std::size_t compute_padded_size(const std::size_t input_size)
Computes size of the padded input.
Definition sha256.cpp:143
std::array< uint32_t, 64 > create_message_schedule_array(const std::string &input, const size_t byte_num)
Creates the message schedule array.
Definition sha256.cpp:195
std::string sha256(const std::string &input)
Computes the final hash value.
Definition sha256.cpp:232
char get_char(const std::string &input, std::size_t pos)
Returns the character at pos after the input is padded.
Definition sha256.cpp:170
uint32_t right_rotate(uint32_t n, size_t rotate)
Rotates the bits of a 32-bit unsigned integer.
Definition sha256.cpp:58
int main()
Main function.
Definition sha256.cpp:326
static void test_compute_padded_size()
Self-test implementations.
Definition sha256.cpp:248
uint8_t extract_byte(const T in_value, const std::size_t byte_num)
Returns the byte at position byte_num in in_value.
Definition sha256.cpp:157
represents single example inputs and expected output of the function longest_common_string_length