Browse Source

Interim commit of some cert and cert testing work, also other cleanup in Utils.

Adam Ierymenko 5 years ago
parent
commit
73d0e2e7e0

+ 8 - 0
cmd/zerotier/cli/help.go

@@ -80,6 +80,14 @@ Commands:
     sign <identity> <file>               Sign a file with an identity's key
     sign <identity> <file>               Sign a file with an identity's key
     verify <identity> <file> <sig>       Verify a signature
     verify <identity> <file> <sig>       Verify a signature
   certificate <command> [args]         - Certificate commands
   certificate <command> [args]         - Certificate commands
+    newid                                Create a new unique subject ID
+    newcsr <settings>                    Create a new CSR (signing request)
+    sign <crl path> <identity path>      Sign a CRL and create a certificate
+    verify <certificate>                 Verify a certificate
+    show                                 List certificate for current node
+    import <certificate> [<trust>]       Import certificate into this node
+    export <serial>                      Export a certificate from this node
+    delete <serial>                      Delete certificate from this node
 
 
 An <address> may be specified as a 10-digit short ZeroTier address, a
 An <address> may be specified as a 10-digit short ZeroTier address, a
 fingerprint containing both an address and a SHA384 hash, or an identity.
 fingerprint containing both an address and a SHA384 hash, or an identity.

+ 128 - 61
core/Certificate.cpp

@@ -30,6 +30,7 @@ void Certificate::clear()
 	m_subjectNetworks.clear();
 	m_subjectNetworks.clear();
 	m_updateUrls.clear();
 	m_updateUrls.clear();
 	m_subjectCertificates.clear();
 	m_subjectCertificates.clear();
+	m_extendedAttributes.clear();
 }
 }
 
 
 Certificate &Certificate::operator=(const ZT_Certificate &apiCert)
 Certificate &Certificate::operator=(const ZT_Certificate &apiCert)
@@ -45,16 +46,23 @@ Certificate &Certificate::operator=(const Certificate &cert)
 
 
 	// Zero these since we must explicitly attach all the objects from
 	// Zero these since we must explicitly attach all the objects from
 	// the other certificate to copy them into our containers.
 	// the other certificate to copy them into our containers.
+	this->subject.identities = nullptr;
 	this->subject.identityCount = 0;
 	this->subject.identityCount = 0;
+	this->subject.networks = nullptr;
 	this->subject.networkCount = 0;
 	this->subject.networkCount = 0;
+	this->subject.certificates = nullptr;
 	this->subject.certificateCount = 0;
 	this->subject.certificateCount = 0;
+	this->subject.updateUrls = nullptr;
 	this->subject.updateUrlCount = 0;
 	this->subject.updateUrlCount = 0;
+	this->extendedAttributes = nullptr;
+	this->extendedAttributesSize = 0;
+	this->issuer = nullptr;
 
 
 	for (unsigned int i = 0; i < cert.subject.identityCount; ++i) {
 	for (unsigned int i = 0; i < cert.subject.identityCount; ++i) {
 		if (cert.subject.identities[i].identity) {
 		if (cert.subject.identities[i].identity) {
 			if (cert.subject.identities[i].locator)
 			if (cert.subject.identities[i].locator)
-				addSubjectNode(*reinterpret_cast<const Identity *>(cert.subject.identities[i].identity), *reinterpret_cast<const Locator *>(cert.subject.identities[i].locator));
-			else addSubjectNode(*reinterpret_cast<const Identity *>(cert.subject.identities[i].identity));
+				addSubjectIdentity(*reinterpret_cast<const Identity *>(cert.subject.identities[i].identity), *reinterpret_cast<const Locator *>(cert.subject.identities[i].locator));
+			else addSubjectIdentity(*reinterpret_cast<const Identity *>(cert.subject.identities[i].identity));
 		}
 		}
 	}
 	}
 
 
@@ -71,10 +79,16 @@ Certificate &Certificate::operator=(const Certificate &cert)
 	if (cert.subject.updateUrls) {
 	if (cert.subject.updateUrls) {
 		for (unsigned int i = 0; i < cert.subject.updateUrlCount; ++i) {
 		for (unsigned int i = 0; i < cert.subject.updateUrlCount; ++i) {
 			if (cert.subject.updateUrls[i])
 			if (cert.subject.updateUrls[i])
-				addUpdateUrl(cert.subject.updateUrls[i]);
+				addSubjectUpdateUrl(cert.subject.updateUrls[i]);
 		}
 		}
 	}
 	}
 
 
+	if ((cert.extendedAttributes) && (cert.extendedAttributesSize > 0)) {
+		m_extendedAttributes.assign(cert.extendedAttributes, cert.extendedAttributes + cert.extendedAttributesSize);
+		this->extendedAttributes = m_extendedAttributes.data();
+		this->extendedAttributesSize = (unsigned int)m_extendedAttributes.size();
+	}
+
 	if (cert.issuer) {
 	if (cert.issuer) {
 		m_identities.push_back(*reinterpret_cast<const Identity *>(cert.issuer));
 		m_identities.push_back(*reinterpret_cast<const Identity *>(cert.issuer));
 		this->issuer = &(m_identities.back());
 		this->issuer = &(m_identities.back());
@@ -83,7 +97,7 @@ Certificate &Certificate::operator=(const Certificate &cert)
 	return *this;
 	return *this;
 }
 }
 
 
-ZT_Certificate_Identity *Certificate::addSubjectNode(const Identity &id)
+ZT_Certificate_Identity *Certificate::addSubjectIdentity(const Identity &id)
 {
 {
 	// Enlarge array of ZT_Certificate_Identity structs and set pointer to potentially reallocated array.
 	// Enlarge array of ZT_Certificate_Identity structs and set pointer to potentially reallocated array.
 	m_subjectIdentities.resize(++this->subject.identityCount);
 	m_subjectIdentities.resize(++this->subject.identityCount);
@@ -99,10 +113,10 @@ ZT_Certificate_Identity *Certificate::addSubjectNode(const Identity &id)
 	return &(m_subjectIdentities.back());
 	return &(m_subjectIdentities.back());
 }
 }
 
 
-ZT_Certificate_Identity *Certificate::addSubjectNode(const Identity &id, const Locator &loc)
+ZT_Certificate_Identity *Certificate::addSubjectIdentity(const Identity &id, const Locator &loc)
 {
 {
 	// Add identity as above.
 	// Add identity as above.
-	ZT_Certificate_Identity *const n = addSubjectNode(id);
+	ZT_Certificate_Identity *const n = addSubjectIdentity(id);
 
 
 	// Store local copy of locator.
 	// Store local copy of locator.
 	m_locators.push_back(loc);
 	m_locators.push_back(loc);
@@ -138,7 +152,7 @@ void Certificate::addSubjectCertificate(const uint8_t serialNo[ZT_SHA384_DIGEST_
 	this->subject.certificates = m_subjectCertificates.data();
 	this->subject.certificates = m_subjectCertificates.data();
 }
 }
 
 
-void Certificate::addUpdateUrl(const char *url)
+void Certificate::addSubjectUpdateUrl(const char *url)
 {
 {
 	// Store local copy of URL.
 	// Store local copy of URL.
 	m_strings.push_back(url);
 	m_strings.push_back(url);
@@ -159,28 +173,44 @@ Vector< uint8_t > Certificate::encode(const bool omitSignature) const
 	// format. Custom packed formats are used for credentials as these are smaller
 	// format. Custom packed formats are used for credentials as these are smaller
 	// and faster to marshal/unmarshal.
 	// and faster to marshal/unmarshal.
 
 
-	d.add("f", this->flags);
+	if (this->flags != 0)
+		d.add("f", this->flags);
 	d.add("t", (uint64_t)this->timestamp);
 	d.add("t", (uint64_t)this->timestamp);
 	d.add("v0", (uint64_t)this->validity[0]);
 	d.add("v0", (uint64_t)this->validity[0]);
 	d.add("v1", (uint64_t)this->validity[1]);
 	d.add("v1", (uint64_t)this->validity[1]);
+	if ((this->extendedAttributes) && (this->extendedAttributesSize > 0))
+		d["x"].assign(this->extendedAttributes, this->extendedAttributes + this->extendedAttributesSize);
 	d.add("mP", (uint64_t)this->maxPathLength);
 	d.add("mP", (uint64_t)this->maxPathLength);
 
 
-	m_encodeSubject(d, false);
+	m_encodeSubject(this->subject, d, false);
 
 
 	if (this->issuer)
 	if (this->issuer)
 		d.addO("i", *reinterpret_cast<const Identity *>(this->issuer));
 		d.addO("i", *reinterpret_cast<const Identity *>(this->issuer));
 
 
-	d.add("iN.c", this->issuerName.country);
-	d.add("iN.o", this->issuerName.organization);
-	d.add("iN.u", this->issuerName.unit);
-	d.add("iN.l", this->issuerName.locality);
-	d.add("iN.p", this->issuerName.province);
-	d.add("iN.sA", this->issuerName.streetAddress);
-	d.add("iN.pC", this->issuerName.postalCode);
-	d.add("iN.cN", this->issuerName.commonName);
-	d.add("iN.sN", this->issuerName.serialNo);
-	d.add("iN.e", this->issuerName.email);
-	d.add("iN.ur", this->issuerName.url);
+	if (this->issuerName.country[0])
+		d.add("iN.c", this->issuerName.country);
+	if (this->issuerName.organization[0])
+		d.add("iN.o", this->issuerName.organization);
+	if (this->issuerName.unit[0])
+		d.add("iN.u", this->issuerName.unit);
+	if (this->issuerName.locality[0])
+		d.add("iN.l", this->issuerName.locality);
+	if (this->issuerName.province[0])
+		d.add("iN.p", this->issuerName.province);
+	if (this->issuerName.streetAddress[0])
+		d.add("iN.sA", this->issuerName.streetAddress);
+	if (this->issuerName.postalCode[0])
+		d.add("iN.pC", this->issuerName.postalCode);
+	if (this->issuerName.commonName[0])
+		d.add("iN.cN", this->issuerName.commonName);
+	if (this->issuerName.serialNo[0])
+		d.add("iN.sN", this->issuerName.serialNo);
+	if (this->issuerName.email[0])
+		d.add("iN.e", this->issuerName.email);
+	if (this->issuerName.url[0])
+		d.add("iN.ur", this->issuerName.url);
+	if (this->issuerName.host[0])
+		d.add("iN.h", this->issuerName.host);
 
 
 	if ((!omitSignature) && (this->signatureSize > 0) && (this->signatureSize <= sizeof(this->signature)))
 	if ((!omitSignature) && (this->signatureSize > 0) && (this->signatureSize <= sizeof(this->signature)))
 		d["si"].assign(this->signature, this->signature + this->signatureSize);
 		d["si"].assign(this->signature, this->signature + this->signatureSize);
@@ -204,7 +234,11 @@ bool Certificate::decode(const Vector< uint8_t > &data)
 	this->validity[0] = (int64_t)d.getUI("v0");
 	this->validity[0] = (int64_t)d.getUI("v0");
 	this->validity[1] = (int64_t)d.getUI("v1");
 	this->validity[1] = (int64_t)d.getUI("v1");
 	this->maxPathLength = (unsigned int)d.getUI("mP");
 	this->maxPathLength = (unsigned int)d.getUI("mP");
-
+	m_extendedAttributes = d["x"];
+	if (!m_extendedAttributes.empty()) {
+		this->extendedAttributes = m_extendedAttributes.data();
+		this->extendedAttributesSize = (unsigned int)m_extendedAttributes.size();
+	}
 	this->subject.timestamp = (int64_t)d.getUI("s.t");
 	this->subject.timestamp = (int64_t)d.getUI("s.t");
 
 
 	unsigned int cnt = (unsigned int)d.getUI("s.i$");
 	unsigned int cnt = (unsigned int)d.getUI("s.i$");
@@ -220,9 +254,9 @@ bool Certificate::decode(const Vector< uint8_t > &data)
 			Locator loc;
 			Locator loc;
 			if (loc.unmarshal(locatorData.data(), (unsigned int)locatorData.size()) <= 0)
 			if (loc.unmarshal(locatorData.data(), (unsigned int)locatorData.size()) <= 0)
 				return false;
 				return false;
-			this->addSubjectNode(id, loc);
+			this->addSubjectIdentity(id, loc);
 		} else {
 		} else {
-			this->addSubjectNode(id);
+			this->addSubjectIdentity(id);
 		}
 		}
 	}
 	}
 
 
@@ -257,6 +291,7 @@ bool Certificate::decode(const Vector< uint8_t > &data)
 	d.getS("s.n.pC", this->subject.name.postalCode, sizeof(this->subject.name.postalCode));
 	d.getS("s.n.pC", this->subject.name.postalCode, sizeof(this->subject.name.postalCode));
 	d.getS("s.n.e", this->subject.name.email, sizeof(this->subject.name.email));
 	d.getS("s.n.e", this->subject.name.email, sizeof(this->subject.name.email));
 	d.getS("s.n.ur", this->subject.name.url, sizeof(this->subject.name.url));
 	d.getS("s.n.ur", this->subject.name.url, sizeof(this->subject.name.url));
+	d.getS("s.n.h", this->subject.name.host, sizeof(this->subject.name.host));
 
 
 	const Vector< uint8_t > &issuerData = d["i"];
 	const Vector< uint8_t > &issuerData = d["i"];
 	if (!issuerData.empty()) {
 	if (!issuerData.empty()) {
@@ -278,12 +313,13 @@ bool Certificate::decode(const Vector< uint8_t > &data)
 	d.getS("iN.pC", this->issuerName.postalCode, sizeof(this->issuerName.postalCode));
 	d.getS("iN.pC", this->issuerName.postalCode, sizeof(this->issuerName.postalCode));
 	d.getS("iN.e", this->issuerName.email, sizeof(this->issuerName.email));
 	d.getS("iN.e", this->issuerName.email, sizeof(this->issuerName.email));
 	d.getS("iN.ur", this->issuerName.url, sizeof(this->issuerName.url));
 	d.getS("iN.ur", this->issuerName.url, sizeof(this->issuerName.url));
+	d.getS("iN.h", this->issuerName.host, sizeof(this->issuerName.host));
 
 
 	cnt = (unsigned int)d.getUI("u$");
 	cnt = (unsigned int)d.getUI("u$");
 	for (unsigned int i = 0; i < cnt; ++i) {
 	for (unsigned int i = 0; i < cnt; ++i) {
 		const char *const url = d.getS(Dictionary::arraySubscript(tmp, "u$", i), tmp2, sizeof(tmp2));
 		const char *const url = d.getS(Dictionary::arraySubscript(tmp, "u$", i), tmp2, sizeof(tmp2));
 		if (url)
 		if (url)
-			addUpdateUrl(tmp2);
+			addSubjectUpdateUrl(tmp2);
 		else return false;
 		else return false;
 	}
 	}
 
 
@@ -326,7 +362,7 @@ ZT_CertificateError Certificate::verify() const
 				(this->subject.uniqueId[0] != ZT_CERTIFICATE_UNIQUE_ID_PUBLIC_KEY_TYPE_NIST_P_384))
 				(this->subject.uniqueId[0] != ZT_CERTIFICATE_UNIQUE_ID_PUBLIC_KEY_TYPE_NIST_P_384))
 				return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF;
 				return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF;
 			Dictionary tmp;
 			Dictionary tmp;
-			m_encodeSubject(tmp, true);
+			m_encodeSubject(this->subject, tmp, true);
 			Vector< uint8_t > enc;
 			Vector< uint8_t > enc;
 			tmp.encode(enc);
 			tmp.encode(enc);
 			uint8_t h[ZT_SHA384_DIGEST_SIZE];
 			uint8_t h[ZT_SHA384_DIGEST_SIZE];
@@ -368,55 +404,86 @@ ZT_CertificateError Certificate::verify() const
 	return ZT_CERTIFICATE_ERROR_NONE;
 	return ZT_CERTIFICATE_ERROR_NONE;
 }
 }
 
 
-void Certificate::m_encodeSubject(Dictionary &d, bool omitUniqueIdProofSignature) const
+bool Certificate::setSubjectUniqueId(ZT_Certificate_Subject &s, const uint8_t uniqueId[ZT_CERTIFICATE_UNIQUE_ID_SIZE_TYPE_NIST_P_384], const uint8_t uniqueIdPrivate[ZT_CERTIFICATE_UNIQUE_ID_PRIVATE_KEY_SIZE_TYPE_NIST_P_384])
+{
+	Utils::copy<ZT_CERTIFICATE_UNIQUE_ID_SIZE_TYPE_NIST_P_384>(s.uniqueId, uniqueId);
+	s.uniqueIdSize = ZT_CERTIFICATE_UNIQUE_ID_SIZE_TYPE_NIST_P_384;
+
+	Dictionary d;
+	m_encodeSubject(s, d, true);
+	Vector< uint8_t > enc;
+	d.encode(enc);
+	uint8_t h[ZT_ECC384_SIGNATURE_HASH_SIZE];
+	SHA384(h, enc.data(), (unsigned int)enc.size());
+
+	ECC384ECDSASign(uniqueIdPrivate, h, s.uniqueIdProofSignature);
+	s.uniqueIdProofSignatureSize = ZT_ECC384_SIGNATURE_SIZE;
+
+	return true;
+}
+
+void Certificate::m_encodeSubject(const ZT_Certificate_Subject &s, Dictionary &d, bool omitUniqueIdProofSignature)
 {
 {
 	char tmp[256];
 	char tmp[256];
 
 
-	d.add("s.t", (uint64_t)this->subject.timestamp);
+	d.add("s.t", (uint64_t)s.timestamp);
 
 
-	d.add("s.i$", (uint64_t)this->subject.identityCount);
-	for (unsigned int i = 0; i < this->subject.identityCount; ++i) {
-		if (this->subject.identities[i].identity)
-			d.addO(Dictionary::arraySubscript(tmp, "s.i$.i", i), *reinterpret_cast<const Identity *>(this->subject.identities[i].identity));
-		if (this->subject.identities[i].locator)
-			d.addO(Dictionary::arraySubscript(tmp, "s.i$.l", i), *reinterpret_cast<const Locator *>(this->subject.identities[i].locator));
+	d.add("s.i$", (uint64_t)s.identityCount);
+	for (unsigned int i = 0; i < s.identityCount; ++i) {
+		if (s.identities[i].identity)
+			d.addO(Dictionary::arraySubscript(tmp, "s.i$.i", i), *reinterpret_cast<const Identity *>(s.identities[i].identity));
+		if (s.identities[i].locator)
+			d.addO(Dictionary::arraySubscript(tmp, "s.i$.l", i), *reinterpret_cast<const Locator *>(s.identities[i].locator));
 	}
 	}
 
 
-	d.add("s.n$", (uint64_t)this->subject.networkCount);
-	for (unsigned int i = 0; i < this->subject.networkCount; ++i) {
-		d.add(Dictionary::arraySubscript(tmp, "s.n$.i", i), this->subject.networks[i].id);
-		Fingerprint fp(this->subject.networks[i].controller);
+	d.add("s.n$", (uint64_t)s.networkCount);
+	for (unsigned int i = 0; i < s.networkCount; ++i) {
+		d.add(Dictionary::arraySubscript(tmp, "s.n$.i", i), s.networks[i].id);
+		Fingerprint fp(s.networks[i].controller);
 		d.addO(Dictionary::arraySubscript(tmp, "s.n$.c", i), fp);
 		d.addO(Dictionary::arraySubscript(tmp, "s.n$.c", i), fp);
 	}
 	}
 
 
-	d.add("s.c$", (uint64_t)this->subject.certificateCount);
-	for (unsigned int i = 0; i < this->subject.certificateCount; ++i) {
-		if (this->subject.certificates[i])
-			d[Dictionary::arraySubscript(tmp, "s.c$", i)].assign(this->subject.certificates[i], this->subject.certificates[i] + ZT_SHA384_DIGEST_SIZE);
+	d.add("s.c$", (uint64_t)s.certificateCount);
+	for (unsigned int i = 0; i < s.certificateCount; ++i) {
+		if (s.certificates[i])
+			d[Dictionary::arraySubscript(tmp, "s.c$", i)].assign(s.certificates[i], s.certificates[i] + ZT_SHA384_DIGEST_SIZE);
 	}
 	}
 
 
-	d.add("s.u$", (uint64_t)this->subject.updateUrlCount);
-	if (this->subject.updateUrls) {
-		for (unsigned int i = 0; i < this->subject.updateUrlCount; ++i)
-			d.add(Dictionary::arraySubscript(tmp, "s.u$", i), this->subject.updateUrls[i]);
+	d.add("s.u$", (uint64_t)s.updateUrlCount);
+	if (s.updateUrls) {
+		for (unsigned int i = 0; i < s.updateUrlCount; ++i)
+			d.add(Dictionary::arraySubscript(tmp, "s.u$", i), s.updateUrls[i]);
 	}
 	}
 
 
-	d.add("s.n.c", this->subject.name.country);
-	d.add("s.n.o", this->subject.name.organization);
-	d.add("s.n.u", this->subject.name.unit);
-	d.add("s.n.l", this->subject.name.locality);
-	d.add("s.n.p", this->subject.name.province);
-	d.add("s.n.sA", this->subject.name.streetAddress);
-	d.add("s.n.pC", this->subject.name.postalCode);
-	d.add("s.n.cN", this->subject.name.commonName);
-	d.add("s.n.sN", this->subject.name.serialNo);
-	d.add("s.n.e", this->subject.name.email);
-	d.add("s.n.ur", this->subject.name.url);
-
-	if ((this->subject.uniqueIdSize > 0) && (this->subject.uniqueIdSize <= ZT_CERTIFICATE_MAX_UNIQUE_ID_SIZE))
-		d["s.uI"].assign(this->subject.uniqueId, this->subject.uniqueId + this->subject.uniqueIdSize);
-	if ((!omitUniqueIdProofSignature) && (this->subject.uniqueIdProofSignatureSize > 0) && (this->subject.uniqueIdProofSignatureSize <= ZT_CERTIFICATE_MAX_SIGNATURE_SIZE))
-		d["s.uS"].assign(this->subject.uniqueIdProofSignature, this->subject.uniqueIdProofSignature + this->subject.uniqueIdProofSignatureSize);
+	if (s.name.country[0])
+		d.add("s.n.c", s.name.country);
+	if (s.name.organization[0])
+		d.add("s.n.o", s.name.organization);
+	if (s.name.unit[0])
+		d.add("s.n.u", s.name.unit);
+	if (s.name.locality[0])
+		d.add("s.n.l", s.name.locality);
+	if (s.name.province[0])
+		d.add("s.n.p", s.name.province);
+	if (s.name.streetAddress[0])
+		d.add("s.n.sA", s.name.streetAddress);
+	if (s.name.postalCode[0])
+		d.add("s.n.pC", s.name.postalCode);
+	if (s.name.commonName[0])
+		d.add("s.n.cN", s.name.commonName);
+	if (s.name.serialNo[0])
+		d.add("s.n.sN", s.name.serialNo);
+	if (s.name.email[0])
+		d.add("s.n.e", s.name.email);
+	if (s.name.url[0])
+		d.add("s.n.ur", s.name.url);
+	if (s.name.host[0])
+		d.add("s.n.h", s.name.host);
+
+	if ((s.uniqueIdSize > 0) && (s.uniqueIdSize <= ZT_CERTIFICATE_MAX_UNIQUE_ID_SIZE))
+		d["s.uI"].assign(s.uniqueId, s.uniqueId + s.uniqueIdSize);
+	if ((!omitUniqueIdProofSignature) && (s.uniqueIdProofSignatureSize > 0) && (s.uniqueIdProofSignatureSize <= ZT_CERTIFICATE_MAX_SIGNATURE_SIZE))
+		d["s.uS"].assign(s.uniqueIdProofSignature, s.uniqueIdProofSignature + s.uniqueIdProofSignatureSize);
 }
 }
 
 
 } // namespace ZeroTier
 } // namespace ZeroTier

+ 40 - 4
core/Certificate.hpp

@@ -74,7 +74,7 @@ public:
 	 * @param id Identity
 	 * @param id Identity
 	 * @return Pointer to C struct
 	 * @return Pointer to C struct
 	 */
 	 */
-	ZT_Certificate_Identity *addSubjectNode(const Identity &id);
+	ZT_Certificate_Identity *addSubjectIdentity(const Identity &id);
 
 
 	/**
 	/**
 	 * Add a subject node/identity with a locator
 	 * Add a subject node/identity with a locator
@@ -83,7 +83,7 @@ public:
 	 * @param loc Locator signed by identity (signature is NOT checked here)
 	 * @param loc Locator signed by identity (signature is NOT checked here)
 	 * @return Pointer to C struct
 	 * @return Pointer to C struct
 	 */
 	 */
-	ZT_Certificate_Identity *addSubjectNode(const Identity &id, const Locator &loc);
+	ZT_Certificate_Identity *addSubjectIdentity(const Identity &id, const Locator &loc);
 
 
 	/**
 	/**
 	 * Add a subject network
 	 * Add a subject network
@@ -106,7 +106,20 @@ public:
 	 *
 	 *
 	 * @param url Update URL
 	 * @param url Update URL
 	 */
 	 */
-	void addUpdateUrl(const char *url);
+	void addSubjectUpdateUrl(const char *url);
+
+	/**
+	 * Set the extended attributes of this certificate
+	 *
+	 * @param x Extended attributes (set by issuer)
+	 */
+	ZT_INLINE void setExtendedAttributes(const Dictionary &x)
+	{
+		m_extendedAttributes.clear();
+		x.encode(m_extendedAttributes);
+		this->extendedAttributes = m_extendedAttributes.data();
+		this->extendedAttributesSize = (unsigned int)m_extendedAttributes.size();
+	}
 
 
 	/**
 	/**
 	 * Marshal this certificate in binary form
 	 * Marshal this certificate in binary form
@@ -145,6 +158,28 @@ public:
 	 */
 	 */
 	ZT_CertificateError verify() const;
 	ZT_CertificateError verify() const;
 
 
+	/**
+	 * Create a subject unique ID and corresponding private key required for use
+	 *
+	 * @param uniqueId Buffer to receive unique ID
+	 * @param uniqueIdPrivate Buffer to receive private key
+	 */
+	static ZT_INLINE void createSubjectUniqueId(uint8_t uniqueId[ZT_CERTIFICATE_UNIQUE_ID_SIZE_TYPE_NIST_P_384], uint8_t uniqueIdPrivate[ZT_CERTIFICATE_UNIQUE_ID_PRIVATE_KEY_SIZE_TYPE_NIST_P_384])
+	{
+		uniqueId[0] = ZT_CERTIFICATE_UNIQUE_ID_PUBLIC_KEY_TYPE_NIST_P_384;
+		ECC384GenerateKey(uniqueId + 1, uniqueIdPrivate);
+	}
+
+	/**
+	 * Set the unique ID and unique ID proof signature fields in a subject.
+	 *
+	 * @param s Subject to set
+	 * @param uniqueId Unique ID (public)
+	 * @param uniqueIdPrivate Unique ID private key
+	 * @return True on success
+	 */
+	static bool setSubjectUniqueId(ZT_Certificate_Subject &s, const uint8_t uniqueId[ZT_CERTIFICATE_UNIQUE_ID_SIZE_TYPE_NIST_P_384], const uint8_t uniqueIdPrivate[ZT_CERTIFICATE_UNIQUE_ID_PRIVATE_KEY_SIZE_TYPE_NIST_P_384]);
+
 	ZT_INLINE unsigned long hashCode() const noexcept
 	ZT_INLINE unsigned long hashCode() const noexcept
 	{ return (unsigned long)Utils::loadAsIsEndian< uint32_t >(this->serialNo); }
 	{ return (unsigned long)Utils::loadAsIsEndian< uint32_t >(this->serialNo); }
 
 
@@ -162,7 +197,7 @@ public:
 	{ return memcmp(this->serialNo, c.serialNo, ZT_SHA384_DIGEST_SIZE) >= 0; }
 	{ return memcmp(this->serialNo, c.serialNo, ZT_SHA384_DIGEST_SIZE) >= 0; }
 
 
 private:
 private:
-	void m_encodeSubject(Dictionary &d, bool omitUniqueIdProofSignature) const;
+	static void m_encodeSubject(const ZT_Certificate_Subject &s, Dictionary &d, bool omitUniqueIdProofSignature);
 
 
 	// These hold any identity or locator objects that are owned by and should
 	// These hold any identity or locator objects that are owned by and should
 	// be deleted with this certificate. Lists are used so the pointers never
 	// be deleted with this certificate. Lists are used so the pointers never
@@ -177,6 +212,7 @@ private:
 	Vector< ZT_Certificate_Network > m_subjectNetworks;
 	Vector< ZT_Certificate_Network > m_subjectNetworks;
 	Vector< const uint8_t * > m_subjectCertificates;
 	Vector< const uint8_t * > m_subjectCertificates;
 	Vector< const char * > m_updateUrls;
 	Vector< const char * > m_updateUrls;
+	Vector< uint8_t > m_extendedAttributes;
 
 
 	std::atomic<int> __refCount;
 	std::atomic<int> __refCount;
 };
 };

+ 3 - 3
core/Credential.cpp

@@ -36,8 +36,8 @@
 #if ZT_CERTIFICATEOFOWNERSHIP_MARSHAL_SIZE_MAX > ZT_BUF_MEM_SIZE
 #if ZT_CERTIFICATEOFOWNERSHIP_MARSHAL_SIZE_MAX > ZT_BUF_MEM_SIZE
 #error ZT_CERTIFICATEOFOWNERSHIP_MARSHAL_SIZE_MAX exceeds maximum buffer size
 #error ZT_CERTIFICATEOFOWNERSHIP_MARSHAL_SIZE_MAX exceeds maximum buffer size
 #endif
 #endif
-#if ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX > ZT_BUF_MEM_SIZE
-#error ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX exceeds maximum buffer size
+#if ZT_MEMBERSHIP_CREDENTIAL_MARSHAL_SIZE_MAX > ZT_BUF_MEM_SIZE
+#error ZT_MEMBERSHIP_CREDENTIAL_MARSHAL_SIZE_MAX exceeds maximum buffer size
 #endif
 #endif
 
 
 namespace ZeroTier {
 namespace ZeroTier {
@@ -83,7 +83,7 @@ Credential::VerifyResult Credential::_verify(const RuntimeEnvironment *const RR,
 		return Credential::VERIFY_NEED_IDENTITY;
 		return Credential::VERIFY_NEED_IDENTITY;
 
 
 	// Now verify the controller's signature.
 	// Now verify the controller's signature.
-	uint64_t buf[ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX / 8];
+	uint64_t buf[ZT_MEMBERSHIP_CREDENTIAL_MARSHAL_SIZE_MAX / 8];
 	const unsigned int bufSize = credential.m_fillSigningBuf(buf);
 	const unsigned int bufSize = credential.m_fillSigningBuf(buf);
 	return peer->identity().verify(buf, bufSize, credential.m_signature, credential.m_signatureLength) ? Credential::VERIFY_OK : Credential::VERIFY_BAD_SIGNATURE;
 	return peer->identity().verify(buf, bufSize, credential.m_signature, credential.m_signatureLength) ? Credential::VERIFY_OK : Credential::VERIFY_BAD_SIGNATURE;
 }
 }

+ 10 - 3
core/Dictionary.cpp

@@ -115,15 +115,22 @@ char *Dictionary::getS(const char *k, char *v, const unsigned int cap) const
 	if (cap == 0) // sanity check
 	if (cap == 0) // sanity check
 		return v;
 		return v;
 	const Vector< uint8_t > &e = (*this)[k];
 	const Vector< uint8_t > &e = (*this)[k];
+	if (e.empty()) {
+		v[0] = 0;
+		return v;
+	}
 	unsigned int i = 0;
 	unsigned int i = 0;
 	const unsigned int last = cap - 1;
 	const unsigned int last = cap - 1;
 	for (;;) {
 	for (;;) {
-		if ((i == last) || (i >= (unsigned int)e.size()))
+		if ((i >= last) || (i >= (unsigned int)e.size())) {
+			v[i] = 0;
 			break;
 			break;
-		v[i] = (char)e[i];
+		}
+		if ((v[i] = (char)e[i]) == 0) {
+			break;
+		}
 		++i;
 		++i;
 	}
 	}
-	v[i] = 0;
 	return v;
 	return v;
 }
 }
 
 

+ 11 - 0
core/Locator.hpp

@@ -134,6 +134,17 @@ public:
 	int marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX], bool excludeSignature = false) const noexcept;
 	int marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX], bool excludeSignature = false) const noexcept;
 	int unmarshal(const uint8_t *data, int len) noexcept;
 	int unmarshal(const uint8_t *data, int len) noexcept;
 
 
+	ZT_INLINE bool operator==(const Locator &l) const noexcept
+	{
+		return (
+			(m_ts == l.m_ts) &&
+			(m_signer == l.m_signer) &&
+			(m_endpoints == l.m_endpoints) &&
+			(m_signature == l.m_signature));
+	}
+	ZT_INLINE bool operator!=(const Locator &l) const noexcept
+	{ return !(*this == l); }
+
 private:
 private:
 	int64_t m_ts;
 	int64_t m_ts;
 	Fingerprint m_signer;
 	Fingerprint m_signer;

+ 9 - 9
core/MembershipCredential.cpp

@@ -37,10 +37,10 @@ bool MembershipCredential::agreesWith(const MembershipCredential &other) const n
 	}
 	}
 
 
 	// us <> them
 	// us <> them
-	for (FCV<p_Qualifier, ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS>::const_iterator i(m_additionalQualifiers.begin());i != m_additionalQualifiers.end();++i) {
+	for (FCV<p_Qualifier, ZT_MEMBERSHIP_CREDENTIAL_MAX_ADDITIONAL_QUALIFIERS>::const_iterator i(m_additionalQualifiers.begin()); i != m_additionalQualifiers.end(); ++i) {
 		if (i->delta != 0xffffffffffffffffULL) {
 		if (i->delta != 0xffffffffffffffffULL) {
 			const uint64_t *v2 = nullptr;
 			const uint64_t *v2 = nullptr;
-			for (FCV<p_Qualifier, ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS>::const_iterator j(other.m_additionalQualifiers.begin());j != other.m_additionalQualifiers.end();++i) {
+			for (FCV<p_Qualifier, ZT_MEMBERSHIP_CREDENTIAL_MAX_ADDITIONAL_QUALIFIERS>::const_iterator j(other.m_additionalQualifiers.begin()); j != other.m_additionalQualifiers.end(); ++i) {
 				if (j->id == i->id) {
 				if (j->id == i->id) {
 					v2 = &(j->value);
 					v2 = &(j->value);
 					break;
 					break;
@@ -59,10 +59,10 @@ bool MembershipCredential::agreesWith(const MembershipCredential &other) const n
 	}
 	}
 
 
 	// them <> us (we need a second pass in case they have qualifiers we don't or vice versa)
 	// them <> us (we need a second pass in case they have qualifiers we don't or vice versa)
-	for (FCV<p_Qualifier, ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS>::const_iterator i(other.m_additionalQualifiers.begin());i != other.m_additionalQualifiers.end();++i) {
+	for (FCV<p_Qualifier, ZT_MEMBERSHIP_CREDENTIAL_MAX_ADDITIONAL_QUALIFIERS>::const_iterator i(other.m_additionalQualifiers.begin()); i != other.m_additionalQualifiers.end(); ++i) {
 		if (i->delta != 0xffffffffffffffffULL) {
 		if (i->delta != 0xffffffffffffffffULL) {
 			const uint64_t *v2 = nullptr;
 			const uint64_t *v2 = nullptr;
-			for (FCV<p_Qualifier, ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS>::const_iterator j(m_additionalQualifiers.begin());j != m_additionalQualifiers.end();++i) {
+			for (FCV<p_Qualifier, ZT_MEMBERSHIP_CREDENTIAL_MAX_ADDITIONAL_QUALIFIERS>::const_iterator j(m_additionalQualifiers.begin()); j != m_additionalQualifiers.end(); ++i) {
 				if (j->id == i->id) {
 				if (j->id == i->id) {
 					v2 = &(j->value);
 					v2 = &(j->value);
 					break;
 					break;
@@ -88,13 +88,13 @@ bool MembershipCredential::agreesWith(const MembershipCredential &other) const n
 bool MembershipCredential::sign(const Identity &with) noexcept
 bool MembershipCredential::sign(const Identity &with) noexcept
 {
 {
 	m_signedBy = with.address();
 	m_signedBy = with.address();
-	uint64_t buf[ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX / 8];
+	uint64_t buf[ZT_MEMBERSHIP_CREDENTIAL_MARSHAL_SIZE_MAX / 8];
 	const unsigned int bufSize = m_fillSigningBuf(buf);
 	const unsigned int bufSize = m_fillSigningBuf(buf);
 	m_signatureLength = with.sign(buf, bufSize, m_signature, sizeof(m_signature));
 	m_signatureLength = with.sign(buf, bufSize, m_signature, sizeof(m_signature));
 	return m_signatureLength > 0;
 	return m_signatureLength > 0;
 }
 }
 
 
-int MembershipCredential::marshal(uint8_t data[ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX], const bool v2) const noexcept
+int MembershipCredential::marshal(uint8_t data[ZT_MEMBERSHIP_CREDENTIAL_MARSHAL_SIZE_MAX], const bool v2) const noexcept
 {
 {
 	data[0] = v2 ? 2 : 1;
 	data[0] = v2 ? 2 : 1;
 
 
@@ -164,7 +164,7 @@ int MembershipCredential::unmarshal(const uint8_t *data, int len) noexcept
 	TriviallyCopyable::memoryZero(this);
 	TriviallyCopyable::memoryZero(this);
 
 
 	const unsigned int numq = Utils::loadBigEndian<uint16_t>(data + 1);
 	const unsigned int numq = Utils::loadBigEndian<uint16_t>(data + 1);
-	if ((numq < 3) || (numq > (ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS + 3)))
+	if ((numq < 3) || (numq > (ZT_MEMBERSHIP_CREDENTIAL_MAX_ADDITIONAL_QUALIFIERS + 3)))
 		return -1;
 		return -1;
 	int p = 3;
 	int p = 3;
 	for (unsigned int q = 0;q < numq;++q) {
 	for (unsigned int q = 0;q < numq;++q) {
@@ -209,7 +209,7 @@ int MembershipCredential::unmarshal(const uint8_t *data, int len) noexcept
 				break;
 				break;
 
 
 			default:
 			default:
-				if (m_additionalQualifiers.size() >= ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS)
+				if (m_additionalQualifiers.size() >= ZT_MEMBERSHIP_CREDENTIAL_MAX_ADDITIONAL_QUALIFIERS)
 					return -1;
 					return -1;
 				m_additionalQualifiers.push_back(p_Qualifier(id, value, delta));
 				m_additionalQualifiers.push_back(p_Qualifier(id, value, delta));
 				break;
 				break;
@@ -287,7 +287,7 @@ unsigned int MembershipCredential::m_fillSigningBuf(uint64_t *buf) const noexcep
 		buf[p++] = informational;
 		buf[p++] = informational;
 	}
 	}
 
 
-	for (FCV<p_Qualifier, ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS>::const_iterator i(m_additionalQualifiers.begin());i != m_additionalQualifiers.end();++i) { // NOLINT(modernize-loop-convert)
+	for (FCV<p_Qualifier, ZT_MEMBERSHIP_CREDENTIAL_MAX_ADDITIONAL_QUALIFIERS>::const_iterator i(m_additionalQualifiers.begin()); i != m_additionalQualifiers.end(); ++i) { // NOLINT(modernize-loop-convert)
 		buf[p++] = Utils::hton(i->id);
 		buf[p++] = Utils::hton(i->id);
 		buf[p++] = Utils::hton(i->value);
 		buf[p++] = Utils::hton(i->value);
 		buf[p++] = Utils::hton(i->delta);
 		buf[p++] = Utils::hton(i->delta);

+ 5 - 5
core/MembershipCredential.hpp

@@ -32,10 +32,10 @@
 #include "FCV.hpp"
 #include "FCV.hpp"
 
 
 // Maximum number of additional tuples beyond the standard always-present three.
 // Maximum number of additional tuples beyond the standard always-present three.
-#define ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS 8
+#define ZT_MEMBERSHIP_CREDENTIAL_MAX_ADDITIONAL_QUALIFIERS 8
 
 
 // version + qualifier count + three required qualifiers + additional qualifiers +
 // version + qualifier count + three required qualifiers + additional qualifiers +
-#define ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX (1 + 2 + (3 * 3 * 8) + (ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS * 3 * 8) + 144 + 5 + 2 + 96)
+#define ZT_MEMBERSHIP_CREDENTIAL_MARSHAL_SIZE_MAX (1 + 2 + (3 * 3 * 8) + (ZT_MEMBERSHIP_CREDENTIAL_MAX_ADDITIONAL_QUALIFIERS * 3 * 8) + 144 + 5 + 2 + 96)
 
 
 namespace ZeroTier {
 namespace ZeroTier {
 
 
@@ -186,8 +186,8 @@ public:
 
 
 	// NOTE: right now we use v1 serialization format which works with both ZeroTier 1.x and 2.x. V2 format
 	// NOTE: right now we use v1 serialization format which works with both ZeroTier 1.x and 2.x. V2 format
 	// will be switched on once 1.x is pretty much dead and out of support.
 	// will be switched on once 1.x is pretty much dead and out of support.
-	static constexpr int marshalSizeMax() noexcept { return ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX; }
-	int marshal(uint8_t data[ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX],bool v2 = false) const noexcept;
+	static constexpr int marshalSizeMax() noexcept { return ZT_MEMBERSHIP_CREDENTIAL_MARSHAL_SIZE_MAX; }
+	int marshal(uint8_t data[ZT_MEMBERSHIP_CREDENTIAL_MARSHAL_SIZE_MAX], bool v2 = false) const noexcept;
 	int unmarshal(const uint8_t *data,int len) noexcept;
 	int unmarshal(const uint8_t *data,int len) noexcept;
 
 
 private:
 private:
@@ -203,7 +203,7 @@ private:
 		ZT_INLINE bool operator<(const p_Qualifier &q) const noexcept { return (id < q.id); } // sort order
 		ZT_INLINE bool operator<(const p_Qualifier &q) const noexcept { return (id < q.id); } // sort order
 	};
 	};
 
 
-	FCV<p_Qualifier,ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS> m_additionalQualifiers;
+	FCV<p_Qualifier,ZT_MEMBERSHIP_CREDENTIAL_MAX_ADDITIONAL_QUALIFIERS> m_additionalQualifiers;
 	int64_t m_timestamp;
 	int64_t m_timestamp;
 	int64_t m_timestampMaxDelta;
 	int64_t m_timestampMaxDelta;
 	uint64_t m_networkId;
 	uint64_t m_networkId;

File diff suppressed because it is too large
+ 32 - 28
core/Tests.cpp


+ 31 - 55
core/Utils.hpp

@@ -702,58 +702,21 @@ static ZT_INLINE void storeLittleEndian(void *const p, const I i) noexcept
  * @param dest Destination memory
  * @param dest Destination memory
  * @param src Source memory
  * @param src Source memory
  */
  */
-template< unsigned int L >
-static ZT_INLINE void copy(void *const dest, const void *const src) noexcept
-{
-#ifdef ZT_ARCH_X64
-	uint8_t *volatile d = reinterpret_cast<uint8_t *>(dest);
-	const uint8_t *s = reinterpret_cast<const uint8_t *>(src);
-	for (unsigned int i = 0; i < (L >> 6U); ++i) {
-		__m128i x0 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s));
-		__m128i x1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 16));
-		__m128i x2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 32));
-		__m128i x3 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 48));
-		s += 64;
-		_mm_storeu_si128(reinterpret_cast<__m128i *>(d), x0);
-		_mm_storeu_si128(reinterpret_cast<__m128i *>(d + 16), x1);
-		_mm_storeu_si128(reinterpret_cast<__m128i *>(d + 32), x2);
-		_mm_storeu_si128(reinterpret_cast<__m128i *>(d + 48), x3);
-		d += 64;
-	}
-	if ((L & 32U) != 0) {
-		__m128i x0 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s));
-		__m128i x1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 16));
-		s += 32;
-		_mm_storeu_si128(reinterpret_cast<__m128i *>(d), x0);
-		_mm_storeu_si128(reinterpret_cast<__m128i *>(d + 16), x1);
-		d += 32;
-	}
-	if ((L & 16U) != 0) {
-		__m128i x0 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s));
-		s += 16;
-		_mm_storeu_si128(reinterpret_cast<__m128i *>(d), x0);
-		d += 16;
-	}
-	if ((L & 8U) != 0) {
-		*reinterpret_cast<volatile uint64_t *>(d) = *reinterpret_cast<const uint64_t *>(s);
-		s += 8;
-		d += 8;
-	}
-	if ((L & 4U) != 0) {
-		*reinterpret_cast<volatile uint32_t *>(d) = *reinterpret_cast<const uint32_t *>(s);
-		s += 4;
-		d += 4;
-	}
-	if ((L & 2U) != 0) {
-		*reinterpret_cast<volatile uint16_t *>(d) = *reinterpret_cast<const uint16_t *>(s);
-		s += 2;
-		d += 2;
-	}
-	if ((L & 1U) != 0) {
-		*d = *s;
-	}
+template< unsigned long L >
+static ZT_INLINE void copy(void *dest, const void *src) noexcept
+{
+#if defined(ZT_ARCH_X64) && defined(__GNUC__)
+	unsigned long l = L;
+	asm volatile ("rep movsb"
+		: "=D" (dest),
+		"=S" (src),
+		"=c" (l)
+		: "0" (dest),
+		"1" (src),
+		"2" (l)
+		: "memory");
 #else
 #else
-	memcpy(dest,src,L);
+	memcpy(dest, src, L);
 #endif
 #endif
 }
 }
 
 
@@ -764,8 +727,21 @@ static ZT_INLINE void copy(void *const dest, const void *const src) noexcept
  * @param src Source memory
  * @param src Source memory
  * @param len Bytes to copy
  * @param len Bytes to copy
  */
  */
-static ZT_INLINE void copy(void *const dest, const void *const src, unsigned int len) noexcept
-{ memcpy(dest, src, len); }
+static ZT_INLINE void copy(void *dest, const void *src, unsigned long len) noexcept
+{
+#if defined(ZT_ARCH_X64) && defined(__GNUC__)
+	asm volatile ("rep movsb"
+		: "=D" (dest),
+		"=S" (src),
+		"=c" (len)
+		: "0" (dest),
+		"1" (src),
+		"2" (len)
+		: "memory");
+#else
+	memcpy(dest, src, len);
+#endif
+}
 
 
 /**
 /**
  * Zero memory block whose size is known at compile time
  * Zero memory block whose size is known at compile time
@@ -773,7 +749,7 @@ static ZT_INLINE void copy(void *const dest, const void *const src, unsigned int
  * @tparam L Size in bytes
  * @tparam L Size in bytes
  * @param dest Memory to zero
  * @param dest Memory to zero
  */
  */
-template< unsigned int L >
+template< unsigned long L >
 static ZT_INLINE void zero(void *const dest) noexcept
 static ZT_INLINE void zero(void *const dest) noexcept
 { memset(dest, 0, L); }
 { memset(dest, 0, L); }
 
 
@@ -783,7 +759,7 @@ static ZT_INLINE void zero(void *const dest) noexcept
  * @param dest Memory to zero
  * @param dest Memory to zero
  * @param len Size in bytes
  * @param len Size in bytes
  */
  */
-static ZT_INLINE void zero(void *const dest, const unsigned int len) noexcept
+static ZT_INLINE void zero(void *const dest, const unsigned long len) noexcept
 { memset(dest, 0, len); }
 { memset(dest, 0, len); }
 
 
 /**
 /**

+ 45 - 10
core/zerotier.h

@@ -323,6 +323,16 @@ typedef struct
  */
  */
 #define ZT_CERTIFICATE_UNIQUE_ID_PUBLIC_KEY_TYPE_NIST_P_384 1
 #define ZT_CERTIFICATE_UNIQUE_ID_PUBLIC_KEY_TYPE_NIST_P_384 1
 
 
+/**
+ * Size of a unique ID of the given key type (with type prefix byte)
+ */
+#define ZT_CERTIFICATE_UNIQUE_ID_SIZE_TYPE_NIST_P_384 50
+
+/**
+ * Size of the private key corresponding to a unique ID of the given type.
+ */
+#define ZT_CERTIFICATE_UNIQUE_ID_PRIVATE_KEY_SIZE_TYPE_NIST_P_384 48
+
 /**
 /**
  * Errors returned by functions that verify or handle certificates.
  * Errors returned by functions that verify or handle certificates.
  */
  */
@@ -368,24 +378,22 @@ enum ZT_CertificateError
 	 */
 	 */
 	ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF = -6,
 	ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF = -6,
 
 
-	/**
-	 * Certificate is not appropriate for this use
-	 */
-	ZT_CERTIFICATE_ERROR_INAPPROPRIATE_FOR_USE = -7,
-
 	/**
 	/**
 	 * Certificate is missing a required field
 	 * Certificate is missing a required field
 	 */
 	 */
-	ZT_CERTIFICATE_ERROR_MISSING_REQUIRED_FIELDS = -8,
+	ZT_CERTIFICATE_ERROR_MISSING_REQUIRED_FIELDS = -7,
 
 
 	/**
 	/**
 	 * Certificate is expired or not yet in effect
 	 * Certificate is expired or not yet in effect
 	 */
 	 */
-	ZT_CERTIFICATE_ERROR_OUT_OF_VALID_TIME_WINDOW = -9
+	ZT_CERTIFICATE_ERROR_OUT_OF_VALID_TIME_WINDOW = -8
 };
 };
 
 
 /**
 /**
  * Information about a real world entity.
  * Information about a real world entity.
+ *
+ * These fields are all optional and are all taken from the
+ * most common fields present in X509 certificates.
  */
  */
 typedef struct
 typedef struct
 {
 {
@@ -400,6 +408,7 @@ typedef struct
 	char postalCode[ZT_CERTIFICATE_MAX_STRING_LENGTH + 1];
 	char postalCode[ZT_CERTIFICATE_MAX_STRING_LENGTH + 1];
 	char email[ZT_CERTIFICATE_MAX_STRING_LENGTH + 1];
 	char email[ZT_CERTIFICATE_MAX_STRING_LENGTH + 1];
 	char url[ZT_CERTIFICATE_MAX_STRING_LENGTH + 1];
 	char url[ZT_CERTIFICATE_MAX_STRING_LENGTH + 1];
+	char host[ZT_CERTIFICATE_MAX_STRING_LENGTH + 1];
 } ZT_Certificate_Name;
 } ZT_Certificate_Name;
 
 
 /**
 /**
@@ -490,12 +499,28 @@ typedef struct
 	ZT_Certificate_Name name;
 	ZT_Certificate_Name name;
 
 
 	/**
 	/**
-	 * Unique ID, which can be a public key prefixed by a key type.
+	 * Globally unique ID for this subject
+	 *
+	 * Unique IDs are actually public keys. Their size makes them globally
+	 * unique (if generated from good randomness) to within ridiculous
+	 * probability bounds. If a subject has a unique ID it must also have
+	 * a unique ID proof signature, which is the signature of the subject
+	 * with the private key corresponding to its unique ID.
+	 *
+	 * This allows subjects to "own" themselves and exist independent of
+	 * CAs or delegated signers. It also allows a certificate for a given
+	 * subject to be updated.
+	 *
+	 * Subject unique IDs are optional. If no unique ID is specified these
+	 * and their corresponding size fields must be empty/zero.
+	 *
+	 * A subject is valid if it has no unique ID or has one with a valid
+	 * proof signature.
 	 */
 	 */
 	uint8_t uniqueId[ZT_CERTIFICATE_MAX_UNIQUE_ID_SIZE];
 	uint8_t uniqueId[ZT_CERTIFICATE_MAX_UNIQUE_ID_SIZE];
 
 
 	/**
 	/**
-	 * If unique ID is a public key, this can be a signature of the subject.
+	 * Signature proving ownership of unique ID.
 	 */
 	 */
 	uint8_t uniqueIdProofSignature[ZT_CERTIFICATE_MAX_SIGNATURE_SIZE];
 	uint8_t uniqueIdProofSignature[ZT_CERTIFICATE_MAX_SIGNATURE_SIZE];
 
 
@@ -557,6 +582,16 @@ typedef struct
 	 */
 	 */
 	ZT_Certificate_Name issuerName;
 	ZT_Certificate_Name issuerName;
 
 
+	/**
+	 * Extended attributes set by issuer (in Dictionary format, NULL if none)
+	 */
+	uint8_t *extendedAttributes;
+
+	/**
+	 * Size of extended attributes field in bytes
+	 */
+	unsigned int extendedAttributesSize;
+
 	/**
 	/**
 	 * Maximum path length from this certificate toward further certificates.
 	 * Maximum path length from this certificate toward further certificates.
 	 *
 	 *
@@ -1627,7 +1662,7 @@ enum ZT_StateObjectType
 	ZT_STATE_OBJECT_NETWORK_CONFIG = 6,
 	ZT_STATE_OBJECT_NETWORK_CONFIG = 6,
 
 
 	/**
 	/**
-	 * List of certificates and their local trust, and locally added roots
+	 * List of certificates, their local trust, and locally added roots
 	 *
 	 *
 	 * Object ID: (none)
 	 * Object ID: (none)
 	 * Canonical path: <HOME>/trust
 	 * Canonical path: <HOME>/trust

Some files were not shown because too many files changed in this diff