Speed up V1 identity verification a little, and add some comments.

This commit is contained in:
Adam Ierymenko 2020-02-27 09:43:58 -08:00
parent c5bd21a23e
commit fbfa99fac5
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
6 changed files with 193 additions and 114 deletions

View file

@ -338,8 +338,11 @@ void DB::_networkChanged(nlohmann::json &old,nlohmann::json &networkConfig,bool
void DB::_fillSummaryInfo(const std::shared_ptr<_Network> &nw,NetworkSummaryInfo &info)
{
for(auto ab=nw->activeBridgeMembers.begin();ab!=nw->activeBridgeMembers.end();++ab)
info.activeBridges.push_back(Address(*ab));
for(auto ab=nw->activeBridgeMembers.begin();ab!=nw->activeBridgeMembers.end();++ab) {
const Address aba(*ab);
if (nw->authorizedMembers.count(aba) != 0)
info.activeBridges.push_back(aba);
}
std::sort(info.activeBridges.begin(),info.activeBridges.end());
for(auto ip=nw->allocatedIps.begin();ip!=nw->allocatedIps.end();++ip)
info.allocatedIps.push_back(*ip);

View file

@ -89,11 +89,13 @@ bool Identity::generate(const Type t)
switch(t) {
case C25519: {
// Generate C25519/Ed25519 key pair whose hash satisfies a "hashcash" criterion and generate the
// address from the last 40 bits of this hash. This is different from the fingerprint hash for V0.
uint8_t digest[64];
char *const genmem = new char[ZT_V0_IDENTITY_GEN_MEMORY];
do {
C25519::generateSatisfying(_v0_identity_generate_cond(digest,genmem),_pub.c25519,_priv.c25519);
_address.setTo(digest + 59); // last 5 bytes are address
_address.setTo(digest + 59);
} while (_address.isReserved());
delete[] genmem;
_computeHash();
@ -101,9 +103,15 @@ bool Identity::generate(const Type t)
case P384: {
for(;;) {
// Generate C25519, Ed25519, and NIST P-384 key pairs.
C25519::generate(_pub.c25519,_priv.c25519);
ECC384GenerateKey(_pub.p384,_priv.p384);
// 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.data());
if (!_address.isReserved())
@ -118,35 +126,36 @@ bool Identity::generate(const Type t)
return true;
}
bool Identity::locallyValidate() const
bool Identity::locallyValidate() const noexcept
{
if ((_address.isReserved())||(!_address))
return false;
switch (_type) {
try {
if ((!_address.isReserved()) && (_address)) {
switch (_type) {
case C25519:
try {
uint8_t digest[64];
char *genmem = new char[ZT_V0_IDENTITY_GEN_MEMORY];
_computeMemoryHardHash(_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN,digest,genmem);
delete [] genmem;
return ((_address == Address(digest + 59))&&(digest[0] < 17));
} catch ( ... ) {}
break;
case C25519: {
uint8_t digest[64];
char *genmem = new char[ZT_V0_IDENTITY_GEN_MEMORY];
_computeMemoryHardHash(_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN,digest,genmem);
delete[] genmem;
return ((_address == Address(digest + 59)) && (digest[0] < 17));
}
case P384:
if (_address == Address(_fp.data())) {
// 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;
}
case P384:
if (_address == Address(_fp.data())) {
// 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.
const unsigned long rounds = ZT_V1_IDENTITY_MIMC52_VDF_ROUNDS_BASE * ((unsigned long)_pub.t1mimc52[0] + 1U);
if (mimc52Verify(&_pub,sizeof(_pub) - sizeof(_pub.t1mimc52),rounds,Utils::loadBigEndian<uint64_t>(_pub.t1mimc52)))
return true;
}
break;
}
}
} catch ( ... ) {}
return false;
}

View file

@ -105,7 +105,7 @@ public:
*
* @return True if validation check passes
*/
bool locallyValidate() const;
bool locallyValidate() const noexcept;
/**
* @return True if this identity contains a private key

File diff suppressed because one or more lines are too long

View file

@ -180,8 +180,8 @@ static const C25519TestVector C25519_TEST_VECTORS[ZT_NUM_C25519_TEST_VECTORS] =
#define IDENTITY_V0_KNOWN_GOOD_0 "8e4df28b72:0:ac3d46abe0c21f3cfe7a6c8d6a85cfcffcb82fbd55af6a4d6350657c68200843fa2e16f9418bbd9702cae365f2af5fb4c420908b803a681d4daef6114d78a2d7:bd8dd6e4ce7022d2f812797a80c6ee8ad180dc4ebf301dec8b06d1be08832bddd63a2f1cfa7b2c504474c75bdc8898ba476ef92e8e2d0509f8441985171ff16e"
#define IDENTITY_V0_KNOWN_BAD_0 "9e4df28b72:0:ac3d46abe0c21f3cfe7a6c8d6a85cfcffcb82fbd55af6a4d6350657c68200843fa2e16f9418bbd9702cae365f2af5fb4c420908b803a681d4daef6114d78a2d7:bd8dd6e4ce7022d2f812797a80c6ee8ad180dc4ebf301dec8b06d1be08832bddd63a2f1cfa7b2c504474c75bdc8898ba476ef92e8e2d0509f8441985171ff16e"
#define IDENTITY_V1_KNOWN_GOOD_0 "237ce8d8e2:1:5w3rj6am3sa7f5vtwm535iswob6ngmkpdidijz5ormqrfwkj55lhwyyszruu4rkbjycmlxzzoiuwtyw5s2mybknqx5j2cwxnaflqbwycoio2hqzcro5afrpcncnxlemzs6bt5linlib5flsej3f3r3bbzclxk733ei7tdrtm5uruiwpmyi4vgaafze42sx6hpe:mwjavgvhxz75ow2fhgq3zu4qfou5kce4wzegpjjd6545fpjnhjxb26e5unuutv7k3c6sm6umpyvatgpufwehi4wqmyudvq724h2klbiem6txs2h5iit5crgg3e6se5xeomuqhircv7zhkylrtnlgh57il742pwkrdgt4lz5fstetmiw7y3rq"
#define IDENTITY_V1_KNOWN_BAD_0 "238ce8d8e2:1:5w3rj6am3sa7f5vtwm535iswob6ngmkpdidijz5ormqrfwkj55lhwyyszruu4rkbjycmlxzzoiuwtyw5s2mybknqx5j2cwxnaflqbwycoio2hqzcro5afrpcncnxlemzs6bt5linlib5flsej3f3r3bbzclxk733ei7tdrtm5uruiwpmyi4vgaafze42sx6hpe:mwjavgvhxz75ow2fhgq3zu4qfou5kce4wzegpjjd6545fpjnhjxb26e5unuutv7k3c6sm6umpyvatgpufwehi4wqmyudvq724h2klbiem6txs2h5iit5crgg3e6se5xeomuqhircv7zhkylrtnlgh57il742pwkrdgt4lz5fstetmiw7y3rq"
#define IDENTITY_V1_KNOWN_GOOD_0 "12a4e65422:1:norcnqlkhl2ly6aljrguqntd7bfwdpfpgse32gy2nonhrninfyfxbz4qyj4i7jm2jn6c5hnr6fe3j7a556w5irhtmz77ajdkw6ge2nadlwczld5yak4mhdj46bcwvgzpu3evsfbx44psgughwwgp7rl5ju2gcw4mil5csvd3dp6itwqksyzf6aake3fpn5seo4:ds3mdnqdnbveqlyqq446if3tdilsva4fpsaqahqysavvbv23j655yfahihupezaic56uqrk2dbaamdj2fzmyzulhraux2daj3p6hnxyrhbwugu2ukuqi4eucdck4eczjqcgmv33w65nyxkm3gvhahmoc3zwt2nqmexcdsxlqccexfcpvinuq"
#define IDENTITY_V1_KNOWN_BAD_0 "22a4e65422:1:norcnqlkhl2ly6aljrguqntd7bfwdpfpgse32gy2nonhrninfyfxbz4qyj4i7jm2jn6c5hnr6fe3j7a556w5irhtmz77ajdkw6ge2nadlwczld5yak4mhdj46bcwvgzpu3evsfbx44psgughwwgp7rl5ju2gcw4mil5csvd3dp6itwqksyzf6aake3fpn5seo4:ds3mdnqdnbveqlyqq446if3tdilsva4fpsaqahqysavvbv23j655yfahihupezaic56uqrk2dbaamdj2fzmyzulhraux2daj3p6hnxyrhbwugu2ukuqi4eucdck4eczjqcgmv33w65nyxkm3gvhahmoc3zwt2nqmexcdsxlqccexfcpvinuq"
// --------------------------------------------------------------------------------------------------------------------
@ -639,7 +639,7 @@ 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 != 0x0007a1a0a1b0fe32)) {
if ((!mimc52Verify("testing",7,1000,proof))||(proof != 0x000b501115c73369)) {
ZT_T_PRINTF("FAILED (%.16llx)" ZT_EOL_S,proof);
return "MIMC52 failed simple delay/verify test";
}

View file

@ -423,14 +423,14 @@ static ZT_ALWAYS_INLINE uint64_t swapBytes(uint64_t n) noexcept
#endif
#else
return (
((n & 0x00000000000000FFULL) << 56) |
((n & 0x000000000000FF00ULL) << 40) |
((n & 0x0000000000FF0000ULL) << 24) |
((n & 0x00000000FF000000ULL) << 8) |
((n & 0x000000FF00000000ULL) >> 8) |
((n & 0x0000FF0000000000ULL) >> 24) |
((n & 0x00FF000000000000ULL) >> 40) |
((n & 0xFF00000000000000ULL) >> 56)
((n & 0x00000000000000ffULL) << 56) |
((n & 0x000000000000ff00ULL) << 40) |
((n & 0x0000000000ff0000ULL) << 24) |
((n & 0x00000000ff000000ULL) << 8) |
((n & 0x000000ff00000000ULL) >> 8) |
((n & 0x0000ff0000000000ULL) >> 24) |
((n & 0x00ff000000000000ULL) >> 40) |
((n & 0xff00000000000000ULL) >> 56)
);
#endif
}
@ -446,15 +446,32 @@ template<typename I>
static ZT_ALWAYS_INLINE I loadBigEndian(const void *const p) noexcept
{
#ifdef ZT_NO_UNALIGNED_ACCESS
I x = (I)0;
for(unsigned int k=0;k<sizeof(I);++k) {
#if __BYTE_ORDER == __LITTLE_ENDIAN
reinterpret_cast<uint8_t *>(&x)[k] = reinterpret_cast<const uint8_t *>(p)[(sizeof(I)-1)-k];
#else
reinterpret_cast<uint8_t *>(&x)[k] = reinterpret_cast<const uint8_t *>(p)[k];
#endif
if (sizeof(I) == 8) {
return (I)(
((uint64_t)reinterpret_cast<const uint8_t *>(p)[0] << 56U) |
((uint64_t)reinterpret_cast<const uint8_t *>(p)[1] << 48U) |
((uint64_t)reinterpret_cast<const uint8_t *>(p)[2] << 40U) |
((uint64_t)reinterpret_cast<const uint8_t *>(p)[3] << 32U) |
((uint64_t)reinterpret_cast<const uint8_t *>(p)[4] << 24U) |
((uint64_t)reinterpret_cast<const uint8_t *>(p)[5] << 16U) |
((uint64_t)reinterpret_cast<const uint8_t *>(p)[6] << 8U) |
(uint64_t)reinterpret_cast<const uint8_t *>(p)[7]
);
} else if (sizeof(I) == 4) {
return (I)(
((uint32_t)reinterpret_cast<const uint8_t *>(p)[0] << 24U) |
((uint32_t)reinterpret_cast<const uint8_t *>(p)[1] << 16U) |
((uint32_t)reinterpret_cast<const uint8_t *>(p)[2] << 8U) |
(uint32_t)reinterpret_cast<const uint8_t *>(p)[3]
);
} else if (sizeof(I) == 2) {
return (I)(
((unsigned int)reinterpret_cast<const uint8_t *>(p)[0] << 8U) |
(unsigned int)reinterpret_cast<const uint8_t *>(p)[1]
);
} else {
return (I)reinterpret_cast<const uint8_t *>(p)[0];
}
return x;
#else
return ntoh(*reinterpret_cast<const I *>(p));
#endif
@ -468,15 +485,28 @@ static ZT_ALWAYS_INLINE I loadBigEndian(const void *const p) noexcept
* #param i Integer to write
*/
template<typename I>
static ZT_ALWAYS_INLINE void storeBigEndian(void *const p,const I i) noexcept
static ZT_ALWAYS_INLINE void storeBigEndian(void *const p,I i) noexcept
{
#ifdef ZT_NO_UNALIGNED_ACCESS
for(unsigned int k=0;k<sizeof(I);++k) {
#if __BYTE_ORDER == __LITTLE_ENDIAN
reinterpret_cast<uint8_t *>(p)[k] = reinterpret_cast<const uint8_t *>(&i)[(sizeof(I)-1)-k];
#else
reinterpret_cast<uint8_t *>(p)[k] = reinterpret_cast<const uint8_t *>(&i)[k];
#endif
if (sizeof(I) == 8) {
reinterpret_cast<uint8_t *>(p)[0] = (uint8_t)(reinterpret_cast<uint64_t>(i) >> 56U);
reinterpret_cast<uint8_t *>(p)[1] = (uint8_t)(reinterpret_cast<uint64_t>(i) >> 48U);
reinterpret_cast<uint8_t *>(p)[2] = (uint8_t)(reinterpret_cast<uint64_t>(i) >> 40U);
reinterpret_cast<uint8_t *>(p)[3] = (uint8_t)(reinterpret_cast<uint64_t>(i) >> 32U);
reinterpret_cast<uint8_t *>(p)[4] = (uint8_t)(reinterpret_cast<uint64_t>(i) >> 24U);
reinterpret_cast<uint8_t *>(p)[5] = (uint8_t)(reinterpret_cast<uint64_t>(i) >> 16U);
reinterpret_cast<uint8_t *>(p)[6] = (uint8_t)(reinterpret_cast<uint64_t>(i) >> 8U);
reinterpret_cast<uint8_t *>(p)[7] = (uint8_t)reinterpret_cast<uint64_t>(i);
} else if (sizeof(I) == 4) {
reinterpret_cast<uint8_t *>(p)[0] = (uint8_t)(reinterpret_cast<uint32_t>(i) >> 24U);
reinterpret_cast<uint8_t *>(p)[1] = (uint8_t)(reinterpret_cast<uint32_t>(i) >> 16U);
reinterpret_cast<uint8_t *>(p)[2] = (uint8_t)(reinterpret_cast<uint32_t>(i) >> 8U);
reinterpret_cast<uint8_t *>(p)[3] = (uint8_t)reinterpret_cast<uint32_t>(i);
} else if (sizeof(I) == 2) {
reinterpret_cast<uint8_t *>(p)[0] = (uint8_t)(reinterpret_cast<uint16_t>(i) >> 8U);
reinterpret_cast<uint8_t *>(p)[1] = (uint8_t)reinterpret_cast<uint16_t>(i);
} else {
reinterpret_cast<uint8_t *>(p)[0] = (uint8_t)i;
}
#else
*reinterpret_cast<I *>(p) = hton(i);
@ -486,33 +516,42 @@ static ZT_ALWAYS_INLINE void storeBigEndian(void *const p,const I i) noexcept
/**
* Decode a little-endian value from a byte stream
*
* @tparam I Type to decode (should be unsigned e.g. uint32_t or uint64_t)
* @tparam I Type to decode
* @param p Byte stream, must be at least sizeof(I) in size
* @return Decoded integer
*/
template<typename I>
static ZT_ALWAYS_INLINE I loadLittleEndian(const void *const p) noexcept
{
#ifdef ZT_NO_UNALIGNED_ACCESS
I x = (I)0;
for(unsigned int k=0;k<sizeof(I);++k) {
#if __BYTE_ORDER == __LITTLE_ENDIAN
reinterpret_cast<uint8_t *>(&x)[k] = reinterpret_cast<const uint8_t *>(p)[k];
#else
reinterpret_cast<uint8_t *>(&x)[k] = reinterpret_cast<const uint8_t *>(p)[(sizeof(I)-1)-k];
#endif
#if __BYTE_ORDER == __BIG_ENDIAN || defined(ZT_NO_UNALIGNED_ACCESS)
if (sizeof(I) == 8) {
return (I)(
(uint64_t)reinterpret_cast<const uint8_t *>(p)[0] |
((uint64_t)reinterpret_cast<const uint8_t *>(p)[1] << 8U) |
((uint64_t)reinterpret_cast<const uint8_t *>(p)[2] << 16U) |
((uint64_t)reinterpret_cast<const uint8_t *>(p)[3] << 24U) |
((uint64_t)reinterpret_cast<const uint8_t *>(p)[4] << 32U) |
((uint64_t)reinterpret_cast<const uint8_t *>(p)[5] << 40U) |
((uint64_t)reinterpret_cast<const uint8_t *>(p)[6] << 48U) |
((uint64_t)reinterpret_cast<const uint8_t *>(p)[7] << 56U)
);
} else if (sizeof(I) == 4) {
return (I)(
(uint32_t)reinterpret_cast<const uint8_t *>(p)[0] |
((uint32_t)reinterpret_cast<const uint8_t *>(p)[1] << 8U) |
((uint32_t)reinterpret_cast<const uint8_t *>(p)[2] << 16U) |
((uint32_t)reinterpret_cast<const uint8_t *>(p)[3] << 24U)
);
} else if (sizeof(I) == 2) {
return (I)(
(unsigned int)reinterpret_cast<const uint8_t *>(p)[0] |
((unsigned int)reinterpret_cast<const uint8_t *>(p)[1] << 8U)
);
} else {
return (I)reinterpret_cast<const uint8_t *>(p)[0];
}
return x;
#else
#if __BYTE_ORDER == __LITTLE_ENDIAN
return *reinterpret_cast<const I *>(p);
#else
I x = (I)0;
for(unsigned int k=0;k<sizeof(I);++k) {
reinterpret_cast<uint8_t *>(&x)[k] = reinterpret_cast<const uint8_t *>(p)[(sizeof(I)-1)-k];
}
return x;
#endif
#endif
}
@ -526,22 +565,29 @@ static ZT_ALWAYS_INLINE I loadLittleEndian(const void *const p) noexcept
template<typename I>
static ZT_ALWAYS_INLINE void storeLittleEndian(void *const p,const I i) noexcept
{
#ifdef ZT_NO_UNALIGNED_ACCESS
for(unsigned int k=0;k<sizeof(I);++k) {
#if __BYTE_ORDER == __LITTLE_ENDIAN
reinterpret_cast<uint8_t *>(p)[k] = reinterpret_cast<const uint8_t *>(&i)[k];
#else
reinterpret_cast<uint8_t *>(p)[k] = reinterpret_cast<const uint8_t *>(&i)[(sizeof(I)-1)-k];
#endif
#if __BYTE_ORDER == __BIG_ENDIAN || defined(ZT_NO_UNALIGNED_ACCESS)
if (sizeof(I) == 8) {
reinterpret_cast<uint8_t *>(p)[0] = (uint8_t)reinterpret_cast<uint64_t>(i);
reinterpret_cast<uint8_t *>(p)[1] = (uint8_t)(reinterpret_cast<uint64_t>(i) >> 8U);
reinterpret_cast<uint8_t *>(p)[2] = (uint8_t)(reinterpret_cast<uint64_t>(i) >> 16U);
reinterpret_cast<uint8_t *>(p)[3] = (uint8_t)(reinterpret_cast<uint64_t>(i) >> 24U);
reinterpret_cast<uint8_t *>(p)[4] = (uint8_t)(reinterpret_cast<uint64_t>(i) >> 32U);
reinterpret_cast<uint8_t *>(p)[5] = (uint8_t)(reinterpret_cast<uint64_t>(i) >> 40U);
reinterpret_cast<uint8_t *>(p)[6] = (uint8_t)(reinterpret_cast<uint64_t>(i) >> 48U);
reinterpret_cast<uint8_t *>(p)[7] = (uint8_t)(reinterpret_cast<uint64_t>(i) >> 56U);
} else if (sizeof(I) == 4) {
reinterpret_cast<uint8_t *>(p)[0] = (uint8_t)reinterpret_cast<uint32_t>(i);
reinterpret_cast<uint8_t *>(p)[1] = (uint8_t)(reinterpret_cast<uint32_t>(i) >> 8U);
reinterpret_cast<uint8_t *>(p)[2] = (uint8_t)(reinterpret_cast<uint32_t>(i) >> 16U);
reinterpret_cast<uint8_t *>(p)[3] = (uint8_t)(reinterpret_cast<uint32_t>(i) >> 24U);
} else if (sizeof(I) == 2) {
reinterpret_cast<uint8_t *>(p)[0] = (uint8_t)reinterpret_cast<uint16_t>(i);
reinterpret_cast<uint8_t *>(p)[1] = (uint8_t)(reinterpret_cast<uint16_t>(i) >> 8U);
} else {
reinterpret_cast<uint8_t *>(p)[0] = (uint8_t)i;
}
#else
#if __BYTE_ORDER == __LITTLE_ENDIAN
*reinterpret_cast<I *>(p) = i;
#else
for(unsigned int k=0;k<sizeof(I);++k) {
reinterpret_cast<uint8_t *>(p)[k] = reinterpret_cast<const uint8_t *>(&i)[(sizeof(I)-1)-k];
}
#endif
#endif
}