TheAlgorithms/C++ 1.0.0
All the algorithms implemented in C++
Loading...
Searching...
No Matches
sha1.cpp
Go to the documentation of this file.
1
32#include <algorithm>
33#include <array>
34#include <cassert>
35#include <cstdint>
36#include <cstring>
37#include <iostream>
38#include <string>
39#include <vector>
40
45namespace hashing {
51namespace sha1 {
58uint32_t leftRotate32bits(uint32_t n, std::size_t rotate) {
59 return (n << rotate) | (n >> (32 - rotate));
60}
61
67std::string sig2hex(void* sig) {
68 const char* hexChars = "0123456789abcdef";
69 auto* intsig = static_cast<uint8_t*>(sig);
70 std::string hex = "";
71 for (uint8_t i = 0; i < 20; i++) {
72 hex.push_back(hexChars[(intsig[i] >> 4) & 0xF]);
73 hex.push_back(hexChars[(intsig[i]) & 0xF]);
74 }
75 return hex;
76}
77
84void* hash_bs(const void* input_bs, uint64_t input_size) {
85 auto* input = static_cast<const uint8_t*>(input_bs);
86
87 // Step 0: The initial 160-bit state
88 uint32_t h0 = 0x67452301, a = 0;
89 uint32_t h1 = 0xEFCDAB89, b = 0;
90 uint32_t h2 = 0x98BADCFE, c = 0;
91 uint32_t h3 = 0x10325476, d = 0;
92 uint32_t h4 = 0xC3D2E1F0, e = 0;
93
94 // Step 1: Processing the bytestring
95 // First compute the size the padded message will have
96 // so it is possible to allocate the right amount of memory
97 uint64_t padded_message_size = 0;
98 if (input_size % 64 < 56) {
99 padded_message_size = input_size + 64 - (input_size % 64);
100 } else {
101 padded_message_size = input_size + 128 - (input_size % 64);
102 }
103
104 // Allocate the memory for the padded message
105 std::vector<uint8_t> padded_message(padded_message_size);
106
107 // Beginning of the padded message is the original message
108 std::copy(input, input + input_size, padded_message.begin());
109
110 // Afterwards comes a single 1 bit and then only zeroes
111 padded_message[input_size] = 1 << 7; // 10000000
112 for (uint64_t i = input_size; i % 64 != 56; i++) {
113 if (i == input_size) {
114 continue; // pass first iteration
115 }
116 padded_message[i] = 0;
117 }
118
119 // We then have to add the 64-bit size of the message in bits (hence the
120 // times 8) in the last 8 bytes
121 uint64_t input_bitsize = input_size * 8;
122 for (uint8_t i = 0; i < 8; i++) {
123 padded_message[padded_message_size - 8 + i] =
124 (input_bitsize >> (56 - 8 * i)) & 0xFF;
125 }
126
127 // Already allocate memory for blocks
128 std::array<uint32_t, 80> blocks{};
129
130 // Rounds
131 for (uint64_t chunk = 0; chunk * 64 < padded_message_size; chunk++) {
132 // First, build 16 32-bits blocks from the chunk
133 for (uint8_t bid = 0; bid < 16; bid++) {
134 blocks[bid] = 0;
135
136 // Having to build a 32-bit word from 4-bit words
137 // Add each and shift them to the left
138 for (uint8_t cid = 0; cid < 4; cid++) {
139 blocks[bid] = (blocks[bid] << 8) +
140 padded_message[chunk * 64 + bid * 4 + cid];
141 }
142
143 // Extend the 16 32-bit words into 80 32-bit words
144 for (uint8_t i = 16; i < 80; i++) {
145 blocks[i] =
146 leftRotate32bits(blocks[i - 3] ^ blocks[i - 8] ^
147 blocks[i - 14] ^ blocks[i - 16],
148 1);
149 }
150 }
151
152 a = h0;
153 b = h1;
154 c = h2;
155 d = h3;
156 e = h4;
157
158 // Main "hashing" loop
159 for (uint8_t i = 0; i < 80; i++) {
160 uint32_t F = 0, g = 0;
161 if (i < 20) {
162 F = (b & c) | ((~b) & d);
163 g = 0x5A827999;
164 } else if (i < 40) {
165 F = b ^ c ^ d;
166 g = 0x6ED9EBA1;
167 } else if (i < 60) {
168 F = (b & c) | (b & d) | (c & d);
169 g = 0x8F1BBCDC;
170 } else {
171 F = b ^ c ^ d;
172 g = 0xCA62C1D6;
173 }
174
175 // Update the accumulators
176 uint32_t temp = leftRotate32bits(a, 5) + F + e + g + blocks[i];
177 e = d;
178 d = c;
179 c = leftRotate32bits(b, 30);
180 b = a;
181 a = temp;
182 }
183 // Update the state with this chunk's hash
184 h0 += a;
185 h1 += b;
186 h2 += c;
187 h3 += d;
188 h4 += e;
189 }
190
191 // Build signature from state
192 // Note, any type could be used for the signature
193 // uint8_t was used to make the 20 bytes obvious
194 auto* sig = new uint8_t[20];
195 for (uint8_t i = 0; i < 4; i++) {
196 sig[i] = (h0 >> (24 - 8 * i)) & 0xFF;
197 sig[i + 4] = (h1 >> (24 - 8 * i)) & 0xFF;
198 sig[i + 8] = (h2 >> (24 - 8 * i)) & 0xFF;
199 sig[i + 12] = (h3 >> (24 - 8 * i)) & 0xFF;
200 sig[i + 16] = (h4 >> (24 - 8 * i)) & 0xFF;
201 }
202
203 return sig;
204}
205
211void* hash(const std::string& message) {
212 return hash_bs(&message[0], message.size());
213}
214} // namespace sha1
215} // namespace hashing
216
221static void test() {
222 // Hashes empty string and stores signature
223 void* sig = hashing::sha1::hash("");
224 std::cout << "Hashing empty string" << std::endl;
225 // Prints signature hex representation
226 std::cout << hashing::sha1::sig2hex(sig) << std::endl << std::endl;
227 // Test with cassert wether sig is correct from expected value
228 assert(hashing::sha1::sig2hex(sig).compare(
229 "da39a3ee5e6b4b0d3255bfef95601890afd80709") == 0);
230
231 // Hashes "The quick brown fox jumps over the lazy dog" and stores signature
232 void* sig2 =
233 hashing::sha1::hash("The quick brown fox jumps over the lazy dog");
234 std::cout << "Hashing The quick brown fox jumps over the lazy dog"
235 << std::endl;
236 // Prints signature hex representation
237 std::cout << hashing::sha1::sig2hex(sig2) << std::endl << std::endl;
238 // Test with cassert wether sig is correct from expected value
239 assert(hashing::sha1::sig2hex(sig2).compare(
240 "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12") == 0);
241
242 // Hashes "The quick brown fox jumps over the lazy dog." (notice the
243 // additional period) and stores signature
244 void* sig3 =
245 hashing::sha1::hash("The quick brown fox jumps over the lazy dog.");
246 std::cout << "Hashing "
247 "The quick brown fox jumps over the lazy dog."
248 << std::endl;
249 // Prints signature hex representation
250 std::cout << hashing::sha1::sig2hex(sig3) << std::endl << std::endl;
251 // Test with cassert wether sig is correct from expected value
252 assert(hashing::sha1::sig2hex(sig3).compare(
253 "408d94384216f890ff7a0c3528e8bed1e0b01621") == 0);
254
255 // Hashes "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
256 // and stores signature
257 void* sig4 = hashing::sha1::hash(
258 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
259 std::cout
260 << "Hashing "
261 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
262 << std::endl;
263 // Prints signature hex representation
264 std::cout << hashing::sha1::sig2hex(sig4) << std::endl << std::endl;
265 // Test with cassert wether sig is correct from expected value
266 assert(hashing::sha1::sig2hex(sig4).compare(
267 "761c457bf73b14d27e9e9265c46f4b4dda11f940") == 0);
268}
269
275static void interactive() {
276 while (true) {
277 std::string input;
278 std::cout << "Enter a message to be hashed (Ctrl-C to exit): "
279 << std::endl;
280 std::getline(std::cin, input);
281 void* sig = hashing::sha1::hash(input);
282 std::cout << "Hash is: " << hashing::sha1::sig2hex(sig) << std::endl;
283
284 while (true) {
285 std::cout << "Want to enter another message? (y/n) ";
286 std::getline(std::cin, input);
287 if (input.compare("y") == 0) {
288 break;
289 } else if (input.compare("n") == 0) {
290 return;
291 }
292 }
293 }
294}
295
300int main() {
301 test(); // run self-test implementations
302
303 // Launch interactive mode where user can input messages and see
304 // their hash
305 interactive();
306 return 0;
307}
Used for assert.
static void test()
Self-test implementations of well-known SHA-1 hashes.
Definition sha1.cpp:221
static void interactive()
Puts user in a loop where inputs can be given and SHA-1 hash will be computed and printed.
Definition sha1.cpp:275
int main()
Main function.
Definition sha1.cpp:300
int compare(const void *a, const void *b)