Some work on delay function... which is delaying us... but need to get it right because its hard as hell to change later.

This commit is contained in:
Adam Ierymenko 2020-03-09 15:19:58 -07:00
parent 5463c70aaf
commit a20aebaaf8
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
4 changed files with 187 additions and 60 deletions

View file

@ -41,6 +41,7 @@ set(core_headers
SelfAwareness.hpp
SHA512.hpp
SharedPtr.hpp
Speck128.hpp
Tag.hpp
Topology.hpp
Trace.hpp

View file

@ -14,6 +14,9 @@
#include "MIMC52.hpp"
#include "SHA512.hpp"
#include "Utils.hpp"
#include "Speck128.hpp"
#include <cstdio>
// 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<rounds;++r) {
x = (x - k[--kn & 127U]) & m52;
const uint64_t rmin1 = rounds - 1;
const uint64_t sxx = hash[4];
#pragma unroll 16
for(unsigned long r=0;r<rounds;++r) {
uint64_t sx = sxx,sy = rmin1 - r;
roundConstantGenerator.encryptXY(sx,sy);
x = (x - sy) & m52;
x = modpow52(x,e,p);
}
@ -165,18 +132,29 @@ uint64_t mimc52Delay(const void *const salt,const unsigned int saltSize,const un
bool mimc52Verify(const void *const salt,const unsigned int saltSize,unsigned long rounds,const uint64_t proof)
{
uint64_t k[128],x,p;
mimc52Init(k,salt,saltSize,p,x);
uint64_t hash[6];
SHA384(hash,salt,saltSize);
// Note: x64_cubemod() seems slower even here on all tested cores.
#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 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<rounds;++r) {
uint64_t sx = sxx,sy = r;
roundConstantGenerator.encryptXY(sx,sy);
#ifdef ZT_MIMC52_NO_FPU
#ifdef x64_cubemod
x64_cubemod(y,p);
@ -196,7 +174,7 @@ bool mimc52Verify(const void *const salt,const unsigned int saltSize,unsigned lo
y -= ((uint64_t)ii - one) * p;
y %= p;
#endif
y = (y + k[r & 127U]) & m52;
y = (y + sy) & m52;
}
return (y % p) == x;

148
node/Speck128.hpp Normal file
View file

@ -0,0 +1,148 @@
/*
* Copyright (c)2013-2020 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2024-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
#ifndef ZT_SPECK128_HPP
#define ZT_SPECK128_HPP
#include "Constants.hpp"
#include "Utils.hpp"
namespace ZeroTier {
/**
* Tiny and simple 128-bit ARX block cipher
*
* Speck does not specify a mandatory endian-ness. This implementation is
* little-endian for higher performance on the majority of platforms.
*
* @tparam R Number of rounds (default: 32)
*/
template<int R = 32>
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<uint64_t>(k);
uint64_t y = Utils::loadLittleEndian<uint64_t>(reinterpret_cast<const uint8_t *>(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<R;++i) {
const uint64_t kk = _k[i];
x = x >> 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<uint64_t>(in);
uint64_t y = Utils::loadLittleEndian<uint64_t>(reinterpret_cast<const uint8_t *>(in) + 8);
encryptXY(x,y);
Utils::storeLittleEndian<uint64_t>(out,x);
Utils::storeLittleEndian<uint64_t>(reinterpret_cast<uint8_t *>(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<uint64_t>(in);
uint64_t y = Utils::loadLittleEndian<uint64_t>(reinterpret_cast<const uint8_t *>(in) + 8);
decryptXY(x,y);
Utils::storeLittleEndian<uint64_t>(out,x);
Utils::storeLittleEndian<uint64_t>(reinterpret_cast<uint8_t *>(out) + 8,y);
}
private:
uint64_t _k[R];
};
} // namespace ZeroTier
#endif

View file

@ -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";
}