mirror of
https://github.com/nodejs/node.git
synced 2025-08-15 13:48:44 +02:00

PR-URL: https://github.com/nodejs/node/pull/59259 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Ethan Arrowood <ethan@arrowood.dev> Reviewed-By: James M Snell <jasnell@gmail.com>
1563 lines
50 KiB
C++
1563 lines
50 KiB
C++
#pragma once
|
|
|
|
#include <openssl/bio.h>
|
|
#include <openssl/bn.h>
|
|
#include <openssl/dh.h>
|
|
#include <openssl/dsa.h>
|
|
#include <openssl/ec.h>
|
|
#include <openssl/err.h>
|
|
#include <openssl/evp.h>
|
|
#include <openssl/hmac.h>
|
|
#include <openssl/kdf.h>
|
|
#include <openssl/rsa.h>
|
|
#include <openssl/ssl.h>
|
|
#include <openssl/x509.h>
|
|
#include <cstddef>
|
|
#include <functional>
|
|
#include <list>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <string_view>
|
|
#ifndef OPENSSL_NO_ENGINE
|
|
#include <openssl/engine.h>
|
|
#endif // !OPENSSL_NO_ENGINE
|
|
// The FIPS-related functions are only available
|
|
// when the OpenSSL itself was compiled with FIPS support.
|
|
#if defined(OPENSSL_FIPS) && OPENSSL_VERSION_MAJOR < 3
|
|
#include <openssl/fips.h>
|
|
#endif // OPENSSL_FIPS
|
|
|
|
#if OPENSSL_VERSION_MAJOR >= 3
|
|
#define OSSL3_CONST const
|
|
#if OPENSSL_VERSION_MINOR >= 5
|
|
#include <openssl/core_names.h>
|
|
#endif
|
|
#else
|
|
#define OSSL3_CONST
|
|
#endif
|
|
|
|
#ifdef __GNUC__
|
|
#define NCRYPTO_MUST_USE_RESULT __attribute__((warn_unused_result))
|
|
#else
|
|
#define NCRYPTO_MUST_USE_RESULT
|
|
#endif
|
|
|
|
#ifdef OPENSSL_IS_BORINGSSL
|
|
// Boringssl has opted to use size_t for some size related
|
|
// APIs while Openssl is still using ints
|
|
using OPENSSL_SIZE_T = size_t;
|
|
#else
|
|
using OPENSSL_SIZE_T = int;
|
|
#endif
|
|
|
|
namespace ncrypto {
|
|
|
|
// ============================================================================
|
|
// Utility macros
|
|
|
|
#if NCRYPTO_DEVELOPMENT_CHECKS
|
|
#define NCRYPTO_STR(x) #x
|
|
#define NCRYPTO_REQUIRE(EXPR) \
|
|
{ \
|
|
if (!(EXPR) { abort(); }) }
|
|
|
|
#define NCRYPTO_FAIL(MESSAGE) \
|
|
do { \
|
|
std::cerr << "FAIL: " << (MESSAGE) << std::endl; \
|
|
abort(); \
|
|
} while (0);
|
|
#define NCRYPTO_ASSERT_EQUAL(LHS, RHS, MESSAGE) \
|
|
do { \
|
|
if (LHS != RHS) { \
|
|
std::cerr << "Mismatch: '" << LHS << "' - '" << RHS << "'" << std::endl; \
|
|
NCRYPTO_FAIL(MESSAGE); \
|
|
} \
|
|
} while (0);
|
|
#define NCRYPTO_ASSERT_TRUE(COND) \
|
|
do { \
|
|
if (!(COND)) { \
|
|
std::cerr << "Assert at line " << __LINE__ << " of file " << __FILE__ \
|
|
<< std::endl; \
|
|
NCRYPTO_FAIL(NCRYPTO_STR(COND)); \
|
|
} \
|
|
} while (0);
|
|
#else
|
|
#define NCRYPTO_FAIL(MESSAGE)
|
|
#define NCRYPTO_ASSERT_EQUAL(LHS, RHS, MESSAGE)
|
|
#define NCRYPTO_ASSERT_TRUE(COND)
|
|
#endif
|
|
|
|
#define NCRYPTO_DISALLOW_COPY(Name) \
|
|
Name(const Name&) = delete; \
|
|
Name& operator=(const Name&) = delete;
|
|
#define NCRYPTO_DISALLOW_MOVE(Name) \
|
|
Name(Name&&) = delete; \
|
|
Name& operator=(Name&&) = delete;
|
|
#define NCRYPTO_DISALLOW_COPY_AND_MOVE(Name) \
|
|
NCRYPTO_DISALLOW_COPY(Name) \
|
|
NCRYPTO_DISALLOW_MOVE(Name)
|
|
#define NCRYPTO_DISALLOW_NEW_DELETE() \
|
|
void* operator new(size_t) = delete; \
|
|
void operator delete(void*) = delete;
|
|
|
|
[[noreturn]] inline void unreachable() {
|
|
#ifdef __GNUC__
|
|
__builtin_unreachable();
|
|
#elif defined(_MSC_VER)
|
|
__assume(false);
|
|
#else
|
|
#endif
|
|
}
|
|
|
|
static constexpr int kX509NameFlagsMultiline =
|
|
ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_UTF8_CONVERT |
|
|
XN_FLAG_SEP_MULTILINE | XN_FLAG_FN_SN;
|
|
|
|
// ============================================================================
|
|
// Error handling utilities
|
|
|
|
// Capture the current OpenSSL Error Stack. The stack will be ordered such
|
|
// that the error currently at the top of the stack is at the end of the
|
|
// list and the error at the bottom of the stack is at the beginning.
|
|
class CryptoErrorList final {
|
|
public:
|
|
enum class Option { NONE, CAPTURE_ON_CONSTRUCT };
|
|
CryptoErrorList(Option option = Option::CAPTURE_ON_CONSTRUCT);
|
|
|
|
void capture();
|
|
|
|
// Add an error message to the end of the stack.
|
|
void add(std::string message);
|
|
|
|
inline const std::string& peek_back() const { return errors_.back(); }
|
|
inline size_t size() const { return errors_.size(); }
|
|
inline bool empty() const { return errors_.empty(); }
|
|
|
|
inline auto begin() const noexcept { return errors_.begin(); }
|
|
inline auto end() const noexcept { return errors_.end(); }
|
|
inline auto rbegin() const noexcept { return errors_.rbegin(); }
|
|
inline auto rend() const noexcept { return errors_.rend(); }
|
|
|
|
std::optional<std::string> pop_back();
|
|
std::optional<std::string> pop_front();
|
|
|
|
private:
|
|
std::list<std::string> errors_;
|
|
};
|
|
|
|
// Forcibly clears the error stack on destruction. This stops stale errors
|
|
// from popping up later in the lifecycle of crypto operations where they
|
|
// would cause spurious failures. It is a rather blunt method, though, and
|
|
// ERR_clear_error() isn't necessarily cheap.
|
|
//
|
|
// If created with a pointer to a CryptoErrorList, the current OpenSSL error
|
|
// stack will be captured before clearing the error.
|
|
class ClearErrorOnReturn final {
|
|
public:
|
|
ClearErrorOnReturn(CryptoErrorList* errors = nullptr);
|
|
~ClearErrorOnReturn();
|
|
NCRYPTO_DISALLOW_COPY_AND_MOVE(ClearErrorOnReturn)
|
|
NCRYPTO_DISALLOW_NEW_DELETE()
|
|
|
|
int peekError();
|
|
|
|
private:
|
|
CryptoErrorList* errors_;
|
|
};
|
|
|
|
// Pop errors from OpenSSL's error stack that were added between when this
|
|
// was constructed and destructed.
|
|
//
|
|
// If created with a pointer to a CryptoErrorList, the current OpenSSL error
|
|
// stack will be captured before resetting the error to the mark.
|
|
class MarkPopErrorOnReturn final {
|
|
public:
|
|
MarkPopErrorOnReturn(CryptoErrorList* errors = nullptr);
|
|
~MarkPopErrorOnReturn();
|
|
NCRYPTO_DISALLOW_COPY_AND_MOVE(MarkPopErrorOnReturn)
|
|
NCRYPTO_DISALLOW_NEW_DELETE()
|
|
|
|
int peekError();
|
|
|
|
private:
|
|
CryptoErrorList* errors_;
|
|
};
|
|
|
|
// TODO(@jasnell): Eventually replace with std::expected when we are able to
|
|
// bump up to c++23.
|
|
template <typename T, typename E>
|
|
struct Result final {
|
|
const bool has_value;
|
|
T value;
|
|
std::optional<E> error = std::nullopt;
|
|
std::optional<int> openssl_error = std::nullopt;
|
|
Result(T&& value) : has_value(true), value(std::move(value)) {}
|
|
Result(E&& error, std::optional<int> openssl_error = std::nullopt)
|
|
: has_value(false),
|
|
error(std::move(error)),
|
|
openssl_error(std::move(openssl_error)) {}
|
|
inline operator bool() const { return has_value; }
|
|
};
|
|
|
|
// ============================================================================
|
|
// Various smart pointer aliases for OpenSSL types.
|
|
|
|
template <typename T, void (*function)(T*)>
|
|
struct FunctionDeleter {
|
|
void operator()(T* pointer) const { function(pointer); }
|
|
typedef std::unique_ptr<T, FunctionDeleter> Pointer;
|
|
};
|
|
|
|
template <typename T, void (*function)(T*)>
|
|
using DeleteFnPtr = typename FunctionDeleter<T, function>::Pointer;
|
|
|
|
using PKCS8Pointer = DeleteFnPtr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>;
|
|
using RSAPointer = DeleteFnPtr<RSA, RSA_free>;
|
|
using SSLSessionPointer = DeleteFnPtr<SSL_SESSION, SSL_SESSION_free>;
|
|
|
|
class BIOPointer;
|
|
class BignumPointer;
|
|
class CipherCtxPointer;
|
|
class DataPointer;
|
|
class DHPointer;
|
|
class ECKeyPointer;
|
|
class EVPKeyPointer;
|
|
class EVPMDCtxPointer;
|
|
class SSLCtxPointer;
|
|
class SSLPointer;
|
|
class X509View;
|
|
class X509Pointer;
|
|
class ECDSASigPointer;
|
|
class ECGroupPointer;
|
|
class ECPointPointer;
|
|
class ECKeyPointer;
|
|
class Dsa;
|
|
class Rsa;
|
|
class Ec;
|
|
|
|
struct StackOfXASN1Deleter {
|
|
void operator()(STACK_OF(ASN1_OBJECT) * p) const {
|
|
sk_ASN1_OBJECT_pop_free(p, ASN1_OBJECT_free);
|
|
}
|
|
};
|
|
using StackOfASN1 = std::unique_ptr<STACK_OF(ASN1_OBJECT), StackOfXASN1Deleter>;
|
|
|
|
// An unowned, unmanaged pointer to a buffer of data.
|
|
template <typename T>
|
|
struct Buffer {
|
|
T* data = nullptr;
|
|
size_t len = 0;
|
|
};
|
|
|
|
class Digest final {
|
|
public:
|
|
static constexpr size_t MAX_SIZE = EVP_MAX_MD_SIZE;
|
|
Digest() = default;
|
|
Digest(const EVP_MD* md) : md_(md) {}
|
|
Digest(const Digest&) = default;
|
|
Digest& operator=(const Digest&) = default;
|
|
inline Digest& operator=(const EVP_MD* md) {
|
|
md_ = md;
|
|
return *this;
|
|
}
|
|
NCRYPTO_DISALLOW_MOVE(Digest)
|
|
|
|
size_t size() const;
|
|
|
|
inline const EVP_MD* get() const { return md_; }
|
|
inline operator const EVP_MD*() const { return md_; }
|
|
inline operator bool() const { return md_ != nullptr; }
|
|
|
|
static const Digest MD5;
|
|
static const Digest SHA1;
|
|
static const Digest SHA256;
|
|
static const Digest SHA384;
|
|
static const Digest SHA512;
|
|
|
|
static const Digest FromName(const char* name);
|
|
|
|
private:
|
|
const EVP_MD* md_ = nullptr;
|
|
};
|
|
|
|
// Computes a fixed-length digest.
|
|
DataPointer hashDigest(const Buffer<const unsigned char>& data,
|
|
const EVP_MD* md);
|
|
// Computes a variable-length digest for XOF algorithms (e.g. SHAKE128).
|
|
DataPointer xofHashDigest(const Buffer<const unsigned char>& data,
|
|
const EVP_MD* md,
|
|
size_t length);
|
|
|
|
class Cipher final {
|
|
public:
|
|
static constexpr size_t MAX_KEY_LENGTH = EVP_MAX_KEY_LENGTH;
|
|
static constexpr size_t MAX_IV_LENGTH = EVP_MAX_IV_LENGTH;
|
|
#ifdef EVP_MAX_AEAD_TAG_LENGTH
|
|
static constexpr size_t MAX_AUTH_TAG_LENGTH = EVP_MAX_AEAD_TAG_LENGTH;
|
|
#else
|
|
static constexpr size_t MAX_AUTH_TAG_LENGTH = 16;
|
|
#endif
|
|
static_assert(EVP_GCM_TLS_TAG_LEN <= MAX_AUTH_TAG_LENGTH &&
|
|
EVP_CCM_TLS_TAG_LEN <= MAX_AUTH_TAG_LENGTH &&
|
|
EVP_CHACHAPOLY_TLS_TAG_LEN <= MAX_AUTH_TAG_LENGTH);
|
|
|
|
Cipher() = default;
|
|
Cipher(const EVP_CIPHER* cipher) : cipher_(cipher) {}
|
|
Cipher(const Cipher&) = default;
|
|
Cipher& operator=(const Cipher&) = default;
|
|
inline Cipher& operator=(const EVP_CIPHER* cipher) {
|
|
cipher_ = cipher;
|
|
return *this;
|
|
}
|
|
NCRYPTO_DISALLOW_MOVE(Cipher)
|
|
|
|
inline const EVP_CIPHER* get() const { return cipher_; }
|
|
inline operator const EVP_CIPHER*() const { return cipher_; }
|
|
inline operator bool() const { return cipher_ != nullptr; }
|
|
|
|
int getNid() const;
|
|
int getMode() const;
|
|
int getIvLength() const;
|
|
int getKeyLength() const;
|
|
int getBlockSize() const;
|
|
std::string_view getModeLabel() const;
|
|
const char* getName() const;
|
|
|
|
bool isGcmMode() const;
|
|
bool isWrapMode() const;
|
|
bool isCtrMode() const;
|
|
bool isCcmMode() const;
|
|
bool isOcbMode() const;
|
|
bool isStreamMode() const;
|
|
bool isChaCha20Poly1305() const;
|
|
|
|
bool isSupportedAuthenticatedMode() const;
|
|
|
|
int bytesToKey(const Digest& digest,
|
|
const Buffer<const unsigned char>& input,
|
|
unsigned char* key,
|
|
unsigned char* iv) const;
|
|
|
|
static const Cipher FromName(const char* name);
|
|
static const Cipher FromNid(int nid);
|
|
static const Cipher FromCtx(const CipherCtxPointer& ctx);
|
|
|
|
using CipherNameCallback = std::function<void(const char* name)>;
|
|
|
|
// Iterates the known ciphers if the underlying implementation
|
|
// is able to do so.
|
|
static void ForEach(CipherNameCallback callback);
|
|
|
|
// Utilities to get various ciphers by type. If the underlying
|
|
// implementation does not support the requested cipher, then
|
|
// the result will be an empty Cipher object whose bool operator
|
|
// will return false.
|
|
|
|
static const Cipher EMPTY;
|
|
static const Cipher AES_128_CBC;
|
|
static const Cipher AES_192_CBC;
|
|
static const Cipher AES_256_CBC;
|
|
static const Cipher AES_128_CTR;
|
|
static const Cipher AES_192_CTR;
|
|
static const Cipher AES_256_CTR;
|
|
static const Cipher AES_128_GCM;
|
|
static const Cipher AES_192_GCM;
|
|
static const Cipher AES_256_GCM;
|
|
static const Cipher AES_128_KW;
|
|
static const Cipher AES_192_KW;
|
|
static const Cipher AES_256_KW;
|
|
|
|
struct CipherParams {
|
|
int padding;
|
|
Digest digest;
|
|
const Buffer<const void> label;
|
|
};
|
|
|
|
static DataPointer encrypt(const EVPKeyPointer& key,
|
|
const CipherParams& params,
|
|
const Buffer<const void> in);
|
|
static DataPointer decrypt(const EVPKeyPointer& key,
|
|
const CipherParams& params,
|
|
const Buffer<const void> in);
|
|
|
|
static DataPointer sign(const EVPKeyPointer& key,
|
|
const CipherParams& params,
|
|
const Buffer<const void> in);
|
|
|
|
static DataPointer recover(const EVPKeyPointer& key,
|
|
const CipherParams& params,
|
|
const Buffer<const void> in);
|
|
|
|
static constexpr bool IsValidGCMTagLength(unsigned int tag_len) {
|
|
return tag_len == 4 || tag_len == 8 || (tag_len >= 12 && tag_len <= 16);
|
|
}
|
|
|
|
private:
|
|
const EVP_CIPHER* cipher_ = nullptr;
|
|
};
|
|
|
|
// ============================================================================
|
|
// DSA
|
|
|
|
class Dsa final {
|
|
public:
|
|
Dsa();
|
|
Dsa(OSSL3_CONST DSA* dsa);
|
|
NCRYPTO_DISALLOW_COPY_AND_MOVE(Dsa)
|
|
|
|
inline operator bool() const { return dsa_ != nullptr; }
|
|
inline operator OSSL3_CONST DSA*() const { return dsa_; }
|
|
|
|
const BIGNUM* getP() const;
|
|
const BIGNUM* getQ() const;
|
|
size_t getModulusLength() const;
|
|
size_t getDivisorLength() const;
|
|
|
|
private:
|
|
OSSL3_CONST DSA* dsa_;
|
|
};
|
|
|
|
// ============================================================================
|
|
// RSA
|
|
|
|
class Rsa final {
|
|
public:
|
|
Rsa();
|
|
Rsa(OSSL3_CONST RSA* rsa);
|
|
NCRYPTO_DISALLOW_COPY_AND_MOVE(Rsa)
|
|
|
|
inline operator bool() const { return rsa_ != nullptr; }
|
|
inline operator OSSL3_CONST RSA*() const { return rsa_; }
|
|
|
|
struct PublicKey {
|
|
const BIGNUM* n;
|
|
const BIGNUM* e;
|
|
const BIGNUM* d;
|
|
};
|
|
struct PrivateKey {
|
|
const BIGNUM* p;
|
|
const BIGNUM* q;
|
|
const BIGNUM* dp;
|
|
const BIGNUM* dq;
|
|
const BIGNUM* qi;
|
|
};
|
|
struct PssParams {
|
|
std::string_view digest = "sha1";
|
|
std::optional<std::string_view> mgf1_digest = "sha1";
|
|
int64_t salt_length = 20;
|
|
};
|
|
|
|
const PublicKey getPublicKey() const;
|
|
const PrivateKey getPrivateKey() const;
|
|
const std::optional<PssParams> getPssParams() const;
|
|
|
|
bool setPublicKey(BignumPointer&& n, BignumPointer&& e);
|
|
bool setPrivateKey(BignumPointer&& d,
|
|
BignumPointer&& q,
|
|
BignumPointer&& p,
|
|
BignumPointer&& dp,
|
|
BignumPointer&& dq,
|
|
BignumPointer&& qi);
|
|
|
|
using CipherParams = Cipher::CipherParams;
|
|
|
|
static DataPointer encrypt(const EVPKeyPointer& key,
|
|
const CipherParams& params,
|
|
const Buffer<const void> in);
|
|
static DataPointer decrypt(const EVPKeyPointer& key,
|
|
const CipherParams& params,
|
|
const Buffer<const void> in);
|
|
|
|
private:
|
|
OSSL3_CONST RSA* rsa_;
|
|
};
|
|
|
|
class Ec final {
|
|
public:
|
|
Ec();
|
|
Ec(OSSL3_CONST EC_KEY* key);
|
|
NCRYPTO_DISALLOW_COPY_AND_MOVE(Ec)
|
|
|
|
const EC_GROUP* getGroup() const;
|
|
int getCurve() const;
|
|
|
|
inline operator bool() const { return ec_ != nullptr; }
|
|
inline operator OSSL3_CONST EC_KEY*() const { return ec_; }
|
|
|
|
static int GetCurveIdFromName(const char* name);
|
|
|
|
using GetCurveCallback = std::function<bool(const char*)>;
|
|
static bool GetCurves(GetCurveCallback callback);
|
|
|
|
private:
|
|
OSSL3_CONST EC_KEY* ec_ = nullptr;
|
|
};
|
|
|
|
// A managed pointer to a buffer of data. When destroyed the underlying
|
|
// buffer will be freed.
|
|
class DataPointer final {
|
|
public:
|
|
static DataPointer Alloc(size_t len);
|
|
static DataPointer Copy(const Buffer<const void>& buffer);
|
|
|
|
// Attempts to allocate the buffer space using the secure heap, if
|
|
// supported/enabled. If the secure heap is disabled, then this
|
|
// ends up being equivalent to Alloc(len). Note that allocation
|
|
// will fail if there is not enough free space remaining in the
|
|
// secure heap space.
|
|
static DataPointer SecureAlloc(size_t len);
|
|
|
|
// If the secure heap is enabled, returns the amount of data that
|
|
// has been allocated from the heap.
|
|
static size_t GetSecureHeapUsed();
|
|
|
|
enum class InitSecureHeapResult {
|
|
FAILED,
|
|
UNABLE_TO_MEMORY_MAP,
|
|
OK,
|
|
};
|
|
|
|
// Attempt to initialize the secure heap. The secure heap is not
|
|
// supported on all operating systems and whenever boringssl is
|
|
// used.
|
|
static InitSecureHeapResult TryInitSecureHeap(size_t amount, size_t min);
|
|
|
|
DataPointer() = default;
|
|
explicit DataPointer(void* data, size_t len, bool secure = false);
|
|
explicit DataPointer(const Buffer<void>& buffer, bool secure = false);
|
|
DataPointer(DataPointer&& other) noexcept;
|
|
DataPointer& operator=(DataPointer&& other) noexcept;
|
|
NCRYPTO_DISALLOW_COPY(DataPointer)
|
|
~DataPointer();
|
|
|
|
inline bool operator==(std::nullptr_t) noexcept { return data_ == nullptr; }
|
|
inline operator bool() const { return data_ != nullptr; }
|
|
|
|
template <typename T = void>
|
|
inline T* get() const noexcept {
|
|
return static_cast<T*>(data_);
|
|
}
|
|
|
|
inline size_t size() const noexcept { return len_; }
|
|
void reset(void* data = nullptr, size_t len = 0);
|
|
void reset(const Buffer<void>& buffer);
|
|
|
|
// Sets the underlying data buffer to all zeros.
|
|
void zero();
|
|
|
|
DataPointer resize(size_t len);
|
|
|
|
// Releases ownership of the underlying data buffer. It is the caller's
|
|
// responsibility to ensure the buffer is appropriately freed.
|
|
Buffer<void> release();
|
|
|
|
// Returns a Buffer struct that is a view of the underlying data.
|
|
template <typename T = void>
|
|
inline operator const Buffer<T>() const {
|
|
return {
|
|
.data = static_cast<T*>(data_),
|
|
.len = len_,
|
|
};
|
|
}
|
|
|
|
bool isSecure() const { return secure_; }
|
|
|
|
private:
|
|
void* data_ = nullptr;
|
|
size_t len_ = 0;
|
|
bool secure_ = false;
|
|
};
|
|
|
|
class BIOPointer final {
|
|
public:
|
|
static BIOPointer NewMem();
|
|
static BIOPointer NewSecMem();
|
|
static BIOPointer New(const BIO_METHOD* method);
|
|
static BIOPointer New(const void* data, size_t len);
|
|
static BIOPointer New(const BIGNUM* bn);
|
|
static BIOPointer NewFile(const char* filename, const char* mode);
|
|
static BIOPointer NewFp(FILE* fd, int flags);
|
|
|
|
template <typename T>
|
|
static BIOPointer New(const Buffer<T>& buf) {
|
|
return New(buf.data, buf.len);
|
|
}
|
|
|
|
BIOPointer() = default;
|
|
BIOPointer(std::nullptr_t) : bio_(nullptr) {}
|
|
explicit BIOPointer(BIO* bio);
|
|
BIOPointer(BIOPointer&& other) noexcept;
|
|
BIOPointer& operator=(BIOPointer&& other) noexcept;
|
|
NCRYPTO_DISALLOW_COPY(BIOPointer)
|
|
~BIOPointer();
|
|
|
|
inline bool operator==(std::nullptr_t) noexcept { return bio_ == nullptr; }
|
|
inline operator bool() const { return bio_ != nullptr; }
|
|
inline BIO* get() const noexcept { return bio_.get(); }
|
|
|
|
inline operator BUF_MEM*() const {
|
|
BUF_MEM* mem = nullptr;
|
|
if (!bio_) return mem;
|
|
BIO_get_mem_ptr(bio_.get(), &mem);
|
|
return mem;
|
|
}
|
|
|
|
inline operator BIO*() const { return bio_.get(); }
|
|
|
|
void reset(BIO* bio = nullptr);
|
|
BIO* release();
|
|
|
|
bool resetBio() const;
|
|
|
|
static int Write(BIOPointer* bio, std::string_view message);
|
|
|
|
template <typename... Args>
|
|
static void Printf(BIOPointer* bio, const char* format, Args... args) {
|
|
if (bio == nullptr || !*bio) return;
|
|
BIO_printf(bio->get(), format, std::forward<Args...>(args...));
|
|
}
|
|
|
|
private:
|
|
mutable DeleteFnPtr<BIO, BIO_free_all> bio_;
|
|
};
|
|
|
|
class BignumPointer final {
|
|
public:
|
|
BignumPointer() = default;
|
|
explicit BignumPointer(BIGNUM* bignum);
|
|
explicit BignumPointer(const unsigned char* data, size_t len);
|
|
BignumPointer(BignumPointer&& other) noexcept;
|
|
BignumPointer& operator=(BignumPointer&& other) noexcept;
|
|
NCRYPTO_DISALLOW_COPY(BignumPointer)
|
|
~BignumPointer();
|
|
|
|
int operator<=>(const BignumPointer& other) const noexcept;
|
|
int operator<=>(const BIGNUM* other) const noexcept;
|
|
inline operator bool() const { return bn_ != nullptr; }
|
|
inline BIGNUM* get() const noexcept { return bn_.get(); }
|
|
void reset(BIGNUM* bn = nullptr);
|
|
void reset(const unsigned char* data, size_t len);
|
|
BIGNUM* release();
|
|
|
|
bool isZero() const;
|
|
bool isOne() const;
|
|
|
|
bool setWord(unsigned long w); // NOLINT(runtime/int)
|
|
unsigned long getWord() const; // NOLINT(runtime/int)
|
|
|
|
size_t byteLength() const;
|
|
|
|
DataPointer toHex() const;
|
|
DataPointer encode() const;
|
|
DataPointer encodePadded(size_t size) const;
|
|
size_t encodeInto(unsigned char* out) const;
|
|
size_t encodePaddedInto(unsigned char* out, size_t size) const;
|
|
|
|
using PrimeCheckCallback = std::function<bool(int, int)>;
|
|
int isPrime(int checks,
|
|
PrimeCheckCallback cb = defaultPrimeCheckCallback) const;
|
|
struct PrimeConfig {
|
|
int bits;
|
|
bool safe = false;
|
|
const BignumPointer& add;
|
|
const BignumPointer& rem;
|
|
};
|
|
|
|
static BignumPointer NewPrime(
|
|
const PrimeConfig& params,
|
|
PrimeCheckCallback cb = defaultPrimeCheckCallback);
|
|
|
|
bool generate(const PrimeConfig& params,
|
|
PrimeCheckCallback cb = defaultPrimeCheckCallback) const;
|
|
|
|
static BignumPointer New();
|
|
static BignumPointer NewSecure();
|
|
static BignumPointer NewSub(const BignumPointer& a, const BignumPointer& b);
|
|
static BignumPointer NewLShift(size_t length);
|
|
|
|
static DataPointer Encode(const BIGNUM* bn);
|
|
static DataPointer EncodePadded(const BIGNUM* bn, size_t size);
|
|
static size_t EncodePaddedInto(const BIGNUM* bn,
|
|
unsigned char* out,
|
|
size_t size);
|
|
static int GetBitCount(const BIGNUM* bn);
|
|
static int GetByteCount(const BIGNUM* bn);
|
|
static unsigned long GetWord(const BIGNUM* bn); // NOLINT(runtime/int)
|
|
static const BIGNUM* One();
|
|
|
|
BignumPointer clone();
|
|
|
|
private:
|
|
DeleteFnPtr<BIGNUM, BN_clear_free> bn_;
|
|
|
|
static bool defaultPrimeCheckCallback(int, int) { return 1; }
|
|
};
|
|
|
|
class CipherCtxPointer final {
|
|
public:
|
|
static CipherCtxPointer New();
|
|
|
|
CipherCtxPointer() = default;
|
|
explicit CipherCtxPointer(EVP_CIPHER_CTX* ctx);
|
|
CipherCtxPointer(CipherCtxPointer&& other) noexcept;
|
|
CipherCtxPointer& operator=(CipherCtxPointer&& other) noexcept;
|
|
NCRYPTO_DISALLOW_COPY(CipherCtxPointer)
|
|
~CipherCtxPointer();
|
|
|
|
inline bool operator==(std::nullptr_t) const noexcept {
|
|
return ctx_ == nullptr;
|
|
}
|
|
inline operator bool() const { return ctx_ != nullptr; }
|
|
inline EVP_CIPHER_CTX* get() const { return ctx_.get(); }
|
|
inline operator EVP_CIPHER_CTX*() const { return ctx_.get(); }
|
|
void reset(EVP_CIPHER_CTX* ctx = nullptr);
|
|
EVP_CIPHER_CTX* release();
|
|
|
|
void setAllowWrap();
|
|
|
|
bool setKeyLength(size_t length);
|
|
bool setIvLength(size_t length);
|
|
bool setAeadTag(const Buffer<const char>& tag);
|
|
bool setAeadTagLength(size_t length);
|
|
bool setPadding(bool padding);
|
|
bool init(const Cipher& cipher,
|
|
bool encrypt,
|
|
const unsigned char* key = nullptr,
|
|
const unsigned char* iv = nullptr);
|
|
|
|
int getBlockSize() const;
|
|
int getMode() const;
|
|
int getNid() const;
|
|
|
|
bool isGcmMode() const;
|
|
bool isCcmMode() const;
|
|
bool isWrapMode() const;
|
|
bool isChaCha20Poly1305() const;
|
|
|
|
bool update(const Buffer<const unsigned char>& in,
|
|
unsigned char* out,
|
|
int* out_len,
|
|
bool finalize = false);
|
|
bool getAeadTag(size_t len, unsigned char* out);
|
|
|
|
private:
|
|
DeleteFnPtr<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free> ctx_;
|
|
};
|
|
|
|
class EVPKeyCtxPointer final {
|
|
public:
|
|
EVPKeyCtxPointer();
|
|
explicit EVPKeyCtxPointer(EVP_PKEY_CTX* ctx);
|
|
EVPKeyCtxPointer(EVPKeyCtxPointer&& other) noexcept;
|
|
EVPKeyCtxPointer& operator=(EVPKeyCtxPointer&& other) noexcept;
|
|
NCRYPTO_DISALLOW_COPY(EVPKeyCtxPointer)
|
|
~EVPKeyCtxPointer();
|
|
|
|
inline bool operator==(std::nullptr_t) const noexcept {
|
|
return ctx_ == nullptr;
|
|
}
|
|
inline operator bool() const { return ctx_ != nullptr; }
|
|
inline EVP_PKEY_CTX* get() const { return ctx_.get(); }
|
|
void reset(EVP_PKEY_CTX* ctx = nullptr);
|
|
EVP_PKEY_CTX* release();
|
|
|
|
bool initForDerive(const EVPKeyPointer& peer);
|
|
DataPointer derive() const;
|
|
|
|
bool initForParamgen();
|
|
bool setDhParameters(int prime_size, uint32_t generator);
|
|
bool setDsaParameters(uint32_t bits, std::optional<int> q_bits);
|
|
bool setEcParameters(int curve, int encoding);
|
|
|
|
bool setRsaOaepMd(const Digest& md);
|
|
bool setRsaMgf1Md(const Digest& md);
|
|
bool setRsaPadding(int padding);
|
|
bool setRsaKeygenPubExp(BignumPointer&& e);
|
|
bool setRsaKeygenBits(int bits);
|
|
bool setRsaPssKeygenMd(const Digest& md);
|
|
bool setRsaPssKeygenMgf1Md(const Digest& md);
|
|
bool setRsaPssSaltlen(int salt_len);
|
|
bool setRsaImplicitRejection();
|
|
bool setRsaOaepLabel(DataPointer&& data);
|
|
|
|
bool setSignatureMd(const EVPMDCtxPointer& md);
|
|
|
|
bool publicCheck() const;
|
|
bool privateCheck() const;
|
|
|
|
bool verify(const Buffer<const unsigned char>& sig,
|
|
const Buffer<const unsigned char>& data);
|
|
DataPointer sign(const Buffer<const unsigned char>& data);
|
|
bool signInto(const Buffer<const unsigned char>& data,
|
|
Buffer<unsigned char>* sig);
|
|
|
|
static constexpr int kDefaultRsaExponent = 0x10001;
|
|
|
|
static bool setRsaPadding(EVP_PKEY_CTX* ctx,
|
|
int padding,
|
|
std::optional<int> salt_len = std::nullopt);
|
|
|
|
EVPKeyPointer paramgen() const;
|
|
|
|
bool initForEncrypt();
|
|
bool initForDecrypt();
|
|
bool initForKeygen();
|
|
int initForVerify();
|
|
int initForSign();
|
|
|
|
static EVPKeyCtxPointer New(const EVPKeyPointer& key);
|
|
static EVPKeyCtxPointer NewFromID(int id);
|
|
|
|
private:
|
|
DeleteFnPtr<EVP_PKEY_CTX, EVP_PKEY_CTX_free> ctx_;
|
|
};
|
|
|
|
class EVPKeyPointer final {
|
|
public:
|
|
static EVPKeyPointer New();
|
|
static EVPKeyPointer NewRawPublic(int id,
|
|
const Buffer<const unsigned char>& data);
|
|
static EVPKeyPointer NewRawPrivate(int id,
|
|
const Buffer<const unsigned char>& data);
|
|
#if OPENSSL_VERSION_MAJOR >= 3 && OPENSSL_VERSION_MINOR >= 5
|
|
static EVPKeyPointer NewRawSeed(int id,
|
|
const Buffer<const unsigned char>& data);
|
|
#endif
|
|
static EVPKeyPointer NewDH(DHPointer&& dh);
|
|
static EVPKeyPointer NewRSA(RSAPointer&& rsa);
|
|
|
|
enum class PKEncodingType {
|
|
// RSAPublicKey / RSAPrivateKey according to PKCS#1.
|
|
PKCS1,
|
|
// PrivateKeyInfo or EncryptedPrivateKeyInfo according to PKCS#8.
|
|
PKCS8,
|
|
// SubjectPublicKeyInfo according to X.509.
|
|
SPKI,
|
|
// ECPrivateKey according to SEC1.
|
|
SEC1,
|
|
};
|
|
|
|
enum class PKFormatType {
|
|
DER,
|
|
PEM,
|
|
JWK,
|
|
};
|
|
|
|
enum class PKParseError { NOT_RECOGNIZED, NEED_PASSPHRASE, FAILED };
|
|
using ParseKeyResult = Result<EVPKeyPointer, PKParseError>;
|
|
|
|
struct AsymmetricKeyEncodingConfig {
|
|
bool output_key_object = false;
|
|
PKFormatType format = PKFormatType::DER;
|
|
PKEncodingType type = PKEncodingType::PKCS8;
|
|
AsymmetricKeyEncodingConfig() = default;
|
|
AsymmetricKeyEncodingConfig(bool output_key_object,
|
|
PKFormatType format,
|
|
PKEncodingType type);
|
|
AsymmetricKeyEncodingConfig(const AsymmetricKeyEncodingConfig&) = default;
|
|
AsymmetricKeyEncodingConfig& operator=(const AsymmetricKeyEncodingConfig&) =
|
|
default;
|
|
};
|
|
using PublicKeyEncodingConfig = AsymmetricKeyEncodingConfig;
|
|
|
|
struct PrivateKeyEncodingConfig : public AsymmetricKeyEncodingConfig {
|
|
const EVP_CIPHER* cipher = nullptr;
|
|
std::optional<DataPointer> passphrase = std::nullopt;
|
|
PrivateKeyEncodingConfig() = default;
|
|
PrivateKeyEncodingConfig(bool output_key_object,
|
|
PKFormatType format,
|
|
PKEncodingType type)
|
|
: AsymmetricKeyEncodingConfig(output_key_object, format, type) {}
|
|
PrivateKeyEncodingConfig(const PrivateKeyEncodingConfig&);
|
|
PrivateKeyEncodingConfig& operator=(const PrivateKeyEncodingConfig&);
|
|
};
|
|
|
|
static ParseKeyResult TryParsePublicKey(
|
|
const PublicKeyEncodingConfig& config,
|
|
const Buffer<const unsigned char>& buffer);
|
|
|
|
static ParseKeyResult TryParsePublicKeyPEM(
|
|
const Buffer<const unsigned char>& buffer);
|
|
|
|
static ParseKeyResult TryParsePrivateKey(
|
|
const PrivateKeyEncodingConfig& config,
|
|
const Buffer<const unsigned char>& buffer);
|
|
|
|
EVPKeyPointer() = default;
|
|
explicit EVPKeyPointer(EVP_PKEY* pkey);
|
|
EVPKeyPointer(EVPKeyPointer&& other) noexcept;
|
|
EVPKeyPointer& operator=(EVPKeyPointer&& other) noexcept;
|
|
NCRYPTO_DISALLOW_COPY(EVPKeyPointer)
|
|
~EVPKeyPointer();
|
|
|
|
bool assign(const ECKeyPointer& eckey);
|
|
bool set(const ECKeyPointer& eckey);
|
|
operator const EC_KEY*() const;
|
|
|
|
inline bool operator==(std::nullptr_t) const noexcept {
|
|
return pkey_ == nullptr;
|
|
}
|
|
inline operator bool() const { return pkey_ != nullptr; }
|
|
inline EVP_PKEY* get() const { return pkey_.get(); }
|
|
void reset(EVP_PKEY* pkey = nullptr);
|
|
EVP_PKEY* release();
|
|
|
|
static int id(const EVP_PKEY* key);
|
|
static int base_id(const EVP_PKEY* key);
|
|
|
|
int id() const;
|
|
int base_id() const;
|
|
int bits() const;
|
|
size_t size() const;
|
|
|
|
size_t rawPublicKeySize() const;
|
|
size_t rawPrivateKeySize() const;
|
|
DataPointer rawPublicKey() const;
|
|
DataPointer rawPrivateKey() const;
|
|
BIOPointer derPublicKey() const;
|
|
|
|
#if OPENSSL_VERSION_MAJOR >= 3 && OPENSSL_VERSION_MINOR >= 5
|
|
DataPointer rawSeed() const;
|
|
#endif
|
|
|
|
Result<BIOPointer, bool> writePrivateKey(
|
|
const PrivateKeyEncodingConfig& config) const;
|
|
Result<BIOPointer, bool> writePublicKey(
|
|
const PublicKeyEncodingConfig& config) const;
|
|
|
|
EVPKeyCtxPointer newCtx() const;
|
|
|
|
static bool IsRSAPrivateKey(const Buffer<const unsigned char>& buffer);
|
|
|
|
std::optional<uint32_t> getBytesOfRS() const;
|
|
int getDefaultSignPadding() const;
|
|
operator Rsa() const;
|
|
operator Dsa() const;
|
|
|
|
bool isRsaVariant() const;
|
|
bool isOneShotVariant() const;
|
|
bool isSigVariant() const;
|
|
bool validateDsaParameters() const;
|
|
|
|
private:
|
|
DeleteFnPtr<EVP_PKEY, EVP_PKEY_free> pkey_;
|
|
};
|
|
|
|
class DHPointer final {
|
|
public:
|
|
enum class FindGroupOption {
|
|
NONE,
|
|
// There are known and documented security issues with prime groups smaller
|
|
// than 2048 bits. When the NO_SMALL_PRIMES option is set, these small prime
|
|
// groups will not be supported.
|
|
NO_SMALL_PRIMES,
|
|
};
|
|
|
|
static BignumPointer GetStandardGenerator();
|
|
|
|
static BignumPointer FindGroup(
|
|
std::string_view name, FindGroupOption option = FindGroupOption::NONE);
|
|
static DHPointer FromGroup(std::string_view name,
|
|
FindGroupOption option = FindGroupOption::NONE);
|
|
|
|
static DHPointer New(BignumPointer&& p, BignumPointer&& g);
|
|
static DHPointer New(size_t bits, unsigned int generator);
|
|
|
|
DHPointer() = default;
|
|
explicit DHPointer(DH* dh);
|
|
DHPointer(DHPointer&& other) noexcept;
|
|
DHPointer& operator=(DHPointer&& other) noexcept;
|
|
NCRYPTO_DISALLOW_COPY(DHPointer)
|
|
~DHPointer();
|
|
|
|
inline bool operator==(std::nullptr_t) noexcept { return dh_ == nullptr; }
|
|
inline operator bool() const { return dh_ != nullptr; }
|
|
inline DH* get() const { return dh_.get(); }
|
|
void reset(DH* dh = nullptr);
|
|
DH* release();
|
|
|
|
enum class CheckResult {
|
|
NONE,
|
|
P_NOT_PRIME = DH_CHECK_P_NOT_PRIME,
|
|
P_NOT_SAFE_PRIME = DH_CHECK_P_NOT_SAFE_PRIME,
|
|
UNABLE_TO_CHECK_GENERATOR = DH_UNABLE_TO_CHECK_GENERATOR,
|
|
NOT_SUITABLE_GENERATOR = DH_NOT_SUITABLE_GENERATOR,
|
|
Q_NOT_PRIME = DH_CHECK_Q_NOT_PRIME,
|
|
#ifndef OPENSSL_IS_BORINGSSL
|
|
// Boringssl does not define the DH_CHECK_INVALID_[Q or J]_VALUE
|
|
INVALID_Q = DH_CHECK_INVALID_Q_VALUE,
|
|
INVALID_J = DH_CHECK_INVALID_J_VALUE,
|
|
#endif
|
|
CHECK_FAILED = 512,
|
|
};
|
|
CheckResult check();
|
|
|
|
enum class CheckPublicKeyResult {
|
|
NONE,
|
|
#ifndef OPENSSL_IS_BORINGSSL
|
|
// Boringssl does not define DH_R_CHECK_PUBKEY_TOO_SMALL or TOO_LARGE
|
|
TOO_SMALL = DH_R_CHECK_PUBKEY_TOO_SMALL,
|
|
TOO_LARGE = DH_R_CHECK_PUBKEY_TOO_LARGE,
|
|
INVALID = DH_R_CHECK_PUBKEY_INVALID,
|
|
#else
|
|
INVALID = DH_R_INVALID_PUBKEY,
|
|
#endif
|
|
CHECK_FAILED = 512,
|
|
};
|
|
// Check to see if the given public key is suitable for this DH instance.
|
|
CheckPublicKeyResult checkPublicKey(const BignumPointer& pub_key);
|
|
|
|
DataPointer getPrime() const;
|
|
DataPointer getGenerator() const;
|
|
DataPointer getPublicKey() const;
|
|
DataPointer getPrivateKey() const;
|
|
DataPointer generateKeys() const;
|
|
DataPointer computeSecret(const BignumPointer& peer) const;
|
|
|
|
bool setPublicKey(BignumPointer&& key);
|
|
bool setPrivateKey(BignumPointer&& key);
|
|
|
|
size_t size() const;
|
|
|
|
static DataPointer stateless(const EVPKeyPointer& ourKey,
|
|
const EVPKeyPointer& theirKey);
|
|
|
|
private:
|
|
DeleteFnPtr<DH, DH_free> dh_;
|
|
};
|
|
|
|
struct StackOfX509Deleter {
|
|
void operator()(STACK_OF(X509) * p) const { sk_X509_pop_free(p, X509_free); }
|
|
};
|
|
using StackOfX509 = std::unique_ptr<STACK_OF(X509), StackOfX509Deleter>;
|
|
|
|
class SSLCtxPointer final {
|
|
public:
|
|
SSLCtxPointer() = default;
|
|
explicit SSLCtxPointer(SSL_CTX* ctx);
|
|
SSLCtxPointer(SSLCtxPointer&& other) noexcept;
|
|
SSLCtxPointer& operator=(SSLCtxPointer&& other) noexcept;
|
|
NCRYPTO_DISALLOW_COPY(SSLCtxPointer)
|
|
~SSLCtxPointer();
|
|
|
|
inline bool operator==(std::nullptr_t) const noexcept {
|
|
return ctx_ == nullptr;
|
|
}
|
|
inline operator bool() const { return ctx_ != nullptr; }
|
|
inline SSL_CTX* get() const { return ctx_.get(); }
|
|
void reset(SSL_CTX* ctx = nullptr);
|
|
void reset(const SSL_METHOD* method);
|
|
SSL_CTX* release();
|
|
|
|
bool setGroups(const char* groups);
|
|
void setStatusCallback(auto callback) {
|
|
if (!ctx_) return;
|
|
SSL_CTX_set_tlsext_status_cb(get(), callback);
|
|
SSL_CTX_set_tlsext_status_arg(get(), nullptr);
|
|
}
|
|
|
|
bool setCipherSuites(const char* ciphers);
|
|
|
|
static SSLCtxPointer NewServer();
|
|
static SSLCtxPointer NewClient();
|
|
static SSLCtxPointer New(const SSL_METHOD* method = TLS_method());
|
|
|
|
private:
|
|
DeleteFnPtr<SSL_CTX, SSL_CTX_free> ctx_;
|
|
};
|
|
|
|
class SSLPointer final {
|
|
public:
|
|
SSLPointer() = default;
|
|
explicit SSLPointer(SSL* ssl);
|
|
SSLPointer(SSLPointer&& other) noexcept;
|
|
SSLPointer& operator=(SSLPointer&& other) noexcept;
|
|
NCRYPTO_DISALLOW_COPY(SSLPointer)
|
|
~SSLPointer();
|
|
|
|
inline bool operator==(std::nullptr_t) noexcept { return ssl_ == nullptr; }
|
|
inline operator bool() const { return ssl_ != nullptr; }
|
|
inline SSL* get() const { return ssl_.get(); }
|
|
inline operator SSL*() const { return ssl_.get(); }
|
|
void reset(SSL* ssl = nullptr);
|
|
SSL* release();
|
|
|
|
bool setSession(const SSLSessionPointer& session);
|
|
bool setSniContext(const SSLCtxPointer& ctx) const;
|
|
|
|
const char* getClientHelloAlpn() const;
|
|
const char* getClientHelloServerName() const;
|
|
|
|
std::optional<const std::string_view> getServerName() const;
|
|
X509View getCertificate() const;
|
|
EVPKeyPointer getPeerTempKey() const;
|
|
const SSL_CIPHER* getCipher() const;
|
|
bool isServer() const;
|
|
|
|
std::optional<std::string_view> getCipherName() const;
|
|
std::optional<std::string_view> getCipherStandardName() const;
|
|
std::optional<std::string_view> getCipherVersion() const;
|
|
|
|
std::optional<uint32_t> verifyPeerCertificate() const;
|
|
|
|
static std::optional<int> getSecurityLevel();
|
|
|
|
void getCiphers(std::function<void(const char*)> cb) const;
|
|
|
|
static SSLPointer New(const SSLCtxPointer& ctx);
|
|
static std::optional<const std::string_view> GetServerName(const SSL* ssl);
|
|
|
|
private:
|
|
DeleteFnPtr<SSL, SSL_free> ssl_;
|
|
};
|
|
|
|
class X509Name final {
|
|
public:
|
|
X509Name();
|
|
explicit X509Name(const X509_NAME* name);
|
|
NCRYPTO_DISALLOW_COPY_AND_MOVE(X509Name)
|
|
|
|
inline operator const X509_NAME*() const { return name_; }
|
|
inline operator bool() const { return name_ != nullptr; }
|
|
inline const X509_NAME* get() const { return name_; }
|
|
inline size_t size() const { return total_; }
|
|
|
|
class Iterator final {
|
|
public:
|
|
Iterator(const X509Name& name, int pos);
|
|
Iterator(const Iterator& other) = default;
|
|
Iterator(Iterator&& other) = default;
|
|
Iterator& operator=(const Iterator& other) = delete;
|
|
Iterator& operator=(Iterator&& other) = delete;
|
|
Iterator& operator++();
|
|
operator bool() const;
|
|
bool operator==(const Iterator& other) const;
|
|
bool operator!=(const Iterator& other) const;
|
|
std::pair<std::string, std::string> operator*() const;
|
|
|
|
private:
|
|
const X509Name& name_;
|
|
int loc_;
|
|
};
|
|
|
|
inline Iterator begin() const { return Iterator(*this, 0); }
|
|
inline Iterator end() const { return Iterator(*this, total_); }
|
|
|
|
private:
|
|
const X509_NAME* name_;
|
|
int total_;
|
|
};
|
|
|
|
class X509View final {
|
|
public:
|
|
static X509View From(const SSLPointer& ssl);
|
|
static X509View From(const SSLCtxPointer& ctx);
|
|
|
|
X509View() = default;
|
|
inline explicit X509View(const X509* cert) : cert_(cert) {}
|
|
X509View(const X509View& other) = default;
|
|
X509View& operator=(const X509View& other) = default;
|
|
NCRYPTO_DISALLOW_MOVE(X509View)
|
|
|
|
inline X509* get() const { return const_cast<X509*>(cert_); }
|
|
inline operator X509*() const { return const_cast<X509*>(cert_); }
|
|
inline operator const X509*() const { return cert_; }
|
|
|
|
inline bool operator==(std::nullptr_t) noexcept { return cert_ == nullptr; }
|
|
inline operator bool() const { return cert_ != nullptr; }
|
|
|
|
BIOPointer toPEM() const;
|
|
BIOPointer toDER() const;
|
|
|
|
const X509Name getSubjectName() const;
|
|
const X509Name getIssuerName() const;
|
|
BIOPointer getSubject() const;
|
|
BIOPointer getSubjectAltName() const;
|
|
BIOPointer getIssuer() const;
|
|
BIOPointer getInfoAccess() const;
|
|
BIOPointer getValidFrom() const;
|
|
BIOPointer getValidTo() const;
|
|
int64_t getValidFromTime() const;
|
|
int64_t getValidToTime() const;
|
|
DataPointer getSerialNumber() const;
|
|
Result<EVPKeyPointer, int> getPublicKey() const;
|
|
StackOfASN1 getKeyUsage() const;
|
|
|
|
bool isCA() const;
|
|
bool isIssuedBy(const X509View& other) const;
|
|
bool checkPrivateKey(const EVPKeyPointer& pkey) const;
|
|
bool checkPublicKey(const EVPKeyPointer& pkey) const;
|
|
|
|
std::optional<std::string> getFingerprint(const Digest& method) const;
|
|
|
|
X509Pointer clone() const;
|
|
|
|
enum class CheckMatch {
|
|
NO_MATCH,
|
|
MATCH,
|
|
INVALID_NAME,
|
|
OPERATION_FAILED,
|
|
};
|
|
CheckMatch checkHost(std::string_view host,
|
|
int flags,
|
|
DataPointer* peerName = nullptr) const;
|
|
CheckMatch checkEmail(std::string_view email, int flags) const;
|
|
CheckMatch checkIp(std::string_view ip, int flags) const;
|
|
|
|
using UsageCallback = std::function<void(const char*)>;
|
|
bool enumUsages(UsageCallback callback) const;
|
|
|
|
template <typename T>
|
|
using KeyCallback = std::function<bool(const T& t)>;
|
|
bool ifRsa(KeyCallback<Rsa> callback) const;
|
|
bool ifEc(KeyCallback<Ec> callback) const;
|
|
|
|
private:
|
|
const X509* cert_ = nullptr;
|
|
};
|
|
|
|
class X509Pointer final {
|
|
public:
|
|
static Result<X509Pointer, int> Parse(Buffer<const unsigned char> buffer);
|
|
static X509Pointer IssuerFrom(const SSLPointer& ssl, const X509View& view);
|
|
static X509Pointer IssuerFrom(const SSL_CTX* ctx, const X509View& view);
|
|
static X509Pointer PeerFrom(const SSLPointer& ssl);
|
|
|
|
X509Pointer() = default;
|
|
explicit X509Pointer(X509* cert);
|
|
X509Pointer(X509Pointer&& other) noexcept;
|
|
X509Pointer& operator=(X509Pointer&& other) noexcept;
|
|
NCRYPTO_DISALLOW_COPY(X509Pointer)
|
|
~X509Pointer();
|
|
|
|
inline bool operator==(std::nullptr_t) noexcept { return cert_ == nullptr; }
|
|
inline operator bool() const { return cert_ != nullptr; }
|
|
inline X509* get() const { return cert_.get(); }
|
|
inline operator X509*() const { return cert_.get(); }
|
|
inline operator const X509*() const { return cert_.get(); }
|
|
void reset(X509* cert = nullptr);
|
|
X509* release();
|
|
|
|
X509View view() const;
|
|
operator X509View() const { return view(); }
|
|
|
|
static const char* ErrorCode(int32_t err);
|
|
static std::optional<const char*> ErrorReason(int32_t err);
|
|
|
|
private:
|
|
DeleteFnPtr<X509, X509_free> cert_;
|
|
};
|
|
|
|
class ECDSASigPointer final {
|
|
public:
|
|
explicit ECDSASigPointer();
|
|
explicit ECDSASigPointer(ECDSA_SIG* sig);
|
|
ECDSASigPointer(ECDSASigPointer&& other) noexcept;
|
|
ECDSASigPointer& operator=(ECDSASigPointer&& other) noexcept;
|
|
NCRYPTO_DISALLOW_COPY(ECDSASigPointer)
|
|
~ECDSASigPointer();
|
|
|
|
inline bool operator==(std::nullptr_t) noexcept { return sig_ == nullptr; }
|
|
inline operator bool() const { return sig_ != nullptr; }
|
|
inline ECDSA_SIG* get() const { return sig_.get(); }
|
|
inline operator ECDSA_SIG*() const { return sig_.get(); }
|
|
void reset(ECDSA_SIG* sig = nullptr);
|
|
ECDSA_SIG* release();
|
|
|
|
static ECDSASigPointer New();
|
|
static ECDSASigPointer Parse(const Buffer<const unsigned char>& buffer);
|
|
|
|
inline const BIGNUM* r() const { return pr_; }
|
|
inline const BIGNUM* s() const { return ps_; }
|
|
|
|
bool setParams(BignumPointer&& r, BignumPointer&& s);
|
|
|
|
Buffer<unsigned char> encode() const;
|
|
|
|
private:
|
|
DeleteFnPtr<ECDSA_SIG, ECDSA_SIG_free> sig_;
|
|
const BIGNUM* pr_ = nullptr;
|
|
const BIGNUM* ps_ = nullptr;
|
|
};
|
|
|
|
class ECGroupPointer final {
|
|
public:
|
|
explicit ECGroupPointer();
|
|
explicit ECGroupPointer(EC_GROUP* group);
|
|
ECGroupPointer(ECGroupPointer&& other) noexcept;
|
|
ECGroupPointer& operator=(ECGroupPointer&& other) noexcept;
|
|
NCRYPTO_DISALLOW_COPY(ECGroupPointer)
|
|
~ECGroupPointer();
|
|
|
|
inline bool operator==(std::nullptr_t) noexcept { return group_ == nullptr; }
|
|
inline operator bool() const { return group_ != nullptr; }
|
|
inline EC_GROUP* get() const { return group_.get(); }
|
|
inline operator EC_GROUP*() const { return group_.get(); }
|
|
void reset(EC_GROUP* group = nullptr);
|
|
EC_GROUP* release();
|
|
|
|
static ECGroupPointer NewByCurveName(int nid);
|
|
|
|
private:
|
|
DeleteFnPtr<EC_GROUP, EC_GROUP_free> group_;
|
|
};
|
|
|
|
class ECPointPointer final {
|
|
public:
|
|
ECPointPointer();
|
|
explicit ECPointPointer(EC_POINT* point);
|
|
ECPointPointer(ECPointPointer&& other) noexcept;
|
|
ECPointPointer& operator=(ECPointPointer&& other) noexcept;
|
|
NCRYPTO_DISALLOW_COPY(ECPointPointer)
|
|
~ECPointPointer();
|
|
|
|
inline bool operator==(std::nullptr_t) noexcept { return point_ == nullptr; }
|
|
inline operator bool() const { return point_ != nullptr; }
|
|
inline EC_POINT* get() const { return point_.get(); }
|
|
inline operator EC_POINT*() const { return point_.get(); }
|
|
void reset(EC_POINT* point = nullptr);
|
|
EC_POINT* release();
|
|
|
|
bool setFromBuffer(const Buffer<const unsigned char>& buffer,
|
|
const EC_GROUP* group);
|
|
bool mul(const EC_GROUP* group, const BIGNUM* priv_key);
|
|
|
|
static ECPointPointer New(const EC_GROUP* group);
|
|
|
|
private:
|
|
DeleteFnPtr<EC_POINT, EC_POINT_free> point_;
|
|
};
|
|
|
|
class ECKeyPointer final {
|
|
public:
|
|
ECKeyPointer();
|
|
explicit ECKeyPointer(EC_KEY* key);
|
|
ECKeyPointer(ECKeyPointer&& other) noexcept;
|
|
ECKeyPointer& operator=(ECKeyPointer&& other) noexcept;
|
|
NCRYPTO_DISALLOW_COPY(ECKeyPointer)
|
|
~ECKeyPointer();
|
|
|
|
inline bool operator==(std::nullptr_t) noexcept { return key_ == nullptr; }
|
|
inline operator bool() const { return key_ != nullptr; }
|
|
inline EC_KEY* get() const { return key_.get(); }
|
|
inline operator EC_KEY*() const { return key_.get(); }
|
|
void reset(EC_KEY* key = nullptr);
|
|
EC_KEY* release();
|
|
|
|
ECKeyPointer clone() const;
|
|
bool setPrivateKey(const BignumPointer& priv);
|
|
bool setPublicKey(const ECPointPointer& pub);
|
|
bool setPublicKeyRaw(const BignumPointer& x, const BignumPointer& y);
|
|
bool generate();
|
|
bool checkKey() const;
|
|
|
|
const EC_GROUP* getGroup() const;
|
|
const BIGNUM* getPrivateKey() const;
|
|
const EC_POINT* getPublicKey() const;
|
|
|
|
static ECKeyPointer New(const EC_GROUP* group);
|
|
static ECKeyPointer NewByCurveName(int nid);
|
|
|
|
static const EC_POINT* GetPublicKey(const EC_KEY* key);
|
|
static const BIGNUM* GetPrivateKey(const EC_KEY* key);
|
|
static const EC_GROUP* GetGroup(const EC_KEY* key);
|
|
static int GetGroupName(const EC_KEY* key);
|
|
static bool Check(const EC_KEY* key);
|
|
|
|
private:
|
|
DeleteFnPtr<EC_KEY, EC_KEY_free> key_;
|
|
};
|
|
|
|
class EVPMDCtxPointer final {
|
|
public:
|
|
EVPMDCtxPointer();
|
|
explicit EVPMDCtxPointer(EVP_MD_CTX* ctx);
|
|
EVPMDCtxPointer(EVPMDCtxPointer&& other) noexcept;
|
|
EVPMDCtxPointer& operator=(EVPMDCtxPointer&& other) noexcept;
|
|
NCRYPTO_DISALLOW_COPY(EVPMDCtxPointer)
|
|
~EVPMDCtxPointer();
|
|
|
|
inline bool operator==(std::nullptr_t) noexcept { return ctx_ == nullptr; }
|
|
inline operator bool() const { return ctx_ != nullptr; }
|
|
inline EVP_MD_CTX* get() const { return ctx_.get(); }
|
|
inline operator EVP_MD_CTX*() const { return ctx_.get(); }
|
|
void reset(EVP_MD_CTX* ctx = nullptr);
|
|
EVP_MD_CTX* release();
|
|
|
|
bool digestInit(const Digest& digest);
|
|
bool digestUpdate(const Buffer<const void>& in);
|
|
DataPointer digestFinal(size_t length);
|
|
bool digestFinalInto(Buffer<void>* buf);
|
|
size_t getExpectedSize();
|
|
|
|
std::optional<EVP_PKEY_CTX*> signInit(const EVPKeyPointer& key,
|
|
const Digest& digest);
|
|
std::optional<EVP_PKEY_CTX*> verifyInit(const EVPKeyPointer& key,
|
|
const Digest& digest);
|
|
|
|
DataPointer signOneShot(const Buffer<const unsigned char>& buf) const;
|
|
DataPointer sign(const Buffer<const unsigned char>& buf) const;
|
|
bool verify(const Buffer<const unsigned char>& buf,
|
|
const Buffer<const unsigned char>& sig) const;
|
|
|
|
const EVP_MD* getDigest() const;
|
|
size_t getDigestSize() const;
|
|
bool hasXofFlag() const;
|
|
|
|
bool copyTo(const EVPMDCtxPointer& other) const;
|
|
|
|
static EVPMDCtxPointer New();
|
|
|
|
private:
|
|
DeleteFnPtr<EVP_MD_CTX, EVP_MD_CTX_free> ctx_;
|
|
};
|
|
|
|
class HMACCtxPointer final {
|
|
public:
|
|
HMACCtxPointer();
|
|
explicit HMACCtxPointer(HMAC_CTX* ctx);
|
|
HMACCtxPointer(HMACCtxPointer&& other) noexcept;
|
|
HMACCtxPointer& operator=(HMACCtxPointer&& other) noexcept;
|
|
NCRYPTO_DISALLOW_COPY(HMACCtxPointer)
|
|
~HMACCtxPointer();
|
|
|
|
inline bool operator==(std::nullptr_t) noexcept { return ctx_ == nullptr; }
|
|
inline operator bool() const { return ctx_ != nullptr; }
|
|
inline HMAC_CTX* get() const { return ctx_.get(); }
|
|
inline operator HMAC_CTX*() const { return ctx_.get(); }
|
|
void reset(HMAC_CTX* ctx = nullptr);
|
|
HMAC_CTX* release();
|
|
|
|
bool init(const Buffer<const void>& buf, const Digest& md);
|
|
bool update(const Buffer<const void>& buf);
|
|
DataPointer digest();
|
|
bool digestInto(Buffer<void>* buf);
|
|
|
|
static HMACCtxPointer New();
|
|
|
|
private:
|
|
DeleteFnPtr<HMAC_CTX, HMAC_CTX_free> ctx_;
|
|
};
|
|
|
|
#ifndef OPENSSL_NO_ENGINE
|
|
class EnginePointer final {
|
|
public:
|
|
EnginePointer() = default;
|
|
|
|
explicit EnginePointer(ENGINE* engine_, bool finish_on_exit = false);
|
|
EnginePointer(EnginePointer&& other) noexcept;
|
|
EnginePointer& operator=(EnginePointer&& other) noexcept;
|
|
NCRYPTO_DISALLOW_COPY(EnginePointer)
|
|
~EnginePointer();
|
|
|
|
inline operator bool() const { return engine != nullptr; }
|
|
inline ENGINE* get() { return engine; }
|
|
inline void setFinishOnExit() { finish_on_exit = true; }
|
|
|
|
void reset(ENGINE* engine_ = nullptr, bool finish_on_exit_ = false);
|
|
|
|
bool setAsDefault(uint32_t flags, CryptoErrorList* errors = nullptr);
|
|
bool init(bool finish_on_exit = false);
|
|
EVPKeyPointer loadPrivateKey(const char* key_name);
|
|
|
|
// Release ownership of the ENGINE* pointer.
|
|
ENGINE* release();
|
|
|
|
// Retrieve an OpenSSL Engine instance by name. If the name does not
|
|
// identify a valid named engine, the returned EnginePointer will be
|
|
// empty.
|
|
static EnginePointer getEngineByName(const char* name,
|
|
CryptoErrorList* errors = nullptr);
|
|
|
|
// Call once when initializing OpenSSL at startup for the process.
|
|
static void initEnginesOnce();
|
|
|
|
private:
|
|
ENGINE* engine = nullptr;
|
|
bool finish_on_exit = false;
|
|
};
|
|
#endif // !OPENSSL_NO_ENGINE
|
|
|
|
// ============================================================================
|
|
// FIPS
|
|
bool isFipsEnabled();
|
|
|
|
bool setFipsEnabled(bool enabled, CryptoErrorList* errors);
|
|
|
|
bool testFipsEnabled();
|
|
|
|
// ============================================================================
|
|
// Various utilities
|
|
|
|
bool CSPRNG(void* buffer, size_t length) NCRYPTO_MUST_USE_RESULT;
|
|
|
|
// This callback is used to avoid the default passphrase callback in OpenSSL
|
|
// which will typically prompt for the passphrase. The prompting is designed
|
|
// for the OpenSSL CLI, but works poorly for some environments like Node.js
|
|
// because it involves synchronous interaction with the controlling terminal,
|
|
// something we never want, and use this function to avoid it.
|
|
int NoPasswordCallback(char* buf, int size, int rwflag, void* u);
|
|
|
|
int PasswordCallback(char* buf, int size, int rwflag, void* u);
|
|
|
|
bool SafeX509SubjectAltNamePrint(const BIOPointer& out, X509_EXTENSION* ext);
|
|
bool SafeX509InfoAccessPrint(const BIOPointer& out, X509_EXTENSION* ext);
|
|
|
|
// ============================================================================
|
|
// SPKAC
|
|
|
|
bool VerifySpkac(const char* input, size_t length);
|
|
BIOPointer ExportPublicKey(const char* input, size_t length);
|
|
|
|
// The caller takes ownership of the returned Buffer<char>
|
|
Buffer<char> ExportChallenge(const char* input, size_t length);
|
|
|
|
// ============================================================================
|
|
// KDF
|
|
|
|
const EVP_MD* getDigestByName(const char* name);
|
|
const EVP_CIPHER* getCipherByName(const char* name);
|
|
|
|
// Verify that the specified HKDF output length is valid for the given digest.
|
|
// The maximum length for HKDF output for a given digest is 255 times the
|
|
// hash size for the given digest algorithm.
|
|
bool checkHkdfLength(const Digest& digest, size_t length);
|
|
|
|
bool extractP1363(const Buffer<const unsigned char>& buf,
|
|
unsigned char* dest,
|
|
size_t n);
|
|
|
|
DataPointer hkdf(const Digest& md,
|
|
const Buffer<const unsigned char>& key,
|
|
const Buffer<const unsigned char>& info,
|
|
const Buffer<const unsigned char>& salt,
|
|
size_t length);
|
|
|
|
bool checkScryptParams(uint64_t N, uint64_t r, uint64_t p, uint64_t maxmem);
|
|
|
|
DataPointer scrypt(const Buffer<const char>& pass,
|
|
const Buffer<const unsigned char>& salt,
|
|
uint64_t N,
|
|
uint64_t r,
|
|
uint64_t p,
|
|
uint64_t maxmem,
|
|
size_t length);
|
|
|
|
DataPointer pbkdf2(const Digest& md,
|
|
const Buffer<const char>& pass,
|
|
const Buffer<const unsigned char>& salt,
|
|
uint32_t iterations,
|
|
size_t length);
|
|
|
|
// ============================================================================
|
|
// Version metadata
|
|
#define NCRYPTO_VERSION "0.0.1"
|
|
|
|
enum {
|
|
NCRYPTO_VERSION_MAJOR = 0,
|
|
NCRYPTO_VERSION_MINOR = 0,
|
|
NCRYPTO_VERSION_REVISION = 1,
|
|
};
|
|
|
|
} // namespace ncrypto
|