TheAlgorithms/C++ 1.0.0
All the algorithms implemented in C++
Loading...
Searching...
No Matches
sha1.cpp File Reference

Simple C++ implementation of the SHA-1 Hashing Algorithm More...

#include <algorithm>
#include <array>
#include <cassert>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <string>
#include <vector>
Include dependency graph for sha1.cpp:

Go to the source code of this file.

Namespaces

namespace  hashing
 Used for assert.
 
namespace  SHA
 Functions for the SHA-1 algorithm implementation.
 

Functions

uint32_t hashing::sha1::leftRotate32bits (uint32_t n, std::size_t rotate)
 Rotates the bits of a 32-bit unsigned integer.
 
std::string hashing::sha1::sig2hex (void *sig)
 Transforms the 160-bit SHA-1 signature into a 40 char hex string.
 
void * hashing::sha1::hash_bs (const void *input_bs, uint64_t input_size)
 The SHA-1 algorithm itself, taking in a bytestring.
 
void * hashing::sha1::hash (const std::string &message)
 Converts the string to bytestring and calls the main algorithm.
 
static void test ()
 Self-test implementations of well-known SHA-1 hashes.
 
static void interactive ()
 Puts user in a loop where inputs can be given and SHA-1 hash will be computed and printed.
 
int main ()
 Main function.
 

Detailed Description

Simple C++ implementation of the SHA-1 Hashing Algorithm

Author
tGautot

SHA-1 is a cryptographic hash function that was developped by the NSA 1995. SHA-1 is not considered secure since around 2010.

Algorithm

The first step of the algorithm is to pad the message for its length to be a multiple of 64 (bytes). This is done by first adding 0x80 (10000000) and then only zeroes until the last 8 bytes must be filled, where then the 64 bit size of the input will be added

Once this is done, the algo breaks down this padded message into 64 bytes chunks. Each chunk is used for one round, a round breaks the chunk into 16 blocks of 4 bytes. These 16 blocks are then extended to 80 blocks using XOR operations on existing blocks (see code for more details). The algorithm will then update its 160-bit state (here represented used 5 32-bits integer) using partial hashes computed using special functions on the blocks previously built. Please take a look at the wikipedia article for more precision on these operations

Note
This is a simple implementation for a byte string but some implmenetations can work on bytestream, messages of unknown length.

Definition in file sha1.cpp.

Function Documentation

◆ hash()

void * hashing::sha1::hash ( const std::string & message)

Converts the string to bytestring and calls the main algorithm.

Parameters
messagePlain character message to hash
Returns
void* Pointer to the SHA-1 signature

Definition at line 211 of file sha1.cpp.

211 {
212 return hash_bs(&message[0], message.size());
213}
void * hash_bs(const void *input_bs, uint64_t input_size)
The MD5 algorithm itself, taking in a bytestring.
Definition md5.cpp:139

◆ hash_bs()

void * hashing::sha1::hash_bs ( const void * input_bs,
uint64_t input_size )

The SHA-1 algorithm itself, taking in a bytestring.

Parameters
input_bsThe bytestring to hash
input_sizeThe size (in BYTES) of the input
Returns
void* Pointer to the 160-bit signature

Definition at line 84 of file sha1.cpp.

84 {
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}
double g(double x)
Another test function.
uint32_t leftRotate32bits(uint32_t n, std::size_t rotate)
Rotates the bits of a 32-bit unsigned integer.
Definition md5.cpp:67

◆ interactive()

static void interactive ( )
static

Puts user in a loop where inputs can be given and SHA-1 hash will be computed and printed.

Returns
void

Definition at line 275 of file sha1.cpp.

275 {
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}
void * hash(const std::string &message)
Converts the string to bytestring and calls the main algorithm.
Definition sha1.cpp:211
std::string sig2hex(void *sig)
Transforms the 160-bit SHA-1 signature into a 40 char hex string.
Definition sha1.cpp:67

◆ leftRotate32bits()

uint32_t hashing::sha1::leftRotate32bits ( uint32_t n,
std::size_t rotate )

Rotates the bits of a 32-bit unsigned integer.

Parameters
nInteger to rotate
rotateHow many bits for the rotation
Returns
uint32_t The rotated integer

Definition at line 58 of file sha1.cpp.

58 {
59 return (n << rotate) | (n >> (32 - rotate));
60}

◆ main()

int main ( void )

Main function.

Returns
0 on exit

Definition at line 300 of file sha1.cpp.

300 {
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}
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

◆ sig2hex()

std::string hashing::sha1::sig2hex ( void * sig)

Transforms the 160-bit SHA-1 signature into a 40 char hex string.

Parameters
sigThe SHA-1 signature (Expected 20 bytes)
Returns
std::string The hex signature

Definition at line 67 of file sha1.cpp.

67 {
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}

◆ test()

static void test ( )
static

Self-test implementations of well-known SHA-1 hashes.

Returns
void

Definition at line 221 of file sha1.cpp.

221 {
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}
int compare(const void *a, const void *b)