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

View file

@ -21,31 +21,23 @@ namespace ZeroTier {
Certificate::Certificate() noexcept Certificate::Certificate() noexcept
{ {
ZT_Certificate* const sup = this; ZT_Certificate *const sup = this;
Utils::zero<sizeof(ZT_Certificate)>(sup); Utils::zero<sizeof(ZT_Certificate)>(sup);
} }
Certificate::Certificate(const ZT_Certificate& apiCert) : Certificate() Certificate::Certificate(const ZT_Certificate &apiCert) : Certificate() { *this = apiCert; }
{
*this = apiCert;
}
Certificate::Certificate(const Certificate& cert) : Certificate() Certificate::Certificate(const Certificate &cert) : Certificate() { *this = cert; }
{
*this = cert;
}
Certificate::~Certificate() Certificate::~Certificate() {}
{
}
Certificate& Certificate::operator=(const ZT_Certificate& cert) Certificate &Certificate::operator=(const ZT_Certificate &cert)
{ {
m_clear(); m_clear();
Utils::copy<sizeof(this->serialNo)>(this->serialNo, cert.serialNo); Utils::copy<sizeof(this->serialNo)>(this->serialNo, cert.serialNo);
this->usageFlags = cert.usageFlags; this->usageFlags = cert.usageFlags;
this->timestamp = cert.timestamp; this->timestamp = cert.timestamp;
this->validity[0] = cert.validity[0]; this->validity[0] = cert.validity[0];
this->validity[1] = cert.validity[1]; 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) { for (unsigned int i = 0; i < cert.subject.identityCount; ++i) {
if (cert.subject.identities[i].identity) { if (cert.subject.identities[i].identity) {
if (cert.subject.identities[i].locator) { if (cert.subject.identities[i].locator) {
addSubjectIdentity(*reinterpret_cast<const Identity*>(cert.subject.identities[i].identity), *reinterpret_cast<const Locator*>(cert.subject.identities[i].locator)); addSubjectIdentity(
*reinterpret_cast<const Identity *>(cert.subject.identities[i].identity),
*reinterpret_cast<const Locator *>(cert.subject.identities[i].locator));
} }
else { 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.identityCount = cert.subject.identityCount;
this->subject.networkCount = cert.subject.networkCount; this->subject.networkCount = cert.subject.networkCount;
this->subject.updateURLCount = cert.subject.updateURLCount; this->subject.updateURLCount = cert.subject.updateURLCount;
Utils::copy<sizeof(ZT_Certificate_Name)>(&(this->subject.name), &(cert.subject.name)); 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.uniqueId)>(this->subject.uniqueId, cert.subject.uniqueId);
Utils::copy<sizeof(this->subject.uniqueIdSignature)>(this->subject.uniqueIdSignature, cert.subject.uniqueIdSignature); Utils::copy<sizeof(this->subject.uniqueIdSignature)>(
this->subject.uniqueIdSize = cert.subject.uniqueIdSize; this->subject.uniqueIdSignature, cert.subject.uniqueIdSignature);
this->subject.uniqueIdSize = cert.subject.uniqueIdSize;
this->subject.uniqueIdSignatureSize = cert.subject.uniqueIdSignatureSize; this->subject.uniqueIdSignatureSize = cert.subject.uniqueIdSignatureSize;
Utils::copy<sizeof(this->issuer)>(this->issuer, cert.issuer); 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->issuerPublicKey)>(this->issuerPublicKey, cert.issuerPublicKey);
Utils::copy<sizeof(this->publicKey)>(this->publicKey, cert.publicKey); Utils::copy<sizeof(this->publicKey)>(this->publicKey, cert.publicKey);
this->issuerPublicKeySize = cert.issuerPublicKeySize; this->issuerPublicKeySize = cert.issuerPublicKeySize;
this->publicKeySize = cert.publicKeySize; this->publicKeySize = cert.publicKeySize;
if ((cert.extendedAttributes != nullptr) && (cert.extendedAttributesSize > 0)) { if ((cert.extendedAttributes != nullptr) && (cert.extendedAttributesSize > 0)) {
m_extendedAttributes.assign(cert.extendedAttributes, cert.extendedAttributes + cert.extendedAttributesSize); 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(); this->extendedAttributesSize = (unsigned int)m_extendedAttributes.size();
} }
@ -112,7 +107,7 @@ Certificate& Certificate::operator=(const ZT_Certificate& cert)
return *this; 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. // Store a local copy of the actual identity.
m_identities.push_front(id); 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. // Enlarge array of ZT_Certificate_Identity structs and set pointer to potentially reallocated array.
m_subjectIdentities.push_back(ZT_Certificate_Identity()); m_subjectIdentities.push_back(ZT_Certificate_Identity());
m_subjectIdentities.back().identity = &(m_identities.front()); 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(); this->subject.identityCount = (unsigned int)m_subjectIdentities.size();
return &(m_subjectIdentities.back()); 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. // Add identity as above.
ZT_Certificate_Identity* const n = addSubjectIdentity(id); ZT_Certificate_Identity *const n = addSubjectIdentity(id);
// Store local copy of locator. // Store local copy of locator.
m_locators.push_front(loc); m_locators.push_front(loc);
@ -143,7 +138,7 @@ ZT_Certificate_Identity* Certificate::addSubjectIdentity(const Identity& id, con
return n; 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. // Enlarge array of ZT_Certificate_Network and set pointer to potentially reallocated array.
m_subjectNetworks.resize(++this->subject.networkCount); m_subjectNetworks.resize(++this->subject.networkCount);
@ -156,7 +151,7 @@ ZT_Certificate_Network* Certificate::addSubjectNetwork(const uint64_t id, const
return &(m_subjectNetworks.back()); return &(m_subjectNetworks.back());
} }
void Certificate::addSubjectUpdateUrl(const char* url) void Certificate::addSubjectUpdateUrl(const char *url)
{ {
if ((url != nullptr) && (url[0] != 0)) { if ((url != nullptr) && (url[0] != 0)) {
// Store local copy of URL. // 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 // Add pointer to local copy to pointer array and update C structure to point to
// potentially reallocated array. // potentially reallocated array.
m_updateUrls.push_back(m_strings.front().c_str()); m_updateUrls.push_back(m_strings.front().c_str());
this->subject.updateURLs = m_updateUrls.data(); this->subject.updateURLs = m_updateUrls.data();
this->subject.updateURLCount = (unsigned int)m_updateUrls.size(); this->subject.updateURLCount = (unsigned int)m_updateUrls.size();
} }
} }
@ -196,7 +191,7 @@ Vector<uint8_t> Certificate::encode(const bool omitSignature) const
m_encodeSubject(this->subject, d, false); 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)); d.add("i", this->issuer, sizeof(this->issuer));
if (this->issuerPublicKeySize > 0) if (this->issuerPublicKeySize > 0)
@ -210,7 +205,7 @@ Vector<uint8_t> Certificate::encode(const bool omitSignature) const
if ((this->extendedAttributes != nullptr) && (this->extendedAttributesSize > 0)) if ((this->extendedAttributes != nullptr) && (this->extendedAttributesSize > 0))
d["x"].assign(this->extendedAttributes, this->extendedAttributes + this->extendedAttributesSize); d["x"].assign(this->extendedAttributes, this->extendedAttributes + this->extendedAttributesSize);
if ((! omitSignature) && (this->signatureSize > 0)) if ((!omitSignature) && (this->signatureSize > 0))
d["si"].assign(this->signature, this->signature + this->signatureSize); d["si"].assign(this->signature, this->signature + this->signatureSize);
if (this->maxPathLength > 0) if (this->maxPathLength > 0)
@ -220,18 +215,18 @@ Vector<uint8_t> Certificate::encode(const bool omitSignature) const
return enc; 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]; char tmp[32], tmp2[ZT_CERTIFICATE_MAX_STRING_LENGTH + 1];
Dictionary d; Dictionary d;
if (! d.decode(data, len)) if (!d.decode(data, len))
return false; return false;
m_clear(); m_clear();
this->usageFlags = d.getUI("f"); this->usageFlags = d.getUI("f");
this->timestamp = (int64_t)d.getUI("t"); this->timestamp = (int64_t)d.getUI("t");
this->validity[0] = (int64_t)d.getUI("v#0"); this->validity[0] = (int64_t)d.getUI("v#0");
this->validity[1] = (int64_t)d.getUI("v#1"); 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$"); unsigned int cnt = (unsigned int)d.getUI("s.i$");
for (unsigned int i = 0; i < cnt; ++i) { for (unsigned int i = 0; i < cnt; ++i) {
const Vector<uint8_t>& identityData = d[Dictionary::arraySubscript(tmp, sizeof(tmp), "s.i$.i", 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> &locatorData = d[Dictionary::arraySubscript(tmp, sizeof(tmp), "s.i$.l", i)];
if (identityData.empty()) if (identityData.empty())
return false; return false;
Identity id; Identity id;
@ -259,8 +254,8 @@ bool Certificate::decode(const void* const data, const unsigned int len)
cnt = (unsigned int)d.getUI("s.nw$"); cnt = (unsigned int)d.getUI("s.nw$");
for (unsigned int i = 0; i < cnt; ++i) { for (unsigned int i = 0; i < cnt; ++i) {
const uint64_t nwid = d.getUI(Dictionary::arraySubscript(tmp, sizeof(tmp), "s.nw$.i", i)); const uint64_t nwid = d.getUI(Dictionary::arraySubscript(tmp, sizeof(tmp), "s.nw$.i", i));
const Vector<uint8_t>& fingerprintData = d[Dictionary::arraySubscript(tmp, sizeof(tmp), "s.nw$.c", i)]; const Vector<uint8_t> &fingerprintData = d[Dictionary::arraySubscript(tmp, sizeof(tmp), "s.nw$.c", i)];
if ((nwid == 0) || (fingerprintData.empty())) if ((nwid == 0) || (fingerprintData.empty()))
return false; return false;
Fingerprint fp; Fingerprint fp;
@ -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.ur", this->subject.name.url, sizeof(this->subject.name.url));
d.getS("s.n.h", this->subject.name.host, sizeof(this->subject.name.host)); d.getS("s.n.h", this->subject.name.host, sizeof(this->subject.name.host));
const Vector<uint8_t>& uniqueId = d["s.uI"]; const Vector<uint8_t> &uniqueId = d["s.uI"];
if ((! uniqueId.empty()) && (uniqueId.size() <= sizeof(this->subject.uniqueId))) { if ((!uniqueId.empty()) && (uniqueId.size() <= sizeof(this->subject.uniqueId))) {
Utils::copy(this->subject.uniqueId, uniqueId.data(), uniqueId.size()); Utils::copy(this->subject.uniqueId, uniqueId.data(), uniqueId.size());
this->subject.uniqueIdSize = (unsigned int)uniqueId.size(); this->subject.uniqueIdSize = (unsigned int)uniqueId.size();
} }
const Vector<uint8_t>& uniqueIdSignature = d["s.uS"]; const Vector<uint8_t> &uniqueIdSignature = d["s.uS"];
if ((! uniqueIdSignature.empty()) && (uniqueIdSignature.size() <= sizeof(this->subject.uniqueIdSignature))) { if ((!uniqueIdSignature.empty()) && (uniqueIdSignature.size() <= sizeof(this->subject.uniqueIdSignature))) {
Utils::copy(this->subject.uniqueIdSignature, uniqueIdSignature.data(), uniqueIdSignature.size()); Utils::copy(this->subject.uniqueIdSignature, uniqueIdSignature.data(), uniqueIdSignature.size());
this->subject.uniqueIdSignatureSize = (unsigned int)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)) { if (issuerData.size() == sizeof(this->issuer)) {
Utils::copy<sizeof(this->issuer)>(this->issuer, issuerData.data()); Utils::copy<sizeof(this->issuer)>(this->issuer, issuerData.data());
} }
const Vector<uint8_t>& issuerPublicKey = d["iPK"]; const Vector<uint8_t> &issuerPublicKey = d["iPK"];
if ((! issuerPublicKey.empty()) && (issuerPublicKey.size() <= sizeof(this->issuerPublicKey))) { if ((!issuerPublicKey.empty()) && (issuerPublicKey.size() <= sizeof(this->issuerPublicKey))) {
Utils::copy(this->issuerPublicKey, issuerPublicKey.data(), issuerPublicKey.size()); Utils::copy(this->issuerPublicKey, issuerPublicKey.data(), issuerPublicKey.size());
this->issuerPublicKeySize = (unsigned int)issuerPublicKey.size(); this->issuerPublicKeySize = (unsigned int)issuerPublicKey.size();
} }
const Vector<uint8_t>& publicKey = d["pK"]; const Vector<uint8_t> &publicKey = d["pK"];
if ((! publicKey.empty()) && (publicKey.size() <= sizeof(this->publicKey))) { if ((!publicKey.empty()) && (publicKey.size() <= sizeof(this->publicKey))) {
Utils::copy(this->publicKey, publicKey.data(), publicKey.size()); Utils::copy(this->publicKey, publicKey.data(), publicKey.size());
this->publicKeySize = (unsigned int)publicKey.size(); this->publicKeySize = (unsigned int)publicKey.size();
} }
const Vector<uint8_t>& subjectSignature = d["sS"]; const Vector<uint8_t> &subjectSignature = d["sS"];
if ((! subjectSignature.empty()) && (subjectSignature.size() <= sizeof(this->subjectSignature))) { if ((!subjectSignature.empty()) && (subjectSignature.size() <= sizeof(this->subjectSignature))) {
Utils::copy(this->subjectSignature, subjectSignature.data(), subjectSignature.size()); Utils::copy(this->subjectSignature, subjectSignature.data(), subjectSignature.size());
this->subjectSignatureSize = (unsigned int)subjectSignature.size(); this->subjectSignatureSize = (unsigned int)subjectSignature.size();
} }
m_extendedAttributes = d["x"]; m_extendedAttributes = d["x"];
if (! m_extendedAttributes.empty()) { if (!m_extendedAttributes.empty()) {
this->extendedAttributes = m_extendedAttributes.data(); this->extendedAttributes = m_extendedAttributes.data();
this->extendedAttributesSize = (unsigned int)m_extendedAttributes.size(); this->extendedAttributesSize = (unsigned int)m_extendedAttributes.size();
} }
const Vector<uint8_t>& signature = d["si"]; const Vector<uint8_t> &signature = d["si"];
if ((! signature.empty()) && (signature.size() <= sizeof(this->signature))) { if ((!signature.empty()) && (signature.size() <= sizeof(this->signature))) {
Utils::copy(this->signature, signature.data(), signature.size()); Utils::copy(this->signature, signature.data(), signature.size());
this->signatureSize = (unsigned int)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; 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; return false;
switch (reinterpret_cast<const uint8_t*>(issuerPrivateKey)[0]) { switch (reinterpret_cast<const uint8_t *>(issuerPrivateKey)[0]) {
default: default: return false;
return false;
case ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384: case ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384:
if (issuerPrivateKeySize == (1 + ZT_ECC384_PUBLIC_KEY_SIZE + ZT_ECC384_PRIVATE_KEY_SIZE)) { if (issuerPrivateKeySize == (1 + ZT_ECC384_PUBLIC_KEY_SIZE + ZT_ECC384_PRIVATE_KEY_SIZE)) {
Utils::copy<sizeof(this->issuer)>(this->issuer, issuer); if ((!issuer)||((this->publicKeySize == (ZT_ECC384_PUBLIC_KEY_SIZE + 1))&&(memcmp(issuerPrivateKey, this->publicKey, ZT_ECC384_PUBLIC_KEY_SIZE + 1) == 0))) {
Utils::copy<1 + ZT_ECC384_PUBLIC_KEY_SIZE>(this->issuerPublicKey, // If public key and issuer public key match, this is a self-signed certificate.
issuerPrivateKey); // private is prefixed with public // This can also be specified by signing with issuer set to NULL.
this->issuerPublicKeySize = 1 + ZT_ECC384_PUBLIC_KEY_SIZE; 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)); const Vector<uint8_t> enc(encode(true));
SHA384(this->serialNo, enc.data(), (unsigned int)enc.size()); SHA384(this->serialNo, enc.data(), (unsigned int)enc.size());
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; this->signatureSize = ZT_ECC384_SIGNATURE_SIZE;
return true; 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.identityCount > 0) {
if (this->subject.identities) { if (this->subject.identities) {
for (unsigned int i = 0; i < this->subject.identityCount; ++i) { 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; return ZT_CERTIFICATE_ERROR_INVALID_FORMAT;
} }
if (checkSignatures) { 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; 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; 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.networkCount > 0) {
if (this->subject.networks) { if (this->subject.networks) {
for (unsigned int i = 0; i < this->subject.networkCount; ++i) { 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; 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.updateURLCount > 0) {
if (this->subject.updateURLs) { if (this->subject.updateURLs) {
for (unsigned int i = 0; i < this->subject.updateURLCount; ++i) { 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; 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; return ZT_CERTIFICATE_ERROR_INVALID_FORMAT;
} }
if ((this->extendedAttributesSize > 0) && (! this->extendedAttributes)) { if ((this->extendedAttributesSize > 0) && (!this->extendedAttributes)) {
return ZT_CERTIFICATE_ERROR_INVALID_FORMAT; return ZT_CERTIFICATE_ERROR_INVALID_FORMAT;
} }
if (this->signatureSize > sizeof(this->signature)) { if (this->signatureSize > sizeof(this->signature)) {
@ -432,58 +447,92 @@ ZT_CertificateError Certificate::verify(const int64_t clock, const bool checkSig
} }
if (checkSignatures) { 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; Dictionary d;
Vector<uint8_t> enc; Vector<uint8_t> enc;
// Check subject signature to verify that CRL was intact and the // If the issuer is all 1's (0xffffff...) then this cert is self-signed.
// public key was the one intended by the CRL creator. bool selfSigned = true;
if (this->publicKeySize > 0) { for (unsigned int i = 0; i < ZT_CERTIFICATE_HASH_SIZE; ++i) {
d.clear(); if (this->issuer[i] != 0xff) {
m_encodeSubject(this->subject, d, false); selfSigned = false;
d.encode(enc); break;
}
}
switch (this->publicKey[0]) { if (!selfSigned) {
case ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384: // Regular certs have an issuer signature and a self-signature
if ((this->publicKeySize == (ZT_ECC384_PUBLIC_KEY_SIZE + 1)) && (this->subjectSignatureSize == ZT_ECC384_SIGNATURE_SIZE)) { // of their subject to ensure CSR integrity.
uint8_t h[ZT_SHA384_DIGEST_SIZE];
SHA384(h, enc.data(), (unsigned int)enc.size()); if (this->issuerPublicKeySize > 0) {
if (! ECC384ECDSAVerify(this->publicKey + 1, h, this->subjectSignature)) { 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; return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE;
} }
} break;
else { default: return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE;
return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE; }
} }
break; else {
default: return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE;
return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE;
} }
} }
else { 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 // 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 > 0) {
if (this->subject.uniqueIdSize <= (unsigned int)sizeof(this->subject.uniqueId)) { if (this->subject.uniqueIdSize <= (unsigned int)sizeof(this->subject.uniqueId)) {
switch (this->subject.uniqueId[0]) { switch (this->subject.uniqueId[0]) {
case ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_NONE: case ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_NONE: break;
break;
case ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384: 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(); d.clear();
m_encodeSubject(this->subject, d, true); m_encodeSubject(this->subject, d, true);
d.encode(enc); 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]; uint8_t h[ZT_SHA384_DIGEST_SIZE];
SHA384(h, enc.data(), (unsigned int)enc.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; 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; return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF;
} }
break; break;
default: default: return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF;
return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF;
} }
} }
else { else {
@ -523,7 +574,7 @@ ZT_CertificateError Certificate::verify(const int64_t clock, const bool checkSig
} }
if (clock >= 0) { if (clock >= 0) {
if (! this->verifyTimeWindow(clock)) if (!this->verifyTimeWindow(clock))
return ZT_CERTIFICATE_ERROR_OUT_OF_VALID_TIME_WINDOW; 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; 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) { switch (type) {
case ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384: case ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384:
publicKey[0] = (uint8_t)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::ECC384GenerateKey(publicKey + 1, privateKey + ZT_ECC384_PUBLIC_KEY_SIZE + 1);
ZeroTier::Utils::copy<ZT_ECC384_PUBLIC_KEY_SIZE + 1>(privateKey, publicKey); 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; *privateKeySize = 1 + ZT_ECC384_PUBLIC_KEY_SIZE + ZT_ECC384_PRIVATE_KEY_SIZE;
return true; return true;
default: default: break;
break;
} }
return false; 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; Vector<uint8_t> enc;
ZT_Certificate_Subject sc; ZT_Certificate_Subject sc;
Utils::copy<sizeof(ZT_Certificate_Subject)>(&sc, &s); Utils::copy<sizeof(ZT_Certificate_Subject)>(&sc, &s);
if ((! certificatePrivateKey) || (certificatePrivateKeySize != (1 + ZT_ECC384_PUBLIC_KEY_SIZE + ZT_ECC384_PRIVATE_KEY_SIZE)) if ((!certificatePrivateKey)
|| (reinterpret_cast<const uint8_t*>(certificatePrivateKey)[0] != ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384)) || (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; return enc;
if (m_setSubjectUniqueId(sc, uniqueIdPrivate, uniqueIdPrivateSize)) { 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]; uint8_t subjectHash[ZT_SHA384_DIGEST_SIZE], subjectSig[ZT_ECC384_SIGNATURE_SIZE];
SHA384(subjectHash, enc.data(), (unsigned int)enc.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.add("sS", subjectSig, ZT_ECC384_SIGNATURE_SIZE);
d.encode(enc); d.encode(enc);
@ -581,7 +639,7 @@ Vector<uint8_t> Certificate::createCSR(const ZT_Certificate_Subject& s, const vo
void Certificate::m_clear() void Certificate::m_clear()
{ {
ZT_Certificate* const sup = this; ZT_Certificate *const sup = this;
Utils::zero<sizeof(ZT_Certificate)>(sup); Utils::zero<sizeof(ZT_Certificate)>(sup);
m_identities.clear(); m_identities.clear();
@ -594,11 +652,14 @@ void Certificate::m_clear()
m_extendedAttributes.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 (uniqueIdPrivateSize > 0) {
if ((uniqueIdPrivate != nullptr) && (uniqueIdPrivateSize == (1 + ZT_ECC384_PUBLIC_KEY_SIZE + ZT_ECC384_PRIVATE_KEY_SIZE)) if ((uniqueIdPrivate != nullptr)
&& (reinterpret_cast<const uint8_t*>(uniqueIdPrivate)[0] == (uint8_t)ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384)) { && (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); Utils::copy<1 + ZT_ECC384_PUBLIC_KEY_SIZE>(s.uniqueId, uniqueIdPrivate);
s.uniqueIdSize = 1 + ZT_ECC384_PUBLIC_KEY_SIZE; // private is prefixed with public 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]; uint8_t h[ZT_SHA384_DIGEST_SIZE];
SHA384(h, enc.data(), (unsigned int)enc.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; s.uniqueIdSignatureSize = ZT_ECC384_SIGNATURE_SIZE;
} }
else { else {
@ -626,7 +689,7 @@ bool Certificate::m_setSubjectUniqueId(ZT_Certificate_Subject& s, const void* un
return true; 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]; 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); d.add("s.i$", (uint64_t)s.identityCount);
for (unsigned int i = 0; i < s.identityCount; ++i) { for (unsigned int i = 0; i < s.identityCount; ++i) {
if (s.identities[i].identity) 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) 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) if (s.uniqueIdSize > 0)
d["s.uI"].assign(s.uniqueId, s.uniqueId + s.uniqueIdSize); d["s.uI"].assign(s.uniqueId, s.uniqueId + s.uniqueIdSize);
if ((! omitUniqueIdProofSignature) && (s.uniqueIdSignatureSize > 0)) if ((!omitUniqueIdProofSignature) && (s.uniqueIdSignatureSize > 0))
d["s.uS"].assign(s.uniqueIdSignature, s.uniqueIdSignature + s.uniqueIdSignatureSize); d["s.uS"].assign(s.uniqueIdSignature, s.uniqueIdSignature + s.uniqueIdSignatureSize);
} }

View file

@ -61,11 +61,26 @@ class Certificate : public ZT_Certificate {
return *this; return *this;
} }
/**
* @return Serial number in a H384 object
*/
ZT_INLINE H384 getSerialNo() const noexcept ZT_INLINE H384 getSerialNo() const noexcept
{ {
return H384(this->serialNo); 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 * 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 ZT_INLINE identityV0ProofOfWorkCriteria(unsigned char* restrict sb, char* restrict gm) noexcept
: digest(sb) : digest(sb)
, genmem(gm) , 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; Vector<Entry*> visited;
visited.reserve(8); visited.reserve(8);
for (Map<H384, SharedPtr<Entry> >::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->m_error == ZT_CERTIFICATE_ERROR_NONE) && (! c->second->m_onTrustPath) && ((c->second->m_localTrust & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA) == 0)) { if (c->second->m_error == ZT_CERTIFICATE_ERROR_NONE) {
// Trace the path of each certificate all the way back to a trusted CA. if (c->second->m_certificate.isSelfSigned()) {
unsigned int pathLength = 0; // If this is a self-signed certificate it's only valid if it's trusted as a CA.
Map<H384, SharedPtr<Entry> >::const_iterator current(c); if ((c->second->m_localTrust & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA) == 0) {
visited.clear(); c->second->m_error = ZT_CERTIFICATE_ERROR_INVALID_CHAIN;
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 else {
// toward CA. if ((! c->second->m_onTrustPath) && ((c->second->m_localTrust & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA) == 0)) {
if (((current->second->m_localTrust & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA) == 0) && (! current->second->m_onTrustPath)) { // Trace the path of each certificate all the way back to a trusted CA.
// If the issuer (parent) certificiate is (1) valid, (2) not already visited (to prevent unsigned int pathLength = 0;
// loops), and (3) has a public key that matches this cert's issuer public key (sanity Map<H384, SharedPtr<Entry> >::const_iterator current(c);
// check), proceed up the certificate graph toward a potential CA. visited.clear();
visited.push_back(current->second.ptr()); for (;;) {
const Map<H384, SharedPtr<Entry> >::const_iterator prevChild(current); if (pathLength <= current->second->m_certificate.maxPathLength) {
current = m_bySerial.find(H384(current->second->m_certificate.issuer)); // Check if this cert isn't a CA or already part of a valid trust path. If so then step upward
if ((current != m_bySerial.end()) && (std::find(visited.begin(), visited.end(), current->second.ptr()) == visited.end()) && (current->second->m_error == ZT_CERTIFICATE_ERROR_NONE) // toward CA.
&& (current->second->m_certificate.publicKeySize == prevChild->second->m_certificate.issuerPublicKeySize) if (((current->second->m_localTrust & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA) == 0) && (! current->second->m_onTrustPath)) {
&& (memcmp(current->second->m_certificate.publicKey, prevChild->second->m_certificate.issuerPublicKey, current->second->m_certificate.publicKeySize) == 0)) { // If the issuer (parent) certificiate is (1) valid, (2) not already visited (to prevent
++pathLength; // loops), and (3) has a public key that matches this cert's issuer public key (sanity
continue; // 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);
else { current = m_bySerial.find(H384(current->second->m_certificate.issuer));
// If we've traced this to a root CA, flag its parents as also being on a trust path. Then if ((current != m_bySerial.end()) && (std::find(visited.begin(), visited.end(), current->second.ptr()) == visited.end()) && (current->second->m_error == ZT_CERTIFICATE_ERROR_NONE)
// break the loop without setting an error. We don't flag the current cert as being on a && (current->second->m_certificate.publicKeySize == prevChild->second->m_certificate.issuerPublicKeySize)
// trust path since no other certificates depend on it. && (memcmp(current->second->m_certificate.publicKey, prevChild->second->m_certificate.issuerPublicKey, current->second->m_certificate.publicKeySize) == 0)) {
for (Vector<Entry*>::const_iterator v(visited.begin()); v != visited.end(); ++v) { ++pathLength;
if (*v != c->second.ptr()) continue;
(*v)->m_onTrustPath = true; }
}
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; 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 #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 * Compute 32-bit FNV-1a checksum
* *

View file

@ -657,6 +657,11 @@ typedef struct {
/** /**
* Issuer certificate serial number. * 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]; uint8_t issuer[ZT_CERTIFICATE_HASH_SIZE];
@ -673,7 +678,7 @@ typedef struct {
/** /**
* Signature of subject with public key. * 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. * not modified in transit or by the signer.
*/ */
uint8_t subjectSignature[ZT_CERTIFICATE_MAX_SIGNATURE_SIZE]; uint8_t subjectSignature[ZT_CERTIFICATE_MAX_SIGNATURE_SIZE];

View file

@ -55,50 +55,6 @@ clock_serv_t OSUtils::s_machMonotonicClock = _machGetMonotonicClock();
#endif #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> OSUtils::listDirectory(const char* path, bool includeDirectories)
{ {
Vector<String> r; Vector<String> r;

View file

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

View file

@ -98,6 +98,25 @@ impl CertificateSerialNo {
pub fn is_nil(&self) -> bool { pub fn is_nil(&self) -> bool {
is_all_zeroes(self.0) 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 { 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(); let mut cert: Vec<u8> = Vec::new();
cert.resize(16384, 0); cert.resize(16384, 0);
let mut cert_size: c_int = 16384; let mut cert_size: c_int = 16384;
@ -682,7 +701,7 @@ impl Certificate {
} }
} }
cert.resize(cert_size as usize, 0); cert.resize(cert_size as usize, 0);
return Ok(cert.into_boxed_slice()); return Ok(cert);
} }
/// Sign this certificate, returning new signed certificate. /// 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); 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). /// Maximum UDP MTU (we never actually get this high).
pub const MAX_UDP_MTU: u32 = ztcore::ZT_MAX_UDP_MTU; 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 { pub fn base64_encode<T: AsRef<[u8]>>(t: &T) -> String {
base64::encode_config(t, base64::URL_SAFE_NO_PAD) 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> { pub fn base64_decode<T: AsRef<[u8]>>(t: &T) -> Result<Vec<u8>, base64::DecodeError> {
base64::decode_config(t, base64::URL_SAFE_NO_PAD) 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()); println!("ERROR: unable to read subject unique ID secret file: {}", b.err().unwrap().to_string());
return 1; return 1;
} }
let privk_hex = String::from_utf8(b.unwrap()); let privk_encoded = String::from_utf8(b.unwrap());
if privk_hex.is_err() { if privk_encoded.is_err() {
println!("ERROR: invalid UTF-8 in secret"); println!("ERROR: invalid UTF-8 in secret");
return 1; 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() { if privk.is_err() || privk.as_ref().unwrap().is_empty() {
println!("ERROR: invalid unique ID secret: {}", privk.err().unwrap().to_string()); println!("ERROR: invalid unique ID secret: {}", privk.err().unwrap().to_string());
return 1; return 1;
@ -416,7 +417,7 @@ fn newcsr(cli_args: &ArgMatches) -> i32 {
1 1
}, |_| { }, |_| {
let secret_path = cli_args.value_of("secretpath").unwrap(); 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); let _ = std::fs::remove_file(csr_path);
println!("ERROR: unable to write secret: {}", e.to_string()); println!("ERROR: unable to write secret: {}", e.to_string());
1 1
@ -429,8 +430,98 @@ fn newcsr(cli_args: &ArgMatches) -> i32 {
} }
fn sign<'a>(store: &Arc<Store>, cli_args: &ArgMatches<'a>) -> 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 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), ("delete", Some(sub_cli_args)) => delete(&store, sub_cli_args),
("factoryreset", None) => factoryreset(&store), ("factoryreset", None) => factoryreset(&store),
_ => { _ => {
crate::print_help(); crate::print_help(true);
1 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), ("sign", Some(sub_cli_args)) => sign(sub_cli_args),
("verify", Some(sub_cli_args)) => verify(sub_cli_args), ("verify", Some(sub_cli_args)) => verify(sub_cli_args),
_ => { _ => {
crate::print_help(); crate::print_help(true);
1 1
} }
} }

View file

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

View file

@ -16,7 +16,8 @@ mod commands;
mod fastudpsocket; mod fastudpsocket;
mod localconfig; mod localconfig;
mod getifaddrs; mod getifaddrs;
#[macro_use] mod log; #[macro_use]
mod log;
mod store; mod store;
mod network; mod network;
mod vnic; mod vnic;
@ -38,7 +39,7 @@ use crate::store::Store;
pub const HTTP_API_OBJECT_SIZE_LIMIT: usize = 131072; 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(); let ver = zerotier_core::version();
format!(r###"ZeroTier Network Hypervisor Service Version {}.{}.{} format!(r###"ZeroTier Network Hypervisor Service Version {}.{}.{}
(c)2013-2021 ZeroTier, Inc. (c)2013-2021 ZeroTier, Inc.
@ -56,6 +57,7 @@ Global Options:
Common Operations: Common Operations:
help Show this help help Show this help
longhelp Show help with advanced commands
oldhelp Show v1.x legacy commands oldhelp Show v1.x legacy commands
version Print version (of this binary) version Print version (of this binary)
@ -85,10 +87,11 @@ Common Operations:
· globalroutes <boolean> Can global IP routes be set? · globalroutes <boolean> Can global IP routes be set?
· defaultroute <boolean> Can default route be overridden? · defaultroute <boolean> Can default route be overridden?
· join [-...] <network> Join a virtual network · join <network> Join a virtual network
-c <?identity | fingerprint> Controller identity / fingerprint
· leave <network> Leave a virtual network · leave <network> Leave a virtual network
{}"###,
ver.0, ver.1, ver.2, if long_help {
r###"
Advanced Operations: Advanced Operations:
service Start node service Start node
@ -120,12 +123,27 @@ Advanced Operations:
· list List certificates at local node · list List certificates at local node
show <@cert|·serial> Show certificate details show <@cert|·serial> Show certificate details
newsuid [@secret out] Create a subject unique ID secret newsuid [@secret out] Create a subject unique ID secret
newcsr <@csr> <@secret out> Create a CSR (interactive) newcsr <@csr out> <@secret out> Create a CSR (interactive)
sign <@csr> <@secret> <@cert out> Sign a CSR to create a certificate 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 verify <@cert> Internally verify certificate
· import <@cert> [trust,trust,...] Import certificate into this node · import <@cert> [trust,trust,...] Import certificate into this node
trust flag: rootca Root (or self-signed) CA ca Trust: root CA or self-signed
trust flag: config Can influence node configuration config Trust: node configuration
· export <serial> [@cert] Export a certificate from this node · export <serial> [@cert] Export a certificate from this node
· delete <serial|ALL> Delete certificate from this node · delete <serial|ALL> Delete certificate from this node
· factoryreset Re-import compiled-in default certs · factoryreset Re-import compiled-in default certs
@ -133,11 +151,12 @@ Advanced Operations:
· Command (or command with argument type) requires a running node. · Command (or command with argument type) requires a running node.
@ Argument is the path to a file containing the object. @ Argument is the path to a file containing the object.
? Argument can be either the object or a path to it (auto-detected). ? 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() { pub(crate) fn print_help(long_help: bool) {
let h = make_help(); let h = make_help(long_help);
let _ = std::io::stdout().write_all(h.as_bytes()); 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)) Err(format!("invalid boolean value: '{}'", v))
} }
#[inline(always)]
fn is_valid_bool(v: String) -> Result<(), String> { fn is_valid_bool(v: String) -> Result<(), String> {
parse_bool(v.as_str()).map(|_| ()) parse_bool(v.as_str()).map(|_| ())
} }
@ -188,7 +208,7 @@ fn get_global_flags(cli_args: &ArgMatches) -> GlobalFlags {
fn main() { fn main() {
let cli_args = { let cli_args = {
let help = make_help(); let help = make_help(false);
let args = App::new("zerotier") let args = App::new("zerotier")
.arg(Arg::with_name("json").short("j")) .arg(Arg::with_name("json").short("j"))
.arg(Arg::with_name("path").short("p").takes_value(true)) .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("setting").index(2).required(false))
.arg(Arg::with_name("value").index(3).required(false)))) .arg(Arg::with_name("value").index(3).required(false))))
.subcommand(App::new("join") .subcommand(App::new("join")
.arg(Arg::with_name("controller").short("c").takes_value(true))
.arg(Arg::with_name("nwid").index(1).required(true))) .arg(Arg::with_name("nwid").index(1).required(true)))
.subcommand(App::new("leave") .subcommand(App::new("leave")
.arg(Arg::with_name("nwid").index(1).required(true))) .arg(Arg::with_name("nwid").index(1).required(true)))
@ -282,8 +301,13 @@ fn main() {
.arg(Arg::with_name("secretpath").index(2).required(true))) .arg(Arg::with_name("secretpath").index(2).required(true)))
.subcommand(App::new("sign") .subcommand(App::new("sign")
.arg(Arg::with_name("csr").index(1).required(true)) .arg(Arg::with_name("csr").index(1).required(true))
.arg(Arg::with_name("secretpath").index(2).required(true)) .arg(Arg::with_name("certout").index(2).required(true))
.arg(Arg::with_name("output").index(3).required(false))) .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") .subcommand(App::new("verify")
.arg(Arg::with_name("cert").index(1).required(true))) .arg(Arg::with_name("cert").index(1).required(true)))
.subcommand(App::new("dump") .subcommand(App::new("dump")
@ -302,13 +326,13 @@ fn main() {
if args.is_err() { if args.is_err() {
let e = args.err().unwrap(); let e = args.err().unwrap();
if e.kind != ErrorKind::HelpDisplayed { if e.kind != ErrorKind::HelpDisplayed {
print_help(); print_help(false);
} }
std::process::exit(1); std::process::exit(1);
} }
let args = args.unwrap(); let args = args.unwrap();
if args.subcommand_name().is_none() { if args.subcommand_name().is_none() {
print_help(); print_help(false);
std::process::exit(1); std::process::exit(1);
} }
args args
@ -316,36 +340,40 @@ fn main() {
std::process::exit({ std::process::exit({
match cli_args.subcommand() { match cli_args.subcommand() {
("help", _) => { ("help", None) => {
print_help(); print_help(false);
0 0
} }
("oldhelp", _) => { ("longhelp", None) => {
print_help(true);
0
}
("oldhelp", None) => {
// TODO // TODO
0 0
} }
("version", _) => { ("version", None) => {
let ver = zerotier_core::version(); let ver = zerotier_core::version();
println!("{}.{}.{}", ver.0, ver.1, ver.2); println!("{}.{}.{}", ver.0, ver.1, ver.2);
0 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 } ("set", Some(sub_cli_args)) => { 0 }
("peer", Some(sub_cli_args)) => { 0 } ("peer", Some(sub_cli_args)) => { 0 }
("network", Some(sub_cli_args)) => { 0 } ("network", Some(sub_cli_args)) => { 0 }
("join", Some(sub_cli_args)) => { 0 } ("join", Some(sub_cli_args)) => { 0 }
("leave", Some(sub_cli_args)) => { 0 } ("leave", Some(sub_cli_args)) => { 0 }
("service", _) => { ("service", None) => {
let store = make_store(&cli_args); let store = make_store(&cli_args);
drop(cli_args); // free no longer needed memory before entering service drop(cli_args); // free no longer needed memory before entering service
service::run(store) service::run(store)
}, }
("controller", Some(sub_cli_args)) => { 0 } ("controller", Some(sub_cli_args)) => { 0 }
("identity", Some(sub_cli_args)) => crate::commands::identity::run(sub_cli_args), ("identity", Some(sub_cli_args)) => crate::commands::identity::run(sub_cli_args),
("locator", Some(sub_cli_args)) => crate::commands::locator::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), ("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 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, /// 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. /// the client and server may check the age of a digest auth exchange.
pub(crate) fn create_http_auth_nonce(timestamp: i64) -> String { 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 { unsafe {
osdep::encryptHttpAuthNonce(nonce_plaintext.as_mut_ptr().cast()); osdep::encryptHttpAuthNonce(nonce_plaintext.as_mut_ptr().cast());
hex::encode(*nonce_plaintext.as_ptr().cast::<[u8; 16]>()) 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. /// This returns zero if the input was not valid.
pub(crate) fn decrypt_http_auth_nonce(nonce: &str) -> i64 { pub(crate) fn decrypt_http_auth_nonce(nonce: &str) -> i64 {
let nonce = hex::decode(nonce.trim()); let nonce = hex::decode(nonce.trim());
if nonce.is_err() { if !nonce.is_err() {
return 0; let mut nonce = nonce.unwrap();
} if nonce.len() == 16 {
let mut nonce = nonce.unwrap(); unsafe {
if nonce.len() != 16 { osdep::decryptHttpAuthNonce(nonce.as_mut_ptr().cast());
return 0; let nonce = *nonce.as_ptr().cast::<[u64; 2]>();
} if nonce[0] == nonce[1] {
unsafe { return nonce[0] as i64;
osdep::decryptHttpAuthNonce(nonce.as_mut_ptr().cast()); }
let nonce = *nonce.as_ptr().cast::<[u64; 2]>(); }
nonce[0] as i64 }
} }
return 0;
} }
/// Shortcut to use serde_json to serialize an object, returns "null" on error. /// 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)] #[allow(unused_imports)]
use zerotier_core::{MAC, MulticastGroup}; use zerotier_core::{MAC, MulticastGroup};
@ -21,8 +21,8 @@ use num_traits::AsPrimitive;
/// BSD based OSes support getifmaddrs(). /// 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"))] #[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> { pub(crate) fn get_l2_multicast_subscriptions(dev: &str) -> HashSet<MulticastGroup> {
let mut groups: BTreeSet<MulticastGroup> = BTreeSet::new(); let mut groups: HashSet<MulticastGroup> = HashSet::new();
let dev = dev.as_bytes(); let dev = dev.as_bytes();
unsafe { unsafe {
let mut maddrs: *mut osdep::ifmaddrs = std::ptr::null_mut(); 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. /// Linux stores this stuff in /proc and it needs to be fetched from there.
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub(crate) fn get_l2_multicast_subscriptions(dev: &str) -> BTreeSet<MulticastGroup> { pub(crate) fn get_l2_multicast_subscriptions(dev: &str) -> HashSet<MulticastGroup> {
let mut groups: BTreeSet<MulticastGroup> = BTreeSet::new(); let mut groups: HashSet<MulticastGroup> = HashSet::new();
groups groups
} }

View file

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