This commit is contained in:
Adam Ierymenko 2019-12-11 12:04:22 -08:00
parent 5a4d681af8
commit b55f98b813
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
2 changed files with 172 additions and 110 deletions

View file

@ -52,30 +52,35 @@ public:
}; };
ZT_ALWAYS_INLINE Identity() { memset(reinterpret_cast<void *>(this),0,sizeof(Identity)); } ZT_ALWAYS_INLINE Identity() { memset(reinterpret_cast<void *>(this),0,sizeof(Identity)); }
ZT_ALWAYS_INLINE Identity(const char *str) ZT_ALWAYS_INLINE ~Identity() { Utils::burn(reinterpret_cast<void *>(&this->_priv),sizeof(this->_priv)); }
{
if (!fromString(str)) /**
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE; * Construct identity from string
} *
* If the identity is not basically valid (no deep checking is done) the result will
* be a null identity.
*
* @param str Identity in canonical string format
*/
ZT_ALWAYS_INLINE Identity(const char *str) { fromString(str); }
template<unsigned int C> template<unsigned int C>
ZT_ALWAYS_INLINE Identity(const Buffer<C> &b,unsigned int startAt = 0) { deserialize(b,startAt); } ZT_ALWAYS_INLINE Identity(const Buffer<C> &b,unsigned int startAt = 0) { deserialize(b,startAt); }
ZT_ALWAYS_INLINE ~Identity() { Utils::burn(reinterpret_cast<void *>(this),sizeof(Identity)); }
/** /**
* Set identity to NIL value (all zero) * Set identity to NIL value (all zero)
*/ */
ZT_ALWAYS_INLINE void zero() { memset(reinterpret_cast<void *>(this),0,sizeof(Identity)); } ZT_ALWAYS_INLINE void zero() { memset(reinterpret_cast<void *>(this),0,sizeof(Identity)); }
/** /**
* @return Identity type * @return Identity type (undefined if identity is null or invalid)
*/ */
ZT_ALWAYS_INLINE Type type() const { return _type; } ZT_ALWAYS_INLINE Type type() const { return _type; }
/** /**
* Generate a new identity (address, key pair) * Generate a new identity (address, key pair)
* *
* This is a time consuming operation. * This is a time consuming operation taking up to 5-10 seconds on some slower systems.
* *
* @param t Type of identity to generate * @param t Type of identity to generate
*/ */
@ -94,10 +99,12 @@ public:
ZT_ALWAYS_INLINE bool hasPrivate() const { return _hasPrivate; } ZT_ALWAYS_INLINE bool hasPrivate() const { return _hasPrivate; }
/** /**
* This generates a SHA384 hash of this identity's keys.
*
* @param h Buffer to receive SHA384 of public key(s) * @param h Buffer to receive SHA384 of public key(s)
* @param includePrivate If true, hash private key(s) too * @param includePrivate If true, hash private key(s) as well
*/ */
ZT_ALWAYS_INLINE bool hash(uint8_t h[48],const bool includePrivate) const ZT_ALWAYS_INLINE bool hash(uint8_t h[48],const bool includePrivate = false) const
{ {
switch(_type) { switch(_type) {
@ -142,8 +149,10 @@ public:
case P384: case P384:
if (siglen >= ZT_ECC384_SIGNATURE_SIZE) { if (siglen >= ZT_ECC384_SIGNATURE_SIZE) {
// When signing with P384 we also hash the C25519 public key as an
// extra measure to ensure that only this identity can verify.
uint8_t h[48]; uint8_t h[48];
SHA384(h,data,len); SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
ECC384ECDSASign(_priv.p384,h,(uint8_t *)sig); ECC384ECDSASign(_priv.p384,h,(uint8_t *)sig);
return ZT_ECC384_SIGNATURE_SIZE; return ZT_ECC384_SIGNATURE_SIZE;
} }
@ -165,15 +174,18 @@ public:
ZT_ALWAYS_INLINE bool verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const ZT_ALWAYS_INLINE bool verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const
{ {
switch(_type) { switch(_type) {
case C25519: case C25519:
return C25519::verify(_pub.c25519,data,len,sig,siglen); return C25519::verify(_pub.c25519,data,len,sig,siglen);
case P384: case P384:
if (siglen == ZT_ECC384_SIGNATURE_SIZE) { if (siglen == ZT_ECC384_SIGNATURE_SIZE) {
uint8_t h[48]; uint8_t h[48];
SHA384(h,data,len); SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
return ECC384ECDSAVerify(_pub.p384,h,(const uint8_t *)sig); return ECC384ECDSAVerify(_pub.p384,h,(const uint8_t *)sig);
} }
break; break;
} }
return false; return false;
} }
@ -193,6 +205,7 @@ public:
uint8_t h[64]; uint8_t h[64];
if (_hasPrivate) { if (_hasPrivate) {
if (_type == C25519) { if (_type == C25519) {
if ((id._type == C25519)||(id._type == P384)) { if ((id._type == C25519)||(id._type == P384)) {
// If we are a C25519 key we can agree with another C25519 key or with only the // If we are a C25519 key we can agree with another C25519 key or with only the
// C25519 portion of a type 1 P-384 key. // C25519 portion of a type 1 P-384 key.
@ -201,12 +214,10 @@ public:
memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH); memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH);
return true; return true;
} }
} else if (_type == P384) { } else if (_type == P384) {
if (id._type == P384) { if (id._type == P384) {
// Perform key agreement over both curves for the same reason that C25519 public
// keys are included in P-384 signature inputs: to bind the keys together so
// that a type 1 identity with the same C25519 public key (and therefore address)
// but a different P-384 key will not work.
C25519::agree(_priv.c25519,id._pub.c25519,rawkey); C25519::agree(_priv.c25519,id._pub.c25519,rawkey);
ECC384ECDH(id._pub.p384,_priv.p384,rawkey + ZT_C25519_SHARED_KEY_LEN); ECC384ECDH(id._pub.p384,_priv.p384,rawkey + ZT_C25519_SHARED_KEY_LEN);
SHA384(h,rawkey,ZT_C25519_SHARED_KEY_LEN + ZT_ECC384_SHARED_SECRET_SIZE); SHA384(h,rawkey,ZT_C25519_SHARED_KEY_LEN + ZT_ECC384_SHARED_SECRET_SIZE);
@ -219,6 +230,7 @@ public:
memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH); memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH);
return true; return true;
} }
} }
} }
return false; return false;
@ -229,28 +241,6 @@ public:
*/ */
ZT_ALWAYS_INLINE const Address &address() const { return _address; } ZT_ALWAYS_INLINE const Address &address() const { return _address; }
/**
* Attempt to generate an older type identity from a newer type
*
* @param dest Destination to fill with downgraded identity
* @param toType Desired identity type
*/
ZT_ALWAYS_INLINE bool downgrade(Identity &dest,const Type toType)
{
if (_type == toType) {
return true;
} else if ((_type == P384)&&(toType == C25519)) {
dest._address = _address;
dest._type = C25519;
dest._hasPrivate = _hasPrivate;
memcpy(dest._pub.c25519,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
if (_hasPrivate)
memcpy(dest._priv.c25519,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN);
return true;
}
return false;
}
/** /**
* Serialize this identity (binary) * Serialize this identity (binary)
* *
@ -284,7 +274,7 @@ public:
} else { } else {
b.append((uint8_t)0); b.append((uint8_t)0);
} }
b.append((uint16_t)0); // size of additional fields b.append((uint16_t)0); // size of additional fields (should have included such a thing in v0!)
break; break;
} }
@ -358,7 +348,7 @@ public:
* *
* @param includePrivate If true, include private key (if it exists) * @param includePrivate If true, include private key (if it exists)
* @param buf Buffer to store string * @param buf Buffer to store string
* @return ASCII string representation of identity * @return ASCII string representation of identity (pointer to buf)
*/ */
char *toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const; char *toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const;
@ -382,12 +372,9 @@ public:
{ {
if ((_address == id._address)&&(_type == id._type)) { if ((_address == id._address)&&(_type == id._type)) {
switch(_type) { switch(_type) {
case C25519: case C25519: return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) == 0);
return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) == 0); // case P384:
case P384: default: return (memcmp(&_pub,&id._pub,sizeof(_pub)) == 0);
return (memcmp(&_pub,&id._pub,sizeof(_pub)) == 0);
default:
return false;
} }
} }
return false; return false;
@ -401,10 +388,9 @@ public:
return true; return true;
if (_type == id._type) { if (_type == id._type) {
switch(_type) { switch(_type) {
case C25519: case C25519: return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) < 0);
return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) < 0); // case P384:
case P384: default: return (memcmp(&_pub,&id._pub,sizeof(_pub)) < 0);
return (memcmp(&_pub,&id._pub,sizeof(_pub)) < 0);
} }
} }
} }

View file

@ -424,10 +424,12 @@ public:
* <[8] timestamp for determining latency> * <[8] timestamp for determining latency>
* <[...] binary serialized identity (see Identity)> * <[...] binary serialized identity (see Identity)>
* <[...] physical destination address of packet> * <[...] physical destination address of packet>
* [... begin encrypted section ...] * [... begin encrypted region ...]
* <[2] 16-bit reserved field, always 0> * <[2] 16-bit reserved field, always 0>
* <[2] 16-bit length of locator> * <[2] 16-bit length of locator>
* <[...] locator for this node> * <[...] locator for this node>
* <[2] 16-bit length of meta-data dictionary>
* <[...] meta-data dictionary>
* *
* HELLO is sent in the clear as it is how peers share their identity * HELLO is sent in the clear as it is how peers share their identity
* public keys. * public keys.
@ -441,6 +443,9 @@ public:
* very sensitive, but hiding the locator and other meta-data slightly * very sensitive, but hiding the locator and other meta-data slightly
* improves privacy. * improves privacy.
* *
* The 16-bit zero after encryption starts is for backward compatibility
* with pre-2.0 nodes.
*
* OK payload: * OK payload:
* <[8] HELLO timestamp field echo> * <[8] HELLO timestamp field echo>
* <[1] protocol version> * <[1] protocol version>
@ -578,6 +583,20 @@ public:
*/ */
VERB_ECHO = 0x08, VERB_ECHO = 0x08,
/**
* Announce interest in multicast group(s):
* <[8] 64-bit network ID>
* <[6] multicast Ethernet address>
* <[4] multicast additional distinguishing information (ADI)>
* [... additional tuples of network/address/adi ...]
*
* LIKEs may be sent to any peer, though a good implementation should
* restrict them to peers on the same network they're for and to network
* controllers and root servers. In the current network, root servers
* will provide the service of final multicast cache.
*/
VERB_MULTICAST_LIKE = 0x09,
/** /**
* Network credentials push: * Network credentials push:
* [<[...] one or more certificates of membership>] * [<[...] one or more certificates of membership>]
@ -682,6 +701,73 @@ public:
*/ */
VERB_NETWORK_CONFIG = 0x0c, VERB_NETWORK_CONFIG = 0x0c,
/**
* Request endpoints for multicast distribution:
* <[8] 64-bit network ID>
* <[1] flags>
* <[6] MAC address of multicast group being queried>
* <[4] 32-bit ADI for multicast group being queried>
* <[4] 32-bit requested max number of multicast peers>
*
* This message asks a peer for additional known endpoints that have
* LIKEd a given multicast group. It's sent when the sender wishes
* to send multicast but does not have the desired number of recipient
* peers.
*
* OK response payload: (multiple OKs can be generated)
* <[8] 64-bit network ID>
* <[6] MAC address of multicast group being queried>
* <[4] 32-bit ADI for multicast group being queried>
* <[4] 32-bit total number of known members in this multicast group>
* <[2] 16-bit number of members enumerated in this packet>
* <[...] series of 5-byte ZeroTier addresses of enumerated members>
*
* ERROR is not generated; queries that return no response are dropped.
*/
VERB_MULTICAST_GATHER = 0x0d,
/** *** DEPRECATED ***
* Multicast frame:
* <[8] 64-bit network ID>
* <[1] flags>
* [<[4] 32-bit implicit gather limit>]
* [<[6] source MAC>]
* <[6] destination MAC (multicast address)>
* <[4] 32-bit multicast ADI (multicast address extension)>
* <[2] 16-bit ethertype>
* <[...] ethernet payload>
*
* Flags:
* 0x01 - Network certificate of membership attached (DEPRECATED)
* 0x02 - Implicit gather limit field is present
* 0x04 - Source MAC is specified -- otherwise it's computed from sender
* 0x08 - Please replicate (sent to multicast replicators)
*
* OK and ERROR responses are optional. OK may be generated if there are
* implicit gather results or if the recipient wants to send its own
* updated certificate of network membership to the sender. ERROR may be
* generated if a certificate is needed or if multicasts to this group
* are no longer wanted (multicast unsubscribe).
*
* OK response payload:
* <[8] 64-bit network ID>
* <[6] MAC address of multicast group>
* <[4] 32-bit ADI for multicast group>
* <[1] flags>
* [<[...] network certificate of membership (DEPRECATED)>]
* [<[...] implicit gather results if flag 0x01 is set>]
*
* OK flags (same bits as request flags):
* 0x01 - OK includes certificate of network membership (DEPRECATED)
* 0x02 - OK includes implicit gather results
*
* ERROR response payload:
* <[8] 64-bit network ID>
* <[6] multicast group MAC>
* <[4] 32-bit multicast group ADI>
*/
VERB_MULTICAST_FRAME = 0x0e,
/** /**
* Push of potential endpoints for direct communication: * Push of potential endpoints for direct communication:
* <[2] 16-bit number of paths> * <[2] 16-bit number of paths>
@ -784,77 +870,67 @@ public:
VERB_REMOTE_TRACE = 0x15, VERB_REMOTE_TRACE = 0x15,
/** /**
* Multipurpose VL2 network multicast: * Peer-to-peer propagated multicast packet:
* <[5] start of range of addresses for propagation> * <[128] 1024-bit bloom filter>
* <[5] end of range of addresses for propagation> * <[2] 16-bit perturbation coefficient to minimize bloom collisions>
* <[1] 8-bit propagation depth / hops or 0xff to not propagate> * <[5] 40-bit start of range of recipient addresses>
* <[1] 8-bit length of bloom filter in 256-byte/2048-bit chunks> * <[5] 40-bit end of range of recipient addresses>
* <[...] propagation bloom filter> * [... begin signed portion ...]
* [... start of signed portion ...] * <[1] 8-bit flags>
* <[8] 64-bit timestamp> * <[5] 40-bit ZeroTier address of sender>
* <[8] 64-bit network ID> * <[8] 64-bit network ID>
* <[5] 40-bit address of sender>
* <[2] 16-bit length of multicast payload>
* [... start multicast payload ...]
* <[1] 8-bit payload type>
* [... end multicast payload and signed portion ...]
* <[2] 16-bit length of signature or 0 if not present>
* <[...] signature of signed portion>
*
* Payload type 0x00: multicast frame:
* <[6] MAC address of multicast group> * <[6] MAC address of multicast group>
* <[4] 32-bit ADI of multicast group> * <[4] 32-bit ADI for multicast group>
* <[6] 48-bit source MAC of packet or all 0 if from sender> * <[6] MAC address of sender>
* <[2] 16-bit ethertype> * <[2] 16-bit ethertype>
* <[2] 16-bit length of ethernet payload>
* <[...] ethernet payload> * <[...] ethernet payload>
* [... end signed portion ...]
* <[2] 16-bit length of signature or 0 if unsigned>
* [<[...] optional signature of multicast>]
* *
* Payload type 0x01: multicast subscribe: * This packet contains a multicast that is to be peer-to-peer replicated.
* <[2] 16-bit number of multicast group IDs to subscribe> * The range of recipient addresses is a subset of the global list of
* <[...] series of 32-bit multicast group IDs> * subscribers to this multicast group. As the packet is propagated bits
* * in the bloom filter will be set. The sender may attempt to select a
* Payload type 0x02: multicast unsubscribe: * perturbation coefficient to prevent collisions within the selected
* <[2] 16-bit number of multicast group IDs to unsubscribe> * recipient range.
* <[...] series of 32-bit multicast group IDs>
*
* This is the common packet structure for VL2 network-level multicasts
* and is used for multicast frames, multicast group subscribe and
* unsubscribe, and could be used in the future for other purposes such
* as credential propagation or diagnostics.
*
* The header contains an address range, bloom filter, and depth/hop
* counter. The bloom filter tracks which nodes have seen this multicast,
* with bits being set prior to send. The range allows the total set of
* subscribers to be partitioned in the case of huge networks that would
* saturate the bloom filter or have collisions. The propagation depth
* allows propagation to stop at some maximum value, and the value 0xff
* can be used to indicate that further propagation is not desired.
*
* Logic connected to the parsing of the multicast payload will determine
* whether or not and to whom this multicast is propagated. Subscribe and
* unsubscribe messages are propagated to online nodes up to a maximum
* depth, while frames have the added constraint of being propagated only
* to nodes that subscribe to the target multicast group.
*/ */
VERB_VL2_MULTICAST = 0x16, VERB_MULTICAST = 0x16,
/** /**
* Negotiate a new ephemeral key: * Negotiate a new ephemeral key:
* <[8] first 64 bits of SHA-384 of currently known key for destination> * <[48] SHA384 of ephemeral key we currently have for recipient>
* <[...] ephemeral key for sender> * [<[...] sender's ephemeral key>]
* *
* If the 64-bit hash of the currently known key sent by the sender does * REKEY is used to negotiate ephemeral keys. The first byte is a step
* not match the key the destination is currently using, the destination * number from 0 to 2. Here's a new session initiated by Alice:
* will send its own REKEY after sending OK to ensure that keys are up to
* date on both sides. This causes either side sending REKEY to trigger
* an automatic two-way handshake. Either side may therefore rekey at
* any time, though a rate limit should be in effect to prevent flooding.
* *
* OK payload: * Alice: REKEY[0x000...,AliceKey] -> Bob
* <[8] first 64 bits of SHA-384 of received ephemeral key> * Bob: REKEY[SHA384(AliceKey),BobKey] -> Alice
* Alice: REKEY[SHA384(BobKey),(omitted)] -> Bob
*
* REKEY messages will continue until both sides have acknowledged each
* others' keys. Either Alice or Bob can send REKEY to negotiate a new
* ephemeral key pair at any time.
*
* OK isn't used because this is an ongoing handshake until both sides
* agree on a key. REKEY triggers a REKEY in reply if the hash for the
* recipient's ephemeral public key doesn't match the ephemeral key it
* wants to use.
*/ */
VERB_REKEY = 0x17 VERB_REKEY = 0x17,
// TODO: legacy multicast message types must be supported /**
* Encapsulate a full ZeroTier packet in another:
* <[...] raw encapsulated packet>
*
* Encapsulation exists to enable secure relaying as opposed to the usual
* "dumb" relaying. The latter is faster but secure relaying has roles
* where endpoint privacy is desired. Multiply nested ENCAP packets
* could allow ZeroTier to act as an onion router.
*/
VERB_ENCAP = 0x18
// protocol max: 0x1f // protocol max: 0x1f
}; };