mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-01-18 02:40:13 +00:00
Docs, code cleanup, and protect the extra new fields of HELLO with encryption as a precaution.
This commit is contained in:
parent
594cb1fad8
commit
43182f8f57
@ -72,13 +72,11 @@ This manual describes the design and operation of ZeroTier and its associated se
|
||||
|
||||
ZeroTier is a smart Ethernet switch for planet Earth.
|
||||
|
||||
We've re-thought networking from first principles to deliver the flat end-to-end simplicity of the original pre-NAT pre-mobility Internet in a way that meets the security and mobility requirements of the 21st century. ZeroTier transforms the world into a unified modern data center where VPN, SDN, SD-WAN, and application peer to peer networking converge and where the distinction between the cloud and the endpoint largely disappears. All the complexity of managing these networking aspects as disparate systems is replaced by the simplicity of a single virtual cloud.
|
||||
We've re-thought networking from first principles to deliver the flat end-to-end simplicity of the original pre-NAT pre-mobility Internet, yet in a way that meets the security and mobility needs of the 21st century. ZeroTier erases the distinction between cloud and endpoint, bringing modern data center SDN to every device regardless of its physical location. The objectives of VPN, SDN, SD-WAN, and application peer to peer networking can all be achieved together and with reduced complexity.
|
||||
|
||||
At first some users struggle with this paradigm, finding it difficult to forget the fragmentation and complexity that has accreted around networking over the past decade or two. We urge skeptical users to just try it and see how many networking acronyms vanish before their eyes.
|
||||
Unlike most networking products it won't take you hours, days, or weeks to test or deploy ZeroTier. Most of the time everything just works with zero configuration, and most users with some level of TCP/IP knowledge can get up and running in minutes. More advanced features like federation, rules, micro-segmentation, capability based security credentials, network monitoring, and clustering are available but you don't need to worry about them until they're needed.
|
||||
|
||||
Unlike most networking products it won't take you hours, days, or weeks to test or deploy ZeroTier. Most of the time everything just works with zero configuration, and most users with some level of TCP/IP knowledge can get up and running in minutes. More advanced features like rules, micro-segmentation, capability based security credentials, network monitoring, and clustering are available but you don't need to worry about them until they're needed.
|
||||
|
||||
The first section (2) of this guide explains ZeroTier's design and operation in detail and is written for users with at least an intermediate knowledge of topics like TCP/IP and Ethernet networking. Reading and understanding everything in it is not mandatory but we've written it as a deep technical dive as serious IT users typically like to understand the systems they deploy and use. Sections 3 and 4 deal more concretely with the ZeroTier One endpoint service software and how to deploy for common use cases.
|
||||
The first section (2) of this guide explains ZeroTier's design and operation in detail and is written for users with at least an intermediate knowledge of topics like TCP/IP and Ethernet networking. All of it is not required reading for most users, but we've created a deep technical dive to satisfy the desire of IT professionals to understand the systems that they use. Sections 3 and 4 deal more concretely with the ZeroTier One endpoint service software and how to deploy for common use cases.
|
||||
|
||||
## **2.** How it Works <a name="2"></a>
|
||||
|
||||
@ -88,7 +86,7 @@ ZeroTier is comprised of two closely coupled but conceptually distinct layers [i
|
||||
|
||||
To build a planetary data center we first had to begin with the wiring. Tunneling into the Earth's core and putting a giant wire closet down there wasn't an option, so we decided to use software to build virtual wires over the existing Internet instead.
|
||||
|
||||
In conventional networks L1 (OSI layer 1) refers to the actual CAT5/CAT6 cables or wireless radio channels over which data is carried and the physical transciever chips that modulate and demodulate it. VL1 is a peer to peer network that does the same thing by using encryption, authentication, and a lot of networking tricks to create virtual wires as needed.
|
||||
In conventional networks L1 (OSI layer 1) refers to the actual CAT5/CAT6 cables or wireless radio channels over which data is carried and the physical transciever chips that modulate and demodulate it. VL1 is a peer to peer network that does the same thing by using encryption, authentication, and a lot of networking tricks to create virtual wires on a dyniamic as-needed basis.
|
||||
|
||||
### **2.1.1.** Network Topology and Peer Discovery
|
||||
|
||||
@ -98,7 +96,7 @@ Roots run the same software as regular endpoints but reside at fast stable locat
|
||||
|
||||
There is only one planet. Earth's root servers are operated by ZeroTier, Inc. as a free service. Their presence defines and unifies the global data center where we all reside.
|
||||
|
||||
Users can create "moons." These nominate additional roots for redundancy or performance. The most common reasons for doing this are to eliminate hard dependency on ZeroTier's third party infrastructure or to designate local roots inside your building or cloud so ZeroTier can work without a connection to the Internet. Moons are by no means required and most of our users get by just fine without them.
|
||||
Moons can be created by users. These nominate additional roots for redundancy or performance. The most common reasons for doing this are to eliminate hard dependency on ZeroTier's third party infrastructure or to designate local roots inside your building or cloud so ZeroTier can work without a connection to the Internet. Moons are by no means required and most of our users get by just fine without them.
|
||||
|
||||
When peers start out they have no direct links to one another, only upstream to roots. Every peer on VL1 possesses a globally unique address, but unlike IP addresses these are opaque cryptographic identifiers that encode no routing information. To communicate peers first send packets "up" the tree, and as these packets traverse the network they trigger the opportunistic creation of direct links along the way. The tree is constantly trying to "collapse itself" to optimize itself to the pattern of traffic it is carrying.
|
||||
|
||||
|
@ -293,6 +293,11 @@
|
||||
*/
|
||||
#define ZT_PEER_PATH_EXPIRATION ((ZT_PEER_PING_PERIOD * 4) + 3000)
|
||||
|
||||
/**
|
||||
* Send a full HELLO every this often (ms)
|
||||
*/
|
||||
#define ZT_PEER_SEND_FULL_HELLO_EVERY (ZT_PEER_PING_PERIOD * 2)
|
||||
|
||||
/**
|
||||
* How often to retry expired paths that we're still remembering
|
||||
*/
|
||||
|
@ -46,7 +46,7 @@ static inline void _computeMemoryHardHash(const void *publicKey,unsigned int pub
|
||||
// but is not what we want for sequential memory-harndess.
|
||||
memset(genmem,0,ZT_IDENTITY_GEN_MEMORY);
|
||||
Salsa20 s20(digest,256,(char *)digest + 32);
|
||||
s20.encrypt20((char *)genmem,(char *)genmem,64);
|
||||
s20.crypt20((char *)genmem,(char *)genmem,64);
|
||||
for(unsigned long i=64;i<ZT_IDENTITY_GEN_MEMORY;i+=64) {
|
||||
unsigned long k = i - 64;
|
||||
*((uint64_t *)((char *)genmem + i)) = *((uint64_t *)((char *)genmem + k));
|
||||
@ -57,7 +57,7 @@ static inline void _computeMemoryHardHash(const void *publicKey,unsigned int pub
|
||||
*((uint64_t *)((char *)genmem + i + 40)) = *((uint64_t *)((char *)genmem + k + 40));
|
||||
*((uint64_t *)((char *)genmem + i + 48)) = *((uint64_t *)((char *)genmem + k + 48));
|
||||
*((uint64_t *)((char *)genmem + i + 56)) = *((uint64_t *)((char *)genmem + k + 56));
|
||||
s20.encrypt20((char *)genmem + i,(char *)genmem + i,64);
|
||||
s20.crypt20((char *)genmem + i,(char *)genmem + i,64);
|
||||
}
|
||||
|
||||
// Render final digest using genmem as a lookup table
|
||||
@ -67,7 +67,7 @@ static inline void _computeMemoryHardHash(const void *publicKey,unsigned int pub
|
||||
uint64_t tmp = ((uint64_t *)genmem)[idx2];
|
||||
((uint64_t *)genmem)[idx2] = ((uint64_t *)digest)[idx1];
|
||||
((uint64_t *)digest)[idx1] = tmp;
|
||||
s20.encrypt20(digest,digest,64);
|
||||
s20.crypt20(digest,digest,64);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,44 +213,19 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,const bool alreadyAut
|
||||
const unsigned int vRevision = at<uint16_t>(ZT_PROTO_VERB_HELLO_IDX_REVISION);
|
||||
const uint64_t timestamp = at<uint64_t>(ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP);
|
||||
|
||||
Identity id;
|
||||
InetAddress externalSurfaceAddress;
|
||||
uint64_t planetWorldId = 0;
|
||||
uint64_t planetWorldTimestamp = 0;
|
||||
std::vector< std::pair<uint64_t,uint64_t> > moonIdsAndTimestamps;
|
||||
{
|
||||
unsigned int ptr = ZT_PROTO_VERB_HELLO_IDX_IDENTITY + id.deserialize(*this,ZT_PROTO_VERB_HELLO_IDX_IDENTITY);
|
||||
|
||||
// Get external surface address if present (was not in old versions)
|
||||
if (ptr < size())
|
||||
ptr += externalSurfaceAddress.deserialize(*this,ptr);
|
||||
|
||||
// Get primary planet world ID and world timestamp if present
|
||||
if ((ptr + 16) <= size()) {
|
||||
planetWorldId = at<uint64_t>(ptr); ptr += 8;
|
||||
planetWorldTimestamp = at<uint64_t>(ptr);
|
||||
}
|
||||
|
||||
// Get moon IDs and timestamps if present
|
||||
if ((ptr + 2) <= size()) {
|
||||
unsigned int numMoons = at<uint16_t>(ptr); ptr += 2;
|
||||
for(unsigned int i=0;i<numMoons;++i) {
|
||||
if ((World::Type)(*this)[ptr++] == World::TYPE_MOON)
|
||||
moonIdsAndTimestamps.push_back(std::pair<uint64_t,uint64_t>(at<uint64_t>(ptr),at<uint64_t>(ptr + 8)));
|
||||
ptr += 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fromAddress != id.address()) {
|
||||
TRACE("dropped HELLO from %s(%s): identity not for sending address",fromAddress.toString().c_str(),_path->address().toString().c_str());
|
||||
return true;
|
||||
}
|
||||
if (protoVersion < ZT_PROTO_VERSION_MIN) {
|
||||
TRACE("dropped HELLO from %s(%s): protocol version too old",id.address().toString().c_str(),_path->address().toString().c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
Identity id;
|
||||
unsigned int ptr = ZT_PROTO_VERB_HELLO_IDX_IDENTITY + id.deserialize(*this,ZT_PROTO_VERB_HELLO_IDX_IDENTITY);
|
||||
|
||||
if (fromAddress != id.address()) {
|
||||
TRACE("dropped HELLO from %s(%s): identity does not match packet source address",fromAddress.toString().c_str(),_path->address().toString().c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
SharedPtr<Peer> peer(RR->topology->getPeer(id.address()));
|
||||
if (peer) {
|
||||
// We already have an identity with this address -- check for collisions
|
||||
@ -324,6 +299,43 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,const bool alreadyAut
|
||||
|
||||
// VALID -- if we made it here, packet passed identity and authenticity checks!
|
||||
|
||||
// Get external surface address if present (was not in old versions)
|
||||
InetAddress externalSurfaceAddress;
|
||||
if (ptr < size())
|
||||
ptr += externalSurfaceAddress.deserialize(*this,ptr);
|
||||
|
||||
// Get primary planet world ID and world timestamp if present
|
||||
uint64_t planetWorldId = 0;
|
||||
uint64_t planetWorldTimestamp = 0;
|
||||
if ((ptr + 16) <= size()) {
|
||||
planetWorldId = at<uint64_t>(ptr); ptr += 8;
|
||||
planetWorldTimestamp = at<uint64_t>(ptr);
|
||||
}
|
||||
|
||||
std::vector< std::pair<uint64_t,uint64_t> > moonIdsAndTimestamps;
|
||||
if (ptr < size()) {
|
||||
// Remainder of packet, if present, is encrypted
|
||||
cryptField(peer->key(),ptr,size() - ptr);
|
||||
|
||||
// Get moon IDs and timestamps if present
|
||||
if ((ptr + 2) <= size()) {
|
||||
unsigned int numMoons = at<uint16_t>(ptr); ptr += 2;
|
||||
for(unsigned int i=0;i<numMoons;++i) {
|
||||
if ((World::Type)(*this)[ptr++] == World::TYPE_MOON)
|
||||
moonIdsAndTimestamps.push_back(std::pair<uint64_t,uint64_t>(at<uint64_t>(ptr),at<uint64_t>(ptr + 8)));
|
||||
ptr += 16;
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
// Learn our external surface address from other peers to help us negotiate symmetric NATs
|
||||
// and detect changes to our global IP that can trigger path renegotiation.
|
||||
if ((externalSurfaceAddress)&&(hops() == 0))
|
||||
@ -337,6 +349,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,const bool alreadyAut
|
||||
outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
|
||||
outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
|
||||
outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
|
||||
|
||||
if (protoVersion >= 5) {
|
||||
_path->address().serialize(outp);
|
||||
} else {
|
||||
@ -387,6 +400,11 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,const bool alreadyAut
|
||||
}
|
||||
outp.setAt<uint16_t>(worldUpdateSizeAt,(uint16_t)(outp.size() - (worldUpdateSizeAt + 2)));
|
||||
|
||||
const unsigned int corSizeAt = outp.size();
|
||||
outp.addSize(2);
|
||||
RR->topology->appendCertificateOfRepresentation(outp);
|
||||
outp.setAt(corSizeAt,(uint16_t)(outp.size() - (corSizeAt + 2)));
|
||||
|
||||
outp.armor(peer->key(),true);
|
||||
_path->send(RR,outp.data(),outp.size(),now);
|
||||
|
||||
@ -586,7 +604,7 @@ bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr<
|
||||
const InetAddress atAddr(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port);
|
||||
if (RR->node->shouldUsePathForZeroTierTraffic(with,_path->localAddress(),atAddr)) {
|
||||
RR->node->putPacket(_path->localAddress(),atAddr,"ABRE",4,2); // send low-TTL junk packet to 'open' local NAT(s) and stateful firewalls
|
||||
rendezvousWith->attemptToContactAt(_path->localAddress(),atAddr,RR->node->now());
|
||||
rendezvousWith->attemptToContactAt(_path->localAddress(),atAddr,RR->node->now(),false);
|
||||
TRACE("RENDEZVOUS from %s says %s might be at %s, sent verification attempt",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str());
|
||||
} else {
|
||||
TRACE("RENDEZVOUS from %s says %s might be at %s, ignoring since path is not suitable",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str());
|
||||
@ -1155,7 +1173,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha
|
||||
if ( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && (!redundant) && (RR->node->shouldUsePathForZeroTierTraffic(peer->address(),_path->localAddress(),a)) ) {
|
||||
if (++countPerScope[(int)a.ipScope()][0] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
|
||||
TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str());
|
||||
peer->attemptToContactAt(InetAddress(),a,now);
|
||||
peer->attemptToContactAt(InetAddress(),a,now,false);
|
||||
} else {
|
||||
TRACE("ignoring contact for %s at %s -- too many per scope",peer->address().toString().c_str(),a.toString().c_str());
|
||||
}
|
||||
@ -1174,7 +1192,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha
|
||||
if ( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && (!redundant) && (RR->node->shouldUsePathForZeroTierTraffic(peer->address(),_path->localAddress(),a)) ) {
|
||||
if (++countPerScope[(int)a.ipScope()][1] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
|
||||
TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str());
|
||||
peer->attemptToContactAt(InetAddress(),a,now);
|
||||
peer->attemptToContactAt(InetAddress(),a,now,false);
|
||||
} else {
|
||||
TRACE("ignoring contact for %s at %s -- too many per scope",peer->address().toString().c_str(),a.toString().c_str());
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ Node::Node(void *uptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now) :
|
||||
Utils::getSecureRandom(foo,32);
|
||||
_prng.init(foo,256,foo);
|
||||
memset(_prngStream,0,sizeof(_prngStream));
|
||||
_prng.encrypt12(_prngStream,_prngStream,sizeof(_prngStream));
|
||||
_prng.crypt12(_prngStream,_prngStream,sizeof(_prngStream));
|
||||
|
||||
std::string idtmp(dataStoreGet("identity.secret"));
|
||||
if ((!idtmp.length())||(!RR->identity.fromString(idtmp))||(!RR->identity.hasPrivate())) {
|
||||
@ -686,7 +686,7 @@ uint64_t Node::prng()
|
||||
{
|
||||
unsigned int p = (++_prngStreamPtr % ZT_NODE_PRNG_BUF_SIZE);
|
||||
if (!p)
|
||||
_prng.encrypt12(_prngStream,_prngStream,sizeof(_prngStream));
|
||||
_prng.crypt12(_prngStream,_prngStream,sizeof(_prngStream));
|
||||
return _prngStream[p];
|
||||
}
|
||||
|
||||
|
@ -18,9 +18,21 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "Packet.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define FORCE_INLINE static __forceinline
|
||||
#include <intrin.h>
|
||||
#pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
|
||||
#pragma warning(disable : 4293) /* disable: C4293: too large shift (32-bits) */
|
||||
#else
|
||||
#define FORCE_INLINE static inline
|
||||
#endif
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/************************************************************************** */
|
||||
@ -367,7 +379,7 @@ LZ4_decompress_*_continue() :
|
||||
#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */
|
||||
|
||||
#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
|
||||
#include <stdint.h>
|
||||
//#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
uint32_t hashTable[LZ4_HASH_SIZE_U32];
|
||||
@ -536,6 +548,7 @@ union LZ4_streamDecode_u {
|
||||
/*-************************************
|
||||
* Compiler Options
|
||||
**************************************/
|
||||
#if 0
|
||||
#ifdef _MSC_VER /* Visual Studio */
|
||||
# define FORCE_INLINE static __forceinline
|
||||
# include <intrin.h>
|
||||
@ -550,6 +563,7 @@ union LZ4_streamDecode_u {
|
||||
# define FORCE_INLINE static
|
||||
# endif
|
||||
#endif /* _MSC_VER */
|
||||
#endif
|
||||
|
||||
#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__)
|
||||
# define expect(expr,value) (__builtin_expect ((expr),(value)) )
|
||||
@ -564,38 +578,39 @@ union LZ4_streamDecode_u {
|
||||
/*-************************************
|
||||
* Memory routines
|
||||
**************************************/
|
||||
#include <stdlib.h> /* malloc, calloc, free */
|
||||
//#include <stdlib.h> /* malloc, calloc, free */
|
||||
#define ALLOCATOR(n,s) calloc(n,s)
|
||||
#define FREEMEM free
|
||||
#include <string.h> /* memset, memcpy */
|
||||
//#include <string.h> /* memset, memcpy */
|
||||
#define MEM_INIT memset
|
||||
|
||||
|
||||
/*-************************************
|
||||
* Basic Types
|
||||
**************************************/
|
||||
#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
|
||||
# include <stdint.h>
|
||||
//#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
|
||||
//# include <stdint.h>
|
||||
typedef uint8_t BYTE;
|
||||
typedef uint16_t U16;
|
||||
typedef uint32_t U32;
|
||||
typedef int32_t S32;
|
||||
typedef uint64_t U64;
|
||||
typedef uintptr_t uptrval;
|
||||
#else
|
||||
/*#else
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short U16;
|
||||
typedef unsigned int U32;
|
||||
typedef signed int S32;
|
||||
typedef unsigned long long U64;
|
||||
typedef size_t uptrval; /* generally true, except OpenVMS-64 */
|
||||
#endif
|
||||
typedef size_t uptrval;
|
||||
#endif */
|
||||
|
||||
#if defined(__x86_64__)
|
||||
typedef U64 reg_t; /* 64-bits in x32 mode */
|
||||
#else
|
||||
typedef size_t reg_t; /* 32-bits in x32 mode */
|
||||
#endif
|
||||
typedef uintptr_t reg_t;
|
||||
//#if defined(__x86_64__)
|
||||
// typedef U64 reg_t; /* 64-bits in x32 mode */
|
||||
//#else
|
||||
// typedef size_t reg_t; /* 32-bits in x32 mode */
|
||||
//#endif
|
||||
|
||||
/*-************************************
|
||||
* Reading and writing into memory
|
||||
@ -606,7 +621,6 @@ static unsigned LZ4_isLittleEndian(void)
|
||||
return one.c[0];
|
||||
}
|
||||
|
||||
|
||||
#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2)
|
||||
/* lie to the compiler about data alignment; use with caution */
|
||||
|
||||
@ -1975,10 +1989,10 @@ void Packet::armor(const void *key,bool encryptPayload)
|
||||
|
||||
// MAC key is always the first 32 bytes of the Salsa20 key stream
|
||||
// This is the same construction DJB's NaCl library uses
|
||||
s20.encrypt12(ZERO_KEY,macKey,sizeof(macKey));
|
||||
s20.crypt12(ZERO_KEY,macKey,sizeof(macKey));
|
||||
|
||||
if (encryptPayload)
|
||||
s20.encrypt12(payload,payload,payloadLen);
|
||||
s20.crypt12(payload,payload,payloadLen);
|
||||
|
||||
Poly1305::compute(mac,payload,payloadLen,macKey);
|
||||
memcpy(field(ZT_PACKET_IDX_MAC,8),mac,8);
|
||||
@ -1995,20 +2009,30 @@ bool Packet::dearmor(const void *key)
|
||||
|
||||
if ((cs == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)||(cs == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012)) {
|
||||
_salsa20MangleKey((const unsigned char *)key,mangledKey);
|
||||
Salsa20 s20(mangledKey,256,field(ZT_PACKET_IDX_IV,8)/*,ZT_PROTO_SALSA20_ROUNDS*/);
|
||||
Salsa20 s20(mangledKey,256,field(ZT_PACKET_IDX_IV,8));
|
||||
|
||||
s20.encrypt12(ZERO_KEY,macKey,sizeof(macKey));
|
||||
s20.crypt12(ZERO_KEY,macKey,sizeof(macKey));
|
||||
Poly1305::compute(mac,payload,payloadLen,macKey);
|
||||
if (!Utils::secureEq(mac,field(ZT_PACKET_IDX_MAC,8),8))
|
||||
return false;
|
||||
|
||||
if (cs == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012)
|
||||
s20.decrypt12(payload,payload,payloadLen);
|
||||
s20.crypt12(payload,payload,payloadLen);
|
||||
|
||||
return true;
|
||||
} else return false; // unrecognized cipher suite
|
||||
}
|
||||
|
||||
void Packet::cryptField(const void *key,unsigned int start,unsigned int len)
|
||||
{
|
||||
unsigned char mangledKey[32];
|
||||
uint64_t iv = Utils::hton((uint64_t)start ^ at<uint64_t>(ZT_PACKET_IDX_IV));
|
||||
_salsa20MangleKey((const unsigned char *)key,mangledKey);
|
||||
Salsa20 s20(mangledKey,256,&iv);
|
||||
unsigned char *const ptr = field(start,len);
|
||||
s20.crypt12(ptr,ptr,len);
|
||||
}
|
||||
|
||||
bool Packet::compress()
|
||||
{
|
||||
unsigned char buf[ZT_PROTO_MAX_PACKET_LENGTH * 2];
|
||||
|
@ -542,6 +542,7 @@ public:
|
||||
* [<[...] destination address to which packet was sent>]
|
||||
* <[8] 64-bit world ID of current planet>
|
||||
* <[8] 64-bit timestamp of current planet>
|
||||
* [... remainder if packet is encrypted using cryptField() ...]
|
||||
* <[2] 16-bit number of moons>
|
||||
* [<[1] 8-bit type ID of moon>]
|
||||
* [<[8] 64-bit world ID of moon>]
|
||||
@ -550,9 +551,10 @@ public:
|
||||
* <[2] 16-bit length of certificate of representation>
|
||||
* [... certificate of representation ...]
|
||||
*
|
||||
* 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 initial fields of HELLO are sent in the clear. Fields after the
|
||||
* planet definition (which are common knowledge) are however encrypted
|
||||
* using the cryptField() function. The packet is MAC'd as usual using
|
||||
* the same MAC construct as other packets.
|
||||
*
|
||||
* 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
|
||||
@ -566,7 +568,7 @@ public:
|
||||
* 0x04 - 6-byte IPv4 UDP address/port -- format: <[4] IP>, <[2] port>
|
||||
* 0x06 - 18-byte IPv6 UDP address/port -- format: <[16] IP>, <[2] port>
|
||||
*
|
||||
* OK payload:
|
||||
* OK payload (note that OK is encrypted):
|
||||
* <[8] timestamp (echoed from original HELLO)>
|
||||
* <[1] protocol version (of responder)>
|
||||
* <[1] software major version (of responder)>
|
||||
@ -576,6 +578,8 @@ public:
|
||||
* [<[...] destination address>]
|
||||
* <[2] 16-bit length of world update or 0 if none>
|
||||
* [[...] updates to planets and/or moons]
|
||||
* <[2] 16-bit length of certificate of representation (of responder)>
|
||||
* [... certificate of representation ...]
|
||||
*
|
||||
* ERROR has no payload.
|
||||
*/
|
||||
@ -1348,6 +1352,25 @@ public:
|
||||
*/
|
||||
bool dearmor(const void *key);
|
||||
|
||||
/**
|
||||
* Encrypt/decrypt a separately armored portion of a packet
|
||||
*
|
||||
* This keys using the same key in the same way as armor/dearmor, but
|
||||
* uses a different IV computed from the packet's IV plus the starting
|
||||
* point index.
|
||||
*
|
||||
* This currently uses Salsa20/12, but any message that uses this should
|
||||
* incorporate a cipher selector to permit this to be changed later.
|
||||
*
|
||||
* This is currently only used to mask portions of HELLO as an extra
|
||||
* security precation since most of that message is sent in the clear.
|
||||
*
|
||||
* @param key 32-byte key
|
||||
* @param start Start of encrypted portion
|
||||
* @param len Length of encrypted portion
|
||||
*/
|
||||
void cryptField(const void *key,unsigned int start,unsigned int len);
|
||||
|
||||
/**
|
||||
* Attempt to compress payload if not already (must be unencrypted)
|
||||
*
|
||||
|
@ -203,7 +203,7 @@ void Peer::received(
|
||||
#endif
|
||||
} else {
|
||||
TRACE("got %s via unknown path %s(%s), confirming...",Packet::verbString(verb),_id.address().toString().c_str(),path->address().toString().c_str());
|
||||
attemptToContactAt(path->localAddress(),path->address(),now);
|
||||
attemptToContactAt(path->localAddress(),path->address(),now,true);
|
||||
path->sent(now);
|
||||
}
|
||||
}
|
||||
@ -357,6 +357,8 @@ void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,u
|
||||
outp.append((uint64_t)RR->topology->planetWorldId());
|
||||
outp.append((uint64_t)RR->topology->planetWorldTimestamp());
|
||||
|
||||
const unsigned int startCryptedPortionAt = outp.size();
|
||||
|
||||
std::vector<World> moons(RR->topology->moons());
|
||||
outp.append((uint16_t)moons.size());
|
||||
for(std::vector<World>::const_iterator m(moons.begin());m!=moons.end();++m) {
|
||||
@ -368,21 +370,23 @@ void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,u
|
||||
const unsigned int corSizeAt = outp.size();
|
||||
outp.addSize(2);
|
||||
RR->topology->appendCertificateOfRepresentation(outp);
|
||||
outp.setAt(corSizeAt,(uint16_t)((outp.size() - corSizeAt) - 2));
|
||||
outp.setAt(corSizeAt,(uint16_t)(outp.size() - (corSizeAt + 2)));
|
||||
|
||||
outp.cryptField(_key,startCryptedPortionAt,outp.size() - startCryptedPortionAt);
|
||||
|
||||
RR->node->expectReplyTo(outp.packetId());
|
||||
|
||||
if (atAddress) {
|
||||
outp.armor(_key,false);
|
||||
outp.armor(_key,false); // false == don't encrypt full payload, but add MAC
|
||||
RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size());
|
||||
} else {
|
||||
RR->sw->send(outp,false);
|
||||
RR->sw->send(outp,false); // false == don't encrypt full payload, but add MAC
|
||||
}
|
||||
}
|
||||
|
||||
void Peer::attemptToContactAt(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now)
|
||||
void Peer::attemptToContactAt(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,bool sendFullHello)
|
||||
{
|
||||
if ( (_vProto >= 5) && ( !((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0)) ) ) {
|
||||
if ( (!sendFullHello) && (_vProto >= 5) && (!((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0))) ) {
|
||||
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
|
||||
RR->node->expectReplyTo(outp.packetId());
|
||||
outp.armor(_key,true);
|
||||
@ -398,7 +402,7 @@ void Peer::tryMemorizedPath(uint64_t now)
|
||||
_lastTriedMemorizedPath = now;
|
||||
InetAddress mp;
|
||||
if (RR->node->externalPathLookup(_id.address(),-1,mp))
|
||||
attemptToContactAt(InetAddress(),mp,now);
|
||||
attemptToContactAt(InetAddress(),mp,now,true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -420,7 +424,7 @@ bool Peer::doPingAndKeepalive(uint64_t now,int inetAddressFamily)
|
||||
|
||||
if (bestp >= 0) {
|
||||
if ( ((now - _paths[bestp].lastReceive) >= ZT_PEER_PING_PERIOD) || (_paths[bestp].path->needsHeartbeat(now)) ) {
|
||||
attemptToContactAt(_paths[bestp].path->localAddress(),_paths[bestp].path->address(),now);
|
||||
attemptToContactAt(_paths[bestp].path->localAddress(),_paths[bestp].path->address(),now,false);
|
||||
_paths[bestp].path->sent(now);
|
||||
}
|
||||
return true;
|
||||
@ -444,7 +448,7 @@ void Peer::resetWithinScope(InetAddress::IpScope scope,int inetAddressFamily,uin
|
||||
Mutex::Lock _l(_paths_m);
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if ( (_paths[p].path->address().ss_family == inetAddressFamily) && (_paths[p].path->address().ipScope() == scope) ) {
|
||||
attemptToContactAt(_paths[p].path->localAddress(),_paths[p].path->address(),now);
|
||||
attemptToContactAt(_paths[p].path->localAddress(),_paths[p].path->address(),now,false);
|
||||
_paths[p].path->sent(now);
|
||||
_paths[p].lastReceive = 0; // path will not be used unless it speaks again
|
||||
}
|
||||
|
@ -161,8 +161,9 @@ public:
|
||||
* @param localAddr Local address
|
||||
* @param atAddress Destination address
|
||||
* @param now Current time
|
||||
* @param sendFullHello If true, always send a full HELLO instead of just an ECHO
|
||||
*/
|
||||
void attemptToContactAt(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now);
|
||||
void attemptToContactAt(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,bool sendFullHello);
|
||||
|
||||
/**
|
||||
* Try a memorized or statically defined path if any are known
|
||||
|
@ -123,7 +123,7 @@ void Salsa20::init(const void *key,unsigned int kbits,const void *iv)
|
||||
#endif
|
||||
}
|
||||
|
||||
void Salsa20::encrypt12(const void *in,void *out,unsigned int bytes)
|
||||
void Salsa20::crypt12(const void *in,void *out,unsigned int bytes)
|
||||
throw()
|
||||
{
|
||||
uint8_t tmp[64];
|
||||
@ -623,7 +623,7 @@ void Salsa20::encrypt12(const void *in,void *out,unsigned int bytes)
|
||||
}
|
||||
}
|
||||
|
||||
void Salsa20::encrypt20(const void *in,void *out,unsigned int bytes)
|
||||
void Salsa20::crypt20(const void *in,void *out,unsigned int bytes)
|
||||
throw()
|
||||
{
|
||||
uint8_t tmp[64];
|
||||
|
@ -56,51 +56,25 @@ public:
|
||||
throw();
|
||||
|
||||
/**
|
||||
* Encrypt data using Salsa20/12
|
||||
* Encrypt/decrypt data using Salsa20/12
|
||||
*
|
||||
* @param in Input data
|
||||
* @param out Output buffer
|
||||
* @param bytes Length of data
|
||||
*/
|
||||
void encrypt12(const void *in,void *out,unsigned int bytes)
|
||||
void crypt12(const void *in,void *out,unsigned int bytes)
|
||||
throw();
|
||||
|
||||
/**
|
||||
* Encrypt data using Salsa20/20
|
||||
* Encrypt/decrypt data using Salsa20/20
|
||||
*
|
||||
* @param in Input data
|
||||
* @param out Output buffer
|
||||
* @param bytes Length of data
|
||||
*/
|
||||
void encrypt20(const void *in,void *out,unsigned int bytes)
|
||||
void crypt20(const void *in,void *out,unsigned int bytes)
|
||||
throw();
|
||||
|
||||
/**
|
||||
* Decrypt data
|
||||
*
|
||||
* @param in Input data
|
||||
* @param out Output buffer
|
||||
* @param bytes Length of data
|
||||
*/
|
||||
inline void decrypt12(const void *in,void *out,unsigned int bytes)
|
||||
throw()
|
||||
{
|
||||
encrypt12(in,out,bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt data
|
||||
*
|
||||
* @param in Input data
|
||||
* @param out Output buffer
|
||||
* @param bytes Length of data
|
||||
*/
|
||||
inline void decrypt20(const void *in,void *out,unsigned int bytes)
|
||||
throw()
|
||||
{
|
||||
encrypt20(in,out,bytes);
|
||||
}
|
||||
|
||||
private:
|
||||
union {
|
||||
#ifdef ZT_SALSA20_SSE
|
||||
|
@ -777,7 +777,7 @@ bool Switch::_trySend(Packet &packet,bool encrypt)
|
||||
if ((clusterMostRecentMemberId < 0)||(viaPath->lastIn() > clusterMostRecentTs)) {
|
||||
#endif
|
||||
if ((now - viaPath->lastOut()) > std::max((now - viaPath->lastIn()) * 4,(uint64_t)ZT_PATH_MIN_REACTIVATE_INTERVAL)) {
|
||||
peer->attemptToContactAt(viaPath->localAddress(),viaPath->address(),now);
|
||||
peer->attemptToContactAt(viaPath->localAddress(),viaPath->address(),now,false);
|
||||
viaPath->sent(now);
|
||||
}
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
|
@ -182,7 +182,7 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
|
||||
|
||||
#else // not __WINDOWS__
|
||||
|
||||
static char randomBuf[131072];
|
||||
static char randomBuf[65536];
|
||||
static unsigned int randomPtr = sizeof(randomBuf);
|
||||
static int devURandomFd = -1;
|
||||
|
||||
@ -215,7 +215,7 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
|
||||
|
||||
#endif // __WINDOWS__ or not
|
||||
|
||||
s20.encrypt12(buf,buf,bytes);
|
||||
s20.crypt12(buf,buf,bytes);
|
||||
}
|
||||
|
||||
bool Utils::scopy(char *dest,unsigned int len,const char *src)
|
||||
|
Loading…
Reference in New Issue
Block a user