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(const char *str)
{
if (!fromString(str))
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
}
ZT_ALWAYS_INLINE ~Identity() { Utils::burn(reinterpret_cast<void *>(&this->_priv),sizeof(this->_priv)); }
/**
* 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>
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)
*/
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; }
/**
* 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
*/
@ -94,10 +99,12 @@ public:
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 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) {
@ -142,8 +149,10 @@ public:
case P384:
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];
SHA384(h,data,len);
SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
ECC384ECDSASign(_priv.p384,h,(uint8_t *)sig);
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
{
switch(_type) {
case C25519:
return C25519::verify(_pub.c25519,data,len,sig,siglen);
case P384:
if (siglen == ZT_ECC384_SIGNATURE_SIZE) {
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);
}
break;
}
return false;
}
@ -193,6 +205,7 @@ public:
uint8_t h[64];
if (_hasPrivate) {
if (_type == C25519) {
if ((id._type == C25519)||(id._type == P384)) {
// 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.
@ -201,12 +214,10 @@ public:
memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH);
return true;
}
} else if (_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);
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);
@ -219,6 +230,7 @@ public:
memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH);
return true;
}
}
}
return false;
@ -229,28 +241,6 @@ public:
*/
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)
*
@ -284,7 +274,7 @@ public:
} else {
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;
}
@ -358,7 +348,7 @@ public:
*
* @param includePrivate If true, include private key (if it exists)
* @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;
@ -382,12 +372,9 @@ public:
{
if ((_address == id._address)&&(_type == id._type)) {
switch(_type) {
case C25519:
return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) == 0);
case P384:
return (memcmp(&_pub,&id._pub,sizeof(_pub)) == 0);
default:
return false;
case C25519: return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) == 0);
// case P384:
default: return (memcmp(&_pub,&id._pub,sizeof(_pub)) == 0);
}
}
return false;
@ -401,10 +388,9 @@ public:
return true;
if (_type == id._type) {
switch(_type) {
case C25519:
return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) < 0);
case P384:
return (memcmp(&_pub,&id._pub,sizeof(_pub)) < 0);
case C25519: return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) < 0);
// case P384:
default: return (memcmp(&_pub,&id._pub,sizeof(_pub)) < 0);
}
}
}

View file

@ -424,10 +424,12 @@ public:
* <[8] timestamp for determining latency>
* <[...] binary serialized identity (see Identity)>
* <[...] physical destination address of packet>
* [... begin encrypted section ...]
* [... begin encrypted region ...]
* <[2] 16-bit reserved field, always 0>
* <[2] 16-bit length of locator>
* <[...] 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
* public keys.
@ -441,6 +443,9 @@ public:
* very sensitive, but hiding the locator and other meta-data slightly
* improves privacy.
*
* The 16-bit zero after encryption starts is for backward compatibility
* with pre-2.0 nodes.
*
* OK payload:
* <[8] HELLO timestamp field echo>
* <[1] protocol version>
@ -578,6 +583,20 @@ public:
*/
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:
* [<[...] one or more certificates of membership>]
@ -682,6 +701,73 @@ public:
*/
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:
* <[2] 16-bit number of paths>
@ -784,77 +870,67 @@ public:
VERB_REMOTE_TRACE = 0x15,
/**
* Multipurpose VL2 network multicast:
* <[5] start of range of addresses for propagation>
* <[5] end of range of addresses for propagation>
* <[1] 8-bit propagation depth / hops or 0xff to not propagate>
* <[1] 8-bit length of bloom filter in 256-byte/2048-bit chunks>
* <[...] propagation bloom filter>
* [... start of signed portion ...]
* <[8] 64-bit timestamp>
* Peer-to-peer propagated multicast packet:
* <[128] 1024-bit bloom filter>
* <[2] 16-bit perturbation coefficient to minimize bloom collisions>
* <[5] 40-bit start of range of recipient addresses>
* <[5] 40-bit end of range of recipient addresses>
* [... begin signed portion ...]
* <[1] 8-bit flags>
* <[5] 40-bit ZeroTier address of sender>
* <[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>
* <[4] 32-bit ADI of multicast group>
* <[6] 48-bit source MAC of packet or all 0 if from sender>
* <[4] 32-bit ADI for multicast group>
* <[6] MAC address of sender>
* <[2] 16-bit ethertype>
* <[2] 16-bit length of 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:
* <[2] 16-bit number of multicast group IDs to subscribe>
* <[...] series of 32-bit multicast group IDs>
*
* Payload type 0x02: multicast unsubscribe:
* <[2] 16-bit number of multicast group IDs to unsubscribe>
* <[...] 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.
* This packet contains a multicast that is to be peer-to-peer replicated.
* The range of recipient addresses is a subset of the global list of
* 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
* perturbation coefficient to prevent collisions within the selected
* recipient range.
*/
VERB_VL2_MULTICAST = 0x16,
VERB_MULTICAST = 0x16,
/**
* Negotiate a new ephemeral key:
* <[8] first 64 bits of SHA-384 of currently known key for destination>
* <[...] ephemeral key for sender>
* <[48] SHA384 of ephemeral key we currently have for recipient>
* [<[...] sender's ephemeral key>]
*
* If the 64-bit hash of the currently known key sent by the sender does
* not match the key the destination is currently using, the destination
* 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.
* REKEY is used to negotiate ephemeral keys. The first byte is a step
* number from 0 to 2. Here's a new session initiated by Alice:
*
* OK payload:
* <[8] first 64 bits of SHA-384 of received ephemeral key>
* Alice: REKEY[0x000...,AliceKey] -> Bob
* 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
};