diff --git a/cmd/zt_service_tests/certificate.go b/cmd/zt_service_tests/certificate.go index b5c273b82..c1c780f0b 100644 --- a/cmd/zt_service_tests/certificate.go +++ b/cmd/zt_service_tests/certificate.go @@ -29,6 +29,12 @@ func TestCertificate() bool { return false } + uniqueId, uniqueIdPrivate, err := zerotier.NewCertificateSubjectUniqueId(zerotier.CertificateUniqueIdTypeNistP384) + if err != nil { + fmt.Printf(" Error generating unique ID: %s", err.Error()) + return false + } + var c zerotier.Certificate c.SerialNo = make([]byte, 48) @@ -39,6 +45,7 @@ func TestCertificate() bool { c.Timestamp = 5678 c.Validity[0] = 1010 c.Validity[1] = 2020 + c.Subject.Timestamp = 31337 c.Subject.Identities = append(c.Subject.Identities, zerotier.CertificateIdentity{ Identity: id, @@ -65,8 +72,8 @@ func TestCertificate() bool { c.Subject.Name.Email = "j" c.Subject.Name.URL = "k" c.Subject.Name.Host = "l" - c.Subject.UniqueID = []byte("asdf") - c.Subject.UniqueIDProofSignature = []byte("ghij") + c.Subject.UniqueID = uniqueId + c.Issuer = id c.IssuerName.SerialNo = "m" c.IssuerName.CommonName = "n" @@ -80,6 +87,7 @@ func TestCertificate() bool { c.IssuerName.Email = "v" c.IssuerName.URL = "w" c.IssuerName.Host = "x" + c.ExtendedAttributes = c.SerialNo c.MaxPathLength = 9999 c.Signature = []byte("qwerty") @@ -96,9 +104,8 @@ func TestCertificate() bool { return false } - j, _ := json.MarshalIndent(c, "", " ") - j2, _ := json.MarshalIndent(c2, "", " ") - + j, _ := json.Marshal(c) + j2, _ := json.Marshal(c2) if !bytes.Equal(j, j2) { j, _ = json.MarshalIndent(c, "", " ") fmt.Print(" Deep equality test failed: certificates do not match! (see dumps below)\n\n") @@ -130,5 +137,28 @@ func TestCertificate() bool { } fmt.Println("OK") + fmt.Printf("Checking certificate CSR sign/verify... ") + csr, err := zerotier.NewCertificateCSR(&c.Subject, uniqueId, uniqueIdPrivate) + if err != nil { + fmt.Printf("CSR generate FAILED (%s)\n", err.Error()) + return false + } + fmt.Printf("CSR size: %d ",len(csr)) + csr2, err := zerotier.NewCertificateFromBytes(csr, false) + if err != nil { + fmt.Printf("CSR decode FAILED (%s)\n", err.Error()) + return false + } + signedCert, err := csr2.Sign(id) + if err != nil { + fmt.Printf("CSR sign FAILED (%s)\n", err.Error()) + return false + } + if len(signedCert.Signature) == 0 { + fmt.Println("CSR sign FAILED (no signature found)", err.Error()) + return false + } + fmt.Println("OK") + return true } diff --git a/core/Certificate.cpp b/core/Certificate.cpp index f4bb399e7..4d58ae2dc 100644 --- a/core/Certificate.cpp +++ b/core/Certificate.cpp @@ -14,6 +14,7 @@ #include "Certificate.hpp" #include "SHA512.hpp" #include "ECC384.hpp" +#include "ScopedPtr.hpp" namespace ZeroTier { @@ -24,10 +25,7 @@ Certificate::Certificate() noexcept } Certificate::Certificate(const ZT_Certificate &apiCert) -{ - ZT_Certificate *const sup = this; - Utils::copy< sizeof(ZT_Certificate) >(sup, &apiCert); -} +{ *this = apiCert; } Certificate::Certificate(const Certificate &cert) { *this = cert; } @@ -62,22 +60,30 @@ Certificate &Certificate::operator=(const ZT_Certificate &cert) this->signature = nullptr; this->signatureSize = 0; - for (unsigned int i = 0; i < cert.subject.identityCount; ++i) { - if (cert.subject.identities[i].identity) { - if (cert.subject.identities[i].locator) - addSubjectIdentity(*reinterpret_cast(cert.subject.identities[i].identity), *reinterpret_cast(cert.subject.identities[i].locator)); - else addSubjectIdentity(*reinterpret_cast(cert.subject.identities[i].identity)); + if (cert.subject.identities) { + for (unsigned int i = 0; i < cert.subject.identityCount; ++i) { + if (cert.subject.identities[i].identity) { + if (cert.subject.identities[i].locator) { + addSubjectIdentity(*reinterpret_cast(cert.subject.identities[i].identity), *reinterpret_cast(cert.subject.identities[i].locator)); + } else { + addSubjectIdentity(*reinterpret_cast(cert.subject.identities[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); + if (cert.subject.networks) { + 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.certificateCount; ++i) { - if (cert.subject.certificates[i]) - addSubjectCertificate(cert.subject.certificates[i]); + if (cert.subject.certificates) { + for (unsigned int i = 0; i < cert.subject.certificateCount; ++i) { + if (cert.subject.certificates[i]) + addSubjectCertificate(cert.subject.certificates[i]); + } } if (cert.subject.updateURLs) { @@ -99,8 +105,8 @@ Certificate &Certificate::operator=(const ZT_Certificate &cert) } if (cert.issuer) { - m_identities.push_back(*reinterpret_cast(cert.issuer)); - this->issuer = &(m_identities.back()); + m_identities.push_front(*reinterpret_cast(cert.issuer)); + this->issuer = &(m_identities.front()); } if ((cert.extendedAttributes) && (cert.extendedAttributesSize > 0)) { @@ -120,16 +126,16 @@ Certificate &Certificate::operator=(const ZT_Certificate &cert) ZT_Certificate_Identity *Certificate::addSubjectIdentity(const Identity &id) { - // 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_identities.push_front(id); + m_identities.front().erasePrivateKey(); - // Set ZT_Certificate_Identity struct fields to point to local copy of identity. - m_subjectIdentities.back().identity = &(m_identities.back()); + // Enlarge array of ZT_Certificate_Identity structs and set pointer to potentially reallocated array. + m_subjectIdentities.push_back(ZT_Certificate_Identity()); + m_subjectIdentities.back().identity = &(m_identities.front()); m_subjectIdentities.back().locator = nullptr; + this->subject.identities = m_subjectIdentities.data(); + this->subject.identityCount = (unsigned int)m_subjectIdentities.size(); return &(m_subjectIdentities.back()); } @@ -140,10 +146,10 @@ ZT_Certificate_Identity *Certificate::addSubjectIdentity(const Identity &id, con ZT_Certificate_Identity *const n = addSubjectIdentity(id); // Store local copy of locator. - m_locators.push_back(loc); + m_locators.push_front(loc); // Set pointer to stored local copy of locator. - n->locator = &(m_locators.back()); + n->locator = &(m_locators.front()); return n; } @@ -164,23 +170,26 @@ 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(SHA384Hash(serialNo)); + m_serials.push_front(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. m_subjectCertificates.resize(++this->subject.certificateCount); - m_subjectCertificates.back() = m_serials.back().bytes(); + m_subjectCertificates.back() = m_serials.front().bytes(); this->subject.certificates = m_subjectCertificates.data(); } void Certificate::addSubjectUpdateUrl(const char *url) { + if ((!url) || (!url[0])) + return; + // Store local copy of URL. - m_strings.push_back(url); + m_strings.push_front(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()); + m_updateUrls.push_back(m_strings.front().c_str()); this->subject.updateURLs = m_updateUrls.data(); this->subject.updateURLCount = (unsigned int)m_updateUrls.size(); } @@ -193,6 +202,28 @@ void Certificate::setExtendedAttributes(const Dictionary &x) this->extendedAttributesSize = (unsigned int)m_extendedAttributes.size(); } +bool Certificate::setSubjectUniqueId(const uint8_t uniqueId[ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE], const uint8_t uniqueIdPrivate[ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE]) +{ + m_subjectUniqueId.assign(uniqueId, uniqueId + ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE); + this->subject.uniqueId = m_subjectUniqueId.data(); + this->subject.uniqueIdSize = ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE; + + Dictionary d; + m_encodeSubject(this->subject, d, true); + Vector< uint8_t > enc; + d.encode(enc); + uint8_t h[ZT_SHA384_DIGEST_SIZE]; + SHA384(h, enc.data(), (unsigned int)enc.size()); + + m_subjectUniqueIdProofSignature.resize(ZT_ECC384_SIGNATURE_SIZE); + ECC384ECDSASign(uniqueIdPrivate, h, m_subjectUniqueIdProofSignature.data()); + + this->subject.uniqueIdProofSignature = m_subjectUniqueIdProofSignature.data(); + this->subject.uniqueIdProofSignatureSize = ZT_ECC384_SIGNATURE_SIZE; + + return true; +} + Vector< uint8_t > Certificate::encode(const bool omitSignature) const { Vector< uint8_t > enc; @@ -204,12 +235,14 @@ Vector< uint8_t > Certificate::encode(const bool omitSignature) const if (this->flags != 0) d.add("f", this->flags); - d.add("t", (uint64_t)this->timestamp); - d.add("v#0", (uint64_t)this->validity[0]); - d.add("v#1", (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); + if (this->timestamp > 0) + d.add("t", (uint64_t)this->timestamp); + if (this->validity[0] > 0) + d.add("v#0", (uint64_t)this->validity[0]); + if (this->validity[1] > 0) + d.add("v#1", (uint64_t)this->validity[1]); + if (this->maxPathLength > 0) + d.add("mP", (uint64_t)this->maxPathLength); m_encodeSubject(this->subject, d, false); @@ -244,8 +277,8 @@ Vector< uint8_t > Certificate::encode(const bool omitSignature) const if ((this->extendedAttributes) && (this->extendedAttributesSize > 0)) d["x"].assign(this->extendedAttributes, this->extendedAttributes + this->extendedAttributesSize); - if ((!omitSignature) && (this->signatureSize > 0) && (this->signature)) - d["si"].assign(this->signature, this->signature + this->signatureSize); + if ((!omitSignature) && (this->signature) && (this->signatureSize > 0)) + d["S"].assign(this->signature, this->signature + this->signatureSize); d.encode(enc); return enc; @@ -253,7 +286,7 @@ Vector< uint8_t > Certificate::encode(const bool omitSignature) const bool Certificate::decode(const void *const data, const unsigned int len) { - char tmp[256], tmp2[ZT_CERTIFICATE_MAX_STRING_LENGTH + 1]; + char tmp[32], tmp2[ZT_CERTIFICATE_MAX_STRING_LENGTH + 1]; Dictionary d; if (!d.decode(data, len)) @@ -271,46 +304,52 @@ bool Certificate::decode(const void *const data, const unsigned int len) 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)]; - if (identityData.empty()) + const Vector< uint8_t > &identityData = d[Dictionary::arraySubscript(tmp, sizeof(tmp), "s.i$.i", i)]; + if (identityData.empty()) { return false; + } Identity id; - if (id.unmarshal(identityData.data(), (unsigned int)identityData.size()) <= 0) + if (id.unmarshal(identityData.data(), (unsigned int)identityData.size()) <= 0) { return false; - const Vector< uint8_t > &locatorData = d[Dictionary::arraySubscript(tmp, "s.i$.l", i)]; + } + const Vector< uint8_t > &locatorData = d[Dictionary::arraySubscript(tmp, sizeof(tmp), "s.i$.l", i)]; if (!locatorData.empty()) { Locator loc; - if (loc.unmarshal(locatorData.data(), (unsigned int)locatorData.size()) <= 0) + if (loc.unmarshal(locatorData.data(), (unsigned int)locatorData.size()) <= 0) { return false; + } this->addSubjectIdentity(id, loc); } else { this->addSubjectIdentity(id); } } - cnt = (unsigned int)d.getUI("s.n$"); + cnt = (unsigned int)d.getUI("s.nw$"); for (unsigned int i = 0; i < cnt; ++i) { - 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())) + const uint64_t nwid = d.getUI(Dictionary::arraySubscript(tmp, sizeof(tmp), "s.nw$.i", i)); + const Vector< uint8_t > &fingerprintData = d[Dictionary::arraySubscript(tmp, sizeof(tmp), "s.nw$.c", i)]; + if ((nwid == 0) || (fingerprintData.empty())) { return false; + } Fingerprint fp; - if (fp.unmarshal(fingerprintData.data(), (unsigned int)fingerprintData.size()) <= 0) + if (fp.unmarshal(fingerprintData.data(), (unsigned int)fingerprintData.size()) <= 0) { return false; + } 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) + const Vector< uint8_t > &serial = d[Dictionary::arraySubscript(tmp, sizeof(tmp), "s.c$", i)]; + if (serial.size() != ZT_SHA384_DIGEST_SIZE) { return false; + } this->addSubjectCertificate(serial.data()); } cnt = (unsigned int)d.getUI("s.u$"); for (unsigned int i = 0; i < cnt; ++i) - addSubjectUpdateUrl(d.getS(Dictionary::arraySubscript(tmp, "s.u$", i), tmp, sizeof(tmp))); + addSubjectUpdateUrl(d.getS(Dictionary::arraySubscript(tmp, sizeof(tmp), "s.u$", i), tmp2, sizeof(tmp2))); 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)); @@ -340,8 +379,8 @@ bool Certificate::decode(const void *const data, const unsigned int len) if (!issuerData.empty()) { Identity id; if (id.unmarshal(issuerData.data(), (int)issuerData.size()) > 0) { - m_identities.push_back(id); - this->issuer = reinterpret_cast(&(m_identities.back())); + m_identities.push_front(id); + this->issuer = reinterpret_cast(&(m_identities.front())); } } @@ -360,10 +399,12 @@ bool Certificate::decode(const void *const data, const unsigned int len) cnt = (unsigned int)d.getUI("u$"); for (unsigned int i = 0; i < cnt; ++i) { - const char *const url = d.getS(Dictionary::arraySubscript(tmp, "u$", i), tmp2, sizeof(tmp2)); - if (url) - addSubjectUpdateUrl(tmp2); - else return false; + const char *const url = d.getS(Dictionary::arraySubscript(tmp, sizeof(tmp), "u$", i), tmp2, sizeof(tmp2)); + if ((url) && (*url != 0)) { + addSubjectUpdateUrl(url); + } else { + return false; + } } m_extendedAttributes = d["x"]; @@ -372,7 +413,7 @@ bool Certificate::decode(const void *const data, const unsigned int len) this->extendedAttributesSize = (unsigned int)m_extendedAttributes.size(); } - m_signature = d["si"]; + m_signature = d["S"]; if (!m_signature.empty()) { this->signature = m_signature.data(); this->signatureSize = (unsigned int)m_signature.size(); @@ -395,16 +436,23 @@ Vector< uint8_t > Certificate::encodeCSR() bool Certificate::sign(const Identity &issuer) { - Vector< uint8_t > enc(encode(true)); + m_identities.push_front(issuer); + m_identities.front().erasePrivateKey(); + this->issuer = reinterpret_cast(&(m_identities.front())); + + const Vector< uint8_t > enc(encode(true)); SHA384(this->serialNo, enc.data(), (unsigned int)enc.size()); + uint8_t sig[ZT_SIGNATURE_BUFFER_SIZE]; - const unsigned int sigSize = issuer.sign(enc.data(), (unsigned int)enc.size(), sig, sizeof(sig)); + const unsigned int sigSize = issuer.sign(enc.data(), (unsigned int)enc.size(), sig, ZT_SIGNATURE_BUFFER_SIZE); + if (sigSize > 0) { m_signature.assign(sig, sig + sigSize); this->signature = m_signature.data(); this->signatureSize = sigSize; return true; } + m_signature.clear(); this->signature = nullptr; this->signatureSize = 0; @@ -425,7 +473,7 @@ ZT_CertificateError Certificate::verify() const if (this->subject.uniqueIdProofSignatureSize > 0) { if ( (this->subject.uniqueIdProofSignatureSize != ZT_ECC384_SIGNATURE_SIZE) || - (this->subject.uniqueIdSize != (ZT_ECC384_PUBLIC_KEY_SIZE + 1)) || + (this->subject.uniqueIdSize != ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE) || (this->subject.uniqueId[0] != ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384)) return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF; Dictionary tmp; @@ -456,7 +504,7 @@ ZT_CertificateError Certificate::verify() const return ZT_CERTIFICATE_ERROR_MISSING_REQUIRED_FIELDS; } - if (this->subject.updateURLCount) { + if (this->subject.updateURLCount > 0) { if (!this->subject.updateURLs) return ZT_CERTIFICATE_ERROR_INVALID_FORMAT; for (unsigned int i = 0; i < this->subject.updateURLCount; ++i) { @@ -471,26 +519,6 @@ ZT_CertificateError Certificate::verify() const return ZT_CERTIFICATE_ERROR_NONE; } -bool Certificate::setSubjectUniqueId(const uint8_t uniqueId[ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE], const uint8_t uniqueIdPrivate[ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE]) -{ - m_subjectUniqueId.assign(uniqueId, uniqueId + ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE); - this->subject.uniqueId = m_subjectUniqueId.data(); - this->subject.uniqueIdSize = ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE; - - Dictionary d; - m_encodeSubject(this->subject, d, true); - Vector< uint8_t > enc; - d.encode(enc); - uint8_t h[ZT_SHA384_DIGEST_SIZE]; - SHA384(h, enc.data(), (unsigned int)enc.size()); - m_subjectUniqueIdProofSignature.resize(ZT_ECC384_SIGNATURE_SIZE); - ECC384ECDSASign(uniqueIdPrivate, h, m_subjectUniqueIdProofSignature.data()); - this->subject.uniqueIdProofSignature = m_subjectUniqueIdProofSignature.data(); - this->subject.uniqueIdProofSignatureSize = ZT_ECC384_SIGNATURE_SIZE; - - return true; -} - void Certificate::m_clear() { ZT_Certificate *const sup = this; @@ -513,35 +541,41 @@ void Certificate::m_clear() void Certificate::m_encodeSubject(const ZT_Certificate_Subject &s, Dictionary &d, bool omitUniqueIdProofSignature) { - char tmp[64]; + char tmp[32]; d.add("s.t", (uint64_t)s.timestamp); - 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(s.identities[i].identity)); - if (s.identities[i].locator) - d.addO(Dictionary::arraySubscript(tmp, "s.i$.l", i), *reinterpret_cast(s.identities[i].locator)); + if (s.identities) { + 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, sizeof(tmp), "s.i$.i", i), *reinterpret_cast(s.identities[i].identity)); + if (s.identities[i].locator) + d.addO(Dictionary::arraySubscript(tmp, sizeof(tmp), "s.i$.l", i), *reinterpret_cast(s.identities[i].locator)); + } } - 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); + if (s.networks) { + d.add("s.nw$", (uint64_t)s.networkCount); + for (unsigned int i = 0; i < s.networkCount; ++i) { + d.add(Dictionary::arraySubscript(tmp, sizeof(tmp), "s.nw$.i", i), s.networks[i].id); + Fingerprint fp(s.networks[i].controller); + d.addO(Dictionary::arraySubscript(tmp, sizeof(tmp), "s.nw$.c", i), fp); + } } - 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); + if (s.certificates) { + 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, sizeof(tmp), "s.c$", i)].assign(s.certificates[i], s.certificates[i] + ZT_SHA384_DIGEST_SIZE); + } } - d.add("s.u$", (uint64_t)s.updateURLCount); if (s.updateURLs) { + d.add("s.u$", (uint64_t)s.updateURLCount); for (unsigned int i = 0; i < s.updateURLCount; ++i) - d.add(Dictionary::arraySubscript(tmp, "s.u$", i), s.updateURLs[i]); + d.add(Dictionary::arraySubscript(tmp, sizeof(tmp), "s.u$", i), s.updateURLs[i]); } if (s.name.country[0]) @@ -569,9 +603,9 @@ void Certificate::m_encodeSubject(const ZT_Certificate_Subject &s, Dictionary &d if (s.name.host[0]) d.add("s.n.h", s.name.host); - if ((s.uniqueIdSize > 0) && (s.uniqueId != nullptr)) + if ((s.uniqueId) && (s.uniqueIdSize > 0)) d["s.uI"].assign(s.uniqueId, s.uniqueId + s.uniqueIdSize); - if ((!omitUniqueIdProofSignature) && (s.uniqueIdProofSignatureSize > 0) && (s.uniqueIdProofSignature != nullptr)) + if ((!omitUniqueIdProofSignature) && (s.uniqueIdProofSignature) && (s.uniqueIdProofSignatureSize > 0)) d["s.uS"].assign(s.uniqueIdProofSignature, s.uniqueIdProofSignature + s.uniqueIdProofSignatureSize); } @@ -586,7 +620,7 @@ int ZT_Certificate_newSubjectUniqueId( void *uniqueIdPrivate, int *uniqueIdPrivateSize) { - switch(type) { + switch (type) { case ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384: if ((*uniqueIdSize < ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE) || (*uniqueIdPrivateSize < ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE)) return ZT_RESULT_ERROR_BAD_PARAMETER; @@ -631,15 +665,22 @@ int ZT_Certificate_sign( { if (!cert) return ZT_RESULT_ERROR_BAD_PARAMETER; - ZeroTier::Certificate c(*cert); - if (!c.sign(*reinterpret_cast(signer))) - return ZT_RESULT_ERROR_BAD_PARAMETER; - ZeroTier::Vector< uint8_t > enc(c.encode()); - if ((int)enc.size() > *signedCertSize) - return ZT_RESULT_ERROR_BAD_PARAMETER; - ZeroTier::Utils::copy(signedCert, enc.data(), (unsigned int)enc.size()); - *signedCertSize = (int)enc.size(); - return ZT_RESULT_OK; + + try { + const ZeroTier::ScopedPtr< ZeroTier::Certificate > c(new ZeroTier::Certificate(*cert)); + if (!c->sign(*reinterpret_cast(signer))) + return ZT_RESULT_ERROR_INTERNAL; + + const ZeroTier::Vector< uint8_t > enc(c->encode()); + if ((int)enc.size() > *signedCertSize) + return ZT_RESULT_ERROR_BAD_PARAMETER; + ZeroTier::Utils::copy(signedCert, enc.data(), (unsigned int)enc.size()); + *signedCertSize = (int)enc.size(); + + return ZT_RESULT_OK; + } catch ( ... ) { + return ZT_RESULT_FATAL_ERROR_INTERNAL; + } } enum ZT_CertificateError ZT_Certificate_decode( @@ -649,7 +690,7 @@ enum ZT_CertificateError ZT_Certificate_decode( int verify) { try { - if (!decodedCert) + if ((!decodedCert) || (!cert) || (certSize <= 0)) return ZT_CERTIFICATE_ERROR_INVALID_FORMAT; *decodedCert = nullptr; ZeroTier::Certificate *const c = new ZeroTier::Certificate(); @@ -658,7 +699,7 @@ enum ZT_CertificateError ZT_Certificate_decode( return ZT_CERTIFICATE_ERROR_INVALID_FORMAT; } if (verify) { - ZT_CertificateError err = c->verify(); + const ZT_CertificateError err = c->verify(); if (err != ZT_CERTIFICATE_ERROR_NONE) { delete c; return err; @@ -666,7 +707,7 @@ enum ZT_CertificateError ZT_Certificate_decode( } *decodedCert = c; return ZT_CERTIFICATE_ERROR_NONE; - } catch ( ... ) { + } catch (...) { return ZT_CERTIFICATE_ERROR_INVALID_FORMAT; } } @@ -693,7 +734,7 @@ enum ZT_CertificateError ZT_Certificate_verify(const ZT_Certificate *cert) if (!cert) return ZT_CERTIFICATE_ERROR_INVALID_FORMAT; return ZeroTier::Certificate(*cert).verify(); - } catch ( ... ) { + } catch (...) { return ZT_CERTIFICATE_ERROR_INVALID_FORMAT; } } @@ -704,7 +745,7 @@ const ZT_Certificate *ZT_Certificate_clone(const ZT_Certificate *cert) if (!cert) return nullptr; return (const ZT_Certificate *)(new ZeroTier::Certificate(*cert)); - } catch ( ... ) { + } catch (...) { return nullptr; } } diff --git a/core/Certificate.hpp b/core/Certificate.hpp index f30d7db36..671605448 100644 --- a/core/Certificate.hpp +++ b/core/Certificate.hpp @@ -114,6 +114,17 @@ public: */ void setExtendedAttributes(const Dictionary &x); + /** + * Set the unique ID of this certificate's subject + * + * This must be done after all other fields in the subject are set. + * + * @param uniqueId Unique ID + * @param uniqueIdPrivate Private key associated with unique ID to prove ownership of it + * @return True if successful + */ + bool setSubjectUniqueId(const uint8_t uniqueId[ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE], const uint8_t uniqueIdPrivate[ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE]); + /** * Marshal this certificate in binary form * @@ -159,17 +170,6 @@ public: */ ZT_CertificateError verify() const; - /** - * Set the unique ID of this certificate's subject - * - * This must be done after all other fields in the subject are set. - * - * @param uniqueId Unique ID - * @param uniqueIdPrivate Private key associated with unique ID to prove ownership of it - * @return True if successful - */ - bool setSubjectUniqueId(const uint8_t uniqueId[ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE], const uint8_t uniqueIdPrivate[ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE]); - /** * Create a subject unique ID and corresponding private key required for use * @@ -211,10 +211,10 @@ private: // 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 // change. - List< Identity > m_identities; - List< Locator > m_locators; - List< String > m_strings; - List< SHA384Hash > m_serials; + ForwardList< Identity > m_identities; + ForwardList< Locator > m_locators; + ForwardList< String > m_strings; + ForwardList< SHA384Hash > m_serials; // These are stored in a vector because the memory needs to be contiguous. Vector< ZT_Certificate_Identity > m_subjectIdentities; diff --git a/core/Containers.hpp b/core/Containers.hpp index 9c1d66f62..64ec5f86b 100644 --- a/core/Containers.hpp +++ b/core/Containers.hpp @@ -28,6 +28,7 @@ #ifdef __CPP11__ #include #include +#include #endif namespace ZeroTier { @@ -128,6 +129,20 @@ template< typename V > class List : public std::list< V > {}; +#ifdef __CPP11__ + +template< typename V > +class ForwardList : public std::forward_list< V > +{}; + +#else + +template< typename V > +class ForwardList : public std::list< V > +{}; + +#endif + template< typename V > class Set : public std::set< V, std::less< V > > {}; diff --git a/core/Dictionary.cpp b/core/Dictionary.cpp index 57b7767a5..3267af96d 100644 --- a/core/Dictionary.cpp +++ b/core/Dictionary.cpp @@ -22,23 +22,15 @@ Dictionary::~Dictionary() {} Vector< uint8_t > &Dictionary::operator[](const char *const k) -{ return m_entries[s_key(k)]; } +{ return m_entries[k]; } const Vector< uint8_t > &Dictionary::operator[](const char *const k) const { static const Vector< uint8_t > s_emptyEntry; - const SortedMap< String, Vector< uint8_t > >::const_iterator e(m_entries.find(s_key(k))); + const SortedMap< String, Vector< uint8_t > >::const_iterator e(m_entries.find(k)); return (e == m_entries.end()) ? s_emptyEntry : e->second; } -void Dictionary::add(const char *k, bool v) -{ - Vector< uint8_t > &e = (*this)[k]; - e.resize(2); - e[0] = (uint8_t)(v ? '1' : '0'); - e[1] = 0; -} - void Dictionary::add(const char *k, const Address &v) { char tmp[ZT_ADDRESS_STRING_SIZE_MAX]; @@ -66,24 +58,6 @@ void Dictionary::add(const char *k, const void *data, unsigned int len) } } -bool Dictionary::getB(const char *k, bool dfl) const -{ - const Vector< uint8_t > &e = (*this)[k]; - if (!e.empty()) { - switch ((char)e[0]) { - case '1': - case 't': - case 'T': - case 'y': - case 'Y': - return true; - default: - return false; - } - } - return dfl; -} - uint64_t Dictionary::getUI(const char *k, uint64_t dfl) const { char tmp[32]; @@ -97,23 +71,22 @@ char *Dictionary::getS(const char *k, char *v, const unsigned int cap) const { if (cap == 0) // sanity check return v; + const Vector< uint8_t > &e = (*this)[k]; if (e.empty()) { v[0] = 0; return v; } - unsigned int i = 0; - const unsigned int last = cap - 1; - for (;;) { + + for (unsigned int i = 0, last = (cap - 1);; ++i) { if ((i >= last) || (i >= (unsigned int)e.size())) { v[i] = 0; break; } - if ((v[i] = (char)e[i]) == 0) { + if ((v[i] = (char)e[i]) == 0) break; - } - ++i; } + return v; } @@ -131,7 +104,6 @@ void Dictionary::encode(Vector< uint8_t > &out) const s_appendValueByte(out, *i); out.push_back((uint8_t)'\n'); } - out.push_back(0); } bool Dictionary::decode(const void *data, unsigned int len) @@ -176,20 +148,24 @@ bool Dictionary::decode(const void *data, unsigned int len) } else { if (c == (uint8_t)'=') { v = &m_entries[k]; - } else if ((c < 33) || (c > 126) || (c == 92)) { - return false; } else { k.push_back(c); } } - } else break; + } else { + break; + } } return true; } -char *Dictionary::arraySubscript(char buf[256],const char *name,const unsigned long sub) noexcept +char *Dictionary::arraySubscript(char *buf, unsigned int bufSize, const char *name, const unsigned long sub) noexcept { - for(unsigned int i=0;i<(256 - 17);++i) { + if (bufSize < 17) { // sanity check + buf[0] = 0; + return buf; + } + for (unsigned int i = 0; i < (bufSize - 17); ++i) { if ((buf[i] = name[i]) == 0) { buf[i++] = '#'; Utils::hex(sub, buf + i); @@ -200,19 +176,4 @@ char *Dictionary::arraySubscript(char buf[256],const char *name,const unsigned l return buf; } -String Dictionary::s_key(const char *k) noexcept -{ - String buf; - if (likely(k != nullptr)) { - for (;;) { - const char c = *(k++); - if ((c >= 33) && (c <= 126) && (c != 61) && (c != 92)) // printable ASCII with no spaces, equals, or backslash - buf.push_back(c); - else if (c == 0) - break; - } - } - return buf; -} - } // namespace ZeroTier diff --git a/core/Dictionary.hpp b/core/Dictionary.hpp index bc368b326..a9b6be690 100644 --- a/core/Dictionary.hpp +++ b/core/Dictionary.hpp @@ -42,9 +42,10 @@ class Identity; class Dictionary { public: - typedef SortedMap< String, Vector < uint8_t > >::const_iterator const_iterator; + typedef SortedMap< String, Vector< uint8_t > >::const_iterator const_iterator; Dictionary(); + ~Dictionary(); /** @@ -53,7 +54,7 @@ public: * @param k Key to look up * @return Reference to value */ - Vector &operator[](const char *k); + Vector< uint8_t > &operator[](const char *k); /** * Get a const reference to a value @@ -61,7 +62,7 @@ public: * @param k Key to look up * @return Reference to value or to empty vector if not found */ - const Vector &operator[](const char *k) const; + const Vector< uint8_t > &operator[](const char *k) const; /** * @return Start of key->value pairs @@ -75,11 +76,6 @@ public: ZT_INLINE const_iterator end() const noexcept { return m_entries.end(); } - /** - * Add a boolean as '1' or '0' - */ - void add(const char *k, bool v); - /** * Add an integer as a hexadecimal string value * @@ -87,7 +83,10 @@ public: * @param v Integer to set, will be cast to uint64_t and stored as hex */ ZT_INLINE void add(const char *const k, const uint64_t v) - { char buf[17]; add(k, Utils::hex((uint64_t)(v), buf)); } + { + char buf[24]; + add(k, Utils::hex((uint64_t)(v), buf)); + } /** * Add an integer as a hexadecimal string value @@ -96,7 +95,10 @@ public: * @param v Integer to set, will be cast to uint64_t and stored as hex */ ZT_INLINE void add(const char *const k, const int64_t v) - { char buf[17]; add(k, Utils::hex((uint64_t)(v), buf)); } + { + char buf[24]; + add(k, Utils::hex((uint64_t)(v), buf)); + } /** * Add an address in 10-digit hex string format @@ -113,15 +115,6 @@ public: */ void add(const char *k, const void *data, unsigned int len); - /** - * Get a boolean - * - * @param k Key to look up - * @param dfl Default value (default: false) - * @return Value of key or default if not found - */ - bool getB(const char *k, bool dfl = false) const; - /** * Get an integer * @@ -171,13 +164,14 @@ public: template< typename T > ZT_INLINE bool addO(const char *k, T &obj) { - uint8_t tmp[4096]; - static_assert(sizeof(tmp) >= T::marshalSizeMax(),"buffer too small"); - int l = obj.marshal(tmp); + Vector< uint8_t > &d = (*this)[k]; + d.resize(T::marshalSizeMax()); + const int l = obj.marshal(d.data()); if (l > 0) { - (*this)[k].assign(tmp, tmp + l); + d.resize(l); return true; } + d.clear(); return false; } @@ -203,7 +197,7 @@ public: * * @param out String encoded dictionary */ - void encode(Vector &out) const; + void encode(Vector< uint8_t > &out) const; /** * Decode a string encoded dictionary @@ -362,7 +356,7 @@ public: template< typename V, typename T > static ZT_INLINE int appendObject(V &out, const char *const k, const T &v) { - uint8_t tmp[4096]; // large enough for any current object + uint8_t tmp[2048]; // large enough for any current object if (T::marshalSizeMax() > sizeof(tmp)) return -1; const int mlen = v.marshal(tmp); @@ -379,7 +373,7 @@ public: * @param sub Subscript index * @return Pointer to 'buf' */ - static char *arraySubscript(char buf[256],const char *name,const unsigned long sub) noexcept; + static char *arraySubscript(char *buf, unsigned int bufSize, const char *name, const unsigned long sub) noexcept; private: template< typename V > @@ -417,20 +411,17 @@ private: { for (;;) { const char c = *(k++); - if ((c >= 33) && (c <= 126) && (c != 61) && (c != 92)) // printable ASCII with no spaces, equals, or backslash - out.push_back((uint8_t)c); - else if (c == 0) + if (c == 0) break; + out.push_back((uint8_t)c); } out.push_back((uint8_t)'='); } - static String s_key(const char *k) noexcept; - // Dictionary maps need to be sorted so that they always encode in the same order // to yield blobs that can be hashed and signed reproducibly. Other than for areas // where dictionaries are signed and verified the order doesn't matter. - SortedMap < String, Vector< uint8_t > > m_entries; + SortedMap< String, Vector< uint8_t > > m_entries; }; } // namespace ZeroTier diff --git a/core/Fingerprint.hpp b/core/Fingerprint.hpp index 04d4c1fd5..46956fa47 100644 --- a/core/Fingerprint.hpp +++ b/core/Fingerprint.hpp @@ -37,7 +37,7 @@ public: { memoryZero(this); } ZT_INLINE Fingerprint(const ZT_Fingerprint &fp) noexcept - { Utils::copy(this, &fp); } + { Utils::copy< sizeof(ZT_Fingerprint) >(this, &fp); } /** * @return True if hash is not all zero (missing/unspecified) @@ -59,7 +59,12 @@ public: } return s; } - ZT_INLINE String toString() const { char tmp[ZT_FINGERPRINT_STRING_SIZE_MAX]; return String(toString(tmp)); } + + ZT_INLINE String toString() const + { + char tmp[ZT_FINGERPRINT_STRING_SIZE_MAX]; + return String(toString(tmp)); + } /** * Set this fingerprint to a base32-encoded string @@ -71,18 +76,18 @@ public: { if (!s) return false; - const int l = (int) strlen(s); + const int l = (int)strlen(s); if (l < ZT_ADDRESS_LENGTH_HEX) return false; char a[ZT_ADDRESS_LENGTH_HEX + 1]; - Utils::copy(a, s); + Utils::copy< ZT_ADDRESS_LENGTH_HEX >(a, s); a[ZT_ADDRESS_LENGTH_HEX] = 0; this->address = Utils::hexStrToU64(a) & ZT_ADDRESS_MASK; if (l > (ZT_ADDRESS_LENGTH_HEX + 1)) { if (Utils::b32d(s + (ZT_ADDRESS_LENGTH_HEX + 1), this->hash, ZT_FINGERPRINT_HASH_SIZE) != ZT_FINGERPRINT_HASH_SIZE) return false; } else { - Utils::zero(this->hash); + Utils::zero< ZT_FINGERPRINT_HASH_SIZE >(this->hash); } return true; } @@ -120,7 +125,7 @@ public: ZT_INLINE int marshal(uint8_t data[ZT_FINGERPRINT_MARSHAL_SIZE]) const noexcept { Address(this->address).copyTo(data); - Utils::copy(data + ZT_ADDRESS_LENGTH,this->hash); + Utils::copy< ZT_FINGERPRINT_HASH_SIZE >(data + ZT_ADDRESS_LENGTH, this->hash); return ZT_FINGERPRINT_MARSHAL_SIZE; } @@ -129,7 +134,7 @@ public: if (unlikely(len < ZT_FINGERPRINT_MARSHAL_SIZE)) return -1; this->address = Address(data); - Utils::copy(hash,data + ZT_ADDRESS_LENGTH); + Utils::copy< ZT_FINGERPRINT_HASH_SIZE >(hash, data + ZT_ADDRESS_LENGTH); return ZT_FINGERPRINT_MARSHAL_SIZE; } diff --git a/core/Identity.cpp b/core/Identity.cpp index 305ab69bf..914f06820 100644 --- a/core/Identity.cpp +++ b/core/Identity.cpp @@ -258,6 +258,7 @@ unsigned int Identity::sign(const void *data, unsigned int len, void *sig, unsig C25519::sign(m_priv, m_pub, data, len, sig); return ZT_C25519_SIGNATURE_LEN; } + break; case P384: if (siglen >= ZT_ECC384_SIGNATURE_SIZE) { // SECURITY: signatures also include the public keys to further enforce their coupling. @@ -267,6 +268,7 @@ unsigned int Identity::sign(const void *data, unsigned int len, void *sig, unsig ECC384ECDSASign(m_priv + ZT_C25519_COMBINED_PRIVATE_KEY_SIZE, h, (uint8_t *)sig); return ZT_ECC384_SIGNATURE_SIZE; } + break; } } return 0; @@ -350,6 +352,8 @@ char *Identity::toString(bool includePrivate, char buf[ZT_IDENTITY_STRING_BUFFER *p = (char)0; return buf; } + default: + buf[0] = 0; } return nullptr; @@ -445,10 +449,9 @@ int Identity::marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX], const bool inc data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE] = ZT_C25519_COMBINED_PRIVATE_KEY_SIZE; Utils::copy< ZT_C25519_COMBINED_PRIVATE_KEY_SIZE >(data + ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE + 1, m_priv); return ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE + 1 + ZT_C25519_COMBINED_PRIVATE_KEY_SIZE; - } else { - data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE] = 0; - return ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE + 1; } + data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE] = 0; + return ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE + 1; case P384: data[ZT_ADDRESS_LENGTH] = (uint8_t)P384; @@ -457,10 +460,9 @@ int Identity::marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX], const bool inc data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE; Utils::copy< ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE >(data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1, m_priv); return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE; - } else { - data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = 0; - return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1; } + data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = 0; + return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1; } return -1; diff --git a/core/Identity.hpp b/core/Identity.hpp index 81e8a972c..f988e8d83 100644 --- a/core/Identity.hpp +++ b/core/Identity.hpp @@ -68,6 +68,12 @@ public: memoryZero(this); } + ZT_INLINE Identity(const Identity &id) noexcept + { + Utils::memoryLock(this, sizeof(Identity)); + Utils::copy< sizeof(Identity) >(this, &id); + } + /** * Construct identity from string * @@ -88,6 +94,13 @@ public: Utils::burn(reinterpret_cast(&this->m_priv), sizeof(this->m_priv)); } + ZT_INLINE Identity &operator=(const Identity &id) noexcept + { + if (likely(this != &id)) + Utils::copy< sizeof(Identity) >(this, &id); + return *this; + } + /** * Set identity to NIL value (all zero) */ @@ -211,6 +224,15 @@ public: */ bool fromString(const char *str); + /** + * Erase any private key in this identity object + */ + ZT_INLINE void erasePrivateKey() noexcept + { + Utils::burn(m_priv, sizeof(m_priv)); + m_hasPrivate = false; + } + /** * @return True if this identity contains something */ @@ -222,20 +244,27 @@ public: ZT_INLINE bool operator==(const Identity &id) const noexcept { return (m_fp == id.m_fp); } + ZT_INLINE bool operator!=(const Identity &id) const noexcept { return !(*this == id); } + ZT_INLINE bool operator<(const Identity &id) const noexcept { return (m_fp < id.m_fp); } + ZT_INLINE bool operator>(const Identity &id) const noexcept { return (id < *this); } + ZT_INLINE bool operator<=(const Identity &id) const noexcept { return !(id < *this); } + ZT_INLINE bool operator>=(const Identity &id) const noexcept { return !(*this < id); } static constexpr int marshalSizeMax() noexcept { return ZT_IDENTITY_MARSHAL_SIZE_MAX; } + int marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX], bool includePrivate = false) const noexcept; + int unmarshal(const uint8_t *data, int len) noexcept; private: diff --git a/core/Topology.cpp b/core/Topology.cpp index 44429884b..7cfaff07c 100644 --- a/core/Topology.cpp +++ b/core/Topology.cpp @@ -20,7 +20,7 @@ static const SharedPtr< const Certificate > s_nullCert; Topology::Topology(const RuntimeEnvironment *renv, void *tPtr, const int64_t now) : RR(renv) { - char tmp[256]; + char tmp[32]; Vector< uint8_t > trustData(RR->node->stateObjectGet(tPtr, ZT_STATE_OBJECT_TRUST_STORE, Utils::ZERO256)); Dictionary d; @@ -30,20 +30,20 @@ Topology::Topology(const RuntimeEnvironment *renv, void *tPtr, const int64_t now const unsigned long certCount = (unsigned long)d.getUI("c$"); for (unsigned long idx = 0; idx < certCount; ++idx) { uint64_t id[6]; - const Vector< uint8_t > &serialNo = d[Dictionary::arraySubscript(tmp, "c$.s", idx)]; + const Vector< uint8_t > &serialNo = d[Dictionary::arraySubscript(tmp, sizeof(tmp), "c$.s", idx)]; if (serialNo.size() == ZT_SHA384_DIGEST_SIZE) { Utils::copy< 48 >(id, serialNo.data()); Certificate cert; Vector< uint8_t > enc(RR->node->stateObjectGet(tPtr, ZT_STATE_OBJECT_CERT, id)); if (cert.decode(enc.data(), (unsigned int)enc.size())) - addCertificate(tPtr, cert, now, (unsigned int)d.getUI(Dictionary::arraySubscript(tmp, "c$.lt", idx)), false, false, false); + addCertificate(tPtr, cert, now, (unsigned int)d.getUI(Dictionary::arraySubscript(tmp, sizeof(tmp), "c$.lt", idx)), false, false, false); } } const unsigned long localRootCount = (unsigned long)d.getUI("lr$"); for (unsigned long idx = 0; idx < localRootCount; ++idx) { Identity lr; - if (d.getO(Dictionary::arraySubscript(tmp, "lr$.i", idx), lr)) { + if (d.getO(Dictionary::arraySubscript(tmp, sizeof(tmp), "lr$.i", idx), lr)) { if (lr) m_roots[lr].insert(s_nullCert); } @@ -456,7 +456,7 @@ void Topology::m_writeTrustStore_l_roots_certs(void *tPtr) const { // assumes m_roots_l and m_certs_l are locked for write - char tmp[256]; + char tmp[32]; Dictionary d; d.add("v", (uint64_t)0); // version @@ -464,15 +464,15 @@ void Topology::m_writeTrustStore_l_roots_certs(void *tPtr) const unsigned long idx = 0; d.add("c$", (uint64_t)m_certs.size()); for (Map< SHA384Hash, std::pair< SharedPtr< const Certificate >, unsigned int > >::const_iterator c(m_certs.begin()); c != m_certs.end(); ++c) { - d[Dictionary::arraySubscript(tmp, "c$.s", idx)].assign(c->first.data, c->first.data + ZT_SHA384_DIGEST_SIZE); - d.add(Dictionary::arraySubscript(tmp, "c$.lt", idx), (uint64_t)c->second.second); + d[Dictionary::arraySubscript(tmp, sizeof(tmp), "c$.s", idx)].assign(c->first.data, c->first.data + ZT_SHA384_DIGEST_SIZE); + d.add(Dictionary::arraySubscript(tmp, sizeof(tmp), "c$.lt", idx), (uint64_t)c->second.second); ++idx; } unsigned long localRootCount = 0; for (Map< Identity, Set< SharedPtr< const Certificate > > >::const_iterator r(m_roots.begin()); r != m_roots.end();) { if (r->second.find(s_nullCert) != r->second.end()) - d.addO(Dictionary::arraySubscript(tmp, "lr$.i", localRootCount++), r->first); + d.addO(Dictionary::arraySubscript(tmp, sizeof(tmp), "lr$.i", localRootCount++), r->first); } d.add("lr$", (uint64_t)localRootCount); diff --git a/core/Utils.hpp b/core/Utils.hpp index e145d2770..759574374 100644 --- a/core/Utils.hpp +++ b/core/Utils.hpp @@ -733,7 +733,7 @@ static ZT_INLINE void copy(void *dest, const void *src) noexcept #endif } -// Avoid rep/movsb startup time for some small common sizes. +#ifndef ZT_NO_UNALIGNED_ACCESS template<> ZT_INLINE void copy<4>(void *dest, const void *src) noexcept { @@ -756,13 +756,7 @@ ZT_INLINE void copy<16>(void *dest, const void *src) noexcept *reinterpret_cast(dest) = *reinterpret_cast(src); *reinterpret_cast(reinterpret_cast(dest) + 8) = *reinterpret_cast(reinterpret_cast(src) + 8); } -template<> -ZT_INLINE void copy<24>(void *dest, const void *src) noexcept -{ - *reinterpret_cast(dest) = *reinterpret_cast(src); - *reinterpret_cast(reinterpret_cast(dest) + 8) = *reinterpret_cast(reinterpret_cast(src) + 8); - *reinterpret_cast(reinterpret_cast(dest) + 16) = *reinterpret_cast(reinterpret_cast(src) + 16); -} +#endif /** * Copy memory block whose size is known at run time diff --git a/pkg/zerotier/certificate.go b/pkg/zerotier/certificate.go index be0f5642b..31cf13819 100644 --- a/pkg/zerotier/certificate.go +++ b/pkg/zerotier/certificate.go @@ -18,6 +18,7 @@ package zerotier import "C" import ( + "encoding/json" "fmt" "runtime" "unsafe" @@ -136,13 +137,11 @@ func NewCertificateFromBytes(cert []byte, verify bool) (*Certificate, error) { ver = 1 } cerr := C.ZT_Certificate_decode((**C.ZT_Certificate)(unsafe.Pointer(&dec)), unsafe.Pointer(&cert[0]), C.int(len(cert)), ver) - if dec != unsafe.Pointer(nil) { - defer C.ZT_Certificate_delete((*C.ZT_Certificate)(dec)) - } + defer C.ZT_Certificate_delete((*C.ZT_Certificate)(dec)) if cerr != 0 { return nil, certificateErrorToError(int(cerr)) } - if dec == unsafe.Pointer(nil) { + if dec == unsafe.Pointer(uintptr(0)) { return nil, ErrInternal } @@ -309,7 +308,7 @@ func (c *Certificate) CCertificate() *CCertificate { } } cc.subject.identities = &subjectIdentities[0] - cc.subject.identityCount = C.uint(len(c.Subject.Identities)) + cc.subject.identityCount = C.uint(len(subjectIdentities)) } if len(c.Subject.Networks) > 0 { @@ -322,7 +321,7 @@ func (c *Certificate) CCertificate() *CCertificate { } } cc.subject.networks = &subjectNetworks[0] - cc.subject.networkCount = C.uint(len(c.Subject.Networks)) + cc.subject.networkCount = C.uint(len(subjectNetworks)) } if len(c.Subject.Certificates) > 0 { @@ -334,7 +333,7 @@ func (c *Certificate) CCertificate() *CCertificate { subjectCertificates[i] = uintptr(unsafe.Pointer(&cert[0])) } cc.subject.certificates = (**C.uint8_t)(unsafe.Pointer(&subjectCertificates[0])) - cc.subject.certificateCount = C.uint(len(c.Subject.Certificates)) + cc.subject.certificateCount = C.uint(len(subjectCertificates)) } if len(c.Subject.UpdateURLs) > 0 { @@ -345,7 +344,7 @@ func (c *Certificate) CCertificate() *CCertificate { subjectUpdateURLs[i] = uintptr(unsafe.Pointer(&subjectUpdateURLsData[0][0])) } cc.subject.updateURLs = (**C.char)(unsafe.Pointer(&subjectUpdateURLs[0])) - cc.subject.updateURLCount = C.uint(len(c.Subject.UpdateURLs)) + cc.subject.updateURLCount = C.uint(len(subjectUpdateURLs)) } cStrCopy(unsafe.Pointer(&cc.subject.name.serialNo[0]), CertificateMaxStringLength+1, c.Subject.Name.SerialNo) @@ -402,8 +401,8 @@ func (c *Certificate) CCertificate() *CCertificate { cc.signatureSize = C.uint(len(c.Signature)) } - // HACK: pass pointer to cc as uintptr to disable Go's protection against go pointers to - // go pointers, as the C function called here will make a deep clone and then we are going + // HACK: pass pointer to cc as uintptr to disable Go's protection against "Go pointers to + // Go pointers," as the C function called here will make a deep clone and then we are going // to throw away 'cc' and its components. cc2 := &CCertificate{C: unsafe.Pointer(C._ZT_Certificate_clone2(C.uintptr_t(uintptr(unsafe.Pointer(&cc)))))} runtime.SetFinalizer(cc2, func(obj interface{}) { @@ -427,6 +426,27 @@ func (c *Certificate) Marshal() ([]byte, error) { return append(make([]byte, 0, int(encodedSize)), encoded[0:int(encodedSize)]...), nil } +// Sign signs this certificate and returns a new one with signature and issuer filled out. +// This should only be used after decoding a CSR with NewCertificateFromBytes. The non-subject +// parts of this Certificate, if any, are ignored. A new Certificate is returned with a completed +// signature. +func (c *Certificate) Sign(id *Identity) (*Certificate, error) { + if id == nil || !id.HasPrivate() || !id.initCIdentityPtr() { + return nil, ErrInvalidParameter + } + ctmp := c.CCertificate() + if ctmp == nil { + return nil, ErrInternal + } + var signedCert [16384]byte + signedCertSize := C.int(16384) + rv := int(C.ZT_Certificate_sign((*C.ZT_Certificate)(ctmp.C), id.cid, unsafe.Pointer(&signedCert[0]), &signedCertSize)) + if rv != 0 { + return nil, fmt.Errorf("signing failed: error %d", rv) + } + return NewCertificateFromBytes(signedCert[0:int(signedCertSize)], true) +} + // Verify returns nil on success or a certificate error if there is a problem with this certificate. func (c *Certificate) Verify() error { cc := c.CCertificate() @@ -436,6 +456,12 @@ func (c *Certificate) Verify() error { return certificateErrorToError(int(C.ZT_Certificate_verify((*C.ZT_Certificate)(cc.C)))) } +// JSON returns this certificate as a human-readable indented JSON string. +func (c *Certificate) JSON() string { + j, _ := json.MarshalIndent(c, "", " ") + return string(j) +} + // NewCertificateSubjectUniqueId creates a new certificate subject unique ID and corresponding private key. // Right now only one type is supported: CertificateUniqueIdTypeNistP384 func NewCertificateSubjectUniqueId(uniqueIdType int) (id []byte, priv []byte, err error) { @@ -444,13 +470,14 @@ func NewCertificateSubjectUniqueId(uniqueIdType int) (id []byte, priv []byte, er return } id = make([]byte, int(C.ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE)) - priv = make([]byte, int(C.ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE)) + priv = make([]byte, int(C.ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE)) idSize := C.int(len(id)) idPrivateSize := C.int(len(priv)) - if C.ZT_Certificate_newSubjectUniqueId((C.enum_ZT_CertificateUniqueIdType)(uniqueIdType), unsafe.Pointer(&id[0]), &idSize, unsafe.Pointer(&priv[0]), &idPrivateSize) != 0 { + rv := int(C.ZT_Certificate_newSubjectUniqueId((C.enum_ZT_CertificateUniqueIdType)(uniqueIdType), unsafe.Pointer(&id[0]), &idSize, unsafe.Pointer(&priv[0]), &idPrivateSize)) + if rv != 0 { id = nil priv = nil - err = ErrInvalidParameter + err = fmt.Errorf("error %d", rv) return } if int(idSize) != len(id) || int(idPrivateSize) != len(priv) { diff --git a/pkg/zerotier/identity.go b/pkg/zerotier/identity.go index 1e15c2ddb..b10dee291 100644 --- a/pkg/zerotier/identity.go +++ b/pkg/zerotier/identity.go @@ -78,7 +78,11 @@ func newIdentityFromCIdentity(cid unsafe.Pointer) (*Identity, error) { // initCIdentityPtr returns a pointer to the core ZT_Identity instance or nil/0 on error. func (id *Identity) initCIdentityPtr() bool { if uintptr(id.cid) == 0 { - idCStr := C.CString(id.String()) + str := id.PrivateKeyString() + if len(str) == 0 { + str = id.String() + } + idCStr := C.CString(str) defer C.free(unsafe.Pointer(idCStr)) id.cid = C.ZT_Identity_fromString(idCStr) if uintptr(id.cid) == 0 {