diff --git a/node/Network.cpp b/node/Network.cpp index 455820d98..2d43e383a 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -147,6 +147,7 @@ Network::~Network() SharedPtr Network::newInstance(const RuntimeEnvironment *renv,uint64_t id) throw(std::runtime_error) { + // Tag to identify tap device -- used on some OSes like Windows char tag[32]; Utils::snprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)id); @@ -159,8 +160,7 @@ SharedPtr Network::newInstance(const RuntimeEnvironment *renv,uint64_t nw->_ready = false; // disable handling of Ethernet frames during construct nw->_r = renv; nw->_tap = new EthernetTap(renv,tag,renv->identity.address().toMAC(),ZT_IF_MTU,&_CBhandleTapData,nw.ptr()); - nw->_multicastPropagationBreadth = 0; - nw->_multicastPropagationDepth = 0; + nw->_isOpen = false; memset(nw->_etWhitelist,0,sizeof(nw->_etWhitelist)); nw->_id = id; nw->_lastConfigUpdate = 0; @@ -179,28 +179,39 @@ void Network::setConfiguration(const Network::Config &conf) try { if (conf.networkId() == _id) { // sanity check _configuration = conf; + + // Grab some things from conf for faster lookup and memoize them _myCertificate = conf.certificateOfMembership(); _mcRates = conf.multicastRates(); - _multicastPropagationBreadth = conf.multicastPropagationBreadth(); - _multicastPropagationDepth = conf.multicastPropagationDepth(); + _staticAddresses = conf.staticAddresses(); + _isOpen = conf.isOpen(); + _lastConfigUpdate = Utils::now(); - _tap->setIps(conf.staticAddresses()); + _tap->setIps(_staticAddresses); _tap->setDisplayName((std::string("ZeroTier One [") + conf.name() + "]").c_str()); + // Expand ethertype whitelist into fast-lookup bit field memset(_etWhitelist,0,sizeof(_etWhitelist)); std::set wl(conf.etherTypes()); for(std::set::const_iterator t(wl.begin());t!=wl.end();++t) _etWhitelist[*t / 8] |= (unsigned char)(1 << (*t % 8)); + // Save most recent configuration to disk in networks.d std::string confPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + idString() + ".conf"); if (!Utils::writeFile(confPath.c_str(),conf.toString())) { LOG("error: unable to write network configuration file at: %s",confPath.c_str()); } } } catch ( ... ) { + // If conf is invalid, reset everything _configuration = Config(); + _myCertificate = CertificateOfMembership(); + _mcRates = MulticastRates(); + _staticAddresses.clear(); + _isOpen = false; + _lastConfigUpdate = 0; LOG("unexpected exception handling config for network %.16llx, retrying fetch...",(unsigned long long)_id); } @@ -209,9 +220,11 @@ void Network::setConfiguration(const Network::Config &conf) void Network::requestConfiguration() { if (controller() == _r->identity.address()) { + // FIXME: Right now the netconf master cannot be a member of its own nets LOG("unable to request network configuration for network %.16llx: I am the network master, cannot query self",(unsigned long long)_id); return; } + TRACE("requesting netconf for network %.16llx from netconf master %s",(unsigned long long)_id,controller().toString().c_str()); Packet outp(controller(),_r->identity.address(),Packet::VERB_NETWORK_CONFIG_REQUEST); outp.append((uint64_t)_id); @@ -222,7 +235,7 @@ void Network::requestConfiguration() void Network::addMembershipCertificate(const Address &peer,const CertificateOfMembership &cert) { Mutex::Lock _l(_lock); - if (!_configuration.isOpen()) + if (!_isOpen) _membershipCertificates[peer] = cert; } @@ -231,7 +244,7 @@ bool Network::isAllowed(const Address &peer) const // Exceptions can occur if we do not yet have *our* configuration. try { Mutex::Lock _l(_lock); - if (_configuration.isOpen()) + if (_isOpen) return true; std::map::const_iterator pc(_membershipCertificates.find(peer)); if (pc == _membershipCertificates.end()) @@ -249,9 +262,11 @@ void Network::clean() { std::string mcdbPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + idString() + ".mcerts"); + _multicaster.clean(Utils::now()); + Mutex::Lock _l(_lock); - if ((!_id)||(_configuration.isOpen())) { + if ((!_id)||(_isOpen)) { _membershipCertificates.clear(); Utils::rm(mcdbPath); } else { diff --git a/node/Network.hpp b/node/Network.hpp index dd6c52546..9663b9824 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -318,34 +318,6 @@ public: return get("desc",std::string()); } - /** - * @return Breadth for multicast propagation - */ - inline unsigned int multicastPropagationBreadth() const - { - const_iterator mcb(find("mcb")); - if (mcb == end()) - return ZT_MULTICAST_DEFAULT_PROPAGATION_BREADTH; - unsigned int mcb2 = Utils::hexStrToUInt(mcb->second.c_str()); - if (mcb2) - return mcb2; - return ZT_MULTICAST_DEFAULT_PROPAGATION_BREADTH; - } - - /** - * @return Depth for multicast propagation - */ - inline unsigned int multicastPropagationDepth() const - { - const_iterator mcd(find("mcd")); - if (mcd == end()) - return ZT_MULTICAST_DEFAULT_PROPAGATION_DEPTH; - unsigned int mcd2 = Utils::hexStrToUInt(mcd->second.c_str()); - if (mcd2) - return mcd2; - return ZT_MULTICAST_DEFAULT_PROPAGATION_DEPTH; - } - /** * @return Certificate of membership for this network, or empty cert if none */ @@ -462,6 +434,7 @@ private: * Causes all persistent disk presence to be erased on delete */ inline void destroyOnDelete() + throw() { _destroyOnDelete = true; } @@ -498,12 +471,8 @@ public: inline bool isOpen() const throw() { - try { - Mutex::Lock _l(_lock); - return _configuration.isOpen(); - } catch ( ... ) { - return false; - } + Mutex::Lock _l(_lock); + return _isOpen; } /** @@ -616,21 +585,12 @@ public: } /** - * @return Breadth for multicast rumor mill propagation + * @return Multicaster for this network */ - inline unsigned int multicastPropagationBreadth() const + inline Multicaster &multicaster() throw() { - return _multicastPropagationBreadth; - } - - /** - * @return Depth for multicast rumor mill propagation - */ - inline unsigned int multicastPropagationDepth() const - throw() - { - return _multicastPropagationDepth; + return _multicaster; } private: @@ -653,12 +613,15 @@ private: Config _configuration; CertificateOfMembership _myCertificate; // memoized from _configuration MulticastRates _mcRates; // memoized from _configuration - unsigned int _multicastPropagationBreadth; // memoized from _configuration - unsigned int _multicastPropagationDepth; // memoized from _configuration + std::set _staticAddresses; // memoized from _configuration + bool _isOpen; // memoized from _configuration // Ethertype whitelist bit field, set from config, for really fast lookup unsigned char _etWhitelist[65536 / 8]; + // Multicast propagation database + Multicaster _multicaster; + uint64_t _id; volatile uint64_t _lastConfigUpdate; volatile bool _destroyOnDelete; diff --git a/node/Packet.hpp b/node/Packet.hpp index a52c34092..cde381dd0 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -814,75 +814,68 @@ public: } /** - * Generate a message authenticationc code and set MAC field of packet - * - * For encrypted packets, this must be called after encryption. + * Armor packet for transport * - * @param key 256-bit (32 byte) key + * @param key 32-byte key + * @param encryptPayload If true, encrypt packet payload, else just MAC */ - inline void macSet(const void *key) + inline void armor(const void *key,bool encryptPayload) { + unsigned char mangledKey[32]; + unsigned char macKey[32]; unsigned char mac[16]; - unsigned char key2[32]; - _mangleKey((const unsigned char *)key,key2); - unsigned int macLen = (size() >= ZT_PACKET_IDX_VERB) ? (size() - ZT_PACKET_IDX_VERB) : 0; - Poly1305::compute(mac,field(ZT_PACKET_IDX_VERB,macLen),macLen,key2); + const unsigned int payloadLen = size() - ZT_PACKET_IDX_VERB; + unsigned char *const payload = field(ZT_PACKET_IDX_VERB,payloadLen); + + // Set flag now, since it affects key mangle function + if (encryptPayload) + (*this)[ZT_PACKET_IDX_FLAGS] |= (char)ZT_PROTO_FLAG_ENCRYPTED; + else (*this)[ZT_PACKET_IDX_FLAGS] &= (char)(~ZT_PROTO_FLAG_ENCRYPTED); + + _mangleKey((const unsigned char *)key,mangledKey); + Salsa20 s20(mangledKey,256,field(ZT_PACKET_IDX_IV,8)); + + // MAC key is always the first 32 bytes of the Salsa20 key stream + // This is the same technique DJB's NaCl library uses to use poly1305 + memset(macKey,0,sizeof(macKey)); + s20.encrypt(macKey,macKey,sizeof(macKey)); + + if (encryptPayload) + s20.encrypt(payload,payload,payloadLen); + + Poly1305::compute(mac,payload,payloadLen,macKey); memcpy(field(ZT_PACKET_IDX_MAC,8),mac,8); } /** - * Check the MAC of this packet's payload - * - * For encrypted packets, this must be checked before decryption. + * Verify and (if encrypted) decrypt packet * - * @param key 256-bit (32 byte) key + * @param key 32-byte key + * @return False if packet is invalid or failed MAC authenticity check */ - inline bool macVerify(const void *key) const + inline bool dearmor(const void *key) { + unsigned char mangledKey[32]; + unsigned char macKey[32]; unsigned char mac[16]; - unsigned char key2[32]; - if (size() < ZT_PACKET_IDX_VERB) - return false; // incomplete packets fail - _mangleKey((const unsigned char *)key,key2); - unsigned int macLen = size() - ZT_PACKET_IDX_VERB; - Poly1305::compute(mac,field(ZT_PACKET_IDX_VERB,macLen),macLen,key2); - return Utils::secureEq(mac,field(ZT_PACKET_IDX_MAC,8),8); - } + const unsigned int payloadLen = size() - ZT_PACKET_IDX_VERB; + unsigned char *const payload = field(ZT_PACKET_IDX_VERB,payloadLen); - /** - * Encrypt this packet - * - * @param key 256-bit (32 byte) key - */ - inline void encrypt(const void *key) - { - (*this)[ZT_PACKET_IDX_FLAGS] |= ZT_PROTO_FLAG_ENCRYPTED; - unsigned char key2[32]; - if (size() >= ZT_PACKET_IDX_VERB) { - _mangleKey((const unsigned char *)key,key2); - Salsa20 s20(key2,256,field(ZT_PACKET_IDX_IV,8)); - unsigned int encLen = size() - ZT_PACKET_IDX_VERB; - unsigned char *const encBuf = field(ZT_PACKET_IDX_VERB,encLen); - s20.encrypt(encBuf,encBuf,encLen); - } - } + _mangleKey((const unsigned char *)key,mangledKey); + Salsa20 s20(mangledKey,256,field(ZT_PACKET_IDX_IV,8)); - /** - * Decrypt this packet - * - * @param key 256-bit (32 byte) key - */ - inline void decrypt(const void *key) - { - unsigned char key2[32]; - if (size() >= ZT_PACKET_IDX_VERB) { - _mangleKey((const unsigned char *)key,key2); - Salsa20 s20(key2,256,field(ZT_PACKET_IDX_IV,8)); - unsigned int decLen = size() - ZT_PACKET_IDX_VERB; - unsigned char *const decBuf = field(ZT_PACKET_IDX_VERB,decLen); - s20.decrypt(decBuf,decBuf,decLen); + memset(macKey,0,sizeof(macKey)); + s20.encrypt(macKey,macKey,sizeof(macKey)); + Poly1305::compute(mac,payload,payloadLen,macKey); + if (!Utils::secureEq(mac,field(ZT_PACKET_IDX_MAC,8),8)) + return false; + + if (((*this)[ZT_PACKET_IDX_FLAGS] & (char)ZT_PROTO_FLAG_ENCRYPTED)) { + s20.decrypt(payload,payload,payloadLen); + (*this)[ZT_PACKET_IDX_FLAGS] &= (char)(~ZT_PROTO_FLAG_ENCRYPTED); } - (*this)[ZT_PACKET_IDX_FLAGS] &= (char)(~ZT_PROTO_FLAG_ENCRYPTED); + + return true; } /** @@ -932,7 +925,7 @@ public: memcpy(field(ZT_PACKET_IDX_PAYLOAD,(unsigned int)ucl),buf,ucl); } else return false; } - (*this)[ZT_PACKET_IDX_VERB] &= ~ZT_PROTO_VERB_FLAG_COMPRESSED; + (*this)[ZT_PACKET_IDX_VERB] &= (char)(~ZT_PROTO_VERB_FLAG_COMPRESSED); } return true; } @@ -946,11 +939,8 @@ private: */ inline void _mangleKey(const unsigned char *in,unsigned char *out) const { - // IV and source/destination addresses. Salsa uses the IV natively - // so this is redundant there, but not harmful. But Poly1305 depends - // on the key being mangled with the IV. Using the source and - // destination addresses bifurcates the key space into a different - // key space for each direction of the conversation. + // IV and source/destination addresses. Using the addresses divides the + // key space into two halves-- A->B and B->A (since order will change). for(unsigned int i=0;i<18;++i) // 8 + (ZT_ADDRESS_LENGTH * 2) == 18 out[i] = in[i] ^ (unsigned char)(*this)[i]; diff --git a/node/Peer.cpp b/node/Peer.cpp index 7fd33ce92..1d22fd1fb 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -54,7 +54,7 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity) _vRevision(0), _dirty(true) { - if (!myIdentity.agree(peerIdentity,_keys,sizeof(_keys))) + if (!myIdentity.agree(peerIdentity,_key,sizeof(_key))) throw std::runtime_error("new peer identity key agreement failed"); } diff --git a/node/Peer.hpp b/node/Peer.hpp index cab9319b7..38b93a323 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -52,7 +52,7 @@ * Max length of serialized peer record */ #define ZT_PEER_MAX_SERIALIZED_LENGTH ( \ - 64 + \ + 32 + \ ZT_IDENTITY_MAX_BINARY_SERIALIZED_LENGTH + \ ( ( \ (sizeof(uint64_t) * 4) + \ @@ -187,7 +187,7 @@ public: } /** - * @return Time of most recent unicast frame (actual data transferred) + * @return Time of most recent unicast frame received */ uint64_t lastUnicastFrame() const throw() @@ -196,7 +196,7 @@ public: } /** - * @return Time of most recent multicast frame + * @return Time of most recent multicast frame received */ uint64_t lastMulticastFrame() const throw() @@ -305,19 +305,10 @@ public: /** * @return 256-bit encryption key */ - inline const unsigned char *cryptKey() const + inline const unsigned char *key() const throw() { - return _keys; // crypt key is first 32-byte key - } - - /** - * @return 256-bit MAC (message authentication code) key - */ - inline const unsigned char *macKey() const - throw() - { - return (_keys + 32); // mac key is second 32-byte key + return _key; } /** @@ -369,8 +360,8 @@ public: inline void serialize(Buffer &b) throw(std::out_of_range) { - b.append((unsigned char)2); // version - b.append(_keys,sizeof(_keys)); + b.append((unsigned char)3); // version + b.append(_key,sizeof(_key)); _id.serialize(b,false); _ipv4p.serialize(b); _ipv6p.serialize(b); @@ -384,10 +375,10 @@ public: { unsigned int p = startAt; - if (b[p++] != 2) + if (b[p++] != 3) throw std::invalid_argument("Peer: deserialize(): version mismatch"); - memcpy(_keys,b.field(p,sizeof(_keys)),sizeof(_keys)); p += sizeof(_keys); + memcpy(_key,b.field(p,sizeof(_key)),sizeof(_key)); p += sizeof(_key); p += _id.deserialize(b,p); p += _ipv4p.deserialize(b,p); p += _ipv6p.deserialize(b,p); @@ -517,7 +508,7 @@ private: bool fixed; // do not learn address from received packets }; - unsigned char _keys[32 * 2]; // crypt key[32], mac key[32] + unsigned char _key[32]; // shared secret key agreed upon between identities Identity _id; WanPath _ipv4p;