ZeroTierOne/core/Certificate.hpp

230 lines
8 KiB
C++

/*
* Copyright (c)2013-2021 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2026-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
#ifndef ZT_CERTIFICATE_HPP
#define ZT_CERTIFICATE_HPP
#include "Constants.hpp"
#include "SHA512.hpp"
#include "C25519.hpp"
#include "ECC384.hpp"
#include "Identity.hpp"
#include "Locator.hpp"
#include "Dictionary.hpp"
#include "Utils.hpp"
#include "Containers.hpp"
namespace ZeroTier {
/**
* Certificate describing and grouping a set of objects.
*
* This is a wrapper around the straight C ZT_IdentificationCertificate and
* handles allocating memory for objects added via addXXX() and disposing of
* them on delete. If pointers in the underlying C struct are set manually,
* their memory is not freed on delete. Use the addXXX() methods to fill
* out this structure in C++ code.
*
* The serialNo field is filled in automatically by sign() and decode(), so
* it can be left undefined when building certificates. It contains a SHA384
* hash of the certificate marshalled without the signature field.
*
* The hashCode() method and comparison operators compare the serial number
* field, so these will not work correctly before sign() or decode() is
* called.
*/
class Certificate : public ZT_Certificate
{
public:
Certificate() noexcept;
explicit Certificate(const ZT_Certificate &apiCert);
Certificate(const Certificate &cert);
~Certificate();
Certificate &operator=(const ZT_Certificate &cert);
ZT_INLINE Certificate &operator=(const Certificate &cert) noexcept
{
if (likely(&cert != this)) {
const ZT_Certificate *const sup = &cert;
*this = *sup;
}
return *this;
}
ZT_INLINE H384 getSerialNo() const noexcept
{ return H384(this->serialNo); }
/**
* Add a subject node/identity without a locator
*
* @param id Identity
* @return Pointer to C struct
*/
ZT_Certificate_Identity *addSubjectIdentity(const Identity &id);
/**
* Add a subject node/identity with a locator
*
* @param id Identity
* @param loc Locator signed by identity (signature is NOT checked here)
* @return Pointer to C struct
*/
ZT_Certificate_Identity *addSubjectIdentity(const Identity &id, const Locator &loc);
/**
* Add a subject network
*
* @param id Network ID
* @param controller Network controller's full fingerprint
* @return Pointer to C struct
*/
ZT_Certificate_Network *addSubjectNetwork(uint64_t id, const ZT_Fingerprint &controller);
/**
* Add a subject certificate (by its serial number)
*
* @param serialNo 384-bit serial number
*/
void addSubjectCertificate(const uint8_t serialNo[ZT_SHA384_DIGEST_SIZE]);
/**
* Add an update URL to the updateUrls list
*
* @param url Update URL
*/
void addSubjectUpdateUrl(const char *url);
/**
* Sign subject with unique ID private key and set.
*
* This is done when you createCSR but can also be done explicitly here. This
* is mostly for testing purposes.
*
* @param uniqueIdPrivate Unique ID private key (includes public)
* @param uniqueIdPrivateSize Size of private key
* @return True on success
*/
ZT_INLINE bool setSubjectUniqueId(const void *uniqueIdPrivate, unsigned int uniqueIdPrivateSize)
{ return m_setSubjectUniqueId(this->subject, uniqueIdPrivate, uniqueIdPrivateSize); }
/**
* Marshal this certificate in binary form
*
* The internal encoding used here is Dictionary to permit easy
* extensibility.
*
* @param omitSignature If true omit the signature field (for signing and verification, default is false)
* @return Marshaled certificate
*/
Vector< uint8_t > encode(bool omitSignature = false) const;
/**
* 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 void *data, unsigned int len);
/**
* Sign this certificate.
*
* This sets serialNo, issuer, issuerPublicKey, and signature.
*
* @return True on success
*/
bool sign(const uint8_t issuer[ZT_CERTIFICATE_HASH_SIZE], const void *issuerPrivateKey, unsigned int issuerPrivateKeySize);
/**
* Verify self-contained signatures and validity of certificate structure
*
* This doesn't check the entire certificate chain, just the validity of
* the certificate's internal signature and fields.
*
* @param clock If non-negative, check that certificate is in valid time window
* @param checkSignatures If true, perform full signature check (which is more expensive than other checks)
* @return OK (0) or error code indicating why certificate failed verification.
*/
ZT_CertificateError verify(int64_t clock, bool checkSignatures) const;
/**
* Create a new certificate public/private key pair
*
* @param type Key pair type to create
* @param publicKey Buffer to fill with public key
* @param publicKeySize Result parameter: set to size of public key
* @param privateKey Buffer to fill with private key
* @param privateKeySize Result parameter: set to size of private key
* @return True on success
*/
static bool newKeyPair(const ZT_CertificatePublicKeyAlgorithm type, uint8_t publicKey[ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE], int *const publicKeySize, uint8_t privateKey[ZT_CERTIFICATE_MAX_PRIVATE_KEY_SIZE], int *const privateKeySize);
/**
* Create a CSR that encodes the subject of this certificate
*
* @param s Subject to encode
* @param certificatePublicKey Public key for certificate
* @param certificatePublicKeySize Size of public key
* @param uniqueIdPrivate Unique ID private key for proof signature or NULL if none
* @param uniqueIdPrivateSize Size of unique ID private key
* @return Encoded subject (without any unique ID fields) or empty vector on error
*/
static Vector< uint8_t > createCSR(const ZT_Certificate_Subject &s, const void *certificatePublicKey, unsigned int certificatePublicKeySize, const void *uniqueIdPrivate, unsigned int uniqueIdPrivateSize);
ZT_INLINE unsigned long hashCode() const noexcept
{ return (unsigned long)Utils::loadMachineEndian< uint32_t >(this->serialNo); }
ZT_INLINE bool operator==(const ZT_Certificate &c) const noexcept
{ return memcmp(this->serialNo, c.serialNo, ZT_SHA384_DIGEST_SIZE) == 0; }
ZT_INLINE bool operator!=(const ZT_Certificate &c) const noexcept
{ return memcmp(this->serialNo, c.serialNo, ZT_SHA384_DIGEST_SIZE) != 0; }
ZT_INLINE bool operator<(const ZT_Certificate &c) const noexcept
{ return memcmp(this->serialNo, c.serialNo, ZT_SHA384_DIGEST_SIZE) < 0; }
ZT_INLINE bool operator<=(const ZT_Certificate &c) const noexcept
{ return memcmp(this->serialNo, c.serialNo, ZT_SHA384_DIGEST_SIZE) <= 0; }
ZT_INLINE bool operator>(const ZT_Certificate &c) const noexcept
{ return memcmp(this->serialNo, c.serialNo, ZT_SHA384_DIGEST_SIZE) > 0; }
ZT_INLINE bool operator>=(const ZT_Certificate &c) const noexcept
{ return memcmp(this->serialNo, c.serialNo, ZT_SHA384_DIGEST_SIZE) >= 0; }
private:
void m_clear();
static bool m_setSubjectUniqueId(ZT_Certificate_Subject &s, const void *uniqueIdPrivate, unsigned int uniqueIdPrivateSize);
static void m_encodeSubject(const ZT_Certificate_Subject &s, Dictionary &d, bool omitUniqueIdProofSignature);
// These hold any identity or locator objects that are owned by and should
// be deleted with this certificate. Lists are used so the pointers never
// change.
ForwardList< Identity > m_identities;
ForwardList< Locator > m_locators;
ForwardList< String > m_strings;
ForwardList< H384 > m_serials;
// These are stored in a vector because the memory needs to be contiguous.
Vector< ZT_Certificate_Identity > m_subjectIdentities;
Vector< ZT_Certificate_Network > m_subjectNetworks;
Vector< const uint8_t * > m_subjectCertificates;
Vector< const char * > m_updateUrls;
Vector< uint8_t > m_extendedAttributes;
};
} // namespace ZeroTier
#endif