ZeroTierOne/core/Certificate.hpp
2021-07-14 15:38:37 -04:00

234 lines
8.9 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 "C25519.hpp"
#include "Constants.hpp"
#include "Containers.hpp"
#include "Dictionary.hpp"
#include "ECC384.hpp"
#include "Identity.hpp"
#include "Locator.hpp"
#include "SHA512.hpp"
#include "Utils.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;
}
/**
* @return Serial number in a H384 object
*/
ZT_INLINE H384 getSerialNo() const noexcept { return H384(this->serialNo); }
/**
* @return True if this is a self-signed certificate
*/
ZT_INLINE bool isSelfSigned() const noexcept
{
for (unsigned int i = 0; i < ZT_CERTIFICATE_HASH_SIZE; ++i) {
if (this->issuer[i] != 0xff)
return false;
}
return true;
}
/**
* Add a subject node/identity without a locator
*
* @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 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 cannot check the chain of trust back to a CA, only the internal validity
* of this certificate.
*
* @param clock If non-negative, also do verifyTimeWindow()
* @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;
/**
* Check this certificate's expiration status
*
* @param clock Current real world time in milliseconds since epoch
* @return True if certificate is not expired or outside window
*/
ZT_INLINE bool verifyTimeWindow(int64_t clock) const noexcept { return ((clock >= this->validity[0]) && (clock <= this->validity[1]) && (this->validity[0] <= this->validity[1])); }
/**
* 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 certificatePrivateKey Private key for certificate (includes public)
* @param certificatePrivateKeySize Size of private
* @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 *certificatePrivateKey, unsigned int certificatePrivateKeySize, 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;
// 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 char *> m_updateUrls;
Vector<uint8_t> m_extendedAttributes;
};
} // namespace ZeroTier
#endif