Speck128.hpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /*
  2. * Copyright (c)2013-2020 ZeroTier, Inc.
  3. *
  4. * Use of this software is governed by the Business Source License included
  5. * in the LICENSE.TXT file in the project's root directory.
  6. *
  7. * Change Date: 2024-01-01
  8. *
  9. * On the date above, in accordance with the Business Source License, use
  10. * of this software will be governed by version 2.0 of the Apache License.
  11. */
  12. /****/
  13. #ifndef ZT_SPECK128_HPP
  14. #define ZT_SPECK128_HPP
  15. #include "Constants.hpp"
  16. #include "Utils.hpp"
  17. namespace ZeroTier {
  18. /**
  19. * Tiny and simple 128-bit ARX block cipher
  20. *
  21. * Speck does not specify a mandatory endian-ness. This implementation is
  22. * little-endian for higher performance on the majority of platforms.
  23. *
  24. * Right now this is only used as part of the PoW function for V1 identity
  25. * generation. It's used because it's faster than SHA for filling a buffer
  26. * with randomness and unlike AES its relative performance advantage
  27. * across CPU architectures is pretty much identical.
  28. *
  29. * @tparam R Number of rounds (default: 32)
  30. */
  31. template<int R = 32>
  32. class Speck128
  33. {
  34. public:
  35. /**
  36. * Create an uninitialized instance, init() must be called to set up.
  37. */
  38. ZT_INLINE Speck128() noexcept {} // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init,hicpp-use-equals-default,modernize-use-equals-default)
  39. /**
  40. * Initialize Speck from a 128-bit key
  41. *
  42. * @param k 128-bit / 16 byte key
  43. */
  44. ZT_INLINE Speck128(const void *k) noexcept { this->init(k); } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init,google-explicit-constructor,hicpp-explicit-conversions)
  45. ZT_INLINE ~Speck128() { Utils::burn(m_expandedKey, sizeof(m_expandedKey)); }
  46. /**
  47. * Initialize Speck from a 128-bit key
  48. *
  49. * @param k 128-bit / 16 byte key
  50. */
  51. ZT_INLINE void init(const void *k) noexcept
  52. {
  53. initXY(Utils::loadLittleEndian<uint64_t>(k),Utils::loadLittleEndian<uint64_t>(reinterpret_cast<const uint8_t *>(k) + 8));
  54. }
  55. /**
  56. * Initialize Speck from a 128-bit key in two 64-bit words
  57. *
  58. * @param x Least significant 64 bits
  59. * @param y Most significant 64 bits
  60. */
  61. ZT_INLINE void initXY(uint64_t x,uint64_t y) noexcept
  62. {
  63. m_expandedKey[0] = x;
  64. for(uint64_t i=0;i<(R-1);++i) {
  65. x = x >> 8U | x << 56U;
  66. x += y;
  67. x ^= i;
  68. y = y << 3U | y >> 61U;
  69. y ^= x;
  70. m_expandedKey[i + 1] = y;
  71. }
  72. }
  73. /**
  74. * Encrypt a 128-bit block as two 64-bit words
  75. *
  76. * These should be in host byte order. If read or written to/from data
  77. * they should be stored in little-endian byte order.
  78. *
  79. * @param x Least significant 64 bits
  80. * @param y Most significant 64 bits
  81. */
  82. ZT_INLINE void encryptXY(uint64_t &x,uint64_t &y) const noexcept
  83. {
  84. for (int i=0;i<R;++i) {
  85. const uint64_t kk = m_expandedKey[i];
  86. x = x >> 8U | x << 56U;
  87. x += y;
  88. x ^= kk;
  89. y = y << 3U | y >> 61U;
  90. y ^= x;
  91. }
  92. }
  93. /**
  94. * Encrypt 512 bits in parallel with the same key.
  95. *
  96. * Parallel in this case assumes instruction level parallelism, but even without that
  97. * it may be faster due to cache/memory effects.
  98. */
  99. ZT_INLINE void encryptXYXYXYXY(uint64_t &x0,uint64_t &y0,uint64_t &x1,uint64_t &y1,uint64_t &x2,uint64_t &y2,uint64_t &x3,uint64_t &y3) const noexcept
  100. {
  101. for (int i=0;i<R;++i) {
  102. const uint64_t kk = m_expandedKey[i];
  103. x0 = x0 >> 8U | x0 << 56U;
  104. x1 = x1 >> 8U | x1 << 56U;
  105. x2 = x2 >> 8U | x2 << 56U;
  106. x3 = x3 >> 8U | x3 << 56U;
  107. x0 += y0;
  108. x1 += y1;
  109. x2 += y2;
  110. x3 += y3;
  111. x0 ^= kk;
  112. x1 ^= kk;
  113. x2 ^= kk;
  114. x3 ^= kk;
  115. y0 = y0 << 3U | y0 >> 61U;
  116. y1 = y1 << 3U | y1 >> 61U;
  117. y2 = y2 << 3U | y2 >> 61U;
  118. y3 = y3 << 3U | y3 >> 61U;
  119. y0 ^= x0;
  120. y1 ^= x1;
  121. y2 ^= x2;
  122. y3 ^= x3;
  123. }
  124. }
  125. /**
  126. * Decrypt a 128-bit block as two 64-bit words
  127. *
  128. * These should be in host byte order. If read or written to/from data
  129. * they should be stored in little-endian byte order.
  130. *
  131. * @param x Least significant 64 bits
  132. * @param y Most significant 64 bits
  133. */
  134. ZT_INLINE void decryptXY(uint64_t &x,uint64_t &y) const noexcept
  135. {
  136. for (int i=(R-1);i>=0;--i) {
  137. const uint64_t kk = m_expandedKey[i];
  138. y ^= x;
  139. y = y >> 3U | y << 61U;
  140. x ^= kk;
  141. x -= y;
  142. x = x << 8U | x >> 56U;
  143. }
  144. }
  145. /**
  146. * Encrypt a block
  147. *
  148. * @param in 128-bit / 16 byte input
  149. * @param out 128-bit / 16 byte output
  150. */
  151. ZT_INLINE void encrypt(const void *const in,void *const out) const noexcept
  152. {
  153. uint64_t x = Utils::loadLittleEndian<uint64_t>(in); // NOLINT(hicpp-use-auto,modernize-use-auto)
  154. uint64_t y = Utils::loadLittleEndian<uint64_t>(reinterpret_cast<const uint8_t *>(in) + 8); // NOLINT(hicpp-use-auto,modernize-use-auto)
  155. encryptXY(x,y);
  156. Utils::storeLittleEndian<uint64_t>(out,x);
  157. Utils::storeLittleEndian<uint64_t>(reinterpret_cast<uint8_t *>(out) + 8,y);
  158. }
  159. /**
  160. * Decrypt a block
  161. *
  162. * @param in 128-bit / 16 byte input
  163. * @param out 128-bit / 16 byte output
  164. */
  165. ZT_INLINE void decrypt(const void *const in,void *const out) const noexcept
  166. {
  167. uint64_t x = Utils::loadLittleEndian<uint64_t>(in); // NOLINT(hicpp-use-auto,modernize-use-auto)
  168. uint64_t y = Utils::loadLittleEndian<uint64_t>(reinterpret_cast<const uint8_t *>(in) + 8); // NOLINT(hicpp-use-auto,modernize-use-auto)
  169. decryptXY(x,y);
  170. Utils::storeLittleEndian<uint64_t>(out,x);
  171. Utils::storeLittleEndian<uint64_t>(reinterpret_cast<uint8_t *>(out) + 8,y);
  172. }
  173. private:
  174. uint64_t m_expandedKey[R];
  175. };
  176. } // namespace ZeroTier
  177. #endif