|
@@ -13,6 +13,7 @@
|
|
|
|
|
|
#include "Certificate.hpp"
|
|
|
#include "SHA512.hpp"
|
|
|
+#include "ECC384.hpp"
|
|
|
|
|
|
namespace ZeroTier {
|
|
|
|
|
@@ -47,6 +48,7 @@ Certificate &Certificate::operator=(const Certificate &cert)
|
|
|
this->subject.identityCount = 0;
|
|
|
this->subject.networkCount = 0;
|
|
|
this->subject.certificateCount = 0;
|
|
|
+ this->subject.updateUrlCount = 0;
|
|
|
|
|
|
for (unsigned int i = 0; i < cert.subject.identityCount; ++i) {
|
|
|
if (cert.subject.identities[i].identity) {
|
|
@@ -66,16 +68,16 @@ Certificate &Certificate::operator=(const Certificate &cert)
|
|
|
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.subject.updateUrls) {
|
|
|
+ for (unsigned int i = 0; i < cert.subject.updateUrlCount; ++i) {
|
|
|
+ if (cert.subject.updateUrls[i])
|
|
|
+ addUpdateUrl(cert.subject.updateUrls[i]);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- if (cert.updateUrls) {
|
|
|
- for (unsigned int i = 0; i < cert.updateUrlCount; ++i) {
|
|
|
- if (cert.updateUrls[i])
|
|
|
- addUpdateUrl(cert.updateUrls[i]);
|
|
|
- }
|
|
|
+ if (cert.issuer) {
|
|
|
+ m_identities.push_back(*reinterpret_cast<const Identity *>(cert.issuer));
|
|
|
+ this->issuer = &(m_identities.back());
|
|
|
}
|
|
|
|
|
|
return *this;
|
|
@@ -91,7 +93,7 @@ ZT_Certificate_Identity *Certificate::addSubjectNode(const Identity &id)
|
|
|
m_identities.push_back(id);
|
|
|
|
|
|
// 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().identity = &(m_identities.back());
|
|
|
m_subjectIdentities.back().locator = nullptr;
|
|
|
|
|
|
return &(m_subjectIdentities.back());
|
|
@@ -106,7 +108,7 @@ ZT_Certificate_Identity *Certificate::addSubjectNode(const Identity &id, const L
|
|
|
m_locators.push_back(loc);
|
|
|
|
|
|
// Set pointer to stored local copy of locator.
|
|
|
- n->locator = reinterpret_cast<ZT_Locator *>(&(m_locators.back()));
|
|
|
+ n->locator = &(m_locators.back());
|
|
|
|
|
|
return n;
|
|
|
}
|
|
@@ -127,7 +129,7 @@ ZT_Certificate_Network *Certificate::addSubjectNetwork(const uint64_t id, const
|
|
|
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));
|
|
|
+ m_serials.push_back(SHA384Hash(serialNo));
|
|
|
|
|
|
// Enlarge array of uint8_t pointers, set new pointer to local copy of serial, and set
|
|
|
// certificates to point to potentially reallocated array.
|
|
@@ -144,8 +146,8 @@ void Certificate::addUpdateUrl(const char *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();
|
|
|
+ this->subject.updateUrls = m_updateUrls.data();
|
|
|
+ this->subject.updateUrlCount = (unsigned int)m_updateUrls.size();
|
|
|
}
|
|
|
|
|
|
Vector< uint8_t > Certificate::encode(const bool omitSignature) const
|
|
@@ -159,42 +161,12 @@ Vector< uint8_t > Certificate::encode(const bool omitSignature) const
|
|
|
// and faster to marshal/unmarshal.
|
|
|
|
|
|
d.add("f", this->flags);
|
|
|
- d.add("v0", this->validity[0]);
|
|
|
- d.add("v1", this->validity[1]);
|
|
|
+ d.add("t", (uint64_t)this->timestamp);
|
|
|
+ d.add("v0", (uint64_t)this->validity[0]);
|
|
|
+ d.add("v1", (uint64_t)this->validity[1]);
|
|
|
d.add("mP", (uint64_t)this->maxPathLength);
|
|
|
|
|
|
- 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.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.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);
|
|
|
- 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);
|
|
|
+ m_encodeSubject(d, false);
|
|
|
|
|
|
if (this->issuer)
|
|
|
d.addO("i", *reinterpret_cast<const Identity *>(this->issuer));
|
|
@@ -211,12 +183,6 @@ Vector< uint8_t > Certificate::encode(const bool omitSignature) const
|
|
|
d.add("iN.e", this->issuerName.email);
|
|
|
d.add("iN.ur", this->issuerName.url);
|
|
|
|
|
|
- d.add("u$", (uint64_t)this->updateUrlCount);
|
|
|
- if (this->updateUrls) {
|
|
|
- for (unsigned int i = 0; i < this->updateUrlCount; ++i)
|
|
|
- d.add(Dictionary::arraySubscript(tmp, "u$", i), this->updateUrls[i]);
|
|
|
- }
|
|
|
-
|
|
|
if ((!omitSignature) && (this->signatureSize > 0) && (this->signatureSize <= sizeof(this->signature)))
|
|
|
d["si"].assign(this->signature, this->signature + this->signatureSize);
|
|
|
|
|
@@ -235,10 +201,13 @@ bool Certificate::decode(const Vector< uint8_t > &data)
|
|
|
return false;
|
|
|
|
|
|
this->flags = d.getUI("f");
|
|
|
+ this->timestamp = (int64_t)d.getUI("t");
|
|
|
this->validity[0] = (int64_t)d.getUI("v0");
|
|
|
this->validity[1] = (int64_t)d.getUI("v1");
|
|
|
this->maxPathLength = (unsigned int)d.getUI("mP");
|
|
|
|
|
|
+ this->subject.timestamp = (int64_t)d.getUI("s.t");
|
|
|
+
|
|
|
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.i$.i", i)];
|
|
@@ -271,13 +240,15 @@ bool Certificate::decode(const Vector< uint8_t > &data)
|
|
|
}
|
|
|
|
|
|
cnt = (unsigned int)d.getUI("s.c$");
|
|
|
- for (unsigned int i=0;i<cnt;++i) {
|
|
|
+ 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.sN", this->subject.name.serialNo, sizeof(this->subject.name.serialNo));
|
|
|
+ d.getS("s.n.cN", this->subject.name.commonName, sizeof(this->subject.name.commonName));
|
|
|
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));
|
|
@@ -285,8 +256,6 @@ bool Certificate::decode(const Vector< uint8_t > &data)
|
|
|
d.getS("s.n.p", this->subject.name.province, sizeof(this->subject.name.province));
|
|
|
d.getS("s.n.sA", this->subject.name.streetAddress, sizeof(this->subject.name.streetAddress));
|
|
|
d.getS("s.n.pC", this->subject.name.postalCode, sizeof(this->subject.name.postalCode));
|
|
|
- d.getS("s.n.cN", this->subject.name.commonName, sizeof(this->subject.name.commonName));
|
|
|
- d.getS("s.n.sN", this->subject.name.serialNo, sizeof(this->subject.name.serialNo));
|
|
|
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));
|
|
|
|
|
@@ -299,6 +268,8 @@ bool Certificate::decode(const Vector< uint8_t > &data)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ d.getS("iN.sN", this->issuerName.serialNo, sizeof(this->issuerName.serialNo));
|
|
|
+ d.getS("iN.cN", this->issuerName.commonName, sizeof(this->issuerName.commonName));
|
|
|
d.getS("iN.c", this->issuerName.country, sizeof(this->issuerName.country));
|
|
|
d.getS("iN.o", this->issuerName.organization, sizeof(this->issuerName.organization));
|
|
|
d.getS("iN.u", this->issuerName.unit, sizeof(this->issuerName.unit));
|
|
@@ -306,8 +277,6 @@ bool Certificate::decode(const Vector< uint8_t > &data)
|
|
|
d.getS("iN.p", this->issuerName.province, sizeof(this->issuerName.province));
|
|
|
d.getS("iN.sA", this->issuerName.streetAddress, sizeof(this->issuerName.streetAddress));
|
|
|
d.getS("iN.pC", this->issuerName.postalCode, sizeof(this->issuerName.postalCode));
|
|
|
- d.getS("iN.cN", this->issuerName.commonName, sizeof(this->issuerName.commonName));
|
|
|
- d.getS("iN.sN", this->issuerName.serialNo, sizeof(this->issuerName.serialNo));
|
|
|
d.getS("iN.e", this->issuerName.email, sizeof(this->issuerName.email));
|
|
|
d.getS("iN.ur", this->issuerName.url, sizeof(this->issuerName.url));
|
|
|
|
|
@@ -338,15 +307,117 @@ bool Certificate::sign(const Identity &issuer)
|
|
|
return (this->signatureSize = issuer.sign(enc.data(), (unsigned int)enc.size(), this->signature, sizeof(this->signature))) > 0;
|
|
|
}
|
|
|
|
|
|
-bool Certificate::verify() const
|
|
|
+ZT_CertificateError Certificate::verify() const
|
|
|
{
|
|
|
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);
|
|
|
+ const Vector< uint8_t > enc(encode(true));
|
|
|
+ if (!reinterpret_cast<const Identity *>(this->issuer)->verify(enc.data(), (unsigned int)enc.size(), this->signature, this->signatureSize))
|
|
|
+ return ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE;
|
|
|
+ } else {
|
|
|
+ return ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE;
|
|
|
}
|
|
|
- } catch ( ... ) {}
|
|
|
- return false;
|
|
|
+
|
|
|
+ if (this->subject.uniqueIdProofSignatureSize > 0) {
|
|
|
+ static_assert(ZT_ECC384_SIGNATURE_SIZE <= ZT_CERTIFICATE_MAX_SIGNATURE_SIZE, "overflow");
|
|
|
+ static_assert((ZT_ECC384_PUBLIC_KEY_SIZE + 1) <= ZT_CERTIFICATE_MAX_UNIQUE_ID_SIZE, "overflow");
|
|
|
+ if (
|
|
|
+ (this->subject.uniqueIdProofSignatureSize != ZT_ECC384_SIGNATURE_SIZE) ||
|
|
|
+ (this->subject.uniqueIdSize != (ZT_ECC384_PUBLIC_KEY_SIZE + 1)) ||
|
|
|
+ (this->subject.uniqueId[0] != ZT_CERTIFICATE_UNIQUE_ID_PUBLIC_KEY_TYPE_NIST_P_384))
|
|
|
+ return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF;
|
|
|
+ Dictionary tmp;
|
|
|
+ m_encodeSubject(tmp, true);
|
|
|
+ Vector< uint8_t > enc;
|
|
|
+ tmp.encode(enc);
|
|
|
+ uint8_t h[ZT_SHA384_DIGEST_SIZE];
|
|
|
+ SHA384(h, enc.data(), (unsigned int)enc.size());
|
|
|
+ if (!ECC384ECDSAVerify(this->subject.uniqueId + 1, h, this->subject.uniqueIdProofSignature))
|
|
|
+ return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF;
|
|
|
+ } else if (this->subject.uniqueIdSize > ZT_CERTIFICATE_MAX_UNIQUE_ID_SIZE) {
|
|
|
+ return ZT_CERTIFICATE_ERROR_INVALID_FORMAT;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (unsigned int i = 0; i < this->subject.identityCount; ++i) {
|
|
|
+ if (!this->subject.identities[i].identity)
|
|
|
+ return ZT_CERTIFICATE_ERROR_MISSING_REQUIRED_FIELDS;
|
|
|
+ if (!reinterpret_cast<const Identity *>(this->subject.identities[i].identity)->locallyValidate())
|
|
|
+ return ZT_CERTIFICATE_ERROR_INVALID_IDENTITY;
|
|
|
+ if (this->subject.identities[i].locator) {
|
|
|
+ if (!reinterpret_cast<const Locator *>(this->subject.identities[i].locator)->verify(*reinterpret_cast<const Identity *>(this->subject.identities[i].identity)))
|
|
|
+ return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (unsigned int i = 0; i < this->subject.networkCount; ++i) {
|
|
|
+ if (!this->subject.networks[i].id)
|
|
|
+ return ZT_CERTIFICATE_ERROR_MISSING_REQUIRED_FIELDS;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this->subject.updateUrlCount) {
|
|
|
+ if (!this->subject.updateUrls)
|
|
|
+ return ZT_CERTIFICATE_ERROR_INVALID_FORMAT;
|
|
|
+ for (unsigned int i = 0; i < this->subject.updateUrlCount; ++i) {
|
|
|
+ if (!this->subject.updateUrls[i])
|
|
|
+ return ZT_CERTIFICATE_ERROR_MISSING_REQUIRED_FIELDS;
|
|
|
+ }
|
|
|
+ } else if (this->subject.updateUrls) {
|
|
|
+ return ZT_CERTIFICATE_ERROR_INVALID_FORMAT;
|
|
|
+ }
|
|
|
+ } catch (...) {}
|
|
|
+
|
|
|
+ return ZT_CERTIFICATE_ERROR_NONE;
|
|
|
+}
|
|
|
+
|
|
|
+void Certificate::m_encodeSubject(Dictionary &d, bool omitUniqueIdProofSignature) const
|
|
|
+{
|
|
|
+ char tmp[256];
|
|
|
+
|
|
|
+ d.add("s.t", (uint64_t)this->subject.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.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.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.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.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);
|
|
|
}
|
|
|
|
|
|
} // namespace ZeroTier
|