Algorithms_in_C++ 1.0.0
Set of 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 <cstring>
#include <iostream>
#include <string>
#include <vector>
Include dependency graph for sha1.cpp:

Namespaces

namespace  hashing
 Hashing algorithms.
 
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.

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
210 {
211 return hash_bs(&message[0], message.size());
212}
void * hash_bs(const void *input_bs, uint64_t input_size)
The MD5 algorithm itself, taking in a bytestring.
Definition md5.cpp:138
T size(T... args)
Here is the call graph for this function:

◆ 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
83 {
84 auto* input = static_cast<const uint8_t*>(input_bs);
85
86 // Step 0: The initial 160-bit state
87 uint32_t h0 = 0x67452301, a = 0;
88 uint32_t h1 = 0xEFCDAB89, b = 0;
89 uint32_t h2 = 0x98BADCFE, c = 0;
90 uint32_t h3 = 0x10325476, d = 0;
91 uint32_t h4 = 0xC3D2E1F0, e = 0;
92
93 // Step 1: Processing the bytestring
94 // First compute the size the padded message will have
95 // so it is possible to allocate the right amount of memory
96 uint64_t padded_message_size = 0;
97 if (input_size % 64 < 56) {
98 padded_message_size = input_size + 64 - (input_size % 64);
99 } else {
100 padded_message_size = input_size + 128 - (input_size % 64);
101 }
102
103 // Allocate the memory for the padded message
104 std::vector<uint8_t> padded_message(padded_message_size);
105
106 // Beginning of the padded message is the original message
107 std::copy(input, input + input_size, padded_message.begin());
108
109 // Afterwards comes a single 1 bit and then only zeroes
110 padded_message[input_size] = 1 << 7; // 10000000
111 for (uint64_t i = input_size; i % 64 != 56; i++) {
112 if (i == input_size) {
113 continue; // pass first iteration
114 }
115 padded_message[i] = 0;
116 }
117
118 // We then have to add the 64-bit size of the message in bits (hence the
119 // times 8) in the last 8 bytes
120 uint64_t input_bitsize = input_size * 8;
121 for (uint8_t i = 0; i < 8; i++) {
122 padded_message[padded_message_size - 8 + i] =
123 (input_bitsize >> (56 - 8 * i)) & 0xFF;
124 }
125
126 // Already allocate memory for blocks
128
129 // Rounds
130 for (uint64_t chunk = 0; chunk * 64 < padded_message_size; chunk++) {
131 // First, build 16 32-bits blocks from the chunk
132 for (uint8_t bid = 0; bid < 16; bid++) {
133 blocks[bid] = 0;
134
135 // Having to build a 32-bit word from 4-bit words
136 // Add each and shift them to the left
137 for (uint8_t cid = 0; cid < 4; cid++) {
138 blocks[bid] = (blocks[bid] << 8) +
139 padded_message[chunk * 64 + bid * 4 + cid];
140 }
141
142 // Extend the 16 32-bit words into 80 32-bit words
143 for (uint8_t i = 16; i < 80; i++) {
144 blocks[i] =
145 leftRotate32bits(blocks[i - 3] ^ blocks[i - 8] ^
146 blocks[i - 14] ^ blocks[i - 16],
147 1);
148 }
149 }
150
151 a = h0;
152 b = h1;
153 c = h2;
154 d = h3;
155 e = h4;
156
157 // Main "hashing" loop
158 for (uint8_t i = 0; i < 80; i++) {
159 uint32_t F = 0, g = 0;
160 if (i < 20) {
161 F = (b & c) | ((~b) & d);
162 g = 0x5A827999;
163 } else if (i < 40) {
164 F = b ^ c ^ d;
165 g = 0x6ED9EBA1;
166 } else if (i < 60) {
167 F = (b & c) | (b & d) | (c & d);
168 g = 0x8F1BBCDC;
169 } else {
170 F = b ^ c ^ d;
171 g = 0xCA62C1D6;
172 }
173
174 // Update the accumulators
175 uint32_t temp = leftRotate32bits(a, 5) + F + e + g + blocks[i];
176 e = d;
177 d = c;
178 c = leftRotate32bits(b, 30);
179 b = a;
180 a = temp;
181 }
182 // Update the state with this chunk's hash
183 h0 += a;
184 h1 += b;
185 h2 += c;
186 h3 += d;
187 h4 += e;
188 }
189
190 // Build signature from state
191 // Note, any type could be used for the signature
192 // uint8_t was used to make the 20 bytes obvious
193 auto* sig = new uint8_t[20];
194 for (uint8_t i = 0; i < 4; i++) {
195 sig[i] = (h0 >> (24 - 8 * i)) & 0xFF;
196 sig[i + 4] = (h1 >> (24 - 8 * i)) & 0xFF;
197 sig[i + 8] = (h2 >> (24 - 8 * i)) & 0xFF;
198 sig[i + 12] = (h3 >> (24 - 8 * i)) & 0xFF;
199 sig[i + 16] = (h4 >> (24 - 8 * i)) & 0xFF;
200 }
201
202 return sig;
203}
double g(double x)
Another test function.
Definition composite_simpson_rule.cpp:115
T copy(T... args)
uint32_t leftRotate32bits(uint32_t n, std::size_t rotate)
Rotates the bits of a 32-bit unsigned integer.
Definition md5.cpp:66
Here is the call graph for this function:

◆ 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
274 {
275 while (true) {
276 std::string input;
277 std::cout << "Enter a message to be hashed (Ctrl-C to exit): "
278 << std::endl;
279 std::getline(std::cin, input);
280 void* sig = hashing::sha1::hash(input);
281 std::cout << "Hash is: " << hashing::sha1::sig2hex(sig) << std::endl;
282
283 while (true) {
284 std::cout << "Want to enter another message? (y/n) ";
285 std::getline(std::cin, input);
286 if (input.compare("y") == 0) {
287 break;
288 } else if (input.compare("n") == 0) {
289 return;
290 }
291 }
292 }
293}
T compare(T... args)
T endl(T... args)
T getline(T... args)
void * hash(const std::string &message)
Converts the string to bytestring and calls the main algorithm.
Definition sha1.cpp:210
std::string sig2hex(void *sig)
Transforms the 160-bit SHA-1 signature into a 40 char hex string.
Definition sha1.cpp:66
Here is the call graph for this function:

◆ 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
57 {
58 return (n << rotate) | (n >> (32 - rotate));
59}
T rotate(T... args)

◆ main()

int main ( void )

Main function.

Returns
0 on exit
299 {
300 test(); // run self-test implementations
301
302 // Launch interactive mode where user can input messages and see
303 // their hash
304 interactive();
305 return 0;
306}
static void test()
Self-test implementations of well-known SHA-1 hashes.
Definition sha1.cpp:220
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:274
Here is the call graph for this function:

◆ 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
66 {
67 const char* hexChars = "0123456789abcdef";
68 auto* intsig = static_cast<uint8_t*>(sig);
69 std::string hex = "";
70 for (uint8_t i = 0; i < 20; i++) {
71 hex.push_back(hexChars[(intsig[i] >> 4) & 0xF]);
72 hex.push_back(hexChars[(intsig[i]) & 0xF]);
73 }
74 return hex;
75}
T hex(T... args)

◆ test()

static void test ( )
static

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

Returns
void
220 {
221 // Hashes empty string and stores signature
222 void* sig = hashing::sha1::hash("");
223 std::cout << "Hashing empty string" << std::endl;
224 // Prints signature hex representation
226 // Test with cassert wether sig is correct from expected value
227 assert(hashing::sha1::sig2hex(sig).compare(
228 "da39a3ee5e6b4b0d3255bfef95601890afd80709") == 0);
229
230 // Hashes "The quick brown fox jumps over the lazy dog" and stores signature
231 void* sig2 =
232 hashing::sha1::hash("The quick brown fox jumps over the lazy dog");
233 std::cout << "Hashing The quick brown fox jumps over the lazy dog"
234 << std::endl;
235 // Prints signature hex representation
237 // Test with cassert wether sig is correct from expected value
238 assert(hashing::sha1::sig2hex(sig2).compare(
239 "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12") == 0);
240
241 // Hashes "The quick brown fox jumps over the lazy dog." (notice the
242 // additional period) and stores signature
243 void* sig3 =
244 hashing::sha1::hash("The quick brown fox jumps over the lazy dog.");
245 std::cout << "Hashing "
246 "The quick brown fox jumps over the lazy dog."
247 << std::endl;
248 // Prints signature hex representation
250 // Test with cassert wether sig is correct from expected value
251 assert(hashing::sha1::sig2hex(sig3).compare(
252 "408d94384216f890ff7a0c3528e8bed1e0b01621") == 0);
253
254 // Hashes "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
255 // and stores signature
256 void* sig4 = hashing::sha1::hash(
257 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
259 << "Hashing "
260 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
261 << std::endl;
262 // Prints signature hex representation
264 // Test with cassert wether sig is correct from expected value
265 assert(hashing::sha1::sig2hex(sig4).compare(
266 "761c457bf73b14d27e9e9265c46f4b4dda11f940") == 0);
267}
int compare(const void *a, const void *b)
Definition shell_sort2.cpp:87
Here is the call graph for this function: