mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-20 14:06:54 +02:00
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:
parent
5463c70aaf
commit
a20aebaaf8
4 changed files with 187 additions and 60 deletions
|
@ -41,6 +41,7 @@ set(core_headers
|
|||
SelfAwareness.hpp
|
||||
SHA512.hpp
|
||||
SharedPtr.hpp
|
||||
Speck128.hpp
|
||||
Tag.hpp
|
||||
Topology.hpp
|
||||
Trace.hpp
|
||||
|
|
|
@ -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
148
node/Speck128.hpp
Normal 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
|
|
@ -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";
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue