mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-21 22:46:54 +02:00
V1 identities now use a VDF (verifiable delay function)
This commit is contained in:
parent
c6a7b5774c
commit
83e79e1a1b
4 changed files with 72 additions and 79 deletions
|
@ -17,11 +17,14 @@
|
|||
#include "Salsa20.hpp"
|
||||
#include "AES.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "MIMC52.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
|
||||
#define ZT_V1_IDENTITY_MIMC52_VDF_ROUNDS_BASE 250000
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
namespace {
|
||||
|
@ -75,31 +78,18 @@ struct _v0_identity_generate_cond
|
|||
char *genmem;
|
||||
};
|
||||
|
||||
ZT_ALWAYS_INLINE void _v1_hash(uint8_t *const digest,const void *const in,const unsigned int len) noexcept
|
||||
{
|
||||
SHA384(digest,in,len);
|
||||
Utils::storeBigEndian(digest,Utils::loadBigEndian<uint64_t>(digest) % 18446744073709549811ULL);
|
||||
Utils::storeBigEndian(digest + 8,Utils::loadBigEndian<uint64_t>(digest + 8) % 18446744073709549757ULL);
|
||||
Utils::storeBigEndian(digest + 16,Utils::loadBigEndian<uint64_t>(digest + 16) % 18446744073709549733ULL);
|
||||
Utils::storeBigEndian(digest + 24,Utils::loadBigEndian<uint64_t>(digest + 24) % 18446744073709549667ULL);
|
||||
Utils::storeBigEndian(digest + 32,Utils::loadBigEndian<uint64_t>(digest + 32) % 18446744073709549613ULL);
|
||||
Utils::storeBigEndian(digest + 40,Utils::loadBigEndian<uint64_t>(digest + 40) % 18446744073709549583ULL);
|
||||
SHA384(digest,in,len,digest,48);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
const Identity Identity::NIL;
|
||||
|
||||
bool Identity::generate(const Type t)
|
||||
{
|
||||
uint8_t digest[64];
|
||||
|
||||
_type = t;
|
||||
_hasPrivate = true;
|
||||
|
||||
switch(t) {
|
||||
case C25519: {
|
||||
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);
|
||||
|
@ -110,18 +100,15 @@ bool Identity::generate(const Type t)
|
|||
} break;
|
||||
|
||||
case P384: {
|
||||
AES c;
|
||||
for(;;) {
|
||||
C25519::generate(_pub.c25519,_priv.c25519);
|
||||
ECC384GenerateKey(_pub.p384,_priv.p384);
|
||||
_v1_hash(digest,&_pub,sizeof(_pub));
|
||||
if (((digest[46] & 1U)|digest[47]) == 0) { // right-most 9 bits must be zero
|
||||
_address.setTo(digest);
|
||||
if (!_address.isReserved())
|
||||
break;
|
||||
}
|
||||
Utils::storeBigEndian(_pub.t1mimc52,mimc52Delay(&_pub,sizeof(_pub) - sizeof(_pub.t1mimc52),ZT_V1_IDENTITY_MIMC52_VDF_ROUNDS_BASE));
|
||||
_computeHash();
|
||||
_address.setTo(_hash.data());
|
||||
if (!_address.isReserved())
|
||||
break;
|
||||
}
|
||||
_hash.set(digest); // P384 uses the same hash for hash() and address generation
|
||||
} break;
|
||||
|
||||
default:
|
||||
|
@ -145,10 +132,19 @@ bool Identity::locallyValidate() const
|
|||
delete [] genmem;
|
||||
return ((_address == Address(digest + 59))&&(!_address.isReserved())&&(digest[0] < 17));
|
||||
} catch ( ... ) {}
|
||||
return false;
|
||||
break;
|
||||
|
||||
case P384:
|
||||
return ( (Address(_hash.data()) == _address) && (((_hash[46] & 1U)|_hash[47]) == 0) );
|
||||
if ((_address == Address(_hash.data()))&&(!_address.isReserved())) {
|
||||
// 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;
|
||||
|
||||
}
|
||||
return false;
|
||||
|
@ -488,18 +484,13 @@ int Identity::unmarshal(const uint8_t *data,const int len) noexcept
|
|||
|
||||
_hasPrivate = true;
|
||||
memcpy(&_priv,data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1,ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE);
|
||||
_computeHash();
|
||||
if (!this->locallyValidate()) // for P384 we do this always
|
||||
return -1;
|
||||
|
||||
_computeHash();
|
||||
return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE;
|
||||
} else if (privlen == 0) {
|
||||
_hasPrivate = false;
|
||||
|
||||
_computeHash();
|
||||
if (!this->locallyValidate()) // for P384 we do this always
|
||||
return -1;
|
||||
|
||||
return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1;
|
||||
}
|
||||
break;
|
||||
|
@ -521,7 +512,7 @@ void Identity::_computeHash()
|
|||
break;
|
||||
|
||||
case P384:
|
||||
_v1_hash(_hash.data(),&_pub,sizeof(_pub));
|
||||
SHA384(_hash.data(),&_pub,sizeof(_pub));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
#ifndef ZT_IDENTITY_HPP
|
||||
#define ZT_IDENTITY_HPP
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Address.hpp"
|
||||
|
@ -26,8 +23,12 @@
|
|||
#include "TriviallyCopyable.hpp"
|
||||
#include "Hash.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#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)
|
||||
#define ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE (ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE + 8)
|
||||
#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)
|
||||
|
||||
|
@ -41,10 +42,6 @@ namespace ZeroTier {
|
|||
* key pair. Type 1 identities use P-384 for signatures but use both key pairs at once
|
||||
* (hashing both keys together) for key agreement with other type 1 identities, and can
|
||||
* agree with type 0 identities by only using the Curve25519 component.
|
||||
*
|
||||
* Type 1 identities also use a simpler mechanism to rate limit identity generation (as
|
||||
* a defense in depth against intentional collision) that makes local identity validation
|
||||
* faster, allowing full identity validation on all unmarshal() operations.
|
||||
*/
|
||||
class Identity : public TriviallyCopyable
|
||||
{
|
||||
|
@ -210,14 +207,7 @@ public:
|
|||
|
||||
ZT_ALWAYS_INLINE bool operator==(const Identity &id) const noexcept
|
||||
{
|
||||
if ((_address == id._address)&&(_type == id._type)) {
|
||||
switch(_type) {
|
||||
case C25519: return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) == 0);
|
||||
// case P384:
|
||||
default: return (memcmp(&_pub,&id._pub,sizeof(_pub)) == 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return ((_address == id._address)&&(_type == id._type)&&(memcmp(_hash.data(),id._hash.data(),ZT_SHA384_DIGEST_LEN) == 0));
|
||||
}
|
||||
ZT_ALWAYS_INLINE bool operator!=(const Identity &id) const noexcept { return !(*this == id); }
|
||||
ZT_ALWAYS_INLINE bool operator<(const Identity &id) const noexcept
|
||||
|
@ -227,13 +217,8 @@ public:
|
|||
if (_address == id._address) {
|
||||
if ((int)_type < (int)id._type)
|
||||
return true;
|
||||
if (_type == id._type) {
|
||||
switch(_type) {
|
||||
case C25519: return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) < 0);
|
||||
// case P384:
|
||||
default: return (memcmp(&_pub,&id._pub,sizeof(_pub)) < 0);
|
||||
}
|
||||
}
|
||||
if (_type == id._type)
|
||||
return memcmp(_hash.data(),id._hash.data(),ZT_SHA384_DIGEST_LEN) < 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -249,14 +234,15 @@ private:
|
|||
void _computeHash();
|
||||
|
||||
Address _address;
|
||||
Hash<384> _hash;
|
||||
ZT_PACKED_STRUCT(struct { // don't re-order these
|
||||
Fingerprint _hash;
|
||||
ZT_PACKED_STRUCT(struct { // do not re-order these fields
|
||||
uint8_t c25519[ZT_C25519_PRIVATE_KEY_LEN];
|
||||
uint8_t p384[ZT_ECC384_PRIVATE_KEY_SIZE];
|
||||
}) _priv;
|
||||
ZT_PACKED_STRUCT(struct { // don't re-order these
|
||||
ZT_PACKED_STRUCT(struct { // do not re-order these fields
|
||||
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;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
|
|
|
@ -179,8 +179,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 "bc72fb58e4:1:fya26hekqeromqdtpzq3mzj26zecwf7pkjahictpreapv4sw5vjcdkf6tbwaajzw6cq2ro6usrtzerccr37n52hiydogi2boaxk4tjidnhctgsbk4i4g34madrxihraurflyoe3xgeqkbpj2zrlsivscvbygzd3zfqs3qihoi6e24xy2jridq:tqaxnh3pucstd2xuwylgjfapyug7zdxorfwv37ted66qic6fu5g3pveodg7so4vt7cil7ptoht6msn6m2tsrfyd52a5f3b3g5wbd5ljjds2sftrjjw3qcb645eg4iizbqv5mlphgpa2uznonoo77qblbx6fdjh2nbt3ksooebj377rgu6qmq"
|
||||
#define IDENTITY_V1_KNOWN_BAD_0 "bc82fb58e4:1:fya26hekqeromqdtpzq3mzj26zecwf7pkjahictpreapv4sw5vjcdkf6tbwaajzw6cq2ro6usrtzerccr37n52hiydogi2boaxk4tjidnhctgsbk4i4g34madrxihraurflyoe3xgeqkbpj2zrlsivscvbygzd3zfqs3qihoi6e24xy2jridq:tqaxnh3pucstd2xuwylgjfapyug7zdxorfwv37ted66qic6fu5g3pveodg7so4vt7cil7ptoht6msn6m2tsrfyd52a5f3b3g5wbd5ljjds2sftrjjw3qcb645eg4iizbqv5mlphgpa2uznonoo77qblbx6fdjh2nbt3ksooebj377rgu6qmq"
|
||||
#define IDENTITY_V1_KNOWN_GOOD_0 "237ce8d8e2:1:5w3rj6am3sa7f5vtwm535iswob6ngmkpdidijz5ormqrfwkj55lhwyyszruu4rkbjycmlxzzoiuwtyw5s2mybknqx5j2cwxnaflqbwycoio2hqzcro5afrpcncnxlemzs6bt5linlib5flsej3f3r3bbzclxk733ei7tdrtm5uruiwpmyi4vgaafze42sx6hpe:mwjavgvhxz75ow2fhgq3zu4qfou5kce4wzegpjjd6545fpjnhjxb26e5unuutv7k3c6sm6umpyvatgpufwehi4wqmyudvq724h2klbiem6txs2h5iit5crgg3e6se5xeomuqhircv7zhkylrtnlgh57il742pwkrdgt4lz5fstetmiw7y3rq"
|
||||
#define IDENTITY_V1_KNOWN_BAD_0 "238ce8d8e2:1:5w3rj6am3sa7f5vtwm535iswob6ngmkpdidijz5ormqrfwkj55lhwyyszruu4rkbjycmlxzzoiuwtyw5s2mybknqx5j2cwxnaflqbwycoio2hqzcro5afrpcncnxlemzs6bt5linlib5flsej3f3r3bbzclxk733ei7tdrtm5uruiwpmyi4vgaafze42sx6hpe:mwjavgvhxz75ow2fhgq3zu4qfou5kce4wzegpjjd6545fpjnhjxb26e5unuutv7k3c6sm6umpyvatgpufwehi4wqmyudvq724h2klbiem6txs2h5iit5crgg3e6se5xeomuqhircv7zhkylrtnlgh57il742pwkrdgt4lz5fstetmiw7y3rq"
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
@ -353,16 +353,6 @@ extern "C" const char *ZTT_general()
|
|||
ZT_T_PRINTF("OK" ZT_EOL_S);
|
||||
}
|
||||
|
||||
{
|
||||
ZT_T_PRINTF("[general] Testing MIMC52... ");
|
||||
const uint64_t proof = mimc52Delay("testing",7,1000);
|
||||
if ((!mimc52Verify("testing",7,1000,proof))||(proof != 0x0007a1a0a1b0fe32)) {
|
||||
ZT_T_PRINTF("FAILED (%.16llx)" ZT_EOL_S,proof);
|
||||
return "MIMC52 failed simple delay/verify test";
|
||||
}
|
||||
ZT_T_PRINTF("OK (%.16llx)" ZT_EOL_S,proof);
|
||||
}
|
||||
|
||||
{
|
||||
ZT_T_PRINTF("[general] Testing FCV (fixed capacity vector)... ");
|
||||
long cnt = 0;
|
||||
|
@ -646,6 +636,16 @@ 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("testing",7,1000);
|
||||
if ((!mimc52Verify("testing",7,1000,proof))||(proof != 0x0007a1a0a1b0fe32)) {
|
||||
ZT_T_PRINTF("FAILED (%.16llx)" ZT_EOL_S,proof);
|
||||
return "MIMC52 failed simple delay/verify 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... ");
|
||||
|
@ -862,18 +862,18 @@ extern "C" const char *ZTT_benchmarkCrypto()
|
|||
memset(tag,0,sizeof(tag));
|
||||
|
||||
{
|
||||
ZT_T_PRINTF("[crypto] Benchmarking MIMC52 delay... ");
|
||||
ZT_T_PRINTF("[crypto] Benchmarking SHA384... ");
|
||||
int64_t start = now();
|
||||
const uint64_t proof = mimc52Delay("testing",7,250000);
|
||||
for(int i=0;i<10000;++i)
|
||||
SHA384(tmp,tmp,sizeof(tmp));
|
||||
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 verify... ");
|
||||
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 SHA512... ");
|
||||
start = now();
|
||||
foo = (uint8_t)mimc52Verify("testing",7,1000000,proof); // doesn't matter if return is true or false here
|
||||
for(int i=0;i<10000;++i)
|
||||
SHA512(tmp,tmp,sizeof(tmp));
|
||||
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("%.4f MiB/sec" ZT_EOL_S,((16384.0 * 10000.0) / 1048576.0) / ((double)(end - start) / 1000.0));
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -999,6 +999,21 @@ extern "C" const char *ZTT_benchmarkCrypto()
|
|||
ZT_T_PRINTF("%.4f μs/verify" ZT_EOL_S,((double)(end - start) * 1000.0) / (double)(500 * ZT_NUM_C25519_TEST_VECTORS));
|
||||
}
|
||||
|
||||
{
|
||||
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 V0 Identity generation... ");
|
||||
Identity id;
|
||||
|
@ -1008,7 +1023,7 @@ extern "C" const char *ZTT_benchmarkCrypto()
|
|||
foo = (uint8_t)id.address().toInt();
|
||||
}
|
||||
int64_t end = now();
|
||||
ZT_T_PRINTF("%.4f ms/generation" ZT_EOL_S,(double)(end - start) / 5.0);
|
||||
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 V1 Identity generation... ");
|
||||
start = now();
|
||||
for(long i=0;i<5;++i) {
|
||||
|
@ -1016,7 +1031,7 @@ extern "C" const char *ZTT_benchmarkCrypto()
|
|||
foo = (uint8_t)id.address().toInt();
|
||||
}
|
||||
end = now();
|
||||
ZT_T_PRINTF("%.4f ms/generation" ZT_EOL_S,(double)(end - start) / 5.0);
|
||||
ZT_T_PRINTF("%.4f ms/generation (relatively constant time)" ZT_EOL_S,(double)(end - start) / 5.0);
|
||||
}
|
||||
} catch (std::exception &e) {
|
||||
ZT_T_PRINTF(ZT_EOL_S "[crypto] Unexpected exception: %s" ZT_EOL_S,e.what());
|
||||
|
|
Loading…
Add table
Reference in a new issue