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