From 899f0c9749a8ae4f4a6359c1c0c5911dc89ba77b Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 2 Jul 2020 22:22:31 -0700 Subject: [PATCH] Certificate API --- cmd/zerotier/cli/help.go | 4 +- core/Certificate.cpp | 125 +++++++++++++++++++++++++++++++++++--- core/Certificate.hpp | 16 +++-- core/Tests.cpp | 6 +- core/Topology.cpp | 3 +- core/zerotier.h | 126 +++++++++++++++++++++++++++++++++++---- serviceiocore/GoGlue.cpp | 9 +-- 7 files changed, 254 insertions(+), 35 deletions(-) diff --git a/cmd/zerotier/cli/help.go b/cmd/zerotier/cli/help.go index 147aa46fb..38cbf6dfb 100644 --- a/cmd/zerotier/cli/help.go +++ b/cmd/zerotier/cli/help.go @@ -81,8 +81,8 @@ Commands: verify Verify a signature certificate [args] - Certificate commands newid Create a new unique subject ID - newcsr Create a new CSR (signing request) - sign Sign a CRL and create a certificate + newcsr Create a new CSR (signing request) + sign Sign a CSR to create a certificate verify Verify a certificate show List certificate for current node import [] Import certificate into this node diff --git a/core/Certificate.cpp b/core/Certificate.cpp index 9e64f417b..27648a449 100644 --- a/core/Certificate.cpp +++ b/core/Certificate.cpp @@ -251,16 +251,16 @@ Vector< uint8_t > Certificate::encode(const bool omitSignature) const return enc; } -bool Certificate::decode(const Vector< uint8_t > &data) +bool Certificate::decode(const void *const data, const unsigned int len) { char tmp[256], tmp2[ZT_CERTIFICATE_MAX_STRING_LENGTH + 1]; - m_clear(); - Dictionary d; - if (!d.decode(data.data(), (unsigned int)data.size())) + if (!d.decode(data, len)) return false; + m_clear(); + this->flags = d.getUI("f"); this->timestamp = (int64_t)d.getUI("t"); this->validity[0] = (int64_t)d.getUI("v#0"); @@ -384,6 +384,15 @@ bool Certificate::decode(const Vector< uint8_t > &data) return true; } +Vector< uint8_t > Certificate::encodeCSR() +{ + Vector< uint8_t > enc; + Dictionary d; + m_encodeSubject(this->subject, d, false); + d.encode(enc); + return enc; +} + bool Certificate::sign(const Identity &issuer) { Vector< uint8_t > enc(encode(true)); @@ -417,7 +426,7 @@ ZT_CertificateError Certificate::verify() const if ( (this->subject.uniqueIdProofSignatureSize != ZT_ECC384_SIGNATURE_SIZE) || (this->subject.uniqueIdSize != (ZT_ECC384_PUBLIC_KEY_SIZE + 1)) || - (this->subject.uniqueId[0] != ZT_CERTIFICATE_UNIQUE_ID_PUBLIC_KEY_TYPE_NIST_P_384)) + (this->subject.uniqueId[0] != ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384)) return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF; Dictionary tmp; m_encodeSubject(this->subject, tmp, true); @@ -462,11 +471,11 @@ ZT_CertificateError Certificate::verify() const return ZT_CERTIFICATE_ERROR_NONE; } -bool Certificate::setSubjectUniqueId(const uint8_t uniqueId[ZT_CERTIFICATE_UNIQUE_ID_SIZE_TYPE_NIST_P_384], const uint8_t uniqueIdPrivate[ZT_CERTIFICATE_UNIQUE_ID_PRIVATE_KEY_SIZE_TYPE_NIST_P_384]) +bool Certificate::setSubjectUniqueId(const uint8_t uniqueId[ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE], const uint8_t uniqueIdPrivate[ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE]) { - m_subjectUniqueId.assign(uniqueId, uniqueId + ZT_CERTIFICATE_UNIQUE_ID_SIZE_TYPE_NIST_P_384); + m_subjectUniqueId.assign(uniqueId, uniqueId + ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE); this->subject.uniqueId = m_subjectUniqueId.data(); - this->subject.uniqueIdSize = ZT_CERTIFICATE_UNIQUE_ID_SIZE_TYPE_NIST_P_384; + this->subject.uniqueIdSize = ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE; Dictionary d; m_encodeSubject(this->subject, d, true); @@ -567,3 +576,103 @@ void Certificate::m_encodeSubject(const ZT_Certificate_Subject &s, Dictionary &d } } // namespace ZeroTier + +extern "C" { + +int ZT_Certificate_newSubjectUniqueId( + enum ZT_CertificateUniqueIdType type, + void *uniqueId, + int *uniqueIdSize, + void *uniqueIdPrivate, + int *uniqueIdPrivateSize) +{ + switch(type) { + case ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384: + if ((*uniqueIdSize < ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE) || (*uniqueIdPrivateSize < ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE)) + return ZT_RESULT_ERROR_BAD_PARAMETER; + *uniqueIdSize = ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE; + *uniqueIdPrivateSize = ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE; + ZeroTier::Certificate::createSubjectUniqueId(reinterpret_cast(uniqueId), reinterpret_cast(uniqueIdPrivate)); + return ZT_RESULT_OK; + } + return ZT_RESULT_ERROR_BAD_PARAMETER; +} + +int ZT_Certificate_newCSR( + const ZT_Certificate_Subject *subject, + const void *uniqueId, + int uniqueIdSize, + const void *uniqueIdPrivate, + int uniqueIdPrivateSize, + void *csr, + int *csrSize) +{ + ZeroTier::Certificate c; + ZeroTier::Utils::copy< sizeof(ZT_Certificate_Subject) >(&(c.subject), subject); + if ((uniqueId) && (uniqueIdSize > 0) && (uniqueIdPrivate) && (uniqueIdPrivateSize > 0)) { + if ((reinterpret_cast(uniqueId)[0] != ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384) || (uniqueIdSize != ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE) || (uniqueIdPrivateSize != ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE)) + return ZT_RESULT_ERROR_BAD_PARAMETER; + if (!c.setSubjectUniqueId(reinterpret_cast(uniqueId), reinterpret_cast(uniqueIdPrivate))) + return ZT_RESULT_ERROR_INVALID_CREDENTIAL; + } + ZeroTier::Vector< uint8_t > csrV(c.encodeCSR()); + if ((int)csrV.size() > *csrSize) + return ZT_RESULT_ERROR_BAD_PARAMETER; + ZeroTier::Utils::copy(csr, csrV.data(), (unsigned int)csrV.size()); + *csrSize = (int)csrV.size(); + return ZT_RESULT_OK; +} + +int ZT_Certificate_sign( + const ZT_Certificate *cert, + const ZT_Identity *signer, + void *signedCert, + int *signedCertSize) +{ + ZeroTier::Certificate c(*cert); + if (!c.sign(*reinterpret_cast(signer))) + return ZT_RESULT_ERROR_BAD_PARAMETER; + ZeroTier::Vector< uint8_t > enc(c.encode()); + if ((int)enc.size() > *signedCertSize) + return ZT_RESULT_ERROR_BAD_PARAMETER; + ZeroTier::Utils::copy(signedCert, enc.data(), (unsigned int)enc.size()); + *signedCertSize = (int)enc.size(); + return ZT_RESULT_OK; +} + +enum ZT_CertificateError ZT_Certificate_decode( + ZT_Certificate **decodedCert, + const void *cert, + int certSize, + int verify) +{ + try { + if (!decodedCert) + return ZT_CERTIFICATE_ERROR_INVALID_FORMAT; + *decodedCert = nullptr; + ZeroTier::Certificate *const c = new ZeroTier::Certificate(); + if (!c->decode(cert, certSize)) { + delete c; + return ZT_CERTIFICATE_ERROR_INVALID_FORMAT; + } + if (verify) { + ZT_CertificateError err = c->verify(); + if (err != ZT_CERTIFICATE_ERROR_NONE) { + delete c; + return err; + } + } + *decodedCert = c; + return ZT_CERTIFICATE_ERROR_NONE; + } catch ( ... ) { + return ZT_CERTIFICATE_ERROR_INVALID_FORMAT; + } +} + +ZT_SDK_API void ZT_Certificate_delete(ZT_Certificate *cert) +{ + if (cert) + delete reinterpret_cast(cert); +} + +} diff --git a/core/Certificate.hpp b/core/Certificate.hpp index a77c58a8c..f30d7db36 100644 --- a/core/Certificate.hpp +++ b/core/Certificate.hpp @@ -129,9 +129,17 @@ public: * Decode this certificate from marshaled bytes. * * @param data Marshalled certificate + * @param len Length of marshalled certificate * @return True if input is valid and was unmarshalled (signature is NOT checked) */ - bool decode(const Vector< uint8_t > &data); + bool decode(const void *data, unsigned int len); + + /** + * Encode only the subject portion of this certificate as a CSR + * + * @return Encoded CSR + */ + Vector< uint8_t > encodeCSR(); /** * Sign this certificate (and also fill in serialNo). @@ -160,7 +168,7 @@ public: * @param uniqueIdPrivate Private key associated with unique ID to prove ownership of it * @return True if successful */ - bool setSubjectUniqueId(const uint8_t uniqueId[ZT_CERTIFICATE_UNIQUE_ID_SIZE_TYPE_NIST_P_384], const uint8_t uniqueIdPrivate[ZT_CERTIFICATE_UNIQUE_ID_PRIVATE_KEY_SIZE_TYPE_NIST_P_384]); + bool setSubjectUniqueId(const uint8_t uniqueId[ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE], const uint8_t uniqueIdPrivate[ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE]); /** * Create a subject unique ID and corresponding private key required for use @@ -168,9 +176,9 @@ public: * @param uniqueId Buffer to receive unique ID * @param uniqueIdPrivate Buffer to receive private key */ - static ZT_INLINE void createSubjectUniqueId(uint8_t uniqueId[ZT_CERTIFICATE_UNIQUE_ID_SIZE_TYPE_NIST_P_384], uint8_t uniqueIdPrivate[ZT_CERTIFICATE_UNIQUE_ID_PRIVATE_KEY_SIZE_TYPE_NIST_P_384]) + static ZT_INLINE void createSubjectUniqueId(uint8_t uniqueId[ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE], uint8_t uniqueIdPrivate[ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE]) { - uniqueId[0] = ZT_CERTIFICATE_UNIQUE_ID_PUBLIC_KEY_TYPE_NIST_P_384; + uniqueId[0] = ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384; ECC384GenerateKey(uniqueId + 1, uniqueIdPrivate); } diff --git a/core/Tests.cpp b/core/Tests.cpp index dfd48f22d..c9c7b1608 100644 --- a/core/Tests.cpp +++ b/core/Tests.cpp @@ -1107,9 +1107,9 @@ extern "C" const char *ZTT_crypto() ZT_T_PRINTF("OK" ZT_EOL_S); ZT_T_PRINTF(" Create subject unique ID... "); - uint8_t uniqueId[ZT_CERTIFICATE_UNIQUE_ID_SIZE_TYPE_NIST_P_384], uniqueIdPrivate[ZT_CERTIFICATE_UNIQUE_ID_PRIVATE_KEY_SIZE_TYPE_NIST_P_384]; + uint8_t uniqueId[ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE], uniqueIdPrivate[ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE]; Certificate::createSubjectUniqueId(uniqueId, uniqueIdPrivate); - Utils::b32e(uniqueId, ZT_CERTIFICATE_UNIQUE_ID_SIZE_TYPE_NIST_P_384, tmp, sizeof(tmp)); + Utils::b32e(uniqueId, ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE, tmp, sizeof(tmp)); ZT_T_PRINTF("OK %s" ZT_EOL_S, tmp); ZT_T_PRINTF(" Create and sign certificate... "); @@ -1148,7 +1148,7 @@ extern "C" const char *ZTT_crypto() ZT_T_PRINTF(" Test certificate decode from marshaled format... "); SharedPtr cert2(new Certificate()); - if (!cert2->decode(enc)) { + if (!cert2->decode(enc.data(), (unsigned int)enc.size())) { ZT_T_PRINTF("FAILED (decode)" ZT_EOL_S); return "Certificate decode"; } diff --git a/core/Topology.cpp b/core/Topology.cpp index 74e8b9fe2..44429884b 100644 --- a/core/Topology.cpp +++ b/core/Topology.cpp @@ -34,7 +34,8 @@ Topology::Topology(const RuntimeEnvironment *renv, void *tPtr, const int64_t now if (serialNo.size() == ZT_SHA384_DIGEST_SIZE) { Utils::copy< 48 >(id, serialNo.data()); Certificate cert; - if (cert.decode(RR->node->stateObjectGet(tPtr, ZT_STATE_OBJECT_CERT, id))) + Vector< uint8_t > enc(RR->node->stateObjectGet(tPtr, ZT_STATE_OBJECT_CERT, id)); + if (cert.decode(enc.data(), (unsigned int)enc.size())) addCertificate(tPtr, cert, now, (unsigned int)d.getUI(Dictionary::arraySubscript(tmp, "c$.lt", idx)), false, false, false); } } diff --git a/core/zerotier.h b/core/zerotier.h index 0358be8a0..4d018afc0 100644 --- a/core/zerotier.h +++ b/core/zerotier.h @@ -38,7 +38,7 @@ extern "C" { /* This symbol may be defined to anything we need to put in front of API function prototypes. */ #ifndef ZT_SDK_API -#define ZT_SDK_API +#define ZT_SDK_API extern #endif /* ---------------------------------------------------------------------------------------------------------------- */ @@ -308,20 +308,26 @@ typedef struct */ #define ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ZEROTIER_ROOT_SET 0x0002U -/** - * Public key type for NIST P-384 public keys used as subject unique IDs. - */ -#define ZT_CERTIFICATE_UNIQUE_ID_PUBLIC_KEY_TYPE_NIST_P_384 1 - /** * Size of a unique ID of the given key type (with type prefix byte) */ -#define ZT_CERTIFICATE_UNIQUE_ID_SIZE_TYPE_NIST_P_384 50 +#define ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE 50 /** * Size of the private key corresponding to a unique ID of the given type. */ -#define ZT_CERTIFICATE_UNIQUE_ID_PRIVATE_KEY_SIZE_TYPE_NIST_P_384 48 +#define ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE 48 + +/** + * Unique ID types supported for certificate subject unique IDs + */ +enum ZT_CertificateUniqueIdType +{ + /** + * Public key type for NIST P-384 public keys used as subject unique IDs. + */ + ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384 = 1 +}; /** * Errors returned by functions that verify or handle certificates. @@ -2620,7 +2626,10 @@ ZT_SDK_API ZT_Locator *ZT_Locator_fromString(const char *str); * @param bufSize Size of buffer in bytes (needs to be at least 2048 bytes in size) * @return Number of bytes stored to buf or -1 on error such as buffer too small */ -ZT_SDK_API int ZT_Locator_marshal(const ZT_Locator *loc,void *buf,unsigned int bufSize); +ZT_SDK_API int ZT_Locator_marshal( + const ZT_Locator *loc, + void *buf, + unsigned int bufSize); /** * Get this locator in string format @@ -2669,7 +2678,9 @@ ZT_SDK_API unsigned int ZT_Locator_endpointCount(const ZT_Locator *loc); * @param ep Endpoint number from 0 to 1 - endpointCount() * @return Endpoint or NULL if out of bounds */ -ZT_SDK_API const ZT_Endpoint *ZT_Locator_endpoint(const ZT_Locator *loc,const unsigned int ep); +ZT_SDK_API const ZT_Endpoint *ZT_Locator_endpoint( + const ZT_Locator *loc, + const unsigned int ep); /** * Verify this locator's signature @@ -2677,7 +2688,9 @@ ZT_SDK_API const ZT_Endpoint *ZT_Locator_endpoint(const ZT_Locator *loc,const un * @param signer Signing identity * @return Non-zero if locator is valid */ -ZT_SDK_API int ZT_Locator_verify(const ZT_Locator *loc,const ZT_Identity *signer); +ZT_SDK_API int ZT_Locator_verify( + const ZT_Locator *loc, + const ZT_Identity *signer); /** * Delete a locator @@ -2704,6 +2717,97 @@ ZT_SDK_API void ZT_version( /* ---------------------------------------------------------------------------------------------------------------- */ +/** + * Create a new certificate subject unique ID and private key + * + * A unique ID is really a public/private key pair. + * + * @param type Unique ID type (key pair algorithm) + * @param uniqueId Unique ID buffer + * @param uniqueIdSize Value/result: size of buffer + * @param uniqueIdPrivate Unique ID private key buffer + * @param uniqueIdPrivateSize Value/result: size of buffer + * @return OK (0) or error + */ +ZT_SDK_API int ZT_Certificate_newSubjectUniqueId( + enum ZT_CertificateUniqueIdType type, + void *uniqueId, + int *uniqueIdSize, + void *uniqueIdPrivate, + int *uniqueIdPrivateSize); + +/** + * Create a new certificate signing request (CSR) + * + * A CSR is effectively just an encoded certificate subject. + * If both uniqueId and uniqueIdPrivate are specified, the subject + * will be signed with a unique ID. Otherwise these fields are not + * set. If a unique ID and unique ID signature are present in the + * supplied subject, these will be ignored. + * + * @param subject Subject filled in with fields for CSR + * @param uniqueId Unique ID or NULL if none + * @param uniqueIdSize Size of unique ID + * @param uniqueIdPrivate Unique ID private key or NULL if none + * @param uniqueIdPrivateSize Size of unique ID private key + * @param csr Buffer to hold CSR (recommended size: 16384 bytes) + * @param csrSize Value/result: size of buffer + * @return OK (0) or error + */ +ZT_SDK_API int ZT_Certificate_newCSR( + const ZT_Certificate_Subject *subject, + const void *uniqueId, + int uniqueIdSize, + const void *uniqueIdPrivate, + int uniqueIdPrivateSize, + void *csr, + int *csrSize); + +/** + * Sign a CSR to generate a complete certificate + * + * @param cert Certificate to sign + * @param signer Signer identity (must contain secret key) + * @param signedCert Signed certificate buffer (recommended size: 16384 bytes) + * @param signedCertSize Value/result: size of buffer + * @return OK (0) or error + */ +ZT_SDK_API int ZT_Certificate_sign( + const ZT_Certificate *cert, + const ZT_Identity *signer, + void *signedCert, + int *signedCertSize); + +/** + * Decode a certificate or CSR + * + * A CSR is just the encoded subject part of a certificate. Decoding a CSR + * results in a certificate whose subject is filled in but nothing else. + * + * If no error occurs and the pointer at decodedCert is set to non-NULL, + * the returned certificate must be freed with ZT_Certificate_delete(). + * + * @param decodedCert Result parameter: target pointer is set to certificate + * @param cert Certificate or CSR data + * @param certSize Size of data + * @param verify If non-zero, verify signatures and structure + * @return Certificate error, if any + */ +ZT_SDK_API enum ZT_CertificateError ZT_Certificate_decode( + ZT_Certificate **decodedCert, + const void *cert, + int certSize, + int verify); + +/** + * Free a certificate created with ZT_Certificate_decode() + * + * @param cert Certificate to free + */ +ZT_SDK_API void ZT_Certificate_delete(ZT_Certificate *cert); + +/* ---------------------------------------------------------------------------------------------------------------- */ + #ifdef __cplusplus } #endif diff --git a/serviceiocore/GoGlue.cpp b/serviceiocore/GoGlue.cpp index 8998eadaa..328350985 100644 --- a/serviceiocore/GoGlue.cpp +++ b/serviceiocore/GoGlue.cpp @@ -11,7 +11,9 @@ */ /****/ +#ifndef _WIN32_WINNT #define _WIN32_WINNT 0x06010000 +#endif #include "GoGlue.h" @@ -22,11 +24,11 @@ #include "../core/MAC.hpp" #include "../core/Address.hpp" #include "../core/Containers.hpp" +#include "../core/Certificate.hpp" #include "../osdep/OSUtils.hpp" #include "../osdep/EthernetTap.hpp" #ifndef __WINDOWS__ - #include #include #include @@ -35,16 +37,11 @@ #include #include #include - #ifdef __BSD__ - #include - #endif - #include #include - #ifdef __LINUX__ #ifndef IPV6_DONTFRAG #define IPV6_DONTFRAG 62