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 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 var c zerotier.Certificate
c.SerialNo = make([]byte, 48) c.SerialNo = make([]byte, 48)
@ -39,6 +45,7 @@ func TestCertificate() bool {
c.Timestamp = 5678 c.Timestamp = 5678
c.Validity[0] = 1010 c.Validity[0] = 1010
c.Validity[1] = 2020 c.Validity[1] = 2020
c.Subject.Timestamp = 31337 c.Subject.Timestamp = 31337
c.Subject.Identities = append(c.Subject.Identities, zerotier.CertificateIdentity{ c.Subject.Identities = append(c.Subject.Identities, zerotier.CertificateIdentity{
Identity: id, Identity: id,
@ -65,8 +72,8 @@ func TestCertificate() bool {
c.Subject.Name.Email = "j" c.Subject.Name.Email = "j"
c.Subject.Name.URL = "k" c.Subject.Name.URL = "k"
c.Subject.Name.Host = "l" c.Subject.Name.Host = "l"
c.Subject.UniqueID = []byte("asdf") c.Subject.UniqueID = uniqueId
c.Subject.UniqueIDProofSignature = []byte("ghij")
c.Issuer = id c.Issuer = id
c.IssuerName.SerialNo = "m" c.IssuerName.SerialNo = "m"
c.IssuerName.CommonName = "n" c.IssuerName.CommonName = "n"
@ -80,6 +87,7 @@ func TestCertificate() bool {
c.IssuerName.Email = "v" c.IssuerName.Email = "v"
c.IssuerName.URL = "w" c.IssuerName.URL = "w"
c.IssuerName.Host = "x" c.IssuerName.Host = "x"
c.ExtendedAttributes = c.SerialNo c.ExtendedAttributes = c.SerialNo
c.MaxPathLength = 9999 c.MaxPathLength = 9999
c.Signature = []byte("qwerty") c.Signature = []byte("qwerty")
@ -96,9 +104,8 @@ func TestCertificate() bool {
return false return false
} }
j, _ := json.MarshalIndent(c, "", " ") j, _ := json.Marshal(c)
j2, _ := json.MarshalIndent(c2, "", " ") j2, _ := json.Marshal(c2)
if !bytes.Equal(j, j2) { if !bytes.Equal(j, j2) {
j, _ = json.MarshalIndent(c, "", " ") j, _ = json.MarshalIndent(c, "", " ")
fmt.Print(" Deep equality test failed: certificates do not match! (see dumps below)\n\n") 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.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 return true
} }

View file

@ -14,6 +14,7 @@
#include "Certificate.hpp" #include "Certificate.hpp"
#include "SHA512.hpp" #include "SHA512.hpp"
#include "ECC384.hpp" #include "ECC384.hpp"
#include "ScopedPtr.hpp"
namespace ZeroTier { namespace ZeroTier {
@ -24,10 +25,7 @@ Certificate::Certificate() noexcept
} }
Certificate::Certificate(const ZT_Certificate &apiCert) Certificate::Certificate(const ZT_Certificate &apiCert)
{ { *this = apiCert; }
ZT_Certificate *const sup = this;
Utils::copy< sizeof(ZT_Certificate) >(sup, &apiCert);
}
Certificate::Certificate(const Certificate &cert) Certificate::Certificate(const Certificate &cert)
{ *this = cert; } { *this = cert; }
@ -62,22 +60,30 @@ Certificate &Certificate::operator=(const ZT_Certificate &cert)
this->signature = nullptr; this->signature = nullptr;
this->signatureSize = 0; this->signatureSize = 0;
for (unsigned int i = 0; i < cert.subject.identityCount; ++i) { if (cert.subject.identities) {
if (cert.subject.identities[i].identity) { for (unsigned int i = 0; i < cert.subject.identityCount; ++i) {
if (cert.subject.identities[i].locator) if (cert.subject.identities[i].identity) {
addSubjectIdentity(*reinterpret_cast<const Identity *>(cert.subject.identities[i].identity), *reinterpret_cast<const Locator *>(cert.subject.identities[i].locator)); if (cert.subject.identities[i].locator) {
else addSubjectIdentity(*reinterpret_cast<const Identity *>(cert.subject.identities[i].identity)); addSubjectIdentity(*reinterpret_cast<const Identity *>(cert.subject.identities[i].identity), *reinterpret_cast<const Locator *>(cert.subject.identities[i].locator));
} else {
addSubjectIdentity(*reinterpret_cast<const Identity *>(cert.subject.identities[i].identity));
}
}
} }
} }
for (unsigned int i = 0; i < cert.subject.networkCount; ++i) { if (cert.subject.networks) {
if (cert.subject.networks[i].id) for (unsigned int i = 0; i < cert.subject.networkCount; ++i) {
addSubjectNetwork(cert.subject.networks[i].id, cert.subject.networks[i].controller); 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) {
if (cert.subject.certificates[i]) for (unsigned int i = 0; i < cert.subject.certificateCount; ++i) {
addSubjectCertificate(cert.subject.certificates[i]); if (cert.subject.certificates[i])
addSubjectCertificate(cert.subject.certificates[i]);
}
} }
if (cert.subject.updateURLs) { if (cert.subject.updateURLs) {
@ -99,8 +105,8 @@ Certificate &Certificate::operator=(const ZT_Certificate &cert)
} }
if (cert.issuer) { if (cert.issuer) {
m_identities.push_back(*reinterpret_cast<const Identity *>(cert.issuer)); m_identities.push_front(*reinterpret_cast<const Identity *>(cert.issuer));
this->issuer = &(m_identities.back()); this->issuer = &(m_identities.front());
} }
if ((cert.extendedAttributes) && (cert.extendedAttributesSize > 0)) { 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) 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. // 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. // Enlarge array of ZT_Certificate_Identity structs and set pointer to potentially reallocated array.
m_subjectIdentities.back().identity = &(m_identities.back()); m_subjectIdentities.push_back(ZT_Certificate_Identity());
m_subjectIdentities.back().identity = &(m_identities.front());
m_subjectIdentities.back().locator = nullptr; m_subjectIdentities.back().locator = nullptr;
this->subject.identities = m_subjectIdentities.data();
this->subject.identityCount = (unsigned int)m_subjectIdentities.size();
return &(m_subjectIdentities.back()); return &(m_subjectIdentities.back());
} }
@ -140,10 +146,10 @@ ZT_Certificate_Identity *Certificate::addSubjectIdentity(const Identity &id, con
ZT_Certificate_Identity *const n = addSubjectIdentity(id); ZT_Certificate_Identity *const n = addSubjectIdentity(id);
// Store local copy of locator. // Store local copy of locator.
m_locators.push_back(loc); m_locators.push_front(loc);
// Set pointer to stored local copy of locator. // Set pointer to stored local copy of locator.
n->locator = &(m_locators.back()); n->locator = &(m_locators.front());
return n; 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]) void Certificate::addSubjectCertificate(const uint8_t serialNo[ZT_SHA384_DIGEST_SIZE])
{ {
// Store local copy of serial in m_serials container. // 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 // Enlarge array of uint8_t pointers, set new pointer to local copy of serial, and set
// certificates to point to potentially reallocated array. // certificates to point to potentially reallocated array.
m_subjectCertificates.resize(++this->subject.certificateCount); 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(); this->subject.certificates = m_subjectCertificates.data();
} }
void Certificate::addSubjectUpdateUrl(const char *url) void Certificate::addSubjectUpdateUrl(const char *url)
{ {
if ((!url) || (!url[0]))
return;
// Store local copy of URL. // 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 // Add pointer to local copy to pointer array and update C structure to point to
// potentially reallocated array. // 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.updateURLs = m_updateUrls.data();
this->subject.updateURLCount = (unsigned int)m_updateUrls.size(); 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(); 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 > Certificate::encode(const bool omitSignature) const
{ {
Vector< uint8_t > enc; Vector< uint8_t > enc;
@ -204,12 +235,14 @@ Vector< uint8_t > Certificate::encode(const bool omitSignature) const
if (this->flags != 0) if (this->flags != 0)
d.add("f", this->flags); d.add("f", this->flags);
d.add("t", (uint64_t)this->timestamp); if (this->timestamp > 0)
d.add("v#0", (uint64_t)this->validity[0]); d.add("t", (uint64_t)this->timestamp);
d.add("v#1", (uint64_t)this->validity[1]); if (this->validity[0] > 0)
if ((this->extendedAttributes) && (this->extendedAttributesSize > 0)) d.add("v#0", (uint64_t)this->validity[0]);
d["x"].assign(this->extendedAttributes, this->extendedAttributes + this->extendedAttributesSize); if (this->validity[1] > 0)
d.add("mP", (uint64_t)this->maxPathLength); 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); 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)) if ((this->extendedAttributes) && (this->extendedAttributesSize > 0))
d["x"].assign(this->extendedAttributes, this->extendedAttributes + this->extendedAttributesSize); d["x"].assign(this->extendedAttributes, this->extendedAttributes + this->extendedAttributesSize);
if ((!omitSignature) && (this->signatureSize > 0) && (this->signature)) if ((!omitSignature) && (this->signature) && (this->signatureSize > 0))
d["si"].assign(this->signature, this->signature + this->signatureSize); d["S"].assign(this->signature, this->signature + this->signatureSize);
d.encode(enc); d.encode(enc);
return 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) 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; Dictionary d;
if (!d.decode(data, len)) 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$"); unsigned int cnt = (unsigned int)d.getUI("s.i$");
for (unsigned int i = 0; i < cnt; ++i) { for (unsigned int i = 0; i < cnt; ++i) {
const Vector< uint8_t > &identityData = d[Dictionary::arraySubscript(tmp, "s.i$.i", i)]; const Vector< uint8_t > &identityData = d[Dictionary::arraySubscript(tmp, sizeof(tmp), "s.i$.i", i)];
if (identityData.empty()) if (identityData.empty()) {
return false; return false;
}
Identity id; Identity id;
if (id.unmarshal(identityData.data(), (unsigned int)identityData.size()) <= 0) if (id.unmarshal(identityData.data(), (unsigned int)identityData.size()) <= 0) {
return false; 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()) { if (!locatorData.empty()) {
Locator loc; Locator loc;
if (loc.unmarshal(locatorData.data(), (unsigned int)locatorData.size()) <= 0) if (loc.unmarshal(locatorData.data(), (unsigned int)locatorData.size()) <= 0) {
return false; return false;
}
this->addSubjectIdentity(id, loc); this->addSubjectIdentity(id, loc);
} else { } else {
this->addSubjectIdentity(id); 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) { for (unsigned int i = 0; i < cnt; ++i) {
const uint64_t nwid = d.getUI(Dictionary::arraySubscript(tmp, "s.n$.i", i)); const uint64_t nwid = d.getUI(Dictionary::arraySubscript(tmp, sizeof(tmp), "s.nw$.i", i));
const Vector< uint8_t > &fingerprintData = d[Dictionary::arraySubscript(tmp, "s.n$.c", i)]; const Vector< uint8_t > &fingerprintData = d[Dictionary::arraySubscript(tmp, sizeof(tmp), "s.nw$.c", i)];
if ((nwid == 0) || (fingerprintData.empty())) if ((nwid == 0) || (fingerprintData.empty())) {
return false; return false;
}
Fingerprint fp; Fingerprint fp;
if (fp.unmarshal(fingerprintData.data(), (unsigned int)fingerprintData.size()) <= 0) if (fp.unmarshal(fingerprintData.data(), (unsigned int)fingerprintData.size()) <= 0) {
return false; return false;
}
this->addSubjectNetwork(nwid, fp); this->addSubjectNetwork(nwid, fp);
} }
cnt = (unsigned int)d.getUI("s.c$"); cnt = (unsigned int)d.getUI("s.c$");
for (unsigned int i = 0; i < cnt; ++i) { for (unsigned int i = 0; i < cnt; ++i) {
const Vector< uint8_t > &serial = d[Dictionary::arraySubscript(tmp, "s.c$", i)]; const Vector< uint8_t > &serial = d[Dictionary::arraySubscript(tmp, sizeof(tmp), "s.c$", i)];
if (serial.size() != ZT_SHA384_DIGEST_SIZE) if (serial.size() != ZT_SHA384_DIGEST_SIZE) {
return false; return false;
}
this->addSubjectCertificate(serial.data()); this->addSubjectCertificate(serial.data());
} }
cnt = (unsigned int)d.getUI("s.u$"); cnt = (unsigned int)d.getUI("s.u$");
for (unsigned int i = 0; i < cnt; ++i) 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.sN", this->subject.name.serialNo, sizeof(this->subject.name.serialNo));
d.getS("s.n.cN", this->subject.name.commonName, sizeof(this->subject.name.commonName)); d.getS("s.n.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()) { if (!issuerData.empty()) {
Identity id; Identity id;
if (id.unmarshal(issuerData.data(), (int)issuerData.size()) > 0) { if (id.unmarshal(issuerData.data(), (int)issuerData.size()) > 0) {
m_identities.push_back(id); m_identities.push_front(id);
this->issuer = reinterpret_cast<const Identity *>(&(m_identities.back())); 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$"); cnt = (unsigned int)d.getUI("u$");
for (unsigned int i = 0; i < cnt; ++i) { for (unsigned int i = 0; i < cnt; ++i) {
const char *const url = d.getS(Dictionary::arraySubscript(tmp, "u$", i), tmp2, sizeof(tmp2)); const char *const url = d.getS(Dictionary::arraySubscript(tmp, sizeof(tmp), "u$", i), tmp2, sizeof(tmp2));
if (url) if ((url) && (*url != 0)) {
addSubjectUpdateUrl(tmp2); addSubjectUpdateUrl(url);
else return false; } else {
return false;
}
} }
m_extendedAttributes = d["x"]; 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(); this->extendedAttributesSize = (unsigned int)m_extendedAttributes.size();
} }
m_signature = d["si"]; m_signature = d["S"];
if (!m_signature.empty()) { if (!m_signature.empty()) {
this->signature = m_signature.data(); this->signature = m_signature.data();
this->signatureSize = (unsigned int)m_signature.size(); this->signatureSize = (unsigned int)m_signature.size();
@ -395,16 +436,23 @@ Vector< uint8_t > Certificate::encodeCSR()
bool Certificate::sign(const Identity &issuer) 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()); SHA384(this->serialNo, enc.data(), (unsigned int)enc.size());
uint8_t sig[ZT_SIGNATURE_BUFFER_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) { if (sigSize > 0) {
m_signature.assign(sig, sig + sigSize); m_signature.assign(sig, sig + sigSize);
this->signature = m_signature.data(); this->signature = m_signature.data();
this->signatureSize = sigSize; this->signatureSize = sigSize;
return true; return true;
} }
m_signature.clear(); m_signature.clear();
this->signature = nullptr; this->signature = nullptr;
this->signatureSize = 0; this->signatureSize = 0;
@ -425,7 +473,7 @@ ZT_CertificateError Certificate::verify() const
if (this->subject.uniqueIdProofSignatureSize > 0) { if (this->subject.uniqueIdProofSignatureSize > 0) {
if ( if (
(this->subject.uniqueIdProofSignatureSize != ZT_ECC384_SIGNATURE_SIZE) || (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)) (this->subject.uniqueId[0] != ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384))
return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF; return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF;
Dictionary tmp; Dictionary tmp;
@ -456,7 +504,7 @@ ZT_CertificateError Certificate::verify() const
return ZT_CERTIFICATE_ERROR_MISSING_REQUIRED_FIELDS; return ZT_CERTIFICATE_ERROR_MISSING_REQUIRED_FIELDS;
} }
if (this->subject.updateURLCount) { if (this->subject.updateURLCount > 0) {
if (!this->subject.updateURLs) if (!this->subject.updateURLs)
return ZT_CERTIFICATE_ERROR_INVALID_FORMAT; return ZT_CERTIFICATE_ERROR_INVALID_FORMAT;
for (unsigned int i = 0; i < this->subject.updateURLCount; ++i) { for (unsigned int i = 0; i < this->subject.updateURLCount; ++i) {
@ -471,26 +519,6 @@ ZT_CertificateError Certificate::verify() const
return ZT_CERTIFICATE_ERROR_NONE; 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() void Certificate::m_clear()
{ {
ZT_Certificate *const sup = this; 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) 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.t", (uint64_t)s.timestamp);
d.add("s.i$", (uint64_t)s.identityCount); if (s.identities) {
for (unsigned int i = 0; i < s.identityCount; ++i) { d.add("s.i$", (uint64_t)s.identityCount);
if (s.identities[i].identity) for (unsigned int i = 0; i < s.identityCount; ++i) {
d.addO(Dictionary::arraySubscript(tmp, "s.i$.i", i), *reinterpret_cast<const Identity *>(s.identities[i].identity)); if (s.identities[i].identity)
if (s.identities[i].locator) d.addO(Dictionary::arraySubscript(tmp, sizeof(tmp), "s.i$.i", i), *reinterpret_cast<const Identity *>(s.identities[i].identity));
d.addO(Dictionary::arraySubscript(tmp, "s.i$.l", i), *reinterpret_cast<const Locator *>(s.identities[i].locator)); if (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) {
for (unsigned int i = 0; i < s.networkCount; ++i) { d.add("s.nw$", (uint64_t)s.networkCount);
d.add(Dictionary::arraySubscript(tmp, "s.n$.i", i), s.networks[i].id); for (unsigned int i = 0; i < s.networkCount; ++i) {
Fingerprint fp(s.networks[i].controller); d.add(Dictionary::arraySubscript(tmp, sizeof(tmp), "s.nw$.i", i), s.networks[i].id);
d.addO(Dictionary::arraySubscript(tmp, "s.n$.c", i), fp); 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); if (s.certificates) {
for (unsigned int i = 0; i < s.certificateCount; ++i) { d.add("s.c$", (uint64_t)s.certificateCount);
if (s.certificates[i]) for (unsigned int i = 0; i < s.certificateCount; ++i) {
d[Dictionary::arraySubscript(tmp, "s.c$", i)].assign(s.certificates[i], s.certificates[i] + ZT_SHA384_DIGEST_SIZE); 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) { if (s.updateURLs) {
d.add("s.u$", (uint64_t)s.updateURLCount);
for (unsigned int i = 0; i < s.updateURLCount; ++i) 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]) 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]) if (s.name.host[0])
d.add("s.n.h", s.name.host); 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); 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); d["s.uS"].assign(s.uniqueIdProofSignature, s.uniqueIdProofSignature + s.uniqueIdProofSignatureSize);
} }
@ -586,7 +620,7 @@ int ZT_Certificate_newSubjectUniqueId(
void *uniqueIdPrivate, void *uniqueIdPrivate,
int *uniqueIdPrivateSize) int *uniqueIdPrivateSize)
{ {
switch(type) { switch (type) {
case ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384: 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)) 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; return ZT_RESULT_ERROR_BAD_PARAMETER;
@ -631,15 +665,22 @@ int ZT_Certificate_sign(
{ {
if (!cert) if (!cert)
return ZT_RESULT_ERROR_BAD_PARAMETER; return ZT_RESULT_ERROR_BAD_PARAMETER;
ZeroTier::Certificate c(*cert);
if (!c.sign(*reinterpret_cast<const ZeroTier::Identity *>(signer))) try {
return ZT_RESULT_ERROR_BAD_PARAMETER; const ZeroTier::ScopedPtr< ZeroTier::Certificate > c(new ZeroTier::Certificate(*cert));
ZeroTier::Vector< uint8_t > enc(c.encode()); if (!c->sign(*reinterpret_cast<const ZeroTier::Identity *>(signer)))
if ((int)enc.size() > *signedCertSize) return ZT_RESULT_ERROR_INTERNAL;
return ZT_RESULT_ERROR_BAD_PARAMETER;
ZeroTier::Utils::copy(signedCert, enc.data(), (unsigned int)enc.size()); const ZeroTier::Vector< uint8_t > enc(c->encode());
*signedCertSize = (int)enc.size(); if ((int)enc.size() > *signedCertSize)
return ZT_RESULT_OK; 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( enum ZT_CertificateError ZT_Certificate_decode(
@ -649,7 +690,7 @@ enum ZT_CertificateError ZT_Certificate_decode(
int verify) int verify)
{ {
try { try {
if (!decodedCert) if ((!decodedCert) || (!cert) || (certSize <= 0))
return ZT_CERTIFICATE_ERROR_INVALID_FORMAT; return ZT_CERTIFICATE_ERROR_INVALID_FORMAT;
*decodedCert = nullptr; *decodedCert = nullptr;
ZeroTier::Certificate *const c = new ZeroTier::Certificate(); ZeroTier::Certificate *const c = new ZeroTier::Certificate();
@ -658,7 +699,7 @@ enum ZT_CertificateError ZT_Certificate_decode(
return ZT_CERTIFICATE_ERROR_INVALID_FORMAT; return ZT_CERTIFICATE_ERROR_INVALID_FORMAT;
} }
if (verify) { if (verify) {
ZT_CertificateError err = c->verify(); const ZT_CertificateError err = c->verify();
if (err != ZT_CERTIFICATE_ERROR_NONE) { if (err != ZT_CERTIFICATE_ERROR_NONE) {
delete c; delete c;
return err; return err;
@ -666,7 +707,7 @@ enum ZT_CertificateError ZT_Certificate_decode(
} }
*decodedCert = c; *decodedCert = c;
return ZT_CERTIFICATE_ERROR_NONE; return ZT_CERTIFICATE_ERROR_NONE;
} catch ( ... ) { } catch (...) {
return ZT_CERTIFICATE_ERROR_INVALID_FORMAT; return ZT_CERTIFICATE_ERROR_INVALID_FORMAT;
} }
} }
@ -693,7 +734,7 @@ enum ZT_CertificateError ZT_Certificate_verify(const ZT_Certificate *cert)
if (!cert) if (!cert)
return ZT_CERTIFICATE_ERROR_INVALID_FORMAT; return ZT_CERTIFICATE_ERROR_INVALID_FORMAT;
return ZeroTier::Certificate(*cert).verify(); return ZeroTier::Certificate(*cert).verify();
} catch ( ... ) { } catch (...) {
return ZT_CERTIFICATE_ERROR_INVALID_FORMAT; return ZT_CERTIFICATE_ERROR_INVALID_FORMAT;
} }
} }
@ -704,7 +745,7 @@ const ZT_Certificate *ZT_Certificate_clone(const ZT_Certificate *cert)
if (!cert) if (!cert)
return nullptr; return nullptr;
return (const ZT_Certificate *)(new ZeroTier::Certificate(*cert)); return (const ZT_Certificate *)(new ZeroTier::Certificate(*cert));
} catch ( ... ) { } catch (...) {
return nullptr; return nullptr;
} }
} }

View file

@ -114,6 +114,17 @@ public:
*/ */
void setExtendedAttributes(const Dictionary &x); 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 * Marshal this certificate in binary form
* *
@ -159,17 +170,6 @@ public:
*/ */
ZT_CertificateError verify() const; 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 * 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 // These hold any identity or locator objects that are owned by and should
// be deleted with this certificate. Lists are used so the pointers never // be deleted with this certificate. Lists are used so the pointers never
// change. // change.
List< Identity > m_identities; ForwardList< Identity > m_identities;
List< Locator > m_locators; ForwardList< Locator > m_locators;
List< String > m_strings; ForwardList< String > m_strings;
List< SHA384Hash > m_serials; ForwardList< SHA384Hash > m_serials;
// These are stored in a vector because the memory needs to be contiguous. // These are stored in a vector because the memory needs to be contiguous.
Vector< ZT_Certificate_Identity > m_subjectIdentities; Vector< ZT_Certificate_Identity > m_subjectIdentities;

View file

@ -28,6 +28,7 @@
#ifdef __CPP11__ #ifdef __CPP11__
#include <atomic> #include <atomic>
#include <unordered_map> #include <unordered_map>
#include <forward_list>
#endif #endif
namespace ZeroTier { namespace ZeroTier {
@ -128,6 +129,20 @@ template< typename V >
class List : public std::list< 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 > template< typename V >
class Set : public std::set< V, std::less< 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) 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 const Vector< uint8_t > &Dictionary::operator[](const char *const k) const
{ {
static const Vector< uint8_t > s_emptyEntry; 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; 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) void Dictionary::add(const char *k, const Address &v)
{ {
char tmp[ZT_ADDRESS_STRING_SIZE_MAX]; 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 uint64_t Dictionary::getUI(const char *k, uint64_t dfl) const
{ {
char tmp[32]; 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 if (cap == 0) // sanity check
return v; return v;
const Vector< uint8_t > &e = (*this)[k]; const Vector< uint8_t > &e = (*this)[k];
if (e.empty()) { if (e.empty()) {
v[0] = 0; v[0] = 0;
return v; return v;
} }
unsigned int i = 0;
const unsigned int last = cap - 1; for (unsigned int i = 0, last = (cap - 1);; ++i) {
for (;;) {
if ((i >= last) || (i >= (unsigned int)e.size())) { if ((i >= last) || (i >= (unsigned int)e.size())) {
v[i] = 0; v[i] = 0;
break; break;
} }
if ((v[i] = (char)e[i]) == 0) { if ((v[i] = (char)e[i]) == 0)
break; break;
}
++i;
} }
return v; return v;
} }
@ -131,7 +104,6 @@ void Dictionary::encode(Vector< uint8_t > &out) const
s_appendValueByte(out, *i); s_appendValueByte(out, *i);
out.push_back((uint8_t)'\n'); out.push_back((uint8_t)'\n');
} }
out.push_back(0);
} }
bool Dictionary::decode(const void *data, unsigned int len) bool Dictionary::decode(const void *data, unsigned int len)
@ -176,20 +148,24 @@ bool Dictionary::decode(const void *data, unsigned int len)
} else { } else {
if (c == (uint8_t)'=') { if (c == (uint8_t)'=') {
v = &m_entries[k]; v = &m_entries[k];
} else if ((c < 33) || (c > 126) || (c == 92)) {
return false;
} else { } else {
k.push_back(c); k.push_back(c);
} }
} }
} else break; } else {
break;
}
} }
return true; 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) { if ((buf[i] = name[i]) == 0) {
buf[i++] = '#'; buf[i++] = '#';
Utils::hex(sub, 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; 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 } // namespace ZeroTier

View file

@ -42,9 +42,10 @@ class Identity;
class Dictionary class Dictionary
{ {
public: public:
typedef SortedMap< String, Vector < uint8_t > >::const_iterator const_iterator; typedef SortedMap< String, Vector< uint8_t > >::const_iterator const_iterator;
Dictionary(); Dictionary();
~Dictionary(); ~Dictionary();
/** /**
@ -53,7 +54,7 @@ public:
* @param k Key to look up * @param k Key to look up
* @return Reference to value * @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 * Get a const reference to a value
@ -61,7 +62,7 @@ public:
* @param k Key to look up * @param k Key to look up
* @return Reference to value or to empty vector if not found * @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 * @return Start of key->value pairs
@ -75,11 +76,6 @@ public:
ZT_INLINE const_iterator end() const noexcept ZT_INLINE const_iterator end() const noexcept
{ return m_entries.end(); } { 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 * 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 * @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) 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 * 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 * @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) 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 * 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); 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 * Get an integer
* *
@ -171,13 +164,14 @@ public:
template< typename T > template< typename T >
ZT_INLINE bool addO(const char *k, T &obj) ZT_INLINE bool addO(const char *k, T &obj)
{ {
uint8_t tmp[4096]; Vector< uint8_t > &d = (*this)[k];
static_assert(sizeof(tmp) >= T::marshalSizeMax(),"buffer too small"); d.resize(T::marshalSizeMax());
int l = obj.marshal(tmp); const int l = obj.marshal(d.data());
if (l > 0) { if (l > 0) {
(*this)[k].assign(tmp, tmp + l); d.resize(l);
return true; return true;
} }
d.clear();
return false; return false;
} }
@ -203,7 +197,7 @@ public:
* *
* @param out String encoded dictionary * @param out String encoded dictionary
*/ */
void encode(Vector <uint8_t> &out) const; void encode(Vector< uint8_t > &out) const;
/** /**
* Decode a string encoded dictionary * Decode a string encoded dictionary
@ -362,7 +356,7 @@ public:
template< typename V, typename T > template< typename V, typename T >
static ZT_INLINE int appendObject(V &out, const char *const k, const T &v) 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)) if (T::marshalSizeMax() > sizeof(tmp))
return -1; return -1;
const int mlen = v.marshal(tmp); const int mlen = v.marshal(tmp);
@ -379,7 +373,7 @@ public:
* @param sub Subscript index * @param sub Subscript index
* @return Pointer to 'buf' * @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: private:
template< typename V > template< typename V >
@ -417,20 +411,17 @@ private:
{ {
for (;;) { for (;;) {
const char c = *(k++); const char c = *(k++);
if ((c >= 33) && (c <= 126) && (c != 61) && (c != 92)) // printable ASCII with no spaces, equals, or backslash if (c == 0)
out.push_back((uint8_t)c);
else if (c == 0)
break; break;
out.push_back((uint8_t)c);
} }
out.push_back((uint8_t)'='); 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 // 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 // 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. // 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 } // namespace ZeroTier

View file

@ -37,7 +37,7 @@ public:
{ memoryZero(this); } { memoryZero(this); }
ZT_INLINE Fingerprint(const ZT_Fingerprint &fp) noexcept 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) * @return True if hash is not all zero (missing/unspecified)
@ -59,7 +59,12 @@ public:
} }
return s; 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 * Set this fingerprint to a base32-encoded string
@ -71,18 +76,18 @@ public:
{ {
if (!s) if (!s)
return false; return false;
const int l = (int) strlen(s); const int l = (int)strlen(s);
if (l < ZT_ADDRESS_LENGTH_HEX) if (l < ZT_ADDRESS_LENGTH_HEX)
return false; return false;
char a[ZT_ADDRESS_LENGTH_HEX + 1]; 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; a[ZT_ADDRESS_LENGTH_HEX] = 0;
this->address = Utils::hexStrToU64(a) & ZT_ADDRESS_MASK; this->address = Utils::hexStrToU64(a) & ZT_ADDRESS_MASK;
if (l > (ZT_ADDRESS_LENGTH_HEX + 1)) { 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) if (Utils::b32d(s + (ZT_ADDRESS_LENGTH_HEX + 1), this->hash, ZT_FINGERPRINT_HASH_SIZE) != ZT_FINGERPRINT_HASH_SIZE)
return false; return false;
} else { } else {
Utils::zero<ZT_FINGERPRINT_HASH_SIZE>(this->hash); Utils::zero< ZT_FINGERPRINT_HASH_SIZE >(this->hash);
} }
return true; return true;
} }
@ -120,7 +125,7 @@ public:
ZT_INLINE int marshal(uint8_t data[ZT_FINGERPRINT_MARSHAL_SIZE]) const noexcept ZT_INLINE int marshal(uint8_t data[ZT_FINGERPRINT_MARSHAL_SIZE]) const noexcept
{ {
Address(this->address).copyTo(data); 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; return ZT_FINGERPRINT_MARSHAL_SIZE;
} }
@ -129,7 +134,7 @@ public:
if (unlikely(len < ZT_FINGERPRINT_MARSHAL_SIZE)) if (unlikely(len < ZT_FINGERPRINT_MARSHAL_SIZE))
return -1; return -1;
this->address = Address(data); 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; 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); C25519::sign(m_priv, m_pub, data, len, sig);
return ZT_C25519_SIGNATURE_LEN; return ZT_C25519_SIGNATURE_LEN;
} }
break;
case P384: case P384:
if (siglen >= ZT_ECC384_SIGNATURE_SIZE) { if (siglen >= ZT_ECC384_SIGNATURE_SIZE) {
// SECURITY: signatures also include the public keys to further enforce their coupling. // 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); ECC384ECDSASign(m_priv + ZT_C25519_COMBINED_PRIVATE_KEY_SIZE, h, (uint8_t *)sig);
return ZT_ECC384_SIGNATURE_SIZE; return ZT_ECC384_SIGNATURE_SIZE;
} }
break;
} }
} }
return 0; return 0;
@ -350,6 +352,8 @@ char *Identity::toString(bool includePrivate, char buf[ZT_IDENTITY_STRING_BUFFER
*p = (char)0; *p = (char)0;
return buf; return buf;
} }
default:
buf[0] = 0;
} }
return nullptr; 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; 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); 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; 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: case P384:
data[ZT_ADDRESS_LENGTH] = (uint8_t)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; 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); 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; 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; return -1;

View file

@ -68,6 +68,12 @@ public:
memoryZero(this); memoryZero(this);
} }
ZT_INLINE Identity(const Identity &id) noexcept
{
Utils::memoryLock(this, sizeof(Identity));
Utils::copy< sizeof(Identity) >(this, &id);
}
/** /**
* Construct identity from string * Construct identity from string
* *
@ -88,6 +94,13 @@ public:
Utils::burn(reinterpret_cast<void *>(&this->m_priv), sizeof(this->m_priv)); 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) * Set identity to NIL value (all zero)
*/ */
@ -211,6 +224,15 @@ public:
*/ */
bool fromString(const char *str); 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 * @return True if this identity contains something
*/ */
@ -222,20 +244,27 @@ public:
ZT_INLINE bool operator==(const Identity &id) const noexcept ZT_INLINE bool operator==(const Identity &id) const noexcept
{ return (m_fp == id.m_fp); } { return (m_fp == id.m_fp); }
ZT_INLINE bool operator!=(const Identity &id) const noexcept ZT_INLINE bool operator!=(const Identity &id) const noexcept
{ return !(*this == id); } { return !(*this == id); }
ZT_INLINE bool operator<(const Identity &id) const noexcept ZT_INLINE bool operator<(const Identity &id) const noexcept
{ return (m_fp < id.m_fp); } { return (m_fp < id.m_fp); }
ZT_INLINE bool operator>(const Identity &id) const noexcept ZT_INLINE bool operator>(const Identity &id) const noexcept
{ return (id < *this); } { return (id < *this); }
ZT_INLINE bool operator<=(const Identity &id) const noexcept ZT_INLINE bool operator<=(const Identity &id) const noexcept
{ return !(id < *this); } { return !(id < *this); }
ZT_INLINE bool operator>=(const Identity &id) const noexcept ZT_INLINE bool operator>=(const Identity &id) const noexcept
{ return !(*this < id); } { return !(*this < id); }
static constexpr int marshalSizeMax() noexcept static constexpr int marshalSizeMax() noexcept
{ return ZT_IDENTITY_MARSHAL_SIZE_MAX; } { return ZT_IDENTITY_MARSHAL_SIZE_MAX; }
int marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX], bool includePrivate = false) const noexcept; int marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX], bool includePrivate = false) const noexcept;
int unmarshal(const uint8_t *data, int len) noexcept; int unmarshal(const uint8_t *data, int len) noexcept;
private: 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) : Topology::Topology(const RuntimeEnvironment *renv, void *tPtr, const int64_t now) :
RR(renv) RR(renv)
{ {
char tmp[256]; char tmp[32];
Vector< uint8_t > trustData(RR->node->stateObjectGet(tPtr, ZT_STATE_OBJECT_TRUST_STORE, Utils::ZERO256)); Vector< uint8_t > trustData(RR->node->stateObjectGet(tPtr, ZT_STATE_OBJECT_TRUST_STORE, Utils::ZERO256));
Dictionary d; 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$"); const unsigned long certCount = (unsigned long)d.getUI("c$");
for (unsigned long idx = 0; idx < certCount; ++idx) { for (unsigned long idx = 0; idx < certCount; ++idx) {
uint64_t id[6]; 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) { if (serialNo.size() == ZT_SHA384_DIGEST_SIZE) {
Utils::copy< 48 >(id, serialNo.data()); Utils::copy< 48 >(id, serialNo.data());
Certificate cert; Certificate cert;
Vector< uint8_t > enc(RR->node->stateObjectGet(tPtr, ZT_STATE_OBJECT_CERT, id)); Vector< uint8_t > enc(RR->node->stateObjectGet(tPtr, ZT_STATE_OBJECT_CERT, id));
if (cert.decode(enc.data(), (unsigned int)enc.size())) 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$"); const unsigned long localRootCount = (unsigned long)d.getUI("lr$");
for (unsigned long idx = 0; idx < localRootCount; ++idx) { for (unsigned long idx = 0; idx < localRootCount; ++idx) {
Identity lr; 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) if (lr)
m_roots[lr].insert(s_nullCert); 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 // assumes m_roots_l and m_certs_l are locked for write
char tmp[256]; char tmp[32];
Dictionary d; Dictionary d;
d.add("v", (uint64_t)0); // version 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; unsigned long idx = 0;
d.add("c$", (uint64_t)m_certs.size()); 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) { 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[Dictionary::arraySubscript(tmp, sizeof(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.add(Dictionary::arraySubscript(tmp, sizeof(tmp), "c$.lt", idx), (uint64_t)c->second.second);
++idx; ++idx;
} }
unsigned long localRootCount = 0; unsigned long localRootCount = 0;
for (Map< Identity, Set< SharedPtr< const Certificate > > >::const_iterator r(m_roots.begin()); r != m_roots.end();) { 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()) 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); d.add("lr$", (uint64_t)localRootCount);

View file

@ -733,7 +733,7 @@ static ZT_INLINE void copy(void *dest, const void *src) noexcept
#endif #endif
} }
// Avoid rep/movsb startup time for some small common sizes. #ifndef ZT_NO_UNALIGNED_ACCESS
template<> template<>
ZT_INLINE void copy<4>(void *dest, const void *src) noexcept 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 *>(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) + 8) = *reinterpret_cast<const uint64_t *>(reinterpret_cast<const uint8_t *>(src) + 8);
} }
template<> #endif
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);
}
/** /**
* Copy memory block whose size is known at run time * Copy memory block whose size is known at run time

View file

@ -18,6 +18,7 @@ package zerotier
import "C" import "C"
import ( import (
"encoding/json"
"fmt" "fmt"
"runtime" "runtime"
"unsafe" "unsafe"
@ -136,13 +137,11 @@ func NewCertificateFromBytes(cert []byte, verify bool) (*Certificate, error) {
ver = 1 ver = 1
} }
cerr := C.ZT_Certificate_decode((**C.ZT_Certificate)(unsafe.Pointer(&dec)), unsafe.Pointer(&cert[0]), C.int(len(cert)), ver) 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 { if cerr != 0 {
return nil, certificateErrorToError(int(cerr)) return nil, certificateErrorToError(int(cerr))
} }
if dec == unsafe.Pointer(nil) { if dec == unsafe.Pointer(uintptr(0)) {
return nil, ErrInternal return nil, ErrInternal
} }
@ -309,7 +308,7 @@ func (c *Certificate) CCertificate() *CCertificate {
} }
} }
cc.subject.identities = &subjectIdentities[0] 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 { if len(c.Subject.Networks) > 0 {
@ -322,7 +321,7 @@ func (c *Certificate) CCertificate() *CCertificate {
} }
} }
cc.subject.networks = &subjectNetworks[0] 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 { if len(c.Subject.Certificates) > 0 {
@ -334,7 +333,7 @@ func (c *Certificate) CCertificate() *CCertificate {
subjectCertificates[i] = uintptr(unsafe.Pointer(&cert[0])) subjectCertificates[i] = uintptr(unsafe.Pointer(&cert[0]))
} }
cc.subject.certificates = (**C.uint8_t)(unsafe.Pointer(&subjectCertificates[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 { if len(c.Subject.UpdateURLs) > 0 {
@ -345,7 +344,7 @@ func (c *Certificate) CCertificate() *CCertificate {
subjectUpdateURLs[i] = uintptr(unsafe.Pointer(&subjectUpdateURLsData[0][0])) subjectUpdateURLs[i] = uintptr(unsafe.Pointer(&subjectUpdateURLsData[0][0]))
} }
cc.subject.updateURLs = (**C.char)(unsafe.Pointer(&subjectUpdateURLs[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) 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)) cc.signatureSize = C.uint(len(c.Signature))
} }
// HACK: pass pointer to cc as uintptr to disable Go's protection against go pointers to // 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 // 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. // to throw away 'cc' and its components.
cc2 := &CCertificate{C: unsafe.Pointer(C._ZT_Certificate_clone2(C.uintptr_t(uintptr(unsafe.Pointer(&cc)))))} cc2 := &CCertificate{C: unsafe.Pointer(C._ZT_Certificate_clone2(C.uintptr_t(uintptr(unsafe.Pointer(&cc)))))}
runtime.SetFinalizer(cc2, func(obj interface{}) { 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 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. // Verify returns nil on success or a certificate error if there is a problem with this certificate.
func (c *Certificate) Verify() error { func (c *Certificate) Verify() error {
cc := c.CCertificate() cc := c.CCertificate()
@ -436,6 +456,12 @@ func (c *Certificate) Verify() error {
return certificateErrorToError(int(C.ZT_Certificate_verify((*C.ZT_Certificate)(cc.C)))) 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. // NewCertificateSubjectUniqueId creates a new certificate subject unique ID and corresponding private key.
// Right now only one type is supported: CertificateUniqueIdTypeNistP384 // Right now only one type is supported: CertificateUniqueIdTypeNistP384
func NewCertificateSubjectUniqueId(uniqueIdType int) (id []byte, priv []byte, err error) { func NewCertificateSubjectUniqueId(uniqueIdType int) (id []byte, priv []byte, err error) {
@ -444,13 +470,14 @@ func NewCertificateSubjectUniqueId(uniqueIdType int) (id []byte, priv []byte, er
return return
} }
id = make([]byte, int(C.ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE)) 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)) idSize := C.int(len(id))
idPrivateSize := C.int(len(priv)) 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 id = nil
priv = nil priv = nil
err = ErrInvalidParameter err = fmt.Errorf("error %d", rv)
return return
} }
if int(idSize) != len(id) || int(idPrivateSize) != len(priv) { 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. // initCIdentityPtr returns a pointer to the core ZT_Identity instance or nil/0 on error.
func (id *Identity) initCIdentityPtr() bool { func (id *Identity) initCIdentityPtr() bool {
if uintptr(id.cid) == 0 { 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)) defer C.free(unsafe.Pointer(idCStr))
id.cid = C.ZT_Identity_fromString(idCStr) id.cid = C.ZT_Identity_fromString(idCStr)
if uintptr(id.cid) == 0 { if uintptr(id.cid) == 0 {