mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-26 17:03:43 +02:00
264 lines
9 KiB
C++
264 lines
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
|