Documentation changes, and move HMAC auth on HELLO to before object decoding since this is good cryptographic practice.

This commit is contained in:
Adam Ierymenko 2020-02-18 10:31:31 -08:00
parent 8c8a3c58ec
commit 0dc476518b
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
4 changed files with 112 additions and 90 deletions

View file

@ -38,10 +38,8 @@ namespace ZeroTier {
* Identities currently come in two types: type 0 identities based on just Curve25519
* and Ed25519 and type 1 identities that include both a 25519 key pair and a NIST P-384
* key pair. Type 1 identities use P-384 for signatures but use both key pairs at once
* (hashing their results) for key agreement with other type 1 identities, and can agree
* with type 0 identities using only their Curve25519 keys. The ability of type 0 and 1
* identities to agree will allow type 0 identities to keep being used even after type
* 1 becomes the default.
* (hashing both keys together) for key agreement with other type 1 identities, and can
* agree with type 0 identities by only using the Curve25519 component.
*
* Type 1 identities also use a simpler mechanism to rate limit identity generation (as
* a defense in depth against intentional collision) that makes local identity validation

View file

@ -38,6 +38,8 @@ Membership::~Membership()
void Membership::pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const int64_t now,const Address &peerAddress,const NetworkConfig &nconf)
{
Buf outp;
#if 0
const Capability *sendCaps[ZT_MAX_NETWORK_CAPABILITIES];
unsigned int sendCapCount = 0;
for(unsigned int c=0;c<nconf.capabilityCount;++c)
@ -99,6 +101,7 @@ void Membership::pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const i
outp.compress();
RR->sw->send(tPtr,outp,true);
}
#endif
_lastPushedCredentials = now;
}

View file

@ -23,6 +23,64 @@
#include "Address.hpp"
#include "Identity.hpp"
/*
* Core ZeroTier protocol packet formats ------------------------------------------------------------------------------
*
* Packet format:
* <[8] 64-bit packet ID / crypto IV>
* <[5] destination ZT address>
* <[5] source ZT address>
* <[1] outer visible flags, cipher, and hop count (bits: FFCCHHH)>
* <[8] 64-bit MAC (or trusted path ID in trusted path mode)>
* [... -- begin encryption envelope -- ...]
* <[1] inner envelope flags (MS 3 bits) and verb (LS 5 bits)>
* [... verb-specific payload ...]
*
* Packets smaller than 28 bytes are invalid and silently discarded.
*
* The hop count field is masked during message authentication computation
* and is thus the only field that is mutable in transit. It's incremented
* when roots or other nodes forward packets and exists to prevent infinite
* forwarding loops and to detect direct paths.
*
* HELLO is normally sent in the clear with the POLY1305_NONE cipher suite
* and with Poly1305 computed on plain text (Salsa20/12 is still used to
* generate a one time use Poly1305 key). As of protocol version 11 HELLO
* also includes a terminating HMAC (last 48 bytes) that significantly
* hardens HELLO authentication beyond what a 64-bit MAC can guarantee.
*
* Fragmented packets begin with a packet header whose fragment bit (bit
* 0x40 in the flags field) is set. This constitutes fragment zero. The
* total number of expected fragments is contained in each subsequent
* fragment packet. Unfragmented packets must not have the fragment bit
* set or the receiver will expect at least one additional fragment.
*
* --
*
* Packet fragment format (fragments beyond 0):
* <[8] packet ID of packet to which this fragment belongs>
* <[5] destination ZT address>
* <[1] 0xff here signals that this is a fragment>
* <[1] total fragments (most significant 4 bits), fragment no (LS 4 bits)>
* <[1] ZT hop count (least significant 3 bits; others are reserved)>
* <[...] fragment data>
*
* The protocol supports a maximum of 16 fragments including fragment 0
* which contains the full packet header (with fragment bit set). Fragments
* thus always carry fragment numbers between 1 and 15. All fragments
* belonging to the same packet must carry the same total fragment count in
* the most significant 4 bits of the fragment numbering field.
*
* All fragments have the same packet ID and destination. The packet ID
* doubles as the grouping identifier for fragment reassembly.
*
* Fragments do not carry their own packet MAC. The entire packet is
* authenticated once it is assembled by the receiver. Incomplete packets
* are discarded after a receiver configured period of time.
*
* --------------------------------------------------------------------------------------------------------------------
*/
/**
* Protocol version -- incremented only for major changes
*
@ -50,24 +108,25 @@
* + inline push of CertificateOfMembership deprecated
* 9 - 1.2.0 ... 1.2.14
* 10 - 1.4.0 ... 1.4.6
* + Contained early pre-alpha versions of multipath, which are deprecated
* 11 - 2.0.0 ... CURRENT
* + Peer-to-peer multicast replication
* + HELLO and OK(HELLO) include an extra HMAC to further harden auth
* + Old planet/moon stuff is DEAD!
* + AES encryption support
* + NIST P-384 (type 1) identities
* + Ephemeral keys
* + New more WAN-efficient P2P-assisted multicast algorithm
* + HELLO and OK(HELLO) include an extra HMAC to harden authentication
* + HELLO and OK(HELLO) can carry structured meta-data
* + Ephemeral keys for forward secrecy and limited key lifetime
* + Old planet/moon stuff is DEAD! Independent roots are easier.
* + AES encryption is now the default
* + New combined Curve25519/NIST P-384 identity type (type 1)
* + Short probe packets to reduce probe bandwidth
* + Aggressive NAT traversal techniques for IPv4 symmetric NATs
* + Remote diagnostics including rewrite of remote tracing
*/
#define ZT_PROTO_VERSION 11
/**
* Minimum supported protocol version
*
* As of v2 we don't "officially" support anything older than 1.2.14, but this
* is the hard cutoff before which peers will be flat out rejected.
*/
#define ZT_PROTO_VERSION_MIN 6
#define ZT_PROTO_VERSION_MIN 8
/**
* Packet buffer size (can be changed)
@ -93,8 +152,8 @@
* Maximum hop count allowed by packet structure (3 bits, 0-7)
*
* This is a protocol constant. It's the maximum allowed by the length
* of the hop counter -- three bits. See node/Constants.hpp for the
* pragmatic forwarding limit, which is typically lower.
* of the hop counter -- three bits. A lower limit is specified as
* the actual maximum hop count.
*/
#define ZT_PROTO_MAX_HOPS 7
@ -121,7 +180,12 @@
#define ZT_PROTO_CIPHER_SUITE__AES_GCM_NRH 3
/**
* Magic number indicating a fragment
* Minimum viable length for a fragment
*/
#define ZT_PROTO_MIN_FRAGMENT_LENGTH 16
/**
* Magic number indicating a fragment if present at index 13
*/
#define ZT_PROTO_PACKET_FRAGMENT_INDICATOR 0xff
@ -136,12 +200,7 @@
#define ZT_PROTO_PACKET_FLAGS_INDEX 18
/**
* Minimum viable length for a fragment
*/
#define ZT_PROTO_MIN_FRAGMENT_LENGTH 16
/**
* Length of a probe
* Length of a probe packet
*/
#define ZT_PROTO_PROBE_LENGTH 8
@ -155,6 +214,11 @@
*/
#define ZT_PROTO_FLAG_FRAGMENTED 0x40U
/**
* Mask for obtaining hops from the combined flags, cipher, and hops field
*/
#define ZT_PROTO_FLAG_FIELD_HOPS_MASK 0x07U
/**
* Verb flag indicating payload is compressed with LZ4
*/
@ -208,53 +272,6 @@
*/
#define ZT_PROTO_HELLO_NODE_META_LOCATION_Z "gZ"
/****************************************************************************/
/*
* Packet format:
* <[8] 64-bit packet ID / crypto IV>
* <[5] destination ZT address>
* <[5] source ZT address>
* <[1] flags/cipher/hops>
* <[8] 64-bit MAC (or trusted path ID in trusted path mode)>
* [... -- begin encryption envelope -- ...]
* <[1] encrypted flags (MS 3 bits) and verb (LS 5 bits)>
* [... verb-specific payload ...]
*
* Packets smaller than 28 bytes are invalid and silently discarded.
*
* The flags/cipher/hops bit field is: FFCCCHHH where C is a 3-bit cipher
* selection allowing up to 7 cipher suites, F is outside-envelope flags,
* and H is hop count.
*
* The three-bit hop count is the only part of a packet that is mutable in
* transit without invalidating the MAC. All other bits in the packet are
* immutable. This is because intermediate nodes can increment the hop
* count up to 7 (protocol max).
*
* For unencrypted packets, MAC is computed on plaintext. Only HELLO is ever
* sent in the clear, as it's the "here is my public key" message.
*
* The fragmented bit indicates that there is at least one fragment. Fragments
* themselves contain the total, so the receiver must "learn" this from the
* first fragment it receives.
*
* Fragments are sent with the following format:
* <[8] packet ID of packet to which this fragment belongs>
* <[5] destination ZT address>
* <[1] 0xff here signals that this is a fragment>
* <[1] total fragments (most significant 4 bits), fragment no (LS 4 bits)>
* <[1] ZT hop count (least significant 3 bits; others are reserved)>
* <[...] fragment data>
*
* The protocol supports a maximum of 16 fragments. If a fragment is received
* before its main packet header, it should be cached for a brief period of
* time to see if its parent arrives. Loss of any fragment constitutes packet
* loss; there is no retransmission mechanism. The receiver must wait for full
* receipt to authenticate and decrypt; there is no per-fragment MAC. (But if
* fragments are corrupt, the MAC will fail for the whole assembled packet.)
*/
namespace ZeroTier {
namespace Protocol {
@ -947,7 +964,7 @@ ZT_ALWAYS_INLINE uint64_t packetId(const Buf &pkt,const unsigned int packetSize)
* @param packetSize Packet's actual size in bytes
* @return 3-bit hops field embedded in packet flags field
*/
ZT_ALWAYS_INLINE uint8_t packetHops(const Buf &pkt,const unsigned int packetSize) noexcept { return (packetSize >= ZT_PROTO_PACKET_FLAGS_INDEX) ? (pkt.b[ZT_PROTO_PACKET_FLAGS_INDEX] & 0x07U) : 0; }
ZT_ALWAYS_INLINE uint8_t packetHops(const Buf &pkt,const unsigned int packetSize) noexcept { return (packetSize >= ZT_PROTO_PACKET_FLAGS_INDEX) ? (pkt.b[ZT_PROTO_PACKET_FLAGS_INDEX] & ZT_PROTO_FLAG_FIELD_HOPS_MASK) : 0; }
/**
* @param Packet to extract cipher ID from

View file

@ -484,6 +484,7 @@ bool VL1::_HELLO(void *tPtr,const SharedPtr<Path> &path,SharedPtr<Peer> &peer,Bu
}
Protocol::HELLO &p = pkt.as<Protocol::HELLO>();
const uint8_t hops = Protocol::packetHops(p.h);
p.h.flags &= (uint8_t)~ZT_PROTO_FLAG_FIELD_HOPS_MASK; // mask off hops for MAC calculation
int ptr = sizeof(Protocol::HELLO);
if (p.versionProtocol < ZT_PROTO_VERSION_MIN) {
@ -529,12 +530,26 @@ bool VL1::_HELLO(void *tPtr,const SharedPtr<Path> &path,SharedPtr<Peer> &peer,Bu
}
}
// Packet has passed Poly1305 verification --------------------------------------------------------------------------
// Packet has passed Poly1305 MAC authentication --------------------------------------------------------------------
uint8_t hmacKey[ZT_PEER_SECRET_KEY_LENGTH],hmac[ZT_HMACSHA384_LEN];
if (peer->remoteVersionProtocol() >= 11) {
if (packetSize <= ZT_HMACSHA384_LEN) { // sanity check, should be impossible
RR->t->incomingPacketDropped(tPtr,0x1000662a,p.h.packetId,0,id,path->address(),hops,Protocol::VERB_NOP,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED);
return false;
}
KBKDFHMACSHA384(key,ZT_PROTO_KDF_KEY_LABEL_HELLO_HMAC,0,0,hmacKey); // iter == 0 for HELLO, 1 for OK(HELLO)
HMACSHA384(hmacKey,pkt.b,packetSize - ZT_HMACSHA384_LEN,hmac);
if (!Utils::secureEq(pkt.b + (packetSize - ZT_HMACSHA384_LEN),hmac,ZT_HMACSHA384_LEN)) {
RR->t->incomingPacketDropped(tPtr,0x1000662a,p.h.packetId,0,id,path->address(),hops,Protocol::VERB_NOP,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED);
return false;
}
}
// Packet has passed HMAC-SHA384 (if present and/or forced) ---------------------------------------------------------
InetAddress externalSurfaceAddress;
Dictionary nodeMetaData;
uint8_t hmacKey[ZT_PEER_SECRET_KEY_LENGTH],hmac[ZT_HMACSHA384_LEN];
bool hmacAuthenticated = false;
// Get external surface address if present.
if (ptr < packetSize) {
@ -544,7 +559,7 @@ bool VL1::_HELLO(void *tPtr,const SharedPtr<Path> &path,SharedPtr<Peer> &peer,Bu
}
}
if (ptr < packetSize) {
if (((ptr + ZT_HMACSHA384_LEN) < packetSize)&&(peer->remoteVersionProtocol() >= 11)) {
// Everything after this point is encrypted with Salsa20/12. This is only a privacy measure
// since there's nothing truly secret in a HELLO packet. It also means that an observer
// can't even get ephemeral public keys without first knowing the long term secret key,
@ -553,9 +568,9 @@ bool VL1::_HELLO(void *tPtr,const SharedPtr<Path> &path,SharedPtr<Peer> &peer,Bu
for (int i = 0; i < 8; ++i) iv[i] = pkt.b[i];
iv[7] &= 0xf8U;
Salsa20 s20(key,iv);
s20.crypt12(pkt.b + ptr,pkt.b + ptr,packetSize - ptr);
s20.crypt12(pkt.b + ptr,pkt.b + ptr,(packetSize - ZT_HMACSHA384_LEN) - ptr);
ptr += pkt.rI16(ptr); // this field is zero in v2.0+ but can indicate data between this point and dictionary
ptr += pkt.rI16(ptr); // skip length field which currently is always zero in v2.0+
if (ptr < packetSize) {
const unsigned int dictionarySize = pkt.rI16(ptr);
const void *const dictionaryBytes = pkt.b + ptr;
@ -570,16 +585,6 @@ bool VL1::_HELLO(void *tPtr,const SharedPtr<Path> &path,SharedPtr<Peer> &peer,Bu
return false;
}
if ((ptr + ZT_SHA384_DIGEST_LEN) <= packetSize) {
KBKDFHMACSHA384(key,ZT_PROTO_KDF_KEY_LABEL_HELLO_HMAC,0,0,hmacKey); // iter == 0 for HELLO
HMACSHA384(hmacKey,pkt.b + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,packetSize - ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,hmac);
if (!Utils::secureEq(pkt.b + ptr,hmac,ZT_HMACSHA384_LEN)) {
RR->t->incomingPacketDropped(tPtr,0x1000662a,p.h.packetId,0,id,path->address(),hops,Protocol::VERB_NOP,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED);
return false;
}
hmacAuthenticated = true;
}
if (dictionarySize) {
if (!nodeMetaData.decode(dictionaryBytes,dictionarySize)) {
RR->t->incomingPacketDropped(tPtr,0x67192344,p.h.packetId,0,id,path->address(),hops,Protocol::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_INVALID_OBJECT);
@ -589,9 +594,8 @@ bool VL1::_HELLO(void *tPtr,const SharedPtr<Path> &path,SharedPtr<Peer> &peer,Bu
}
}
// v2.x+ peers must include HMAC, older peers don't (we'll drop support for them when 1.x is dead)
if ((!hmacAuthenticated) && (p.versionProtocol >= 11)) {
RR->t->incomingPacketDropped(tPtr,0x571feeea,p.h.packetId,0,id,path->address(),hops,Protocol::VERB_NOP,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED);
if (Buf::readOverflow(ptr,packetSize)) { // sanity check, should be impossible
RR->t->incomingPacketDropped(tPtr,0x457f2347,0,p.h.packetId,id,path->address(),0,Protocol::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET);
return false;
}