Minor future proofing and cleanup in CertificateOfMembership, improve comments in a few places.

This commit is contained in:
Adam Ierymenko 2020-03-02 10:25:15 -08:00
parent 6b49148d4e
commit a8db4a8d2d
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
9 changed files with 74 additions and 48 deletions

View file

@ -17,15 +17,15 @@ namespace ZeroTier {
CertificateOfMembership::CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Address &issuedTo) CertificateOfMembership::CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Address &issuedTo)
{ {
_qualifiers[0].id = COM_RESERVED_ID_TIMESTAMP; _qualifiers[COM_RESERVED_ID_TIMESTAMP].id = COM_RESERVED_ID_TIMESTAMP;
_qualifiers[0].value = timestamp; _qualifiers[COM_RESERVED_ID_TIMESTAMP].value = timestamp;
_qualifiers[0].maxDelta = timestampMaxDelta; _qualifiers[COM_RESERVED_ID_TIMESTAMP].maxDelta = timestampMaxDelta;
_qualifiers[1].id = COM_RESERVED_ID_NETWORK_ID; _qualifiers[COM_RESERVED_ID_NETWORK_ID].id = COM_RESERVED_ID_NETWORK_ID;
_qualifiers[1].value = nwid; _qualifiers[COM_RESERVED_ID_NETWORK_ID].value = nwid;
_qualifiers[1].maxDelta = 0; _qualifiers[COM_RESERVED_ID_NETWORK_ID].maxDelta = 0;
_qualifiers[2].id = COM_RESERVED_ID_ISSUED_TO; _qualifiers[COM_RESERVED_ID_ISSUED_TO].id = COM_RESERVED_ID_ISSUED_TO;
_qualifiers[2].value = issuedTo.toInt(); _qualifiers[COM_RESERVED_ID_ISSUED_TO].value = issuedTo.toInt();
_qualifiers[2].maxDelta = 0xffffffffffffffffULL; _qualifiers[COM_RESERVED_ID_ISSUED_TO].maxDelta = 0xffffffffffffffffULL;
_qualifierCount = 3; _qualifierCount = 3;
_signatureLength = 0; _signatureLength = 0;
} }
@ -107,7 +107,7 @@ bool CertificateOfMembership::sign(const Identity &with)
int CertificateOfMembership::marshal(uint8_t data[ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX]) const noexcept int CertificateOfMembership::marshal(uint8_t data[ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX]) const noexcept
{ {
data[0] = 1; data[0] = 1; // 96-byte signature length; a v2 is supported in unmarshal code where signature is length prefixed
Utils::storeBigEndian<uint16_t>(data + 1,(uint16_t)_qualifierCount); Utils::storeBigEndian<uint16_t>(data + 1,(uint16_t)_qualifierCount);
int p = 3; int p = 3;
for(unsigned int i=0;i<_qualifierCount;++i) { for(unsigned int i=0;i<_qualifierCount;++i) {
@ -117,10 +117,6 @@ int CertificateOfMembership::marshal(uint8_t data[ZT_CERTIFICATEOFMEMBERSHIP_MAR
} }
_signedBy.copyTo(data + p); p += ZT_ADDRESS_LENGTH; _signedBy.copyTo(data + p); p += ZT_ADDRESS_LENGTH;
if ((_signedBy)&&(_signatureLength == 96)) { if ((_signedBy)&&(_signatureLength == 96)) {
// UGLY: Ed25519 signatures in ZT are 96 bytes (64 + 32 bytes of hash).
// P-384 signatures are also 96 bytes, praise the horned one. That means
// we don't need to include a length. If we ever do we will need a new
// serialized object version, but only for those with length != 96.
memcpy(data + p,_signature,96); p += 96; memcpy(data + p,_signature,96); p += 96;
} }
return p; return p;
@ -128,7 +124,7 @@ int CertificateOfMembership::marshal(uint8_t data[ZT_CERTIFICATEOFMEMBERSHIP_MAR
int CertificateOfMembership::unmarshal(const uint8_t *data,int len) noexcept int CertificateOfMembership::unmarshal(const uint8_t *data,int len) noexcept
{ {
if ((len < 3)||(data[0] != 1)) if ((len < 3)||(data[0] == 0))
return -1; return -1;
unsigned int numq = Utils::loadBigEndian<uint16_t>(data + 1); unsigned int numq = Utils::loadBigEndian<uint16_t>(data + 1);
if (numq > ZT_NETWORK_COM_MAX_QUALIFIERS) if (numq > ZT_NETWORK_COM_MAX_QUALIFIERS)
@ -146,9 +142,19 @@ int CertificateOfMembership::unmarshal(const uint8_t *data,int len) noexcept
return -1; return -1;
_signedBy.setTo(data + p); p += ZT_ADDRESS_LENGTH; _signedBy.setTo(data + p); p += ZT_ADDRESS_LENGTH;
if (_signedBy) { if (_signedBy) {
if ((p + 96) > len)
return -1;
_signatureLength = 96; _signatureLength = 96;
if (data[0] > 1) {
// If the version byte is >1, signatures come prefixed by a length. This is the
// way it should have been in the first place. Version byte 1 indicates 96 byte
// signatures and is backward compatible with <2.x nodes.
if ((p + 2) >= len)
return -1;
_signatureLength = Utils::loadBigEndian<uint16_t>(data + p); p += 2;
if (_signatureLength == 0)
return -1;
}
if ((p + _signatureLength) > len)
return -1;
memcpy(_signature,data + p,96); memcpy(_signature,data + p,96);
p += 96; p += 96;
} }

View file

@ -129,6 +129,8 @@ public:
*/ */
ZT_ALWAYS_INLINE int64_t timestamp() const noexcept ZT_ALWAYS_INLINE int64_t timestamp() const noexcept
{ {
if (_qualifiers[COM_RESERVED_ID_TIMESTAMP].id == COM_RESERVED_ID_TIMESTAMP)
return (int64_t)_qualifiers[0].value;
for(unsigned int i=0;i<_qualifierCount;++i) { for(unsigned int i=0;i<_qualifierCount;++i) {
if (_qualifiers[i].id == COM_RESERVED_ID_TIMESTAMP) if (_qualifiers[i].id == COM_RESERVED_ID_TIMESTAMP)
return (int64_t)_qualifiers[i].value; return (int64_t)_qualifiers[i].value;
@ -141,6 +143,8 @@ public:
*/ */
ZT_ALWAYS_INLINE Address issuedTo() const noexcept ZT_ALWAYS_INLINE Address issuedTo() const noexcept
{ {
if (_qualifiers[COM_RESERVED_ID_ISSUED_TO].id == COM_RESERVED_ID_ISSUED_TO)
return Address(_qualifiers[2].value);
for(unsigned int i=0;i<_qualifierCount;++i) { for(unsigned int i=0;i<_qualifierCount;++i) {
if (_qualifiers[i].id == COM_RESERVED_ID_ISSUED_TO) if (_qualifiers[i].id == COM_RESERVED_ID_ISSUED_TO)
return Address(_qualifiers[i].value); return Address(_qualifiers[i].value);
@ -153,6 +157,8 @@ public:
*/ */
ZT_ALWAYS_INLINE uint64_t networkId() const noexcept ZT_ALWAYS_INLINE uint64_t networkId() const noexcept
{ {
if (_qualifiers[COM_RESERVED_ID_NETWORK_ID].id == COM_RESERVED_ID_NETWORK_ID)
return _qualifiers[COM_RESERVED_ID_NETWORK_ID].value;
for(unsigned int i=0;i<_qualifierCount;++i) { for(unsigned int i=0;i<_qualifierCount;++i) {
if (_qualifiers[i].id == COM_RESERVED_ID_NETWORK_ID) if (_qualifiers[i].id == COM_RESERVED_ID_NETWORK_ID)
return _qualifiers[i].value; return _qualifiers[i].value;

View file

@ -242,7 +242,7 @@ public:
} }
} }
ZT_ALWAYS_INLINE bool operator==(const FCV &v) const ZT_ALWAYS_INLINE bool operator==(const FCV &v) const noexcept
{ {
if (_s == v._s) { if (_s == v._s) {
for(unsigned int i=0;i<_s;++i) { for(unsigned int i=0;i<_s;++i) {
@ -253,11 +253,11 @@ public:
} }
return false; return false;
} }
ZT_ALWAYS_INLINE bool operator!=(const FCV &v) const { return (!(*this == v)); } ZT_ALWAYS_INLINE bool operator!=(const FCV &v) const noexcept { return (!(*this == v)); }
ZT_ALWAYS_INLINE bool operator<(const FCV &v) const { return std::lexicographical_compare(begin(),end(),v.begin(),v.end()); } ZT_ALWAYS_INLINE bool operator<(const FCV &v) const noexcept { return std::lexicographical_compare(begin(),end(),v.begin(),v.end()); }
ZT_ALWAYS_INLINE bool operator>(const FCV &v) const { return (v < *this); } ZT_ALWAYS_INLINE bool operator>(const FCV &v) const noexcept { return (v < *this); }
ZT_ALWAYS_INLINE bool operator<=(const FCV &v) const { return !(v < *this); } ZT_ALWAYS_INLINE bool operator<=(const FCV &v) const noexcept { return !(v < *this); }
ZT_ALWAYS_INLINE bool operator>=(const FCV &v) const { return !(*this < v); } ZT_ALWAYS_INLINE bool operator>=(const FCV &v) const noexcept { return !(*this < v); }
private: private:
unsigned int _s; unsigned int _s;

View file

@ -25,7 +25,6 @@
#include "SelfAwareness.hpp" #include "SelfAwareness.hpp"
#include "Network.hpp" #include "Network.hpp"
#include "Trace.hpp" #include "Trace.hpp"
#include "ScopedPtr.hpp"
#include "Locator.hpp" #include "Locator.hpp"
#include "Protocol.hpp" #include "Protocol.hpp"
#include "Expect.hpp" #include "Expect.hpp"
@ -112,6 +111,8 @@ Node::Node(void *uPtr,void *tPtr,const struct ZT_Node_Callbacks *callbacks,int64
stateObjectPut(tPtr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,idtmp,RR->publicIdentityStr,(unsigned int)strlen(RR->publicIdentityStr)); stateObjectPut(tPtr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,idtmp,RR->publicIdentityStr,(unsigned int)strlen(RR->publicIdentityStr));
} }
RR->identity.hashWithPrivate(RR->localCacheSymmetricKey);
// This constructs all the components of the ZeroTier core within a single contiguous memory container, // This constructs all the components of the ZeroTier core within a single contiguous memory container,
// which reduces memory fragmentation and may improve cache locality. // which reduces memory fragmentation and may improve cache locality.
_objects = new _NodeObjects(RR,tPtr); _objects = new _NodeObjects(RR,tPtr);

View file

@ -27,6 +27,9 @@
namespace ZeroTier { namespace ZeroTier {
namespace Protocol { namespace Protocol {
// The counter used to assign packet IDs / cryptographic nonces.
std::atomic<uint64_t> _s_packetIdCtr((uint64_t)time(nullptr) << 32U);
uint64_t createProbe(const Identity &sender,const Identity &recipient,const uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) noexcept uint64_t createProbe(const Identity &sender,const Identity &recipient,const uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) noexcept
{ {
uint8_t tmp[ZT_IDENTITY_HASH_SIZE + ZT_IDENTITY_HASH_SIZE]; uint8_t tmp[ZT_IDENTITY_HASH_SIZE + ZT_IDENTITY_HASH_SIZE];
@ -37,8 +40,6 @@ uint64_t createProbe(const Identity &sender,const Identity &recipient,const uint
return hash[0]; return hash[0];
} }
std::atomic<uint64_t> _s_packetIdCtr((uint64_t)time(nullptr) << 32U);
void armor(Buf &pkt,int packetSize,const uint8_t key[ZT_PEER_SECRET_KEY_LENGTH],uint8_t cipherSuite) noexcept void armor(Buf &pkt,int packetSize,const uint8_t key[ZT_PEER_SECRET_KEY_LENGTH],uint8_t cipherSuite) noexcept
{ {
Protocol::Header &ph = pkt.as<Protocol::Header>(); Protocol::Header &ph = pkt.as<Protocol::Header>();

View file

@ -30,12 +30,16 @@ class Trace;
class Expect; class Expect;
/** /**
* Holds global state for an instance of ZeroTier::Node * ZeroTier::Node execution context
*
* This just holds pointers and various other information used by all the
* various moving parts of a node. It's stored or passed as 'RR' to give it
* a common name througout the code.
*/ */
class RuntimeEnvironment class RuntimeEnvironment
{ {
public: public:
ZT_ALWAYS_INLINE RuntimeEnvironment(Node *n) : ZT_ALWAYS_INLINE RuntimeEnvironment(Node *n) noexcept :
node(n), node(n),
localNetworkController(nullptr), localNetworkController(nullptr),
rtmem(nullptr), rtmem(nullptr),
@ -53,6 +57,7 @@ public:
ZT_ALWAYS_INLINE ~RuntimeEnvironment() ZT_ALWAYS_INLINE ~RuntimeEnvironment()
{ {
Utils::burn(secretIdentityStr,sizeof(secretIdentityStr)); Utils::burn(secretIdentityStr,sizeof(secretIdentityStr));
Utils::burn(localCacheSymmetricKey,sizeof(localCacheSymmetricKey));
} }
// Node instance that owns this RuntimeEnvironment // Node instance that owns this RuntimeEnvironment
@ -64,12 +69,6 @@ public:
// Memory actually occupied by Trace, Switch, etc. // Memory actually occupied by Trace, Switch, etc.
void *rtmem; void *rtmem;
/* Order matters a bit here. These are constructed in this order
* and then deleted in the opposite order on Node exit. The order ensures
* that things that are needed are there before they're needed.
*
* These are constant and never null after startup unless indicated. */
Trace *t; Trace *t;
Expect *expect; Expect *expect;
VL2 *vl2; VL2 *vl2;
@ -81,6 +80,10 @@ public:
Identity identity; Identity identity;
char publicIdentityStr[ZT_IDENTITY_STRING_BUFFER_LENGTH]; char publicIdentityStr[ZT_IDENTITY_STRING_BUFFER_LENGTH];
char secretIdentityStr[ZT_IDENTITY_STRING_BUFFER_LENGTH]; char secretIdentityStr[ZT_IDENTITY_STRING_BUFFER_LENGTH];
// A hash of this node identity's public and private keys that is used as
// a secret key to encrypt locally cached sensitive information.
uint8_t localCacheSymmetricKey[ZT_IDENTITY_HASH_SIZE];
}; };
} // namespace ZeroTier } // namespace ZeroTier

View file

@ -1198,7 +1198,7 @@ int main(int argc,char **argv)
ok &= ZTT_crypto() == nullptr; ok &= ZTT_crypto() == nullptr;
ok &= ZTT_benchmarkCrypto() == nullptr; ok &= ZTT_benchmarkCrypto() == nullptr;
if (!ok) { if (!ok) {
ZT_T_PRINTF(ZT_EOL_S "At least one error occurred!" ZT_EOL_S); ZT_T_PRINTF(ZT_EOL_S "*** AT LEAST ONE TEST FAILED! ***" ZT_EOL_S);
return 1; return 1;
} }
return 0; return 0;

View file

@ -18,20 +18,24 @@
* To build these ensure that ZT_ENABLE_TESTS is defined during build time. * To build these ensure that ZT_ENABLE_TESTS is defined during build time.
* Otherwise they are omitted. * Otherwise they are omitted.
* *
* The macro ZT_STANDALONE_TESTS will also build a main() function for these
* tests for creating a stand-alone test executable. It will return zero if
* all tests pass and non-zero if at least one test fails.
*
* These symbols are defined extern "C" so tests can be called from regular * These symbols are defined extern "C" so tests can be called from regular
* C code, which is important for use via CGo or in plain C projects. * C code, which is important for use via CGo or in plain C projects.
* *
* The ZT_T_PRINTF macro defaults to printf() but if it's defined at compile * The ZT_T_PRINTF macro defaults to printf() but if it's defined at compile
* time (also must be set while building Tests.cpp) it can specify another * time (it must be set while building Tests.cpp) it can specify another
* function to use for output. Defining it to a no-op can be used to disable * function to use for output. Defining it to a no-op can be used to disable
* output. * output.
* *
* Each test function returns NULL if the tests succeeds or an error message * Each test function returns NULL if the tests succeeds or an error message
* on test failure. * on test failure.
* *
* Only the fuzzing test functions are thread-safe. Other test functions * Be aware that fuzzing tests can and will crash the program if a serious
* should not be called concurrently. It's okay to call different tests from * error is discovered. This is the point. It's also beneficial to run these
* different threads, but not the same test. * in "valgrind" or a similar tool to detect marginal bad behvaior.
*/ */
#ifndef ZT_TESTS_HPP #ifndef ZT_TESTS_HPP
@ -52,20 +56,24 @@ extern "C" {
#define ZT_T_PRINTF(fmt,...) printf((fmt),##__VA_ARGS__),fflush(stdout) #define ZT_T_PRINTF(fmt,...) printf((fmt),##__VA_ARGS__),fflush(stdout)
#endif #endif
// Basic self-tests --------------------------------------------------------------------------------------------------- /**
* Test platform, compiler behavior, utility functions, and core classes
*/
const char *ZTT_general(); const char *ZTT_general();
/**
* Test crypto using test vectors and simple scenarios
*
* This is not an absolutely exhaustive test, just a sanity check to make sure
* crypto routines are basically working.
*/
const char *ZTT_crypto(); const char *ZTT_crypto();
// Benchmarks --------------------------------------------------------------------------------------------------------- /**
* Run benchmarks of cryptographic routines and common constructions
*/
const char *ZTT_benchmarkCrypto(); const char *ZTT_benchmarkCrypto();
// Fuzzing ------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -327,6 +327,7 @@ public:
void saveAll(void *tPtr); void saveAll(void *tPtr);
private: private:
// Load cached peer and set 'peer' to it, if one is found.
void _loadCached(void *tPtr,const Address &zta,SharedPtr<Peer> &peer); void _loadCached(void *tPtr,const Address &zta,SharedPtr<Peer> &peer);
// This is a secure random integer created at startup to salt the calculation of path hash map keys // This is a secure random integer created at startup to salt the calculation of path hash map keys