mirror of
https://github.com/savoirfairelinux/opendht.git
synced 2026-11-08 04:21:51 +00:00
865 lines
27 KiB
C++
865 lines
27 KiB
C++
// Copyright (c) 2014-2026 Savoir-faire Linux Inc.
|
|
// SPDX-License-Identifier: MIT
|
|
#pragma once
|
|
|
|
#include "infohash.h"
|
|
#include "utils.h"
|
|
#include "rng.h"
|
|
|
|
extern "C" {
|
|
#include <gnutls/gnutls.h>
|
|
#include <gnutls/abstract.h>
|
|
#include <gnutls/x509.h>
|
|
#include <gnutls/ocsp.h>
|
|
}
|
|
|
|
#include <vector>
|
|
#include <memory>
|
|
#include <atomic>
|
|
#include <mutex>
|
|
#include <string_view>
|
|
|
|
#ifdef _WIN32
|
|
#include <iso646.h>
|
|
#endif
|
|
|
|
namespace dht {
|
|
|
|
/**
|
|
* Contains all crypto primitives
|
|
*/
|
|
namespace crypto {
|
|
|
|
class OPENDHT_PUBLIC CryptoException : public std::runtime_error
|
|
{
|
|
public:
|
|
explicit CryptoException(const std::string& str)
|
|
: std::runtime_error(str) {};
|
|
explicit CryptoException(const char* str)
|
|
: std::runtime_error(str) {};
|
|
CryptoException(const CryptoException& e) noexcept = default;
|
|
CryptoException& operator=(const CryptoException&) noexcept = default;
|
|
};
|
|
|
|
/**
|
|
* Exception thrown when a decryption error happened.
|
|
*/
|
|
class OPENDHT_PUBLIC DecryptError : public CryptoException
|
|
{
|
|
public:
|
|
explicit DecryptError(const std::string& str)
|
|
: CryptoException(str) {};
|
|
explicit DecryptError(const char* str)
|
|
: CryptoException(str) {};
|
|
DecryptError(const DecryptError& e) noexcept = default;
|
|
DecryptError& operator=(const DecryptError&) noexcept = default;
|
|
};
|
|
|
|
struct PrivateKey;
|
|
struct Certificate;
|
|
class RevocationList;
|
|
|
|
using Identity = std::pair<std::shared_ptr<PrivateKey>, std::shared_ptr<Certificate>>;
|
|
|
|
/**
|
|
* A public key.
|
|
*/
|
|
struct OPENDHT_PUBLIC PublicKey
|
|
{
|
|
PublicKey();
|
|
|
|
/**
|
|
* Takes ownership of an existing gnutls_pubkey.
|
|
*/
|
|
PublicKey(gnutls_pubkey_t k)
|
|
: pk(k)
|
|
{}
|
|
|
|
/** Import public key from serialized data */
|
|
PublicKey(const uint8_t* dat, size_t dat_size);
|
|
PublicKey(const Blob& pk)
|
|
: PublicKey(pk.data(), pk.size())
|
|
{}
|
|
PublicKey(std::string_view pk)
|
|
: PublicKey((const uint8_t*) pk.data(), pk.size())
|
|
{}
|
|
PublicKey(PublicKey&& o) noexcept
|
|
: pk(o.pk)
|
|
{
|
|
o.pk = nullptr;
|
|
}
|
|
|
|
~PublicKey();
|
|
explicit operator bool() const { return pk; }
|
|
bool operator==(const PublicKey& o) const { return pk == o.pk || getLongId() == o.getLongId(); }
|
|
bool operator!=(const PublicKey& o) const { return !(*this == o); }
|
|
|
|
PublicKey& operator=(PublicKey&& o) noexcept;
|
|
|
|
/**
|
|
* Get public key fingerprint
|
|
*/
|
|
const InfoHash& getId() const;
|
|
|
|
/**
|
|
* Get public key long fingerprint
|
|
*/
|
|
const PkId& getLongId() const;
|
|
|
|
bool checkSignature(const uint8_t* data, size_t data_len, const uint8_t* signature, size_t signature_len) const;
|
|
inline bool checkSignature(const Blob& data, const Blob& signature) const
|
|
{
|
|
return checkSignature(data.data(), data.size(), signature.data(), signature.size());
|
|
}
|
|
|
|
Blob encrypt(const uint8_t* data, size_t data_len) const;
|
|
inline Blob encrypt(const Blob& data) const { return encrypt(data.data(), data.size()); }
|
|
inline Blob encrypt(std::string_view data) const { return encrypt((const uint8_t*) data.data(), data.size()); }
|
|
void pack(Blob& b) const;
|
|
int pack(uint8_t* out, size_t* out_len) const;
|
|
void unpack(const uint8_t* dat, size_t dat_size);
|
|
|
|
std::string toString() const;
|
|
|
|
template<typename Packer>
|
|
void msgpack_pack(Packer& p) const
|
|
{
|
|
Blob b;
|
|
pack(b);
|
|
p.pack_bin(b.size());
|
|
p.pack_bin_body((const char*) b.data(), b.size());
|
|
}
|
|
|
|
void msgpack_unpack(const msgpack::object& o);
|
|
|
|
gnutls_digest_algorithm_t getPreferredDigest() const;
|
|
|
|
gnutls_pubkey_t pk {nullptr};
|
|
|
|
private:
|
|
mutable InfoHash cachedId_ {};
|
|
mutable PkId cachedLongId_ {};
|
|
mutable std::atomic_bool idCached_ {false};
|
|
mutable std::atomic_bool longIdCached_ {false};
|
|
|
|
PublicKey(const PublicKey&) = delete;
|
|
PublicKey& operator=(const PublicKey&) = delete;
|
|
void encryptBloc(const uint8_t* src, size_t src_size, uint8_t* dst, size_t dst_size) const;
|
|
};
|
|
|
|
/**
|
|
* A private key, including the corresponding public key.
|
|
*/
|
|
struct OPENDHT_PUBLIC PrivateKey
|
|
{
|
|
PrivateKey();
|
|
// PrivateKey(gnutls_privkey_t k) : key(k) {}
|
|
|
|
/**
|
|
* Takes ownership of an existing gnutls_x509_privkey.
|
|
*/
|
|
PrivateKey(gnutls_x509_privkey_t k);
|
|
|
|
PrivateKey(PrivateKey&& o) noexcept;
|
|
PrivateKey& operator=(PrivateKey&& o) noexcept;
|
|
|
|
PrivateKey(const uint8_t* src, size_t src_size, const char* password = nullptr);
|
|
PrivateKey(const Blob& src, const std::string& password = {})
|
|
: PrivateKey(src.data(), src.size(), password.c_str())
|
|
{}
|
|
PrivateKey(std::string_view src, const std::string& password = {})
|
|
: PrivateKey((const uint8_t*) src.data(), src.size(), password.c_str())
|
|
{}
|
|
|
|
~PrivateKey();
|
|
explicit operator bool() const { return key; }
|
|
|
|
const PublicKey& getPublicKey() const;
|
|
const std::shared_ptr<PublicKey>& getSharedPublicKey() const;
|
|
|
|
int serialize(uint8_t* out, size_t* out_len, const std::string& password = {}) const;
|
|
Blob serialize(const std::string& password = {}) const;
|
|
|
|
/**
|
|
* Sign the provided binary object.
|
|
* @returns the signature data.
|
|
*/
|
|
Blob sign(const uint8_t* data, size_t data_len) const;
|
|
inline Blob sign(std::string_view dat) const { return sign((const uint8_t*) dat.data(), dat.size()); }
|
|
inline Blob sign(const Blob& dat) const { return sign(dat.data(), dat.size()); }
|
|
|
|
/**
|
|
* Try to decrypt the provided cypher text.
|
|
* In case of failure a CryptoException is thrown.
|
|
* @returns the decrypted data.
|
|
*/
|
|
Blob decrypt(const uint8_t* cypher, size_t cypher_len) const;
|
|
Blob decrypt(const Blob& cypher) const { return decrypt(cypher.data(), cypher.size()); }
|
|
|
|
/**
|
|
* Generate a new RSA key pair
|
|
* @param key_length : size of the modulus in bits
|
|
* Minimim value: 2048
|
|
* Recommended values: 4096, 8192
|
|
* @param algo : the public key algorithm to use, GNUTLS_PK_RSA or GNUTLS_PK_RSA_OAEP
|
|
*/
|
|
static PrivateKey generate(unsigned key_length = 4096, gnutls_pk_algorithm_t algo = GNUTLS_PK_RSA);
|
|
static PrivateKey generateEC();
|
|
|
|
gnutls_privkey_t key {};
|
|
gnutls_x509_privkey_t x509_key {};
|
|
|
|
private:
|
|
PrivateKey(const PrivateKey&) = delete;
|
|
PrivateKey& operator=(const PrivateKey&) = delete;
|
|
Blob decryptBloc(const uint8_t* src, size_t src_size) const;
|
|
|
|
mutable std::mutex publicKeyMutex_ {};
|
|
mutable std::shared_ptr<PublicKey> publicKey_ {};
|
|
};
|
|
|
|
class OPENDHT_PUBLIC RevocationList
|
|
{
|
|
using clock = std::chrono::system_clock;
|
|
using time_point = clock::time_point;
|
|
using duration = clock::duration;
|
|
|
|
public:
|
|
RevocationList();
|
|
RevocationList(const Blob& b);
|
|
RevocationList(RevocationList&& o) noexcept
|
|
: crl(o.crl)
|
|
{
|
|
o.crl = nullptr;
|
|
}
|
|
~RevocationList();
|
|
|
|
RevocationList& operator=(RevocationList&& o)
|
|
{
|
|
crl = o.crl;
|
|
o.crl = nullptr;
|
|
return *this;
|
|
}
|
|
|
|
void pack(Blob& b) const;
|
|
void unpack(const uint8_t* dat, size_t dat_size);
|
|
Blob getPacked() const
|
|
{
|
|
Blob b;
|
|
pack(b);
|
|
return b;
|
|
}
|
|
|
|
template<typename Packer>
|
|
void msgpack_pack(Packer& p) const
|
|
{
|
|
Blob b = getPacked();
|
|
p.pack_bin(b.size());
|
|
p.pack_bin_body((const char*) b.data(), b.size());
|
|
}
|
|
|
|
void msgpack_unpack(const msgpack::object& o);
|
|
|
|
void revoke(const Certificate& crt, time_point t = time_point::min());
|
|
|
|
bool isRevoked(const Certificate& crt) const;
|
|
|
|
/**
|
|
* Sign this revocation list using provided key and certificate.
|
|
* Validity_period sets the duration until next update (default to no next update).
|
|
*/
|
|
void sign(const PrivateKey&, const Certificate&, duration validity_period = {});
|
|
void sign(const Identity& id) { sign(*id.first, *id.second); }
|
|
|
|
bool isSignedBy(const Certificate& issuer) const;
|
|
|
|
std::string toString() const;
|
|
|
|
/**
|
|
* Read the CRL number extension field.
|
|
*/
|
|
Blob getNumber() const;
|
|
|
|
/** Read CRL issuer Common Name (CN) */
|
|
std::string getIssuerName() const;
|
|
|
|
/** Read CRL issuer User ID (UID) */
|
|
std::string getIssuerUID() const;
|
|
|
|
time_point getUpdateTime() const;
|
|
time_point getNextUpdateTime() const;
|
|
|
|
gnutls_x509_crl_t get() { return crl; }
|
|
gnutls_x509_crl_t getCopy() const
|
|
{
|
|
if (not crl)
|
|
return nullptr;
|
|
auto copy = RevocationList(getPacked());
|
|
gnutls_x509_crl_t ret = copy.crl;
|
|
copy.crl = nullptr;
|
|
return ret;
|
|
}
|
|
|
|
private:
|
|
gnutls_x509_crl_t crl {};
|
|
RevocationList(const RevocationList&) = delete;
|
|
RevocationList& operator=(const RevocationList&) = delete;
|
|
};
|
|
|
|
enum class NameType { UNKNOWN = 0, RFC822, DNS, URI, IP };
|
|
|
|
class OPENDHT_PUBLIC CertificateRequest
|
|
{
|
|
public:
|
|
CertificateRequest();
|
|
CertificateRequest(const uint8_t* data, size_t size);
|
|
CertificateRequest(std::string_view src)
|
|
: CertificateRequest((const uint8_t*) src.data(), src.size())
|
|
{}
|
|
CertificateRequest(const Blob& data)
|
|
: CertificateRequest(data.data(), data.size())
|
|
{}
|
|
|
|
CertificateRequest(CertificateRequest&& o) noexcept
|
|
: request(std::move(o.request))
|
|
{
|
|
o.request = nullptr;
|
|
}
|
|
CertificateRequest& operator=(CertificateRequest&& o) noexcept;
|
|
|
|
~CertificateRequest();
|
|
|
|
void setName(const std::string& name);
|
|
void setUID(const std::string& name);
|
|
void setAltName(NameType type, const std::string& name);
|
|
|
|
std::string getName() const;
|
|
std::string getUID() const;
|
|
|
|
void sign(const PrivateKey& key, const std::string& password = {});
|
|
|
|
bool verify() const;
|
|
|
|
Blob pack() const;
|
|
std::string toString() const;
|
|
|
|
gnutls_x509_crq_t get() const { return request; }
|
|
|
|
private:
|
|
CertificateRequest(const CertificateRequest& o) = delete;
|
|
CertificateRequest& operator=(const CertificateRequest& o) = delete;
|
|
gnutls_x509_crq_t request {nullptr};
|
|
};
|
|
|
|
class OPENDHT_PUBLIC OcspRequest
|
|
{
|
|
public:
|
|
OcspRequest(gnutls_ocsp_req_t r)
|
|
: request(r)
|
|
{}
|
|
OcspRequest(const uint8_t* dat_ptr, size_t dat_size);
|
|
OcspRequest(std::string_view dat)
|
|
: OcspRequest((const uint8_t*) dat.data(), dat.size())
|
|
{}
|
|
~OcspRequest();
|
|
|
|
/*
|
|
* Get OCSP Request in readable format.
|
|
*/
|
|
std::string toString(const bool compact = true) const;
|
|
|
|
Blob pack() const;
|
|
Blob getNonce() const;
|
|
|
|
private:
|
|
gnutls_ocsp_req_t request;
|
|
};
|
|
|
|
class OPENDHT_PUBLIC OcspResponse
|
|
{
|
|
public:
|
|
OcspResponse(const uint8_t* dat_ptr, size_t dat_size);
|
|
OcspResponse(std::string_view response)
|
|
: OcspResponse((const uint8_t*) response.data(), response.size())
|
|
{}
|
|
~OcspResponse();
|
|
|
|
Blob pack() const;
|
|
/*
|
|
* Get OCSP Response in readable format.
|
|
*/
|
|
std::string toString(const bool compact = true) const;
|
|
|
|
/*
|
|
* Get OCSP response certificate status.
|
|
* Return certificate status.
|
|
* http://www.gnu.org/software/gnutls/reference/gnutls-ocsp.html#gnutls-ocsp-cert-status-t
|
|
*/
|
|
gnutls_ocsp_cert_status_t getCertificateStatus() const;
|
|
|
|
/*
|
|
* Verify OCSP response and return OCSP status.
|
|
* Throws CryptoException in case of error in the response.
|
|
* http://www.gnu.org/software/gnutls/reference/gnutls-ocsp.html#gnutls-ocsp-verify-reason-t
|
|
*/
|
|
gnutls_ocsp_cert_status_t verifyDirect(const Certificate& crt, const Blob& nonce);
|
|
|
|
private:
|
|
gnutls_ocsp_resp_t response;
|
|
};
|
|
|
|
struct OPENDHT_PUBLIC Certificate
|
|
{
|
|
Certificate() noexcept {}
|
|
|
|
/**
|
|
* Take ownership of existing gnutls structure
|
|
*/
|
|
Certificate(gnutls_x509_crt_t crt) noexcept
|
|
: cert(crt)
|
|
{}
|
|
|
|
Certificate(Certificate&& o) noexcept
|
|
: cert(o.cert)
|
|
, issuer(std::move(o.issuer))
|
|
, publicKey_(std::move(o.publicKey_))
|
|
{
|
|
o.cert = nullptr;
|
|
};
|
|
|
|
/**
|
|
* Import certificate (PEM or DER) or certificate chain (PEM),
|
|
* ordered from subject to issuer
|
|
*/
|
|
Certificate(const Blob& crt);
|
|
Certificate(const uint8_t* dat, size_t dat_size)
|
|
: cert(nullptr)
|
|
{
|
|
unpack(dat, dat_size);
|
|
}
|
|
Certificate(std::string_view pem)
|
|
: Certificate((const uint8_t*) pem.data(), pem.size())
|
|
{}
|
|
|
|
/**
|
|
* Import certificate chain (PEM or DER),
|
|
* ordered from subject to issuer
|
|
*/
|
|
template<typename Iterator>
|
|
Certificate(const Iterator& begin, const Iterator& end)
|
|
{
|
|
unpack(begin, end);
|
|
}
|
|
|
|
/**
|
|
* Import certificate chain (PEM or DER),
|
|
* ordered from subject to issuer
|
|
*/
|
|
template<typename Iterator>
|
|
Certificate(const std::vector<std::pair<Iterator, Iterator>>& certs)
|
|
{
|
|
unpack(certs);
|
|
}
|
|
|
|
Certificate& operator=(Certificate&& o) noexcept;
|
|
~Certificate();
|
|
|
|
void pack(Blob& b) const;
|
|
void unpack(const uint8_t* dat, size_t dat_size);
|
|
Blob getPacked() const
|
|
{
|
|
Blob b;
|
|
pack(b);
|
|
return b;
|
|
}
|
|
|
|
/**
|
|
* Import certificate chain (PEM or DER).
|
|
* Certificates are not checked during import.
|
|
*
|
|
* Iterator is the type of an iterator or pointer to
|
|
* gnutls_x509_crt_t or Blob instances to import, that should be
|
|
* ordered from subject to issuer.
|
|
*/
|
|
template<typename Iterator>
|
|
void unpack(const Iterator& begin, const Iterator& end)
|
|
{
|
|
std::shared_ptr<Certificate> tmp_subject {};
|
|
std::shared_ptr<Certificate> first {};
|
|
for (Iterator icrt = begin; icrt < end; ++icrt) {
|
|
auto tmp_crt = std::make_shared<Certificate>(*icrt);
|
|
if (tmp_subject)
|
|
tmp_subject->issuer = tmp_crt;
|
|
tmp_subject = std::move(tmp_crt);
|
|
if (!first)
|
|
first = tmp_subject;
|
|
}
|
|
*this = first ? std::move(*first) : Certificate();
|
|
}
|
|
|
|
/**
|
|
* Import certificate chain (PEM or DER).
|
|
* Certificates are not checked during import.
|
|
*
|
|
* Iterator is the type of an iterator or pointer to the bytes of
|
|
* the certificates to import.
|
|
*
|
|
* @param certs list of (begin, end) iterator pairs, pointing to the
|
|
* PEM or DER certificate data to import, that should be
|
|
* ordered from subject to issuer.
|
|
*/
|
|
template<typename Iterator>
|
|
void unpack(const std::vector<std::pair<Iterator, Iterator>>& certs)
|
|
{
|
|
std::shared_ptr<Certificate> tmp_issuer;
|
|
// reverse iteration
|
|
for (auto li = certs.rbegin(); li != certs.rend(); ++li) {
|
|
Certificate tmp_crt;
|
|
gnutls_x509_crt_init(&tmp_crt.cert);
|
|
const gnutls_datum_t crt_dt {(uint8_t*) &(*li->first), (unsigned) (li->second - li->first)};
|
|
int err = gnutls_x509_crt_import(tmp_crt.cert, &crt_dt, GNUTLS_X509_FMT_PEM);
|
|
if (err != GNUTLS_E_SUCCESS)
|
|
err = gnutls_x509_crt_import(tmp_crt.cert, &crt_dt, GNUTLS_X509_FMT_DER);
|
|
if (err != GNUTLS_E_SUCCESS)
|
|
throw CryptoException(std::string("Could not read certificate - ") + gnutls_strerror(err));
|
|
tmp_crt.issuer = tmp_issuer;
|
|
tmp_issuer = std::make_shared<Certificate>(std::move(tmp_crt));
|
|
}
|
|
*this = tmp_issuer ? std::move(*tmp_issuer) : Certificate();
|
|
}
|
|
|
|
template<typename Packer>
|
|
void msgpack_pack(Packer& p) const
|
|
{
|
|
Blob b;
|
|
pack(b);
|
|
p.pack_bin(b.size());
|
|
p.pack_bin_body((const char*) b.data(), b.size());
|
|
}
|
|
|
|
void msgpack_unpack(const msgpack::object& o);
|
|
|
|
explicit operator bool() const { return cert; }
|
|
const PublicKey& getPublicKey() const;
|
|
const std::shared_ptr<PublicKey>& getSharedPublicKey() const;
|
|
|
|
/** Same as getPublicKey().getId() */
|
|
const InfoHash& getId() const;
|
|
/** Same as getPublicKey().getLongId() */
|
|
const PkId& getLongId() const;
|
|
|
|
Blob getSerialNumber() const;
|
|
|
|
/** Read certificate full DN as described in RFC4514 */
|
|
std::string getDN() const;
|
|
|
|
/** Read certificate Common Name (CN) */
|
|
std::string getName() const;
|
|
|
|
/** Read certificate User ID (UID) */
|
|
std::string getUID() const;
|
|
|
|
/** Read certificate issuer DN as described in RFC4514 */
|
|
std::string getIssuerDN() const;
|
|
|
|
/** Read certificate issuer Common Name (CN) */
|
|
std::string getIssuerName() const;
|
|
|
|
/** Read certificate issuer User ID (UID) */
|
|
std::string getIssuerUID() const;
|
|
|
|
/** Read certificate alternative names */
|
|
std::vector<std::pair<NameType, std::string>> getAltNames() const;
|
|
|
|
std::chrono::system_clock::time_point getActivation() const;
|
|
std::chrono::system_clock::time_point getExpiration() const;
|
|
|
|
/**
|
|
* Returns true if the certificate is marked as a Certificate Authority
|
|
* and has necessary key usage flags to sign certificates.
|
|
*/
|
|
bool isCA() const;
|
|
|
|
/**
|
|
* PEM encoded certificate.
|
|
* If chain is true, the issuer chain will be included (default).
|
|
*/
|
|
std::string toString(bool chain = true) const;
|
|
|
|
std::string print() const;
|
|
|
|
/**
|
|
* As a CA, revoke a certificate, adding it to
|
|
* the attached Certificate Revocation List (CRL)
|
|
*/
|
|
void revoke(const PrivateKey&, const Certificate&);
|
|
|
|
/**
|
|
* Get the list of certificates revoked as as CA.
|
|
*/
|
|
std::vector<std::shared_ptr<RevocationList>> getRevocationLists() const;
|
|
|
|
/**
|
|
* Attach existing revocation list.
|
|
*/
|
|
void addRevocationList(RevocationList&&);
|
|
void addRevocationList(std::shared_ptr<RevocationList>);
|
|
|
|
static Certificate generate(const PrivateKey& key,
|
|
const std::string& name = "dhtnode",
|
|
const Identity& ca = {},
|
|
bool is_ca = false,
|
|
int64_t validity = 0);
|
|
static Certificate generate(const CertificateRequest& request, const Identity& ca, int64_t validity = 0);
|
|
|
|
gnutls_x509_crt_t getCopy() const
|
|
{
|
|
if (not cert)
|
|
return nullptr;
|
|
auto copy = Certificate(getPacked());
|
|
gnutls_x509_crt_t ret = copy.cert;
|
|
copy.cert = nullptr;
|
|
return ret;
|
|
}
|
|
|
|
std::vector<gnutls_x509_crt_t> getChain(bool copy = false) const
|
|
{
|
|
if (not cert)
|
|
return {};
|
|
std::vector<gnutls_x509_crt_t> crts;
|
|
for (auto c = this; c; c = c->issuer.get())
|
|
crts.emplace_back(copy ? c->getCopy() : c->cert);
|
|
return crts;
|
|
}
|
|
|
|
std::pair<std::vector<gnutls_x509_crt_t>, std::vector<gnutls_x509_crl_t>> getChainWithRevocations(
|
|
bool copy = false) const
|
|
{
|
|
if (not cert)
|
|
return {};
|
|
std::vector<gnutls_x509_crt_t> crts;
|
|
std::vector<gnutls_x509_crl_t> crls;
|
|
for (auto c = this; c; c = c->issuer.get()) {
|
|
crts.emplace_back(copy ? c->getCopy() : c->cert);
|
|
crls.reserve(crls.size() + c->revocation_lists.size());
|
|
for (const auto& crl : c->revocation_lists)
|
|
crls.emplace_back(copy ? crl->getCopy() : crl->get());
|
|
}
|
|
return {crts, crls};
|
|
}
|
|
|
|
gnutls_digest_algorithm_t getPreferredDigest() const;
|
|
|
|
/*
|
|
* Generate OCSP request.
|
|
* Return GnuTLS error code.
|
|
* https://www.gnutls.org/manual/html_node/Error-codes.html
|
|
*/
|
|
std::pair<std::string, Blob> generateOcspRequest(gnutls_x509_crt_t& issuer);
|
|
|
|
/**
|
|
* Change certificate's expiration
|
|
*/
|
|
void setValidity(const Identity& ca, int64_t validity);
|
|
void setValidity(const PrivateKey& key, int64_t validity);
|
|
|
|
gnutls_x509_crt_t cert {nullptr};
|
|
std::shared_ptr<Certificate> issuer {};
|
|
std::shared_ptr<OcspResponse> ocspResponse;
|
|
|
|
private:
|
|
Certificate(const Certificate&) = delete;
|
|
Certificate& operator=(const Certificate&) = delete;
|
|
mutable InfoHash cachedId_ {};
|
|
mutable PkId cachedLongId_ {};
|
|
mutable std::atomic_bool idCached_ {false};
|
|
mutable std::atomic_bool longIdCached_ {false};
|
|
|
|
struct crlNumberCmp
|
|
{
|
|
bool operator()(const std::shared_ptr<RevocationList>& lhs, const std::shared_ptr<RevocationList>& rhs) const
|
|
{
|
|
return lhs->getNumber() < rhs->getNumber();
|
|
}
|
|
};
|
|
|
|
std::set<std::shared_ptr<RevocationList>, crlNumberCmp> revocation_lists;
|
|
|
|
mutable std::mutex publicKeyMutex_ {};
|
|
mutable std::shared_ptr<PublicKey> publicKey_ {};
|
|
};
|
|
|
|
struct OPENDHT_PUBLIC TrustList
|
|
{
|
|
struct VerifyResult
|
|
{
|
|
int ret;
|
|
unsigned result;
|
|
bool hasError() const { return ret < 0; }
|
|
bool isValid() const { return !hasError() and !(result & GNUTLS_CERT_INVALID); }
|
|
explicit operator bool() const { return isValid(); }
|
|
OPENDHT_PUBLIC std::string toString() const;
|
|
OPENDHT_PUBLIC friend std::ostream& operator<<(std::ostream& s, const VerifyResult& h);
|
|
};
|
|
|
|
TrustList();
|
|
TrustList(TrustList&& o) noexcept
|
|
: trust(std::move(o.trust))
|
|
{
|
|
o.trust = nullptr;
|
|
}
|
|
TrustList& operator=(TrustList&& o) noexcept;
|
|
~TrustList();
|
|
void add(const Certificate& crt);
|
|
void add(const RevocationList& crl);
|
|
void remove(const Certificate& crt, bool parents = true);
|
|
VerifyResult verify(const Certificate& crt) const;
|
|
|
|
private:
|
|
TrustList(const TrustList& o) = delete;
|
|
TrustList& operator=(const TrustList& o) = delete;
|
|
gnutls_x509_trust_list_t trust {nullptr};
|
|
};
|
|
|
|
/**
|
|
* Generate an RSA key pair (4096 bits) and a certificate.
|
|
* @param name the name used in the generated certificate
|
|
* @param ca if set, the certificate authority that will sign the generated certificate.
|
|
* If not set, the generated certificate will be a self-signed CA.
|
|
* @param key_length stength of the generated private key (bits).
|
|
*/
|
|
OPENDHT_PUBLIC Identity generateIdentity(const std::string& name, const Identity& ca, unsigned key_length, bool is_ca);
|
|
OPENDHT_PUBLIC Identity generateIdentity(const std::string& name = "dhtnode",
|
|
const Identity& ca = {},
|
|
unsigned key_length = 4096);
|
|
|
|
OPENDHT_PUBLIC Identity generateEcIdentity(const std::string& name, const Identity& ca, bool is_ca);
|
|
OPENDHT_PUBLIC Identity generateEcIdentity(const std::string& name = "dhtnode", const Identity& ca = {});
|
|
|
|
OPENDHT_PUBLIC void saveIdentity(const Identity& id, const std::string& path, const std::string& privkey_password = {});
|
|
OPENDHT_PUBLIC Identity loadIdentity(const std::string& path, const std::string& privkey_password = {});
|
|
|
|
/**
|
|
* Performs SHA512, SHA256 or SHA1, depending on hash_length.
|
|
* Attempts to choose an hash function with
|
|
* output size of at least hash_length bytes, Current implementation
|
|
* will use SHA1 for hash_length up to 20 bytes,
|
|
* will use SHA256 for hash_length up to 32 bytes,
|
|
* will use SHA512 for hash_length of 33 bytes and more.
|
|
*/
|
|
OPENDHT_PUBLIC Blob hash(const Blob& data, size_t hash_length = 512 / 8);
|
|
|
|
OPENDHT_PUBLIC void hash(const uint8_t* data, size_t data_length, uint8_t* hash, size_t hash_length);
|
|
|
|
/**
|
|
* Generates an encryption key from a text password,
|
|
* making the key longer to bruteforce.
|
|
* The generated key also depends on a unique salt value of any size,
|
|
* that can be transmitted in clear, and will be generated if
|
|
* not provided (32 bytes).
|
|
*/
|
|
OPENDHT_PUBLIC Blob stretchKey(std::string_view password, Blob& salt, size_t key_length = 512 / 8);
|
|
|
|
/**
|
|
* AES-GCM encryption. Key must be 128, 192 or 256 bits long (16, 24 or 32 bytes).
|
|
*/
|
|
OPENDHT_PUBLIC Blob aesEncrypt(const uint8_t* data, size_t data_length, const Blob& key);
|
|
OPENDHT_PUBLIC inline Blob
|
|
aesEncrypt(const Blob& data, const Blob& key)
|
|
{
|
|
return aesEncrypt(data.data(), data.size(), key);
|
|
}
|
|
/**
|
|
* AES-GCM encryption with argon2 key derivation.
|
|
* This function uses `stretchKey` to generate an AES key from the password and a random salt.
|
|
* The result is a bundle including the salt that can be decrypted with `aesDecrypt(data, password)`.
|
|
* If needed, the salt or encrypted data can be individually extracted from the bundle with `aesGetSalt` and
|
|
* `aesGetEncrypted`.
|
|
* @param data: data to encrypt
|
|
* @param password: password to encrypt the data with
|
|
* @param salt: optional salt to use for key derivation. If not provided, a random salt will be generated.
|
|
*/
|
|
OPENDHT_PUBLIC Blob aesEncrypt(const Blob& data, std::string_view password, const Blob& salt = {});
|
|
|
|
/**
|
|
* AES-GCM decryption.
|
|
*/
|
|
OPENDHT_PUBLIC Blob aesDecrypt(const uint8_t* data, size_t data_length, const Blob& key);
|
|
OPENDHT_PUBLIC inline Blob
|
|
aesDecrypt(const Blob& data, const Blob& key)
|
|
{
|
|
return aesDecrypt(data.data(), data.size(), key);
|
|
}
|
|
OPENDHT_PUBLIC inline Blob
|
|
aesDecrypt(std::string_view data, const Blob& key)
|
|
{
|
|
return aesDecrypt((uint8_t*) data.data(), data.size(), key);
|
|
}
|
|
|
|
OPENDHT_PUBLIC Blob aesDecrypt(const uint8_t* data, size_t data_length, std::string_view password);
|
|
OPENDHT_PUBLIC inline Blob
|
|
aesDecrypt(const Blob& data, std::string_view password)
|
|
{
|
|
return aesDecrypt(data.data(), data.size(), password);
|
|
}
|
|
OPENDHT_PUBLIC inline Blob
|
|
aesDecrypt(std::string_view data, std::string_view password)
|
|
{
|
|
return aesDecrypt((uint8_t*) data.data(), data.size(), password);
|
|
}
|
|
|
|
/**
|
|
* Get raw AES key from password and salt stored with the encrypted data.
|
|
*/
|
|
OPENDHT_PUBLIC Blob aesGetKey(const uint8_t* data, size_t data_length, std::string_view password);
|
|
OPENDHT_PUBLIC Blob inline aesGetKey(const Blob& data, std::string_view password)
|
|
{
|
|
return aesGetKey(data.data(), data.size(), password);
|
|
}
|
|
/** Get the salt part of data password-encrypted with `aesEncrypt(data, password)` */
|
|
OPENDHT_PUBLIC Blob aesGetSalt(const uint8_t* data, size_t data_length);
|
|
OPENDHT_PUBLIC Blob inline aesGetSalt(const Blob& data)
|
|
{
|
|
return aesGetSalt(data.data(), data.size());
|
|
}
|
|
/** Get the encrypted data (ciphertext) part of data password-encrypted with `aesEncrypt(data, password)` */
|
|
OPENDHT_PUBLIC std::string_view aesGetEncrypted(const uint8_t* data, size_t data_length);
|
|
OPENDHT_PUBLIC std::string_view inline aesGetEncrypted(const Blob& data)
|
|
{
|
|
return aesGetEncrypted(data.data(), data.size());
|
|
}
|
|
|
|
/** Build an encrypted bundle that can be decrypted with aesDecrypt(data, password).
|
|
* @param encryptedData: result of `aesEncrypt(data, key)` or `aesGetEncrypted`
|
|
* @param salt: should match the encryption key and password so that `stretchKey(password, salk) == key`.
|
|
* Can be obtained from an existing bundle with `aesGetSalt`.
|
|
**/
|
|
OPENDHT_PUBLIC Blob aesBuildEncrypted(const uint8_t* encryptedData, size_t data_length, const Blob& salt);
|
|
OPENDHT_PUBLIC Blob inline aesBuildEncrypted(const Blob& encryptedData, const Blob& salt)
|
|
{
|
|
return aesBuildEncrypted(encryptedData.data(), encryptedData.size(), salt);
|
|
}
|
|
OPENDHT_PUBLIC Blob inline aesBuildEncrypted(std::string_view encryptedData, const Blob& salt)
|
|
{
|
|
return aesBuildEncrypted((const uint8_t*) encryptedData.data(), encryptedData.size(), salt);
|
|
}
|
|
|
|
/**
|
|
* Encrypts a payload for WebPush (RFC 8291).
|
|
* @param p256dh: The user's P-256 public key (uncompressed, 65 bytes).
|
|
* @param auth: The user's authentication secret (16 bytes).
|
|
* @param payload: The data to encrypt.
|
|
* @param privKey: (Optional) The sender's private key (for testing).
|
|
* @param salt: (Optional) The salt to use (for testing).
|
|
* @return The encrypted body (including salt, record size, keyid).
|
|
*/
|
|
OPENDHT_PUBLIC Blob webPushEncrypt(const Blob& p256dh,
|
|
const Blob& auth,
|
|
const uint8_t* payload,
|
|
size_t payload_length,
|
|
const PrivateKey& privKey = {},
|
|
const Blob& salt = {});
|
|
|
|
} // namespace crypto
|
|
} // namespace dht
|