diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 4c172df66..afde06887 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -33,6 +33,7 @@ set(core_headers LZ4.hpp MAC.hpp Member.hpp + MIMC52.hpp MulticastGroup.hpp Mutex.hpp Network.hpp @@ -80,6 +81,7 @@ set(core_src Locator.cpp LZ4.cpp Member.cpp + MIMC52.cpp Network.cpp NetworkConfig.cpp Node.cpp diff --git a/core/Identity.cpp b/core/Identity.cpp index b7624f13e..bc5cde640 100644 --- a/core/Identity.cpp +++ b/core/Identity.cpp @@ -18,6 +18,7 @@ #include "Poly1305.hpp" #include "Utils.hpp" #include "Endpoint.hpp" +#include "MIMC52.hpp" #include #include @@ -27,8 +28,6 @@ namespace ZeroTier { namespace { -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // This is the memory-intensive hash function used to compute v0 identities from v0 public keys. #define ZT_V0_IDENTITY_GEN_MEMORY 2097152 @@ -82,67 +81,25 @@ struct identityV0ProofOfWorkCriteria char *genmem; }; -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -#define ZT_IDENTITY_V1_POW_MEMORY_SIZE 131072 - -struct p_CompareLittleEndian +void v1ChallengeFromPub(const uint8_t pub[ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE], uint64_t challenge[4]) { -#if __BYTE_ORDER == __BIG_ENDIAN - ZT_INLINE bool operator()(const uint64_t a,const uint64_t b) const noexcept - { return Utils::swapBytes(a) < Utils::swapBytes(b); } -#else - ZT_INLINE bool operator()(const uint64_t a, const uint64_t b) const noexcept - { return a < b; } - -#endif -}; - -// This is a simpler memory-intensive frankenhash for V1 identity generation. -bool identityV1ProofOfWorkCriteria(const void *in, const unsigned int len, uint64_t *const w) -{ - // Fill work buffer with pseudorandom bytes using a construction that should be - // relatively hostile to GPU acceleration. GPUs usually implement branching by - // executing all branches and then selecting the answer, which means this - // construction should require a GPU to do ~3X the work of a CPU per iteration. - SHA512(w, in, len); - for (unsigned int i = 8, j = 0; i < (ZT_IDENTITY_V1_POW_MEMORY_SIZE / 8);) { - uint64_t *const ww = w + i; - const uint64_t *const wp = w + j; - i += 8; - j += 8; - if ((wp[0] & 7U) == 0) { - SHA512(ww, wp, 64); - } else if ((wp[1] & 15U) == 0) { - ww[0] = Utils::hton(Utils::ntoh(wp[0]) % 4503599627370101ULL); - ww[1] = Utils::hton(Utils::ntoh(wp[1]) % 4503599627370161ULL); - ww[2] = Utils::hton(Utils::ntoh(wp[2]) % 4503599627370227ULL); - ww[3] = Utils::hton(Utils::ntoh(wp[3]) % 4503599627370287ULL); - ww[4] = Utils::hton(Utils::ntoh(wp[4]) % 4503599627370299ULL); - ww[5] = Utils::hton(Utils::ntoh(wp[5]) % 4503599627370323ULL); - ww[6] = Utils::hton(Utils::ntoh(wp[6]) % 4503599627370353ULL); - ww[7] = Utils::hton(Utils::ntoh(wp[7]) % 4503599627370449ULL); - SHA384(ww, wp, 128); - } else { - Salsa20(wp, wp + 4).crypt12(wp, ww, 64); - } - } - - // Sort 64-bit integers (little-endian) into ascending order and compute a - // cryptographic checksum. Sorting makes the order of values dependent on all - // other values, making a speed competitive implementation that skips on the - // memory requirement extremely hard. - std::sort(w, w + (ZT_IDENTITY_V1_POW_MEMORY_SIZE / 8), p_CompareLittleEndian()); - Poly1305::compute(w, w, ZT_IDENTITY_V1_POW_MEMORY_SIZE, w); - - // PoW criteria passed if this is true. The value 1093 was chosen experimentally - // to yield a good average performance balancing fast setup with intentional - // identity collision resistance. - return (Utils::ntoh(w[0]) % 1000U) == 0; + // This builds a 256-bit challenge by XORing the two public keys together. This doesn't need to be + // a hash, just different for different public keys. Public keys are basically kind of hashes of + // private keys, so that's good enough. This is only used to seed a PRNG in MIMC52 for a proof of + // sequential work. It's not used for authentication beyond checking PoW. + Utils::copy< 32 >(challenge, pub + 7); + challenge[0] ^= Utils::loadMachineEndian< uint64_t >(pub + 40); + challenge[1] ^= Utils::loadMachineEndian< uint64_t >(pub + 48); + challenge[2] ^= Utils::loadMachineEndian< uint64_t >(pub + 56); + challenge[3] ^= Utils::loadMachineEndian< uint64_t >(pub + 64); + challenge[0] ^= Utils::loadMachineEndian< uint64_t >(pub + 72); + challenge[1] ^= Utils::loadMachineEndian< uint64_t >(pub + 80); + challenge[2] ^= Utils::loadMachineEndian< uint64_t >(pub + 88); + challenge[3] ^= Utils::loadMachineEndian< uint64_t >(pub + 96); + challenge[0] ^= Utils::loadMachineEndian< uint64_t >(pub + 104); + challenge[1] ^= Utils::loadMachineEndian< uint64_t >(pub + 112); } -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - } // anonymous namespace const Identity Identity::NIL; @@ -170,38 +127,29 @@ bool Identity::generate(const Type t) } break; - case P384: { - //uint64_t w[ZT_IDENTITY_V1_POW_MEMORY_SIZE / 8]; - uint64_t *const w = (uint64_t *)malloc(ZT_IDENTITY_V1_POW_MEMORY_SIZE); - if (!w) - return false; - try { - for (;;) { - // Loop until we pass the PoW criteria. The nonce is only 8 bits, so generate - // some new key material every time it wraps. The ECC384 generator is slightly - // faster so use that one. - m_pub[0] = 0; // zero nonce - C25519::generateCombined(m_pub + 1, m_priv + 1); - ECC384GenerateKey(m_pub + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE, m_priv + ZT_C25519_COMBINED_PRIVATE_KEY_SIZE); - for (;;) { - if (identityV1ProofOfWorkCriteria(m_pub, sizeof(m_pub), w)) - break; - if (++m_pub[0] == 0) - ECC384GenerateKey(m_pub + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE, m_priv + ZT_C25519_COMBINED_PRIVATE_KEY_SIZE); - } + case P384: + for (;;) { + C25519::generateCombined(m_pub + 7, m_priv); + ECC384GenerateKey(m_pub + 7 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE, m_priv + ZT_C25519_COMBINED_PRIVATE_KEY_SIZE); - // If we passed PoW then check that the address is valid, otherwise loop - // back around and run the whole process again. - m_computeHash(); - const Address addr(m_fp.hash); - if (!addr.isReserved()) { - m_fp.address = addr; - break; - } + uint64_t challenge[4]; + v1ChallengeFromPub(m_pub, challenge); + const uint64_t proof = MIMC52::delay(reinterpret_cast(challenge), ZT_IDENTITY_TYPE1_MIMC52_ROUNDS); + m_pub[0] = (uint8_t)(proof >> 48U); + m_pub[1] = (uint8_t)(proof >> 40U); + m_pub[2] = (uint8_t)(proof >> 32U); + m_pub[3] = (uint8_t)(proof >> 24U); + m_pub[4] = (uint8_t)(proof >> 16U); + m_pub[5] = (uint8_t)(proof >> 8U); + m_pub[6] = (uint8_t)proof; + + m_computeHash(); + const Address addr(m_fp.hash); + if (!addr.isReserved()) { + m_fp.address = addr; + break; } - } catch ( ... ) {} - free(w); - } + } break; default: @@ -216,6 +164,7 @@ bool Identity::locallyValidate() const noexcept try { if ((m_fp) && ((!Address(m_fp.address).isReserved()))) { switch (m_type) { + case C25519: { uint8_t digest[64]; char *const genmem = (char *)malloc(ZT_V0_IDENTITY_GEN_MEMORY); @@ -225,16 +174,15 @@ bool Identity::locallyValidate() const noexcept free(genmem); return ((Address(digest + 59) == m_fp.address) && (digest[0] < 17)); } - case P384: { - if (Address(m_fp.hash) != m_fp.address) - return false; - uint64_t *const w = (uint64_t *)malloc(ZT_IDENTITY_V1_POW_MEMORY_SIZE); - if (!w) - return false; - const bool valid = identityV1ProofOfWorkCriteria(m_pub, sizeof(m_pub), w); - free(w); - return valid; - } + + case P384: + if (Address(m_fp.hash) == m_fp.address) { + uint64_t challenge[4]; + v1ChallengeFromPub(m_pub, challenge); + return MIMC52::verify(reinterpret_cast(challenge), ZT_IDENTITY_TYPE1_MIMC52_ROUNDS, ((uint64_t)m_pub[0] << 48U) | ((uint64_t)m_pub[1] << 40U) | ((uint64_t)m_pub[2] << 32U) | ((uint64_t)m_pub[3] << 24U) | ((uint64_t)m_pub[4] << 16U) | ((uint64_t)m_pub[5] << 8U) | (uint64_t)m_pub[6]); + } + return false; + } } } catch (...) {} @@ -245,12 +193,15 @@ void Identity::hashWithPrivate(uint8_t h[ZT_FINGERPRINT_HASH_SIZE]) const { if (m_hasPrivate) { switch (m_type) { + case C25519: SHA384(h, m_pub, ZT_C25519_COMBINED_PUBLIC_KEY_SIZE, m_priv, ZT_C25519_COMBINED_PRIVATE_KEY_SIZE); return; + case P384: SHA384(h, m_pub, sizeof(m_pub), m_priv, sizeof(m_priv)); return; + } } Utils::zero< ZT_FINGERPRINT_HASH_SIZE >(h); @@ -260,15 +211,16 @@ unsigned int Identity::sign(const void *data, unsigned int len, void *sig, unsig { if (m_hasPrivate) { switch (m_type) { + case C25519: if (siglen >= ZT_C25519_SIGNATURE_LEN) { C25519::sign(m_priv, m_pub, data, len, sig); return ZT_C25519_SIGNATURE_LEN; } break; + case P384: if (siglen >= ZT_ECC384_SIGNATURE_SIZE) { - // SECURITY: signatures also include the public keys to further enforce their coupling. static_assert(ZT_ECC384_SIGNATURE_HASH_SIZE == ZT_SHA384_DIGEST_SIZE, "weird!"); uint8_t h[ZT_ECC384_SIGNATURE_HASH_SIZE]; SHA384(h, data, len, m_pub, ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE); @@ -276,6 +228,7 @@ unsigned int Identity::sign(const void *data, unsigned int len, void *sig, unsig return ZT_ECC384_SIGNATURE_SIZE; } break; + } } return 0; @@ -284,15 +237,18 @@ unsigned int Identity::sign(const void *data, unsigned int len, void *sig, unsig bool Identity::verify(const void *data, unsigned int len, const void *sig, unsigned int siglen) const { switch (m_type) { + case C25519: return C25519::verify(m_pub, data, len, sig, siglen); + case P384: if (siglen == ZT_ECC384_SIGNATURE_SIZE) { uint8_t h[ZT_ECC384_SIGNATURE_HASH_SIZE]; SHA384(h, data, len, m_pub, ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE); - return ECC384ECDSAVerify(m_pub + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE, h, (const uint8_t *)sig); + return ECC384ECDSAVerify(m_pub + 7 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE, h, (const uint8_t *)sig); } break; + } return false; } @@ -315,7 +271,7 @@ bool Identity::agree(const Identity &id, uint8_t key[ZT_SYMMETRIC_KEY_SIZE]) con // or something. For those who don't trust P384 this means the privacy of // your traffic is also protected by C25519. C25519::agree(m_priv, id.m_pub, rawkey); - ECC384ECDH(id.m_pub + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE, m_priv + ZT_C25519_COMBINED_PRIVATE_KEY_SIZE, rawkey + ZT_C25519_ECDH_SHARED_SECRET_SIZE); + ECC384ECDH(id.m_pub + 7 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE, m_priv + ZT_C25519_COMBINED_PRIVATE_KEY_SIZE, rawkey + ZT_C25519_ECDH_SHARED_SECRET_SIZE); SHA384(key, rawkey, ZT_C25519_ECDH_SHARED_SECRET_SIZE + ZT_ECC384_SHARED_SECRET_SIZE); return true; } @@ -516,15 +472,15 @@ int Identity::unmarshal(const uint8_t *data, const int len) noexcept return -1; privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE]; - if (privlen == ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE) { + if (privlen == 0) { + m_hasPrivate = false; + return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1; + } else if (privlen == ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE) { if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE)) return -1; m_hasPrivate = true; Utils::copy< ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE >(&m_priv, data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1); return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE; - } else if (privlen == 0) { - m_hasPrivate = false; - return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1; } break; @@ -539,9 +495,11 @@ void Identity::m_computeHash() default: m_fp.zero(); break; + case C25519: SHA384(m_fp.hash, m_pub, ZT_C25519_COMBINED_PUBLIC_KEY_SIZE); break; + case P384: SHA384(m_fp.hash, m_pub, ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE); break; diff --git a/core/Identity.hpp b/core/Identity.hpp index 37aedb4e3..b074d02f0 100644 --- a/core/Identity.hpp +++ b/core/Identity.hpp @@ -27,9 +27,10 @@ #define ZT_IDENTITY_STRING_BUFFER_LENGTH 1024 #define ZT_IDENTITY_C25519_PUBLIC_KEY_SIZE ZT_C25519_COMBINED_PUBLIC_KEY_SIZE #define ZT_IDENTITY_C25519_PRIVATE_KEY_SIZE ZT_C25519_COMBINED_PRIVATE_KEY_SIZE -#define ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE (1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE + ZT_ECC384_PUBLIC_KEY_SIZE) +#define ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE (7 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE + ZT_ECC384_PUBLIC_KEY_SIZE) #define ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE (ZT_C25519_COMBINED_PRIVATE_KEY_SIZE + 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) +#define ZT_IDENTITY_TYPE1_MIMC52_ROUNDS 262144 namespace ZeroTier { diff --git a/core/InetAddress.cpp b/core/InetAddress.cpp index a355d3d84..f0cb12a6e 100644 --- a/core/InetAddress.cpp +++ b/core/InetAddress.cpp @@ -464,7 +464,9 @@ InetAddress InetAddress::makeIpv66plane(uint64_t nwid, uint64_t zeroTierAddress) return r; } -const int ZT_AF_INET = (int)AF_INET; -const int ZT_AF_INET6 = (int)AF_INET6; +extern "C" { +extern const int ZT_AF_INET = (int)AF_INET; +extern const int ZT_AF_INET6 = (int)AF_INET6; +} } // namespace ZeroTier diff --git a/core/MIMC52.cpp b/core/MIMC52.cpp new file mode 100644 index 000000000..90d70d398 --- /dev/null +++ b/core/MIMC52.cpp @@ -0,0 +1,127 @@ +/* + * 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: 2025-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. + */ +/****/ + +#include "Constants.hpp" +#include "AES.hpp" +#include "MIMC52.hpp" + +namespace { + +// The highest 1024 primes less than 2 ^ 52 (only the least significant 32 bits are needed) +const uint32_t ZT_MIMC52_PRIMES[1024] = {4294895267, 4294895477, 4294895513, 4294895519, 4294895543, 4294895567, 4294895657, 4294895711, 4294895777, 4294895861, 4294895909, 4294895921, 4294895969, 4294896011, 4294896149, 4294896227, 4294896401, 4294896473, 4294896527, 4294896563, 4294896653, 4294896731, 4294896863, 4294896899, 4294896983, 4294897037, 4294897103, 4294897331, 4294897349, 4294897451, 4294897571, 4294897661, 4294897703, 4294897757, 4294897793, 4294897811, 4294897817, 4294897829, 4294897877, 4294897919, 4294897991, 4294898027, 4294898129, 4294898153, 4294898231, 4294898273, + 4294898279, 4294898291, 4294898363, 4294898369, 4294898417, 4294898423, 4294898453, 4294898489, 4294898573, 4294898579, 4294898639, 4294898693, 4294898747, 4294898759, 4294898867, 4294898879, 4294898909, 4294898921, 4294898933, 4294899011, 4294899041, 4294899047, 4294899203, 4294899221, 4294899227, 4294899287, 4294899341, 4294899431, 4294899509, 4294899533, 4294899539, 4294899551, 4294899629, 4294899791, 4294899809, 4294899971, 4294900001, 4294900007, 4294900013, 4294900307, 4294900331, 4294900427, 4294900469, 4294900481, 4294900541, 4294900583, + 4294900781, 4294900853, 4294900931, 4294900991, 4294901033, 4294901087, 4294901159, 4294901267, 4294901393, 4294901411, 4294901489, 4294901657, 4294902011, 4294902071, 4294902101, 4294902107, 4294902353, 4294902377, 4294902599, 4294902647, 4294902743, 4294902869, 4294902977, 4294903067, 4294903103, 4294903259, 4294903289, 4294903397, 4294903421, 4294903493, 4294903577, 4294903631, 4294903637, 4294903733, 4294903799, 4294903823, 4294904003, 4294904033, 4294904081, 4294904129, 4294904279, 4294904297, 4294904303, 4294904333, 4294904351, 4294904381, + 4294904453, 4294904519, 4294904561, 4294904639, 4294904657, 4294904747, 4294904807, 4294904843, 4294905089, 4294905149, 4294905293, 4294905299, 4294905311, 4294905443, 4294905479, 4294905539, 4294905623, 4294905641, 4294905671, 4294905707, 4294905887, 4294905977, 4294906091, 4294906103, 4294906139, 4294906157, 4294906223, 4294906259, 4294906487, 4294906493, 4294906523, 4294906547, 4294906553, 4294906571, 4294906577, 4294906589, 4294906703, 4294906733, 4294906763, 4294906841, 4294906859, 4294906937, 4294907057, 4294907063, 4294907141, 4294907231, + 4294907249, 4294907261, 4294907267, 4294907387, 4294907417, 4294907567, 4294907603, 4294907699, 4294907789, 4294907849, 4294907873, 4294907879, 4294908023, 4294908071, 4294908119, 4294908209, 4294908227, 4294908329, 4294908491, 4294908503, 4294908569, 4294908653, 4294908713, 4294908719, 4294908791, 4294908839, 4294908869, 4294908989, 4294909031, 4294909067, 4294909109, 4294909253, 4294909529, 4294909589, 4294909643, 4294909739, 4294909799, 4294909811, 4294909853, 4294910003, 4294910039, 4294910189, 4294910201, 4294910219, 4294910273, 4294910333, + 4294910369, 4294910393, 4294910471, 4294910549, 4294910651, 4294910669, 4294910681, 4294910711, 4294910753, 4294910801, 4294910981, 4294911053, 4294911143, 4294911227, 4294911239, 4294911359, 4294911383, 4294911407, 4294911521, 4294911551, 4294911611, 4294911641, 4294911689, 4294911719, 4294911869, 4294912109, 4294912133, 4294912151, 4294912187, 4294912223, 4294912331, 4294912439, 4294912607, 4294912703, 4294912859, 4294912871, 4294912907, 4294912961, 4294913003, 4294913111, 4294913309, 4294913333, 4294913357, 4294913399, 4294913411, 4294913459, + 4294913501, 4294913531, 4294913591, 4294913609, 4294913663, 4294913783, 4294913819, 4294913903, 4294914137, 4294914413, 4294914473, 4294914497, 4294914527, 4294914551, 4294914593, 4294914611, 4294914659, 4294914671, 4294914743, 4294914863, 4294914917, 4294915061, 4294915103, 4294915139, 4294915217, 4294915223, 4294915253, 4294915283, 4294915373, 4294915433, 4294915607, 4294916069, 4294916213, 4294916267, 4294916303, 4294916393, 4294916441, 4294916477, 4294916507, 4294916573, 4294916633, 4294916687, 4294916783, 4294916837, 4294916897, 4294916921, + 4294917029, 4294917047, 4294917101, 4294917203, 4294917287, 4294917299, 4294917389, 4294917437, 4294917527, 4294917557, 4294917611, 4294917617, 4294917689, 4294917821, 4294917857, 4294917917, 4294917941, 4294918169, 4294918187, 4294918307, 4294918409, 4294918433, 4294918481, 4294918703, 4294918709, 4294918733, 4294918799, 4294918871, 4294919009, 4294919249, 4294919279, 4294919291, 4294919363, 4294919381, 4294919441, 4294919447, 4294919549, 4294919579, 4294919633, 4294919657, 4294919669, 4294919693, 4294919711, 4294920029, 4294920059, 4294920089, + 4294920197, 4294920239, 4294920257, 4294920263, 4294920269, 4294920341, 4294920353, 4294920407, 4294920503, 4294920599, 4294920647, 4294920743, 4294920803, 4294920809, 4294920881, 4294920899, 4294920983, 4294921043, 4294921139, 4294921151, 4294921181, 4294921229, 4294921289, 4294921331, 4294921343, 4294921391, 4294921469, 4294921709, 4294921721, 4294921823, 4294921847, 4294921889, 4294922057, 4294922171, 4294922201, 4294922237, 4294922309, 4294922399, 4294922447, 4294922507, 4294922513, 4294922549, 4294922609, 4294922663, 4294922861, 4294922933, + 4294923101, 4294923191, 4294923209, 4294923221, 4294923251, 4294923263, 4294923359, 4294923371, 4294923377, 4294923461, 4294923521, 4294923953, 4294924001, 4294924091, 4294924121, 4294924319, 4294924397, 4294924571, 4294924583, 4294924751, 4294924817, 4294924823, 4294924847, 4294924877, 4294925003, 4294925027, 4294925117, 4294925237, 4294925243, 4294925297, 4294925369, 4294925627, 4294925639, 4294925729, 4294925747, 4294925873, 4294925891, 4294925933, 4294926047, 4294926059, 4294926209, 4294926221, 4294926233, 4294926257, 4294926329, 4294926371, + 4294926401, 4294926413, 4294926437, 4294926563, 4294926569, 4294926917, 4294926923, 4294926947, 4294926971, 4294927067, 4294927073, 4294927151, 4294927349, 4294927367, 4294927403, 4294927481, 4294927523, 4294927553, 4294927589, 4294927649, 4294927673, 4294927727, 4294927739, 4294927763, 4294927889, 4294928183, 4294928207, 4294928249, 4294928327, 4294928351, 4294928399, 4294928483, 4294928489, 4294928543, 4294928597, 4294928951, 4294928963, 4294928981, 4294929017, 4294929059, 4294929161, 4294929197, 4294929233, 4294929269, 4294929311, 4294929323, + 4294929341, 4294929383, 4294929401, 4294929497, 4294929509, 4294929581, 4294929707, 4294929743, 4294930043, 4294930121, 4294930193, 4294930223, 4294930349, 4294930403, 4294930571, 4294930613, 4294930721, 4294930751, 4294930877, 4294930931, 4294930961, 4294930967, 4294930973, 4294931021, 4294931051, 4294931057, 4294931063, 4294931219, 4294931273, 4294931339, 4294931423, 4294931441, 4294931453, 4294931567, 4294931639, 4294931717, 4294931897, 4294931969, 4294932023, 4294932053, 4294932239, 4294932299, 4294932443, 4294932671, 4294932677, 4294932731, + 4294932743, 4294932767, 4294932773, 4294932779, 4294932881, 4294932899, 4294932929, 4294933067, 4294933277, 4294933307, 4294933343, 4294933451, 4294933523, 4294933763, 4294933793, 4294933829, 4294933847, 4294933871, 4294933997, 4294934033, 4294934111, 4294934207, 4294934243, 4294934267, 4294934279, 4294934291, 4294934327, 4294934363, 4294934423, 4294934489, 4294934561, 4294934867, 4294934921, 4294934969, 4294935137, 4294935239, 4294935299, 4294935431, 4294935539, 4294935629, 4294935701, 4294935791, 4294935797, 4294935803, 4294935959, 4294936001, + 4294936007, 4294936037, 4294936079, 4294936127, 4294936163, 4294936247, 4294936307, 4294936331, 4294936409, 4294936451, 4294936601, 4294936607, 4294936619, 4294936667, 4294936709, 4294936733, 4294936751, 4294936763, 4294936829, 4294936937, 4294936997, 4294937027, 4294937051, 4294937093, 4294937177, 4294937213, 4294937291, 4294937381, 4294937417, 4294937429, 4294937681, 4294937693, 4294937753, 4294937771, 4294937813, 4294937837, 4294937891, 4294937969, 4294938071, 4294938101, 4294938323, 4294938371, 4294938401, 4294938467, 4294938473, 4294938521, + 4294938599, 4294938731, 4294938779, 4294938833, 4294938899, 4294938977, 4294938983, 4294939067, 4294939127, 4294939223, 4294939277, 4294939331, 4294939337, 4294939391, 4294939457, 4294939559, 4294939673, 4294939691, 4294939901, 4294939991, 4294940087, 4294940093, 4294940189, 4294940213, 4294940417, 4294940657, 4294940699, 4294940753, 4294940801, 4294940873, 4294940951, 4294941047, 4294941143, 4294941161, 4294941227, 4294941281, 4294941377, 4294941509, 4294941551, 4294941701, 4294941731, 4294941767, 4294941911, 4294941923, 4294942043, 4294942139, + 4294942313, 4294942343, 4294942373, 4294942427, 4294942529, 4294942601, 4294942649, 4294942673, 4294942679, 4294942733, 4294942769, 4294942811, 4294942961, 4294943129, 4294943141, 4294943219, 4294943369, 4294943423, 4294943471, 4294943651, 4294943687, 4294943717, 4294943729, 4294943747, 4294943759, 4294943813, 4294943819, 4294943891, 4294944077, 4294944191, 4294944233, 4294944239, 4294944353, 4294944389, 4294944581, 4294944623, 4294944629, 4294944659, 4294944821, 4294945031, 4294945157, 4294945211, 4294945229, 4294945301, 4294945337, 4294945343, + 4294945511, 4294945547, 4294945667, 4294945709, 4294945757, 4294945841, 4294945991, 4294946033, 4294946099, 4294946153, 4294946477, 4294946687, 4294946747, 4294946957, 4294946993, 4294947023, 4294947131, 4294947167, 4294947287, 4294947311, 4294947413, 4294947581, 4294947599, 4294947671, 4294947851, 4294947959, 4294948067, 4294948073, 4294948193, 4294948259, 4294948421, 4294948451, 4294948613, 4294948673, 4294948883, 4294949027, 4294949057, 4294949069, 4294949519, 4294949531, 4294949603, 4294949609, 4294949627, 4294949693, 4294949729, 4294949741, + 4294949807, 4294949921, 4294949939, 4294949981, 4294949993, 4294950083, 4294950173, 4294950197, 4294950251, 4294950287, 4294950317, 4294950323, 4294950329, 4294950581, 4294950593, 4294950617, 4294950629, 4294950713, 4294950929, 4294951151, 4294951163, 4294951169, 4294951379, 4294951583, 4294951613, 4294951853, 4294951907, 4294951913, 4294951937, 4294951961, 4294952063, 4294952183, 4294952393, 4294952543, 4294952549, 4294952597, 4294952627, 4294952687, 4294952723, 4294952729, 4294952789, 4294952819, 4294952873, 4294952891, 4294952903, 4294952969, + 4294952999, 4294953023, 4294953107, 4294953173, 4294953281, 4294953341, 4294953431, 4294953599, 4294953689, 4294953719, 4294953827, 4294953887, 4294953977, 4294954073, 4294954079, 4294954157, 4294954217, 4294954283, 4294954607, 4294954667, 4294954859, 4294954901, 4294954973, 4294955081, 4294955237, 4294955273, 4294955327, 4294955441, 4294955507, 4294955591, 4294955789, 4294955831, 4294955837, 4294955927, 4294955963, 4294955969, 4294955987, 4294956041, 4294956047, 4294956197, 4294956323, 4294956359, 4294956551, 4294956593, 4294956623, 4294956629, + 4294956641, 4294956719, 4294956761, 4294956767, 4294956797, 4294956821, 4294956833, 4294957037, 4294957079, 4294957103, 4294957181, 4294957349, 4294957379, 4294957433, 4294957463, 4294957511, 4294957577, 4294957727, 4294957859, 4294957877, 4294958039, 4294958153, 4294958309, 4294958417, 4294958441, 4294958693, 4294958717, 4294958753, 4294958903, 4294958909, 4294959017, 4294959071, 4294959107, 4294959161, 4294959257, 4294959299, 4294959329, 4294959431, 4294959593, 4294959599, 4294959659, 4294959893, 4294959917, 4294959983, 4294960001, 4294960031, + 4294960061, 4294960079, 4294960097, 4294960271, 4294960283, 4294960349, 4294960367, 4294960421, 4294960529, 4294960541, 4294960583, 4294960613, 4294960673, 4294960691, 4294960697, 4294960787, 4294960919, 4294961003, 4294961039, 4294961153, 4294961159, 4294961171, 4294961321, 4294961411, 4294961471, 4294961507, 4294961537, 4294961669, 4294961717, 4294961741, 4294961873, 4294962059, 4294962137, 4294962167, 4294962263, 4294962281, 4294962311, 4294962341, 4294962413, 4294962521, 4294962563, 4294962761, 4294962893, 4294963103, 4294963163, 4294963223, + 4294963313, 4294963349, 4294963427, 4294963547, 4294963559, 4294963721, 4294963799, 4294963817, 4294963901, 4294963919, 4294964021, 4294964279, 4294964297, 4294964363, 4294964387, 4294964411, 4294964567, 4294964603, 4294964687, 4294964777, 4294965041, 4294965071, 4294965119, 4294965221, 4294965251, 4294965287, 4294965413, 4294965569, 4294965647, 4294965671, 4294965689, 4294965779, 4294965839, 4294965893, 4294966091, 4294966109, 4294966127, 4294966157, 4294966187, 4294966199, 4294966211, 4294966403, 4294966457, 4294966499, 4294966541, 4294966637, + 4294966661, 4294966739, 4294966823, 4294966883, 4294966901, 4294966961, 4294967027, 4294967087, 4294967099, 4294967123, 4294967153, 4294967249}; + +// 52-bit mulmod using FPU hack to achieve better performance than the majority of CPU dividers. +#define mulmod52(a, b, m, mf) ( ( ( a * b ) - ( ((uint64_t)(((double)a * (double)b) / mf) - 1) * m ) ) % m ) + +// Compute a^e%m (mf is m in floating point form to avoid repeated conversion) +ZT_INLINE uint64_t modpow52(uint64_t a, uint64_t e, const uint64_t m, const double mf) +{ + uint64_t res = 1ULL; + for (;;) { + if ((e << 63U)) { + res = mulmod52(res, a, m, mf); + } + if (likely((e >>= 1U) != 0)) { + a = mulmod52(a, a, m, mf); + } else { + break; + } + } + return res; +} + +static const ZeroTier::AES s_mimc52AES("abcdefghijklmnopqrstuvwxyz012345"); + +// This fills k[] with pseudorandom bytes from the challenge. +// This doesn't have to be non-reversible or secure, just strongly random. +ZT_INLINE void fillK(uint64_t k[34], const uint8_t challenge[32]) +{ + s_mimc52AES.encrypt(challenge, k); + s_mimc52AES.encrypt(challenge + 16, k + 2); + k[2] ^= k[0]; + k[3] ^= k[1]; + for (unsigned int i = 2, j = 4; j < 34; i += 2, j += 2) + s_mimc52AES.encrypt(k + i, k + j); + +#if __BYTE_ORDER == __BIG_ENDIAN + for (unsigned int i = 0; i < 34; ++i) + k[i] = Utils::swapBytes(k[i]); +#endif +} + +} // anonymous namespace + +namespace ZeroTier { +namespace MIMC52 { + +uint64_t delay(const uint8_t challenge[32], const unsigned long rounds) +{ + uint64_t k[34]; + fillK(k, challenge); + + const uint64_t p = 0x000fffff00000000ULL | (uint64_t)ZT_MIMC52_PRIMES[((unsigned long)k[32]) & 1023]; + const uint64_t e = ((p * 2ULL) - 1ULL) / 3ULL; + const uint64_t m52 = 0xfffffffffffffULL; + const double pf = (double)p; + + uint64_t x = k[33] % p; + for (unsigned long r = 0, kn = rounds; r < rounds; ++r) { + x = (x - k[--kn & 31]) & m52; + x = modpow52(x, e, p, pf); + } + + return x; +} + +bool verify(const uint8_t challenge[32], const unsigned long rounds, uint64_t proof) +{ + uint64_t k[34]; + fillK(k, challenge); + + const uint64_t p = 0x000fffff00000000ULL | (uint64_t)ZT_MIMC52_PRIMES[((unsigned long)k[32]) & 1023]; + const uint64_t m52 = 0xfffffffffffffULL; + const double pf = (double)p; + + for (unsigned long r = 0; r < rounds; ++r) { + const uint64_t kk = k[r & 31]; + proof = mulmod52(mulmod52(proof, proof, p, pf), proof, p, pf); // y = y ^ 3 + proof = (proof + kk) & m52; + } + + return ((proof % p) == (k[33] % p)); +} + +} // namespace MIMC52 +} // namespace ZeroTier diff --git a/core/MIMC52.hpp b/core/MIMC52.hpp new file mode 100644 index 000000000..8dfd361e0 --- /dev/null +++ b/core/MIMC52.hpp @@ -0,0 +1,44 @@ +/* + * 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: 2025-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_MIMC52_HPP +#define ZT_MIMC52_HPP + +#include "Constants.hpp" + +namespace ZeroTier { +namespace MIMC52 { + +/** + * Compute proof of execution for the delay function + * + * @param challenge 256-bit challenge input to randomize algorithm, making it a unique function + * @param rounds Number of rounds + * @return Proof of execution of delay function (only least significant 52 bits are meaningful) + */ +uint64_t delay(const uint8_t challenge[32], unsigned long rounds); + +/** + * Verify a proof of execution + * + * @param challenge 256-bit challenge + * @param rounds Number of rounds + * @param proof Proof from delay() + * @return True if proof is valid + */ +bool verify(const uint8_t challenge[32], unsigned long rounds, uint64_t proof); + +} // namespace MIMC52 +} // namespcae ZeroTier + +#endif diff --git a/core/Tests.cpp b/core/Tests.cpp index 8f77ddbb9..37eebbccc 100644 --- a/core/Tests.cpp +++ b/core/Tests.cpp @@ -43,6 +43,7 @@ #include "Endpoint.hpp" #include "Locator.hpp" #include "Certificate.hpp" +#include "MIMC52.hpp" #ifdef __UNIX_LIKE__ @@ -67,7 +68,7 @@ static clock_serv_t _machGetRealtimeClock() noexcept static clock_serv_t s_machRealtimeClock = _machGetRealtimeClock(); -#endif +#endif // __APPLE__ using namespace ZeroTier; @@ -218,7 +219,7 @@ static const C25519TestVector C25519_TEST_VECTORS[ZT_NUM_C25519_TEST_VECTORS] = }; #define IDENTITY_V0_KNOWN_GOOD_0 "8e4df28b72:0:ac3d46abe0c21f3cfe7a6c8d6a85cfcffcb82fbd55af6a4d6350657c68200843fa2e16f9418bbd9702cae365f2af5fb4c420908b803a681d4daef6114d78a2d7:bd8dd6e4ce7022d2f812797a80c6ee8ad180dc4ebf301dec8b06d1be08832bddd63a2f1cfa7b2c504474c75bdc8898ba476ef92e8e2d0509f8441985171ff16e" -#define IDENTITY_V1_KNOWN_GOOD_0 "cb8be88914:1:uhvyeplt7yjunjissek2ndvj6p6aj5jl7fhdgu64dnqld6h6daixc2ngqxm6pu62hxwheioy3jr46qyaxbmy536qvk5asltqqeyuqhhxakzkfyc3ejza52a4qpp6fabitfiuu6337zji47c4exrepurn6blovy5fgocmp7icwkrohqx354kk55a:xw62q5dniqpm4v7tmagukxlzgmegm3gbhx6izatu43vkvgvi6gejcvzlfg3d7ovqduzosawkq7agwx4qriqv56tr57cpdxzhrouuht7thiptbcvkh5yqrsturbw2eiudf4fijl4zhivtpiw4rbcxhkaobzhhapynhnahswppjlpmvnf4ncia" +#define IDENTITY_V1_KNOWN_GOOD_0 "26e83e3b8c:1:bwtgzeejkrkxeiuqyy35srdorynvz3nfrepqwiwedkm55bkhx77vaf6deknmikr2tgvymw3dxnifawo4m5pbfcqbnws6wzxlm7rgrad4xu52fcqg7ebwt4rhao6mjmrbn2bhdcevrlard5k7vropjrsuodoysvwbnjqef3q4fkrjgygddl4tatr5ztkjwt7f:a5vpcmommfyvjiqudkk7sjekscc4zyc3vqqdytkwlix5ahsxgz73ic3a62secgmyum65dok7b5aochdhdlinn3olyyswzy64ubx3pzbtryqsfmxpntxsvj7aqrd6kkrr3gobstl5yzln6bgqkihmhktbqt4vfwfqynkfjmncd4xqodogyxzq" // -------------------------------------------------------------------------------------------------------------------- @@ -440,7 +441,7 @@ extern "C" const char *ZTT_general() } #ifdef ZT_ARCH_X64 - ZT_T_PRINTF("[general] X64 CPUID: aes=%d avx=%d avx2=%d avx512f=%d fsrm=%d rdrand=%d sha=%d vaes=%d vpclmulqdq=%d" ZT_EOL_S, + ZT_T_PRINTF("[general] X64 CPUID features: aes=%d avx=%d avx2=%d avx512f=%d fsrm=%d rdrand=%d sha=%d vaes=%d vpclmulqdq=%d" ZT_EOL_S, Utils::CPUID.aes, Utils::CPUID.avx, Utils::CPUID.avx2, @@ -936,6 +937,27 @@ extern "C" const char *ZTT_general() extern "C" const char *ZTT_crypto() { try { + { + ZT_T_PRINTF("[crypto] Testing MIMC52... "); + uint8_t challenge[32]; + Utils::zero<32>(challenge); + uint64_t proof = MIMC52::delay(challenge, ZT_IDENTITY_TYPE1_MIMC52_ROUNDS); + if (!MIMC52::verify(challenge, ZT_IDENTITY_TYPE1_MIMC52_ROUNDS, proof)) { + ZT_T_PRINTF("FAILED (MIMC52 verify)" ZT_EOL_S); + return "MIMC52 failed (verify)"; + } + if (proof != 0x0001bce730224699ULL) { + ZT_T_PRINTF("FAILED (MIMC52 proof not correct)" ZT_EOL_S); + return "MIMC52 failed (proof not correct)"; + } + challenge[0] = 1; + if (MIMC52::verify(challenge, ZT_IDENTITY_TYPE1_MIMC52_ROUNDS, proof)) { + ZT_T_PRINTF("FAILED (MIMC52 verify known-bad)" ZT_EOL_S); + return "MIMC52 failed (verify known-bad)"; + } + ZT_T_PRINTF("OK (%.16llx)" ZT_EOL_S, proof); + } + { ZT_T_PRINTF("[crypto] Testing SHA384 and SHA512... "); uint8_t h[64]; @@ -1263,6 +1285,22 @@ extern "C" const char *ZTT_benchmarkCrypto() Utils::zero< sizeof(tmp) >(tmp); Utils::zero< sizeof(tag) >(tag); + { + Utils::getSecureRandom(tmp, 32); + ZT_T_PRINTF("[crypto] Benchmarking MIMC52 for %u rounds... ", ZT_IDENTITY_TYPE1_MIMC52_ROUNDS); + int64_t start = now(); + uint64_t proof = MIMC52::delay(tmp, ZT_IDENTITY_TYPE1_MIMC52_ROUNDS); + int64_t t = now() - start; + ZT_T_PRINTF("%llu ms total, %.8f ms/round, verification: " , t, (double)t / (double)ZT_IDENTITY_TYPE1_MIMC52_ROUNDS); + start = now(); + if (!MIMC52::verify(tmp, ZT_IDENTITY_TYPE1_MIMC52_ROUNDS, proof)) { + ZT_T_PRINTF("FAILED" ZT_EOL_S); + return "MIMC52 verify failed"; + } + t = now() - start; + ZT_T_PRINTF("%llu ms, %.4f per second" ZT_EOL_S, t, 1000.0 / (double)t); + } + { ZT_T_PRINTF("[crypto] Benchmarking SHA384... "); int64_t start = now();