diff --git a/node/CMakeLists.txt b/node/CMakeLists.txt index b54c76704..047e11f92 100644 --- a/node/CMakeLists.txt +++ b/node/CMakeLists.txt @@ -41,6 +41,7 @@ set(core_headers SelfAwareness.hpp SHA512.hpp SharedPtr.hpp + Speck128.hpp Tag.hpp Topology.hpp Trace.hpp diff --git a/node/MIMC52.cpp b/node/MIMC52.cpp index ab710da5b..a9b915588 100644 --- a/node/MIMC52.cpp +++ b/node/MIMC52.cpp @@ -14,6 +14,9 @@ #include "MIMC52.hpp" #include "SHA512.hpp" #include "Utils.hpp" +#include "Speck128.hpp" + +#include // This gets defined on any architecture whose FPU is not capable of doing the mulmod52() FPU trick. //#define ZT_MIMC52_NO_FPU @@ -96,67 +99,31 @@ ZT_INLINE uint64_t modpow52(uint64_t a,uint64_t e,const uint64_t p) noexcept } } -// See: https://en.wikipedia.org/wiki/Speck_(cipher) -#define SPECK_ROR(x,r) ((x >> r) | (x << (64U - r))) -#define SPECK_ROL(x,r) ((x << r) | (x >> (64U - r))) -#define SPECK_R(x,y,k) (x = SPECK_ROR(x,8U), x += y, x ^= k, y = SPECK_ROL(y,3U), y ^= x) - -ZT_INLINE void mimc52Init(uint64_t k[128],const void *const salt,const unsigned int saltSize,uint64_t &p,uint64_t &x) noexcept -{ - uint64_t hash[6]; - SHA384(hash,salt,saltSize); - - // Choose a prime and a delay starting point / verification end point. -#if __BYTE_ORDER == __LITTLE_ENDIAN - p = s_mimc52Primes[hash[0] & 511U]; - x = hash[1] % p; -#else - p = s_mimc52Primes[Utils::swapBytes(hash[0]) & 511U]; - x = Utils::swapBytes(hash[1]) % p; -#endif - - // Use the Speck128 round function as a fast PRNG to generate MIMC round constants. Speck128 initial inputs are - // from the last 256 bits of the salt hash. MIMC round constants could be static, but generating them from the - // salt may improve the overall randomness and non-reversibility of the delay function. -#if __BYTE_ORDER == __LITTLE_ENDIAN - uint64_t ka = hash[2],kb = hash[3]; -#else - uint64_t ka = Utils::swapBytes(hash[2]),kb = Utils::swapBytes(hash[3]); -#endif - uint64_t sy = hash[4],sx = hash[5]; - for(unsigned long i=0;i<128;i+=2) { - ka += i; - kb += i; - SPECK_R(sx,sy,kb); - SPECK_R(ka,kb,0U); - SPECK_R(sx,sy,kb); - SPECK_R(ka,kb,1U); - SPECK_R(sx,sy,kb); - SPECK_R(ka,kb,2U); - SPECK_R(sx,sy,kb); - SPECK_R(ka,kb,3U); - SPECK_R(sx,sy,kb); -#if __BYTE_ORDER == __LITTLE_ENDIAN - k[i] = sy; - k[i+1] = sx; -#else - k[i] = Utils::swapBytes(sy); - k[i+1] = Utils::swapBytes(sx); -#endif - } -} - } // anonymous namespace uint64_t mimc52Delay(const void *const salt,const unsigned int saltSize,const unsigned long rounds) { - uint64_t k[128],x,p; - mimc52Init(k,salt,saltSize,p,x); + uint64_t hash[6]; + SHA384(hash,salt,saltSize); +#if __BYTE_ORDER == __LITTLE_ENDIAN + uint64_t p = s_mimc52Primes[hash[0] & 511U]; + uint64_t x = hash[1] % p; +#else + uint64_t p = s_mimc52Primes[Utils::swapBytes(hash[0]) & 511U]; + uint64_t x = Utils::swapBytes(hash[1]) % p; +#endif + + Speck128<8> roundConstantGenerator(hash + 2); const uint64_t e = ((p * 2) - 1) / 3; const uint64_t m52 = 0xfffffffffffffULL; - for(unsigned long r=0,kn=rounds;r roundConstantGenerator(hash + 2); const uint64_t m52 = 0xfffffffffffffULL; uint64_t y = proof & m52; + const uint64_t sxx = hash[4]; #if !defined(ZT_MIMC52_NO_FPU) double ii,of,pp = (double)p; uint64_t oi,one = 1; #endif +#pragma unroll 16 for(unsigned long r=0;r +class Speck128 +{ +public: + /** + * Create an uninitialized instance, init() must be called to set up. + */ + ZT_INLINE Speck128() noexcept {} + + /** + * Initialize Speck from a 128-bit key + * + * @param k 128-bit / 16 byte key + */ + ZT_INLINE Speck128(const void *k) noexcept { this->init(k); } + + ZT_INLINE ~Speck128() { Utils::burn(_k,sizeof(_k)); } + + /** + * Initialize Speck from a 128-bit key + * + * @param k 128-bit / 16 byte key + */ + ZT_INLINE void init(const void *k) noexcept + { + uint64_t x = Utils::loadLittleEndian(k); + uint64_t y = Utils::loadLittleEndian(reinterpret_cast(k) + 8); + _k[0] = x; + for(uint64_t i=0;i<(R-1);++i) { + x = x >> 8U | x << 56U; + x += y; + x ^= i; + y = y << 3U | y >> 61U; + y ^= x; + _k[i + 1] = y; + } + } + + /** + * Encrypt a 128-bit block as two 64-bit words + * + * These should be in host byte order. If read or written to/from data + * they should be stored in little-endian byte order. + * + * @param x Least significant 64 bits + * @param y Most significant 64 bits + */ + ZT_INLINE void encryptXY(uint64_t &x,uint64_t &y) const noexcept + { +#pragma unroll + for (int i=0;i> 8U | x << 56U; + x += y; + x ^= kk; + y = y << 3U | y >> 61U; + y ^= x; + } + } + + /** + * Decrypt a 128-bit block as two 64-bit words + * + * These should be in host byte order. If read or written to/from data + * they should be stored in little-endian byte order. + * + * @param x Least significant 64 bits + * @param y Most significant 64 bits + */ + ZT_INLINE void decryptXY(uint64_t &x,uint64_t &y) const noexcept + { +#pragma unroll + for (int i=(R-1);i>=0;--i) { + const uint64_t kk = _k[i]; + y ^= x; + y = y >> 3U | y << 61U; + x ^= kk; + x -= y; + x = x << 8U | x >> 56U; + } + } + + /** + * Encrypt a block + * + * @param in 128-bit / 16 byte input + * @param out 128-bit / 16 byte output + */ + ZT_INLINE void encrypt(const void *const in,void *const out) const noexcept + { + uint64_t x = Utils::loadLittleEndian(in); + uint64_t y = Utils::loadLittleEndian(reinterpret_cast(in) + 8); + encryptXY(x,y); + Utils::storeLittleEndian(out,x); + Utils::storeLittleEndian(reinterpret_cast(out) + 8,y); + } + + /** + * Decrypt a block + * + * @param in 128-bit / 16 byte input + * @param out 128-bit / 16 byte output + */ + ZT_INLINE void decrypt(const void *const in,void *const out) const noexcept + { + uint64_t x = Utils::loadLittleEndian(in); + uint64_t y = Utils::loadLittleEndian(reinterpret_cast(in) + 8); + decryptXY(x,y); + Utils::storeLittleEndian(out,x); + Utils::storeLittleEndian(reinterpret_cast(out) + 8,y); + } + +private: + uint64_t _k[R]; +}; + +} // namespace ZeroTier + +#endif diff --git a/node/Tests.cpp b/node/Tests.cpp index ba08ddf0f..1b6342ddf 100644 --- a/node/Tests.cpp +++ b/node/Tests.cpp @@ -777,8 +777,8 @@ extern "C" const char *ZTT_crypto() { ZT_T_PRINTF("[crypto] Testing MIMC52 VDF... "); - const uint64_t proof = mimc52Delay("testing",7,1000); - if ((!mimc52Verify("testing",7,1000,proof))||(proof != 0x000b501115c73369)) { + const uint64_t proof = mimc52Delay("",1,1000); + if ((!mimc52Verify("",1,1000,proof))||(proof != 0x00036030471c2aec)) { ZT_T_PRINTF("FAILED (%.16llx)" ZT_EOL_S,proof); return "MIMC52 failed simple delay/verify test"; }