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

@ -25,19 +25,11 @@ Certificate::Certificate() noexcept
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)
{ {
@ -55,7 +47,9 @@ 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));
@ -87,7 +81,8 @@ Certificate& Certificate::operator=(const ZT_Certificate& cert)
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.uniqueIdSignature, cert.subject.uniqueIdSignature);
this->subject.uniqueIdSize = cert.subject.uniqueIdSize; this->subject.uniqueIdSize = cert.subject.uniqueIdSize;
this->subject.uniqueIdSignatureSize = cert.subject.uniqueIdSignatureSize; this->subject.uniqueIdSignatureSize = cert.subject.uniqueIdSignatureSize;
@ -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)) {
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<sizeof(this->issuer)>(this->issuer, issuer);
Utils::copy<1 + ZT_ECC384_PUBLIC_KEY_SIZE>(this->issuerPublicKey, Utils::copy<1 + ZT_ECC384_PUBLIC_KEY_SIZE>(
this->issuerPublicKey,
issuerPrivateKey); // private is prefixed with public issuerPrivateKey); // private is prefixed with public
this->issuerPublicKeySize = 1 + ZT_ECC384_PUBLIC_KEY_SIZE; 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;
@ -382,10 +389,14 @@ ZT_CertificateError Certificate::verify(const int64_t clock, const bool checkSig
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;
} }
} }
@ -421,7 +432,11 @@ 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)) {
@ -432,13 +447,27 @@ 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. Dictionary d;
// Note that the serial number / SHA384 hash is computed on decode(), so Vector<uint8_t> enc;
// this value is not something we blindly trust from input.
// 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;
}
}
if (!selfSigned) {
// Regular certs have an issuer signature and a self-signature
// of their subject to ensure CSR integrity.
if (this->issuerPublicKeySize > 0) { if (this->issuerPublicKeySize > 0) {
switch (this->issuerPublicKey[0]) { switch (this->issuerPublicKey[0]) {
case ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384: 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 ((this->issuerPublicKeySize == (ZT_ECC384_PUBLIC_KEY_SIZE + 1))
&& (this->signatureSize == ZT_ECC384_SIGNATURE_SIZE)) {
if (!ECC384ECDSAVerify(this->issuerPublicKey + 1, this->serialNo, this->signature)) { if (!ECC384ECDSAVerify(this->issuerPublicKey + 1, this->serialNo, this->signature)) {
return ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE; return ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE;
} }
@ -447,19 +476,13 @@ ZT_CertificateError Certificate::verify(const int64_t clock, const bool checkSig
return ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE; return ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE;
} }
break; break;
default: default: return ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE;
return ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE;
} }
} }
else { else {
return ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE; 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) { if (this->publicKeySize > 0) {
d.clear(); d.clear();
m_encodeSubject(this->subject, d, false); m_encodeSubject(this->subject, d, false);
@ -467,7 +490,8 @@ ZT_CertificateError Certificate::verify(const int64_t clock, const bool checkSig
switch (this->publicKey[0]) { switch (this->publicKey[0]) {
case ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384: 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)) { if ((this->publicKeySize == (ZT_ECC384_PUBLIC_KEY_SIZE + 1))
&& (this->subjectSignatureSize == ZT_ECC384_SIGNATURE_SIZE)) {
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->publicKey + 1, h, this->subjectSignature)) { if (!ECC384ECDSAVerify(this->publicKey + 1, h, this->subjectSignature)) {
@ -478,13 +502,38 @@ ZT_CertificateError Certificate::verify(const int64_t clock, const bool checkSig
return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE; return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE;
} }
break; break;
default: default: return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE;
return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE;
} }
} }
else { else {
return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE; return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE;
} }
}
else {
// 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
// is present and invalid. A unique ID with type ALGORITHM_NONE is also // is present and invalid. A unique ID with type ALGORITHM_NONE is also
@ -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 {
@ -534,7 +585,9 @@ 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:
@ -544,21 +597,24 @@ bool Certificate::newKeyPair(const ZT_CertificatePublicKeyAlgorithm type, uint8_
*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,7 +624,9 @@ 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);
@ -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 {
@ -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));
} }
} }

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,7 +149,15 @@ 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) {
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. // Trace the path of each certificate all the way back to a trusted CA.
unsigned int pathLength = 0; unsigned int pathLength = 0;
Map<H384, SharedPtr<Entry> >::const_iterator current(c); Map<H384, SharedPtr<Entry> >::const_iterator current(c);
@ -192,6 +200,8 @@ bool TrustStore::update(const int64_t clock, Vector<SharedPtr<Entry> >* const pu
} }
} }
} }
}
}
// Repopulate mapping of subject unique IDs to their certificates, marking older // Repopulate mapping of subject unique IDs to their certificates, marking older
// certificates for the same subject as deprecated. A deprecated certificate is not invalid // certificates for the same subject as deprecated. A deprecated certificate is not invalid

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,19 +109,20 @@ 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(); let mut nonce = nonce.unwrap();
if nonce.len() != 16 { if nonce.len() == 16 {
return 0;
}
unsafe { unsafe {
osdep::decryptHttpAuthNonce(nonce.as_mut_ptr().cast()); osdep::decryptHttpAuthNonce(nonce.as_mut_ptr().cast());
let nonce = *nonce.as_ptr().cast::<[u64; 2]>(); let nonce = *nonce.as_ptr().cast::<[u64; 2]>();
nonce[0] as i64 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. /// Shortcut to use serde_json to serialize an object, returns "null" on error.
pub(crate) fn to_json<O: serde::Serialize>(o: &O) -> String { pub(crate) fn to_json<O: serde::Serialize>(o: &O) -> String {

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())
} }