The MIMC thing was neat but unfortunately is amenable to too much GPU acceleration.

This commit is contained in:
Adam Ierymenko 2020-03-14 20:29:44 -07:00
parent 4b20638568
commit 8a379ba0a1
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
8 changed files with 64 additions and 70 deletions

View file

@ -24,7 +24,6 @@ set(core_headers
LZ4.hpp
MAC.hpp
Membership.hpp
MIMC52.hpp
MulticastGroup.hpp
Mutex.hpp
Network.hpp
@ -67,7 +66,6 @@ set(core_src
Locator.cpp
LZ4.cpp
Membership.cpp
MIMC52.cpp
Network.cpp
NetworkConfig.cpp
Node.cpp

View file

@ -16,7 +16,6 @@
#include "SHA512.hpp"
#include "Salsa20.hpp"
#include "Utils.hpp"
#include "MIMC52.hpp"
#include <cstring>
#include <cstdint>
@ -31,7 +30,7 @@ namespace {
// This is the memory-intensive hash function used to compute v0 identities from v0 public keys.
#define ZT_V0_IDENTITY_GEN_MEMORY 2097152
static void _computeMemoryHardHash(const void *const publicKey,unsigned int publicKeyBytes,void *const digest,void *const genmem) noexcept
void _computeMemoryHardHash(const void *const publicKey,unsigned int publicKeyBytes,void *const digest,void *const genmem) noexcept
{
// Digest publicKey[] to obtain initial digest
SHA512(digest,publicKey,publicKeyBytes);
@ -67,7 +66,6 @@ static void _computeMemoryHardHash(const void *const publicKey,unsigned int publ
}
struct _v0_identity_generate_cond
{
ZT_INLINE _v0_identity_generate_cond() noexcept {}
ZT_INLINE _v0_identity_generate_cond(unsigned char *sb,char *gm) noexcept : digest(sb),genmem(gm) {}
ZT_INLINE bool operator()(const uint8_t pub[ZT_C25519_PUBLIC_KEY_LEN]) const noexcept
{
@ -78,6 +76,44 @@ struct _v0_identity_generate_cond
char *genmem;
};
// This is a simpler memory-intensive hash function for V1 identity generation.
bool _v1_identity_generate_cond(const void *in,const unsigned int len)
{
uint64_t b[98304]; // 768 KiB
SHA512(b,in,len);
for(unsigned long i=8;i<98304;i+=8)
SHA512(b + i,b + (i - 8),64);
#if __BYTE_ORDER == __BIG_ENDIAN
for(unsigned int i=0;i<131072;i+=8) {
b[i] = Utils::swapBytes(b[i]);
b[i + 1] = Utils::swapBytes(b[i + 1]);
b[i + 2] = Utils::swapBytes(b[i + 2]);
b[i + 3] = Utils::swapBytes(b[i + 3]);
b[i + 4] = Utils::swapBytes(b[i + 4]);
b[i + 5] = Utils::swapBytes(b[i + 5]);
b[i + 6] = Utils::swapBytes(b[i + 6]);
b[i + 7] = Utils::swapBytes(b[i + 7]);
}
#endif
std::sort(b,b + 98304);
#if __BYTE_ORDER == __BIG_ENDIAN
for(unsigned int i=0;i<131072;i+=8) {
b[i] = Utils::swapBytes(b[i]);
b[i + 1] = Utils::swapBytes(b[i + 1]);
b[i + 2] = Utils::swapBytes(b[i + 2]);
b[i + 3] = Utils::swapBytes(b[i + 3]);
b[i + 4] = Utils::swapBytes(b[i + 4]);
b[i + 5] = Utils::swapBytes(b[i + 5]);
b[i + 6] = Utils::swapBytes(b[i + 6]);
b[i + 7] = Utils::swapBytes(b[i + 7]);
}
#endif
SHA384(b,b,sizeof(b));
return reinterpret_cast<uint8_t *>(b)[0] == 0;
}
} // anonymous namespace
const Identity Identity::NIL;
@ -103,15 +139,17 @@ bool Identity::generate(const Type t)
case P384: {
for(;;) {
// Generate C25519, Ed25519, and NIST P-384 key pairs.
_pub.nonce = 0;
v1_pow_new_keys:
C25519::generate(_pub.c25519,_priv.c25519);
ECC384GenerateKey(_pub.p384,_priv.p384);
for (;;) {
if (_v1_identity_generate_cond(&_pub,sizeof(_pub)))
break;
if (++_pub.nonce == 0) // endian-ness doesn't matter, just change the nonce each time
goto v1_pow_new_keys;
}
// Execute the MIMC52 verifiable delay function, resulting a near constant time delay relative
// to the speed of the current CPU. This result is incorporated into the final hash.
Utils::storeBigEndian(_pub.t1mimc52,mimc52Delay(&_pub,sizeof(_pub) - sizeof(_pub.t1mimc52),ZT_V1_IDENTITY_MIMC52_VDF_ROUNDS_BASE));
// Compute SHA384 fingerprint hash of keys and MIMC output and generate address directly from it.
_computeHash();
_address.setTo(_fp.hash());
if (!_address.isReserved())
@ -141,17 +179,7 @@ bool Identity::locallyValidate() const noexcept
}
case P384:
if (_address == Address(_fp.hash())) {
// The most significant 8 bits of the MIMC proof included with v1 identities can be used to store a multiplier
// that can indicate that more work than the required minimum has been performed. Right now this is never done
// but it could have some use in the future. There is no harm in doing it, and we'll accept any round count
// that is at least ZT_V1_IDENTITY_MIMC52_VDF_ROUNDS_BASE.
unsigned long rounds = (((unsigned long)_pub.t1mimc52[0] & 15U) + 1U); // max: 16 * ZT_V1_IDENTITY_MIMC52_VDF_ROUNDS_BASE
rounds *= ZT_V1_IDENTITY_MIMC52_VDF_ROUNDS_BASE;
return mimc52Verify(&_pub,sizeof(_pub) - sizeof(_pub.t1mimc52),rounds,Utils::loadBigEndian<uint64_t>(_pub.t1mimc52));
} else {
return false;
}
return ( (_address == Address(_fp.hash())) && _v1_identity_generate_cond(&_pub,sizeof(_pub)) );
}
}

View file

@ -28,7 +28,7 @@
#include <cstring>
#define ZT_IDENTITY_STRING_BUFFER_LENGTH 1024
#define ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE (ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE + 8)
#define ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE (1 + ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE)
#define ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE (ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE)
#define ZT_IDENTITY_MARSHAL_SIZE_MAX (ZT_ADDRESS_LENGTH + 4 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE)
@ -224,9 +224,9 @@ private:
uint8_t p384[ZT_ECC384_PRIVATE_KEY_SIZE];
}) _priv;
ZT_PACKED_STRUCT(struct { // do not re-order these fields
uint8_t nonce; // nonce for PoW generate/verify
uint8_t c25519[ZT_C25519_PUBLIC_KEY_LEN]; // Curve25519 and Ed25519 public keys
uint8_t p384[ZT_ECC384_PUBLIC_KEY_SIZE]; // NIST P-384 public key
uint8_t t1mimc52[8]; // Type 1 MIMC52 proof and work amount in big-endian byte order
}) _pub;
Type _type; // _type determines which fields in _priv and _pub are used
bool _hasPrivate;

View file

@ -22,7 +22,7 @@
#include "Utils.hpp"
#include "TriviallyCopyable.hpp"
#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64))
#ifdef ZT_ARCH_X64
#include <xmmintrin.h>
#include <emmintrin.h>
#include <immintrin.h>

View file

@ -39,7 +39,6 @@
#include "FCV.hpp"
#include "SHA512.hpp"
#include "Defragmenter.hpp"
#include "MIMC52.hpp"
#include "Fingerprint.hpp"
#include <cstdint>
@ -698,9 +697,10 @@ extern "C" const char *ZTT_general()
ZT_T_PRINTF("OK" ZT_EOL_S);
{
ZT_T_PRINTF("[general] Example V1 identity: ");
id.generate(Identity::P384);
id.toString(true,tmp);
ZT_T_PRINTF("[general] Example V1 identity: %s\n",tmp);
ZT_T_PRINTF("%s" ZT_EOL_S,tmp);
id.fingerprint().toString(tmp);
ZT_T_PRINTF("[general] Fingerprint: %s" ZT_EOL_S,tmp);
}
@ -782,24 +782,6 @@ extern "C" const char *ZTT_crypto()
ZT_T_PRINTF("OK" ZT_EOL_S);
}
{
ZT_T_PRINTF("[crypto] Testing MIMC52 VDF... ");
const uint64_t proof = mimc52Delay("",1,1000);
if ((!mimc52Verify("",1,1000,proof))||(proof != 0x000cc1abe2dde7a3)) {
ZT_T_PRINTF("FAILED (%.16llx)" ZT_EOL_S,proof);
return "MIMC52 failed simple delay/verify test";
}
for(int i=0;i<1024;++i) {
uint64_t in = Utils::random();
unsigned long r = 1 + (unsigned long)(Utils::random() % 1024);
if (!mimc52Verify(&in,sizeof(in),r,mimc52Delay(&in,sizeof(in),r))) {
ZT_T_PRINTF("FAILED (random input test)");
return "MIMC52 failed random input test";
}
}
ZT_T_PRINTF("OK (%.16llx)" ZT_EOL_S,proof);
}
{
uint8_t agree0[32],agree1[32],kh[64],sig[96];
ZT_T_PRINTF("[crypto] Testing C25519/Ed25519... ");
@ -1030,21 +1012,6 @@ extern "C" const char *ZTT_benchmarkCrypto()
ZT_T_PRINTF("%.4f MiB/sec" ZT_EOL_S,((16384.0 * 10000.0) / 1048576.0) / ((double)(end - start) / 1000.0));
}
{
ZT_T_PRINTF("[crypto] Benchmarking MIMC52 VDF delay... ");
int64_t start = now();
const uint64_t proof = mimc52Delay("testing",7,250000);
int64_t end = now();
int64_t dtime = end - start;
ZT_T_PRINTF("%.4f μs/round" ZT_EOL_S,((double)dtime * 1000.0) / 250000.0);
ZT_T_PRINTF("[crypto] Benchmarking MIMC52 VDF verify... ");
start = now();
foo = (uint8_t)mimc52Verify("testing",7,1000000,proof); // doesn't matter if return is true or false here
end = now();
int64_t vtime = end - start;
ZT_T_PRINTF("%.8f μs/round, %.4fX faster than delay" ZT_EOL_S,((double)vtime * 1000.0) / 1000000.0,(double)(dtime / 250000.0) / (double)(vtime / 1000000.0));
}
{
ZT_T_PRINTF("[crypto] Benchmarking AES-CTR... ");
AES aes(AES_CTR_TEST_VECTOR_0_KEY);
@ -1169,29 +1136,31 @@ extern "C" const char *ZTT_benchmarkCrypto()
}
{
ZT_T_PRINTF("[crypto] Benchmarking V0 Identity generation... ");
ZT_T_PRINTF("[crypto] Benchmarking V0 Identity generation...");
Identity id;
int64_t start = now();
for(long i=0;i<5;++i) {
for(long i=0;i<10;++i) {
id.generate(Identity::C25519);
foo = (uint8_t)id.address().toInt();
ZT_T_PRINTF(".");
}
int64_t end = now();
ZT_T_PRINTF("%.4f ms/generation (average, can vary quite a bit)" ZT_EOL_S,(double)(end - start) / 5.0);
ZT_T_PRINTF("[crypto] Benchmarking V0 Identity full validation... ");
ZT_T_PRINTF(" %.4f ms/generation (average)" ZT_EOL_S,(double)(end - start) / 10.0);
ZT_T_PRINTF("[crypto] Benchmarking V0 Identity full validation...");
start = now();
for(long i=0;i<10;++i)
foo = (uint8_t)id.locallyValidate();
end = now();
ZT_T_PRINTF("%.4f ms/validation" ZT_EOL_S,(double)(end - start) / 10.0);
ZT_T_PRINTF("[crypto] Benchmarking V1 Identity generation... ");
ZT_T_PRINTF("[crypto] Benchmarking V1 Identity generation...");
start = now();
for(long i=0;i<5;++i) {
for(long i=0;i<10;++i) {
id.generate(Identity::P384);
foo = (uint8_t)id.address().toInt();
ZT_T_PRINTF(".");
}
end = now();
ZT_T_PRINTF("%.4f ms/generation (relatively constant time)" ZT_EOL_S,(double)(end - start) / 5.0);
ZT_T_PRINTF(" %.4f ms/generation (average)" ZT_EOL_S,(double)(end - start) / 10.0);
ZT_T_PRINTF("[crypto] Benchmarking V1 Identity full validation... ");
start = now();
for(long i=0;i<100;++i)

View file

@ -453,10 +453,9 @@ bool scopy(char *const dest,const unsigned int len,const char *const src) noexce
dest[len - 1] = 0;
return false;
}
const char c = src[i];
dest[i] = c;
if (c == 0)
if ((dest[i] = src[i]) == 0)
return true;
++i;
}
}