|
@@ -11,118 +11,177 @@
|
|
|
*/
|
|
|
/****/
|
|
|
|
|
|
-#include "IdentificationCertificate.hpp"
|
|
|
+#include "Certificate.hpp"
|
|
|
#include "SHA512.hpp"
|
|
|
|
|
|
namespace ZeroTier {
|
|
|
|
|
|
-void IdentificationCertificate::clear()
|
|
|
+void Certificate::clear()
|
|
|
{
|
|
|
- Utils::zero< sizeof(ZT_IdentificationCertificate) >((ZT_IdentificationCertificate *)this);
|
|
|
+ Utils::zero< sizeof(ZT_Certificate) >((ZT_Certificate *)this);
|
|
|
+
|
|
|
m_identities.clear();
|
|
|
m_locators.clear();
|
|
|
m_strings.clear();
|
|
|
- m_nodes.clear();
|
|
|
- m_networks.clear();
|
|
|
+ m_serials.clear();
|
|
|
+
|
|
|
+ m_subjectIdentities.clear();
|
|
|
+ m_subjectNetworks.clear();
|
|
|
m_updateUrls.clear();
|
|
|
+ m_subjectCertificates.clear();
|
|
|
}
|
|
|
|
|
|
-IdentificationCertificate &IdentificationCertificate::operator=(const ZT_IdentificationCertificate &apiCert)
|
|
|
+Certificate &Certificate::operator=(const ZT_Certificate &apiCert)
|
|
|
{
|
|
|
clear();
|
|
|
- Utils::copy< sizeof(ZT_IdentificationCertificate) >((ZT_IdentificationCertificate *)this, &apiCert);
|
|
|
+ Utils::copy< sizeof(ZT_Certificate) >((ZT_Certificate *)this, &apiCert);
|
|
|
return *this;
|
|
|
}
|
|
|
|
|
|
-IdentificationCertificate &IdentificationCertificate::operator=(const IdentificationCertificate &cert)
|
|
|
+Certificate &Certificate::operator=(const Certificate &cert)
|
|
|
{
|
|
|
- *this = *((const ZT_IdentificationCertificate *)(&cert));
|
|
|
+ *this = *((const ZT_Certificate *)(&cert));
|
|
|
|
|
|
- this->subject.nodeCount = 0;
|
|
|
+ // Zero these since we must explicitly attach all the objects from
|
|
|
+ // the other certificate to copy them into our containers.
|
|
|
+ this->subject.identityCount = 0;
|
|
|
this->subject.networkCount = 0;
|
|
|
+ this->subject.certificateCount = 0;
|
|
|
|
|
|
- if (cert.issuer) {
|
|
|
- m_identities.push_back(*reinterpret_cast<const Identity *>(cert.issuer));
|
|
|
- this->issuer = reinterpret_cast<ZT_Identity *>(&(m_identities.back()));
|
|
|
+ for (unsigned int i = 0; i < cert.subject.identityCount; ++i) {
|
|
|
+ if (cert.subject.identities[i].identity) {
|
|
|
+ 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));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- for (unsigned int i = 0; i < cert.subject.nodeCount; ++i) {
|
|
|
- if (cert.subject.nodes[i].locator)
|
|
|
- addSubjectNode(*reinterpret_cast<const Identity *>(cert.subject.nodes[i].identity), *reinterpret_cast<const Locator *>(cert.subject.nodes[i].locator));
|
|
|
- else if (cert.subject.nodes[i].identity)
|
|
|
- addSubjectNode(*reinterpret_cast<const Identity *>(cert.subject.nodes[i].identity));
|
|
|
+ for (unsigned int i = 0; i < cert.subject.networkCount; ++i) {
|
|
|
+ if (cert.subject.networks[i].id)
|
|
|
+ addSubjectNetwork(cert.subject.networks[i].id, cert.subject.networks[i].controller);
|
|
|
}
|
|
|
|
|
|
- for (unsigned int i = 0; i < cert.subject.networkCount; ++i)
|
|
|
- addSubjectNetwork(cert.subject.networks[i].id, cert.subject.networks[i].controller);
|
|
|
+ for (unsigned int i = 0; i < cert.subject.certificateCount; ++i) {
|
|
|
+ if (cert.subject.certificates[i])
|
|
|
+ addSubjectCertificate(cert.subject.certificates[i]);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (cert.issuer) {
|
|
|
+ m_identities.push_back(*reinterpret_cast<const Identity *>(cert.issuer));
|
|
|
+ this->issuer = reinterpret_cast<ZT_Identity *>(&(m_identities.back()));
|
|
|
+ }
|
|
|
|
|
|
if (cert.updateUrls) {
|
|
|
- for (unsigned int i = 0; i < cert.updateUrlCount; ++i)
|
|
|
- addUpdateUrl(cert.updateUrls[i]);
|
|
|
+ for (unsigned int i = 0; i < cert.updateUrlCount; ++i) {
|
|
|
+ if (cert.updateUrls[i])
|
|
|
+ addUpdateUrl(cert.updateUrls[i]);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
return *this;
|
|
|
}
|
|
|
|
|
|
-ZT_IdentificationCertificate_Node *IdentificationCertificate::addSubjectNode(const Identity &id)
|
|
|
+ZT_Certificate_Identity *Certificate::addSubjectNode(const Identity &id)
|
|
|
{
|
|
|
- m_nodes.resize(++this->subject.nodeCount);
|
|
|
- this->subject.nodes = m_nodes.data();
|
|
|
+ // Enlarge array of ZT_Certificate_Identity structs and set pointer to potentially reallocated array.
|
|
|
+ m_subjectIdentities.resize(++this->subject.identityCount);
|
|
|
+ this->subject.identities = m_subjectIdentities.data();
|
|
|
+
|
|
|
+ // Store a local copy of the actual identity.
|
|
|
m_identities.push_back(id);
|
|
|
- m_nodes.back().identity = reinterpret_cast<ZT_Identity *>(&(m_identities.back()));
|
|
|
- m_nodes.back().locator = nullptr;
|
|
|
- return &(m_nodes.back());
|
|
|
+
|
|
|
+ // Set ZT_Certificate_Identity struct fields to point to local copy of identity.
|
|
|
+ m_subjectIdentities.back().identity = reinterpret_cast<ZT_Identity *>(&(m_identities.back()));
|
|
|
+ m_subjectIdentities.back().locator = nullptr;
|
|
|
+
|
|
|
+ return &(m_subjectIdentities.back());
|
|
|
}
|
|
|
|
|
|
-ZT_IdentificationCertificate_Node *IdentificationCertificate::addSubjectNode(const Identity &id, const Locator &loc)
|
|
|
+ZT_Certificate_Identity *Certificate::addSubjectNode(const Identity &id, const Locator &loc)
|
|
|
{
|
|
|
- ZT_IdentificationCertificate_Node *n = addSubjectNode(id);
|
|
|
+ // Add identity as above.
|
|
|
+ ZT_Certificate_Identity *const n = addSubjectNode(id);
|
|
|
+
|
|
|
+ // Store local copy of locator.
|
|
|
m_locators.push_back(loc);
|
|
|
+
|
|
|
+ // Set pointer to stored local copy of locator.
|
|
|
n->locator = reinterpret_cast<ZT_Locator *>(&(m_locators.back()));
|
|
|
+
|
|
|
return n;
|
|
|
}
|
|
|
|
|
|
-ZT_IdentificationCertificate_Network *IdentificationCertificate::addSubjectNetwork(const uint64_t id, const ZT_Fingerprint &controller)
|
|
|
+ZT_Certificate_Network *Certificate::addSubjectNetwork(const uint64_t id, const ZT_Fingerprint &controller)
|
|
|
{
|
|
|
- m_networks.resize(++this->subject.networkCount);
|
|
|
- this->subject.networks = m_networks.data();
|
|
|
- m_networks.back().id = id;
|
|
|
- Utils::copy< sizeof(ZT_Fingerprint) >(&(m_networks.back().controller), &controller);
|
|
|
- return &(m_networks.back());
|
|
|
+ // Enlarge array of ZT_Certificate_Network and set pointer to potentially reallocated array.
|
|
|
+ m_subjectNetworks.resize(++this->subject.networkCount);
|
|
|
+ this->subject.networks = m_subjectNetworks.data();
|
|
|
+
|
|
|
+ // Set fields in new ZT_Certificate_Network structure.
|
|
|
+ m_subjectNetworks.back().id = id;
|
|
|
+ Utils::copy< sizeof(ZT_Fingerprint) >(&(m_subjectNetworks.back().controller), &controller);
|
|
|
+
|
|
|
+ return &(m_subjectNetworks.back());
|
|
|
+}
|
|
|
+
|
|
|
+void Certificate::addSubjectCertificate(const uint8_t serialNo[ZT_SHA384_DIGEST_SIZE])
|
|
|
+{
|
|
|
+ // Store local copy of serial in m_serials container.
|
|
|
+ m_serials.push_back(Blob< ZT_SHA384_DIGEST_SIZE >(serialNo));
|
|
|
+
|
|
|
+ // Enlarge array of uint8_t pointers, set new pointer to local copy of serial, and set
|
|
|
+ // certificates to point to potentially reallocated array.
|
|
|
+ m_subjectCertificates.resize(++this->subject.certificateCount);
|
|
|
+ m_subjectCertificates.back() = m_serials.back().data;
|
|
|
+ this->subject.certificates = m_subjectCertificates.data();
|
|
|
}
|
|
|
|
|
|
-void IdentificationCertificate::addUpdateUrl(const char *url)
|
|
|
+void Certificate::addUpdateUrl(const char *url)
|
|
|
{
|
|
|
+ // Store local copy of URL.
|
|
|
m_strings.push_back(url);
|
|
|
+
|
|
|
+ // Add pointer to local copy to pointer array and update C structure to point to
|
|
|
+ // potentially reallocated array.
|
|
|
m_updateUrls.push_back(m_strings.back().c_str());
|
|
|
this->updateUrls = m_updateUrls.data();
|
|
|
this->updateUrlCount = (unsigned int)m_updateUrls.size();
|
|
|
}
|
|
|
|
|
|
-Vector< uint8_t > IdentificationCertificate::encode(const bool omitSignature) const
|
|
|
+Vector< uint8_t > Certificate::encode(const bool omitSignature) const
|
|
|
{
|
|
|
char tmp[256];
|
|
|
Vector< uint8_t > enc;
|
|
|
Dictionary d;
|
|
|
|
|
|
- d.add("v", (uint64_t)this->version);
|
|
|
- d.add("mP", (uint64_t)this->maxPathLength);
|
|
|
+ // A Dictionary is used to encode certificates as it's a common and extensible
|
|
|
+ // format. Custom packed formats are used for credentials as these are smaller
|
|
|
+ // and faster to marshal/unmarshal.
|
|
|
+
|
|
|
d.add("f", this->flags);
|
|
|
d.add("v0", this->validity[0]);
|
|
|
d.add("v1", this->validity[1]);
|
|
|
+ d.add("mP", (uint64_t)this->maxPathLength);
|
|
|
|
|
|
- d.add("s.n[]", (uint64_t)this->subject.nodeCount);
|
|
|
- for (unsigned int i = 0; i < this->subject.nodeCount; ++i) {
|
|
|
- d.addO(Dictionary::arraySubscript(tmp, "s.n[].i", i), *reinterpret_cast<const Identity *>(this->subject.nodes[i].identity));
|
|
|
- if (this->subject.nodes[i].locator)
|
|
|
- d.addO(Dictionary::arraySubscript(tmp, "s.n[].l", i), *reinterpret_cast<const Locator *>(this->subject.nodes[i].locator));
|
|
|
+ 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.nw[]", (uint64_t)this->subject.networkCount);
|
|
|
+ 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.nw[].i", i), this->subject.networks[i].id);
|
|
|
+ d.add(Dictionary::arraySubscript(tmp, "s.n$.i", i), this->subject.networks[i].id);
|
|
|
Fingerprint fp(this->subject.networks[i].controller);
|
|
|
- d.addO(Dictionary::arraySubscript(tmp, "s.nw[].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.n.c", this->subject.name.country);
|
|
@@ -152,10 +211,10 @@ Vector< uint8_t > IdentificationCertificate::encode(const bool omitSignature) co
|
|
|
d.add("iN.e", this->issuerName.email);
|
|
|
d.add("iN.ur", this->issuerName.url);
|
|
|
|
|
|
- d.add("uU[]", (uint64_t)this->updateUrlCount);
|
|
|
+ d.add("u$", (uint64_t)this->updateUrlCount);
|
|
|
if (this->updateUrls) {
|
|
|
for (unsigned int i = 0; i < this->updateUrlCount; ++i)
|
|
|
- d.add(Dictionary::arraySubscript(tmp, "uU[]", i), this->updateUrls[i]);
|
|
|
+ d.add(Dictionary::arraySubscript(tmp, "u$", i), this->updateUrls[i]);
|
|
|
}
|
|
|
|
|
|
if ((!omitSignature) && (this->signatureSize > 0) && (this->signatureSize <= sizeof(this->signature)))
|
|
@@ -165,9 +224,9 @@ Vector< uint8_t > IdentificationCertificate::encode(const bool omitSignature) co
|
|
|
return enc;
|
|
|
}
|
|
|
|
|
|
-bool IdentificationCertificate::decode(const Vector< uint8_t > &data)
|
|
|
+bool Certificate::decode(const Vector< uint8_t > &data)
|
|
|
{
|
|
|
- char tmp[256], tmp2[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
|
|
|
+ char tmp[256], tmp2[ZT_CERTIFICATE_MAX_STRING_LENGTH + 1];
|
|
|
|
|
|
clear();
|
|
|
|
|
@@ -175,21 +234,20 @@ bool IdentificationCertificate::decode(const Vector< uint8_t > &data)
|
|
|
if (!d.decode(data.data(), (unsigned int)data.size()))
|
|
|
return false;
|
|
|
|
|
|
- this->version = (unsigned int)d.getUI("v");
|
|
|
- this->maxPathLength = (unsigned int)d.getUI("mP");
|
|
|
this->flags = d.getUI("f");
|
|
|
this->validity[0] = (int64_t)d.getUI("v0");
|
|
|
this->validity[1] = (int64_t)d.getUI("v1");
|
|
|
+ this->maxPathLength = (unsigned int)d.getUI("mP");
|
|
|
|
|
|
- unsigned int cnt = (unsigned int)d.getUI("s.n[]");
|
|
|
+ unsigned int cnt = (unsigned int)d.getUI("s.i$");
|
|
|
for (unsigned int i = 0; i < cnt; ++i) {
|
|
|
- const Vector< uint8_t > &identityData = d[Dictionary::arraySubscript(tmp, "s.n[].i", i)];
|
|
|
+ const Vector< uint8_t > &identityData = d[Dictionary::arraySubscript(tmp, "s.i$.i", i)];
|
|
|
if (identityData.empty())
|
|
|
return false;
|
|
|
Identity id;
|
|
|
if (id.unmarshal(identityData.data(), (unsigned int)identityData.size()) <= 0)
|
|
|
return false;
|
|
|
- const Vector< uint8_t > &locatorData = d[Dictionary::arraySubscript(tmp, "s.n[].l", i)];
|
|
|
+ const Vector< uint8_t > &locatorData = d[Dictionary::arraySubscript(tmp, "s.i$.l", i)];
|
|
|
if (!locatorData.empty()) {
|
|
|
Locator loc;
|
|
|
if (loc.unmarshal(locatorData.data(), (unsigned int)locatorData.size()) <= 0)
|
|
@@ -200,13 +258,11 @@ bool IdentificationCertificate::decode(const Vector< uint8_t > &data)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- cnt = (unsigned int)d.getUI("s.nw[]");
|
|
|
+ cnt = (unsigned int)d.getUI("s.n$");
|
|
|
for (unsigned int i = 0; i < cnt; ++i) {
|
|
|
- const uint64_t nwid = d.getUI(Dictionary::arraySubscript(tmp, "s.nw[].i", i));
|
|
|
- if (nwid == 0)
|
|
|
- return false;
|
|
|
- const Vector< uint8_t > &fingerprintData = d[Dictionary::arraySubscript(tmp, "s.nw[].c", i)];
|
|
|
- if (fingerprintData.empty())
|
|
|
+ const uint64_t nwid = d.getUI(Dictionary::arraySubscript(tmp, "s.n$.i", i));
|
|
|
+ const Vector< uint8_t > &fingerprintData = d[Dictionary::arraySubscript(tmp, "s.n$.c", i)];
|
|
|
+ if ((nwid == 0) || (fingerprintData.empty()))
|
|
|
return false;
|
|
|
Fingerprint fp;
|
|
|
if (fp.unmarshal(fingerprintData.data(), (unsigned int)fingerprintData.size()) <= 0)
|
|
@@ -214,6 +270,14 @@ bool IdentificationCertificate::decode(const Vector< uint8_t > &data)
|
|
|
this->addSubjectNetwork(nwid, fp);
|
|
|
}
|
|
|
|
|
|
+ cnt = (unsigned int)d.getUI("s.c$");
|
|
|
+ for (unsigned int i=0;i<cnt;++i) {
|
|
|
+ const Vector< uint8_t > &serial = d[Dictionary::arraySubscript(tmp, "s.c$", i)];
|
|
|
+ if (serial.size() != ZT_SHA384_DIGEST_SIZE)
|
|
|
+ return false;
|
|
|
+ this->addSubjectCertificate(serial.data());
|
|
|
+ }
|
|
|
+
|
|
|
d.getS("s.n.c", this->subject.name.country, sizeof(this->subject.name.country));
|
|
|
d.getS("s.n.o", this->subject.name.organization, sizeof(this->subject.name.organization));
|
|
|
d.getS("s.n.u", this->subject.name.unit, sizeof(this->subject.name.unit));
|
|
@@ -247,9 +311,9 @@ bool IdentificationCertificate::decode(const Vector< uint8_t > &data)
|
|
|
d.getS("iN.e", this->issuerName.email, sizeof(this->issuerName.email));
|
|
|
d.getS("iN.ur", this->issuerName.url, sizeof(this->issuerName.url));
|
|
|
|
|
|
- cnt = (unsigned int)d.getUI("uU[]");
|
|
|
+ cnt = (unsigned int)d.getUI("u$");
|
|
|
for (unsigned int i = 0; i < cnt; ++i) {
|
|
|
- const char *const url = d.getS(Dictionary::arraySubscript(tmp, "uU[]", i), tmp2, sizeof(tmp2));
|
|
|
+ const char *const url = d.getS(Dictionary::arraySubscript(tmp, "u$", i), tmp2, sizeof(tmp2));
|
|
|
if (url)
|
|
|
addUpdateUrl(tmp2);
|
|
|
else return false;
|
|
@@ -267,19 +331,21 @@ bool IdentificationCertificate::decode(const Vector< uint8_t > &data)
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-bool IdentificationCertificate::sign(const Identity &issuer)
|
|
|
+bool Certificate::sign(const Identity &issuer)
|
|
|
{
|
|
|
Vector< uint8_t > enc(encode(true));
|
|
|
SHA384(this->serialNo, enc.data(), (unsigned int)enc.size());
|
|
|
return (this->signatureSize = issuer.sign(enc.data(), (unsigned int)enc.size(), this->signature, sizeof(this->signature))) > 0;
|
|
|
}
|
|
|
|
|
|
-bool IdentificationCertificate::verify() const
|
|
|
+bool Certificate::verify() const
|
|
|
{
|
|
|
- if (this->issuer) {
|
|
|
- Vector< uint8_t > enc(encode(true));
|
|
|
- return reinterpret_cast<const Identity *>(this->issuer)->verify(enc.data(), (unsigned int)enc.size(), this->signature, this->signatureSize);
|
|
|
- }
|
|
|
+ try {
|
|
|
+ if (this->issuer) {
|
|
|
+ Vector< uint8_t > enc(encode(true));
|
|
|
+ return reinterpret_cast<const Identity *>(this->issuer)->verify(enc.data(), (unsigned int)enc.size(), this->signature, this->signatureSize);
|
|
|
+ }
|
|
|
+ } catch ( ... ) {}
|
|
|
return false;
|
|
|
}
|
|
|
|