diff --git a/core/Buf.cpp b/core/Buf.cpp index a9b94777d..c0229fcd0 100644 --- a/core/Buf.cpp +++ b/core/Buf.cpp @@ -27,43 +27,46 @@ void *Buf::operator new(std::size_t sz) uintptr_t bb; for (;;) { bb = s_pool.exchange(ZT_ATOMIC_PTR_LOCKED, std::memory_order_acquire); - if (likely(bb != ZT_ATOMIC_PTR_LOCKED)) - break; + + if (likely(bb != ZT_ATOMIC_PTR_LOCKED)) { + Buf *b; + if (likely(bb != 0)) { + b = reinterpret_cast(bb); + s_pool.store(b->__nextInPool, std::memory_order_release); + } else { + s_pool.store(0, std::memory_order_release); + b = reinterpret_cast(malloc(sz)); + if (!b) + throw Utils::BadAllocException; + s_allocated.fetch_add(1, std::memory_order_relaxed); + } + + b->__refCount.store(0, std::memory_order_relaxed); + + return reinterpret_cast(b); + } + Spinlock::pause(); } - - Buf *b; - if (bb) { - s_pool.store(((Buf *)bb)->__nextInPool.load(std::memory_order_relaxed), std::memory_order_release); - b = (Buf *)bb; - } else { - s_pool.store(0, std::memory_order_release); - b = (Buf *)malloc(sz); - if (!b) - throw Utils::BadAllocException; - s_allocated.fetch_add(1, std::memory_order_relaxed); - } - - b->__refCount.store(0, std::memory_order_relaxed); - return (void *)b; } void Buf::operator delete(void *ptr) { - if (ptr) { + if (likely(ptr != nullptr)) { if (s_allocated.load(std::memory_order_relaxed) > ZT_BUF_MAX_POOL_SIZE) { + s_allocated.fetch_sub(1, std::memory_order_relaxed); free(ptr); } else { uintptr_t bb; for (;;) { bb = s_pool.exchange(ZT_ATOMIC_PTR_LOCKED, std::memory_order_acquire); - if (likely(bb != ZT_ATOMIC_PTR_LOCKED)) - break; + if (likely(bb != ZT_ATOMIC_PTR_LOCKED)) { + reinterpret_cast(ptr)->__nextInPool = bb; + s_pool.store(reinterpret_cast(ptr), std::memory_order_release); + return; + } Spinlock::pause(); } - - ((Buf *)ptr)->__nextInPool.store(bb, std::memory_order_relaxed); - s_pool.store((uintptr_t)ptr, std::memory_order_release); } } } @@ -73,19 +76,22 @@ void Buf::freePool() noexcept uintptr_t bb; for (;;) { bb = s_pool.exchange(ZT_ATOMIC_PTR_LOCKED, std::memory_order_acquire); - if (likely(bb != ZT_ATOMIC_PTR_LOCKED)) - break; + + if (likely(bb != ZT_ATOMIC_PTR_LOCKED)) { + s_pool.store(0, std::memory_order_release); + + while (bb != 0) { + const uintptr_t next = reinterpret_cast(bb)->__nextInPool; + s_allocated.fetch_sub(1, std::memory_order_relaxed); + free(reinterpret_cast(bb)); + bb = next; + } + + return; + } + Spinlock::pause(); } - - s_pool.store(0, std::memory_order_release); - - while (bb != 0) { - const uintptr_t next = ((Buf *)bb)->__nextInPool.load(std::memory_order_relaxed); - s_allocated.fetch_sub(1, std::memory_order_relaxed); - free((void *)bb); - bb = next; - } } long Buf::poolAllocated() noexcept diff --git a/core/Buf.hpp b/core/Buf.hpp index d209a2ae8..cb07b7e50 100644 --- a/core/Buf.hpp +++ b/core/Buf.hpp @@ -857,10 +857,7 @@ public: { return ZT_BUF_MEM_SIZE; } private: - // Next item in free buffer pool linked list if Buf is placed in pool, undefined and unused otherwise - std::atomic< uintptr_t > __nextInPool; - - // Reference counter for SharedPtr<> + volatile uintptr_t __nextInPool; std::atomic< int > __refCount; }; diff --git a/core/CAPI.cpp b/core/CAPI.cpp index 77e507537..01f3c9115 100644 --- a/core/CAPI.cpp +++ b/core/CAPI.cpp @@ -20,6 +20,7 @@ #include "VL1.hpp" #include "VL2.hpp" #include "CallContext.hpp" +#include "ECC384.hpp" extern "C" { @@ -658,43 +659,34 @@ ZT_MAYBE_UNUSED void ZT_Identity_delete(const ZT_Identity *id) /********************************************************************************************************************/ -ZT_MAYBE_UNUSED int ZT_Certificate_newSubjectUniqueId( - enum ZT_CertificateUniqueIdType type, - void *uniqueId, - int *uniqueIdSize, - void *uniqueIdPrivate, - int *uniqueIdPrivateSize) +ZT_MAYBE_UNUSED int ZT_Certificate_newKeyPair( + const enum ZT_CertificatePublicKeyAlgorithm type, + uint8_t publicKey[ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE], + int *const publicKeySize, + uint8_t privateKey[ZT_CERTIFICATE_MAX_PRIVATE_KEY_SIZE], + int *const privateKeySize) { try { - 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; - *uniqueIdSize = ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE; - *uniqueIdPrivateSize = ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE; - ZeroTier::Certificate::createSubjectUniqueId(reinterpret_cast(uniqueId), reinterpret_cast(uniqueIdPrivate)); - return ZT_RESULT_OK; - } - return ZT_RESULT_ERROR_BAD_PARAMETER; - } catch (...) { + return ZeroTier::Certificate::newKeyPair(type, publicKey, publicKeySize, privateKey, privateKeySize) ? ZT_RESULT_OK : ZT_RESULT_ERROR_BAD_PARAMETER; + } catch ( ... ) { return ZT_RESULT_FATAL_ERROR_INTERNAL; } } ZT_MAYBE_UNUSED int ZT_Certificate_newCSR( const ZT_Certificate_Subject *subject, - const void *uniqueId, - int uniqueIdSize, - const void *uniqueIdPrivate, - int uniqueIdPrivateSize, - void *csr, - int *csrSize) + const void *const certificatePublicKey, + const int certificatePublicKeySize, + const void *const uniqueIdPrivateKey, + const int uniqueIdPrivateKeySize, + void *const csr, + int *const csrSize) { try { - if (!subject) + if ((!subject) || (!certificatePublicKey) || (certificatePublicKeySize <= 0) || (certificatePublicKeySize > ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE)) return ZT_RESULT_ERROR_BAD_PARAMETER; - const ZeroTier::Vector< uint8_t > csrV(ZeroTier::Certificate::createCSR(*subject, uniqueId, uniqueIdSize, uniqueIdPrivate, uniqueIdPrivateSize)); - if ((int)csrV.size() > *csrSize) + const ZeroTier::Vector< uint8_t > csrV(ZeroTier::Certificate::createCSR(*subject, certificatePublicKey, (unsigned int)certificatePublicKeySize, uniqueIdPrivateKey, (unsigned int)uniqueIdPrivateKeySize)); + if (csrV.empty() || ((int)csrV.size() > *csrSize)) return ZT_RESULT_ERROR_BAD_PARAMETER; ZeroTier::Utils::copy(csr, csrV.data(), (unsigned int)csrV.size()); *csrSize = (int)csrV.size(); @@ -704,29 +696,21 @@ ZT_MAYBE_UNUSED int ZT_Certificate_newCSR( } } -ZT_MAYBE_UNUSED int ZT_Certificate_sign( +ZT_MAYBE_UNUSED ZT_Certificate *ZT_Certificate_sign( const ZT_Certificate *cert, - const ZT_Identity *signer, - void *signedCert, - int *signedCertSize) + const uint8_t issuer[ZT_CERTIFICATE_HASH_SIZE], + const void *issuerPrivateKey, + int issuerPrivateKeySize) { try { - if (!cert) - return ZT_RESULT_ERROR_BAD_PARAMETER; - ZeroTier::Certificate c(*cert); - if (!c.sign(*reinterpret_cast(signer))) - return ZT_RESULT_ERROR_INTERNAL; - - const ZeroTier::Vector< uint8_t > enc(c.encode()); - if ((int)enc.size() > *signedCertSize) - return ZT_RESULT_ERROR_BAD_PARAMETER; - ZeroTier::Utils::copy(signedCert, enc.data(), (unsigned int)enc.size()); - *signedCertSize = (int)enc.size(); - - return ZT_RESULT_OK; - } catch (...) { - return ZT_RESULT_FATAL_ERROR_INTERNAL; - } + ZeroTier::Certificate *const c = new ZeroTier::Certificate(*cert); + if (c->sign(issuer, issuerPrivateKey, issuerPrivateKeySize)) { + return c; + } else { + delete c; + } + } catch (...) {} + return nullptr; } ZT_MAYBE_UNUSED enum ZT_CertificateError ZT_Certificate_decode( diff --git a/core/Certificate.cpp b/core/Certificate.cpp index 5bacc980d..0a47722cc 100644 --- a/core/Certificate.cpp +++ b/core/Certificate.cpp @@ -39,7 +39,7 @@ Certificate &Certificate::operator=(const ZT_Certificate &cert) { m_clear(); - Utils::copy< 48 >(this->serialNo, cert.serialNo); + Utils::copy< sizeof(this->serialNo) >(this->serialNo, cert.serialNo); this->flags = cert.flags; this->timestamp = cert.timestamp; this->validity[0] = cert.validity[0]; @@ -47,7 +47,7 @@ Certificate &Certificate::operator=(const ZT_Certificate &cert) this->subject.timestamp = cert.subject.timestamp; - if (cert.subject.identities) { + if (cert.subject.identities != nullptr) { for (unsigned int i = 0; i < cert.subject.identityCount; ++i) { if (cert.subject.identities[i].identity) { if (cert.subject.identities[i].locator) { @@ -59,60 +59,59 @@ Certificate &Certificate::operator=(const ZT_Certificate &cert) } } - if (cert.subject.networks) { + if (cert.subject.networks != nullptr) { for (unsigned int i = 0; i < cert.subject.networkCount; ++i) { - if (cert.subject.networks[i].id) + if (cert.subject.networks[i].id) { addSubjectNetwork(cert.subject.networks[i].id, cert.subject.networks[i].controller); + } } } - if (cert.subject.certificates) { + if (cert.subject.certificates != nullptr) { for (unsigned int i = 0; i < cert.subject.certificateCount; ++i) { - if (cert.subject.certificates[i]) + if (cert.subject.certificates[i]) { addSubjectCertificate(cert.subject.certificates[i]); + } } } - if (cert.subject.updateURLs) { + if (cert.subject.updateURLs != nullptr) { for (unsigned int i = 0; i < cert.subject.updateURLCount; ++i) { - if (cert.subject.updateURLs[i]) + if (cert.subject.updateURLs[i]) { addSubjectUpdateUrl(cert.subject.updateURLs[i]); + } } } + this->subject.identityCount = cert.subject.identityCount; + this->subject.networkCount = cert.subject.networkCount; + this->subject.certificateCount = cert.subject.certificateCount; + this->subject.updateURLCount = cert.subject.updateURLCount; + Utils::copy< sizeof(ZT_Certificate_Name) >(&(this->subject.name), &(cert.subject.name)); - if ((cert.subject.uniqueId) && (cert.subject.uniqueIdSize > 0)) { - m_subjectUniqueId.assign(cert.subject.uniqueId, cert.subject.uniqueId + cert.subject.uniqueIdSize); - this->subject.uniqueId = m_subjectUniqueId.data(); - this->subject.uniqueIdSize = (unsigned int)m_subjectUniqueId.size(); - } - if ((cert.subject.uniqueIdProofSignature) && (cert.subject.uniqueIdProofSignatureSize > 0)) { - m_subjectUniqueIdProofSignature.assign(cert.subject.uniqueIdProofSignature, cert.subject.uniqueIdProofSignature + cert.subject.uniqueIdProofSignatureSize); - this->subject.uniqueIdProofSignature = m_subjectUniqueIdProofSignature.data(); - this->subject.uniqueIdProofSignatureSize = (unsigned int)m_subjectUniqueIdProofSignature.size(); - } + Utils::copy< sizeof(this->subject.uniqueId) >(this->subject.uniqueId, cert.subject.uniqueId); + Utils::copy< sizeof(this->subject.uniqueIdSignature) >(this->subject.uniqueIdSignature, cert.subject.uniqueIdSignature); + this->subject.uniqueIdSize = cert.subject.uniqueIdSize; + this->subject.uniqueIdSignatureSize = cert.subject.uniqueIdSignatureSize; - if (cert.issuer) { - m_identities.push_front(*reinterpret_cast(cert.issuer)); - this->issuer = &(m_identities.front()); - } + Utils::copy< sizeof(this->issuer) >(this->issuer, cert.issuer); - Utils::copy< sizeof(ZT_Certificate_Name) >(&(this->issuerName), &(cert.issuerName)); + Utils::copy< sizeof(this->issuerPublicKey) >(this->issuerPublicKey, cert.issuerPublicKey); + Utils::copy< sizeof(this->publicKey) >(this->publicKey, cert.publicKey); + this->issuerPublicKeySize = cert.issuerPublicKeySize; + this->publicKeySize = cert.publicKeySize; - if ((cert.extendedAttributes) && (cert.extendedAttributesSize > 0)) { + if ((cert.extendedAttributes != nullptr) && (cert.extendedAttributesSize > 0)) { m_extendedAttributes.assign(cert.extendedAttributes, cert.extendedAttributes + cert.extendedAttributesSize); this->extendedAttributes = m_extendedAttributes.data(); this->extendedAttributesSize = (unsigned int)m_extendedAttributes.size(); } - this->maxPathLength = cert.maxPathLength; + Utils::copy< sizeof(this->signature) >(this->signature, cert.signature); + this->signatureSize = cert.signatureSize; - if ((cert.signature) && (cert.signatureSize > 0)) { - m_signature.assign(cert.signature, cert.signature + cert.signatureSize); - this->signature = m_signature.data(); - this->signatureSize = (unsigned int)m_signature.size(); - } + this->maxPathLength = cert.maxPathLength; return *this; } @@ -127,6 +126,7 @@ ZT_Certificate_Identity *Certificate::addSubjectIdentity(const Identity &id) 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(); @@ -174,49 +174,16 @@ void Certificate::addSubjectCertificate(const uint8_t serialNo[ZT_SHA384_DIGEST_ void Certificate::addSubjectUpdateUrl(const char *url) { - if ((!url) || (!url[0])) - return; + if ((url != nullptr) && (url[0] != 0)) { + // Store local copy of URL. + m_strings.push_front(url); - // Store local copy of 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.front().c_str()); - this->subject.updateURLs = m_updateUrls.data(); - this->subject.updateURLCount = (unsigned int)m_updateUrls.size(); -} - -/* -void Certificate::setExtendedAttributes(const Dictionary &x) -{ - m_extendedAttributes.clear(); - x.encode(m_extendedAttributes); - this->extendedAttributes = m_extendedAttributes.data(); - 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; + // Add pointer to local copy to pointer array and update C structure to point to + // potentially reallocated array. + m_updateUrls.push_back(m_strings.front().c_str()); + this->subject.updateURLs = m_updateUrls.data(); + this->subject.updateURLCount = (unsigned int)m_updateUrls.size(); + } } Vector< uint8_t > Certificate::encode(const bool omitSignature) const @@ -242,44 +209,26 @@ Vector< uint8_t > Certificate::encode(const bool omitSignature) const d.add("v#0", (uint64_t)this->validity[0]); if (this->validity[1] > 0) d.add("v#1", (uint64_t)this->validity[1]); - if (this->maxPathLength > 0) - d.add("mP", (uint64_t)this->maxPathLength); m_encodeSubject(this->subject, d, false); - if (this->issuer) - d.addO("i", *reinterpret_cast(this->issuer)); + if (!Utils::allZero(this->issuer, sizeof(this->issuer))) + d.add("i", this->issuer, sizeof(this->issuer)); - if (this->issuerName.country[0]) - d.add("iN.c", this->issuerName.country); - if (this->issuerName.organization[0]) - d.add("iN.o", this->issuerName.organization); - if (this->issuerName.unit[0]) - d.add("iN.u", this->issuerName.unit); - if (this->issuerName.locality[0]) - d.add("iN.l", this->issuerName.locality); - if (this->issuerName.province[0]) - d.add("iN.p", this->issuerName.province); - if (this->issuerName.streetAddress[0]) - d.add("iN.sA", this->issuerName.streetAddress); - if (this->issuerName.postalCode[0]) - d.add("iN.pC", this->issuerName.postalCode); - if (this->issuerName.commonName[0]) - d.add("iN.cN", this->issuerName.commonName); - if (this->issuerName.serialNo[0]) - d.add("iN.sN", this->issuerName.serialNo); - if (this->issuerName.email[0]) - d.add("iN.e", this->issuerName.email); - if (this->issuerName.url[0]) - d.add("iN.ur", this->issuerName.url); - if (this->issuerName.host[0]) - d.add("iN.h", this->issuerName.host); + if (this->issuerPublicKeySize > 0) + d.add("iPK", this->issuerPublicKey, this->issuerPublicKeySize); - if ((this->extendedAttributes) && (this->extendedAttributesSize > 0)) + if (this->publicKeySize > 0) + d.add("pK", this->publicKey, this->publicKeySize); + + if ((this->extendedAttributes != nullptr) && (this->extendedAttributesSize > 0)) d["x"].assign(this->extendedAttributes, this->extendedAttributes + this->extendedAttributesSize); - if ((!omitSignature) && (this->signature) && (this->signatureSize > 0)) - d["S"].assign(this->signature, this->signature + this->signatureSize); + if ((!omitSignature) && (this->signatureSize > 0)) + d["si"].assign(this->signature, this->signature + this->signatureSize); + + if (this->maxPathLength > 0) + d.add("l", (uint64_t)this->maxPathLength); d.encode(enc); return enc; @@ -299,7 +248,6 @@ bool Certificate::decode(const void *const data, const unsigned int len) this->timestamp = (int64_t)d.getUI("t"); this->validity[0] = (int64_t)d.getUI("v#0"); this->validity[1] = (int64_t)d.getUI("v#1"); - this->maxPathLength = (unsigned int)d.getUI("mP"); this->subject.timestamp = (int64_t)d.getUI("s.t"); @@ -359,47 +307,32 @@ bool Certificate::decode(const void *const data, const unsigned int len) d.getS("s.n.ur", this->subject.name.url, sizeof(this->subject.name.url)); d.getS("s.n.h", this->subject.name.host, sizeof(this->subject.name.host)); - m_subjectUniqueId = d["s.uI"]; - if (!m_subjectUniqueId.empty()) { - this->subject.uniqueId = m_subjectUniqueId.data(); - this->subject.uniqueIdSize = (unsigned int)m_subjectUniqueId.size(); + const Vector< uint8_t > &uniqueId = d["s.uI"]; + if ((!uniqueId.empty()) && (uniqueId.size() <= sizeof(this->subject.uniqueId))) { + Utils::copy(this->subject.uniqueId, uniqueId.data(), uniqueId.size()); + this->subject.uniqueIdSize = (unsigned int)uniqueId.size(); } - m_subjectUniqueIdProofSignature = d["s.uS"]; - if (!m_subjectUniqueIdProofSignature.empty()) { - this->subject.uniqueIdProofSignature = m_subjectUniqueIdProofSignature.data(); - this->subject.uniqueIdProofSignatureSize = (unsigned int)m_subjectUniqueIdProofSignature.size(); + const Vector< uint8_t > &uniqueIdSignature = d["s.uS"]; + if ((!uniqueIdSignature.empty()) && (uniqueIdSignature.size() <= sizeof(this->subject.uniqueIdSignature))) { + Utils::copy(this->subject.uniqueIdSignature, uniqueIdSignature.data(), uniqueIdSignature.size()); + this->subject.uniqueIdSignatureSize = (unsigned int)uniqueIdSignature.size(); } const Vector< uint8_t > &issuerData = d["i"]; - if (!issuerData.empty()) { - Identity id; - if (id.unmarshal(issuerData.data(), (int)issuerData.size()) > 0) { - m_identities.push_front(id); - this->issuer = reinterpret_cast(&(m_identities.front())); - } + if (issuerData.size() == sizeof(this->issuer)) { + Utils::copy< sizeof(this->issuer) >(this->issuer, issuerData.data()); } - d.getS("iN.sN", this->issuerName.serialNo, sizeof(this->issuerName.serialNo)); - d.getS("iN.cN", this->issuerName.commonName, sizeof(this->issuerName.commonName)); - d.getS("iN.c", this->issuerName.country, sizeof(this->issuerName.country)); - d.getS("iN.o", this->issuerName.organization, sizeof(this->issuerName.organization)); - d.getS("iN.u", this->issuerName.unit, sizeof(this->issuerName.unit)); - d.getS("iN.l", this->issuerName.locality, sizeof(this->issuerName.locality)); - d.getS("iN.p", this->issuerName.province, sizeof(this->issuerName.province)); - d.getS("iN.sA", this->issuerName.streetAddress, sizeof(this->issuerName.streetAddress)); - d.getS("iN.pC", this->issuerName.postalCode, sizeof(this->issuerName.postalCode)); - d.getS("iN.e", this->issuerName.email, sizeof(this->issuerName.email)); - d.getS("iN.ur", this->issuerName.url, sizeof(this->issuerName.url)); - d.getS("iN.h", this->issuerName.host, sizeof(this->issuerName.host)); + const Vector< uint8_t > &issuerPublicKey = d["iPK"]; + if ((!issuerPublicKey.empty()) && (issuerPublicKey.size() <= sizeof(this->issuerPublicKey))) { + Utils::copy(this->issuerPublicKey, issuerPublicKey.data(), issuerPublicKey.size()); + this->issuerPublicKeySize = (unsigned int)issuerPublicKey.size(); + } - cnt = (unsigned int)d.getUI("u$"); - for (unsigned int i = 0; i < cnt; ++i) { - 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; - } + const Vector< uint8_t > &publicKey = d["pK"]; + if ((!publicKey.empty()) && (publicKey.size() <= sizeof(this->publicKey))) { + Utils::copy(this->publicKey, publicKey.data(), publicKey.size()); + this->publicKeySize = (unsigned int)publicKey.size(); } m_extendedAttributes = d["x"]; @@ -408,40 +341,45 @@ bool Certificate::decode(const void *const data, const unsigned int len) this->extendedAttributesSize = (unsigned int)m_extendedAttributes.size(); } - m_signature = d["S"]; - if (!m_signature.empty()) { - this->signature = m_signature.data(); - this->signatureSize = (unsigned int)m_signature.size(); + const Vector< uint8_t > &signature = d["si"]; + if ((!signature.empty()) && (signature.size() <= sizeof(this->signature))) { + Utils::copy(this->signature, signature.data(), signature.size()); + this->signatureSize = (unsigned int)signature.size(); } + this->maxPathLength = (unsigned int)d.getUI("l"); + const Vector< uint8_t > enc(encode(true)); SHA384(this->serialNo, enc.data(), (unsigned int)enc.size()); return true; } -bool Certificate::sign(const Identity &issuer) +bool Certificate::sign(const uint8_t issuer[ZT_CERTIFICATE_HASH_SIZE], const void *const issuerPrivateKey, const unsigned int issuerPrivateKeySize) { - m_identities.push_front(issuer); - m_identities.front().erasePrivateKey(); - this->issuer = reinterpret_cast(&(m_identities.front())); + if ((!issuerPrivateKey) || (issuerPrivateKeySize == 0)) + return false; - const Vector< uint8_t > enc(encode(true)); - SHA384(this->serialNo, enc.data(), (unsigned int)enc.size()); + switch (reinterpret_cast(issuerPrivateKey)[0]) { + default: + return false; + case ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384: + if (issuerPrivateKeySize == (1 + ZT_ECC384_PUBLIC_KEY_SIZE + ZT_ECC384_PRIVATE_KEY_SIZE)) { + Utils::copy< sizeof(this->issuer) >(this->issuer, issuer); + Utils::copy< 1 + ZT_ECC384_PUBLIC_KEY_SIZE >(this->issuerPublicKey, issuerPrivateKey); // private is prefixed with public + this->issuerPublicKeySize = 1 + ZT_ECC384_PUBLIC_KEY_SIZE; - uint8_t sig[ZT_SIGNATURE_BUFFER_SIZE]; - const unsigned int sigSize = issuer.sign(enc.data(), (unsigned int)enc.size(), sig, ZT_SIGNATURE_BUFFER_SIZE); + const Vector< uint8_t > enc(encode(true)); + SHA384(this->serialNo, enc.data(), (unsigned int)enc.size()); - if (sigSize > 0) { - m_signature.assign(sig, sig + sigSize); - this->signature = m_signature.data(); - this->signatureSize = sigSize; - return true; + ECC384ECDSASign(reinterpret_cast(issuerPrivateKey) + 1 + ZT_ECC384_PUBLIC_KEY_SIZE, this->serialNo, this->signature); + this->signatureSize = ZT_ECC384_SIGNATURE_SIZE; + + return true; + } + break; } - m_signature.clear(); - this->signature = nullptr; - this->signatureSize = 0; return false; } @@ -455,105 +393,129 @@ ZT_CertificateError Certificate::verify(const int64_t clock, const bool checkSig return ZT_CERTIFICATE_ERROR_OUT_OF_VALID_TIME_WINDOW; } - if (this->issuer) { - if (checkSignatures) { - const Vector< uint8_t > enc(encode(true)); - if (!reinterpret_cast(this->issuer)->verify(enc.data(), (unsigned int)enc.size(), this->signature, this->signatureSize)) - return ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE; + if (checkSignatures) { + // Signature check fails if main signature is not present or invalid. + // Note that the serial number / SHA384 hash is computed on decode(), so + // this value is not something we blindly trust from input. + if ((this->issuerPublicKeySize > 0) && (this->issuerPublicKeySize <= (unsigned int)sizeof(this->issuerPublicKey))) { + switch (this->issuerPublicKey[0]) { + case ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384: + if ((this->issuerPublicKeySize == (ZT_ECC384_PUBLIC_KEY_SIZE + 1)) && (this->signatureSize == ZT_ECC384_SIGNATURE_SIZE)) { + if (!ECC384ECDSAVerify(this->issuerPublicKey + 1, this->serialNo, this->signature)) { + return ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE; + } + } else { + return ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE; + } + break; + default: + return ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE; + } + } else { + return ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE; } - } else { - return ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE; - } - if (this->subject.uniqueIdProofSignatureSize > 0) { - if ( - (this->subject.uniqueIdProofSignatureSize != ZT_ECC384_SIGNATURE_SIZE) || - (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; + // Subject unique ID signatures are optional, so this only fails if it + // is present and invalid. A unique ID with type ALGORITHM_NONE is also + // allowed, but this means its signature is not checked. + if (this->subject.uniqueIdSize > 0) { + if (this->subject.uniqueIdSize <= (unsigned int)sizeof(this->subject.uniqueId)) { + switch (this->subject.uniqueId[0]) { + case ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_NONE: + break; + case ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384: + if ((this->subject.uniqueIdSize == (ZT_ECC384_PUBLIC_KEY_SIZE + 1)) && (this->subject.uniqueIdSignatureSize == ZT_ECC384_SIGNATURE_SIZE)) { + Dictionary d; + m_encodeSubject(this->subject, d, true); - if (checkSignatures) { - Dictionary d; - m_encodeSubject(this->subject, d, true); - Vector< uint8_t > enc; - d.encode(enc); + Vector< uint8_t > enc; + enc.reserve(1024); + d.encode(enc); - uint8_t h[ZT_SHA384_DIGEST_SIZE]; - SHA384(h, enc.data(), (unsigned int)enc.size()); - static_assert(ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE == (ZT_ECC384_PUBLIC_KEY_SIZE + 1), "incorrect size"); - if (!ECC384ECDSAVerify(this->subject.uniqueId + 1, h, this->subject.uniqueIdProofSignature)) + static_assert(ZT_ECC384_SIGNATURE_HASH_SIZE == ZT_SHA384_DIGEST_SIZE, "ECC384 should take 384-bit hash"); + uint8_t h[ZT_SHA384_DIGEST_SIZE]; + SHA384(h, enc.data(), (unsigned int)enc.size()); + + if (!ECC384ECDSAVerify(this->subject.uniqueId + 1, h, this->subject.uniqueIdSignature)) { + return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF; + } + } else { + return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF; + } + break; + default: + return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF; + } + } else { return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF; + } } - } else if (this->subject.uniqueIdSize > 0) { - return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF; } for (unsigned int i = 0; i < this->subject.identityCount; ++i) { if (!this->subject.identities[i].identity) return ZT_CERTIFICATE_ERROR_MISSING_REQUIRED_FIELDS; if (checkSignatures) { - if (!reinterpret_cast(this->subject.identities[i].identity)->locallyValidate()) + if (!reinterpret_cast(this->subject.identities[i].identity)->locallyValidate()) { return ZT_CERTIFICATE_ERROR_INVALID_IDENTITY; - if (this->subject.identities[i].locator) { - if (!reinterpret_cast(this->subject.identities[i].locator)->verify(*reinterpret_cast(this->subject.identities[i].identity))) - return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE; + } + if ((this->subject.identities[i].locator) && (!reinterpret_cast(this->subject.identities[i].locator)->verify(*reinterpret_cast(this->subject.identities[i].identity)))) { + return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE; } } } for (unsigned int i = 0; i < this->subject.networkCount; ++i) { - if (!this->subject.networks[i].id) + if (!this->subject.networks[i].id) { return ZT_CERTIFICATE_ERROR_MISSING_REQUIRED_FIELDS; + } } if (this->subject.updateURLCount > 0) { - if (!this->subject.updateURLs) + if (!this->subject.updateURLs) { return ZT_CERTIFICATE_ERROR_MISSING_REQUIRED_FIELDS; + } for (unsigned int i = 0; i < this->subject.updateURLCount; ++i) { if (!this->subject.updateURLs[i]) return ZT_CERTIFICATE_ERROR_MISSING_REQUIRED_FIELDS; } } - } catch (...) {} + } catch (...) { + return ZT_CERTIFICATE_ERROR_INVALID_FORMAT; + } return ZT_CERTIFICATE_ERROR_NONE; } -Vector< uint8_t > Certificate::createCSR(const ZT_Certificate_Subject &s, const void *uniqueId, unsigned int uniqueIdSize, const void *uniqueIdPrivate, unsigned int uniqueIdPrivateSize) +bool Certificate::newKeyPair(const ZT_CertificatePublicKeyAlgorithm type, uint8_t publicKey[ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE], int *const publicKeySize, uint8_t privateKey[ZT_CERTIFICATE_MAX_PRIVATE_KEY_SIZE], int *const privateKeySize) { + switch (type) { + case ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384: + publicKey[0] = (uint8_t)ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384; + ZeroTier::ECC384GenerateKey(publicKey + 1, privateKey + ZT_ECC384_PUBLIC_KEY_SIZE + 1); + ZeroTier::Utils::copy< ZT_ECC384_PUBLIC_KEY_SIZE + 1 >(privateKey, publicKey); + *publicKeySize = ZT_ECC384_PUBLIC_KEY_SIZE + 1; + *privateKeySize = ZT_ECC384_PUBLIC_KEY_SIZE + 1 + ZT_ECC384_PRIVATE_KEY_SIZE; + return true; + default: + break; + } + return false; +} + +Vector< uint8_t > Certificate::createCSR(const ZT_Certificate_Subject &s, const void *const certificatePublicKey, const unsigned int certificatePublicKeySize, const void *uniqueIdPrivate, unsigned int uniqueIdPrivateSize) +{ + Vector< uint8_t > enc; + ZT_Certificate_Subject sc; Utils::copy< sizeof(ZT_Certificate_Subject) >(&sc, &s); - if ((uniqueId) && (uniqueIdSize > 0) && (uniqueIdPrivate) && (uniqueIdPrivateSize > 0)) { - sc.uniqueId = reinterpret_cast(uniqueId); - sc.uniqueIdSize = uniqueIdSize; - } else { - sc.uniqueId = nullptr; - sc.uniqueIdSize = 0; - } - - Dictionary d; - m_encodeSubject(sc, d, true); - Vector< uint8_t > enc; - d.encode(enc); - - if (sc.uniqueId) { - uint8_t h[ZT_SHA384_DIGEST_SIZE]; - SHA384(h, enc.data(), (unsigned int)enc.size()); - enc.clear(); - if ((reinterpret_cast(uniqueId)[0] == ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384) && - (uniqueIdSize == ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE) && - (uniqueIdPrivateSize == ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE)) { - uint8_t sig[ZT_ECC384_SIGNATURE_SIZE]; - ECC384ECDSASign(reinterpret_cast(uniqueIdPrivate), h, sig); - - sc.uniqueIdProofSignature = sig; - sc.uniqueIdProofSignatureSize = ZT_ECC384_SIGNATURE_SIZE; - - d.clear(); - m_encodeSubject(sc, d, false); - d.encode(enc); - } + if (m_setSubjectUniqueId(sc, uniqueIdPrivate, uniqueIdPrivateSize)) { + Dictionary d; + m_encodeSubject(sc, d, false); + if (certificatePublicKeySize > 0) + d.add("pK", certificatePublicKey, certificatePublicKeySize); + d.encode(enc); } return enc; @@ -574,9 +536,35 @@ void Certificate::m_clear() m_updateUrls.clear(); m_subjectCertificates.clear(); m_extendedAttributes.clear(); - m_subjectUniqueId.clear(); - m_subjectUniqueIdProofSignature.clear(); - m_signature.clear(); +} + +bool Certificate::m_setSubjectUniqueId(ZT_Certificate_Subject &s, const void *uniqueIdPrivate, unsigned int uniqueIdPrivateSize) +{ + if (uniqueIdPrivateSize > 0) { + if ((uniqueIdPrivate != nullptr) && (uniqueIdPrivateSize == (1 + ZT_ECC384_PUBLIC_KEY_SIZE + ZT_ECC384_PRIVATE_KEY_SIZE)) && (reinterpret_cast(uniqueIdPrivate)[0] == (uint8_t)ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384)) { + Utils::copy< 1 + ZT_ECC384_PUBLIC_KEY_SIZE >(s.uniqueId, uniqueIdPrivate); + s.uniqueIdSize = 1 + ZT_ECC384_PUBLIC_KEY_SIZE; // private is prefixed with public + + Vector< uint8_t > enc; + Dictionary d; + m_encodeSubject(s, d, true); + d.encode(enc); + + uint8_t h[ZT_SHA384_DIGEST_SIZE]; + SHA384(h, enc.data(), (unsigned int)enc.size()); + + ECC384ECDSASign(reinterpret_cast(uniqueIdPrivate) + 1 + ZT_ECC384_PUBLIC_KEY_SIZE, h, s.uniqueIdSignature); + s.uniqueIdSignatureSize = ZT_ECC384_SIGNATURE_SIZE; + } else { + return false; + } + } else { + Utils::zero< sizeof(s.uniqueId) >(s.uniqueId); + s.uniqueIdSize = 0; + Utils::zero< sizeof(s.uniqueIdSignature) >(s.uniqueIdSignature); + s.uniqueIdSignatureSize = 0; + } + return true; } void Certificate::m_encodeSubject(const ZT_Certificate_Subject &s, Dictionary &d, bool omitUniqueIdProofSignature) @@ -643,10 +631,10 @@ 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.uniqueId) && (s.uniqueIdSize > 0)) + if (s.uniqueIdSize > 0) d["s.uI"].assign(s.uniqueId, s.uniqueId + s.uniqueIdSize); - if ((!omitUniqueIdProofSignature) && (s.uniqueIdProofSignature) && (s.uniqueIdProofSignatureSize > 0)) - d["s.uS"].assign(s.uniqueIdProofSignature, s.uniqueIdProofSignature + s.uniqueIdProofSignatureSize); + if ((!omitUniqueIdProofSignature) && (s.uniqueIdSignatureSize > 0)) + d["s.uS"].assign(s.uniqueIdSignature, s.uniqueIdSignature + s.uniqueIdSignatureSize); } } // namespace ZeroTier diff --git a/core/Certificate.hpp b/core/Certificate.hpp index f78274286..7ccb2a712 100644 --- a/core/Certificate.hpp +++ b/core/Certificate.hpp @@ -106,22 +106,17 @@ public: void addSubjectUpdateUrl(const char *url); /** - * Set the extended attributes of this certificate + * Sign subject with unique ID private key and set. * - * @param x Extended attributes (set by issuer) + * This is done when you createCSR but can also be done explicitly here. This + * is mostly for testing purposes. + * + * @param uniqueIdPrivate Unique ID private key (includes public) + * @param uniqueIdPrivateSize Size of private key + * @return True on success */ - //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]); + ZT_INLINE bool setSubjectUniqueId(const void *uniqueIdPrivate, unsigned int uniqueIdPrivateSize) + { return m_setSubjectUniqueId(this->subject, uniqueIdPrivate, uniqueIdPrivateSize); } /** * Marshal this certificate in binary form @@ -144,12 +139,13 @@ public: bool decode(const void *data, unsigned int len); /** - * Sign this certificate (and also fill in serialNo). + * Sign this certificate. + * + * This sets serialNo, issuer, issuerPublicKey, and signature. * - * @param issuer Issuer identity (must have secret key) * @return True on success */ - bool sign(const Identity &issuer); + bool sign(const uint8_t issuer[ZT_CERTIFICATE_HASH_SIZE], const void *issuerPrivateKey, unsigned int issuerPrivateKeySize); /** * Verify self-contained signatures and validity of certificate structure @@ -163,29 +159,29 @@ public: */ ZT_CertificateError verify(int64_t clock, bool checkSignatures) const; + /** + * Create a new certificate public/private key pair + * + * @param type Key pair type to create + * @param publicKey Buffer to fill with public key + * @param publicKeySize Result parameter: set to size of public key + * @param privateKey Buffer to fill with private key + * @param privateKeySize Result parameter: set to size of private key + * @return True on success + */ + static bool newKeyPair(const ZT_CertificatePublicKeyAlgorithm type, uint8_t publicKey[ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE], int *const publicKeySize, uint8_t privateKey[ZT_CERTIFICATE_MAX_PRIVATE_KEY_SIZE], int *const privateKeySize); + /** * Create a CSR that encodes the subject of this certificate * * @param s Subject to encode - * @param uniqueId Unique ID to sign subject with or NULL if none - * @param uniqueIdSize Size of unique ID or 0 if none + * @param certificatePublicKey Public key for certificate + * @param certificatePublicKeySize Size of public key * @param uniqueIdPrivate Unique ID private key for proof signature or NULL if none * @param uniqueIdPrivateSize Size of unique ID private key * @return Encoded subject (without any unique ID fields) or empty vector on error */ - static Vector< uint8_t > createCSR(const ZT_Certificate_Subject &s, const void *uniqueId, unsigned int uniqueIdSize, const void *uniqueIdPrivate, unsigned int uniqueIdPrivateSize); - - /** - * Create a subject unique ID and corresponding private key required for use - * - * @param uniqueId Buffer to receive unique ID - * @param uniqueIdPrivate Buffer to receive private key - */ - static ZT_INLINE void createSubjectUniqueId(uint8_t uniqueId[ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE], uint8_t uniqueIdPrivate[ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE]) - { - uniqueId[0] = ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384; - ECC384GenerateKey(uniqueId + 1, uniqueIdPrivate); - } + static Vector< uint8_t > createCSR(const ZT_Certificate_Subject &s, const void *certificatePublicKey, unsigned int certificatePublicKeySize, const void *uniqueIdPrivate, unsigned int uniqueIdPrivateSize); ZT_INLINE unsigned long hashCode() const noexcept { return (unsigned long)Utils::loadMachineEndian< uint32_t >(this->serialNo); } @@ -210,7 +206,7 @@ public: private: void m_clear(); - + static bool m_setSubjectUniqueId(ZT_Certificate_Subject &s, const void *uniqueIdPrivate, unsigned int uniqueIdPrivateSize); static void m_encodeSubject(const ZT_Certificate_Subject &s, Dictionary &d, bool omitUniqueIdProofSignature); // These hold any identity or locator objects that are owned by and should @@ -227,9 +223,6 @@ private: Vector< const uint8_t * > m_subjectCertificates; Vector< const char * > m_updateUrls; Vector< uint8_t > m_extendedAttributes; - Vector< uint8_t > m_subjectUniqueId; - Vector< uint8_t > m_subjectUniqueIdProofSignature; - Vector< uint8_t > m_signature; }; } // namespace ZeroTier diff --git a/core/Containers.hpp b/core/Containers.hpp index b70d58eba..18cffee3d 100644 --- a/core/Containers.hpp +++ b/core/Containers.hpp @@ -198,7 +198,7 @@ struct Blob explicit ZT_INLINE Blob(const void *const d, const unsigned int l) noexcept { - Utils::copy(data, d, l); + Utils::copy(data, d, (l > (unsigned int)S) ? (unsigned int)S : l); if (l < S) { Utils::zero(data + l, S - l); } diff --git a/core/Defaults.cpp b/core/Defaults.cpp index 44410870a..14c31f42b 100644 --- a/core/Defaults.cpp +++ b/core/Defaults.cpp @@ -16,8 +16,9 @@ namespace ZeroTier { namespace Defaults { -const unsigned int CERTIFICATES_BYTES = 0; -const uint8_t CERTIFICATES[4] = {0,0,0,0}; +const uint8_t *CERTIFICATE[DEFAULT_CERTIFICATE_COUNT] = {}; + +unsigned int CERTIFICATE_SIZE[DEFAULT_CERTIFICATE_COUNT] = {}; } // namespace Defaults } // namespace ZeroTier diff --git a/core/Defaults.hpp b/core/Defaults.hpp index bcda32275..5cb239d94 100644 --- a/core/Defaults.hpp +++ b/core/Defaults.hpp @@ -19,8 +19,10 @@ namespace ZeroTier { namespace Defaults { -extern const unsigned int CERTIFICATES_BYTES; -extern const uint8_t CERTIFICATES[]; +#define DEFAULT_CERTIFICATE_COUNT 0 + +extern const uint8_t *CERTIFICATE[DEFAULT_CERTIFICATE_COUNT]; +extern unsigned int CERTIFICATE_SIZE[DEFAULT_CERTIFICATE_COUNT]; } // namespace Defaults } // namespace ZeroTier diff --git a/core/SharedPtr.hpp b/core/SharedPtr.hpp index 534871454..0ea2398df 100644 --- a/core/SharedPtr.hpp +++ b/core/SharedPtr.hpp @@ -54,7 +54,8 @@ public: ZT_INLINE void set(T *ptr) noexcept { m_release(); - const_cast *>(&((m_ptr = ptr)->__refCount))->fetch_add(1, std::memory_order_acquire); + m_ptr = ptr; + const_cast *>(&(ptr->__refCount))->fetch_add(1, std::memory_order_acquire); } /** @@ -169,8 +170,10 @@ private: ZT_INLINE void m_release() const noexcept { - if (unlikely((m_ptr != nullptr)&&(const_cast *>(&(m_ptr->__refCount))->fetch_sub(1, std::memory_order_release) <= 1))) - delete m_ptr; + if (likely(m_ptr != nullptr)) { + if (unlikely(const_cast *>(&(m_ptr->__refCount))->fetch_sub(1, std::memory_order_release) <= 1)) + delete m_ptr; + } } T *m_ptr; diff --git a/core/Tests.cpp b/core/Tests.cpp index 99a2028b7..9fef38859 100644 --- a/core/Tests.cpp +++ b/core/Tests.cpp @@ -309,32 +309,25 @@ static bool ZTT_deepCompareCertificates(const Certificate &a, const Certificate (a.subject.networkCount != b.subject.networkCount) || (a.subject.updateURLCount != b.subject.updateURLCount) || (a.subject.uniqueIdSize != b.subject.uniqueIdSize) || - (a.subject.uniqueIdProofSignatureSize != b.subject.uniqueIdProofSignatureSize) || + (a.subject.uniqueIdSignatureSize != b.subject.uniqueIdSignatureSize) || (a.maxPathLength != b.maxPathLength) || (a.signatureSize != b.signatureSize) ) return false; - if ((a.subject.uniqueId == nullptr) != (b.subject.uniqueId == nullptr)) - return false; - if ((a.subject.uniqueIdProofSignature == nullptr) != (b.subject.uniqueIdProofSignature == nullptr)) + + if ((memcmp(a.subject.uniqueId, b.subject.uniqueId, a.subject.uniqueIdSize) != 0) || (memcmp(a.subject.uniqueIdSignature, b.subject.uniqueIdSignature, a.subject.uniqueIdSignatureSize) != 0) || (memcmp(a.signature, b.signature, a.signatureSize) != 0)) return false; - if ((a.subject.uniqueId != nullptr) && (a.subject.uniqueIdProofSignature != nullptr)) { - if ( - (memcmp(a.subject.uniqueId, b.subject.uniqueId, a.subject.uniqueIdSize) != 0) || - (memcmp(a.subject.uniqueIdProofSignature, b.subject.uniqueIdProofSignature, a.subject.uniqueIdProofSignatureSize) != 0) || - (memcmp(a.signature, b.signature, a.signatureSize) != 0) - ) - return false; - } - - if ((!ZTT_deepCompareCertificateName(a.subject.name, b.subject.name)) || (!ZTT_deepCompareCertificateName(a.issuerName, b.issuerName))) + if (!ZTT_deepCompareCertificateName(a.subject.name, b.subject.name)) return false; - if ((a.issuer == nullptr) != (b.issuer == nullptr)) + if (memcmp(a.issuer, b.issuer, ZT_CERTIFICATE_HASH_SIZE) != 0) return false; - if ((a.issuer != nullptr) && ((*reinterpret_cast(a.issuer)) != (*reinterpret_cast(b.issuer)))) + if ((a.issuerPublicKeySize != b.issuerPublicKeySize) || (memcmp(a.issuerPublicKey, b.issuerPublicKey, a.issuerPublicKeySize) != 0)) + return false; + + if ((a.publicKeySize != b.publicKeySize) || (memcmp(a.publicKey, b.publicKey, a.publicKeySize))) return false; for (unsigned int i = 0; i < a.subject.identityCount; ++i) { @@ -655,7 +648,7 @@ extern "C" const char *ZTT_general() } bufs.clear(); if (Buf::poolAllocated() != ZT_BUF_MAX_POOL_SIZE) { - ZT_T_PRINTF("FAILED (2)" ZT_EOL_S); + ZT_T_PRINTF("FAILED (2) (%ld)" ZT_EOL_S, Buf::poolAllocated()); return "Buf memory pool test failed"; } Buf::freePool(); @@ -1195,19 +1188,24 @@ extern "C" const char *ZTT_crypto() { char tmp[256]; + uint8_t fakeIssuer[48]; + Utils::getSecureRandom(fakeIssuer, sizeof(fakeIssuer)); ZT_T_PRINTF("[crypto] Testing Certificate..." ZT_EOL_S); - ZT_T_PRINTF(" Create test subject and issuer identities... "); - Identity testSubjectId, testIssuerId; + ZT_T_PRINTF(" Create test identities... "); + Identity testSubjectId; testSubjectId.generate(Identity::C25519); - testIssuerId.generate(Identity::P384); ZT_T_PRINTF("OK" ZT_EOL_S); - ZT_T_PRINTF(" Create subject unique ID... "); - uint8_t uniqueId[ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE], uniqueIdPrivate[ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE]; - Certificate::createSubjectUniqueId(uniqueId, uniqueIdPrivate); - Utils::b32e(uniqueId, ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE, tmp, sizeof(tmp)); + ZT_T_PRINTF(" Create subject unique ID key pair... "); + uint8_t uniqueId[ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE], uniqueIdPrivate[ZT_CERTIFICATE_MAX_PRIVATE_KEY_SIZE]; + int uniqueIdSize = 0, uniqueIdPrivateKeySize = 0; + if (!Certificate::newKeyPair(ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384, uniqueId, &uniqueIdSize, uniqueIdPrivate, &uniqueIdPrivateKeySize)) { + ZT_T_PRINTF("FAILED" ZT_EOL_S); + return "Certificate key pair create"; + } + Utils::b32e(uniqueId, uniqueIdSize, tmp, sizeof(tmp)); ZT_T_PRINTF("OK %s" ZT_EOL_S, tmp); ZT_T_PRINTF(" Create and sign certificate... "); @@ -1231,9 +1229,15 @@ extern "C" const char *ZTT_crypto() cert->timestamp = cert->subject.timestamp; cert->validity[0] = 0; cert->validity[1] = 9223372036854775807LL; - Utils::copy< sizeof(ZT_Certificate_Name) >(&cert->issuerName, &cert->subject.name); - cert->setSubjectUniqueId(uniqueId, uniqueIdPrivate); - cert->sign(testIssuerId); + uint8_t issuerPrivateKey[ZT_CERTIFICATE_MAX_PRIVATE_KEY_SIZE], certPrivateKey[ZT_CERTIFICATE_MAX_PRIVATE_KEY_SIZE]; + int issuerPrivateKeySize = 0, certPrivateKeySize = 0; + Certificate::newKeyPair(ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384, cert->issuerPublicKey, (int *)&cert->issuerPublicKeySize, issuerPrivateKey, &issuerPrivateKeySize); + Certificate::newKeyPair(ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384, cert->publicKey, (int *)&cert->publicKeySize, certPrivateKey, &certPrivateKeySize); + cert->setSubjectUniqueId(uniqueIdPrivate, uniqueIdPrivateKeySize); + if (!cert->sign(fakeIssuer, issuerPrivateKey, issuerPrivateKeySize)) { + ZT_T_PRINTF("FAILED" ZT_EOL_S); + return "Certificate sign"; + } Vector< uint8_t > enc(cert->encode()); ZT_T_PRINTF("OK (%d bytes)" ZT_EOL_S, (int)enc.size()); diff --git a/core/TrustStore.cpp b/core/TrustStore.cpp index 7b26554d7..767dfdef8 100644 --- a/core/TrustStore.cpp +++ b/core/TrustStore.cpp @@ -25,8 +25,8 @@ TrustStore::~TrustStore() SharedPtr< TrustStore::Entry > TrustStore::get(const H384 &serial) const { RWMutex::RLock l(m_lock); - Map< H384, SharedPtr< Entry > >::const_iterator i(m_bySerial.find(serial)); - return (i != m_bySerial.end()) ? i->second : SharedPtr< TrustStore::Entry >(); + Map< H384, SharedPtr< Entry > >::const_iterator c(m_bySerial.find(serial)); + return (c != m_bySerial.end()) ? c->second : SharedPtr< TrustStore::Entry >(); } Map< Identity, SharedPtr< const Locator > > TrustStore::roots() @@ -56,9 +56,9 @@ Vector< SharedPtr< TrustStore::Entry > > TrustStore::all(const bool includeRejec RWMutex::RLock l(m_lock); Vector< SharedPtr< Entry > > r; r.reserve(m_bySerial.size()); - for (Map< H384, SharedPtr< Entry > >::const_iterator i(m_bySerial.begin()); i != m_bySerial.end(); ++i) { - if ((includeRejectedCertificates) || (i->second->error() == ZT_CERTIFICATE_ERROR_NONE)) - r.push_back(i->second); + for (Map< H384, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end(); ++c) { + if ((includeRejectedCertificates) || (c->second->error() == ZT_CERTIFICATE_ERROR_NONE)) + r.push_back(c->second); } return r; } @@ -75,25 +75,6 @@ void TrustStore::erase(const H384 &serial) m_deleteQueue.push_front(serial); } -// Recursive function to trace a certificate up the chain to a CA, returning true -// if the CA is reached and the path length is less than the maximum. Note that only -// non-rejected (no errors) certificates will be in bySignedCert. -static bool p_validatePath(const Map< H384, Vector< SharedPtr< TrustStore::Entry > > > &bySignedCert, const SharedPtr< TrustStore::Entry > &entry, unsigned int pathLength) -{ - if (((entry->localTrust() & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA) != 0) && (pathLength <= entry->certificate().maxPathLength)) - return true; - if (pathLength < ZT_CERTIFICATE_MAX_PATH_LENGTH) { - const Map< H384, Vector< SharedPtr< TrustStore::Entry > > >::const_iterator signers(bySignedCert.find(H384(entry->certificate().serialNo))); - if (signers != bySignedCert.end()) { - for (Vector< SharedPtr< TrustStore::Entry > >::const_iterator signer(signers->second.begin()); signer != signers->second.end(); ++signer) { - if ((*signer != entry) && (p_validatePath(bySignedCert, *signer, pathLength + 1))) - return true; - } - } - } - return false; -} - bool TrustStore::update(const int64_t clock, Vector< SharedPtr< Entry > > *const purge) { RWMutex::Lock l(m_lock); @@ -135,20 +116,30 @@ bool TrustStore::update(const int64_t clock, Vector< SharedPtr< Entry > > *const Map< H384, Vector< SharedPtr< Entry > > > bySignedCert; for (;;) { - // Create a reverse lookup mapping from signed certs to signer certs for certificate - // path validation. Only include good certificates. - for (Map< H384, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end(); ++c) { - if (c->second->error() == ZT_CERTIFICATE_ERROR_NONE) { - for (unsigned int j = 0; j < c->second->m_certificate.subject.certificateCount; ++j) - bySignedCert[H384(c->second->m_certificate.subject.certificates[j])].push_back(c->second); - } - } - // Validate certificate paths and reject any certificates that do not trace back to a CA. - for (Map< H384, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end(); ++c) { + for (Map< H384, SharedPtr< Entry > >::iterator c(m_bySerial.begin()); c != m_bySerial.end(); ++c) { if (c->second->error() == ZT_CERTIFICATE_ERROR_NONE) { - if (!p_validatePath(bySignedCert, c->second, 0)) + unsigned int pathLength = 0; + Map< H384, SharedPtr< Entry > >::const_iterator current(c); + Set< H384 > visited; // prevent infinite loops if there's a cycle + for (;;) { + if (pathLength <= current->second->m_certificate.maxPathLength) { + if ((current->second->localTrust() & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA) == 0) { + visited.insert(current->second->m_certificate.getSerialNo()); + const H384 next(current->second->m_certificate.issuer); + if (visited.find(next) == visited.end()) { + current = m_bySerial.find(next); + if ((current != m_bySerial.end()) && (current->second->error() == ZT_CERTIFICATE_ERROR_NONE)) { + ++pathLength; + continue; + } + } + } else { + break; // traced to root CA, abort without setting error + } + } c->second->m_error.store((int)ZT_CERTIFICATE_ERROR_INVALID_CHAIN, std::memory_order_relaxed); + } } } @@ -159,8 +150,8 @@ bool TrustStore::update(const int64_t clock, Vector< SharedPtr< Entry > > *const for (Map< H384, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end();) { if (c->second->error() == ZT_CERTIFICATE_ERROR_NONE) { const unsigned int uniqueIdSize = c->second->m_certificate.subject.uniqueIdSize; - if ((uniqueIdSize > 0) && (uniqueIdSize <= ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE)) { - SharedPtr< Entry > ¤t = m_bySubjectUniqueId[Blob< ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE >(c->second->m_certificate.subject.uniqueId, uniqueIdSize)]; + if ((uniqueIdSize > 0) && (uniqueIdSize <= ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE)) { + SharedPtr< Entry > ¤t = m_bySubjectUniqueId[Blob< ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE >(c->second->m_certificate.subject.uniqueId, uniqueIdSize)]; if (current) { exitLoop = false; if (c->second->m_certificate.subject.timestamp > current->m_certificate.subject.timestamp) { diff --git a/core/TrustStore.hpp b/core/TrustStore.hpp index 28730f982..90b85c1b3 100644 --- a/core/TrustStore.hpp +++ b/core/TrustStore.hpp @@ -178,7 +178,7 @@ public: private: Map< H384, SharedPtr< Entry > > m_bySerial; // all certificates - Map< Blob< ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE >, SharedPtr< Entry > > m_bySubjectUniqueId; // non-rejected certificates only + Map< Blob< ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE >, SharedPtr< Entry > > m_bySubjectUniqueId; // non-rejected certificates only Map< Fingerprint, Vector< SharedPtr< Entry > > > m_bySubjectIdentity; // non-rejected certificates only ForwardList< SharedPtr< Entry > > m_addQueue; ForwardList< H384 > m_deleteQueue; diff --git a/core/zerotier.h b/core/zerotier.h index 95ae318ec..bb1842e46 100644 --- a/core/zerotier.h +++ b/core/zerotier.h @@ -337,27 +337,6 @@ typedef struct */ #define ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ZEROTIER_ROOT_SET 0x0002U -/** - * Size of a unique ID of the given key type (with type prefix byte) - */ -#define ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE 50 - -/** - * Size of the private key corresponding to a unique ID of the given type. - */ -#define ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE 48 - -/** - * Unique ID types supported for certificate subject unique IDs - */ -enum ZT_CertificateUniqueIdType -{ - /** - * Public key type for NIST P-384 public keys used as subject unique IDs. - */ - ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384 = 1 -}; - /** * Errors returned by functions that verify or handle certificates. */ @@ -414,6 +393,42 @@ enum ZT_CertificateError ZT_CERTIFICATE_ERROR_OUT_OF_VALID_TIME_WINDOW = -8 }; +/** + * Public key signing algorithm for certificates + */ +enum ZT_CertificatePublicKeyAlgorithm +{ + /** + * Nil value indicating no signature. + */ + ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_NONE = 0, + + /** + * ECDSA with the NIST P-384 curve. + */ + ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384 = 1 +}; + +/** + * Maximum size of a public key in bytes (can be increased) + */ +#define ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE 64 + +/** + * Maximum size of a private key in bytes (can be increased) + */ +#define ZT_CERTIFICATE_MAX_PRIVATE_KEY_SIZE 128 + +/** + * Maximum size of a signature in bytes (can be increased) + */ +#define ZT_CERTIFICATE_MAX_SIGNATURE_SIZE 128 + +/** + * Size of a SHA384 hash + */ +#define ZT_CERTIFICATE_HASH_SIZE 48 + /** * Information about a real world entity. * @@ -447,7 +462,7 @@ typedef struct const ZT_Identity *identity; /** - * Locator, or NULL if none + * Locator (NULL if no locator included) */ const ZT_Locator *locator; } ZT_Certificate_Identity; @@ -526,28 +541,15 @@ typedef struct /** * Globally unique ID for this subject * - * Unique IDs are actually public keys. Their size makes them globally - * unique (if generated from good randomness) to within ridiculous - * probability bounds. If a subject has a unique ID it must also have - * a unique ID proof signature, which is the signature of the subject - * with the private key corresponding to its unique ID. - * - * This allows subjects to "own" themselves and exist independent of - * CAs or delegated signers. It also allows a certificate for a given - * subject to be updated. - * - * Subject unique IDs are optional. If no unique ID is specified these - * and their corresponding size fields must be empty/zero. - * - * A subject is valid if it has no unique ID or has one with a valid - * proof signature. + * This is actually a public key and is generated the same way as a normal + * certificate public key. */ - const uint8_t *uniqueId; + uint8_t uniqueId[ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE]; /** * Signature proving ownership of unique ID. */ - const uint8_t *uniqueIdProofSignature; + uint8_t uniqueIdSignature[ZT_CERTIFICATE_MAX_SIGNATURE_SIZE]; /** * Size of unique ID in bytes or 0 if none. @@ -557,7 +559,7 @@ typedef struct /** * Proof signature size or 0 if none. */ - unsigned int uniqueIdProofSignatureSize; + unsigned int uniqueIdSignatureSize; } ZT_Certificate_Subject; /** @@ -573,9 +575,9 @@ typedef struct typedef struct { /** - * Serial number, a SHA384 hash of this certificate. + * Serial number, a SHA384 hash of this certificate (minus signature). */ - uint8_t serialNo[48]; + uint8_t serialNo[ZT_CERTIFICATE_HASH_SIZE]; /** * Flags indicating certificate usage and any other attributes. @@ -598,14 +600,29 @@ typedef struct ZT_Certificate_Subject subject; /** - * Issuer node identity and public key(s). + * Issuer certificate serial number. */ - const ZT_Identity *issuer; + uint8_t issuer[ZT_CERTIFICATE_HASH_SIZE]; /** - * Issuer information + * Public key of issuer certificate. */ - ZT_Certificate_Name issuerName; + uint8_t issuerPublicKey[ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE]; + + /** + * Certificate public key (first byte is ZT_CertificatePublicKeyAlgorithm) + */ + uint8_t publicKey[ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE]; + + /** + * Size of issuer public key. + */ + unsigned int issuerPublicKeySize; + + /** + * Size of public key in bytes + */ + unsigned int publicKeySize; /** * Extended attributes set by issuer (in Dictionary format, NULL if none) @@ -617,6 +634,16 @@ typedef struct */ unsigned int extendedAttributesSize; + /** + * Signature by issuer. + */ + uint8_t signature[ZT_CERTIFICATE_MAX_SIGNATURE_SIZE]; + + /** + * Size of signature in bytes. + */ + unsigned int signatureSize; + /** * Maximum path length from this certificate toward further certificates. * @@ -625,16 +652,6 @@ typedef struct * may be signed (not a CA). */ unsigned int maxPathLength; - - /** - * Signature by issuer (algorithm determined by identity type). - */ - const uint8_t *signature; - - /** - * Size of signature in bytes. - */ - unsigned int signatureSize; } ZT_Certificate; /** @@ -2756,23 +2773,24 @@ ZT_SDK_API void ZT_version( /* ---------------------------------------------------------------------------------------------------------------- */ /** - * Create a new certificate subject unique ID and private key + * Create a new public/private key pair. * - * A unique ID is really a public/private key pair. + * This is for use as a certificate public key or as the unique ID for + * enforcing subject ownership. * - * @param type Unique ID type (key pair algorithm) - * @param uniqueId Unique ID buffer - * @param uniqueIdSize Value/result: size of buffer - * @param uniqueIdPrivate Unique ID private key buffer - * @param uniqueIdPrivateSize Value/result: size of buffer + * @param type Type to create + * @param publicKey Public key buffer + * @param publicKeySize Result parameter: set to size of public key + * @param privateKey Private key buffer + * @param privateKeySize Result parameter: set to size of private key * @return OK (0) or error */ -ZT_SDK_API int ZT_Certificate_newSubjectUniqueId( - enum ZT_CertificateUniqueIdType type, - void *uniqueId, - int *uniqueIdSize, - void *uniqueIdPrivate, - int *uniqueIdPrivateSize); +ZT_SDK_API int ZT_Certificate_newKeyPair( + enum ZT_CertificatePublicKeyAlgorithm type, + uint8_t publicKey[ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE], + int *const publicKeySize, + uint8_t privateKey[ZT_CERTIFICATE_MAX_PRIVATE_KEY_SIZE], + int *const privateKeySize); /** * Create a new certificate signing request (CSR) @@ -2784,20 +2802,20 @@ ZT_SDK_API int ZT_Certificate_newSubjectUniqueId( * supplied subject, these will be ignored. * * @param subject Subject filled in with fields for CSR - * @param uniqueId Unique ID or NULL if none - * @param uniqueIdSize Size of unique ID - * @param uniqueIdPrivate Unique ID private key or NULL if none - * @param uniqueIdPrivateSize Size of unique ID private key + * @param certificatePublicKey Public key for new certificate + * @param certificatePublicKeySize Public key size in bytes + * @param uniqueIdPrivateKey Unique ID private key or NULL if none + * @param uniqueIdPrivateKeySize Size of unique ID private key * @param csr Buffer to hold CSR (recommended size: 16384 bytes) * @param csrSize Value/result: size of buffer * @return OK (0) or error */ ZT_SDK_API int ZT_Certificate_newCSR( const ZT_Certificate_Subject *subject, - const void *uniqueId, - int uniqueIdSize, - const void *uniqueIdPrivate, - int uniqueIdPrivateSize, + const void *certificatePublicKey, + int certificatePublicKeySize, + const void *uniqueIdPrivateKey, + int uniqueIdPrivateKeySize, void *csr, int *csrSize); @@ -2808,17 +2826,19 @@ ZT_SDK_API int ZT_Certificate_newCSR( * certificate fields before signing. Things outside the subject are * filled in (or can be modified) by the signer. * + * The returned certificate must be freed with ZT_Certificate_delete(). + * * @param cert Certificate to sign - * @param signer Signer identity (must contain secret key) - * @param signedCert Signed certificate buffer (recommended size: 16384 bytes) - * @param signedCertSize Value/result: size of buffer - * @return OK (0) or error + * @param issuer Serial number of issuer certificate + * @param issuerPrivateKey Private key of issuer (also includes public) + * @param issuerPrivateKeySize Size of private key in bytes + * @return Signed certificate or NULL on error */ -ZT_SDK_API int ZT_Certificate_sign( +ZT_SDK_API ZT_Certificate *ZT_Certificate_sign( const ZT_Certificate *cert, - const ZT_Identity *signer, - void *signedCert, - int *signedCertSize); + const uint8_t issuer[ZT_CERTIFICATE_HASH_SIZE], + const void *issuerPrivateKey, + int issuerPrivateKeySize); /** * Decode a certificate or CSR diff --git a/service/src/commands/locator.rs b/service/src/commands/locator.rs index faec4979f..897c75e20 100644 --- a/service/src/commands/locator.rs +++ b/service/src/commands/locator.rs @@ -20,11 +20,11 @@ fn new_(cli_args: &ArgMatches) -> i32 { if ts.is_empty() { 0_i64 } else { - i64::from_str_radix(ts, 10).unwrap_or(0_i64) * 1000_i64 // internally uses ms since epoch + i64::from_str_radix(ts, 10).unwrap_or(0) } }); - if revision <= 0 { - println!("ERROR: invalid or empty timestamp specified."); + if revision < 0 { + println!("ERROR: invalid revision (must be >= 0)."); return 1; } diff --git a/service/src/main.rs b/service/src/main.rs index 207f526b9..6727eb933 100644 --- a/service/src/main.rs +++ b/service/src/main.rs @@ -111,7 +111,7 @@ Advanced Operations: locator [args] new [-...] [...] Create new signed locator - -r Revision number + -r Revision number (default: time) verify Verify locator signature show Show contents of a locator