From 6e730cfad1db338ce42f4e0a7720297793e5f966 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 28 Aug 2019 07:31:17 -0700 Subject: [PATCH] Cleanup, multicast fingerprint, benchmark asymmetric crypto --- node/MulticastGroup.hpp | 44 ++++++-- node/Mutex.hpp | 109 ++++---------------- selftest.cpp | 222 ++++++++++------------------------------ 3 files changed, 111 insertions(+), 264 deletions(-) diff --git a/node/MulticastGroup.hpp b/node/MulticastGroup.hpp index 264d99674..408b0c84c 100644 --- a/node/MulticastGroup.hpp +++ b/node/MulticastGroup.hpp @@ -43,15 +43,11 @@ class MulticastGroup public: ZT_ALWAYS_INLINE MulticastGroup() : _mac(), - _adi(0) - { - } + _adi(0) {} ZT_ALWAYS_INLINE MulticastGroup(const MAC &m,uint32_t a) : _mac(m), - _adi(a) - { - } + _adi(a) {} /** * Derive the multicast group used for address resolution (ARP/NDP) for an IP @@ -97,6 +93,42 @@ public: ZT_ALWAYS_INLINE bool operator<=(const MulticastGroup &g) const { return !(g < *this); } ZT_ALWAYS_INLINE bool operator>=(const MulticastGroup &g) const { return !(*this < g); } + /** + * Compute a 32-bit fnv1a hash of a multicast group and a network ID + * + * @param mg Multicast group + * @param nwid Network ID + * @return 32-bit relatively-unique ID + */ + static ZT_ALWAYS_INLINE uint32_t id(const MulticastGroup &mg,const uint64_t nwid) + { + const uint32_t fnv1aPrime = 0x01000193; + uint32_t i = 0x811c9dc5; + i = (((uint32_t)(nwid >> 56) & 0xff) ^ i) * fnv1aPrime; + i = (((uint32_t)(nwid >> 48) & 0xff) ^ i) * fnv1aPrime; + i = (((uint32_t)(nwid >> 40) & 0xff) ^ i) * fnv1aPrime; + i = (((uint32_t)(nwid >> 32) & 0xff) ^ i) * fnv1aPrime; + i = (((uint32_t)(nwid >> 24) & 0xff) ^ i) * fnv1aPrime; + i = (((uint32_t)(nwid >> 16) & 0xff) ^ i) * fnv1aPrime; + i = (((uint32_t)(nwid >> 8) & 0xff) ^ i) * fnv1aPrime; + i = (((uint32_t)nwid & 0xff) ^ i) * fnv1aPrime; + const uint64_t mac = mg._mac.toInt(); + i = (((uint32_t)(mac >> 56) & 0xff) ^ i) * fnv1aPrime; + i = (((uint32_t)(mac >> 48) & 0xff) ^ i) * fnv1aPrime; + i = (((uint32_t)(mac >> 40) & 0xff) ^ i) * fnv1aPrime; + i = (((uint32_t)(mac >> 32) & 0xff) ^ i) * fnv1aPrime; + i = (((uint32_t)(mac >> 24) & 0xff) ^ i) * fnv1aPrime; + i = (((uint32_t)(mac >> 16) & 0xff) ^ i) * fnv1aPrime; + i = (((uint32_t)(mac >> 8) & 0xff) ^ i) * fnv1aPrime; + i = (((uint32_t)mac & 0xff) ^ i) * fnv1aPrime; + const uint32_t adi = mg._adi; + i = (((adi >> 24) & 0xff) ^ i) * fnv1aPrime; + i = (((adi >> 16) & 0xff) ^ i) * fnv1aPrime; + i = (((adi >> 8) & 0xff) ^ i) * fnv1aPrime; + i = ((adi & 0xff) ^ i) * fnv1aPrime; + return i; + } + private: MAC _mac; uint32_t _adi; diff --git a/node/Mutex.hpp b/node/Mutex.hpp index 508586878..6a3272667 100644 --- a/node/Mutex.hpp +++ b/node/Mutex.hpp @@ -26,15 +26,11 @@ namespace ZeroTier { #if defined(__GNUC__) && (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64)) -// Inline ticket lock on x64 systems with GCC and CLANG (Mac, Linux) -- this is really fast as long as locking durations are very short +// Inline ticket lock on x64 systems with GCC and CLANG (Mac, Linux) -- this is really fast as long as contention is LOW class Mutex { public: - ZT_ALWAYS_INLINE Mutex() : - nextTicket(0), - nowServing(0) - { - } + ZT_ALWAYS_INLINE Mutex() : nextTicket(0),nowServing(0) {} ZT_ALWAYS_INLINE void lock() const { @@ -47,9 +43,6 @@ public: ZT_ALWAYS_INLINE void unlock() const { ++(const_cast(this)->nowServing); } - /** - * Uses C++ contexts and constructor/destructor to lock/unlock automatically - */ class Lock { public: @@ -74,46 +67,17 @@ private: class Mutex { public: - ZT_ALWAYS_INLINE Mutex() - { - pthread_mutex_init(&_mh,(const pthread_mutexattr_t *)0); - } - - ZT_ALWAYS_INLINE ~Mutex() - { - pthread_mutex_destroy(&_mh); - } - - ZT_ALWAYS_INLINE void lock() const - { - pthread_mutex_lock(&((const_cast (this))->_mh)); - } - - ZT_ALWAYS_INLINE void unlock() const - { - pthread_mutex_unlock(&((const_cast (this))->_mh)); - } + ZT_ALWAYS_INLINE Mutex() { pthread_mutex_init(&_mh,(const pthread_mutexattr_t *)0); } + ZT_ALWAYS_INLINE ~Mutex() { pthread_mutex_destroy(&_mh); } + ZT_ALWAYS_INLINE void lock() const { pthread_mutex_lock(&((const_cast (this))->_mh)); } + ZT_ALWAYS_INLINE void unlock() const { pthread_mutex_unlock(&((const_cast (this))->_mh)); } class Lock { public: - ZT_ALWAYS_INLINE Lock(Mutex &m) : - _m(&m) - { - m.lock(); - } - - ZT_ALWAYS_INLINE Lock(const Mutex &m) : - _m(const_cast(&m)) - { - _m->lock(); - } - - ZT_ALWAYS_INLINE ~Lock() - { - _m->unlock(); - } - + ZT_ALWAYS_INLINE Lock(Mutex &m) : _m(&m) { m.lock(); } + ZT_ALWAYS_INLINE Lock(const Mutex &m) : _m(const_cast(&m)) { _m->lock(); } + ZT_ALWAYS_INLINE ~Lock() { _m->unlock(); } private: Mutex *const _m; }; @@ -142,56 +106,19 @@ namespace ZeroTier { class Mutex { public: - inline Mutex() - { - InitializeCriticalSection(&_cs); - } - - inline ~Mutex() - { - DeleteCriticalSection(&_cs); - } - - inline void lock() - { - EnterCriticalSection(&_cs); - } - - inline void unlock() - { - LeaveCriticalSection(&_cs); - } - - inline void lock() const - { - (const_cast (this))->lock(); - } - - inline void unlock() const - { - (const_cast (this))->unlock(); - } + ZT_ALWAYS_INLINE Mutex() { InitializeCriticalSection(&_cs); } + ZT_ALWAYS_INLINE ~Mutex() { DeleteCriticalSection(&_cs); } + ZT_ALWAYS_INLINE void lock() { EnterCriticalSection(&_cs); } + ZT_ALWAYS_INLINE void unlock() { LeaveCriticalSection(&_cs); } + ZT_ALWAYS_INLINE void lock() const { (const_cast (this))->lock(); } + ZT_ALWAYS_INLINE void unlock() const { (const_cast (this))->unlock(); } class Lock { public: - inline Lock(Mutex &m) : - _m(&m) - { - m.lock(); - } - - inline Lock(const Mutex &m) : - _m(const_cast(&m)) - { - _m->lock(); - } - - inline ~Lock() - { - _m->unlock(); - } - + ZT_ALWAYS_INLINE Lock(Mutex &m) : _m(&m) { m.lock(); } + ZT_ALWAYS_INLINE Lock(const Mutex &m) : _m(const_cast(&m)) { _m->lock(); } + ZT_ALWAYS_INLINE ~Lock() { _m->unlock(); } private: Mutex *const _m; }; diff --git a/selftest.cpp b/selftest.cpp index d035409cf..0daa452a5 100644 --- a/selftest.cpp +++ b/selftest.cpp @@ -178,7 +178,7 @@ static int testCrypto() } { - std::cout << "[crypto] Testing and benchmarking AES-256 and GCM..." ZT_EOL_S << " AES-256 (test vectors): "; std::cout.flush(); + std::cout << "[crypto] Testing and benchmarking AES-256..." ZT_EOL_S << " AES-256 (test vectors): "; std::cout.flush(); AES tv(AES_TEST_VECTOR_0_KEY); tv.encrypt(AES_TEST_VECTOR_0_IN,(uint8_t *)buf1); if (memcmp(buf1,AES_TEST_VECTOR_0_OUT,16) != 0) { @@ -458,6 +458,60 @@ static int testCrypto() std::cout << "[crypto] ECDSA Test Vector: PASS" ZT_EOL_S; } + std::cout << "[crypto] Benchmarking asymmetric crypto..." ZT_EOL_S; + { + uint8_t pub[128],priv[128],hash[128],sig[128]; + volatile uint8_t foo = 0; + Utils::getSecureRandom(hash,sizeof(hash)); + + C25519::generate(pub,priv); + int64_t start = OSUtils::now(); + for(int k=0;k<1500;++k) { + ++hash[0]; + C25519::sign(priv,pub,hash,sizeof(hash),sig); + foo = sig[0]; + } + int64_t end = OSUtils::now(); + std::cout << " Ed25519 sign: " << (1500.0 / ((double)(end - start) / 1000.0)) << " signatures/second" ZT_EOL_S; + start = OSUtils::now(); + for(int k=0;k<1000;++k) { + ++sig[0]; + foo = (uint8_t)C25519::verify(pub,hash,sizeof(hash),sig,ZT_C25519_SIGNATURE_LEN); + } + end = OSUtils::now(); + std::cout << " Ed25519 verify: " << (1000.0 / ((double)(end - start) / 1000.0)) << " verifications/second" ZT_EOL_S; + start = OSUtils::now(); + for(int k=0;k<1000;++k) { + C25519::agree(priv,pub,hash); + foo = hash[0]; + } + end = OSUtils::now(); + std::cout << " C25519 ECDH: " << (1000.0 / ((double)(end - start) / 1000.0)) << " agreements/second" ZT_EOL_S; + + ECC384GenerateKey(pub,priv); + start = OSUtils::now(); + for(int k=0;k<1000;++k) { + ++hash[0]; + ECC384ECDSASign(priv,hash,sig); + foo = sig[0]; + } + end = OSUtils::now(); + std::cout << " ECC P-384 sign: " << (1000.0 / ((double)(end - start) / 1000.0)) << " signatures/second" ZT_EOL_S; + start = OSUtils::now(); + for(int k=0;k<1000;++k) { + foo = ECC384ECDSAVerify(pub,hash,sig); + } + end = OSUtils::now(); + std::cout << " ECC P-384 verify: " << (1000.0 / ((double)(end - start) / 1000.0)) << " verifications/second" ZT_EOL_S; + start = OSUtils::now(); + for(int k=0;k<1000;++k) { + ECC384ECDH(pub,priv,hash); + foo = hash[0]; + } + end = OSUtils::now(); + std::cout << " ECC P-384 ECDH: " << (1000.0 / ((double)(end - start) / 1000.0)) << " agreements/second" ZT_EOL_S; + } + return 0; } @@ -786,172 +840,6 @@ static int testOther() std::cout << " " << InetAddress("").toString(buf); std::cout << ZT_EOL_S; -#if 0 - std::cout << "[other] Testing Hashtable... "; std::cout.flush(); - { - Hashtable ht; - std::map ref; // assume std::map works correctly :) - for(int x=0;x<2;++x) { - for(int i=0;i<77777;++i) { - uint64_t k = rand(); - while ((k == 0)||(ref.count(k) > 0)) - ++k; - std::string v("!"); - for(int j=0;j<(int)(k % 64);++j) - v.push_back("0123456789"[rand() % 10]); - ref[k] = v; - ht.set(0xffffffffffffffffULL,v); - std::string &vref = ht[k]; - vref = v; - ht.erase(0xffffffffffffffffULL); - } - if (ht.size() != ref.size()) { - std::cout << "FAILED! (size mismatch, original)" ZT_EOL_S; - return -1; - } - { - Hashtable::Iterator i(ht); - uint64_t *k = (uint64_t *)0; - std::string *v = (std::string *)0; - while(i.next(k,v)) { - if (ref.find(*k)->second != *v) { - std::cout << "FAILED! (data mismatch!)" ZT_EOL_S; - return -1; - } - } - } - for(std::map::const_iterator i(ref.begin());i!=ref.end();++i) { - if (ht[i->first] != i->second) { - std::cout << "FAILED! (data mismatch!)" ZT_EOL_S; - return -1; - } - } - - Hashtable ht2; - ht2 = ht; - Hashtable ht3(ht2); - if (ht2.size() != ref.size()) { - std::cout << "FAILED! (size mismatch, assigned)" ZT_EOL_S; - return -1; - } - if (ht3.size() != ref.size()) { - std::cout << "FAILED! (size mismatch, copied)" ZT_EOL_S; - return -1; - } - - for(std::map::iterator i(ref.begin());i!=ref.end();++i) { - std::string *v = ht.get(i->first); - if (!v) { - std::cout << "FAILED! (key " << i->first << " not found, original)" ZT_EOL_S; - return -1; - } - if (*v != i->second) { - std::cout << "FAILED! (key " << i->first << " not equal, original)" ZT_EOL_S; - return -1; - } - v = ht2.get(i->first); - if (!v) { - std::cout << "FAILED! (key " << i->first << " not found, assigned)" ZT_EOL_S; - return -1; - } - if (*v != i->second) { - std::cout << "FAILED! (key " << i->first << " not equal, assigned)" ZT_EOL_S; - return -1; - } - v = ht3.get(i->first); - if (!v) { - std::cout << "FAILED! (key " << i->first << " not found, copied)" ZT_EOL_S; - return -1; - } - if (*v != i->second) { - std::cout << "FAILED! (key " << i->first << " not equal, copied)" ZT_EOL_S; - return -1; - } - } - { - uint64_t *k; - std::string *v; - Hashtable::Iterator i(ht); - unsigned long ic = 0; - while (i.next(k,v)) { - if (ref[*k] != *v) { - std::cout << "FAILED! (iterate)" ZT_EOL_S; - return -1; - } - ++ic; - } - if (ic != ht.size()) { - std::cout << "FAILED! (iterate coverage)" ZT_EOL_S; - return -1; - } - } - for(std::map::iterator i(ref.begin());i!=ref.end();) { - if (!ht.get(i->first)) { - std::cout << "FAILED! (erase, check if exists)" ZT_EOL_S; - return -1; - } - ht.erase(i->first); - if (ht.get(i->first)) { - std::cout << "FAILED! (erase, check if erased)" ZT_EOL_S; - return -1; - } - ref.erase(i++); - if (ht.size() != ref.size()) { - std::cout << "FAILED! (erase, size)" ZT_EOL_S; - return -1; - } - } - if (!ht.empty()) { - std::cout << "FAILED! (erase, empty)" ZT_EOL_S; - return -1; - } - for(int i=0;i<10000;++i) { - uint64_t k = rand(); - while ((k == 0)||(ref.count(k) > 0)) - ++k; - std::string v; - for(int j=0;j<(int)(k % 64);++j) - v.push_back("0123456789"[rand() % 10]); - ht.set(k,v); - ref[k] = v; - } - if (ht.size() != ref.size()) { - std::cout << "FAILED! (second populate)" ZT_EOL_S; - return -1; - } - ht.clear(); - ref.clear(); - if (ht.size() != ref.size()) { - std::cout << "FAILED! (clear)" ZT_EOL_S; - return -1; - } - for(int i=0;i<10000;++i) { - uint64_t k = rand(); - while ((k == 0)||(ref.count(k) > 0)) - ++k; - std::string v; - for(int j=0;j<(int)(k % 64);++j) - v.push_back("0123456789"[rand() % 10]); - ht.set(k,v); - ref[k] = v; - } - { - Hashtable::Iterator i(ht); - uint64_t *k; - std::string *v; - while (i.next(k,v)) - ht.erase(*k); - } - ref.clear(); - if (ht.size() != ref.size()) { - std::cout << "FAILED! (clear by iterate, " << ht.size() << ")" ZT_EOL_S; - return -1; - } - } - } - std::cout << "PASS" ZT_EOL_S; -#endif - { std::cout << "[other] Testing/fuzzing Dictionary... "; std::cout.flush(); for(int k=0;k<250;++k) {