summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--node/Capability.hpp9
-rw-r--r--node/CertificateOfRepresentation.hpp161
-rw-r--r--node/Constants.hpp5
-rw-r--r--node/IncomingPacket.cpp9
-rw-r--r--node/Packet.hpp32
-rw-r--r--node/Peer.cpp7
-rw-r--r--node/Peer.hpp11
-rw-r--r--node/Revocation.hpp2
-rw-r--r--node/Tag.hpp14
-rw-r--r--node/Topology.cpp11
-rw-r--r--node/Topology.hpp21
11 files changed, 247 insertions, 35 deletions
diff --git a/node/Capability.hpp b/node/Capability.hpp
index 2c829ee5..ddbfd9ee 100644
--- a/node/Capability.hpp
+++ b/node/Capability.hpp
@@ -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);
diff --git a/node/CertificateOfRepresentation.hpp b/node/CertificateOfRepresentation.hpp
new file mode 100644
index 00000000..7c239a96
--- /dev/null
+++ b/node/CertificateOfRepresentation.hpp
@@ -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
diff --git a/node/Constants.hpp b/node/Constants.hpp
index ab6dfb32..be4eb475 100644
--- a/node/Constants.hpp
+++ b/node/Constants.hpp
@@ -227,6 +227,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
*/
#define ZT_MULTICAST_LIKE_EXPIRE 600000
diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp
index cecbe2fa..6b38c4ec 100644
--- a/node/IncomingPacket.cpp
+++ b/node/IncomingPacket.cpp
@@ -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())
diff --git a/node/Packet.hpp b/node/Packet.hpp
index a5831c8d..7d404b25 100644
--- a/node/Packet.hpp
+++ b/node/Packet.hpp
@@ -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
};
/**
diff --git a/node/Peer.cpp b/node/Peer.cpp
index 129c2437..bb6b945d 100644
--- a/node/Peer.cpp
+++ b/node/Peer.cpp
@@ -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) {
diff --git a/node/Peer.hpp b/node/Peer.hpp
index bbe13a2e..e79739a3 100644
--- a/node/Peer.hpp
+++ b/node/Peer.hpp
@@ -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;
diff --git a/node/Revocation.hpp b/node/Revocation.hpp
index 18916985..bc290e75 100644
--- a/node/Revocation.hpp
+++ b/node/Revocation.hpp
@@ -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);
diff --git a/node/Tag.hpp b/node/Tag.hpp
index 97228157..65348200 100644
--- a/node/Tag.hpp
+++ b/node/Tag.hpp
@@ -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())
diff --git a/node/Topology.cpp b/node/Topology.cpp
index 0cd3db9e..f19d8656 100644
--- a/node/Topology.cpp
+++ b/node/Topology.cpp
@@ -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
diff --git a/node/Topology.hpp b/node/Topology.hpp
index 78dc0fe8..dca35789 100644
--- a/node/Topology.hpp
+++ b/node/Topology.hpp
@@ -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.
};