Some cleanup, and fix a really obscure bug in Certificate.

This commit is contained in:
Adam Ierymenko 2020-07-09 10:12:24 -07:00
parent 8b1c691a5e
commit 1eacbdf374
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
13 changed files with 367 additions and 268 deletions

View file

@ -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
}

View file

@ -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,23 +60,31 @@ Certificate &Certificate::operator=(const ZT_Certificate &cert)
this->signature = nullptr;
this->signatureSize = 0;
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)
if (cert.subject.identities[i].locator) {
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));
} else {
addSubjectIdentity(*reinterpret_cast<const Identity *>(cert.subject.identities[i].identity));
}
}
}
}
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);
}
}
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) {
for (unsigned int i = 0; i < cert.subject.updateURLCount; ++i) {
@ -99,8 +105,8 @@ Certificate &Certificate::operator=(const ZT_Certificate &cert)
}
if (cert.issuer) {
m_identities.push_back(*reinterpret_cast<const Identity *>(cert.issuer));
this->issuer = &(m_identities.back());
m_identities.push_front(*reinterpret_cast<const Identity *>(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,11 +235,13 @@ Vector< uint8_t > Certificate::encode(const bool omitSignature) const
if (this->flags != 0)
d.add("f", this->flags);
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->extendedAttributes) && (this->extendedAttributesSize > 0))
d["x"].assign(this->extendedAttributes, this->extendedAttributes + this->extendedAttributesSize);
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<const Identity *>(&(m_identities.back()));
m_identities.push_front(id);
this->issuer = reinterpret_cast<const Identity *>(&(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<const ZT_Identity *>(&(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);
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, "s.i$.i", i), *reinterpret_cast<const Identity *>(s.identities[i].identity));
d.addO(Dictionary::arraySubscript(tmp, sizeof(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.addO(Dictionary::arraySubscript(tmp, sizeof(tmp), "s.i$.l", i), *reinterpret_cast<const Locator *>(s.identities[i].locator));
}
}
d.add("s.n$", (uint64_t)s.networkCount);
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, "s.n$.i", i), s.networks[i].id);
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, "s.n$.c", i), fp);
d.addO(Dictionary::arraySubscript(tmp, sizeof(tmp), "s.nw$.c", i), fp);
}
}
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, "s.c$", i)].assign(s.certificates[i], s.certificates[i] + ZT_SHA384_DIGEST_SIZE);
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<const ZeroTier::Identity *>(signer)))
return ZT_RESULT_ERROR_BAD_PARAMETER;
ZeroTier::Vector< uint8_t > enc(c.encode());
try {
const ZeroTier::ScopedPtr< ZeroTier::Certificate > c(new ZeroTier::Certificate(*cert));
if (!c->sign(*reinterpret_cast<const ZeroTier::Identity *>(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;
}
}

View file

@ -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;

View file

@ -28,6 +28,7 @@
#ifdef __CPP11__
#include <atomic>
#include <unordered_map>
#include <forward_list>
#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 > >
{};

View file

@ -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

View file

@ -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 <uint8_t> &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 <uint8_t> &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 <uint8_t> &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

View file

@ -37,7 +37,7 @@ public:
{ memoryZero(this); }
ZT_INLINE Fingerprint(const ZT_Fingerprint &fp) noexcept
{ Utils::copy<sizeof(ZT_Fingerprint)>(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<ZT_ADDRESS_LENGTH_HEX>(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<ZT_FINGERPRINT_HASH_SIZE>(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<ZT_FINGERPRINT_HASH_SIZE>(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<ZT_FINGERPRINT_HASH_SIZE>(hash,data + ZT_ADDRESS_LENGTH);
Utils::copy< ZT_FINGERPRINT_HASH_SIZE >(hash, data + ZT_ADDRESS_LENGTH);
return ZT_FINGERPRINT_MARSHAL_SIZE;
}

View file

@ -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;
}
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;
}
}
return -1;

View file

@ -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<void *>(&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:

View file

@ -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);

View file

@ -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<uint64_t *>(dest) = *reinterpret_cast<const uint64_t *>(src);
*reinterpret_cast<uint64_t *>(reinterpret_cast<uint8_t *>(dest) + 8) = *reinterpret_cast<const uint64_t *>(reinterpret_cast<const uint8_t *>(src) + 8);
}
template<>
ZT_INLINE void copy<24>(void *dest, const void *src) noexcept
{
*reinterpret_cast<uint64_t *>(dest) = *reinterpret_cast<const uint64_t *>(src);
*reinterpret_cast<uint64_t *>(reinterpret_cast<uint8_t *>(dest) + 8) = *reinterpret_cast<const uint64_t *>(reinterpret_cast<const uint8_t *>(src) + 8);
*reinterpret_cast<uint64_t *>(reinterpret_cast<uint8_t *>(dest) + 16) = *reinterpret_cast<const uint64_t *>(reinterpret_cast<const uint8_t *>(src) + 16);
}
#endif
/**
* Copy memory block whose size is known at run time

View file

@ -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))
}
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) {

View file

@ -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 {