mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-07 13:03:45 +02:00
Implement AES-GMAC-SIV and benchmark, rework COM and add a lot of comments and docs, and comments and docs elsewhere too.
This commit is contained in:
parent
f3540a5c7c
commit
d3a7468e83
12 changed files with 452 additions and 267 deletions
76
node/AES.hpp
76
node/AES.hpp
|
@ -118,11 +118,15 @@ public:
|
||||||
_decryptSW(reinterpret_cast<const uint8_t *>(in),reinterpret_cast<uint8_t *>(out));
|
_decryptSW(reinterpret_cast<const uint8_t *>(in),reinterpret_cast<uint8_t *>(out));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class GMACSIVEncryptor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Streaming GMAC calculator
|
* Streaming GMAC calculator
|
||||||
*/
|
*/
|
||||||
class GMAC
|
class GMAC
|
||||||
{
|
{
|
||||||
|
friend class GMACSIVEncryptor;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Create a new instance of GMAC (must be initialized with init() before use)
|
* Create a new instance of GMAC (must be initialized with init() before use)
|
||||||
|
@ -190,6 +194,8 @@ public:
|
||||||
*/
|
*/
|
||||||
class CTR
|
class CTR
|
||||||
{
|
{
|
||||||
|
friend class GMACSIVEncryptor;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ZT_INLINE CTR(const AES &aes) noexcept : _aes(aes) {}
|
ZT_INLINE CTR(const AES &aes) noexcept : _aes(aes) {}
|
||||||
|
|
||||||
|
@ -229,6 +235,76 @@ public:
|
||||||
unsigned int _len;
|
unsigned int _len;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt with AES-GMAC-SIV
|
||||||
|
*/
|
||||||
|
class GMACSIVEncryptor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a new AES-GMAC-SIV encryptor keyed with the provided AES instances
|
||||||
|
*
|
||||||
|
* @param k0 First of two AES instances keyed with K0
|
||||||
|
* @param k1 Second of two AES instances keyed with K1
|
||||||
|
*/
|
||||||
|
ZT_INLINE GMACSIVEncryptor(const AES &k0,const AES &k1) noexcept :
|
||||||
|
_gmac(k0),
|
||||||
|
_ctr(k1) {}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize AES-GMAC-SIV
|
||||||
|
*
|
||||||
|
* @param iv IV in network byte order (byte order in which it will appear on the wire)
|
||||||
|
* @param output Pointer to buffer to receive ciphertext, must be large enough for all to-be-processed data!
|
||||||
|
*/
|
||||||
|
ZT_INLINE void init(const uint64_t iv,void *const output) noexcept
|
||||||
|
{
|
||||||
|
_output = output;
|
||||||
|
_iv[0] = iv;
|
||||||
|
_iv[1] = 0;
|
||||||
|
_gmac.init(reinterpret_cast<const uint8_t *>(_iv));
|
||||||
|
}
|
||||||
|
|
||||||
|
ZT_INLINE void update1(const void *const input,const unsigned int len) noexcept
|
||||||
|
{
|
||||||
|
_gmac.update(input,len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finish first pass, compute CTR IV, initialize second pass.
|
||||||
|
*/
|
||||||
|
ZT_INLINE void finish1() noexcept
|
||||||
|
{
|
||||||
|
uint64_t gmacTag[2];
|
||||||
|
_gmac.finish(reinterpret_cast<uint8_t *>(gmacTag));
|
||||||
|
_iv[1] = gmacTag[0];
|
||||||
|
_ctr._aes.encrypt(_iv,_iv);
|
||||||
|
_ctr.init(reinterpret_cast<const uint8_t *>(_iv),_output);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZT_INLINE void update2(const void *const input,const unsigned int len) noexcept
|
||||||
|
{
|
||||||
|
_ctr.crypt(input,len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finish second pass and return a pointer to the opaque 128-bit IV+MAC block
|
||||||
|
*
|
||||||
|
* @return Pointer to 128-bit opaque IV+MAC
|
||||||
|
*/
|
||||||
|
ZT_INLINE const uint8_t *finish2()
|
||||||
|
{
|
||||||
|
_ctr.finish();
|
||||||
|
return reinterpret_cast<const uint8_t *>(_iv);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void *_output;
|
||||||
|
uint64_t _iv[2];
|
||||||
|
AES::GMAC _gmac;
|
||||||
|
AES::CTR _ctr;
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const uint32_t Te0[256];
|
static const uint32_t Te0[256];
|
||||||
static const uint32_t Te1[256];
|
static const uint32_t Te1[256];
|
||||||
|
|
|
@ -15,167 +15,267 @@
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
CertificateOfMembership::CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Address &issuedTo)
|
CertificateOfMembership::CertificateOfMembership(const int64_t timestamp,const int64_t timestampMaxDelta,const uint64_t nwid,const Identity &issuedTo) noexcept :
|
||||||
|
_timestamp(timestamp),
|
||||||
|
_timestampMaxDelta(timestampMaxDelta),
|
||||||
|
_networkId(nwid),
|
||||||
|
_issuedTo(issuedTo.fingerprint()),
|
||||||
|
_signatureLength(0) {}
|
||||||
|
|
||||||
|
bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other) const noexcept
|
||||||
{
|
{
|
||||||
_qualifiers[COM_RESERVED_ID_TIMESTAMP].id = COM_RESERVED_ID_TIMESTAMP;
|
// NOTE: we always do explicit absolute value with an if() since llabs() can have overflow
|
||||||
_qualifiers[COM_RESERVED_ID_TIMESTAMP].value = timestamp;
|
// conditions that could introduce a vulnerability.
|
||||||
_qualifiers[COM_RESERVED_ID_TIMESTAMP].maxDelta = timestampMaxDelta;
|
|
||||||
_qualifiers[COM_RESERVED_ID_NETWORK_ID].id = COM_RESERVED_ID_NETWORK_ID;
|
|
||||||
_qualifiers[COM_RESERVED_ID_NETWORK_ID].value = nwid;
|
|
||||||
_qualifiers[COM_RESERVED_ID_NETWORK_ID].maxDelta = 0;
|
|
||||||
_qualifiers[COM_RESERVED_ID_ISSUED_TO].id = COM_RESERVED_ID_ISSUED_TO;
|
|
||||||
_qualifiers[COM_RESERVED_ID_ISSUED_TO].value = issuedTo.toInt();
|
|
||||||
_qualifiers[COM_RESERVED_ID_ISSUED_TO].maxDelta = 0xffffffffffffffffULL;
|
|
||||||
_qualifierCount = 3;
|
|
||||||
_signatureLength = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CertificateOfMembership::setQualifier(uint64_t id,uint64_t value,uint64_t maxDelta)
|
if (other._timestamp > _timestamp) {
|
||||||
{
|
if ((other._timestamp - _timestamp) > _timestampMaxDelta)
|
||||||
_signedBy.zero();
|
|
||||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
|
||||||
if (_qualifiers[i].id == id) {
|
|
||||||
_qualifiers[i].value = value;
|
|
||||||
_qualifiers[i].maxDelta = maxDelta;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_qualifierCount < ZT_NETWORK_COM_MAX_QUALIFIERS) {
|
|
||||||
_qualifiers[_qualifierCount].id = id;
|
|
||||||
_qualifiers[_qualifierCount].value = value;
|
|
||||||
_qualifiers[_qualifierCount].maxDelta = maxDelta;
|
|
||||||
++_qualifierCount;
|
|
||||||
std::sort(&(_qualifiers[0]),&(_qualifiers[_qualifierCount]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other) const
|
|
||||||
{
|
|
||||||
unsigned int myidx = 0;
|
|
||||||
unsigned int otheridx = 0;
|
|
||||||
|
|
||||||
if ((_qualifierCount == 0)||(other._qualifierCount == 0))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
while (myidx < _qualifierCount) {
|
|
||||||
// Fail if we're at the end of other, since this means the field is
|
|
||||||
// missing.
|
|
||||||
if (otheridx >= other._qualifierCount)
|
|
||||||
return false;
|
return false;
|
||||||
|
} else {
|
||||||
|
if ((_timestamp - other._timestamp) > _timestampMaxDelta)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Seek to corresponding tuple in other, ignoring tuples that
|
// us <> them
|
||||||
// we may not have. If we run off the end of other, the tuple is
|
for(FCV<_Qualifier,ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS>::const_iterator i(_additionalQualifiers.begin());i != _additionalQualifiers.end();++i) {
|
||||||
// missing. This works because tuples are sorted by ID.
|
if (i->delta != 0xffffffffffffffffULL) {
|
||||||
while (other._qualifiers[otheridx].id != _qualifiers[myidx].id) {
|
const uint64_t *v2 = nullptr;
|
||||||
++otheridx;
|
for(FCV<_Qualifier,ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS>::const_iterator j(other._additionalQualifiers.begin());j != other._additionalQualifiers.end();++i) {
|
||||||
if (otheridx >= other._qualifierCount)
|
if (j->id == i->id) {
|
||||||
|
v2 = &(j->value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!v2)
|
||||||
return false;
|
return false;
|
||||||
|
if (*v2 > i->value) {
|
||||||
|
if ((*v2 - i->value) > i->delta)
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if ((i->value - *v2) > i->delta)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare to determine if the absolute value of the difference
|
|
||||||
// between these two parameters is within our maxDelta.
|
|
||||||
const uint64_t a = _qualifiers[myidx].value;
|
|
||||||
const uint64_t b = other._qualifiers[myidx].value;
|
|
||||||
if (((a >= b) ? (a - b) : (b - a)) > _qualifiers[myidx].maxDelta)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
++myidx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
// them <> us
|
||||||
|
for(FCV<_Qualifier,ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS>::const_iterator i(other._additionalQualifiers.begin());i != other._additionalQualifiers.end();++i) {
|
||||||
|
if (i->delta != 0xffffffffffffffffULL) {
|
||||||
|
const uint64_t *v2 = nullptr;
|
||||||
|
for(FCV<_Qualifier,ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS>::const_iterator j(_additionalQualifiers.begin());j != _additionalQualifiers.end();++i) {
|
||||||
|
if (j->id == i->id) {
|
||||||
|
v2 = &(j->value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!v2)
|
||||||
|
return false;
|
||||||
|
if (*v2 > i->value) {
|
||||||
|
if ((*v2 - i->value) > i->delta)
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if ((i->value - *v2) > i->delta)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SECURITY: check for issued-to inequality is a sanity check. This should be impossible since elsewhere
|
||||||
|
// in the code COMs are checked to ensure that they do in fact belong to their issued-to identities.
|
||||||
|
return (other._networkId != _networkId) && (other._issuedTo.address() != _issuedTo.address());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CertificateOfMembership::sign(const Identity &with)
|
bool CertificateOfMembership::sign(const Identity &with) noexcept
|
||||||
{
|
{
|
||||||
uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3];
|
_signedBy = with.address();
|
||||||
unsigned int ptr = 0;
|
uint64_t buf[ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX / 8];
|
||||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
const unsigned int bufSize = _fillSigningBuf(buf);
|
||||||
buf[ptr++] = Utils::hton(_qualifiers[i].id);
|
_signatureLength = with.sign(buf,bufSize,_signature,sizeof(_signature));
|
||||||
buf[ptr++] = Utils::hton(_qualifiers[i].value);
|
return _signatureLength > 0;
|
||||||
buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
_signatureLength = with.sign(buf,ptr * sizeof(uint64_t),_signature,sizeof(_signature));
|
|
||||||
_signedBy = with.address();
|
|
||||||
return true;
|
|
||||||
} catch ( ... ) {
|
|
||||||
_signedBy.zero();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 bool v2) const noexcept
|
||||||
{
|
{
|
||||||
data[0] = 1; // 96-byte signature length; a v2 is supported in unmarshal code where signature is length prefixed
|
data[0] = v2 ? 2 : 1;
|
||||||
Utils::storeBigEndian<uint16_t>(data + 1,(uint16_t)_qualifierCount);
|
|
||||||
|
// All formats start with the standard three qualifiers: timestamp with delta, network ID as a strict
|
||||||
|
// equality compare, and the address of the issued-to node as an informational tuple.
|
||||||
int p = 3;
|
int p = 3;
|
||||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
Utils::storeBigEndian<uint64_t>(data + p,0); p += 8;
|
||||||
Utils::storeBigEndian<uint64_t>(data + p,_qualifiers[i].id); p += 8;
|
Utils::storeBigEndian<uint64_t>(data + p,(uint64_t)_timestamp); p += 8;
|
||||||
Utils::storeBigEndian<uint64_t>(data + p,_qualifiers[i].value); p += 8;
|
Utils::storeBigEndian<uint64_t>(data + p,(uint64_t)_timestampMaxDelta); p += 8;
|
||||||
Utils::storeBigEndian<uint64_t>(data + p,_qualifiers[i].maxDelta); p += 8;
|
Utils::storeBigEndian<uint64_t>(data + p,1); p += 8;
|
||||||
|
Utils::storeBigEndian<uint64_t>(data + p,_networkId); p += 8;
|
||||||
|
Utils::storeBigEndian<uint64_t>(data + p,0); p += 8;
|
||||||
|
Utils::storeBigEndian<uint64_t>(data + p,2); p += 8;
|
||||||
|
Utils::storeBigEndian<uint64_t>(data + p,_issuedTo.address().toInt()); p += 8;
|
||||||
|
Utils::storeAsIsEndian<uint64_t>(data + p,0xffffffffffffffffULL); p += 8;
|
||||||
|
|
||||||
|
if (v2) {
|
||||||
|
// V2 marshal format will have three tuples followed by the fingerprint hash.
|
||||||
|
Utils::storeBigEndian<uint16_t>(data + 1,3);
|
||||||
|
memcpy(data + p,_issuedTo.hash(),48);
|
||||||
|
p += 48;
|
||||||
|
} else {
|
||||||
|
// V1 marshal format must shove everything into tuples, resulting in nine.
|
||||||
|
Utils::storeBigEndian<uint16_t>(data + 1,9);
|
||||||
|
for(int k=0;k<6;++k) {
|
||||||
|
Utils::storeBigEndian<uint64_t>(data + p,(uint64_t)k + 3); p += 8;
|
||||||
|
Utils::storeAsIsEndian<uint64_t>(data + p,Utils::loadAsIsEndian<uint64_t>(_issuedTo.hash() + (k * 8))); p += 8;
|
||||||
|
Utils::storeAsIsEndian<uint64_t>(data + p,0xffffffffffffffffULL); p += 8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_signedBy.copyTo(data + p); p += ZT_ADDRESS_LENGTH;
|
|
||||||
if ((_signedBy)&&(_signatureLength == 96)) {
|
_signedBy.copyTo(data + p); p += 5;
|
||||||
memcpy(data + p,_signature,96); p += 96;
|
|
||||||
|
if (v2) {
|
||||||
|
// V2 marshal format prefixes signatures with a 16-bit length to support future signature types.
|
||||||
|
Utils::storeBigEndian<uint16_t>(data + p,(uint16_t)_signatureLength); p += 2;
|
||||||
|
memcpy(data + p,_signature,_signatureLength);
|
||||||
|
p += (int)_signatureLength;
|
||||||
|
} else {
|
||||||
|
// V1 only supports 96-byte signature fields.
|
||||||
|
memcpy(data + p,_signature,96);
|
||||||
|
p += 96;
|
||||||
}
|
}
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
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] == 0))
|
if (len < (1 + 2 + 72))
|
||||||
return -1;
|
return -1;
|
||||||
unsigned int numq = Utils::loadBigEndian<uint16_t>(data + 1);
|
|
||||||
if (numq > ZT_NETWORK_COM_MAX_QUALIFIERS)
|
TriviallyCopyable::memoryZero(this);
|
||||||
|
|
||||||
|
const unsigned int numq = Utils::loadBigEndian<uint16_t>(data + 1);
|
||||||
|
if ((numq < 3)||(numq > (ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS + 3)))
|
||||||
return -1;
|
return -1;
|
||||||
_qualifierCount = numq;
|
|
||||||
int p = 3;
|
int p = 3;
|
||||||
for(unsigned int i=0;i<numq;++i) {
|
for(unsigned int q=0;q<numq;++q) {
|
||||||
if ((p + 24) > len)
|
if ((p + 24) > len)
|
||||||
return -1;
|
return -1;
|
||||||
_qualifiers[i].id = Utils::loadBigEndian<uint64_t>(data + p); p += 8;
|
const uint64_t id = Utils::loadBigEndian<uint64_t>(data + p); p += 8;
|
||||||
_qualifiers[i].value = Utils::loadBigEndian<uint64_t>(data + p); p += 8;
|
const uint64_t value = Utils::loadBigEndian<uint64_t>(data + p); p += 8;
|
||||||
_qualifiers[i].maxDelta = Utils::loadBigEndian<uint64_t>(data + p); p += 8;
|
const uint64_t delta = Utils::loadBigEndian<uint64_t>(data + p); p += 8;
|
||||||
}
|
switch(id) {
|
||||||
if ((p + ZT_ADDRESS_LENGTH) > len)
|
case 0:
|
||||||
return -1;
|
_timestamp = (int64_t)value;
|
||||||
_signedBy.setTo(data + p); p += ZT_ADDRESS_LENGTH;
|
_timestampMaxDelta = (int64_t)delta;
|
||||||
if (_signedBy) {
|
break;
|
||||||
_signatureLength = 96;
|
case 1:
|
||||||
if (data[0] > 1) {
|
_networkId = value;
|
||||||
// If the version byte is >1, signatures come prefixed by a length. This is the
|
break;
|
||||||
// way it should have been in the first place. Version byte 1 indicates 96 byte
|
case 2:
|
||||||
// signatures and is backward compatible with <2.x nodes.
|
_issuedTo.apiFingerprint()->address = value;
|
||||||
if ((p + 2) >= len)
|
break;
|
||||||
return -1;
|
|
||||||
_signatureLength = Utils::loadBigEndian<uint16_t>(data + p); p += 2;
|
// V1 nodes will pack the hash into qualifier tuples.
|
||||||
if (_signatureLength == 0)
|
case 3:
|
||||||
return -1;
|
Utils::storeBigEndian<uint64_t>(_issuedTo.apiFingerprint()->hash,value);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
Utils::storeBigEndian<uint64_t>(_issuedTo.apiFingerprint()->hash + 8,value);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
Utils::storeBigEndian<uint64_t>(_issuedTo.apiFingerprint()->hash + 16,value);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
Utils::storeBigEndian<uint64_t>(_issuedTo.apiFingerprint()->hash + 24,value);
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
Utils::storeBigEndian<uint64_t>(_issuedTo.apiFingerprint()->hash + 32,value);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
Utils::storeBigEndian<uint64_t>(_issuedTo.apiFingerprint()->hash + 40,value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (_additionalQualifiers.size() == _additionalQualifiers.capacity())
|
||||||
|
return -1;
|
||||||
|
_additionalQualifiers.push_back(_Qualifier(id,value,delta));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if ((int)(p + _signatureLength) > len)
|
|
||||||
return -1;
|
|
||||||
memcpy(_signature,data + p,96);
|
|
||||||
p += 96;
|
|
||||||
}
|
}
|
||||||
return p;
|
|
||||||
|
std::sort(_additionalQualifiers.begin(),_additionalQualifiers.end());
|
||||||
|
|
||||||
|
if (data[0] == 1) {
|
||||||
|
if ((p + 96) > len)
|
||||||
|
return -1;
|
||||||
|
_signatureLength = 96;
|
||||||
|
memcpy(_signature,data + p,96);
|
||||||
|
return p + 96;
|
||||||
|
} else if (data[0] == 2) {
|
||||||
|
if ((p + 48) > len)
|
||||||
|
return -1;
|
||||||
|
memcpy(_issuedTo.apiFingerprint()->hash,data + p,48);
|
||||||
|
p += 48;
|
||||||
|
if ((p + 2) > len)
|
||||||
|
return -1;
|
||||||
|
_signatureLength = Utils::loadBigEndian<uint16_t>(data + p);
|
||||||
|
if ((_signatureLength > sizeof(_signature))||((p + _signatureLength) > len))
|
||||||
|
return -1;
|
||||||
|
memcpy(_signature,data + p,_signatureLength);
|
||||||
|
return p + (int)_signatureLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CertificateOfMembership::operator==(const CertificateOfMembership &c) const
|
unsigned int CertificateOfMembership::_fillSigningBuf(uint64_t buf[ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX / 8]) const noexcept
|
||||||
{
|
{
|
||||||
if (_signedBy != c._signedBy)
|
const uint64_t informational = 0xffffffffffffffffULL;
|
||||||
return false;
|
|
||||||
if (_qualifierCount != c._qualifierCount)
|
/*
|
||||||
return false;
|
* Signing always embeds all data to be signed in qualifier tuple format for
|
||||||
if (_signatureLength != c._signatureLength)
|
* backward compatibility with V1 nodes, since otherwise we'd need a signature
|
||||||
return false;
|
* for v1 nodes to verify and another for v2 nodes to verify.
|
||||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
*/
|
||||||
const _Qualifier &a = _qualifiers[i];
|
|
||||||
const _Qualifier &b = c._qualifiers[i];
|
// The standard three tuples that must begin every COM.
|
||||||
if ((a.id != b.id)||(a.value != b.value)||(a.maxDelta != b.maxDelta))
|
buf[0] = 0;
|
||||||
return false;
|
buf[1] = Utils::hton((uint64_t)_timestamp);
|
||||||
|
buf[2] = Utils::hton((uint64_t)_timestampMaxDelta);
|
||||||
|
buf[3] = ZT_CONST_TO_BE_UINT64(1);
|
||||||
|
buf[4] = Utils::hton(_networkId);
|
||||||
|
buf[5] = 0;
|
||||||
|
buf[6] = ZT_CONST_TO_BE_UINT64(2);
|
||||||
|
buf[7] = Utils::hton(_issuedTo.address().toInt());
|
||||||
|
buf[8] = informational;
|
||||||
|
|
||||||
|
unsigned int p = 9;
|
||||||
|
|
||||||
|
// The full identity fingerprint of the peer to whom the COM was issued,
|
||||||
|
// embeded as a series of informational tuples.
|
||||||
|
if (_issuedTo.haveHash()) {
|
||||||
|
buf[p++] = ZT_CONST_TO_BE_UINT64(3);
|
||||||
|
buf[p++] = Utils::loadAsIsEndian<uint64_t>(_issuedTo.hash());
|
||||||
|
buf[p++] = informational;
|
||||||
|
buf[p++] = ZT_CONST_TO_BE_UINT64(4);
|
||||||
|
buf[p++] = Utils::loadAsIsEndian<uint64_t>(_issuedTo.hash() + 8);
|
||||||
|
buf[p++] = informational;
|
||||||
|
buf[p++] = ZT_CONST_TO_BE_UINT64(5);
|
||||||
|
buf[p++] = Utils::loadAsIsEndian<uint64_t>(_issuedTo.hash() + 16);
|
||||||
|
buf[p++] = informational;
|
||||||
|
buf[p++] = ZT_CONST_TO_BE_UINT64(6);
|
||||||
|
buf[p++] = Utils::loadAsIsEndian<uint64_t>(_issuedTo.hash() + 24);
|
||||||
|
buf[p++] = informational;
|
||||||
|
buf[p++] = ZT_CONST_TO_BE_UINT64(7);
|
||||||
|
buf[p++] = Utils::loadAsIsEndian<uint64_t>(_issuedTo.hash() + 32);
|
||||||
|
buf[p++] = informational;
|
||||||
|
buf[p++] = ZT_CONST_TO_BE_UINT64(8);
|
||||||
|
buf[p++] = Utils::loadAsIsEndian<uint64_t>(_issuedTo.hash() + 40);
|
||||||
|
buf[p++] = informational;
|
||||||
}
|
}
|
||||||
return (memcmp(_signature,c._signature,_signatureLength) == 0);
|
|
||||||
|
for(FCV<_Qualifier,ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS>::const_iterator i(_additionalQualifiers.begin());i != _additionalQualifiers.end();++i) {
|
||||||
|
buf[p++] = Utils::hton(i->id);
|
||||||
|
buf[p++] = Utils::hton(i->value);
|
||||||
|
buf[p++] = Utils::hton(i->delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
return p * 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
#ifndef ZT_CERTIFICATEOFMEMBERSHIP_HPP
|
#ifndef ZT_CERTIFICATEOFMEMBERSHIP_HPP
|
||||||
#define ZT_CERTIFICATEOFMEMBERSHIP_HPP
|
#define ZT_CERTIFICATEOFMEMBERSHIP_HPP
|
||||||
|
|
||||||
|
// TODO: redo
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
@ -27,13 +29,13 @@
|
||||||
#include "C25519.hpp"
|
#include "C25519.hpp"
|
||||||
#include "Identity.hpp"
|
#include "Identity.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
|
#include "FCV.hpp"
|
||||||
|
|
||||||
/**
|
// Maximum number of additional tuples beyond the standard always-present three.
|
||||||
* Maximum number of qualifiers allowed in a COM (absolute max: 65535)
|
#define ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS 8
|
||||||
*/
|
|
||||||
#define ZT_NETWORK_COM_MAX_QUALIFIERS 8
|
|
||||||
|
|
||||||
#define ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX (1 + 2 + (24 * ZT_NETWORK_COM_MAX_QUALIFIERS) + 5 + ZT_SIGNATURE_BUFFER_SIZE)
|
// version + qualifier count + three required qualifiers + additional qualifiers +
|
||||||
|
#define ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX (1 + 2 + (3 * 3 * 8) + (ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS * 3 * 8) + 144 + 5 + 2 + 96)
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
|
@ -42,28 +44,62 @@ class RuntimeEnvironment;
|
||||||
/**
|
/**
|
||||||
* Certificate of network membership
|
* Certificate of network membership
|
||||||
*
|
*
|
||||||
* The COM contains a sorted set of three-element tuples called qualifiers.
|
* This is the fundamental permission object issued by network controllers to members of networks
|
||||||
* These contain an id, a value, and a maximum delta.
|
* to admit them into networks.
|
||||||
*
|
*
|
||||||
* The ID is arbitrary and should be assigned using a scheme that makes
|
* A certificate of membership (COM) consists of a series of tuples called qualifiers as well
|
||||||
* every ID globally unique. IDs beneath 65536 are reserved for global
|
* as the full identity fingerprint of the node being admitted, the address of the controller
|
||||||
* assignment by ZeroTier Networks.
|
* (for sanity checking), and a signature.
|
||||||
*
|
*
|
||||||
* The value's meaning is ID-specific and isn't important here. What's
|
* A qualifier is a tuple of three 64-bit unsigned integers: an id, a value, and a delta.
|
||||||
* important is the value and the third member of the tuple: the maximum
|
|
||||||
* delta. The maximum delta is the maximum difference permitted between
|
|
||||||
* values for a given ID between certificates for the two certificates to
|
|
||||||
* themselves agree.
|
|
||||||
*
|
*
|
||||||
* Network membership is checked by checking whether a peer's certificate
|
* Certiciates are checked between peers by determining if they agree. If the absolute value
|
||||||
* agrees with your own. The timestamp provides the fundamental criterion--
|
* of the difference between any two qualifier values exceeds its delta, the certificates do
|
||||||
* each member of a private network must constantly obtain new certificates
|
* not agree. A delta if 1 for example means that the values of two peers may differ by no more
|
||||||
* often enough to stay within the max delta for this qualifier. But other
|
* than one. A delta of 0 indicates values that must be the same. A delta of uint64_max is for
|
||||||
* criteria could be added in the future for very special behaviors, things
|
* informational tuples that are not included in certificate checking, as this means they may
|
||||||
* like latitude and longitude for instance.
|
* differ by any amount.
|
||||||
*
|
*
|
||||||
* This is a memcpy()'able structure and is safe (in a crash sense) to modify
|
* All COMs contain three initial tuples: timestamp, network ID, and the address of the
|
||||||
* without locks.
|
* issued-to node. The latter is informational. The network ID must equal exactly, though in
|
||||||
|
* theory a controller could allow a delta there to e.g. allow cross-communication between all
|
||||||
|
* of its networks. (This has never been done in practice.) The most important field is the
|
||||||
|
* timestamp, whose delta defines a moving window within which certificates must be timestamped
|
||||||
|
* by the network controller to agree. A certificate that is too old will fall out of this
|
||||||
|
* window vs its peers and will no longer be considered valid.
|
||||||
|
*
|
||||||
|
* (Revocations are a method to rapidly revoke access that works alongside this slower but
|
||||||
|
* more definitive method.)
|
||||||
|
*
|
||||||
|
* Certificate of membership wire format:
|
||||||
|
*
|
||||||
|
* This wire format comes in two versions: version 1 for ZeroTier 1.x, which will
|
||||||
|
* eventually go away once 1.x is out of support, and version 2 for ZeroTier 2.x and later.
|
||||||
|
*
|
||||||
|
* Version 2:
|
||||||
|
*
|
||||||
|
* <[1] wire format type byte: 1 or 2>
|
||||||
|
* <[2] 16-bit number of qualifier tuples>
|
||||||
|
* <[...] qualifier tuples>
|
||||||
|
* <[48] fingerprint hash of identity of peer to whom COM was issued>
|
||||||
|
* <[5] address of network controller>
|
||||||
|
* <[2] 16-bit size of signature>
|
||||||
|
* <[...] signature>
|
||||||
|
*
|
||||||
|
* Version 1 is identical except the fingerprint hash is omitted and is instead loaded
|
||||||
|
* into a series of six informational tuples. The signature size is also omitted and a
|
||||||
|
* 96-byte signature field is assumed.
|
||||||
|
*
|
||||||
|
* Qualifier tuples must appear in numeric order of ID, and the first three tuples
|
||||||
|
* must have IDs 0, 1, and 2 being the timestamp, network ID, and issued-to address
|
||||||
|
* respectively. In version 1 COMs the IDs 3-8 are used to pack in the full identity
|
||||||
|
* fingerprint, so these are reserved as well. Optional additional tuples (not currently
|
||||||
|
* used) must use ID 65536 or higher.
|
||||||
|
*
|
||||||
|
* Signatures are computed over tuples only for backward compatibility with v1, and we
|
||||||
|
* don't plan to change this. Tuples are emitted into a buffer in ascending numeric
|
||||||
|
* order with the fingerprint hash being packed into tuple IDs 3-8 and this buffer is
|
||||||
|
* then signed.
|
||||||
*/
|
*/
|
||||||
class CertificateOfMembership : public Credential
|
class CertificateOfMembership : public Credential
|
||||||
{
|
{
|
||||||
|
@ -72,33 +108,6 @@ class CertificateOfMembership : public Credential
|
||||||
public:
|
public:
|
||||||
static constexpr ZT_CredentialType credentialType() noexcept { return ZT_CREDENTIAL_TYPE_COM; }
|
static constexpr ZT_CredentialType credentialType() noexcept { return ZT_CREDENTIAL_TYPE_COM; }
|
||||||
|
|
||||||
/**
|
|
||||||
* Reserved qualifier IDs
|
|
||||||
*
|
|
||||||
* IDs below 1024 are reserved for use as standard IDs. Others are available
|
|
||||||
* for user-defined use.
|
|
||||||
*
|
|
||||||
* Addition of new required fields requires that code in hasRequiredFields
|
|
||||||
* be updated as well.
|
|
||||||
*/
|
|
||||||
enum ReservedId
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Timestamp of certificate
|
|
||||||
*/
|
|
||||||
COM_RESERVED_ID_TIMESTAMP = 0,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Network ID for which certificate was issued
|
|
||||||
*/
|
|
||||||
COM_RESERVED_ID_NETWORK_ID = 1,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ZeroTier address to whom certificate was issued
|
|
||||||
*/
|
|
||||||
COM_RESERVED_ID_ISSUED_TO = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an empty certificate of membership
|
* Create an empty certificate of membership
|
||||||
*/
|
*/
|
||||||
|
@ -112,12 +121,12 @@ public:
|
||||||
* @param nwid Network ID
|
* @param nwid Network ID
|
||||||
* @param issuedTo Certificate recipient
|
* @param issuedTo Certificate recipient
|
||||||
*/
|
*/
|
||||||
CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Address &issuedTo);
|
CertificateOfMembership(int64_t timestamp,int64_t timestampMaxDelta,uint64_t nwid,const Identity &issuedTo) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return True if there's something here
|
* @return True if there's something here
|
||||||
*/
|
*/
|
||||||
ZT_INLINE operator bool() const noexcept { return (_qualifierCount != 0); }
|
ZT_INLINE operator bool() const noexcept { return (_networkId != 0); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Credential ID, always 0 for COMs
|
* @return Credential ID, always 0 for COMs
|
||||||
|
@ -127,57 +136,17 @@ public:
|
||||||
/**
|
/**
|
||||||
* @return Timestamp for this cert and maximum delta for timestamp
|
* @return Timestamp for this cert and maximum delta for timestamp
|
||||||
*/
|
*/
|
||||||
ZT_INLINE int64_t timestamp() const noexcept
|
ZT_INLINE int64_t timestamp() const noexcept { return _timestamp; }
|
||||||
{
|
|
||||||
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) {
|
|
||||||
if (_qualifiers[i].id == COM_RESERVED_ID_TIMESTAMP)
|
|
||||||
return (int64_t)_qualifiers[i].value;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Address to which this cert was issued
|
* @return Fingerprint of identity to which this cert was issued
|
||||||
*/
|
*/
|
||||||
ZT_INLINE Address issuedTo() const noexcept
|
ZT_INLINE const Fingerprint &issuedTo() const noexcept { return _issuedTo; }
|
||||||
{
|
|
||||||
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) {
|
|
||||||
if (_qualifiers[i].id == COM_RESERVED_ID_ISSUED_TO)
|
|
||||||
return Address(_qualifiers[i].value);
|
|
||||||
}
|
|
||||||
return Address();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Network ID for which this cert was issued
|
* @return Network ID for which this cert was issued
|
||||||
*/
|
*/
|
||||||
ZT_INLINE uint64_t networkId() const noexcept
|
ZT_INLINE uint64_t networkId() const noexcept { return _networkId; }
|
||||||
{
|
|
||||||
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) {
|
|
||||||
if (_qualifiers[i].id == COM_RESERVED_ID_NETWORK_ID)
|
|
||||||
return _qualifiers[i].value;
|
|
||||||
}
|
|
||||||
return 0ULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add or update a qualifier in this certificate
|
|
||||||
*
|
|
||||||
* Any signature is invalidated and signedBy is set to null.
|
|
||||||
*
|
|
||||||
* @param id Qualifier ID
|
|
||||||
* @param value Qualifier value
|
|
||||||
* @param maxDelta Qualifier maximum allowed difference (absolute value of difference)
|
|
||||||
*/
|
|
||||||
void setQualifier(uint64_t id,uint64_t value,uint64_t maxDelta);
|
|
||||||
|
|
||||||
ZT_INLINE void setQualifier(ReservedId id,uint64_t value,uint64_t maxDelta) { setQualifier((uint64_t)id,value,maxDelta); }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compare two certificates for parameter agreement
|
* Compare two certificates for parameter agreement
|
||||||
|
@ -192,7 +161,7 @@ public:
|
||||||
* @param other Cert to compare with
|
* @param other Cert to compare with
|
||||||
* @return True if certs agree and 'other' may be communicated with
|
* @return True if certs agree and 'other' may be communicated with
|
||||||
*/
|
*/
|
||||||
bool agreesWith(const CertificateOfMembership &other) const;
|
bool agreesWith(const CertificateOfMembership &other) const noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sign this certificate
|
* Sign this certificate
|
||||||
|
@ -200,7 +169,7 @@ public:
|
||||||
* @param with Identity to sign with, must include private key
|
* @param with Identity to sign with, must include private key
|
||||||
* @return True if signature was successful
|
* @return True if signature was successful
|
||||||
*/
|
*/
|
||||||
bool sign(const Identity &with);
|
bool sign(const Identity &with) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify this COM and its signature
|
* Verify this COM and its signature
|
||||||
|
@ -210,31 +179,29 @@ public:
|
||||||
*/
|
*/
|
||||||
ZT_INLINE Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const { return _verify(RR,tPtr,*this); }
|
ZT_INLINE Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const { return _verify(RR,tPtr,*this); }
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Address that signed this certificate or null address if none
|
|
||||||
*/
|
|
||||||
ZT_INLINE const Address &signedBy() const noexcept { return _signedBy; }
|
|
||||||
|
|
||||||
static constexpr int marshalSizeMax() noexcept { return ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX; }
|
static constexpr int marshalSizeMax() noexcept { return ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX; }
|
||||||
int marshal(uint8_t data[ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX]) const noexcept;
|
int marshal(uint8_t data[ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX],bool v2) const noexcept;
|
||||||
int unmarshal(const uint8_t *data,int len) noexcept;
|
int unmarshal(const uint8_t *data,int len) noexcept;
|
||||||
|
|
||||||
bool operator==(const CertificateOfMembership &c) const;
|
|
||||||
ZT_INLINE bool operator!=(const CertificateOfMembership &c) const { return (!(*this == c)); }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
unsigned int _fillSigningBuf(uint64_t buf[ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX / 8]) const noexcept;
|
||||||
|
|
||||||
struct _Qualifier
|
struct _Qualifier
|
||||||
{
|
{
|
||||||
ZT_INLINE _Qualifier() noexcept : id(0),value(0),maxDelta(0) {}
|
ZT_INLINE _Qualifier() noexcept : id(0),value(0),delta(0) {}
|
||||||
|
ZT_INLINE _Qualifier(const uint64_t id_,const uint64_t value_,const uint64_t delta_) noexcept : id(id_),value(value_),delta(delta_) {}
|
||||||
uint64_t id;
|
uint64_t id;
|
||||||
uint64_t value;
|
uint64_t value;
|
||||||
uint64_t maxDelta;
|
uint64_t delta;
|
||||||
ZT_INLINE bool operator<(const _Qualifier &q) const noexcept { return (id < q.id); } // sort order
|
ZT_INLINE bool operator<(const _Qualifier &q) const noexcept { return (id < q.id); } // sort order
|
||||||
};
|
};
|
||||||
|
|
||||||
|
FCV<_Qualifier,ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS> _additionalQualifiers;
|
||||||
|
int64_t _timestamp;
|
||||||
|
int64_t _timestampMaxDelta;
|
||||||
|
uint64_t _networkId;
|
||||||
|
Fingerprint _issuedTo;
|
||||||
Address _signedBy;
|
Address _signedBy;
|
||||||
_Qualifier _qualifiers[ZT_NETWORK_COM_MAX_QUALIFIERS];
|
|
||||||
unsigned int _qualifierCount;
|
|
||||||
unsigned int _signatureLength;
|
unsigned int _signatureLength;
|
||||||
uint8_t _signature[ZT_SIGNATURE_BUFFER_SIZE];
|
uint8_t _signature[ZT_SIGNATURE_BUFFER_SIZE];
|
||||||
};
|
};
|
||||||
|
|
|
@ -72,22 +72,19 @@ Credential::VerifyResult Credential::_verify(const RuntimeEnvironment *const RR,
|
||||||
|
|
||||||
Credential::VerifyResult Credential::_verify(const RuntimeEnvironment *const RR,void *tPtr,const CertificateOfMembership &credential) const
|
Credential::VerifyResult Credential::_verify(const RuntimeEnvironment *const RR,void *tPtr,const CertificateOfMembership &credential) const
|
||||||
{
|
{
|
||||||
if ((!credential._signedBy)||(credential._signedBy != Network::controllerFor(credential.networkId()))||(credential._qualifierCount > ZT_NETWORK_COM_MAX_QUALIFIERS))
|
// Sanity check network ID.
|
||||||
|
if ((!credential._signedBy)||(credential._signedBy != Network::controllerFor(credential._networkId)))
|
||||||
return Credential::VERIFY_BAD_SIGNATURE;
|
return Credential::VERIFY_BAD_SIGNATURE;
|
||||||
|
|
||||||
|
// If we don't know the peer, get its identity. This shouldn't happen here but should be handled.
|
||||||
const SharedPtr<Peer> peer(RR->topology->peer(tPtr,credential._signedBy));
|
const SharedPtr<Peer> peer(RR->topology->peer(tPtr,credential._signedBy));
|
||||||
if (!peer)
|
if (!peer)
|
||||||
return Credential::VERIFY_NEED_IDENTITY;
|
return Credential::VERIFY_NEED_IDENTITY;
|
||||||
|
|
||||||
uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3];
|
// Now verify the controller's signature.
|
||||||
unsigned int ptr = 0;
|
uint64_t buf[ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX / 8];
|
||||||
for(unsigned int i=0;i<credential._qualifierCount;++i) {
|
const unsigned int bufSize = credential._fillSigningBuf(buf);
|
||||||
buf[ptr++] = Utils::hton(credential._qualifiers[i].id);
|
return peer->identity().verify(buf,bufSize,credential._signature,credential._signatureLength) ? Credential::VERIFY_OK : Credential::VERIFY_BAD_SIGNATURE;
|
||||||
buf[ptr++] = Utils::hton(credential._qualifiers[i].value);
|
|
||||||
buf[ptr++] = Utils::hton(credential._qualifiers[i].maxDelta);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (peer->identity().verify(buf,ptr * sizeof(uint64_t),credential._signature,credential._signatureLength) ? Credential::VERIFY_OK : Credential::VERIFY_BAD_SIGNATURE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Credential::VerifyResult Credential::_verify(const RuntimeEnvironment *RR,void *tPtr,const Capability &credential) const
|
Credential::VerifyResult Credential::_verify(const RuntimeEnvironment *RR,void *tPtr,const Capability &credential) const
|
||||||
|
|
|
@ -36,6 +36,11 @@ class RuntimeEnvironment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for credentials
|
* Base class for credentials
|
||||||
|
*
|
||||||
|
* Note that all credentials are and must be trivially copyable.
|
||||||
|
*
|
||||||
|
* All credential verification methods are implemented in Credential.cpp as they share a lot
|
||||||
|
* of common code and logic and grouping them makes auditing easier.
|
||||||
*/
|
*/
|
||||||
class Credential : public TriviallyCopyable
|
class Credential : public TriviallyCopyable
|
||||||
{
|
{
|
||||||
|
|
|
@ -145,7 +145,7 @@ public:
|
||||||
const unsigned int fragmentNo,
|
const unsigned int fragmentNo,
|
||||||
const unsigned int totalFragmentsExpected,
|
const unsigned int totalFragmentsExpected,
|
||||||
const int64_t now,
|
const int64_t now,
|
||||||
const SharedPtr< Path > &via,
|
const SharedPtr<Path> &via,
|
||||||
const unsigned int maxIncomingFragmentsPerPath)
|
const unsigned int maxIncomingFragmentsPerPath)
|
||||||
{
|
{
|
||||||
// Sanity checks for malformed fragments or invalid input parameters.
|
// Sanity checks for malformed fragments or invalid input parameters.
|
||||||
|
|
|
@ -33,6 +33,9 @@ namespace ZeroTier {
|
||||||
* This doesn't implement everything in std::vector, just what we need. It
|
* This doesn't implement everything in std::vector, just what we need. It
|
||||||
* also adds a few special things for use in ZT core code.
|
* also adds a few special things for use in ZT core code.
|
||||||
*
|
*
|
||||||
|
* Note that an FCV will be TriviallyCopyable IF and only if its contained
|
||||||
|
* type is TriviallyCopyable. There's a const static checker for this.
|
||||||
|
*
|
||||||
* @tparam T Type to contain
|
* @tparam T Type to contain
|
||||||
* @tparam C Maximum capacity of vector
|
* @tparam C Maximum capacity of vector
|
||||||
*/
|
*/
|
||||||
|
@ -43,6 +46,11 @@ public:
|
||||||
typedef T * iterator;
|
typedef T * iterator;
|
||||||
typedef const T * const_iterator;
|
typedef const T * const_iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return True if this FCV is trivially copyable, which means its type is also.
|
||||||
|
*/
|
||||||
|
static constexpr bool isTriviallyCopyable() noexcept { return isTriviallyCopyable(reinterpret_cast<const T *>(0)); }
|
||||||
|
|
||||||
ZT_INLINE FCV() noexcept : _s(0) {}
|
ZT_INLINE FCV() noexcept : _s(0) {}
|
||||||
ZT_INLINE FCV(const FCV &v) : _s(0) { *this = v; }
|
ZT_INLINE FCV(const FCV &v) : _s(0) { *this = v; }
|
||||||
|
|
||||||
|
|
|
@ -45,19 +45,14 @@ public:
|
||||||
|
|
||||||
ZT_INLINE Address address() const noexcept { return Address(_fp.address); }
|
ZT_INLINE Address address() const noexcept { return Address(_fp.address); }
|
||||||
ZT_INLINE const uint8_t *hash() const noexcept { return _fp.hash; }
|
ZT_INLINE const uint8_t *hash() const noexcept { return _fp.hash; }
|
||||||
|
ZT_INLINE ZT_Fingerprint *apiFingerprint() noexcept { return &_fp; }
|
||||||
/**
|
|
||||||
* Copy into ZT_Fingerprint struct as used in API and trace messages
|
|
||||||
*
|
|
||||||
* @param fp ZT_Fingerprint
|
|
||||||
*/
|
|
||||||
ZT_INLINE void getAPIFingerprint(ZT_Fingerprint *fp) const noexcept { memcpy(fp,&_fp,sizeof(ZT_Fingerprint)); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Pointer to ZT_Fingerprint for API use
|
|
||||||
*/
|
|
||||||
ZT_INLINE const ZT_Fingerprint *apiFingerprint() const noexcept { return &_fp; }
|
ZT_INLINE const ZT_Fingerprint *apiFingerprint() const noexcept { return &_fp; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return True if hash is not all zero (missing/unspecified)
|
||||||
|
*/
|
||||||
|
ZT_INLINE bool haveHash() const noexcept { return (!Utils::allZero(_fp.hash,sizeof(_fp.hash))); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a base32-encoded representation of this fingerprint
|
* Get a base32-encoded representation of this fingerprint
|
||||||
*
|
*
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
#include "Address.hpp"
|
#include "Address.hpp"
|
||||||
#include "Identity.hpp"
|
#include "Identity.hpp"
|
||||||
|
|
||||||
|
// TODO: mlock
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Core ZeroTier protocol packet formats ------------------------------------------------------------------------------
|
* Core ZeroTier protocol packet formats ------------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
|
|
|
@ -1039,6 +1039,24 @@ extern "C" const char *ZTT_benchmarkCrypto()
|
||||||
ZT_T_PRINTF("%.4f MiB/sec" ZT_EOL_S,((16384.0 * 350000.0) / 1048576.0) / ((double)(end - start) / 1000.0));
|
ZT_T_PRINTF("%.4f MiB/sec" ZT_EOL_S,((16384.0 * 350000.0) / 1048576.0) / ((double)(end - start) / 1000.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ZT_T_PRINTF("[crypto] Benchmarking AES-GMAC-SIV... ");
|
||||||
|
AES k0(AES_CTR_TEST_VECTOR_0_KEY);
|
||||||
|
AES k1(AES_GMAC_VECTOR_0_KEY);
|
||||||
|
AES::GMACSIVEncryptor enc(k0,k1);
|
||||||
|
int64_t start = now();
|
||||||
|
for(long i=0;i<350000;++i) {
|
||||||
|
enc.init((uint64_t)i,tmp);
|
||||||
|
enc.update1(tmp,sizeof(tmp));
|
||||||
|
enc.finish1();
|
||||||
|
enc.update2(tmp,sizeof(tmp));
|
||||||
|
enc.finish2();
|
||||||
|
}
|
||||||
|
int64_t end = now();
|
||||||
|
foo = tmp[0]; // prevent optimization
|
||||||
|
ZT_T_PRINTF("%.4f MiB/sec" ZT_EOL_S,((16384.0 * 350000.0) / 1048576.0) / ((double)(end - start) / 1000.0));
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
ZT_T_PRINTF("[crypto] Benchmarking Poly1305... ");
|
ZT_T_PRINTF("[crypto] Benchmarking Poly1305... ");
|
||||||
int64_t start = now();
|
int64_t start = now();
|
||||||
|
|
|
@ -166,6 +166,12 @@ ZT_PACKED_STRUCT(struct TriviallyCopyable
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
static constexpr bool isTriviallyCopyable(const TriviallyCopyable *const anything) noexcept { return true; }
|
||||||
|
static constexpr bool isTriviallyCopyable(const void *const anything) noexcept { return false; }
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static constexpr bool isTriviallyCopyable(const T &anything) noexcept { return isTriviallyCopyable(&anything); }
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -16,16 +16,27 @@
|
||||||
|
|
||||||
#include "Constants.hpp"
|
#include "Constants.hpp"
|
||||||
|
|
||||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
|
||||||
#define ZT_CONST_TO_BE_UINT16(x) ((uint16_t)((uint16_t)((uint16_t)(x) << 8U) | (uint16_t)((uint16_t)(x) >> 8U)))
|
|
||||||
#else
|
|
||||||
#define ZT_CONST_TO_BE_UINT16(x) ((uint16_t)(x))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
|
|
||||||
|
// Macros to convert endian-ness at compile time for constants.
|
||||||
|
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||||
|
#define ZT_CONST_TO_BE_UINT16(x) ((uint16_t)((uint16_t)((uint16_t)(x) << 8U) | (uint16_t)((uint16_t)(x) >> 8U)))
|
||||||
|
#define ZT_CONST_TO_BE_UINT64(x) ( \
|
||||||
|
(((uint64_t)(x) & 0x00000000000000ffULL) << 56U) | \
|
||||||
|
(((uint64_t)(x) & 0x000000000000ff00ULL) << 40U) | \
|
||||||
|
(((uint64_t)(x) & 0x0000000000ff0000ULL) << 24U) | \
|
||||||
|
(((uint64_t)(x) & 0x00000000ff000000ULL) << 8U) | \
|
||||||
|
(((uint64_t)(x) & 0x000000ff00000000ULL) >> 8U) | \
|
||||||
|
(((uint64_t)(x) & 0x0000ff0000000000ULL) >> 24U) | \
|
||||||
|
(((uint64_t)(x) & 0x00ff000000000000ULL) >> 40U) | \
|
||||||
|
(((uint64_t)(x) & 0xff00000000000000ULL) >> 56U))
|
||||||
|
#else
|
||||||
|
#define ZT_CONST_TO_BE_UINT16(x) ((uint16_t)(x))
|
||||||
|
#define ZT_CONST_TO_BE_UINT64(x) ((uint64_t)(x))
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef ZT_ARCH_X64
|
#ifdef ZT_ARCH_X64
|
||||||
struct CPUIDRegisters
|
struct CPUIDRegisters
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Reference in a new issue