Some cleanup, certificate self-signing fixes, test fixes, test full certificate life cycle including CSR.

This commit is contained in:
Adam Ierymenko 2021-05-17 23:40:13 -04:00
parent ec2919ac2e
commit a8e3cffd16
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
20 changed files with 2403 additions and 1701 deletions

View file

@ -4,72 +4,72 @@ BreakBeforeBraces: Stroustrup
IndentWidth: 4
TabWidth: 4
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveMacros: 'true'
AlignConsecutiveAssignments: 'false'
AlignConsecutiveDeclarations: 'false'
AlignConsecutiveMacros: Consecutive
AlignConsecutiveAssignments: Consecutive
AlignConsecutiveDeclarations: None
AlignEscapedNewlines: Right
AlignOperands: 'true'
AlignTrailingComments: 'true'
AllowAllArgumentsOnNextLine: 'false'
AllowAllConstructorInitializersOnNextLine: 'false'
AllowAllParametersOfDeclarationOnNextLine: 'false'
AllowShortBlocksOnASingleLine: 'true'
AllowShortCaseLabelsOnASingleLine: 'false'
AllowShortFunctionsOnASingleLine: None
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AlwaysBreakAfterReturnType: None
BinPackArguments: 'false'
BinPackParameters: 'false'
BinPackArguments: true
BinPackParameters: true
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeTernaryOperators: 'true'
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeComma
BreakInheritanceList: BeforeComma
CompactNamespaces: 'false'
ConstructorInitializerAllOnOneLineOrOnePerLine: 'true'
ConstructorInitializerIndentWidth: '4'
ContinuationIndentWidth: '4'
Cpp11BracedListStyle: 'false'
FixNamespaceComments: 'true'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: false
FixNamespaceComments: true
IncludeBlocks: Regroup
IndentCaseLabels: 'true'
IndentCaseLabels: true
IndentPPDirectives: None
IndentWrappedFunctionNames: 'false'
KeepEmptyLinesAtTheStartOfBlocks: 'false'
MaxEmptyLinesToKeep: '1'
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
PointerAlignment: Left
ReflowComments: 'true'
SortIncludes: 'true'
SortUsingDeclarations: 'true'
SpaceAfterCStyleCast: 'false'
SpaceAfterLogicalNot: 'true'
SpaceAfterTemplateKeyword: 'true'
SpaceBeforeAssignmentOperators: 'true'
SpaceBeforeCpp11BracedList: 'true'
SpaceBeforeCtorInitializerColon: 'true'
SpaceBeforeInheritanceColon: 'true'
PointerAlignment: Right
ReflowComments: true
SortIncludes: CaseInsensitive
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: 'true'
SpaceInEmptyParentheses: 'false'
SpacesBeforeTrailingComments: '3'
SpacesInAngles: 'false'
SpacesInCStyleCastParentheses: 'false'
SpacesInContainerLiterals: 'true'
SpacesInParentheses: 'false'
SpacesInSquareBrackets: 'false'
UseTab: 'false'
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 3
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: true
SpacesInParentheses: false
SpacesInSquareBrackets: false
UseTab: Never
---
Language: Cpp
Standard: Cpp03
ColumnLimit: '240'
Standard: c++11
ColumnLimit: 120
---
Language: ObjC
ColumnLimit: '240'
ColumnLimit: 120
---
Language: Java
ColumnLimit: '240'
ColumnLimit: 120
---
Language: CSharp
ColumnLimit: '240'
ColumnLimit: 120
...

View file

@ -21,31 +21,23 @@ namespace ZeroTier {
Certificate::Certificate() noexcept
{
ZT_Certificate* const sup = this;
ZT_Certificate *const sup = this;
Utils::zero<sizeof(ZT_Certificate)>(sup);
}
Certificate::Certificate(const ZT_Certificate& apiCert) : Certificate()
{
*this = apiCert;
}
Certificate::Certificate(const ZT_Certificate &apiCert) : Certificate() { *this = apiCert; }
Certificate::Certificate(const Certificate& cert) : Certificate()
{
*this = cert;
}
Certificate::Certificate(const Certificate &cert) : Certificate() { *this = cert; }
Certificate::~Certificate()
{
}
Certificate::~Certificate() {}
Certificate& Certificate::operator=(const ZT_Certificate& cert)
Certificate &Certificate::operator=(const ZT_Certificate &cert)
{
m_clear();
Utils::copy<sizeof(this->serialNo)>(this->serialNo, cert.serialNo);
this->usageFlags = cert.usageFlags;
this->timestamp = cert.timestamp;
this->usageFlags = cert.usageFlags;
this->timestamp = cert.timestamp;
this->validity[0] = cert.validity[0];
this->validity[1] = cert.validity[1];
@ -55,10 +47,12 @@ Certificate& Certificate::operator=(const ZT_Certificate& cert)
for (unsigned int i = 0; i < cert.subject.identityCount; ++i) {
if (cert.subject.identities[i].identity) {
if (cert.subject.identities[i].locator) {
addSubjectIdentity(*reinterpret_cast<const Identity*>(cert.subject.identities[i].identity), *reinterpret_cast<const Locator*>(cert.subject.identities[i].locator));
addSubjectIdentity(
*reinterpret_cast<const Identity *>(cert.subject.identities[i].identity),
*reinterpret_cast<const Locator *>(cert.subject.identities[i].locator));
}
else {
addSubjectIdentity(*reinterpret_cast<const Identity*>(cert.subject.identities[i].identity));
addSubjectIdentity(*reinterpret_cast<const Identity *>(cert.subject.identities[i].identity));
}
}
}
@ -80,15 +74,16 @@ Certificate& Certificate::operator=(const ZT_Certificate& cert)
}
}
this->subject.identityCount = cert.subject.identityCount;
this->subject.networkCount = cert.subject.networkCount;
this->subject.identityCount = cert.subject.identityCount;
this->subject.networkCount = cert.subject.networkCount;
this->subject.updateURLCount = cert.subject.updateURLCount;
Utils::copy<sizeof(ZT_Certificate_Name)>(&(this->subject.name), &(cert.subject.name));
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;
Utils::copy<sizeof(this->subject.uniqueIdSignature)>(
this->subject.uniqueIdSignature, cert.subject.uniqueIdSignature);
this->subject.uniqueIdSize = cert.subject.uniqueIdSize;
this->subject.uniqueIdSignatureSize = cert.subject.uniqueIdSignatureSize;
Utils::copy<sizeof(this->issuer)>(this->issuer, cert.issuer);
@ -96,11 +91,11 @@ Certificate& Certificate::operator=(const ZT_Certificate& cert)
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;
this->publicKeySize = cert.publicKeySize;
if ((cert.extendedAttributes != nullptr) && (cert.extendedAttributesSize > 0)) {
m_extendedAttributes.assign(cert.extendedAttributes, cert.extendedAttributes + cert.extendedAttributesSize);
this->extendedAttributes = m_extendedAttributes.data();
this->extendedAttributes = m_extendedAttributes.data();
this->extendedAttributesSize = (unsigned int)m_extendedAttributes.size();
}
@ -112,7 +107,7 @@ Certificate& Certificate::operator=(const ZT_Certificate& cert)
return *this;
}
ZT_Certificate_Identity* Certificate::addSubjectIdentity(const Identity& id)
ZT_Certificate_Identity *Certificate::addSubjectIdentity(const Identity &id)
{
// Store a local copy of the actual identity.
m_identities.push_front(id);
@ -121,18 +116,18 @@ ZT_Certificate_Identity* Certificate::addSubjectIdentity(const Identity& id)
// Enlarge array of ZT_Certificate_Identity structs and set pointer to potentially reallocated array.
m_subjectIdentities.push_back(ZT_Certificate_Identity());
m_subjectIdentities.back().identity = &(m_identities.front());
m_subjectIdentities.back().locator = nullptr;
m_subjectIdentities.back().locator = nullptr;
this->subject.identities = m_subjectIdentities.data();
this->subject.identities = m_subjectIdentities.data();
this->subject.identityCount = (unsigned int)m_subjectIdentities.size();
return &(m_subjectIdentities.back());
}
ZT_Certificate_Identity* Certificate::addSubjectIdentity(const Identity& id, const Locator& loc)
ZT_Certificate_Identity *Certificate::addSubjectIdentity(const Identity &id, const Locator &loc)
{
// Add identity as above.
ZT_Certificate_Identity* const n = addSubjectIdentity(id);
ZT_Certificate_Identity *const n = addSubjectIdentity(id);
// Store local copy of locator.
m_locators.push_front(loc);
@ -143,7 +138,7 @@ ZT_Certificate_Identity* Certificate::addSubjectIdentity(const Identity& id, con
return n;
}
ZT_Certificate_Network* Certificate::addSubjectNetwork(const uint64_t id, const ZT_Fingerprint& controller)
ZT_Certificate_Network *Certificate::addSubjectNetwork(const uint64_t id, const ZT_Fingerprint &controller)
{
// Enlarge array of ZT_Certificate_Network and set pointer to potentially reallocated array.
m_subjectNetworks.resize(++this->subject.networkCount);
@ -156,7 +151,7 @@ ZT_Certificate_Network* Certificate::addSubjectNetwork(const uint64_t id, const
return &(m_subjectNetworks.back());
}
void Certificate::addSubjectUpdateUrl(const char* url)
void Certificate::addSubjectUpdateUrl(const char *url)
{
if ((url != nullptr) && (url[0] != 0)) {
// Store local copy of URL.
@ -165,7 +160,7 @@ void Certificate::addSubjectUpdateUrl(const char* url)
// Add pointer to local copy to pointer array and update C structure to point to
// potentially reallocated array.
m_updateUrls.push_back(m_strings.front().c_str());
this->subject.updateURLs = m_updateUrls.data();
this->subject.updateURLs = m_updateUrls.data();
this->subject.updateURLCount = (unsigned int)m_updateUrls.size();
}
}
@ -196,7 +191,7 @@ Vector<uint8_t> Certificate::encode(const bool omitSignature) const
m_encodeSubject(this->subject, d, false);
if (! Utils::allZero(this->issuer, sizeof(this->issuer)))
if (!Utils::allZero(this->issuer, sizeof(this->issuer)))
d.add("i", this->issuer, sizeof(this->issuer));
if (this->issuerPublicKeySize > 0)
@ -210,7 +205,7 @@ Vector<uint8_t> Certificate::encode(const bool omitSignature) const
if ((this->extendedAttributes != nullptr) && (this->extendedAttributesSize > 0))
d["x"].assign(this->extendedAttributes, this->extendedAttributes + this->extendedAttributesSize);
if ((! omitSignature) && (this->signatureSize > 0))
if ((!omitSignature) && (this->signatureSize > 0))
d["si"].assign(this->signature, this->signature + this->signatureSize);
if (this->maxPathLength > 0)
@ -220,18 +215,18 @@ Vector<uint8_t> Certificate::encode(const bool omitSignature) const
return enc;
}
bool Certificate::decode(const void* const data, const unsigned int len)
bool Certificate::decode(const void *const data, const unsigned int len)
{
char tmp[32], tmp2[ZT_CERTIFICATE_MAX_STRING_LENGTH + 1];
Dictionary d;
if (! d.decode(data, len))
if (!d.decode(data, len))
return false;
m_clear();
this->usageFlags = d.getUI("f");
this->timestamp = (int64_t)d.getUI("t");
this->usageFlags = d.getUI("f");
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");
@ -239,8 +234,8 @@ bool Certificate::decode(const void* const data, const unsigned int len)
unsigned int cnt = (unsigned int)d.getUI("s.i$");
for (unsigned int i = 0; i < cnt; ++i) {
const Vector<uint8_t>& identityData = d[Dictionary::arraySubscript(tmp, sizeof(tmp), "s.i$.i", i)];
const Vector<uint8_t>& locatorData = d[Dictionary::arraySubscript(tmp, sizeof(tmp), "s.i$.l", i)];
const Vector<uint8_t> &identityData = d[Dictionary::arraySubscript(tmp, sizeof(tmp), "s.i$.i", i)];
const Vector<uint8_t> &locatorData = d[Dictionary::arraySubscript(tmp, sizeof(tmp), "s.i$.l", i)];
if (identityData.empty())
return false;
Identity id;
@ -259,8 +254,8 @@ bool Certificate::decode(const void* const data, const unsigned int len)
cnt = (unsigned int)d.getUI("s.nw$");
for (unsigned int i = 0; i < cnt; ++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, sizeof(tmp), "s.nw$.c", 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, sizeof(tmp), "s.nw$.c", i)];
if ((nwid == 0) || (fingerprintData.empty()))
return false;
Fingerprint fp;
@ -286,47 +281,47 @@ 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));
const Vector<uint8_t>& uniqueId = d["s.uI"];
if ((! uniqueId.empty()) && (uniqueId.size() <= sizeof(this->subject.uniqueId))) {
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();
}
const Vector<uint8_t>& uniqueIdSignature = d["s.uS"];
if ((! uniqueIdSignature.empty()) && (uniqueIdSignature.size() <= sizeof(this->subject.uniqueIdSignature))) {
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"];
const Vector<uint8_t> &issuerData = d["i"];
if (issuerData.size() == sizeof(this->issuer)) {
Utils::copy<sizeof(this->issuer)>(this->issuer, issuerData.data());
}
const Vector<uint8_t>& issuerPublicKey = d["iPK"];
if ((! issuerPublicKey.empty()) && (issuerPublicKey.size() <= sizeof(this->issuerPublicKey))) {
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();
}
const Vector<uint8_t>& publicKey = d["pK"];
if ((! publicKey.empty()) && (publicKey.size() <= sizeof(this->publicKey))) {
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();
}
const Vector<uint8_t>& subjectSignature = d["sS"];
if ((! subjectSignature.empty()) && (subjectSignature.size() <= sizeof(this->subjectSignature))) {
const Vector<uint8_t> &subjectSignature = d["sS"];
if ((!subjectSignature.empty()) && (subjectSignature.size() <= sizeof(this->subjectSignature))) {
Utils::copy(this->subjectSignature, subjectSignature.data(), subjectSignature.size());
this->subjectSignatureSize = (unsigned int)subjectSignature.size();
}
m_extendedAttributes = d["x"];
if (! m_extendedAttributes.empty()) {
this->extendedAttributes = m_extendedAttributes.data();
if (!m_extendedAttributes.empty()) {
this->extendedAttributes = m_extendedAttributes.data();
this->extendedAttributesSize = (unsigned int)m_extendedAttributes.size();
}
const Vector<uint8_t>& signature = d["si"];
if ((! signature.empty()) && (signature.size() <= sizeof(this->signature))) {
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();
}
@ -339,25 +334,37 @@ bool Certificate::decode(const void* const data, const unsigned int len)
return true;
}
bool Certificate::sign(const uint8_t issuer[ZT_CERTIFICATE_HASH_SIZE], const void* const issuerPrivateKey, const unsigned int issuerPrivateKeySize)
bool Certificate::sign(
const uint8_t issuer[ZT_CERTIFICATE_HASH_SIZE], const void *const issuerPrivateKey,
const unsigned int issuerPrivateKeySize)
{
if ((! issuerPrivateKey) || (issuerPrivateKeySize == 0))
if ((!issuerPrivateKey) || (issuerPrivateKeySize == 0))
return false;
switch (reinterpret_cast<const uint8_t*>(issuerPrivateKey)[0]) {
default:
return false;
switch (reinterpret_cast<const uint8_t *>(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;
if ((!issuer)||((this->publicKeySize == (ZT_ECC384_PUBLIC_KEY_SIZE + 1))&&(memcmp(issuerPrivateKey, this->publicKey, ZT_ECC384_PUBLIC_KEY_SIZE + 1) == 0))) {
// If public key and issuer public key match, this is a self-signed certificate.
// This can also be specified by signing with issuer set to NULL.
Utils::fill<sizeof(this->issuer), 0xff>(this->issuer);
this->issuerPublicKeySize = 0;
} else {
// Otherwise set the issuer and issuer public key.
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;
}
const Vector<uint8_t> enc(encode(true));
SHA384(this->serialNo, enc.data(), (unsigned int)enc.size());
ECC384ECDSASign(reinterpret_cast<const uint8_t*>(issuerPrivateKey) + 1 + ZT_ECC384_PUBLIC_KEY_SIZE, this->serialNo, this->signature);
ECC384ECDSASign(
reinterpret_cast<const uint8_t *>(issuerPrivateKey) + 1 + ZT_ECC384_PUBLIC_KEY_SIZE, this->serialNo,
this->signature);
this->signatureSize = ZT_ECC384_SIGNATURE_SIZE;
return true;
@ -378,14 +385,18 @@ ZT_CertificateError Certificate::verify(const int64_t clock, const bool checkSig
if (this->subject.identityCount > 0) {
if (this->subject.identities) {
for (unsigned int i = 0; i < this->subject.identityCount; ++i) {
if (! this->subject.identities[i].identity) {
if (!this->subject.identities[i].identity) {
return ZT_CERTIFICATE_ERROR_INVALID_FORMAT;
}
if (checkSignatures) {
if (! reinterpret_cast<const Identity*>(this->subject.identities[i].identity)->locallyValidate()) {
if (!reinterpret_cast<const Identity *>(this->subject.identities[i].identity)
->locallyValidate()) {
return ZT_CERTIFICATE_ERROR_INVALID_IDENTITY;
}
if ((this->subject.identities[i].locator) && (! reinterpret_cast<const Locator*>(this->subject.identities[i].locator)->verify(*reinterpret_cast<const Identity*>(this->subject.identities[i].identity)))) {
if ((this->subject.identities[i].locator)
&& (!reinterpret_cast<const Locator *>(this->subject.identities[i].locator)
->verify(
*reinterpret_cast<const Identity *>(this->subject.identities[i].identity)))) {
return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE;
}
}
@ -399,7 +410,7 @@ ZT_CertificateError Certificate::verify(const int64_t clock, const bool checkSig
if (this->subject.networkCount > 0) {
if (this->subject.networks) {
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;
}
}
@ -412,7 +423,7 @@ ZT_CertificateError Certificate::verify(const int64_t clock, const bool checkSig
if (this->subject.updateURLCount > 0) {
if (this->subject.updateURLs) {
for (unsigned int i = 0; i < this->subject.updateURLCount; ++i) {
if (! this->subject.updateURLs[i])
if (!this->subject.updateURLs[i])
return ZT_CERTIFICATE_ERROR_MISSING_REQUIRED_FIELDS;
}
}
@ -421,10 +432,14 @@ ZT_CertificateError Certificate::verify(const int64_t clock, const bool checkSig
}
}
if ((this->subject.uniqueIdSize > sizeof(this->subject.uniqueId)) || (this->subject.uniqueIdSignatureSize > sizeof(this->subject.uniqueIdSignature)) || (this->issuerPublicKeySize > sizeof(this->issuerPublicKey)) || (this->publicKeySize > sizeof(this->publicKey)) || (this->subjectSignatureSize > sizeof(this->subjectSignature))) {
if ((this->subject.uniqueIdSize > sizeof(this->subject.uniqueId))
|| (this->subject.uniqueIdSignatureSize > sizeof(this->subject.uniqueIdSignature))
|| (this->issuerPublicKeySize > sizeof(this->issuerPublicKey))
|| (this->publicKeySize > sizeof(this->publicKey))
|| (this->subjectSignatureSize > sizeof(this->subjectSignature))) {
return ZT_CERTIFICATE_ERROR_INVALID_FORMAT;
}
if ((this->extendedAttributesSize > 0) && (! this->extendedAttributes)) {
if ((this->extendedAttributesSize > 0) && (!this->extendedAttributes)) {
return ZT_CERTIFICATE_ERROR_INVALID_FORMAT;
}
if (this->signatureSize > sizeof(this->signature)) {
@ -432,58 +447,92 @@ ZT_CertificateError Certificate::verify(const int64_t clock, const bool checkSig
}
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) {
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;
}
Dictionary d;
Vector<uint8_t> enc;
// Check subject signature to verify that CRL was intact and the
// public key was the one intended by the CRL creator.
if (this->publicKeySize > 0) {
d.clear();
m_encodeSubject(this->subject, d, false);
d.encode(enc);
// If the issuer is all 1's (0xffffff...) then this cert is self-signed.
bool selfSigned = true;
for (unsigned int i = 0; i < ZT_CERTIFICATE_HASH_SIZE; ++i) {
if (this->issuer[i] != 0xff) {
selfSigned = false;
break;
}
}
switch (this->publicKey[0]) {
case ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384:
if ((this->publicKeySize == (ZT_ECC384_PUBLIC_KEY_SIZE + 1)) && (this->subjectSignatureSize == ZT_ECC384_SIGNATURE_SIZE)) {
uint8_t h[ZT_SHA384_DIGEST_SIZE];
SHA384(h, enc.data(), (unsigned int)enc.size());
if (! ECC384ECDSAVerify(this->publicKey + 1, h, this->subjectSignature)) {
if (!selfSigned) {
// Regular certs have an issuer signature and a self-signature
// of their subject to ensure CSR integrity.
if (this->issuerPublicKeySize > 0) {
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;
}
if (this->publicKeySize > 0) {
d.clear();
m_encodeSubject(this->subject, d, false);
d.encode(enc);
switch (this->publicKey[0]) {
case ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384:
if ((this->publicKeySize == (ZT_ECC384_PUBLIC_KEY_SIZE + 1))
&& (this->subjectSignatureSize == ZT_ECC384_SIGNATURE_SIZE)) {
uint8_t h[ZT_SHA384_DIGEST_SIZE];
SHA384(h, enc.data(), (unsigned int)enc.size());
if (!ECC384ECDSAVerify(this->publicKey + 1, h, this->subjectSignature)) {
return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE;
}
}
else {
return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE;
}
}
else {
return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE;
}
break;
default:
return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE;
break;
default: return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE;
}
}
else {
return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE;
}
}
else {
return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE;
// Self-signed certs are just signed by their own public keys.
// The issuer public key and subject self-signature are ignored
// and can be empty.
if (this->publicKeySize > 0) {
switch (this->publicKey[0]) {
case ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384:
if ((this->publicKeySize == (ZT_ECC384_PUBLIC_KEY_SIZE + 1))
&& (this->signatureSize == ZT_ECC384_SIGNATURE_SIZE)) {
if (!ECC384ECDSAVerify(this->publicKey + 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;
}
}
// Subject unique ID signatures are optional, so this only fails if it
@ -492,19 +541,22 @@ ZT_CertificateError Certificate::verify(const int64_t clock, const bool checkSig
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_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)) {
if ((this->subject.uniqueIdSize == (ZT_ECC384_PUBLIC_KEY_SIZE + 1))
&& (this->subject.uniqueIdSignatureSize == ZT_ECC384_SIGNATURE_SIZE)) {
d.clear();
m_encodeSubject(this->subject, d, true);
d.encode(enc);
static_assert(ZT_ECC384_SIGNATURE_HASH_SIZE == ZT_SHA384_DIGEST_SIZE, "ECC384 should take 384-bit hash");
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)) {
if (!ECC384ECDSAVerify(
this->subject.uniqueId + 1, h, this->subject.uniqueIdSignature)) {
return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF;
}
}
@ -512,8 +564,7 @@ ZT_CertificateError Certificate::verify(const int64_t clock, const bool checkSig
return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF;
}
break;
default:
return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF;
default: return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF;
}
}
else {
@ -523,7 +574,7 @@ ZT_CertificateError Certificate::verify(const int64_t clock, const bool checkSig
}
if (clock >= 0) {
if (! this->verifyTimeWindow(clock))
if (!this->verifyTimeWindow(clock))
return ZT_CERTIFICATE_ERROR_OUT_OF_VALID_TIME_WINDOW;
}
}
@ -534,31 +585,36 @@ ZT_CertificateError Certificate::verify(const int64_t clock, const bool checkSig
return ZT_CERTIFICATE_ERROR_NONE;
}
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)
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;
*publicKeySize = ZT_ECC384_PUBLIC_KEY_SIZE + 1;
*privateKeySize = 1 + ZT_ECC384_PUBLIC_KEY_SIZE + ZT_ECC384_PRIVATE_KEY_SIZE;
return true;
default:
break;
default: break;
}
return false;
}
Vector<uint8_t> Certificate::createCSR(const ZT_Certificate_Subject& s, const void* const certificatePrivateKey, const unsigned int certificatePrivateKeySize, const void* uniqueIdPrivate, unsigned int uniqueIdPrivateSize)
Vector<uint8_t> Certificate::createCSR(
const ZT_Certificate_Subject &s, const void *const certificatePrivateKey,
const unsigned int certificatePrivateKeySize, const void *uniqueIdPrivate, unsigned int uniqueIdPrivateSize)
{
Vector<uint8_t> enc;
ZT_Certificate_Subject sc;
Utils::copy<sizeof(ZT_Certificate_Subject)>(&sc, &s);
if ((! certificatePrivateKey) || (certificatePrivateKeySize != (1 + ZT_ECC384_PUBLIC_KEY_SIZE + ZT_ECC384_PRIVATE_KEY_SIZE))
|| (reinterpret_cast<const uint8_t*>(certificatePrivateKey)[0] != ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384))
if ((!certificatePrivateKey)
|| (certificatePrivateKeySize != (1 + ZT_ECC384_PUBLIC_KEY_SIZE + ZT_ECC384_PRIVATE_KEY_SIZE))
|| (reinterpret_cast<const uint8_t *>(certificatePrivateKey)[0]
!= ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384))
return enc;
if (m_setSubjectUniqueId(sc, uniqueIdPrivate, uniqueIdPrivateSize)) {
@ -568,9 +624,11 @@ Vector<uint8_t> Certificate::createCSR(const ZT_Certificate_Subject& s, const vo
uint8_t subjectHash[ZT_SHA384_DIGEST_SIZE], subjectSig[ZT_ECC384_SIGNATURE_SIZE];
SHA384(subjectHash, enc.data(), (unsigned int)enc.size());
ECC384ECDSASign(reinterpret_cast<const uint8_t*>(certificatePrivateKey) + 1 + ZT_ECC384_PUBLIC_KEY_SIZE, subjectHash, subjectSig);
ECC384ECDSASign(
reinterpret_cast<const uint8_t *>(certificatePrivateKey) + 1 + ZT_ECC384_PUBLIC_KEY_SIZE, subjectHash,
subjectSig);
d.add("pK", reinterpret_cast<const uint8_t*>(certificatePrivateKey), (1 + ZT_ECC384_PUBLIC_KEY_SIZE));
d.add("pK", reinterpret_cast<const uint8_t *>(certificatePrivateKey), (1 + ZT_ECC384_PUBLIC_KEY_SIZE));
d.add("sS", subjectSig, ZT_ECC384_SIGNATURE_SIZE);
d.encode(enc);
@ -581,7 +639,7 @@ Vector<uint8_t> Certificate::createCSR(const ZT_Certificate_Subject& s, const vo
void Certificate::m_clear()
{
ZT_Certificate* const sup = this;
ZT_Certificate *const sup = this;
Utils::zero<sizeof(ZT_Certificate)>(sup);
m_identities.clear();
@ -594,11 +652,14 @@ void Certificate::m_clear()
m_extendedAttributes.clear();
}
bool Certificate::m_setSubjectUniqueId(ZT_Certificate_Subject& s, const void* uniqueIdPrivate, unsigned int uniqueIdPrivateSize)
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<const uint8_t*>(uniqueIdPrivate)[0] == (uint8_t)ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384)) {
if ((uniqueIdPrivate != nullptr)
&& (uniqueIdPrivateSize == (1 + ZT_ECC384_PUBLIC_KEY_SIZE + ZT_ECC384_PRIVATE_KEY_SIZE))
&& (reinterpret_cast<const uint8_t *>(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
@ -610,7 +671,9 @@ bool Certificate::m_setSubjectUniqueId(ZT_Certificate_Subject& s, const void* un
uint8_t h[ZT_SHA384_DIGEST_SIZE];
SHA384(h, enc.data(), (unsigned int)enc.size());
ECC384ECDSASign(reinterpret_cast<const uint8_t*>(uniqueIdPrivate) + 1 + ZT_ECC384_PUBLIC_KEY_SIZE, h, s.uniqueIdSignature);
ECC384ECDSASign(
reinterpret_cast<const uint8_t *>(uniqueIdPrivate) + 1 + ZT_ECC384_PUBLIC_KEY_SIZE, h,
s.uniqueIdSignature);
s.uniqueIdSignatureSize = ZT_ECC384_SIGNATURE_SIZE;
}
else {
@ -626,7 +689,7 @@ bool Certificate::m_setSubjectUniqueId(ZT_Certificate_Subject& s, const void* un
return true;
}
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[32];
@ -636,9 +699,13 @@ void Certificate::m_encodeSubject(const ZT_Certificate_Subject& s, Dictionary& d
d.add("s.i$", (uint64_t)s.identityCount);
for (unsigned int i = 0; i < s.identityCount; ++i) {
if (s.identities[i].identity)
d.addO(Dictionary::arraySubscript(tmp, sizeof(tmp), "s.i$.i", i), *reinterpret_cast<const Identity*>(s.identities[i].identity));
d.addO(
Dictionary::arraySubscript(tmp, sizeof(tmp), "s.i$.i", i),
*reinterpret_cast<const Identity *>(s.identities[i].identity));
if (s.identities[i].locator)
d.addO(Dictionary::arraySubscript(tmp, sizeof(tmp), "s.i$.l", i), *reinterpret_cast<const Locator*>(s.identities[i].locator));
d.addO(
Dictionary::arraySubscript(tmp, sizeof(tmp), "s.i$.l", i),
*reinterpret_cast<const Locator *>(s.identities[i].locator));
}
}
@ -684,7 +751,7 @@ void Certificate::m_encodeSubject(const ZT_Certificate_Subject& s, Dictionary& d
if (s.uniqueIdSize > 0)
d["s.uI"].assign(s.uniqueId, s.uniqueId + s.uniqueIdSize);
if ((! omitUniqueIdProofSignature) && (s.uniqueIdSignatureSize > 0))
if ((!omitUniqueIdProofSignature) && (s.uniqueIdSignatureSize > 0))
d["s.uS"].assign(s.uniqueIdSignature, s.uniqueIdSignature + s.uniqueIdSignatureSize);
}

View file

@ -61,11 +61,26 @@ class Certificate : public ZT_Certificate {
return *this;
}
/**
* @return Serial number in a H384 object
*/
ZT_INLINE H384 getSerialNo() const noexcept
{
return H384(this->serialNo);
}
/**
* @return True if this is a self-signed certificate
*/
ZT_INLINE bool isSelfSigned() const noexcept
{
for(unsigned int i=0;i<ZT_CERTIFICATE_HASH_SIZE;++i) {
if (this->issuer[i] != 0xff)
return false;
}
return true;
}
/**
* Add a subject node/identity without a locator
*

File diff suppressed because it is too large Load diff

View file

@ -65,7 +65,8 @@ void identityV0ProofOfWorkFrankenhash(const void* const restrict c25519CombinedP
}
}
struct identityV0ProofOfWorkCriteria {
struct identityV0ProofOfWorkCriteria
{
ZT_INLINE identityV0ProofOfWorkCriteria(unsigned char* restrict sb, char* restrict gm) noexcept
: digest(sb)
, genmem(gm)

File diff suppressed because it is too large Load diff

View file

@ -149,45 +149,55 @@ bool TrustStore::update(const int64_t clock, Vector<SharedPtr<Entry> >* const pu
Vector<Entry*> visited;
visited.reserve(8);
for (Map<H384, SharedPtr<Entry> >::iterator c(m_bySerial.begin()); c != m_bySerial.end(); ++c) {
if ((c->second->m_error == ZT_CERTIFICATE_ERROR_NONE) && (! c->second->m_onTrustPath) && ((c->second->m_localTrust & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA) == 0)) {
// Trace the path of each certificate all the way back to a trusted CA.
unsigned int pathLength = 0;
Map<H384, SharedPtr<Entry> >::const_iterator current(c);
visited.clear();
for (;;) {
if (pathLength <= current->second->m_certificate.maxPathLength) {
// Check if this cert isn't a CA or already part of a valid trust path. If so then step upward
// toward CA.
if (((current->second->m_localTrust & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA) == 0) && (! current->second->m_onTrustPath)) {
// If the issuer (parent) certificiate is (1) valid, (2) not already visited (to prevent
// loops), and (3) has a public key that matches this cert's issuer public key (sanity
// check), proceed up the certificate graph toward a potential CA.
visited.push_back(current->second.ptr());
const Map<H384, SharedPtr<Entry> >::const_iterator prevChild(current);
current = m_bySerial.find(H384(current->second->m_certificate.issuer));
if ((current != m_bySerial.end()) && (std::find(visited.begin(), visited.end(), current->second.ptr()) == visited.end()) && (current->second->m_error == ZT_CERTIFICATE_ERROR_NONE)
&& (current->second->m_certificate.publicKeySize == prevChild->second->m_certificate.issuerPublicKeySize)
&& (memcmp(current->second->m_certificate.publicKey, prevChild->second->m_certificate.issuerPublicKey, current->second->m_certificate.publicKeySize) == 0)) {
++pathLength;
continue;
}
}
else {
// If we've traced this to a root CA, flag its parents as also being on a trust path. Then
// break the loop without setting an error. We don't flag the current cert as being on a
// trust path since no other certificates depend on it.
for (Vector<Entry*>::const_iterator v(visited.begin()); v != visited.end(); ++v) {
if (*v != c->second.ptr())
(*v)->m_onTrustPath = true;
if (c->second->m_error == ZT_CERTIFICATE_ERROR_NONE) {
if (c->second->m_certificate.isSelfSigned()) {
// If this is a self-signed certificate it's only valid if it's trusted as a CA.
if ((c->second->m_localTrust & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA) == 0) {
c->second->m_error = ZT_CERTIFICATE_ERROR_INVALID_CHAIN;
}
}
else {
if ((! c->second->m_onTrustPath) && ((c->second->m_localTrust & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA) == 0)) {
// Trace the path of each certificate all the way back to a trusted CA.
unsigned int pathLength = 0;
Map<H384, SharedPtr<Entry> >::const_iterator current(c);
visited.clear();
for (;;) {
if (pathLength <= current->second->m_certificate.maxPathLength) {
// Check if this cert isn't a CA or already part of a valid trust path. If so then step upward
// toward CA.
if (((current->second->m_localTrust & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA) == 0) && (! current->second->m_onTrustPath)) {
// If the issuer (parent) certificiate is (1) valid, (2) not already visited (to prevent
// loops), and (3) has a public key that matches this cert's issuer public key (sanity
// check), proceed up the certificate graph toward a potential CA.
visited.push_back(current->second.ptr());
const Map<H384, SharedPtr<Entry> >::const_iterator prevChild(current);
current = m_bySerial.find(H384(current->second->m_certificate.issuer));
if ((current != m_bySerial.end()) && (std::find(visited.begin(), visited.end(), current->second.ptr()) == visited.end()) && (current->second->m_error == ZT_CERTIFICATE_ERROR_NONE)
&& (current->second->m_certificate.publicKeySize == prevChild->second->m_certificate.issuerPublicKeySize)
&& (memcmp(current->second->m_certificate.publicKey, prevChild->second->m_certificate.issuerPublicKey, current->second->m_certificate.publicKeySize) == 0)) {
++pathLength;
continue;
}
}
else {
// If we've traced this to a root CA, flag its parents as also being on a trust path. Then
// break the loop without setting an error. We don't flag the current cert as being on a
// trust path since no other certificates depend on it.
for (Vector<Entry*>::const_iterator v(visited.begin()); v != visited.end(); ++v) {
if (*v != c->second.ptr())
(*v)->m_onTrustPath = true;
}
break;
}
}
// If we made it here without breaking or continuing, no path to a
// CA was found and the certificate's chain is invalid.
c->second->m_error = ZT_CERTIFICATE_ERROR_INVALID_CHAIN;
break;
}
}
// If we made it here without breaking or continuing, no path to a
// CA was found and the certificate's chain is invalid.
c->second->m_error = ZT_CERTIFICATE_ERROR_INVALID_CHAIN;
break;
}
}
}

View file

@ -648,6 +648,37 @@ static ZT_INLINE void zero(void* dest, unsigned long len) noexcept
#endif
}
/**
* Zero memory block whose size is known at compile time
*
* @tparam L Size in bytes
* @param dest Memory to zero
*/
template <unsigned long L, uint8_t B> static ZT_INLINE void fill(void* dest) noexcept
{
#if defined(ZT_ARCH_X64) && defined(__GNUC__)
uintptr_t l = L;
__asm__ __volatile__("cld ; rep stosb" : "+c"(l), "+D"(dest) : "a"(B) : "memory");
#else
memset(dest, B, L);
#endif
}
/**
* Zero memory block whose size is known at run time
*
* @param dest Memory to zero
* @param len Size in bytes
*/
template <uint8_t B> static ZT_INLINE void fill(void* dest, unsigned long len) noexcept
{
#if defined(ZT_ARCH_X64) && defined(__GNUC__)
__asm__ __volatile__("cld ; rep stosb" : "+c"(len), "+D"(dest) : "a"(B) : "memory");
#else
memset(dest, B, len);
#endif
}
/**
* Compute 32-bit FNV-1a checksum
*

View file

@ -657,6 +657,11 @@ typedef struct {
/**
* Issuer certificate serial number.
*
* If this is a self-signed certificate this will be all 0xff (all 1s).
* The issuerPublicKey and subjectSignature fields will be empty and
* are not used. The signature will be a signature of this certificate
* with its own public key.
*/
uint8_t issuer[ZT_CERTIFICATE_HASH_SIZE];
@ -673,7 +678,7 @@ typedef struct {
/**
* Signature of subject with public key.
*
* This couples the subject to the public key, ensuring that the CRL was
* This couples the subject to the public key, ensuring that the CSR was
* not modified in transit or by the signer.
*/
uint8_t subjectSignature[ZT_CERTIFICATE_MAX_SIGNATURE_SIZE];

View file

@ -55,50 +55,6 @@ clock_serv_t OSUtils::s_machMonotonicClock = _machGetMonotonicClock();
#endif
unsigned int OSUtils::ztsnprintf(char* buf, unsigned int len, const char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
int n = (int)vsnprintf(buf, len, fmt, ap);
va_end(ap);
if ((n >= (int)len) || (n < 0)) {
if (len)
buf[len - 1] = (char)0;
throw std::length_error("buf[] overflow");
}
return (unsigned int)n;
}
#ifdef __UNIX_LIKE__
bool OSUtils::redirectUnixOutputs(const char* stdoutPath, const char* stderrPath)
{
int fdout = open(stdoutPath, O_WRONLY | O_CREAT, 0600);
if (fdout > 0) {
int fderr;
if (stderrPath) {
fderr = open(stderrPath, O_WRONLY | O_CREAT, 0600);
if (fderr <= 0) {
::close(fdout);
return false;
}
}
else
fderr = fdout;
::close(STDOUT_FILENO);
::close(STDERR_FILENO);
::dup2(fdout, STDOUT_FILENO);
::dup2(fderr, STDERR_FILENO);
return true;
}
return false;
}
#endif // __UNIX_LIKE__
Vector<String> OSUtils::listDirectory(const char* path, bool includeDirectories)
{
Vector<String> r;

View file

@ -58,21 +58,19 @@ class OSUtils {
* @param ... Format arguments
* @throws std::length_error buf[] too short (buf[] will still be left null-terminated)
*/
static unsigned int ztsnprintf(char* buf, unsigned int len, const char* fmt, ...);
#ifdef __UNIX_LIKE__
/**
* Close STDOUT_FILENO and STDERR_FILENO and replace them with output to given path
*
* This can be called after fork() and prior to exec() to suppress output
* from a subprocess, such as auto-update.
*
* @param stdoutPath Path to file to use for stdout
* @param stderrPath Path to file to use for stderr, or NULL for same as stdout (default)
* @return True on success
*/
static bool redirectUnixOutputs(const char* stdoutPath, const char* stderrPath = nullptr);
#endif // __UNIX_LIKE__
static ZT_INLINE unsigned int ztsnprintf(char* buf, unsigned int len, const char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
int n = (int)vsnprintf(buf, len, fmt, ap);
va_end(ap);
if ((n >= (int)len) || (n < 0)) {
if (len)
buf[len - 1] = (char)0;
throw std::length_error("buf[] overflow");
}
return (unsigned int)n;
}
/**
* Delete a file

View file

@ -98,6 +98,25 @@ impl CertificateSerialNo {
pub fn is_nil(&self) -> bool {
is_all_zeroes(self.0)
}
/// Returns true if this serial indicates a self-signed certificate.
/// This is meaningful for the serial in the issuer field of Certificate,
/// which will be all 0xff in self-signed certificates.
#[inline(always)]
pub fn is_self_signed(&self) -> bool {
for x in self.0.iter() {
if *x != 0xff {
return false;
}
}
return true;
}
/// Set this to all 0xff which in the issuer field means self-signed.
#[inline(always)]
pub fn set_self_signed(&mut self) {
self.0.fill(0xff);
}
}
impl Hash for CertificateSerialNo {
@ -671,7 +690,7 @@ impl Certificate {
}
}
pub fn to_bytes(&self) -> Result<Box<[u8]>, ResultCode> {
pub fn to_bytes(&self) -> Result<Vec<u8>, ResultCode> {
let mut cert: Vec<u8> = Vec::new();
cert.resize(16384, 0);
let mut cert_size: c_int = 16384;
@ -682,7 +701,7 @@ impl Certificate {
}
}
cert.resize(cert_size as usize, 0);
return Ok(cert.into_boxed_slice());
return Ok(cert);
}
/// Sign this certificate, returning new signed certificate.
@ -709,6 +728,16 @@ impl Certificate {
return CertificateError::from_i32(ztcore::ZT_Certificate_verify(&capi.certificate as *const ztcore::ZT_Certificate, clock) as i32).unwrap_or(CertificateError::InvalidFormat);
}
}
/// Returns true if this is a self-signed certificate.
pub fn is_self_signed(&self) -> bool {
for b in self.issuer.0.iter() {
if *b != 0xff {
return false;
}
}
return true;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View file

@ -84,12 +84,12 @@ pub const DEFAULT_UDP_MTU: u32 = ztcore::ZT_DEFAULT_UDP_MTU;
/// Maximum UDP MTU (we never actually get this high).
pub const MAX_UDP_MTU: u32 = ztcore::ZT_MAX_UDP_MTU;
/// Base64 encode using the URL-safe with no padding configuration.
/// Base64 encode using the URL-safe alphabet with no padding.
pub fn base64_encode<T: AsRef<[u8]>>(t: &T) -> String {
base64::encode_config(t, base64::URL_SAFE_NO_PAD)
}
/// Base64 decode using the URL-safe with no padding configuration.
/// Base64 decode using the URL-safe alphabet with no padding.
pub fn base64_decode<T: AsRef<[u8]>>(t: &T) -> Result<Vec<u8>, base64::DecodeError> {
base64::decode_config(t, base64::URL_SAFE_NO_PAD)
}

View file

@ -252,12 +252,13 @@ fn newcsr(cli_args: &ArgMatches) -> i32 {
println!("ERROR: unable to read subject unique ID secret file: {}", b.err().unwrap().to_string());
return 1;
}
let privk_hex = String::from_utf8(b.unwrap());
if privk_hex.is_err() {
let privk_encoded = String::from_utf8(b.unwrap());
if privk_encoded.is_err() {
println!("ERROR: invalid UTF-8 in secret");
return 1;
}
let privk = hex::decode(privk_hex.unwrap().trim());
let privk_encoded = privk_encoded.unwrap();
let privk = base64_decode(&privk_encoded);
if privk.is_err() || privk.as_ref().unwrap().is_empty() {
println!("ERROR: invalid unique ID secret: {}", privk.err().unwrap().to_string());
return 1;
@ -416,7 +417,7 @@ fn newcsr(cli_args: &ArgMatches) -> i32 {
1
}, |_| {
let secret_path = cli_args.value_of("secretpath").unwrap();
std::fs::write(secret_path, hex::encode(privk)).map_or_else(|e| {
std::fs::write(secret_path, base64_encode(&privk)).map_or_else(|e| {
let _ = std::fs::remove_file(csr_path);
println!("ERROR: unable to write secret: {}", e.to_string());
1
@ -429,8 +430,98 @@ fn newcsr(cli_args: &ArgMatches) -> i32 {
}
fn sign<'a>(store: &Arc<Store>, cli_args: &ArgMatches<'a>) -> i32 {
let theme = &dialoguer::theme::SimpleTheme;
let now = ms_since_epoch();
let csr_path = cli_args.value_of("csr").unwrap().trim(); // required
let certout_path = cli_args.value_of("certout").unwrap().trim(); // required
let usage = cert_string_to_usage_flags(cli_args.value_of("usage").unwrap_or(""));
let timestamp = cli_args.value_of("timestamp").map_or_else(|| now, |ts| i64::from_str(ts.trim()).unwrap_or(now));
let start_time = cli_args.value_of("start").map_or_else(|| timestamp, |s| i64::from_str(s.trim()).unwrap_or(now));
let end_time = cli_args.value_of("ttl").map_or(i64::MAX, |e| i64::from_str(e.trim()).map_or(i64::MAX, |e| timestamp + e));
let issuer_path = cli_args.value_of("issuer").unwrap().trim(); // required
let issuer_secret_path = cli_args.value_of("issuersecret").unwrap().trim(); // required
let csr = crate::utils::read_limit(csr_path, 131072);
if csr.is_err() {
println!("ERROR: unable to read CSR from '{}': {}", csr_path, csr.err().unwrap().to_string());
return 1;
}
let csr = csr.unwrap();
let cert = Certificate::new_from_bytes(csr.as_slice(), false);
if cert.is_err() {
println!("ERROR: error decoding CSR read from '{}': {}", csr_path, cert.err().unwrap().to_str());
return 1;
}
let mut cert = cert.ok().unwrap();
cert.usage_flags = usage;
cert.timestamp = timestamp;
cert.validity[0] = start_time;
cert.validity[1] = end_time;
let issuer = if issuer_path == "self" {
cert.issuer_public_key.clear();
cert.subject_signature.clear();
cert.issuer.clone()
} else {
let issuer = crate::utils::read_limit(issuer_path, 131072);
if issuer.is_err() {
println!("ERROR: unable to read issuer from '{}': {}", issuer_path, issuer.err().unwrap().to_string());
return 1;
}
let issuer = issuer.unwrap();
let issuer_cert = Certificate::new_from_bytes(issuer.as_slice(), true);
if issuer_cert.is_err() {
println!("ERROR: issuer at '{}' is invalid: {}", issuer_path, issuer_cert.err().unwrap().to_str());
return 1;
}
let issuer_cert = issuer_cert.ok().unwrap();
if (issuer_cert.usage_flags & CERTIFICATE_USAGE_CERTIFICATE_SIGNING) == 0 {
println!("ERROR: issuer at '{}' usage does not permit certificate signing.", issuer_path);
return 1;
}
issuer_cert.issuer.clone()
};
let issuer_secret = crate::utils::read_limit(issuer_secret_path, 1024);
if issuer_secret.is_err() {
println!("ERROR: unable to read issuer secret from '{}': {}", issuer_secret_path, issuer_secret.err().unwrap().to_string());
return 1;
}
let issuer_secret = issuer_secret.unwrap();
let issuer_secret = base64_decode(&issuer_secret);
if issuer_secret.is_err() {
println!("ERROR: invalid issuer secret in '{}': invalid base64: {}", issuer_secret_path, issuer_secret.err().unwrap().to_string());
return 1;
}
let issuer_secret = issuer_secret.unwrap();
let signed_cert = cert.sign(&issuer, issuer_secret.as_slice());
if signed_cert.is_err() {
println!("ERROR: error signing certificate: {}", signed_cert.err().unwrap().to_str());
return 1;
}
let signed_cert = signed_cert.ok().unwrap();
let signed_cert_check = signed_cert.verify(-1);
if signed_cert_check != CertificateError::None {
println!("ERROR: error signing certificate: failed verify after sign: {}", signed_cert_check.to_str());
return 1;
}
cert_print(&signed_cert);
println!();
let signed_cert = signed_cert.to_bytes().ok().unwrap();
let signed_write_result = std::fs::write(certout_path, signed_cert.as_slice());
if signed_write_result.is_err() {
println!("ERROR: unable to write result to '{}': {}", certout_path, signed_write_result.err().unwrap().to_string());
return 1;
}
println!("Signed certificate written to {}", certout_path);
0
}
@ -467,7 +558,7 @@ pub(crate) fn run(store: Arc<Store>, global_flags: GlobalFlags, cli_args: &ArgMa
("delete", Some(sub_cli_args)) => delete(&store, sub_cli_args),
("factoryreset", None) => factoryreset(&store),
_ => {
crate::print_help();
crate::print_help(true);
1
}
}

View file

@ -127,7 +127,7 @@ pub(crate) fn run<'a>(cli_args: &ArgMatches<'a>) -> i32 {
("sign", Some(sub_cli_args)) => sign(sub_cli_args),
("verify", Some(sub_cli_args)) => verify(sub_cli_args),
_ => {
crate::print_help();
crate::print_help(true);
1
}
}

View file

@ -109,7 +109,7 @@ pub(crate) fn run(cli_args: &ArgMatches) -> i32 {
("verify", Some(sub_cli_args)) => verify(sub_cli_args),
("show", Some(sub_cli_args)) => show(sub_cli_args),
_ => {
crate::print_help();
crate::print_help(true);
1
}
}

View file

@ -16,7 +16,8 @@ mod commands;
mod fastudpsocket;
mod localconfig;
mod getifaddrs;
#[macro_use] mod log;
#[macro_use]
mod log;
mod store;
mod network;
mod vnic;
@ -38,7 +39,7 @@ use crate::store::Store;
pub const HTTP_API_OBJECT_SIZE_LIMIT: usize = 131072;
fn make_help() -> String {
fn make_help(long_help: bool) -> String {
let ver = zerotier_core::version();
format!(r###"ZeroTier Network Hypervisor Service Version {}.{}.{}
(c)2013-2021 ZeroTier, Inc.
@ -56,6 +57,7 @@ Global Options:
Common Operations:
help Show this help
longhelp Show help with advanced commands
oldhelp Show v1.x legacy commands
version Print version (of this binary)
@ -85,10 +87,11 @@ Common Operations:
· globalroutes <boolean> Can global IP routes be set?
· defaultroute <boolean> Can default route be overridden?
· join [-...] <network> Join a virtual network
-c <?identity | fingerprint> Controller identity / fingerprint
· join <network> Join a virtual network
· leave <network> Leave a virtual network
{}"###,
ver.0, ver.1, ver.2, if long_help {
r###"
Advanced Operations:
service Start node
@ -120,12 +123,27 @@ Advanced Operations:
· list List certificates at local node
show <@cert|·serial> Show certificate details
newsuid [@secret out] Create a subject unique ID secret
newcsr <@csr> <@secret out> Create a CSR (interactive)
sign <@csr> <@secret> <@cert out> Sign a CSR to create a certificate
newcsr <@csr out> <@secret out> Create a CSR (interactive)
sign <@csr> <@cert out> Sign a CSR to create a certificate
-u <usage flags (ex: sedacr)> Set usage flags (recommended)
s Usage: digital signature
n Usage: non-repudiation
e Usage: key encipherment
d Usage: key decipherment
a Usage: key agreement
c Usage: certificate signing (CA)
r Usage: CRL signing (CA)
x Usage: executable signing
t Usage: timestamping
-t <timestamp, seconds since epoch> Timestamp (default: current time)
-s <validity start time, seconds> Start time (default: timestamp)
-l <#y|d|h|m|s... (ex: 1y, 3d12h)> Time to live (default: forever)
-i <@issuer | self> Issuer or self-sign (required)
-k <@issuer secret> Secret key for issuer (required)
verify <@cert> Internally verify certificate
· import <@cert> [trust,trust,...] Import certificate into this node
trust flag: rootca Root (or self-signed) CA
trust flag: config Can influence node configuration
ca Trust: root CA or self-signed
config Trust: node configuration
· export <serial> [@cert] Export a certificate from this node
· delete <serial|ALL> Delete certificate from this node
· factoryreset Re-import compiled-in default certs
@ -133,11 +151,12 @@ Advanced Operations:
· Command (or command with argument type) requires a running node.
@ Argument is the path to a file containing the object.
? Argument can be either the object or a path to it (auto-detected).
"###, ver.0, ver.1, ver.2)
"###
} else { "" })
}
pub(crate) fn print_help() {
let h = make_help();
pub(crate) fn print_help(long_help: bool) {
let h = make_help(long_help);
let _ = std::io::stdout().write_all(h.as_bytes());
}
@ -152,6 +171,7 @@ pub(crate) fn parse_bool(v: &str) -> Result<bool, String> {
Err(format!("invalid boolean value: '{}'", v))
}
#[inline(always)]
fn is_valid_bool(v: String) -> Result<(), String> {
parse_bool(v.as_str()).map(|_| ())
}
@ -188,7 +208,7 @@ fn get_global_flags(cli_args: &ArgMatches) -> GlobalFlags {
fn main() {
let cli_args = {
let help = make_help();
let help = make_help(false);
let args = App::new("zerotier")
.arg(Arg::with_name("json").short("j"))
.arg(Arg::with_name("path").short("p").takes_value(true))
@ -226,7 +246,6 @@ fn main() {
.arg(Arg::with_name("setting").index(2).required(false))
.arg(Arg::with_name("value").index(3).required(false))))
.subcommand(App::new("join")
.arg(Arg::with_name("controller").short("c").takes_value(true))
.arg(Arg::with_name("nwid").index(1).required(true)))
.subcommand(App::new("leave")
.arg(Arg::with_name("nwid").index(1).required(true)))
@ -282,8 +301,13 @@ fn main() {
.arg(Arg::with_name("secretpath").index(2).required(true)))
.subcommand(App::new("sign")
.arg(Arg::with_name("csr").index(1).required(true))
.arg(Arg::with_name("secretpath").index(2).required(true))
.arg(Arg::with_name("output").index(3).required(false)))
.arg(Arg::with_name("certout").index(2).required(true))
.arg(Arg::with_name("usage").short("u").required(false))
.arg(Arg::with_name("timestamp").short("t").required(false))
.arg(Arg::with_name("start").short("s").required(false))
.arg(Arg::with_name("ttl").short("l").required(false))
.arg(Arg::with_name("issuer").short("i").required(true))
.arg(Arg::with_name("issuersecret").short("k").required(true)))
.subcommand(App::new("verify")
.arg(Arg::with_name("cert").index(1).required(true)))
.subcommand(App::new("dump")
@ -302,13 +326,13 @@ fn main() {
if args.is_err() {
let e = args.err().unwrap();
if e.kind != ErrorKind::HelpDisplayed {
print_help();
print_help(false);
}
std::process::exit(1);
}
let args = args.unwrap();
if args.subcommand_name().is_none() {
print_help();
print_help(false);
std::process::exit(1);
}
args
@ -316,36 +340,40 @@ fn main() {
std::process::exit({
match cli_args.subcommand() {
("help", _) => {
print_help();
("help", None) => {
print_help(false);
0
}
("oldhelp", _) => {
("longhelp", None) => {
print_help(true);
0
}
("oldhelp", None) => {
// TODO
0
}
("version", _) => {
("version", None) => {
let ver = zerotier_core::version();
println!("{}.{}.{}", ver.0, ver.1, ver.2);
0
}
("status", _) => crate::httpclient::run_command(make_store(&cli_args), get_global_flags(&cli_args), crate::commands::status::run),
("status", None) => crate::httpclient::run_command(make_store(&cli_args), get_global_flags(&cli_args), crate::commands::status::run),
("set", Some(sub_cli_args)) => { 0 }
("peer", Some(sub_cli_args)) => { 0 }
("network", Some(sub_cli_args)) => { 0 }
("join", Some(sub_cli_args)) => { 0 }
("leave", Some(sub_cli_args)) => { 0 }
("service", _) => {
("service", None) => {
let store = make_store(&cli_args);
drop(cli_args); // free no longer needed memory before entering service
service::run(store)
},
}
("controller", Some(sub_cli_args)) => { 0 }
("identity", Some(sub_cli_args)) => crate::commands::identity::run(sub_cli_args),
("locator", Some(sub_cli_args)) => crate::commands::locator::run(sub_cli_args),
("cert", Some(sub_cli_args)) => crate::commands::cert::run(make_store(&cli_args), get_global_flags(&cli_args), sub_cli_args),
_ => {
print_help();
print_help(false);
1
}
}

View file

@ -98,7 +98,7 @@ pub(crate) fn read_locator(input: &str) -> Result<Locator, String> {
/// each execution of the process. By decrypting this nonce when it is returned,
/// the client and server may check the age of a digest auth exchange.
pub(crate) fn create_http_auth_nonce(timestamp: i64) -> String {
let mut nonce_plaintext: [u64; 2] = [timestamp as u64, 12345]; // the second u64 is arbitrary and unused
let mut nonce_plaintext: [u64; 2] = [timestamp as u64, timestamp as u64];
unsafe {
osdep::encryptHttpAuthNonce(nonce_plaintext.as_mut_ptr().cast());
hex::encode(*nonce_plaintext.as_ptr().cast::<[u8; 16]>())
@ -109,18 +109,19 @@ pub(crate) fn create_http_auth_nonce(timestamp: i64) -> String {
/// This returns zero if the input was not valid.
pub(crate) fn decrypt_http_auth_nonce(nonce: &str) -> i64 {
let nonce = hex::decode(nonce.trim());
if nonce.is_err() {
return 0;
}
let mut nonce = nonce.unwrap();
if nonce.len() != 16 {
return 0;
}
unsafe {
osdep::decryptHttpAuthNonce(nonce.as_mut_ptr().cast());
let nonce = *nonce.as_ptr().cast::<[u64; 2]>();
nonce[0] as i64
if !nonce.is_err() {
let mut nonce = nonce.unwrap();
if nonce.len() == 16 {
unsafe {
osdep::decryptHttpAuthNonce(nonce.as_mut_ptr().cast());
let nonce = *nonce.as_ptr().cast::<[u64; 2]>();
if nonce[0] == nonce[1] {
return nonce[0] as i64;
}
}
}
}
return 0;
}
/// Shortcut to use serde_json to serialize an object, returns "null" on error.

View file

@ -11,7 +11,7 @@
*/
/****/
use std::collections::BTreeSet;
use std::collections::HashSet;
#[allow(unused_imports)]
use zerotier_core::{MAC, MulticastGroup};
@ -21,8 +21,8 @@ use num_traits::AsPrimitive;
/// BSD based OSes support getifmaddrs().
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "netbsd", target_os = "openbsd", target_os = "dragonfly", target_os = "freebsd", target_os = "darwin"))]
pub(crate) fn get_l2_multicast_subscriptions(dev: &str) -> BTreeSet<MulticastGroup> {
let mut groups: BTreeSet<MulticastGroup> = BTreeSet::new();
pub(crate) fn get_l2_multicast_subscriptions(dev: &str) -> HashSet<MulticastGroup> {
let mut groups: HashSet<MulticastGroup> = HashSet::new();
let dev = dev.as_bytes();
unsafe {
let mut maddrs: *mut osdep::ifmaddrs = std::ptr::null_mut();
@ -50,7 +50,7 @@ pub(crate) fn get_l2_multicast_subscriptions(dev: &str) -> BTreeSet<MulticastGro
/// Linux stores this stuff in /proc and it needs to be fetched from there.
#[cfg(target_os = "linux")]
pub(crate) fn get_l2_multicast_subscriptions(dev: &str) -> BTreeSet<MulticastGroup> {
let mut groups: BTreeSet<MulticastGroup> = BTreeSet::new();
pub(crate) fn get_l2_multicast_subscriptions(dev: &str) -> HashSet<MulticastGroup> {
let mut groups: HashSet<MulticastGroup> = HashSet::new();
groups
}

View file

@ -36,7 +36,7 @@
*/
use std::cell::Cell;
use std::collections::BTreeSet;
use std::collections::HashSet;
use std::error::Error;
use std::ffi::CString;
use std::ptr::{null_mut, copy_nonoverlapping};
@ -53,7 +53,7 @@ use zerotier_core::{InetAddress, MAC, MulticastGroup, NetworkId};
use crate::osdep as osdep;
use crate::getifaddrs;
use crate::vnic::VNIC;
use crate::vnic::vnic::VNIC;
use crate::osdep::getifmaddrs;
const BPF_BUFFER_SIZE: usize = 131072;
@ -423,7 +423,7 @@ impl VNIC for MacFethTap {
}
#[inline(always)]
fn get_multicast_groups(&self) -> BTreeSet<MulticastGroup> {
fn get_multicast_groups(&self) -> HashSet<MulticastGroup> {
crate::vnic::common::get_l2_multicast_subscriptions(self.device.name.as_str())
}