commonlib
sha1.h
Go to the documentation of this file.
1 
11 #pragma once
12 #include <cstdint>
13 #include <array>
14 #include "crypto.h"
15 #include <stdexcept>
16 
17 BEGIN_NAMESPACE_CRYPTO
18 
19 namespace Detail
20 {
21  enum class Sha1Status
22  {
23  Ok,
24  InvalidArgument,
25  Overflow,
26  };
27 
28  struct Sha1State
29  {
30  constexpr Sha1State()
31  : length(0)
32  , state{ 0x67452301UL, 0xefcdab89UL, 0x98badcfeUL, 0x10325476UL, 0xc3d2e1f0UL }
33  , currentLength(0)
34  , buffer{}
35  {
36  }
37 
38  uint64_t length;
39  uint32_t state[5];
40  uint32_t currentLength;
41  unsigned char buffer[64];
42  };
43 
44  #define F0(x,y,z) (z ^ (x & (y ^ z)))
45  #define F1(x,y,z) (x ^ y ^ z)
46  #define F2(x,y,z) ((x & y) | (z & (x | y)))
47  #define F3(x,y,z) (x ^ y ^ z)
48 
49  /* rotates the hard way */
50  #define ROL(x, y) ( (((uint32_t)(x)<<(uint32_t)((y)&31)) | (((uint32_t)(x)&0xFFFFFFFFUL)>>(uint32_t)((32-((y)&31))&31))) & 0xFFFFFFFFUL)
51  #define ROR(x, y) ( ((((uint32_t)(x)&0xFFFFFFFFUL)>>(uint32_t)((y)&31)) | ((uint32_t)(x)<<(uint32_t)((32-((y)&31))&31))) & 0xFFFFFFFFUL)
52  #define ROLc(x, y) ( (((uint32_t)(x)<<(uint32_t)((y)&31)) | (((uint32_t)(x)&0xFFFFFFFFUL)>>(uint32_t)((32-((y)&31))&31))) & 0xFFFFFFFFUL)
53  #define RORc(x, y) ( ((((uint32_t)(x)&0xFFFFFFFFUL)>>(uint32_t)((y)&31)) | ((uint32_t)(x)<<(uint32_t)((32-((y)&31))&31))) & 0xFFFFFFFFUL)
54 
55  #define STORE64H(x, y) \
56  do { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \
57  (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \
58  (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \
59  (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } while(0)
60 
61  #define STORE32H(x, y) \
62  do { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255); \
63  (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); } while(0)
64 
65  #define LOAD32H(x, y) \
66  do { x = ((uint32_t)((y)[0] & 255)<<24) | \
67  ((uint32_t)((y)[1] & 255)<<16) | \
68  ((uint32_t)((y)[2] & 255)<<8) | \
69  ((uint32_t)((y)[3] & 255)); } while(0)
70 
71  #ifndef MIN
72  #define MIN(x, y) ( ((x)<(y))?(x):(y) )
73  #endif
74 
75  constexpr Sha1Status sha1Compress(Sha1State& md, const unsigned char* buffer)
76  {
77  uint32_t W[80]{};
78  uint32_t i = 0;
79 
80  /* copy the state into 512-bits into W[0..15] */
81  for (i = 0; i < 16; i++)
82  {
83  LOAD32H(W[i], buffer + (4 * i));
84  }
85 
86  /* copy state */
87  uint64_t a = md.state[0];
88  uint64_t b = md.state[1];
89  uint64_t c = md.state[2];
90  uint64_t d = md.state[3];
91  uint64_t e = md.state[4];
92 
93  /* expand it */
94  for (i = 16; i < 80; i++)
95  {
96  W[i] = ROL(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1);
97  }
98 
99  /* compress */
100  /* round one */
101  #define FF0(a,b,c,d,e,i) e = (ROLc(a, 5) + F0(b,c,d) + e + W[i] + 0x5a827999UL); b = ROLc(b, 30);
102  #define FF1(a,b,c,d,e,i) e = (ROLc(a, 5) + F1(b,c,d) + e + W[i] + 0x6ed9eba1UL); b = ROLc(b, 30);
103  #define FF2(a,b,c,d,e,i) e = (ROLc(a, 5) + F2(b,c,d) + e + W[i] + 0x8f1bbcdcUL); b = ROLc(b, 30);
104  #define FF3(a,b,c,d,e,i) e = (ROLc(a, 5) + F3(b,c,d) + e + W[i] + 0xca62c1d6UL); b = ROLc(b, 30);
105 
106  for (i = 0; i < 20;)
107  {
108  FF0(a, b, c, d, e, i++);
109  FF0(e, a, b, c, d, i++);
110  FF0(d, e, a, b, c, i++);
111  FF0(c, d, e, a, b, i++);
112  FF0(b, c, d, e, a, i++);
113  }
114 
115  /* round two */
116  for (; i < 40;)
117  {
118  FF1(a, b, c, d, e, i++);
119  FF1(e, a, b, c, d, i++);
120  FF1(d, e, a, b, c, i++);
121  FF1(c, d, e, a, b, i++);
122  FF1(b, c, d, e, a, i++);
123  }
124 
125  /* round three */
126  for (; i < 60;)
127  {
128  FF2(a, b, c, d, e, i++);
129  FF2(e, a, b, c, d, i++);
130  FF2(d, e, a, b, c, i++);
131  FF2(c, d, e, a, b, i++);
132  FF2(b, c, d, e, a, i++);
133  }
134 
135  /* round four */
136  for (; i < 80;)
137  {
138  FF3(a, b, c, d, e, i++);
139  FF3(e, a, b, c, d, i++);
140  FF3(d, e, a, b, c, i++);
141  FF3(c, d, e, a, b, i++);
142  FF3(b, c, d, e, a, i++);
143  }
144 
145  #undef FF0
146  #undef FF1
147  #undef FF2
148  #undef FF3
149 
150  /* store */
151  md.state[0] = (uint32_t)(md.state[0] + a);
152  md.state[1] = (uint32_t)(md.state[1] + b);
153  md.state[2] = (uint32_t)(md.state[2] + c);
154  md.state[3] = (uint32_t)(md.state[3] + d);
155  md.state[4] = (uint32_t)(md.state[4] + e);
156 
157  return Sha1Status::Ok;
158  }
159 
168  template <bool HasTrailingZero = false, std::uint32_t N>
169  constexpr Sha1Status sha1Process(Sha1State& md, const unsigned char(&in)[N])
170  {
171  std::uint32_t inLength = HasTrailingZero ? N - 1 : N;
172 
173  const unsigned char* ptr = &in[0];
174 
175  if (md.currentLength > sizeof(md.buffer))
176  {
177  return Sha1Status::InvalidArgument;
178  }
179 
180  if ((md.length + inLength) < md.length)
181  {
182  return Sha1Status::Overflow;
183  }
184 
185  Sha1Status err = Sha1Status::Ok;
186  while (inLength > 0)
187  {
188  if (md.currentLength == 0 && inLength >= 64)
189  {
190  if ((err = sha1Compress(md, ptr)) != Sha1Status::Ok)
191  {
192  return err;
193  }
194  md.length += 64 * 8;
195  ptr += 64;
196  inLength -= 64;
197  }
198  else
199  {
200  const uint32_t n = MIN(inLength, (64 - md.currentLength));
201  unsigned char* const dest = md.buffer + md.currentLength;
202 
203  for (std::size_t i = 0; i < n; i++)
204  {
205  dest[i] = ptr[i];
206  }
207 
208  md.currentLength += n;
209  ptr += n;
210  inLength -= n;
211 
212  if (md.currentLength == 64)
213  {
214  if ((err = sha1Compress(md, (const unsigned char*)&md.buffer)) != Sha1Status::Ok)
215  {
216  return err;
217  }
218  md.length += 8 * 64;
219  md.currentLength = 0;
220  }
221  }
222  }
223  return Sha1Status::Ok;
224  }
225 
232  constexpr Sha1Status sha1Done(Sha1State& md, unsigned char* const out)
233  {
234  if (md.currentLength >= sizeof(md.buffer))
235  {
236  return Sha1Status::InvalidArgument;
237  }
238 
239  /* increase the length of the message */
240  md.length += md.currentLength * 8;
241 
242  /* append the '1' bit */
243  md.buffer[md.currentLength++] = 0x80;
244 
245  /* if the length is currently above 56 bytes we append zeros
246  * then compress. Then we can fall back to padding zeros and length
247  * encoding like normal.
248  */
249  if (md.currentLength > 56)
250  {
251  while (md.currentLength < 64)
252  {
253  md.buffer[md.currentLength++] = 0;
254  }
255  sha1Compress(md, md.buffer);
256  md.currentLength = 0;
257  }
258 
259  /* pad up to 56 bytes of zeroes */
260  while (md.currentLength < 56)
261  {
262  md.buffer[md.currentLength++] = 0;
263  }
264 
265  /* store length */
266  STORE64H(md.length, md.buffer + 56);
267  sha1Compress(md, md.buffer);
268 
269  /* copy output */
270  for (int i = 0; i < 5; i++)
271  {
272  STORE32H(md.state[i], out + (4 * i));
273  }
274  return Sha1Status::Ok;
275  }
276 
277 }
278 
286 template <bool HasTrailingZero = false, std::size_t N >
287 constexpr std::array<unsigned char, 20> ct_sha1(const unsigned char (&in)[N])
288 {
290  Detail::Sha1Status status = Detail::sha1Process<HasTrailingZero, N>(md, in);
291  if (status == Detail::Sha1Status::InvalidArgument)
292  {
293  throw std::runtime_error("Invalid argument");
294  }
295 
296  if (status == Detail::Sha1Status::Overflow)
297  {
298  throw std::runtime_error("Overflow");
299  }
300 
301 #if __cplusplus >= 201703L
302  std::array<unsigned char, 20> tmp{};
303  Detail::sha1Done(md, &tmp[0]);
304 #else
305  // Undefined behaviour hack
306  // but works on MSVC/GCC but not on Clang.
307  const std::array<unsigned char, 20> tmp{};
308  Detail::sha1Done(md, (unsigned char* const) &tmp[0]);
309 #endif
310 
311  return tmp;
312 }
313 
314 END_NAMESPACE_CRYPTO
Definition: sha1.h:19
constexpr std::array< unsigned char, 20 > ct_sha1(const unsigned char(&in)[N])
Calculates the SHA-1 digest in compile-time if the arguments are constexpr.
Definition: sha1.h:287
constexpr Sha1Status sha1Process(Sha1State &md, const unsigned char(&in)[N])
Process a block of memory though the hash.
Definition: sha1.h:169
constexpr Sha1Status sha1Done(Sha1State &md, unsigned char *const out)
Terminate the hash to get the digest.
Definition: sha1.h:232
Definition: sha1.h:28