diff --git a/node/MIMC52.cpp b/attic/MIMC52.cpp similarity index 100% rename from node/MIMC52.cpp rename to attic/MIMC52.cpp diff --git a/node/MIMC52.hpp b/attic/MIMC52.hpp similarity index 100% rename from node/MIMC52.hpp rename to attic/MIMC52.hpp diff --git a/node/CMakeLists.txt b/node/CMakeLists.txt index 047e11f92..b36d8e760 100644 --- a/node/CMakeLists.txt +++ b/node/CMakeLists.txt @@ -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 diff --git a/node/Identity.cpp b/node/Identity.cpp index afd5d4830..97aac3fd5 100644 --- a/node/Identity.cpp +++ b/node/Identity.cpp @@ -16,7 +16,6 @@ #include "SHA512.hpp" #include "Salsa20.hpp" #include "Utils.hpp" -#include "MIMC52.hpp" #include #include @@ -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(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(_pub.t1mimc52)); - } else { - return false; - } + return ( (_address == Address(_fp.hash())) && _v1_identity_generate_cond(&_pub,sizeof(_pub)) ); } } diff --git a/node/Identity.hpp b/node/Identity.hpp index db3ccc6cb..2ed39edf9 100644 --- a/node/Identity.hpp +++ b/node/Identity.hpp @@ -28,7 +28,7 @@ #include #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; diff --git a/node/Salsa20.hpp b/node/Salsa20.hpp index cfe7e345d..50aa22b94 100644 --- a/node/Salsa20.hpp +++ b/node/Salsa20.hpp @@ -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 #include #include diff --git a/node/Tests.cpp b/node/Tests.cpp index d38d8c155..9cd30aaf2 100644 --- a/node/Tests.cpp +++ b/node/Tests.cpp @@ -39,7 +39,6 @@ #include "FCV.hpp" #include "SHA512.hpp" #include "Defragmenter.hpp" -#include "MIMC52.hpp" #include "Fingerprint.hpp" #include @@ -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) diff --git a/node/Utils.cpp b/node/Utils.cpp index 1f900bdc2..c7ad82337 100644 --- a/node/Utils.cpp +++ b/node/Utils.cpp @@ -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; } }