Browse Source

Refactoring to eliminate duplicated code

Adam Ierymenko 6 years ago
parent
commit
e6b4006c70

+ 1 - 1
node/AtomicCounter.hpp

@@ -72,7 +72,7 @@ public:
 
 private:
 	inline AtomicCounter(const AtomicCounter &) {}
-	const AtomicCounter &operator=(const AtomicCounter &) { return *this; }
+	inline const AtomicCounter &operator=(const AtomicCounter &) { return *this; }
 
 #ifdef __GNUC__
 	int _v;

+ 1 - 5
node/CMakeLists.txt

@@ -52,8 +52,7 @@ set(core_src
 	AES.cpp
 	C25519.cpp
 	Capability.cpp
-	CertificateOfMembership.cpp
-	CertificateOfOwnership.cpp
+	Credential.cpp
 	ECC384.cpp
 	Identity.cpp
 	IncomingPacket.cpp
@@ -68,13 +67,10 @@ set(core_src
 	Path.cpp
 	Peer.cpp
 	Poly1305.cpp
-	Revocation.cpp
 	Salsa20.cpp
 	SelfAwareness.cpp
 	SHA512.cpp
 	Switch.cpp
-	Tag.cpp
-	Topology.cpp
 	Trace.cpp
 	Utils.cpp
 )

+ 0 - 17
node/Capability.cpp

@@ -34,23 +34,6 @@
 
 namespace ZeroTier {
 
-bool Capability::sign(const Identity &from,const Address &to)
-{
-	try {
-		for(unsigned int i=0;((i<_maxCustodyChainLength)&&(i<ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH));++i) {
-			if (!(_custody[i].to)) {
-				Buffer<(sizeof(Capability) * 2)> tmp;
-				this->serialize(tmp,true);
-				_custody[i].to = to;
-				_custody[i].from = from.address();
-				_custody[i].signatureLength = from.sign(tmp.data(),tmp.size(),_custody[i].signature,sizeof(_custody[i].signature));
-				return true;
-			}
-		}
-	} catch ( ... ) {}
-	return false;
-}
-
 int Capability::verify(const RuntimeEnvironment *RR,void *tPtr) const
 {
 	try {

+ 20 - 3
node/Capability.hpp

@@ -69,6 +69,8 @@ class RuntimeEnvironment;
  */
 class Capability : public Credential
 {
+	friend class Credential;
+
 public:
 	static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_CAPABILITY; }
 
@@ -154,8 +156,23 @@ public:
 	 * @param to Recipient of this signature
 	 * @return True if signature successful and chain of custody appended
 	 */
-	bool sign(const Identity &from,const Address &to);
-
+	inline bool sign(const Identity &from,const Address &to)
+	{
+		try {
+			for(unsigned int i=0;((i<_maxCustodyChainLength)&&(i<ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH));++i) {
+				if (!(_custody[i].to)) {
+					Buffer<(sizeof(Capability) * 2)> tmp;
+					this->serialize(tmp,true);
+					_custody[i].to = to;
+					_custody[i].from = from.address();
+					_custody[i].signatureLength = from.sign(tmp.data(),tmp.size(),_custody[i].signature,sizeof(_custody[i].signature));
+					return true;
+				}
+			}
+		} catch ( ... ) {}
+		return false;
+	}
+	
 	/**
 	 * Verify this capability's chain of custody and signatures
 	 *
@@ -163,7 +180,7 @@ public:
 	 * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or chain
 	 */
 	int verify(const RuntimeEnvironment *RR,void *tPtr) const;
-
+	
 	template<unsigned int C>
 	static inline void serializeRules(Buffer<C> &b,const ZT_VirtualNetworkRule *rules,unsigned int ruleCount)
 	{

+ 0 - 240
node/CertificateOfMembership.cpp

@@ -1,240 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2019  ZeroTier, Inc.  https://www.zerotier.com/
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * --
- *
- * You can be released from the requirements of the license by purchasing
- * a commercial license. Buying such a license is mandatory as soon as you
- * develop commercial closed-source software that incorporates or links
- * directly against ZeroTier software without disclosing the source code
- * of your own application.
- */
-
-#include "CertificateOfMembership.hpp"
-#include "RuntimeEnvironment.hpp"
-#include "Topology.hpp"
-#include "Switch.hpp"
-#include "Network.hpp"
-#include "Node.hpp"
-
-namespace ZeroTier {
-
-void CertificateOfMembership::setQualifier(uint64_t id,uint64_t value,uint64_t maxDelta)
-{
-	_signedBy.zero();
-
-	for(unsigned int i=0;i<_qualifierCount;++i) {
-		if (_qualifiers[i].id == id) {
-			_qualifiers[i].value = value;
-			_qualifiers[i].maxDelta = maxDelta;
-			return;
-		}
-	}
-
-	if (_qualifierCount < ZT_NETWORK_COM_MAX_QUALIFIERS) {
-		_qualifiers[_qualifierCount].id = id;
-		_qualifiers[_qualifierCount].value = value;
-		_qualifiers[_qualifierCount].maxDelta = maxDelta;
-		++_qualifierCount;
-		std::sort(&(_qualifiers[0]),&(_qualifiers[_qualifierCount]));
-	}
-}
-
-#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
-
-std::string CertificateOfMembership::toString() const
-{
-	char tmp[ZT_NETWORK_COM_MAX_QUALIFIERS * 32];
-	std::string s;
-
-	s.append("1:"); // COM_UINT64_ED25519
-
-	uint64_t *const buf = new uint64_t[_qualifierCount * 3];
-	try {
-		unsigned int ptr = 0;
-		for(unsigned int i=0;i<_qualifierCount;++i) {
-			buf[ptr++] = Utils::hton(_qualifiers[i].id);
-			buf[ptr++] = Utils::hton(_qualifiers[i].value);
-			buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta);
-		}
-		s.append(Utils::hex(buf,ptr * sizeof(uint64_t),tmp));
-		delete [] buf;
-	} catch ( ... ) {
-		delete [] buf;
-		throw;
-	}
-
-	s.push_back(':');
-
-	s.append(_signedBy.toString(tmp));
-
-	if (_signedBy) {
-		s.push_back(':');
-		s.append(Utils::hex(_signature,_signatureLength,tmp));
-	}
-
-	return s;
-}
-
-void CertificateOfMembership::fromString(const char *s)
-{
-	_signedBy.zero();
-	_qualifierCount = 0;
-	_signatureLength = 0;
-
-	if (!*s)
-		return;
-
-	unsigned int colonAt = 0;
-	while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt;
-
-	if (!((colonAt == 1)&&(s[0] == '1'))) // COM_UINT64_ED25519?
-		return;
-
-	s += colonAt + 1;
-	colonAt = 0;
-	while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt;
-
-	if (colonAt) {
-		const unsigned int buflen = colonAt / 2;
-		char *const buf = new char[buflen];
-		unsigned int bufactual = Utils::unhex(s,colonAt,buf,buflen);
-		char *bufptr = buf;
-		try {
-			while (bufactual >= 24) {
-				if (_qualifierCount < ZT_NETWORK_COM_MAX_QUALIFIERS) {
-					_qualifiers[_qualifierCount].id = Utils::ntoh(*((uint64_t *)bufptr)); bufptr += 8;
-					_qualifiers[_qualifierCount].value = Utils::ntoh(*((uint64_t *)bufptr)); bufptr += 8;
-					_qualifiers[_qualifierCount].maxDelta = Utils::ntoh(*((uint64_t *)bufptr)); bufptr += 8;
-					++_qualifierCount;
-				} else {
-					bufptr += 24;
-				}
-				bufactual -= 24;
-			}
-		} catch ( ... ) {}
-		delete [] buf;
-	}
-
-	if (s[colonAt]) {
-		s += colonAt + 1;
-		colonAt = 0;
-		while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt;
-
-		if (colonAt) {
-			char addrbuf[ZT_ADDRESS_LENGTH];
-			if (Utils::unhex(s,colonAt,addrbuf,sizeof(addrbuf)) == ZT_ADDRESS_LENGTH)
-				_signedBy.setTo(addrbuf,ZT_ADDRESS_LENGTH);
-
-			if ((_signedBy)&&(s[colonAt])) {
-				s += colonAt + 1;
-				colonAt = 0;
-				while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt;
-				if (colonAt) {
-					_signatureLength = Utils::unhex(s,colonAt,_signature,sizeof(_signature));
-				} else {
-					_signedBy.zero();
-				}
-			} else {
-				_signedBy.zero();
-			}
-		}
-	}
-
-	std::sort(&(_qualifiers[0]),&(_qualifiers[_qualifierCount]));
-}
-
-#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
-
-bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other) const
-{
-	unsigned int myidx = 0;
-	unsigned int otheridx = 0;
-
-	if ((_qualifierCount == 0)||(other._qualifierCount == 0))
-		return false;
-
-	while (myidx < _qualifierCount) {
-		// Fail if we're at the end of other, since this means the field is
-		// missing.
-		if (otheridx >= other._qualifierCount)
-			return false;
-
-		// Seek to corresponding tuple in other, ignoring tuples that
-		// we may not have. If we run off the end of other, the tuple is
-		// missing. This works because tuples are sorted by ID.
-		while (other._qualifiers[otheridx].id != _qualifiers[myidx].id) {
-			++otheridx;
-			if (otheridx >= other._qualifierCount)
-				return false;
-		}
-
-		// Compare to determine if the absolute value of the difference
-		// between these two parameters is within our maxDelta.
-		const uint64_t a = _qualifiers[myidx].value;
-		const uint64_t b = other._qualifiers[myidx].value;
-		if (((a >= b) ? (a - b) : (b - a)) > _qualifiers[myidx].maxDelta)
-			return false;
-
-		++myidx;
-	}
-
-	return true;
-}
-
-bool CertificateOfMembership::sign(const Identity &with)
-{
-	uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3];
-	unsigned int ptr = 0;
-	for(unsigned int i=0;i<_qualifierCount;++i) {
-		buf[ptr++] = Utils::hton(_qualifiers[i].id);
-		buf[ptr++] = Utils::hton(_qualifiers[i].value);
-		buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta);
-	}
-
-	try {
-		_signatureLength = with.sign(buf,ptr * sizeof(uint64_t),_signature,sizeof(_signature));
-		_signedBy = with.address();
-		return true;
-	} catch ( ... ) {
-		_signedBy.zero();
-		return false;
-	}
-}
-
-int CertificateOfMembership::verify(const RuntimeEnvironment *RR,void *tPtr) const
-{
-	if ((!_signedBy)||(_signedBy != Network::controllerFor(networkId()))||(_qualifierCount > ZT_NETWORK_COM_MAX_QUALIFIERS))
-		return -1;
-
-	const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
-	if (!id) {
-		RR->sw->requestWhois(tPtr,RR->node->now(),_signedBy);
-		return 1;
-	}
-
-	uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3];
-	unsigned int ptr = 0;
-	for(unsigned int i=0;i<_qualifierCount;++i) {
-		buf[ptr++] = Utils::hton(_qualifiers[i].id);
-		buf[ptr++] = Utils::hton(_qualifiers[i].value);
-		buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta);
-	}
-	return (id.verify(buf,ptr * sizeof(uint64_t),_signature,_signatureLength) ? 0 : -1);
-}
-
-} // namespace ZeroTier

+ 77 - 24
node/CertificateOfMembership.hpp

@@ -79,6 +79,8 @@ class RuntimeEnvironment;
  */
 class CertificateOfMembership : public Credential
 {
+	friend class Credential;
+
 public:
 	static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_COM; }
 
@@ -168,7 +170,7 @@ public:
 	{
 		for(unsigned int i=0;i<_qualifierCount;++i) {
 			if (_qualifiers[i].id == COM_RESERVED_ID_TIMESTAMP)
-				return _qualifiers[i].value;
+				return (int64_t)_qualifiers[i].value;
 		}
 		return 0;
 	}
@@ -206,26 +208,26 @@ public:
 	 * @param value Qualifier value
 	 * @param maxDelta Qualifier maximum allowed difference (absolute value of difference)
 	 */
-	void setQualifier(uint64_t id,uint64_t value,uint64_t maxDelta);
-	inline void setQualifier(ReservedId id,uint64_t value,uint64_t maxDelta) { setQualifier((uint64_t)id,value,maxDelta); }
-
-#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
-	/**
-	 * @return String-serialized representation of this certificate
-	 */
-	std::string toString() const;
+	inline void setQualifier(uint64_t id,uint64_t value,uint64_t maxDelta)
+	{
+		_signedBy.zero();
+		for(unsigned int i=0;i<_qualifierCount;++i) {
+			if (_qualifiers[i].id == id) {
+				_qualifiers[i].value = value;
+				_qualifiers[i].maxDelta = maxDelta;
+				return;
+			}
+		}
+		if (_qualifierCount < ZT_NETWORK_COM_MAX_QUALIFIERS) {
+			_qualifiers[_qualifierCount].id = id;
+			_qualifiers[_qualifierCount].value = value;
+			_qualifiers[_qualifierCount].maxDelta = maxDelta;
+			++_qualifierCount;
+			std::sort(&(_qualifiers[0]),&(_qualifiers[_qualifierCount]));
+		}
+	}
 
-	/**
-	 * Set this certificate equal to the hex-serialized string
-	 *
-	 * Invalid strings will result in invalid or undefined certificate
-	 * contents. These will subsequently fail validation and comparison.
-	 * Empty strings will result in an empty certificate.
-	 *
-	 * @param s String to deserialize
-	 */
-	void fromString(const char *s);
-#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
+	inline void setQualifier(ReservedId id,uint64_t value,uint64_t maxDelta) { setQualifier((uint64_t)id,value,maxDelta); }
 
 	/**
 	 * Compare two certificates for parameter agreement
@@ -240,7 +242,41 @@ public:
 	 * @param other Cert to compare with
 	 * @return True if certs agree and 'other' may be communicated with
 	 */
-	bool agreesWith(const CertificateOfMembership &other) const;
+	inline bool agreesWith(const CertificateOfMembership &other) const
+	{
+		unsigned int myidx = 0;
+		unsigned int otheridx = 0;
+	
+		if ((_qualifierCount == 0)||(other._qualifierCount == 0))
+			return false;
+	
+		while (myidx < _qualifierCount) {
+			// Fail if we're at the end of other, since this means the field is
+			// missing.
+			if (otheridx >= other._qualifierCount)
+				return false;
+	
+			// Seek to corresponding tuple in other, ignoring tuples that
+			// we may not have. If we run off the end of other, the tuple is
+			// missing. This works because tuples are sorted by ID.
+			while (other._qualifiers[otheridx].id != _qualifiers[myidx].id) {
+				++otheridx;
+				if (otheridx >= other._qualifierCount)
+					return false;
+			}
+	
+			// Compare to determine if the absolute value of the difference
+			// between these two parameters is within our maxDelta.
+			const uint64_t a = _qualifiers[myidx].value;
+			const uint64_t b = other._qualifiers[myidx].value;
+			if (((a >= b) ? (a - b) : (b - a)) > _qualifiers[myidx].maxDelta)
+				return false;
+	
+			++myidx;
+		}
+	
+		return true;
+	}
 
 	/**
 	 * Sign this certificate
@@ -248,16 +284,33 @@ public:
 	 * @param with Identity to sign with, must include private key
 	 * @return True if signature was successful
 	 */
-	bool sign(const Identity &with);
+	inline bool sign(const Identity &with)
+	{
+		uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3];
+		unsigned int ptr = 0;
+		for(unsigned int i=0;i<_qualifierCount;++i) {
+			buf[ptr++] = Utils::hton(_qualifiers[i].id);
+			buf[ptr++] = Utils::hton(_qualifiers[i].value);
+			buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta);
+		}
+	
+		try {
+			_signatureLength = with.sign(buf,ptr * sizeof(uint64_t),_signature,sizeof(_signature));
+			_signedBy = with.address();
+			return true;
+		} catch ( ... ) {
+			_signedBy.zero();
+			return false;
+		}
+	}
 
 	/**
 	 * Verify this COM and its signature
 	 *
 	 * @param RR Runtime environment for looking up peers
 	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
-	 * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
 	 */
-	int verify(const RuntimeEnvironment *RR,void *tPtr) const;
+	inline Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const { return _verify(RR,tPtr,*this); }
 
 	/**
 	 * @return True if signed

+ 0 - 98
node/CertificateOfOwnership.cpp

@@ -1,98 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2019  ZeroTier, Inc.  https://www.zerotier.com/
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * --
- *
- * You can be released from the requirements of the license by purchasing
- * a commercial license. Buying such a license is mandatory as soon as you
- * develop commercial closed-source software that incorporates or links
- * directly against ZeroTier software without disclosing the source code
- * of your own application.
- */
-
-#include "CertificateOfOwnership.hpp"
-#include "RuntimeEnvironment.hpp"
-#include "Identity.hpp"
-#include "Topology.hpp"
-#include "Switch.hpp"
-#include "Network.hpp"
-#include "Node.hpp"
-
-namespace ZeroTier {
-
-void CertificateOfOwnership::addThing(const InetAddress &ip)
-{
-	if (_thingCount >= ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) return;
-	if (ip.ss_family == AF_INET) {
-		_thingTypes[_thingCount] = THING_IPV4_ADDRESS;
-		memcpy(_thingValues[_thingCount],&(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr),4);
-		++_thingCount;
-	} else if (ip.ss_family == AF_INET6) {
-		_thingTypes[_thingCount] = THING_IPV6_ADDRESS;
-		memcpy(_thingValues[_thingCount],reinterpret_cast<const struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
-		++_thingCount;
-	}
-}
-
-bool CertificateOfOwnership::sign(const Identity &signer)
-{
-	if (signer.hasPrivate()) {
-		Buffer<sizeof(CertificateOfOwnership) + 64> tmp;
-		_signedBy = signer.address();
-		this->serialize(tmp,true);
-		_signatureLength = signer.sign(tmp.data(),tmp.size(),_signature,sizeof(_signature));
-		return true;
-	}
-	return false;
-}
-
-int CertificateOfOwnership::verify(const RuntimeEnvironment *RR,void *tPtr) const
-{
-	if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
-		return -1;
-	const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
-	if (!id) {
-		RR->sw->requestWhois(tPtr,RR->node->now(),_signedBy);
-		return 1;
-	}
-	try {
-		Buffer<(sizeof(CertificateOfOwnership) + 64)> tmp;
-		this->serialize(tmp,true);
-		return (id.verify(tmp.data(),tmp.size(),_signature,_signatureLength) ? 0 : -1);
-	} catch ( ... ) {
-		return -1;
-	}
-}
-
-bool CertificateOfOwnership::_owns(const CertificateOfOwnership::Thing &t,const void *v,unsigned int l) const
-{
-	for(unsigned int i=0,j=_thingCount;i<j;++i) {
-		if (_thingTypes[i] == (uint8_t)t) {
-			unsigned int k = 0;
-			while (k < l) {
-				if (reinterpret_cast<const uint8_t *>(v)[k] != _thingValues[i][k])
-					break;
-				++k;
-			}
-			if (k == l)
-				return true;
-		}
-	}
-	return false;
-}
-
-} // namespace ZeroTier

+ 48 - 11
node/CertificateOfOwnership.hpp

@@ -56,6 +56,8 @@ class RuntimeEnvironment;
  */
 class CertificateOfOwnership : public Credential
 {
+	friend class Credential;
+
 public:
 	static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_COO; }
 
@@ -85,6 +87,9 @@ public:
 	inline int64_t timestamp() const { return _ts; }
 	inline uint32_t id() const { return _id; }
 	inline unsigned int thingCount() const { return (unsigned int)_thingCount; }
+	inline const Address &signer() const { return _signedBy; }
+	inline const uint8_t *signature() const { return _signature; }
+	inline unsigned int signatureLength() const { return _signatureLength; }
 
 	inline Thing thingType(const unsigned int i) const { return (Thing)_thingTypes[i]; }
 	inline const uint8_t *thingValue(const unsigned int i) const { return _thingValues[i]; }
@@ -107,8 +112,20 @@ public:
 		return this->_owns(THING_MAC_ADDRESS,tmp,6);
 	}
 
-	void addThing(const InetAddress &ip);
-
+	inline void addThing(const InetAddress &ip)
+	{
+		if (_thingCount >= ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) return;
+		if (ip.ss_family == AF_INET) {
+			_thingTypes[_thingCount] = THING_IPV4_ADDRESS;
+			memcpy(_thingValues[_thingCount],&(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr),4);
+			++_thingCount;
+		} else if (ip.ss_family == AF_INET6) {
+			_thingTypes[_thingCount] = THING_IPV6_ADDRESS;
+			memcpy(_thingValues[_thingCount],reinterpret_cast<const struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
+			++_thingCount;
+		}
+	}
+	
 	inline void addThing(const MAC &mac)
 	{
 		if (_thingCount >= ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) return;
@@ -121,14 +138,19 @@ public:
 	 * @param signer Signing identity, must have private key
 	 * @return True if signature was successful
 	 */
-	bool sign(const Identity &signer);
+	inline bool sign(const Identity &signer)
+	{
+		if (signer.hasPrivate()) {
+			Buffer<sizeof(CertificateOfOwnership) + 64> tmp;
+			_signedBy = signer.address();
+			this->serialize(tmp,true);
+			_signatureLength = signer.sign(tmp.data(),tmp.size(),_signature,sizeof(_signature));
+			return true;
+		}
+		return false;
+	}
 
-	/**
-	 * @param RR Runtime environment to allow identity lookup for signedBy
-	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
-	 * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature
-	 */
-	int verify(const RuntimeEnvironment *RR,void *tPtr) const;
+	inline Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const { return _verify(RR,tPtr,*this); }
 
 	template<unsigned int C>
 	inline void serialize(Buffer<C> &b,const bool forSign = false) const
@@ -204,8 +226,23 @@ public:
 	inline bool operator!=(const CertificateOfOwnership &coo) const { return (memcmp(this,&coo,sizeof(CertificateOfOwnership)) != 0); }
 
 private:
-	bool _owns(const Thing &t,const void *v,unsigned int l) const;
-
+	inline bool _owns(const Thing &t,const void *v,unsigned int l) const
+	{
+		for(unsigned int i=0,j=_thingCount;i<j;++i) {
+			if (_thingTypes[i] == (uint8_t)t) {
+				unsigned int k = 0;
+				while (k < l) {
+					if (reinterpret_cast<const uint8_t *>(v)[k] != _thingValues[i][k])
+						break;
+					++k;
+				}
+				if (k == l)
+					return true;
+			}
+		}
+		return false;
+	}
+	
 	uint64_t _networkId;
 	int64_t _ts;
 	uint64_t _flags;

+ 88 - 0
node/Credential.cpp

@@ -0,0 +1,88 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2019  ZeroTier, Inc.  https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#include "Constants.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Credential.hpp"
+#include "Capability.hpp"
+#include "Tag.hpp"
+#include "CertificateOfMembership.hpp"
+#include "CertificateOfOwnership.hpp"
+#include "Revocation.hpp"
+#include "Switch.hpp"
+#include "Network.hpp"
+
+namespace ZeroTier {
+
+template<typename CRED>
+static inline Credential::VerifyResult _credVerify(const RuntimeEnvironment *const RR,void *tPtr,CRED credential)
+{
+	const Address signedBy(credential.signer());
+	const uint64_t networkId = credential.networkId();
+	if ((!signedBy)||(signedBy != Network::controllerFor(networkId)))
+		return Credential::VERIFY_BAD_SIGNATURE;
+	const Identity id(RR->topology->getIdentity(tPtr,signedBy));
+	if (!id) {
+		RR->sw->requestWhois(tPtr,RR->node->now(),signedBy);
+		return Credential::VERIFY_NEED_IDENTITY;
+	}
+	try {
+		Buffer<(sizeof(CRED) + 64)> *const tmp = new Buffer<(sizeof(CRED) + 64)>();
+		credential.serialize(*tmp,true);
+		const Credential::VerifyResult result = (id.verify(tmp->data(),tmp->size(),credential.signature(),credential.signatureLength()) ? Credential::VERIFY_OK : Credential::VERIFY_BAD_SIGNATURE);
+		delete tmp;
+		return result;
+	} catch ( ... ) {}
+	return Credential::VERIFY_BAD_SIGNATURE;
+}
+
+Credential::VerifyResult Credential::_verify(const RuntimeEnvironment *const RR,void *tPtr,const Revocation &credential) const { return _credVerify(RR,tPtr,credential); }
+Credential::VerifyResult Credential::_verify(const RuntimeEnvironment *const RR,void *tPtr,const Tag &credential) const { return _credVerify(RR,tPtr,credential); }
+Credential::VerifyResult Credential::_verify(const RuntimeEnvironment *const RR,void *tPtr,const CertificateOfOwnership &credential) const { return _credVerify(RR,tPtr,credential); }
+
+Credential::VerifyResult Credential::_verify(const RuntimeEnvironment *const RR,void *tPtr,const CertificateOfMembership &credential) const
+{
+	if ((!credential._signedBy)||(credential._signedBy != Network::controllerFor(credential.networkId()))||(credential._qualifierCount > ZT_NETWORK_COM_MAX_QUALIFIERS))
+		return Credential::VERIFY_BAD_SIGNATURE;
+
+	const Identity id(RR->topology->getIdentity(tPtr,credential._signedBy));
+	if (!id) {
+		RR->sw->requestWhois(tPtr,RR->node->now(),credential._signedBy);
+		return Credential::VERIFY_NEED_IDENTITY;
+	}
+
+	uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3];
+	unsigned int ptr = 0;
+	for(unsigned int i=0;i<credential._qualifierCount;++i) {
+		buf[ptr++] = Utils::hton(credential._qualifiers[i].id);
+		buf[ptr++] = Utils::hton(credential._qualifiers[i].value);
+		buf[ptr++] = Utils::hton(credential._qualifiers[i].maxDelta);
+	}
+
+	return (id.verify(buf,ptr * sizeof(uint64_t),credential._signature,credential._signatureLength) ? Credential::VERIFY_OK : Credential::VERIFY_BAD_SIGNATURE);
+}
+
+} // namespace ZeroTier

+ 23 - 0
node/Credential.hpp

@@ -40,6 +40,13 @@
 
 namespace ZeroTier {
 
+class Capability;
+class Revocation;
+class Tag;
+class CertificateOfMembership;
+class CertificateOfOwnership;
+class RuntimeEnvironment;
+
 /**
  * Base class for credentials
  */
@@ -58,6 +65,22 @@ public:
 		CREDENTIAL_TYPE_COO = 4,        // CertificateOfOwnership
 		CREDENTIAL_TYPE_REVOCATION = 6
 	};
+
+	/**
+	 * Result of verify() operations
+	 */
+	enum VerifyResult
+	{
+		VERIFY_OK = 0,
+		VERIFY_BAD_SIGNATURE = 1,
+		VERIFY_NEED_IDENTITY = 2
+	};
+
+protected:
+	VerifyResult _verify(const RuntimeEnvironment *const RR,void *tPtr,const CertificateOfMembership &credential) const;
+	VerifyResult _verify(const RuntimeEnvironment *const RR,void *tPtr,const Revocation &credential) const;
+	VerifyResult _verify(const RuntimeEnvironment *const RR,void *tPtr,const Tag &credential) const;
+	VerifyResult _verify(const RuntimeEnvironment *const RR,void *tPtr,const CertificateOfOwnership &credential) const;
 };
 
 } // namespace ZeroTier

+ 5 - 5
node/Membership.cpp

@@ -132,13 +132,13 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
 		return ADD_ACCEPTED_REDUNDANT;
 
 	switch(com.verify(RR,tPtr)) {
-		default:
-			RR->t->credentialRejected(tPtr,com,"invalid");
-			return ADD_REJECTED;
-		case 0:
+		case Credential::VERIFY_OK:
 			_com = com;
 			return ADD_ACCEPTED_NEW;
-		case 1:
+		case Credential::VERIFY_BAD_SIGNATURE:
+			RR->t->credentialRejected(tPtr,com,"invalid");
+			return ADD_REJECTED;
+		case Credential::VERIFY_NEED_IDENTITY:
 			return ADD_DEFERRED_FOR_WHOIS;
 	}
 }

+ 0 - 67
node/Revocation.cpp

@@ -1,67 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2019  ZeroTier, Inc.  https://www.zerotier.com/
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * --
- *
- * You can be released from the requirements of the license by purchasing
- * a commercial license. Buying such a license is mandatory as soon as you
- * develop commercial closed-source software that incorporates or links
- * directly against ZeroTier software without disclosing the source code
- * of your own application.
- */
-
-#include "Revocation.hpp"
-#include "RuntimeEnvironment.hpp"
-#include "Identity.hpp"
-#include "Topology.hpp"
-#include "Switch.hpp"
-#include "Network.hpp"
-#include "Node.hpp"
-
-namespace ZeroTier {
-
-bool Revocation::sign(const Identity &signer)
-{
-	if (signer.hasPrivate()) {
-		Buffer<sizeof(Revocation) + 64> tmp;
-		_signedBy = signer.address();
-		this->serialize(tmp,true);
-		_signatureLength = signer.sign(tmp.data(),tmp.size(),_signature,sizeof(_signature));
-		return true;
-	}
-	return false;
-}
-
-int Revocation::verify(const RuntimeEnvironment *RR,void *tPtr) const
-{
-	if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
-		return -1;
-	const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
-	if (!id) {
-		RR->sw->requestWhois(tPtr,RR->node->now(),_signedBy);
-		return 1;
-	}
-	try {
-		Buffer<sizeof(Revocation) + 64> tmp;
-		this->serialize(tmp,true);
-		return (id.verify(tmp.data(),tmp.size(),_signature,_signatureLength) ? 0 : -1);
-	} catch ( ... ) {
-		return -1;
-	}
-}
-
-} // namespace ZeroTier

+ 16 - 3
node/Revocation.hpp

@@ -55,6 +55,8 @@ class RuntimeEnvironment;
  */
 class Revocation : public Credential
 {
+	friend class Credential;
+
 public:
 	static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_REVOCATION; }
 
@@ -100,6 +102,8 @@ public:
 	inline const Address &target() const { return _target; }
 	inline const Address &signer() const { return _signedBy; }
 	inline Credential::Type type() const { return _type; }
+	inline const uint8_t *signature() const { return _signature; }
+	inline unsigned int signatureLength() const { return _signatureLength; }
 
 	inline bool fastPropagate() const { return ((_flags & ZT_REVOCATION_FLAG_FAST_PROPAGATE) != 0); }
 
@@ -107,16 +111,25 @@ public:
 	 * @param signer Signing identity, must have private key
 	 * @return True if signature was successful
 	 */
-	bool sign(const Identity &signer);
+	inline bool sign(const Identity &signer)
+	{
+		if (signer.hasPrivate()) {
+			Buffer<sizeof(Revocation) + 64> tmp;
+			_signedBy = signer.address();
+			this->serialize(tmp,true);
+			_signatureLength = signer.sign(tmp.data(),tmp.size(),_signature,sizeof(_signature));
+			return true;
+		}
+		return false;
+	}
 
 	/**
 	 * Verify this revocation's signature
 	 *
 	 * @param RR Runtime environment to provide for peer lookup, etc.
 	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
-	 * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or chain
 	 */
-	int verify(const RuntimeEnvironment *RR,void *tPtr) const;
+	inline Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const { return _verify(RR,tPtr,*this); }
 
 	template<unsigned int C>
 	inline void serialize(Buffer<C> &b,const bool forSign = false) const

+ 0 - 67
node/Tag.cpp

@@ -1,67 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2019  ZeroTier, Inc.  https://www.zerotier.com/
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * --
- *
- * You can be released from the requirements of the license by purchasing
- * a commercial license. Buying such a license is mandatory as soon as you
- * develop commercial closed-source software that incorporates or links
- * directly against ZeroTier software without disclosing the source code
- * of your own application.
- */
-
-#include "Tag.hpp"
-#include "RuntimeEnvironment.hpp"
-#include "Identity.hpp"
-#include "Topology.hpp"
-#include "Switch.hpp"
-#include "Network.hpp"
-#include "Node.hpp"
-
-namespace ZeroTier {
-
-bool Tag::sign(const Identity &signer)
-{
-	if (signer.hasPrivate()) {
-		Buffer<sizeof(Tag) + 64> tmp;
-		_signedBy = signer.address();
-		this->serialize(tmp,true);
-		_signatureLength = signer.sign(tmp.data(),tmp.size(),_signature,sizeof(_signature));
-		return true;
-	}
-	return false;
-}
-
-int Tag::verify(const RuntimeEnvironment *RR,void *tPtr) const
-{
-	if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
-		return -1;
-	const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
-	if (!id) {
-		RR->sw->requestWhois(tPtr,RR->node->now(),_signedBy);
-		return 1;
-	}
-	try {
-		Buffer<(sizeof(Tag) * 2)> tmp;
-		this->serialize(tmp,true);
-		return (id.verify(tmp.data(),tmp.size(),_signature,_signatureLength) ? 0 : -1);
-	} catch ( ... ) {
-		return -1;
-	}
-}
-
-} // namespace ZeroTier

+ 18 - 5
node/Tag.hpp

@@ -62,6 +62,8 @@ class RuntimeEnvironment;
  */
 class Tag : public Credential
 {
+	friend class Credential;
+
 public:
 	static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_TAG; }
 
@@ -97,7 +99,9 @@ public:
 	inline uint64_t networkId() const { return _networkId; }
 	inline int64_t timestamp() const { return _ts; }
 	inline const Address &issuedTo() const { return _issuedTo; }
-	inline const Address &signedBy() const { return _signedBy; }
+	inline const Address &signer() const { return _signedBy; }
+	inline const uint8_t *signature() const { return _signature; }
+	inline unsigned int signatureLength() const { return _signatureLength; }
 
 	/**
 	 * Sign this tag
@@ -105,16 +109,25 @@ public:
 	 * @param signer Signing identity, must have private key
 	 * @return True if signature was successful
 	 */
-	bool sign(const Identity &signer);
-
+	inline bool sign(const Identity &signer)
+	{
+		if (signer.hasPrivate()) {
+			Buffer<sizeof(Tag) + 64> tmp;
+			_signedBy = signer.address();
+			this->serialize(tmp,true);
+			_signatureLength = signer.sign(tmp.data(),tmp.size(),_signature,sizeof(_signature));
+			return true;
+		}
+		return false;
+	}
+	
 	/**
 	 * Check this tag's signature
 	 *
 	 * @param RR Runtime environment to allow identity lookup for signedBy
 	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
-	 * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or tag
 	 */
-	int verify(const RuntimeEnvironment *RR,void *tPtr) const;
+	inline Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const { return _verify(RR,tPtr,*this); }
 
 	template<unsigned int C>
 	inline void serialize(Buffer<C> &b,const bool forSign = false) const

+ 1 - 4
objects.mk

@@ -2,8 +2,7 @@ CORE_OBJS=\
 	node/AES.o \
 	node/C25519.o \
 	node/Capability.o \
-	node/CertificateOfMembership.o \
-	node/CertificateOfOwnership.o \
+	node/Credential.o \
 	node/ECC384.o \
 	node/Identity.o \
 	node/IncomingPacket.o \
@@ -18,12 +17,10 @@ CORE_OBJS=\
 	node/Path.o \
 	node/Peer.o \
 	node/Poly1305.o \
-	node/Revocation.o \
 	node/Salsa20.o \
 	node/SelfAwareness.o \
 	node/SHA512.o \
 	node/Switch.o \
-	node/Tag.o \
 	node/Trace.o \
 	node/Utils.o