Line data Source code
1 : #ifndef THINGER_UTIL_SHA1_HPP
2 : #define THINGER_UTIL_SHA1_HPP
3 :
4 : #include <string>
5 : #include <cstring>
6 : #include <cstdint>
7 :
8 : namespace thinger::util {
9 :
10 : class sha1 {
11 : private:
12 : static constexpr uint32_t K[] = {
13 : 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6
14 : };
15 :
16 : uint32_t H[5];
17 : unsigned char buffer[64];
18 : uint64_t count;
19 :
20 34048 : static uint32_t rotate_left(uint32_t value, size_t count) {
21 34048 : return (value << count) | (value >> (32 - count));
22 : }
23 :
24 152 : void process_block() {
25 : uint32_t W[80];
26 :
27 : // Copy block to W[0..15]
28 2584 : for (int i = 0; i < 16; i++) {
29 2432 : W[i] = (buffer[i * 4] << 24) |
30 2432 : (buffer[i * 4 + 1] << 16) |
31 2432 : (buffer[i * 4 + 2] << 8) |
32 2432 : (buffer[i * 4 + 3]);
33 : }
34 :
35 : // Extend W[16..79]
36 9880 : for (int i = 16; i < 80; i++) {
37 9728 : W[i] = rotate_left(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16], 1);
38 : }
39 :
40 152 : uint32_t A = H[0];
41 152 : uint32_t B = H[1];
42 152 : uint32_t C = H[2];
43 152 : uint32_t D = H[3];
44 152 : uint32_t E = H[4];
45 :
46 : // Main loop
47 12312 : for (int i = 0; i < 80; i++) {
48 : uint32_t f, k;
49 12160 : if (i < 20) {
50 3040 : f = (B & C) | ((~B) & D);
51 3040 : k = K[0];
52 9120 : } else if (i < 40) {
53 3040 : f = B ^ C ^ D;
54 3040 : k = K[1];
55 6080 : } else if (i < 60) {
56 3040 : f = (B & C) | (B & D) | (C & D);
57 3040 : k = K[2];
58 : } else {
59 3040 : f = B ^ C ^ D;
60 3040 : k = K[3];
61 : }
62 :
63 12160 : uint32_t temp = rotate_left(A, 5) + f + E + k + W[i];
64 12160 : E = D;
65 12160 : D = C;
66 12160 : C = rotate_left(B, 30);
67 12160 : B = A;
68 12160 : A = temp;
69 : }
70 :
71 152 : H[0] += A;
72 152 : H[1] += B;
73 152 : H[2] += C;
74 152 : H[3] += D;
75 152 : H[4] += E;
76 152 : }
77 :
78 : public:
79 76 : sha1() {
80 76 : reset();
81 76 : }
82 :
83 76 : void reset() {
84 76 : H[0] = 0x67452301;
85 76 : H[1] = 0xEFCDAB89;
86 76 : H[2] = 0x98BADCFE;
87 76 : H[3] = 0x10325476;
88 76 : H[4] = 0xC3D2E1F0;
89 76 : count = 0;
90 76 : memset(buffer, 0, sizeof(buffer));
91 76 : }
92 :
93 76 : void update(const unsigned char* data, size_t len) {
94 152 : while (len > 0) {
95 76 : size_t buffer_pos = count % 64;
96 76 : size_t to_copy = std::min(len, 64 - buffer_pos);
97 :
98 76 : memcpy(buffer + buffer_pos, data, to_copy);
99 76 : count += to_copy;
100 76 : data += to_copy;
101 76 : len -= to_copy;
102 :
103 76 : if ((count % 64) == 0) {
104 0 : process_block();
105 : }
106 : }
107 76 : }
108 :
109 76 : void update(const std::string& str) {
110 76 : update(reinterpret_cast<const unsigned char*>(str.c_str()), str.length());
111 76 : }
112 :
113 76 : std::string finalize() {
114 : // Padding
115 76 : size_t buffer_pos = count % 64;
116 76 : buffer[buffer_pos++] = 0x80;
117 :
118 76 : if (buffer_pos > 56) {
119 76 : memset(buffer + buffer_pos, 0, 64 - buffer_pos);
120 76 : process_block();
121 76 : buffer_pos = 0;
122 : }
123 :
124 76 : memset(buffer + buffer_pos, 0, 56 - buffer_pos);
125 :
126 : // Append length in bits
127 76 : uint64_t bit_count = count * 8;
128 684 : for (int i = 0; i < 8; i++) {
129 608 : buffer[56 + i] = (bit_count >> ((7 - i) * 8)) & 0xFF;
130 : }
131 :
132 76 : process_block();
133 :
134 : // Convert hash to string
135 76 : std::string result;
136 76 : result.reserve(20);
137 456 : for (int i = 0; i < 5; i++) {
138 380 : result += static_cast<char>((H[i] >> 24) & 0xFF);
139 380 : result += static_cast<char>((H[i] >> 16) & 0xFF);
140 380 : result += static_cast<char>((H[i] >> 8) & 0xFF);
141 380 : result += static_cast<char>(H[i] & 0xFF);
142 : }
143 :
144 76 : return result;
145 0 : }
146 :
147 76 : static std::string hash(const std::string& input) {
148 76 : sha1 hasher;
149 76 : hasher.update(input);
150 152 : return hasher.finalize();
151 : }
152 : };
153 :
154 : } // namespace thinger::util
155 :
156 : #endif // THINGER_UTIL_SHA1_HPP
|