1 /*
2  *  Chocobo1/Hash
3  *
4  *   Copyright 2017-2020 by Mike Tzou (Chocobo1)
5  *     https://github.com/Chocobo1/Hash
6  *
7  *   Licensed under GNU General Public License 3 or later.
8  *
9  *  @license GPL3 <https://www.gnu.org/licenses/gpl-3.0-standalone.html>
10  */
11 
12 #ifndef CHOCOBO1_SHA2_512_H
13 #define CHOCOBO1_SHA2_512_H
14 
15 #include <array>
16 #include <cassert>
17 #include <climits>
18 #include <cmath>
19 #include <cstdint>
20 #include <initializer_list>
21 #include <string>
22 #include <type_traits>
23 #include <vector>
24 
25 #if (__cplusplus > 201703L)
26 #include <version>
27 #endif
28 
29 #ifndef USE_STD_SPAN_CHOCOBO1_HASH
30 #if (__cpp_lib_span >= 202002L)
31 #define USE_STD_SPAN_CHOCOBO1_HASH 1
32 #else
33 #define USE_STD_SPAN_CHOCOBO1_HASH 0
34 #endif
35 #endif
36 
37 #if (USE_STD_SPAN_CHOCOBO1_HASH == 1)
38 #include <span>
39 #else
40 #include "gsl/span"
41 #endif
42 
43 
44 namespace Chocobo1
45 {
46 	// Use these!!
47 	// SHA2_512();
48 }
49 
50 
51 namespace Chocobo1
52 {
53 // users should ignore things in this namespace
54 
55 namespace Hash
56 {
57 #ifndef CONSTEXPR_CPP17_CHOCOBO1_HASH
58 #if __cplusplus >= 201703L
59 #define CONSTEXPR_CPP17_CHOCOBO1_HASH constexpr
60 #else
61 #define CONSTEXPR_CPP17_CHOCOBO1_HASH
62 #endif
63 #endif
64 
65 #if (USE_STD_SPAN_CHOCOBO1_HASH == 1)
66 	using IndexType = std::size_t;
67 #else
68 	using IndexType = gsl::index;
69 #endif
70 
71 #ifndef CHOCOBO1_HASH_BUFFER_IMPL
72 #define CHOCOBO1_HASH_BUFFER_IMPL
73 	template <typename T, IndexType N>
74 	class Buffer
75 	{
76 		public:
77 			using value_type = T;
78 			using index_type = IndexType;
79 			using size_type = std::size_t;
80 
81 			constexpr Buffer() = default;
82 			constexpr Buffer(const Buffer &) = default;
83 
Buffer(const std::initializer_list<T> initList)84 			CONSTEXPR_CPP17_CHOCOBO1_HASH Buffer(const std::initializer_list<T> initList)
85 			{
86 #if !defined(NDEBUG)
87 				// check if out-of-bounds
88 				static_cast<void>(m_array.at(m_dataEndIdx + initList.size() - 1));
89 #endif
90 
91 				for (const auto &i : initList)
92 				{
93 					m_array[m_dataEndIdx] = i;
94 					++m_dataEndIdx;
95 				}
96 			}
97 
98 			template <typename InputIt>
Buffer(const InputIt first,const InputIt last)99 			constexpr Buffer(const InputIt first, const InputIt last)
100 			{
101 				for (InputIt iter = first; iter != last; ++iter)
102 				{
103 					this->fill(*iter);
104 				}
105 			}
106 
107 			constexpr T& operator[](const index_type pos)
108 			{
109 				return m_array[pos];
110 			}
111 
112 			constexpr T operator[](const index_type pos) const
113 			{
114 				return m_array[pos];
115 			}
116 
117 			CONSTEXPR_CPP17_CHOCOBO1_HASH void fill(const T &value, const index_type count = 1)
118 			{
119 #if !defined(NDEBUG)
120 				// check if out-of-bounds
121 				static_cast<void>(m_array.at(m_dataEndIdx + count - 1));
122 #endif
123 
124 				for (index_type i = 0; i < count; ++i)
125 				{
126 					m_array[m_dataEndIdx] = value;
127 					++m_dataEndIdx;
128 				}
129 			}
130 
131 			template <typename InputIt>
push_back(const InputIt first,const InputIt last)132 			constexpr void push_back(const InputIt first, const InputIt last)
133 			{
134 				for (InputIt iter = first; iter != last; ++iter)
135 				{
136 					this->fill(*iter);
137 				}
138 			}
139 
clear()140 			constexpr void clear()
141 			{
142 				m_array = {};
143 				m_dataEndIdx = 0;
144 			}
145 
empty()146 			constexpr bool empty() const
147 			{
148 				return (m_dataEndIdx == 0);
149 			}
150 
size()151 			constexpr size_type size() const
152 			{
153 				return m_dataEndIdx;
154 			}
155 
data()156 			constexpr const T* data() const
157 			{
158 				return m_array.data();
159 			}
160 
161 		private:
162 			std::array<T, N> m_array {};
163 			index_type m_dataEndIdx = 0;
164 	};
165 #endif
166 
167 #ifndef CHOCOBO1_HASH_UINT128_IMPL
168 #define CHOCOBO1_HASH_UINT128_IMPL
169 	class Uint128
170 	{
171 		public:
Uint128()172 			constexpr Uint128()
173 				: m_lo(0), m_hi(0)
174 			{
175 			}
176 
177 			constexpr Uint128& operator= (const uint64_t n)
178 			{
179 				this->m_lo = n;
180 				this->m_hi = 0;
181 				return (*this);
182 			}
183 
184 			constexpr Uint128 operator+ (const uint64_t n)
185 			{
186 				Uint128 ret = *this;
187 				ret += n;
188 				return ret;
189 			}
190 
191 			constexpr Uint128& operator* (const unsigned int n)
192 			{
193 				// only handle `*8` case
194 				assert(n == 8);
195 
196 				const uint8_t msb = static_cast<uint8_t>(m_lo >> 61);
197 				m_hi = (m_hi << 3) | msb;
198 				m_lo = m_lo << 3;
199 
200 				return (*this);
201 			}
202 
203 			constexpr Uint128& operator+= (const uint64_t n)
204 			{
205 				const uint64_t newLo = (m_lo + n);
206 				if (newLo < m_lo)
207 					++m_hi;
208 				m_lo = newLo;
209 
210 				return (*this);
211 			}
212 
low()213 			constexpr uint64_t low() const
214 			{
215 				return m_lo;
216 			}
217 
high()218 			constexpr uint64_t high() const
219 			{
220 				return m_hi;
221 			}
222 
223 		private:
224 			uint64_t m_lo;
225 			uint64_t m_hi;
226 	};
227 #endif
228 
229 
230 namespace SHA2_512_NS
231 {
232 	class SHA2_512
233 	{
234 		// https://tools.ietf.org/html/rfc6234
235 
236 		public:
237 			using Byte = uint8_t;
238 			using ResultArrayType = std::array<Byte, 64>;
239 
240 #if (USE_STD_SPAN_CHOCOBO1_HASH == 1)
241 			template <typename T, std::size_t Extent = std::dynamic_extent>
242 			using Span = std::span<T, Extent>;
243 #else
244 			template <typename T, std::size_t Extent = gsl::dynamic_extent>
245 			using Span = gsl::span<T, Extent>;
246 #endif
247 
248 
249 			constexpr SHA2_512();
250 
251 			constexpr void reset();
252 			CONSTEXPR_CPP17_CHOCOBO1_HASH SHA2_512& finalize();  // after this, only `toArray()`, `toString()`, `toVector()`, `reset()` are available
253 
254 			std::string toString() const;
255 			std::vector<Byte> toVector() const;
256 			CONSTEXPR_CPP17_CHOCOBO1_HASH ResultArrayType toArray() const;
257 
258 			CONSTEXPR_CPP17_CHOCOBO1_HASH SHA2_512& addData(const Span<const Byte> inData);
259 			CONSTEXPR_CPP17_CHOCOBO1_HASH SHA2_512& addData(const void *ptr, const std::size_t length);
260 			template <std::size_t N>
261 			CONSTEXPR_CPP17_CHOCOBO1_HASH SHA2_512& addData(const Byte (&array)[N]);
262 			template <typename T, std::size_t N>
263 			SHA2_512& addData(const T (&array)[N]);
264 			template <typename T>
265 			SHA2_512& addData(const Span<T> inSpan);
266 
267 		private:
268 			CONSTEXPR_CPP17_CHOCOBO1_HASH void addDataImpl(const Span<const Byte> data);
269 
270 			static constexpr int BLOCK_SIZE = 128;
271 
272 			Buffer<Byte, (BLOCK_SIZE * 2)> m_buffer;  // x2 for paddings
273 			Uint128 m_sizeCounter;
274 
275 			uint64_t m_h[8] = {};
276 
277 			static constexpr uint64_t kTable[80] =
278 			{
279 				0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
280 				0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
281 				0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
282 				0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
283 				0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
284 				0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
285 				0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
286 				0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
287 				0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
288 				0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
289 				0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
290 				0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
291 				0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
292 				0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
293 				0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
294 				0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
295 				0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
296 				0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
297 				0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
298 				0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
299 			};
300 	};
301 
302 	constexpr uint64_t SHA2_512::kTable[80];
303 
304 
305 	// helpers
306 	template <typename T>
307 	class Loader
308 	{
309 		// this class workaround loading data from unaligned memory boundaries
310 		// also eliminate endianness issues
311 		public:
Loader(const uint8_t * ptr)312 			explicit constexpr Loader(const uint8_t *ptr)
313 				: m_ptr(ptr)
314 			{
315 			}
316 
317 			constexpr T operator[](const IndexType idx) const
318 			{
319 				static_assert(std::is_same<T, uint64_t>::value, "");
320 				// handle specific endianness here
321 				const uint8_t *ptr = m_ptr + (sizeof(T) * idx);
322 				return  ( (static_cast<T>(*(ptr + 0)) << 56)
323 						| (static_cast<T>(*(ptr + 1)) << 48)
324 						| (static_cast<T>(*(ptr + 2)) << 40)
325 						| (static_cast<T>(*(ptr + 3)) << 32)
326 						| (static_cast<T>(*(ptr + 4)) << 24)
327 						| (static_cast<T>(*(ptr + 5)) << 16)
328 						| (static_cast<T>(*(ptr + 6)) <<  8)
329 						| (static_cast<T>(*(ptr + 7)) <<  0));
330 			}
331 
332 		private:
333 			const uint8_t *m_ptr;
334 	};
335 
336 	template <typename R, typename T>
ror(const T x,const unsigned int s)337 	constexpr R ror(const T x, const unsigned int s)
338 	{
339 		static_assert(std::is_unsigned<R>::value, "");
340 		static_assert(std::is_unsigned<T>::value, "");
341 		return static_cast<R>(x >> s);
342 	}
343 
344 	template <typename T>
rotr(const T x,const unsigned int s)345 	constexpr T rotr(const T x, const unsigned int s)
346 	{
347 		static_assert(std::is_unsigned<T>::value, "");
348 		if (s == 0)
349 			return x;
350 		return ((x >> s) | (x << ((sizeof(T) * 8) - s)));
351 	}
352 
353 
354 	//
SHA2_512()355 	constexpr SHA2_512::SHA2_512()
356 	{
357 		static_assert((CHAR_BIT == 8), "Sorry, we don't support exotic CPUs");
358 		reset();
359 	}
360 
reset()361 	constexpr void SHA2_512::reset()
362 	{
363 		m_buffer.clear();
364 		m_sizeCounter = 0;
365 
366 		m_h[0] = 0x6a09e667f3bcc908;
367 		m_h[1] = 0xbb67ae8584caa73b;
368 		m_h[2] = 0x3c6ef372fe94f82b;
369 		m_h[3] = 0xa54ff53a5f1d36f1;
370 		m_h[4] = 0x510e527fade682d1;
371 		m_h[5] = 0x9b05688c2b3e6c1f;
372 		m_h[6] = 0x1f83d9abfb41bd6b;
373 		m_h[7] = 0x5be0cd19137e2179;
374 	}
375 
finalize()376 	CONSTEXPR_CPP17_CHOCOBO1_HASH SHA2_512& SHA2_512::finalize()
377 	{
378 		m_sizeCounter += m_buffer.size();
379 
380 		// append 1 bit
381 		m_buffer.fill(1 << 7);
382 
383 		// append paddings
384 		const size_t len = BLOCK_SIZE - ((m_buffer.size() + 16) % BLOCK_SIZE);
385 		m_buffer.fill(0, (len + 16));
386 
387 		// append size in bits
388 		const Uint128 sizeCounterBits = m_sizeCounter * 8;
389 		const uint64_t sizeCounterBitsL = sizeCounterBits.low();
390 		const uint64_t sizeCounterBitsH = sizeCounterBits.high();
391 		for (int i = 0; i < 8; ++i)
392 		{
393 			m_buffer[m_buffer.size() - 16 + i] = ror<Byte>(sizeCounterBitsH, (8 * (7 - i)));
394 			m_buffer[m_buffer.size() - 8 + i] = ror<Byte>(sizeCounterBitsL, (8 * (7 - i)));
395 		}
396 
397 		addDataImpl({m_buffer.data(), m_buffer.size()});
398 		m_buffer.clear();
399 
400 		return (*this);
401 	}
402 
toString()403 	std::string SHA2_512::toString() const
404 	{
405 		const auto a = toArray();
406 		std::string ret;
407 		ret.resize(2 * a.size());
408 
409 		auto retPtr = &ret.front();
410 		for (const auto c : a)
411 		{
412 			const Byte upper = ror<Byte>(c, 4);
413 			*(retPtr++) = static_cast<char>((upper < 10) ? (upper + '0') : (upper - 10 + 'a'));
414 
415 			const Byte lower = c & 0xf;
416 			*(retPtr++) = static_cast<char>((lower < 10) ? (lower + '0') : (lower - 10 + 'a'));
417 		}
418 
419 		return ret;
420 	}
421 
toVector()422 	std::vector<SHA2_512::Byte> SHA2_512::toVector() const
423 	{
424 		const auto a = toArray();
425 		return {a.begin(), a.end()};
426 	}
427 
toArray()428 	CONSTEXPR_CPP17_CHOCOBO1_HASH SHA2_512::ResultArrayType SHA2_512::toArray() const
429 	{
430 		const Span<const uint64_t> state(m_h);
431 		const int dataSize = sizeof(decltype(state)::value_type);
432 
433 		ResultArrayType ret {};
434 		auto retPtr = ret.data();
435 		for (const auto i : state)
436 		{
437 			for (int j = (dataSize - 1); j >= 0; --j)
438 				*(retPtr++) = ror<Byte>(i, (j * 8));
439 		}
440 
441 		return ret;
442 	}
443 
addData(const Span<const Byte> inData)444 	CONSTEXPR_CPP17_CHOCOBO1_HASH SHA2_512& SHA2_512::addData(const Span<const Byte> inData)
445 	{
446 		Span<const Byte> data = inData;
447 
448 		if (!m_buffer.empty())
449 		{
450 			const size_t len = std::min<size_t>((BLOCK_SIZE - m_buffer.size()), data.size());  // try fill to BLOCK_SIZE bytes
451 			m_buffer.push_back(data.begin(), (data.begin() + len));
452 
453 			if (m_buffer.size() < BLOCK_SIZE)  // still doesn't fill the buffer
454 				return (*this);
455 
456 			addDataImpl({m_buffer.data(), m_buffer.size()});
457 			m_buffer.clear();
458 
459 			data = data.subspan(len);
460 		}
461 
462 		const size_t dataSize = data.size();
463 		if (dataSize < BLOCK_SIZE)
464 		{
465 			m_buffer = {data.begin(), data.end()};
466 			return (*this);
467 		}
468 
469 		const size_t len = dataSize - (dataSize % BLOCK_SIZE);  // align on BLOCK_SIZE bytes
470 		addDataImpl(data.first(len));
471 
472 		if (len < dataSize)  // didn't consume all data
473 			m_buffer = {(data.begin() + len), data.end()};
474 
475 		return (*this);
476 	}
477 
addData(const void * ptr,const std::size_t length)478 	CONSTEXPR_CPP17_CHOCOBO1_HASH SHA2_512& SHA2_512::addData(const void *ptr, const std::size_t length)
479 	{
480 		// Span::size_type = std::size_t
481 		return addData({static_cast<const Byte*>(ptr), length});
482 	}
483 
484 	template <std::size_t N>
addData(const Byte (& array)[N])485 	CONSTEXPR_CPP17_CHOCOBO1_HASH SHA2_512& SHA2_512::addData(const Byte (&array)[N])
486 	{
487 		return addData({array, N});
488 	}
489 
490 	template <typename T, std::size_t N>
addData(const T (& array)[N])491 	SHA2_512& SHA2_512::addData(const T (&array)[N])
492 	{
493 		return addData({reinterpret_cast<const Byte*>(array), (sizeof(T) * N)});
494 	}
495 
496 	template <typename T>
addData(const Span<T> inSpan)497 	SHA2_512& SHA2_512::addData(const Span<T> inSpan)
498 	{
499 		return addData({reinterpret_cast<const Byte*>(inSpan.data()), inSpan.size_bytes()});
500 	}
501 
addDataImpl(const Span<const Byte> data)502 	CONSTEXPR_CPP17_CHOCOBO1_HASH void SHA2_512::addDataImpl(const Span<const Byte> data)
503 	{
504 		assert((data.size() % BLOCK_SIZE) == 0);
505 
506 		m_sizeCounter += data.size();
507 
508 		for (size_t iter = 0, iend = static_cast<size_t>(data.size() / BLOCK_SIZE); iter < iend; ++iter)
509 		{
510 			const Loader<uint64_t> m(static_cast<const Byte *>(data.data() + (iter * BLOCK_SIZE)));
511 
512 			// TODO: kTable was here, move it back when static variable in constexpr function is allowed
513 
514 			const auto ssig0 = [](const uint64_t x) -> uint64_t
515 			{
516 				return (rotr(x, 1) ^ rotr(x, 8) ^ ror<uint64_t>(x, 7));
517 			};
518 			const auto ssig1 = [](const uint64_t x) -> uint64_t
519 			{
520 				return (rotr(x, 19) ^ rotr(x, 61) ^ ror<uint64_t>(x, 6));
521 			};
522 			uint64_t wTable[80] {};
523 			for (int t = 0; t < 16; ++t)
524 				wTable[t] = m[t];
525 			for (int t = 16; t < 80; ++t)
526 				wTable[t] = ssig1(wTable[t - 2]) + wTable[t - 7] + ssig0(wTable[t - 15]) + wTable[t - 16];
527 
528 			uint64_t a = m_h[0];
529 			uint64_t b = m_h[1];
530 			uint64_t c = m_h[2];
531 			uint64_t d = m_h[3];
532 			uint64_t e = m_h[4];
533 			uint64_t f = m_h[5];
534 			uint64_t g = m_h[6];
535 			uint64_t h = m_h[7];
536 
537 			const auto round = [&wTable](uint64_t &a, uint64_t &b, uint64_t &c, uint64_t &d, uint64_t &e, uint64_t &f, uint64_t &g, uint64_t &h, const unsigned int t) -> void
538 			{
539 				const auto ch = [](const uint64_t x, const uint64_t y, const uint64_t z) -> uint64_t
540 				{
541 					return ((x & (y ^ z)) ^ z);  // alternative
542 				};
543 				const auto maj = [](const uint64_t x, const uint64_t y, const uint64_t z) -> uint64_t
544 				{
545 					return ((x & (y | z)) | (y & z));  // alternative
546 				};
547 				const auto bsig0 = [](const uint64_t x) -> uint64_t
548 				{
549 					return (rotr(x, 28) ^ rotr(x, 34) ^ rotr(x, 39));
550 				};
551 				const auto bsig1 = [](const uint64_t x) -> uint64_t
552 				{
553 					return (rotr(x, 14) ^ rotr(x, 18) ^ rotr(x, 41));
554 				};
555 
556 				const uint64_t t1 = h + bsig1(e) + ch(e, f, g) + kTable[t] + wTable[t];
557 				const uint64_t t2 = bsig0(a) + maj(a, b, c);
558 
559 				h = t1;
560 				d += h;
561 				h += t2;
562 			};
563 			for (int t = 0; t < 10; ++t)
564 			{
565 				round(a, b, c, d, e, f, g, h, (8 * t) + 0);
566 				round(h, a, b, c, d, e, f, g, (8 * t) + 1);
567 				round(g, h, a, b, c, d, e, f, (8 * t) + 2);
568 				round(f, g, h, a, b, c, d, e, (8 * t) + 3);
569 				round(e, f, g, h, a, b, c, d, (8 * t) + 4);
570 				round(d, e, f, g, h, a, b, c, (8 * t) + 5);
571 				round(c, d, e, f, g, h, a, b, (8 * t) + 6);
572 				round(b, c, d, e, f, g, h, a, (8 * t) + 7);
573 			}
574 
575 			m_h[0] += a;
576 			m_h[1] += b;
577 			m_h[2] += c;
578 			m_h[3] += d;
579 			m_h[4] += e;
580 			m_h[5] += f;
581 			m_h[6] += g;
582 			m_h[7] += h;
583 		}
584 	}
585 }
586 }
587 	using SHA2_512 = Hash::SHA2_512_NS::SHA2_512;
588 }
589 
590 #endif  // CHOCOBO1_SHA2_512_H
591