Add and send certificates of representation to tell people what our valid upstreams are. These are not used yet but will be needed for future privacy modes, etc. Also some cleanup.

This commit is contained in:
Adam Ierymenko 2017-02-04 13:17:00 -08:00
parent beb642faa5
commit 3587aa1ea7
11 changed files with 247 additions and 35 deletions

View File

@ -414,7 +414,14 @@ public:
throw std::runtime_error("unterminated custody chain");
_custody[i].to = to;
_custody[i].from.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
memcpy(_custody[i].signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
if (b[p++] == 1) {
if (b.template at<uint16_t>(p) != ZT_C25519_SIGNATURE_LEN)
throw std::runtime_error("invalid signature");
p += 2;
memcpy(_custody[i].signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
} else {
p += 2 + b.template at<uint16_t>(p);
}
}
p += 2 + b.template at<uint16_t>(p);

View File

@ -0,0 +1,161 @@
/*
* ZeroTier One - Network Virtualization Everywhere
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ZT_CERTIFICATEOFREPRESENTATION_HPP
#define ZT_CERTIFICATEOFREPRESENTATION_HPP
#include "Constants.hpp"
#include "Address.hpp"
#include "C25519.hpp"
#include "Identity.hpp"
#include "Buffer.hpp"
/**
* Maximum number of addresses allowed in a COR
*/
#define ZT_CERTIFICATEOFREPRESENTATION_MAX_ADDRESSES ZT_MAX_UPSTREAMS
namespace ZeroTier {
class CertificateOfRepresentation
{
public:
CertificateOfRepresentation()
{
memset(this,0,sizeof(CertificateOfRepresentation));
}
inline uint64_t timestamp() const { return _timestamp; }
inline const Address &representative(const unsigned int i) const { return _reps[i]; }
inline unsigned int repCount() const { return _repCount; }
inline void clear()
{
memset(this,0,sizeof(CertificateOfRepresentation));
}
/**
* Add a representative if space remains
*
* @param r Representative to add
* @return True if representative was added
*/
inline bool addRepresentative(const Address &r)
{
if (_repCount < ZT_CERTIFICATEOFREPRESENTATION_MAX_ADDRESSES) {
_reps[_repCount++] = r;
return true;
}
return false;
}
/**
* Sign this COR with my identity
*
* @param myIdentity This node's identity
* @param ts COR timestamp for establishing new vs. old
*/
inline void sign(const Identity &myIdentity,const uint64_t ts)
{
_timestamp = ts;
Buffer<sizeof(CertificateOfRepresentation) + 32> tmp;
this->serialize(tmp,true);
_signature = myIdentity.sign(tmp.data(),tmp.size());
}
/**
* Verify this COR's signature
*
* @param senderIdentity Identity of sender of COR
* @return True if COR is valid
*/
inline bool verify(const Identity &senderIdentity)
{
try {
Buffer<sizeof(CertificateOfRepresentation) + 32> tmp;
this->serialize(tmp,true);
return senderIdentity.verify(tmp.data(),tmp.size(),_signature.data,ZT_C25519_SIGNATURE_LEN);
} catch ( ... ) {
return false;
}
}
template<unsigned int C>
inline void serialize(Buffer<C> &b,const bool forSign = false) const
{
if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
b.append((uint64_t)_timestamp);
b.append((uint16_t)_repCount);
for(unsigned int i=0;i<_repCount;++i)
_reps[i].appendTo(b);
if (!forSign) {
b.append((uint8_t)1); // 1 == Ed25519 signature
b.append((uint16_t)ZT_C25519_SIGNATURE_LEN);
b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
}
b.append((uint16_t)0); // size of any additional fields, currently 0
if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
}
template<unsigned int C>
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
{
clear();
unsigned int p = startAt;
_timestamp = b.template at<uint64_t>(p); p += 8;
const unsigned int rc = b.template at<uint16_t>(p); p += 2;
for(unsigned int i=0;i<rc;++i) {
if (i < ZT_CERTIFICATEOFREPRESENTATION_MAX_ADDRESSES)
_reps[i].setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
p += ZT_ADDRESS_LENGTH;
}
_repCount = (rc > ZT_CERTIFICATEOFREPRESENTATION_MAX_ADDRESSES) ? ZT_CERTIFICATEOFREPRESENTATION_MAX_ADDRESSES : rc;
if (b[p++] == 1) {
if (b.template at<uint16_t>(p) == ZT_C25519_SIGNATURE_LEN) {
p += 2;
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
p += ZT_C25519_SIGNATURE_LEN;
} else throw std::runtime_error("invalid signature");
} else {
p += 2 + b.template at<uint16_t>(p);
}
p += 2 + b.template at<uint16_t>(p);
if (p > b.size())
throw std::runtime_error("extended field overflow");
return (p - startAt);
}
private:
uint64_t _timestamp;
Address _reps[ZT_CERTIFICATEOFREPRESENTATION_MAX_ADDRESSES];
unsigned int _repCount;
C25519::Signature _signature;
};
} // namespace ZeroTier
#endif

View File

@ -226,6 +226,11 @@
*/
#define ZT_RELAY_MAX_HOPS 3
/**
* Maximum number of upstreams to use (far more than we should ever need)
*/
#define ZT_MAX_UPSTREAMS 64
/**
* Expire time for multicast 'likes' and indirect multicast memberships in ms
*/

View File

@ -37,6 +37,7 @@
#include "Cluster.hpp"
#include "Node.hpp"
#include "CertificateOfMembership.hpp"
#include "CertificateOfRepresentation.hpp"
#include "Capability.hpp"
#include "Tag.hpp"
#include "Revocation.hpp"
@ -445,6 +446,14 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
}
}
// Handle COR if present (older versions don't send this)
if ((ptr + 2) <= size()) {
//const unsigned int corSize = at<uint16_t>(ptr); ptr += 2;
ptr += 2;
CertificateOfRepresentation cor;
ptr += cor.deserialize(*this,ptr);
}
TRACE("%s(%s): OK(HELLO), version %u.%u.%u, latency %u, reported external address %s",source().toString().c_str(),_path->address().toString().c_str(),vMajor,vMinor,vRevision,latency,((externalSurfaceAddress) ? externalSurfaceAddress.toString().c_str() : "(none)"));
if (!hops())

View File

@ -531,7 +531,7 @@ public:
VERB_NOP = 0x00,
/**
* Announcement of a node's existence:
* Announcement of a node's existence and vitals:
* <[1] protocol version>
* <[1] software major version>
* <[1] software minor version>
@ -547,10 +547,12 @@ public:
* [<[8] 64-bit world ID of moon>]
* [<[8] 64-bit timestamp of moon>]
* [... additional moons ...]
* <[2] 16-bit length of certificate of representation>
* [... certificate of representation ...]
*
* Important security note: this message is sent in the clear as it
* contains the initial identity for key agreement. It can therefore
* contain no secrets or sensitive information.
* HELLO is sent in the clear, and therefore cannot contain anything
* secret or highly confidential. It should contain nothing that is
* not either public or easy to obtain via other means.
*
* The destination address is the wire address to which this packet is
* being sent, and in OK is *also* the destination address of the OK
@ -1059,27 +1061,7 @@ public:
* ZeroTier, Inc. itself. We recommend making up random ones for your own
* implementations.
*/
VERB_USER_MESSAGE = 0x14,
/**
* Announce that we can reach a particular address:
* <[1] protocol version>
* <[1] software major version>
* <[1] software minor version>
* <[2] software revision>
* <[...] binary serialized identity (see Identity)>
* <[1] 8-bit number of direct addresses where peer is reachable (if any)>
* [... serialized direct addresses ...]
*
* This message can be sent upstream to announce that we can reach a
* particular address. It can optionally report physical paths upstream
* to allow upstream peers to send RENDEZVOUS, but this may be omitted
* if it is not known or if endpoint address privacy is desired.
*
* The receiving peer should confirm this message by sending a message
* downstream and waiting for a reply.
*/
VERB_CAN_REACH = 0x15
VERB_USER_MESSAGE = 0x14
};
/**

View File

@ -38,6 +38,7 @@
namespace ZeroTier {
Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity) :
RR(renv),
_lastReceive(0),
_lastNontrivialReceive(0),
_lastTriedMemorizedPath(0),
@ -50,7 +51,6 @@ Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Ident
_lastComRequestSent(0),
_lastCredentialsReceived(0),
_lastTrustEstablishedPacketReceived(0),
RR(renv),
_remoteClusterOptimal4(0),
_vProto(0),
_vMajor(0),
@ -365,6 +365,11 @@ void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,u
outp.append((uint64_t)m->timestamp());
}
const unsigned int corSizeAt = outp.size();
outp.addSize(2);
RR->topology->appendCertificateOfRepresentation(outp);
outp.setAt(corSizeAt,(uint16_t)((outp.size() - corSizeAt) - 2));
RR->node->expectReplyTo(outp.packetId());
if (atAddress) {

View File

@ -439,7 +439,9 @@ private:
}
uint8_t _key[ZT_PEER_SECRET_KEY_LENGTH];
uint8_t _remoteClusterOptimal6[16];
const RuntimeEnvironment *RR;
uint64_t _lastReceive; // direct or indirect
uint64_t _lastNontrivialReceive; // frames, things like netconf, etc.
uint64_t _lastTriedMemorizedPath;
@ -452,13 +454,17 @@ private:
uint64_t _lastComRequestSent;
uint64_t _lastCredentialsReceived;
uint64_t _lastTrustEstablishedPacketReceived;
const RuntimeEnvironment *RR;
uint8_t _remoteClusterOptimal6[16];
uint32_t _remoteClusterOptimal4;
uint16_t _vProto;
uint16_t _vMajor;
uint16_t _vMinor;
uint16_t _vRevision;
Identity _id;
struct {
uint64_t lastReceive;
SharedPtr<Path> path;
@ -467,6 +473,7 @@ private:
#endif
} _paths[ZT_MAX_PEER_NETWORK_PATHS];
Mutex _paths_m;
unsigned int _numPaths;
unsigned int _latency;
unsigned int _directPathPushCutoffCount;

View File

@ -152,6 +152,8 @@ public:
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
p += ZT_C25519_SIGNATURE_LEN;
} else throw std::runtime_error("invalid signature");
} else {
p += 2 + b.template at<uint16_t>(p);
}
p += 2 + b.template at<uint16_t>(p);

View File

@ -148,12 +148,14 @@ public:
_issuedTo.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
_signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
if (b[p++] != 1)
throw std::runtime_error("unrecognized signature type");
if (b.template at<uint16_t>(p) != ZT_C25519_SIGNATURE_LEN)
throw std::runtime_error("invalid signature length");
p += 2;
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
if (b[p++] == 1) {
if (b.template at<uint16_t>(p) != ZT_C25519_SIGNATURE_LEN)
throw std::runtime_error("invalid signature length");
p += 2;
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
} else {
p += 2 + b.template at<uint16_t>(p);
}
p += 2 + b.template at<uint16_t>(p);
if (p > b.size())

View File

@ -417,6 +417,7 @@ void Topology::_memoizeUpstreams()
// assumes _upstreams_m and _peers_m are locked
_upstreamAddresses.clear();
_amRoot = false;
for(std::vector<World::Root>::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) {
if (i->identity == RR->identity) {
_amRoot = true;
@ -429,6 +430,7 @@ void Topology::_memoizeUpstreams()
}
}
}
for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
for(std::vector<World::Root>::const_iterator i(m->roots().begin());i!=m->roots().end();++i) {
if (i->identity == RR->identity) {
@ -443,6 +445,15 @@ void Topology::_memoizeUpstreams()
}
}
}
std::sort(_upstreamAddresses.begin(),_upstreamAddresses.end());
_cor.clear();
for(std::vector<Address>::const_iterator a(_upstreamAddresses.begin());a!=_upstreamAddresses.end();++a) {
if (!_cor.addRepresentative(*a))
break;
}
_cor.sign(RR->identity,RR->node->now());
}
} // namespace ZeroTier

View File

@ -38,6 +38,7 @@
#include "InetAddress.hpp"
#include "Hashtable.hpp"
#include "World.hpp"
#include "CertificateOfRepresentation.hpp"
namespace ZeroTier {
@ -383,6 +384,25 @@ public:
_trustedPathCount = count;
}
/**
* @return Current certificate of representation (copy)
*/
inline CertificateOfRepresentation certificateOfRepresentation() const
{
Mutex::Lock _l(_upstreams_m);
return _cor;
}
/**
* @param buf Buffer to receive COR
*/
template<unsigned int C>
void appendCertificateOfRepresentation(Buffer<C> &buf)
{
Mutex::Lock _l(_upstreams_m);
_cor.serialize(buf);
}
private:
Identity _getIdentity(const Address &zta);
void _memoizeUpstreams();
@ -404,6 +424,7 @@ private:
std::vector<World> _moons;
std::vector<Address> _contactingMoons;
std::vector<Address> _upstreamAddresses;
CertificateOfRepresentation _cor;
bool _amRoot;
Mutex _upstreams_m; // locks worlds, upstream info, moon info, etc.
};