From 7f7ba2bf289c80c59a65fa2349438762bdc32b22 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 11 Jan 2026 01:19:52 +0100 Subject: [PATCH 01/11] use code in nodejs/node --- include/dh-primes.h | 67 --- include/ncrypto.h | 392 ++++---------- src/ncrypto.cpp | 1231 ++++++++----------------------------------- 3 files changed, 321 insertions(+), 1369 deletions(-) delete mode 100644 include/dh-primes.h diff --git a/include/dh-primes.h b/include/dh-primes.h deleted file mode 100644 index 6564d77..0000000 --- a/include/dh-primes.h +++ /dev/null @@ -1,67 +0,0 @@ -/* ==================================================================== - * Copyright (c) 2011 The OpenSSL Project. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. All advertising materials mentioning features or use of this - * software must display the following acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - * - * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - * endorse or promote products derived from this software without - * prior written permission. For written permission, please contact - * licensing@OpenSSL.org. - * - * 5. Products derived from this software may not be called "OpenSSL" - * nor may "OpenSSL" appear in their names without prior written - * permission of the OpenSSL Project. - * - * 6. Redistributions of any form whatsoever must retain the following - * acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - * - * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY - * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * ==================================================================== - * - * This product includes cryptographic software written by Eric Young - * (eay@cryptsoft.com). This product includes software written by Tim - * Hudson (tjh@cryptsoft.com). */ - -#ifndef DEPS_NCRYPTO_DH_PRIMES_H_ -#define DEPS_NCRYPTO_DH_PRIMES_H_ - -#include - -// Backporting primes that may not be supported in earlier boringssl versions. -// Intentionally keeping the existing C-style formatting. - -BIGNUM* BN_get_rfc3526_prime_2048(BIGNUM* ret); -BIGNUM* BN_get_rfc3526_prime_3072(BIGNUM* ret); -BIGNUM* BN_get_rfc3526_prime_4096(BIGNUM* ret); -BIGNUM* BN_get_rfc3526_prime_6144(BIGNUM* ret); -BIGNUM* BN_get_rfc3526_prime_8192(BIGNUM* ret); - -#endif // DEPS_NCRYPTO_DH_PRIMES_H_ diff --git a/include/ncrypto.h b/include/ncrypto.h index 6a84162..5ffaee3 100644 --- a/include/ncrypto.h +++ b/include/ncrypto.h @@ -8,30 +8,17 @@ #include #include #include +#include #include #include #include - -#ifdef OPENSSL_IS_BORINGSSL -#include -#endif - -#include #include -#include #include #include #include #include #include #include -#include -#include - -#if NCRYPTO_DEVELOPMENT_CHECKS -#include -#endif - #ifndef OPENSSL_NO_ENGINE #include #endif // !OPENSSL_NO_ENGINE @@ -70,12 +57,6 @@ using OPENSSL_SIZE_T = size_t; using OPENSSL_SIZE_T = int; #endif -#ifdef OPENSSL_IS_BORINGSSL -#ifdef NCRYPTO_BSSL_NEEDS_DH_PRIMES -#include "dh-primes.h" -#endif // NCRYPTO_BSSL_NEEDS_DH_PRIMES -#endif // OPENSSL_IS_BORINGSSL - namespace ncrypto { // ============================================================================ @@ -85,7 +66,10 @@ namespace ncrypto { #define NCRYPTO_STR(x) #x #define NCRYPTO_REQUIRE(EXPR) \ { \ - if (!(EXPR) { abort(); }) } + if (!(EXPR)) { \ + abort(); \ + } \ + } #define NCRYPTO_FAIL(MESSAGE) \ do { \ @@ -262,8 +246,6 @@ class ECKeyPointer; class Dsa; class Rsa; class Ec; -class Aead; -class AeadCtxPointer; struct StackOfXASN1Deleter { void operator()(STACK_OF(ASN1_OBJECT) * p) const { @@ -318,25 +300,7 @@ DataPointer xofHashDigest(const Buffer& data, const EVP_MD* md, size_t length); -template -class ModeMixin { - public: - std::string_view getModeLabel() const; - - bool isGcmMode() const { return self().getMode() == EVP_CIPH_GCM_MODE; } - bool isWrapMode() const { return self().getMode() == EVP_CIPH_WRAP_MODE; } - bool isCtrMode() const { return self().getMode() == EVP_CIPH_CTR_MODE; } - bool isCcmMode() const { return self().getMode() == EVP_CIPH_CCM_MODE; } - bool isOcbMode() const { return self().getMode() == EVP_CIPH_OCB_MODE; } - bool isStreamMode() const { - return self().getMode() == EVP_CIPH_STREAM_CIPHER; - } - - private: - const T& self() const { return static_cast(*this); } -}; - -class Cipher final : public ModeMixin { +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; @@ -345,10 +309,9 @@ class Cipher final : public ModeMixin { #else static constexpr size_t MAX_AUTH_TAG_LENGTH = 16; #endif - // FIXME: These constants are not available in all OpenSSL/BoringSSL versions - // 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); + 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) {} @@ -369,9 +332,15 @@ class Cipher final : public ModeMixin { 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; @@ -464,78 +433,8 @@ class Dsa final { OSSL3_CONST DSA* dsa_; }; -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; - size_t bitLength() 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; - 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 bn_; - - static bool defaultPrimeCheckCallback(int, int) { return 1; } -}; +// ============================================================================ +// RSA class Rsa final { public: @@ -597,10 +496,6 @@ class Ec final { const EC_GROUP* getGroup() const; int getCurve() const; - uint32_t getDegree() const; - std::string getCurveName() const; - const EC_POINT* getPublicKey() const; - const BIGNUM* getPrivateKey() const; inline operator bool() const { return ec_ != nullptr; } inline operator OSSL3_CONST EC_KEY*() const { return ec_; } @@ -610,16 +505,8 @@ class Ec final { using GetCurveCallback = std::function; static bool GetCurves(GetCurveCallback callback); - inline const BignumPointer& getX() const { return x_; } - inline const BignumPointer& getY() const { return y_; } - inline const BignumPointer& getD() const { return d_; } - private: OSSL3_CONST EC_KEY* ec_ = nullptr; - // Affine coordinates for the EC_KEY. - BignumPointer x_; - BignumPointer y_; - BignumPointer d_; }; // A managed pointer to a buffer of data. When destroyed the underlying @@ -750,6 +637,78 @@ class BIOPointer final { mutable DeleteFnPtr 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; + 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 bn_; + + static bool defaultPrimeCheckCallback(int, int) { return 1; } +}; + class CipherCtxPointer final { public: static CipherCtxPointer New(); @@ -991,15 +950,12 @@ class EVPKeyPointer final { int getDefaultSignPadding() const; operator Rsa() const; operator Dsa() const; - operator Ec() const; bool isRsaVariant() const; bool isOneShotVariant() const; bool isSigVariant() const; bool validateDsaParameters() const; - EVPKeyPointer clone() const; - private: DeleteFnPtr pkey_; }; @@ -1628,19 +1584,11 @@ bool SafeX509InfoAccessPrint(const BIOPointer& out, X509_EXTENSION* ext); // ============================================================================ // SPKAC -[[deprecated("Use the version that takes a Buffer")]] bool VerifySpkac( - const char* input, size_t length); - -[[deprecated("Use the version that takes a Buffer")]] BIOPointer -ExportPublicKey(const char* input, size_t length); +bool VerifySpkac(const char* input, size_t length); +BIOPointer ExportPublicKey(const char* input, size_t length); // The caller takes ownership of the returned Buffer -[[deprecated("Use the version that takes a Buffer")]] Buffer -ExportChallenge(const char* input, size_t length); - -bool VerifySpkac(const Buffer& buf); -BIOPointer ExportPublicKey(const Buffer& buf); -DataPointer ExportChallenge(const Buffer& buf); +Buffer ExportChallenge(const char* input, size_t length); // ============================================================================ // KDF @@ -1657,13 +1605,6 @@ bool extractP1363(const Buffer& buf, unsigned char* dest, size_t n); -bool hkdfInfo(const Digest& md, - const Buffer& key, - const Buffer& info, - const Buffer& salt, - size_t length, - Buffer* out); - DataPointer hkdf(const Digest& md, const Buffer& key, const Buffer& info, @@ -1672,15 +1613,6 @@ DataPointer hkdf(const Digest& md, bool checkScryptParams(uint64_t N, uint64_t r, uint64_t p, uint64_t maxmem); -bool scryptInto(const Buffer& pass, - const Buffer& salt, - uint64_t N, - uint64_t r, - uint64_t p, - uint64_t maxmem, - size_t length, - Buffer* out); - DataPointer scrypt(const Buffer& pass, const Buffer& salt, uint64_t N, @@ -1689,13 +1621,6 @@ DataPointer scrypt(const Buffer& pass, uint64_t maxmem, size_t length); -bool pbkdf2Into(const Digest& md, - const Buffer& pass, - const Buffer& salt, - uint32_t iterations, - size_t length, - Buffer* out); - DataPointer pbkdf2(const Digest& md, const Buffer& pass, const Buffer& salt, @@ -1753,137 +1678,6 @@ class KEM final { #endif // OPENSSL_VERSION_MAJOR >= 3 -// ============================================================================ -// AEAD (Authenticated Encryption with Associated Data) -// Note that the underlying EVP_AEAD interface is specific to BoringSSL. AEAD -// primitives are accessed through the Cipher class instead, if using OpenSSL. - -#ifdef OPENSSL_IS_BORINGSSL -class Aead final : public ModeMixin { - private: - // BoringSSL does not keep a list of AEADs, so we need to maintain our own. - struct AeadInfo { - std::string name; - int mode; - int nid = 0; // Note: BoringSSL only defines NIDs for some AEADs - }; - - public: - Aead() = default; - Aead(const AeadInfo* info, const EVP_AEAD* aead) : info_(info), aead_(aead) {} - Aead(const Aead&) = default; - Aead& operator=(const Aead&) = default; - NCRYPTO_DISALLOW_MOVE(Aead) - - inline const EVP_AEAD* get() const { return aead_; } - inline operator const EVP_AEAD*() const { return aead_; } - inline operator bool() const { return aead_ != nullptr; } - - int getMode() const; - int getNonceLength() const; - int getKeyLength() const; - int getBlockSize() const; - int getMaxOverhead() const; - int getMaxTagLength() const; - std::string_view getName() const; - - static const Aead FromName(std::string_view name); - - // TODO(npaun): BoringSSL does not define NIDs for all AEADs. - // This method is included only for implementing getCipherInfo and can't be - // used to construct an Aead instance. - int getNid() const; - // static const AEAD FromNid(int nid); - - static const Aead FromCtx(std::string_view name, const AeadCtxPointer& ctx); - - using AeadNameCallback = std::function; - - // Iterates the known ciphers if the underlying implementation - // is able to do so. - static void ForEach(AeadNameCallback callback); - - // Utilities to get various AEADs by type. - - static const Aead EMPTY; - static const Aead AES_128_GCM; - static const Aead AES_192_GCM; - static const Aead AES_256_GCM; - static const Aead CHACHA20_POLY1305; - static const Aead XCHACHA20_POLY1305; - static const Aead AES_128_CTR_HMAC_SHA256; - static const Aead AES_256_CTR_HMAC_SHA256; - static const Aead AES_128_GCM_SIV; - static const Aead AES_256_GCM_SIV; - static const Aead AES_128_GCM_RANDNONCE; - static const Aead AES_256_GCM_RANDNONCE; - static const Aead AES_128_CCM_BLUETOOTH; - static const Aead AES_128_CCM_BLUETOOTH_8; - static const Aead AES_128_CCM_MATTER; - static const Aead AES_128_EAX; - static const Aead AES_256_EAX; - - private: - const EVP_AEAD* aead_ = nullptr; - const AeadInfo* info_ = nullptr; - - using AeadConstructor = const EVP_AEAD* (*)(); - static const std::unordered_map aeadIndex; - static const Aead FromConstructor(AeadConstructor construct); -}; - -class AeadCtxPointer final { - public: - static AeadCtxPointer New( - const Aead& aead, - bool encrypt, - const unsigned char* key = nullptr, - size_t keyLen = 0, - size_t tagLen = EVP_AEAD_DEFAULT_TAG_LENGTH /* = 0 */); - - AeadCtxPointer() = default; - explicit AeadCtxPointer(EVP_AEAD_CTX* ctx); - AeadCtxPointer(AeadCtxPointer&& other) noexcept; - AeadCtxPointer& operator=(AeadCtxPointer&& other) noexcept; - NCRYPTO_DISALLOW_COPY(AeadCtxPointer) - ~AeadCtxPointer(); - - inline bool operator==(std::nullptr_t) const noexcept { - return ctx_ == nullptr; - } - inline operator bool() const { return ctx_ != nullptr; } - inline EVP_AEAD_CTX* get() const { return ctx_.get(); } - inline operator EVP_AEAD_CTX*() const { return ctx_.get(); } - void reset(EVP_AEAD_CTX* ctx = nullptr); - EVP_AEAD_CTX* release(); - - bool init(const Aead& aead, - bool encrypt, - const unsigned char* key = nullptr, - size_t keyLen = 0, - size_t tagLen = EVP_AEAD_DEFAULT_TAG_LENGTH /* = 0 */); - - // TODO(npaun): BoringSSL does not define NIDs for all AEADs. - // Decide if we will even implement this method. - // int getNid() const; - - bool encrypt(const Buffer& in, - Buffer& out, - Buffer& tag, - const Buffer& nonce, - const Buffer& aad); - - bool decrypt(const Buffer& in, - Buffer& out, - const Buffer& tag, - const Buffer& nonce, - const Buffer& aad); - - private: - DeleteFnPtr ctx_; -}; -#endif - // ============================================================================ // Version metadata #define NCRYPTO_VERSION "0.0.1" diff --git a/src/ncrypto.cpp b/src/ncrypto.cpp index f482c53..461819c 100644 --- a/src/ncrypto.cpp +++ b/src/ncrypto.cpp @@ -1,5 +1,4 @@ #include "ncrypto.h" - #include #include #include @@ -8,22 +7,10 @@ #include #include #include -#include "openssl/cipher.h" - -#ifndef NCRYPTO_NO_KDF_H -#include -#else -#include -#endif - #include #include -#include -#include #include #include -#include - #if OPENSSL_VERSION_MAJOR >= 3 #include #include @@ -72,8 +59,6 @@ constexpr static PQCMapping pqc_mappings[] = { nullptr) #endif -// ============================================================================ - namespace ncrypto { namespace { using BignumCtxPointer = DeleteFnPtr; @@ -262,7 +247,7 @@ Buffer DataPointer::release() { DataPointer DataPointer::resize(size_t len) { size_t actual_len = std::min(len_, len); auto buf = release(); - if (actual_len == len_) return DataPointer(buf); + if (actual_len == len_) return DataPointer(buf.data, actual_len); buf.data = OPENSSL_realloc(buf.data, actual_len); buf.len = actual_len; return DataPointer(buf); @@ -303,7 +288,7 @@ bool testFipsEnabled() { #else #ifdef OPENSSL_FIPS return FIPS_selftest(); -#else // OPENSSL_FIPS +#else // OPENSSL_FIPS return false; #endif // OPENSSL_FIPS #endif @@ -355,15 +340,10 @@ BIGNUM* BignumPointer::release() { } size_t BignumPointer::byteLength() const { - if (!bn_) return 0; + if (bn_ == nullptr) return 0; return BN_num_bytes(bn_.get()); } -size_t BignumPointer::bitLength() const { - if (!bn_) return 0; - return BN_num_bits(bn_.get()); -} - DataPointer BignumPointer::encode() const { return EncodePadded(bn_.get(), byteLength()); } @@ -473,11 +453,8 @@ int BignumPointer::isPrime(int nchecks, // TODO(@jasnell): This could be refactored to allow inlining. // Not too important right now tho. [](int a, int b, BN_GENCB* ctx) mutable -> int { - PrimeCheckCallback& ptr = *static_cast(ctx->arg); - // Newer versions of openssl and boringssl define the BN_GENCB_get_arg - // API which is what is supposed to be used here. Older versions, - // however, omit that API. - // *static_cast(BN_GENCB_get_arg(ctx)); + PrimeCheckCallback& ptr = + *static_cast(BN_GENCB_get_arg(ctx)); return ptr(a, b) ? 1 : 0; }, &innerCb); @@ -507,11 +484,8 @@ bool BignumPointer::generate(const PrimeConfig& params, BN_GENCB_set( cb.get(), [](int a, int b, BN_GENCB* ctx) mutable -> int { - PrimeCheckCallback& ptr = *static_cast(ctx->arg); - // Newer versions of openssl and boringssl define the BN_GENCB_get_arg - // API which is what is supposed to be used here. Older versions, - // however, omit that API. - // *static_cast(BN_GENCB_get_arg(ctx)); + PrimeCheckCallback& ptr = + *static_cast(BN_GENCB_get_arg(ctx)); return ptr(a, b) ? 1 : 0; }, &innerCb); @@ -640,9 +614,7 @@ int64_t PortableTimeGM(struct tm* t) { // ============================================================================ // SPKAC -namespace { -bool VerifySpkacImpl(const char* input, size_t length) { - ClearErrorOnReturn clearErrorOnReturn; +bool VerifySpkac(const char* input, size_t length) { #ifdef OPENSSL_IS_BORINGSSL // OpenSSL uses EVP_DecodeBlock, which explicitly removes trailing characters, // while BoringSSL uses EVP_DecodedLength and EVP_DecodeBase64, which do not. @@ -660,11 +632,9 @@ bool VerifySpkacImpl(const char* input, size_t length) { return pkey ? NETSCAPE_SPKI_verify(spki.get(), pkey.get()) > 0 : false; } -BIOPointer ExportPublicKeyImpl(const char* input, size_t length) { - ClearErrorOnReturn clearErrorOnReturn; - auto bio = BIOPointer::NewMem(); - if (!bio) [[unlikely]] - return {}; +BIOPointer ExportPublicKey(const char* input, size_t length) { + BIOPointer bio(BIO_new(BIO_s_mem())); + if (!bio) return {}; #ifdef OPENSSL_IS_BORINGSSL // OpenSSL uses EVP_DecodeBlock, which explicitly removes trailing characters, @@ -673,21 +643,17 @@ BIOPointer ExportPublicKeyImpl(const char* input, size_t length) { length = std::string_view(input, length).find_last_not_of(" \n\r\t") + 1; #endif NetscapeSPKIPointer spki(NETSCAPE_SPKI_b64_decode(input, length)); - if (!spki) [[unlikely]] { - return {}; - } + if (!spki) return {}; EVPKeyPointer pkey(NETSCAPE_SPKI_get_pubkey(spki.get())); + if (!pkey) return {}; - if (!pkey || PEM_write_bio_PUBKEY(bio.get(), pkey.get()) <= 0) [[unlikely]] { - return {}; - } + if (PEM_write_bio_PUBKEY(bio.get(), pkey.get()) <= 0) return {}; return bio; } -DataPointer ExportChallengeImpl(const char* input, size_t length) { - ClearErrorOnReturn clearErrorOnReturn; +Buffer ExportChallenge(const char* input, size_t length) { #ifdef OPENSSL_IS_BORINGSSL // OpenSSL uses EVP_DecodeBlock, which explicitly removes trailing characters, // while BoringSSL uses EVP_DecodedLength and EVP_DecodeBase64, which do not. @@ -700,44 +666,12 @@ DataPointer ExportChallengeImpl(const char* input, size_t length) { unsigned char* buf = nullptr; int buf_size = ASN1_STRING_to_UTF8(&buf, sp->spkac->challenge); if (buf_size >= 0) { - return DataPointer({ + return { .data = reinterpret_cast(buf), .len = static_cast(buf_size), - }); - } - - return {}; -} -} // namespace - -bool VerifySpkac(const Buffer& input) { - return VerifySpkacImpl(input.data, input.len); -} - -BIOPointer ExportPublicKey(const Buffer& input) { - return ExportPublicKeyImpl(input.data, input.len); -} - -DataPointer ExportChallenge(const Buffer& input) { - return ExportChallengeImpl(input.data, input.len); -} - -bool VerifySpkac(const char* input, size_t length) { - return VerifySpkacImpl(input, length); -} - -BIOPointer ExportPublicKey(const char* input, size_t length) { - return ExportPublicKeyImpl(input, length); -} - -Buffer ExportChallenge(const char* input, size_t length) { - if (auto dp = ExportChallengeImpl(input, length)) { - auto released = dp.release(); - return Buffer{ - .data = static_cast(released.data), - .len = released.len, }; } + return {}; } @@ -1187,7 +1121,6 @@ std::optional X509View::getSignatureAlgorithmOID() const { int64_t X509View::getValidToTime() const { #ifdef OPENSSL_IS_BORINGSSL -#ifndef NCRYPTO_NO_ASN1_TIME // Boringssl does not implement ASN1_TIME_to_tm in a public way, // and only recently added ASN1_TIME_to_posix. Some boringssl // users on older version may still need to patch around this @@ -1196,11 +1129,6 @@ int64_t X509View::getValidToTime() const { ASN1_TIME_to_posix(X509_get0_notAfter(cert_), &tp); return tp; #else - // Older versions of Boringssl do not implement the ASN1_TIME_to_* - // version functions. For now, neither shall we. - return 0LL; -#endif // NCRYPTO_NO_ASN1_TIME -#else // OPENSSL_IS_BORINGSSL struct tm tp; ASN1_TIME_to_tm(X509_get0_notAfter(cert_), &tp); return PortableTimeGM(&tp); @@ -1209,15 +1137,9 @@ int64_t X509View::getValidToTime() const { int64_t X509View::getValidFromTime() const { #ifdef OPENSSL_IS_BORINGSSL -#ifndef NCRYPTO_NO_ASN1_TIME int64_t tp; ASN1_TIME_to_posix(X509_get0_notBefore(cert_), &tp); return tp; -#else - // Older versions of Boringssl do not implement the ASN1_TIME_to_* - // version functions. For now, neither shall we. - return 0LL; -#endif // NCRYPTO_NO_ASN1_TIME #else struct tm tp; ASN1_TIME_to_tm(X509_get0_notBefore(cert_), &tp); @@ -1411,16 +1333,10 @@ bool X509View::enumUsages(UsageCallback callback) const { bool X509View::ifRsa(KeyCallback callback) const { if (cert_ == nullptr) return true; - // The const_cast is a bit unfortunate. The X509_get_pubkey API accepts - // a const X509* in newer versions of openssl and boringssl but a non-const - // X509* in older versions. By removing the const if it exists we can - // support both. - EVPKeyPointer pkey(X509_get_pubkey(const_cast(cert_))); - if (!pkey) [[unlikely]] - return true; - auto id = pkey.id(); + OSSL3_CONST EVP_PKEY* pkey = X509_get0_pubkey(cert_); + auto id = EVP_PKEY_id(pkey); if (id == EVP_PKEY_RSA || id == EVP_PKEY_RSA2 || id == EVP_PKEY_RSA_PSS) { - Rsa rsa = pkey; + Rsa rsa(EVP_PKEY_get0_RSA(pkey)); if (!rsa) [[unlikely]] return true; return callback(rsa); @@ -1430,16 +1346,10 @@ bool X509View::ifRsa(KeyCallback callback) const { bool X509View::ifEc(KeyCallback callback) const { if (cert_ == nullptr) return true; - // The const_cast is a bit unfortunate. The X509_get_pubkey API accepts - // a const X509* in newer versions of openssl and boringssl but a non-const - // X509* in older versions. By removing the const if it exists we can - // support both. - EVPKeyPointer pkey(X509_get_pubkey(const_cast(cert_))); - if (!pkey) [[unlikely]] - return true; - auto id = pkey.id(); + OSSL3_CONST EVP_PKEY* pkey = X509_get0_pubkey(cert_); + auto id = EVP_PKEY_id(pkey); if (id == EVP_PKEY_EC) { - Ec ec = pkey; + Ec ec(EVP_PKEY_get0_EC_KEY(pkey)); if (!ec) [[unlikely]] return true; return callback(ec); @@ -1873,28 +1783,18 @@ bool checkHkdfLength(const Digest& md, size_t length) { return true; } -bool hkdfInfo(const Digest& md, - const Buffer& key, - const Buffer& info, - const Buffer& salt, - size_t length, - Buffer* out) { +DataPointer hkdf(const Digest& md, + const Buffer& key, + const Buffer& info, + const Buffer& salt, + size_t length) { ClearErrorOnReturn clearErrorOnReturn; if (!checkHkdfLength(md, length) || info.len > INT_MAX || salt.len > INT_MAX) { - return false; - } - - std::string_view actual_salt; - static const char default_salt[EVP_MAX_MD_SIZE] = {0}; - if (salt.len > 0) { - actual_salt = {reinterpret_cast(salt.data), salt.len}; - } else { - actual_salt = {default_salt, static_cast(md.size())}; + return {}; } -#ifndef NCRYPTO_NO_KDF_H auto ctx = EVPKeyCtxPointer::NewFromID(EVP_PKEY_HKDF); // OpenSSL < 3.0.0 accepted only a void* as the argument of // EVP_PKEY_CTX_set_hkdf_md. @@ -1902,14 +1802,22 @@ bool hkdfInfo(const Digest& md, if (!ctx || !EVP_PKEY_derive_init(ctx.get()) || !EVP_PKEY_CTX_set_hkdf_md(ctx.get(), md_ptr) || !EVP_PKEY_CTX_add1_hkdf_info(ctx.get(), info.data, info.len)) { - return false; + return {}; + } + + std::string_view actual_salt; + static const char default_salt[EVP_MAX_MD_SIZE] = {0}; + if (salt.len > 0) { + actual_salt = {reinterpret_cast(salt.data), salt.len}; + } else { + actual_salt = {default_salt, static_cast(md.size())}; } // We do not use EVP_PKEY_HKDF_MODE_EXTRACT_AND_EXPAND because and instead // implement the extraction step ourselves because EVP_PKEY_derive does not // handle zero-length keys, which are required for Web Crypto. // TODO(jasnell): Once OpenSSL 1.1.1 support is dropped completely, and once - // BoringSSL is confirmed to support it, we can hopefully drop this and use + // BoringSSL is confirmed to support it, wen can hopefully drop this and use // EVP_KDF directly which does support zero length keys. unsigned char pseudorandom_key[EVP_MAX_MD_SIZE]; unsigned pseudorandom_key_len = sizeof(pseudorandom_key); @@ -1921,41 +1829,23 @@ bool hkdfInfo(const Digest& md, key.len, pseudorandom_key, &pseudorandom_key_len) == nullptr) { - return false; + return {}; } if (!EVP_PKEY_CTX_hkdf_mode(ctx.get(), EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) || !EVP_PKEY_CTX_set1_hkdf_key( ctx.get(), pseudorandom_key, pseudorandom_key_len)) { - return false; + return {}; } - if (out == nullptr || out->len != length) return false; - - return EVP_PKEY_derive(ctx.get(), out->data, &length) > 0; -#else - return HKDF(out->data, - length, - md, - key.data, - key.len, - salt.data, - salt.len, - info.data, - info.len); -#endif -} - -DataPointer hkdf(const Digest& md, - const Buffer& key, - const Buffer& info, - const Buffer& salt, - size_t length) { auto buf = DataPointer::Alloc(length); if (!buf) return {}; - Buffer out = buf; - return hkdfInfo(md, key, info, salt, length, &out) ? std::move(buf) - : DataPointer(); + if (EVP_PKEY_derive( + ctx.get(), static_cast(buf.get()), &length) <= 0) { + return {}; + } + + return buf; } bool checkScryptParams(uint64_t N, uint64_t r, uint64_t p, uint64_t maxmem) { @@ -1963,36 +1853,6 @@ bool checkScryptParams(uint64_t N, uint64_t r, uint64_t p, uint64_t maxmem) { 1; } -bool scryptInto(const Buffer& pass, - const Buffer& salt, - uint64_t N, - uint64_t r, - uint64_t p, - uint64_t maxmem, - size_t length, - Buffer* out) { - ClearErrorOnReturn clearErrorOnReturn; - - if (pass.len > INT_MAX || salt.len > INT_MAX || out == nullptr) { - return false; - } - - if (auto dp = DataPointer::Alloc(length)) { - return EVP_PBE_scrypt(pass.data, - pass.len, - salt.data, - salt.len, - N, - r, - p, - maxmem, - out->data, - length); - } - - return false; -} - DataPointer scrypt(const Buffer& pass, const Buffer& salt, uint64_t N, @@ -2000,42 +1860,27 @@ DataPointer scrypt(const Buffer& pass, uint64_t p, uint64_t maxmem, size_t length) { - if (auto dp = DataPointer::Alloc(length)) { - Buffer buf = dp; - if (scryptInto(pass, salt, N, r, p, maxmem, length, &buf)) { - return dp; - } - } - - return {}; -} - -bool pbkdf2Into(const Digest& md, - const Buffer& pass, - const Buffer& salt, - uint32_t iterations, - size_t length, - Buffer* out) { ClearErrorOnReturn clearErrorOnReturn; - if (pass.len > INT_MAX || salt.len > INT_MAX || length > INT_MAX || - out == nullptr) { - return false; + if (pass.len > INT_MAX || salt.len > INT_MAX) { + return {}; } - const EVP_MD* md_ptr = md; - if (PKCS5_PBKDF2_HMAC(pass.data, - pass.len, - salt.data, - salt.len, - iterations, - md_ptr, - length, - out->data)) { - return true; + auto dp = DataPointer::Alloc(length); + if (dp && EVP_PBE_scrypt(pass.data, + pass.len, + salt.data, + salt.len, + N, + r, + p, + maxmem, + reinterpret_cast(dp.get()), + length)) { + return dp; } - return false; + return {}; } DataPointer pbkdf2(const Digest& md, @@ -2043,11 +1888,23 @@ DataPointer pbkdf2(const Digest& md, const Buffer& salt, uint32_t iterations, size_t length) { - if (auto dp = DataPointer::Alloc(length)) { - Buffer buf = dp; - if (pbkdf2Into(md, pass, salt, iterations, length, &buf)) { - return dp; - } + ClearErrorOnReturn clearErrorOnReturn; + + if (pass.len > INT_MAX || salt.len > INT_MAX || length > INT_MAX) { + return {}; + } + + auto dp = DataPointer::Alloc(length); + const EVP_MD* md_ptr = md; + if (dp && PKCS5_PBKDF2_HMAC(pass.data, + pass.len, + salt.data, + salt.len, + iterations, + md_ptr, + length, + reinterpret_cast(dp.get()))) { + return dp; } return {}; @@ -2220,7 +2077,6 @@ EVPKeyPointer EVPKeyPointer::NewRawSeed( #endif EVPKeyPointer EVPKeyPointer::NewDH(DHPointer&& dh) { -#ifndef NCRYPTO_NO_EVP_DH if (!dh) return {}; auto key = New(); if (!key) return {}; @@ -2228,11 +2084,6 @@ EVPKeyPointer EVPKeyPointer::NewDH(DHPointer&& dh) { dh.release(); } return key; -#else - // Older versions of openssl/boringssl do not implement the EVP_PKEY_*_DH - // APIs - return {}; -#endif } EVPKeyPointer EVPKeyPointer::NewRSA(RSAPointer&& rsa) { @@ -2250,12 +2101,6 @@ EVPKeyPointer::EVPKeyPointer(EVP_PKEY* pkey) : pkey_(pkey) {} EVPKeyPointer::EVPKeyPointer(EVPKeyPointer&& other) noexcept : pkey_(other.release()) {} -EVPKeyPointer EVPKeyPointer::clone() const { - if (!pkey_) return {}; - if (!EVP_PKEY_up_ref(pkey_.get())) return {}; - return EVPKeyPointer(pkey_.get()); -} - EVPKeyPointer& EVPKeyPointer::operator=(EVPKeyPointer&& other) noexcept { if (this == &other) return *this; this->~EVPKeyPointer(); @@ -2604,7 +2449,6 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey( ERR_GET_REASON(err) == PEM_R_BAD_PASSWORD_READ && !had_passphrase) { return ParseKeyResult(PKParseError::NEED_PASSPHRASE); } - return ParseKeyResult(PKParseError::FAILED, err); } if (!pkey) return ParseKeyResult(PKParseError::FAILED); @@ -2680,8 +2524,11 @@ Result EVPKeyPointer::writePrivateKey( // PKCS1 is only permitted for RSA keys. if (id() != EVP_PKEY_RSA) return Result(false); - OSSL3_CONST RSA* rsa = EVP_PKEY_get0_RSA(get()); - +#if OPENSSL_VERSION_MAJOR >= 3 + const RSA* rsa = EVP_PKEY_get0_RSA(get()); +#else + RSA* rsa = EVP_PKEY_get0_RSA(get()); +#endif switch (config.format) { case PKFormatType::PEM: { err = PEM_write_bio_RSAPrivateKey( @@ -2740,8 +2587,11 @@ Result EVPKeyPointer::writePrivateKey( // SEC1 is only permitted for EC keys if (id() != EVP_PKEY_EC) return Result(false); - OSSL3_CONST EC_KEY* ec = EVP_PKEY_get0_EC_KEY(get()); - +#if OPENSSL_VERSION_MAJOR >= 3 + const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(get()); +#else + EC_KEY* ec = EVP_PKEY_get0_EC_KEY(get()); +#endif switch (config.format) { case PKFormatType::PEM: { err = PEM_write_bio_ECPrivateKey( @@ -2917,15 +2767,6 @@ EVPKeyPointer::operator Dsa() const { return Dsa(dsa); } -EVPKeyPointer::operator Ec() const { - int type = id(); - if (type != EVP_PKEY_EC) return {}; - - OSSL3_CONST EC_KEY* ec = EVP_PKEY_get0_EC_KEY(get()); - if (ec == nullptr) return {}; - return Ec(ec); -} - bool EVPKeyPointer::validateDsaParameters() const { if (!pkey_) return false; /* Validate DSA2 parameters from FIPS 186-4 */ @@ -3208,13 +3049,7 @@ SSLCtxPointer SSLCtxPointer::New(const SSL_METHOD* method) { } bool SSLCtxPointer::setGroups(const char* groups) { -#ifndef NCRYPTO_NO_SSL_GROUP_LIST return SSL_CTX_set1_groups_list(get(), groups) == 1; -#else - // Older versions of openssl/boringssl do not implement the - // SSL_CTX_set1_groups_list API - return false; -#endif } bool SSLCtxPointer::setCipherSuites(const char* ciphers) { @@ -3227,36 +3062,6 @@ bool SSLCtxPointer::setCipherSuites(const char* ciphers) { return true; #endif } -// ============================================================================ - -template -std::string_view ModeMixin::getModeLabel() const { - switch (self().getMode()) { - case EVP_CIPH_CCM_MODE: - return "ccm"; - case EVP_CIPH_CFB_MODE: - return "cfb"; - case EVP_CIPH_CBC_MODE: - return "cbc"; - case EVP_CIPH_CTR_MODE: - return "ctr"; - case EVP_CIPH_ECB_MODE: - return "ecb"; - case EVP_CIPH_GCM_MODE: - return "gcm"; - case EVP_CIPH_OCB_MODE: - return "ocb"; - case EVP_CIPH_OFB_MODE: - return "ofb"; - case EVP_CIPH_WRAP_MODE: - return "wrap"; - case EVP_CIPH_XTS_MODE: - return "xts"; - case EVP_CIPH_STREAM_CIPHER: - return "stream"; - } - return "{unknown}"; -} // ============================================================================ @@ -3285,22 +3090,48 @@ const Cipher Cipher::AES_256_GCM = Cipher::FromNid(NID_aes_256_gcm); const Cipher Cipher::AES_128_KW = Cipher::FromNid(NID_id_aes128_wrap); const Cipher Cipher::AES_192_KW = Cipher::FromNid(NID_id_aes192_wrap); const Cipher Cipher::AES_256_KW = Cipher::FromNid(NID_id_aes256_wrap); - -#ifndef OPENSSL_IS_BORINGSSL const Cipher Cipher::AES_128_OCB = Cipher::FromNid(NID_aes_128_ocb); const Cipher Cipher::AES_192_OCB = Cipher::FromNid(NID_aes_192_ocb); const Cipher Cipher::AES_256_OCB = Cipher::FromNid(NID_aes_256_ocb); -#endif - const Cipher Cipher::CHACHA20_POLY1305 = Cipher::FromNid(NID_chacha20_poly1305); +bool Cipher::isGcmMode() const { + if (!cipher_) return false; + return getMode() == EVP_CIPH_GCM_MODE; +} + +bool Cipher::isWrapMode() const { + if (!cipher_) return false; + return getMode() == EVP_CIPH_WRAP_MODE; +} + +bool Cipher::isCtrMode() const { + if (!cipher_) return false; + return getMode() == EVP_CIPH_CTR_MODE; +} + +bool Cipher::isCcmMode() const { + if (!cipher_) return false; + return getMode() == EVP_CIPH_CCM_MODE; +} + +bool Cipher::isOcbMode() const { + if (!cipher_) return false; + return getMode() == EVP_CIPH_OCB_MODE; +} + +bool Cipher::isStreamMode() const { + if (!cipher_) return false; + return getMode() == EVP_CIPH_STREAM_CIPHER; +} + bool Cipher::isChaCha20Poly1305() const { if (!cipher_) return false; return getNid() == NID_chacha20_poly1305; } int Cipher::getMode() const { - if (!cipher_) return -1; + if (!cipher_) return 0; return EVP_CIPHER_mode(cipher_); } @@ -3324,6 +3155,35 @@ int Cipher::getNid() const { return EVP_CIPHER_nid(cipher_); } +std::string_view Cipher::getModeLabel() const { + if (!cipher_) return {}; + switch (getMode()) { + case EVP_CIPH_CCM_MODE: + return "ccm"; + case EVP_CIPH_CFB_MODE: + return "cfb"; + case EVP_CIPH_CBC_MODE: + return "cbc"; + case EVP_CIPH_CTR_MODE: + return "ctr"; + case EVP_CIPH_ECB_MODE: + return "ecb"; + case EVP_CIPH_GCM_MODE: + return "gcm"; + case EVP_CIPH_OCB_MODE: + return "ocb"; + case EVP_CIPH_OFB_MODE: + return "ofb"; + case EVP_CIPH_WRAP_MODE: + return "wrap"; + case EVP_CIPH_XTS_MODE: + return "xts"; + case EVP_CIPH_STREAM_CIPHER: + return "stream"; + } + return "{unknown}"; +} + const char* Cipher::getName() const { if (!cipher_) return {}; // OBJ_nid2sn(EVP_CIPHER_nid(cipher)) is used here instead of @@ -3354,76 +3214,6 @@ int Cipher::bytesToKey(const Digest& digest, *this, Digest::MD5, nullptr, input.data, input.len, 1, key, iv); } -template class ModeMixin; - -namespace { -struct CipherCallbackContext { - Cipher::CipherNameCallback cb; - void operator()(const char* name) { cb(name); } -}; - -#if OPENSSL_VERSION_MAJOR >= 3 -template -void array_push_back(const TypeName* evp_ref, - const char* from, - const char* to, - void* arg) { - if (from == nullptr) return; - - const TypeName* real_instance = getbyname(from); - if (!real_instance) return; - - const char* real_name = getname(real_instance); - if (!real_name) return; - - // EVP_*_fetch() does not support alias names, so we need to pass it the - // real/original algorithm name. - // We use EVP_*_fetch() as a filter here because it will only return an - // instance if the algorithm is supported by the public OpenSSL APIs (some - // algorithms are used internally by OpenSSL and are also passed to this - // callback). - TypeName* fetched = fetch_type(nullptr, real_name, nullptr); - if (fetched == nullptr) return; - - free_type(fetched); - auto& cb = *(static_cast(arg)); - cb(from); -} -#else -template -void array_push_back(const TypeName* evp_ref, - const char* from, - const char* to, - void* arg) { - if (!from) return; - auto& cb = *(static_cast(arg)); - cb(from); -} -#endif -} // namespace - -void Cipher::ForEach(Cipher::CipherNameCallback callback) { - ClearErrorOnReturn clearErrorOnReturn; - CipherCallbackContext context; - context.cb = std::move(callback); - - EVP_CIPHER_do_all_sorted( -#if OPENSSL_VERSION_MAJOR >= 3 - array_push_back, -#else - array_push_back, -#endif - &context); -} - // ============================================================================ CipherCtxPointer CipherCtxPointer::New() { @@ -3500,11 +3290,6 @@ int CipherCtxPointer::getMode() const { return EVP_CIPHER_CTX_mode(ctx_.get()); } -int CipherCtxPointer::getNid() const { - if (!ctx_) return 0; - return EVP_CIPHER_CTX_nid(ctx_.get()); -} - bool CipherCtxPointer::isGcmMode() const { if (!ctx_) return false; return getMode() == EVP_CIPH_GCM_MODE; @@ -3530,6 +3315,11 @@ bool CipherCtxPointer::isChaCha20Poly1305() const { return getNid() == NID_chacha20_poly1305; } +int CipherCtxPointer::getNid() const { + if (!ctx_) return 0; + return EVP_CIPHER_CTX_nid(ctx_.get()); +} + bool CipherCtxPointer::init(const Cipher& cipher, bool encrypt, const unsigned char* key, @@ -3876,7 +3666,6 @@ bool EVPKeyCtxPointer::setDhParameters(int prime_size, uint32_t generator) { bool EVPKeyCtxPointer::setDsaParameters(uint32_t bits, std::optional q_bits) { -#ifndef NCRYPTO_NO_DSA_KEYGEN if (!ctx_) return false; if (EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx_.get(), bits) != 1) { return false; @@ -3886,10 +3675,6 @@ bool EVPKeyCtxPointer::setDsaParameters(uint32_t bits, return false; } return true; -#else - // Older versions of openssl/boringssl do not implement the DSA keygen. - return false; -#endif } bool EVPKeyCtxPointer::setEcParameters(int curve, int encoding) { @@ -4230,14 +4015,9 @@ const std::optional Rsa::getPssParams() const { } if (params->saltLength != nullptr) { - // Older versions of openssl/boringssl do not implement - // ASN1_INTEGER_get_int64, which the salt length here technically - // is. Let's walk it through uint64_t with a conversion. - uint64_t temp; - if (ASN1_INTEGER_get_uint64(&temp, params->saltLength) != 1) { + if (ASN1_INTEGER_get_int64(&ret.salt_length, params->saltLength) != 1) { return std::nullopt; } - ret.salt_length = static_cast(temp); } return ret; } @@ -4322,18 +4102,79 @@ DataPointer Cipher::recover(const EVPKeyPointer& key, key, params, in); } +namespace { +struct CipherCallbackContext { + Cipher::CipherNameCallback cb; + void operator()(const char* name) { cb(name); } +}; + +#if OPENSSL_VERSION_MAJOR >= 3 +template +void array_push_back(const TypeName* evp_ref, + const char* from, + const char* to, + void* arg) { + if (from == nullptr) return; + + const TypeName* real_instance = getbyname(from); + if (!real_instance) return; + + const char* real_name = getname(real_instance); + if (!real_name) return; + + // EVP_*_fetch() does not support alias names, so we need to pass it the + // real/original algorithm name. + // We use EVP_*_fetch() as a filter here because it will only return an + // instance if the algorithm is supported by the public OpenSSL APIs (some + // algorithms are used internally by OpenSSL and are also passed to this + // callback). + TypeName* fetched = fetch_type(nullptr, real_name, nullptr); + if (fetched == nullptr) return; + + free_type(fetched); + auto& cb = *(static_cast(arg)); + cb(from); +} +#else +template +void array_push_back(const TypeName* evp_ref, + const char* from, + const char* to, + void* arg) { + if (!from) return; + auto& cb = *(static_cast(arg)); + cb(from); +} +#endif +} // namespace + +void Cipher::ForEach(Cipher::CipherNameCallback callback) { + ClearErrorOnReturn clearErrorOnReturn; + CipherCallbackContext context; + context.cb = std::move(callback); + + EVP_CIPHER_do_all_sorted( +#if OPENSSL_VERSION_MAJOR >= 3 + array_push_back, +#else + array_push_back, +#endif + &context); +} + // ============================================================================ Ec::Ec() : ec_(nullptr) {} -Ec::Ec(OSSL3_CONST EC_KEY* key) - : ec_(key), x_(BignumPointer::New()), y_(BignumPointer::New()) { - if (ec_ != nullptr) { - MarkPopErrorOnReturn mark_pop_error_on_return; - EC_POINT_get_affine_coordinates( - getGroup(), getPublicKey(), x_.get(), y_.get(), nullptr); - } -} +Ec::Ec(OSSL3_CONST EC_KEY* key) : ec_(key) {} const EC_GROUP* Ec::getGroup() const { return ECKeyPointer::GetGroup(ec_); @@ -4363,22 +4204,6 @@ bool Ec::GetCurves(Ec::GetCurveCallback callback) { return true; } -uint32_t Ec::getDegree() const { - return EC_GROUP_get_degree(getGroup()); -} - -std::string Ec::getCurveName() const { - return std::string(OBJ_nid2sn(getCurve())); -} - -const EC_POINT* Ec::getPublicKey() const { - return EC_KEY_get0_public_key(ec_); -} - -const BIGNUM* Ec::getPrivateKey() const { - return EC_KEY_get0_private_key(ec_); -} - // ============================================================================ EVPMDCtxPointer::EVPMDCtxPointer() : ctx_(nullptr) {} @@ -4841,9 +4666,10 @@ std::pair X509Name::Iterator::operator*() const { unsigned char* value_str; int value_str_size = ASN1_STRING_to_UTF8(&value_str, value); - return { - std::move(name_str), - std::string(reinterpret_cast(value_str), value_str_size)}; + std::string out(reinterpret_cast(value_str), value_str_size); + OPENSSL_free(value_str); // free after copy + + return {std::move(name_str), std::move(out)}; } // ============================================================================ @@ -5014,605 +4840,4 @@ DataPointer KEM::Decapsulate(const EVPKeyPointer& private_key, #endif // OPENSSL_VERSION_MAJOR >= 3 -// ============================================================================ -// AEAD (Authenticated Encryption with Associated Data) -#ifdef OPENSSL_IS_BORINGSSL - -const Aead Aead::FromName(std::string_view name) { - for (const auto& [construct, info] : aeadIndex) { - if (EqualNoCase(info.name, name)) { - return Aead(&info, construct()); - } - } - - return Aead(); -} - -const Aead Aead::FromCtx(std::string_view name, const AeadCtxPointer& ctx) { - for (const auto& [_, info] : aeadIndex) { - if (info.name == name) { - return Aead(&info, EVP_AEAD_CTX_aead(ctx.get())); - } - } - - return Aead(); -} - -int Aead::getMode() const { - if (!aead_) return -1; - - return info_->mode; -} - -int Aead::getNonceLength() const { - if (!aead_) return 0; - return EVP_AEAD_nonce_length(aead_); -} - -int Aead::getKeyLength() const { - if (!aead_) return 0; - return EVP_AEAD_key_length(aead_); -} - -int Aead::getMaxOverhead() const { - if (!aead_) return 0; - return EVP_AEAD_max_overhead(aead_); -} - -int Aead::getMaxTagLength() const { - if (!aead_) return 0; - return EVP_AEAD_max_tag_len(aead_); -} - -int Aead::getBlockSize() const { - if (!aead_) return 0; - - // EVP_CIPHER_CTX_block_size returns the block size, in bytes, of the cipher - // underlying |ctx|, or one if the cipher is a stream cipher. - return 1; -} - -std::string_view Aead::getName() const { - if (!aead_) return ""; - - return info_->name; -} - -int Aead::getNid() const { - if (!aead_) return 0; - - return info_->nid; -} - -const Aead Aead::FromConstructor(Aead::AeadConstructor construct) { - return Aead(&aeadIndex.at(construct), construct()); -} - -const std::unordered_map - Aead::aeadIndex = { - {EVP_aead_aes_128_gcm, - {.name = LN_aes_128_gcm, - .mode = EVP_CIPH_GCM_MODE, - .nid = NID_aes_128_gcm}}, - {EVP_aead_aes_192_gcm, - {.name = LN_aes_192_gcm, - .mode = EVP_CIPH_GCM_MODE, - .nid = NID_aes_192_gcm}}, - {EVP_aead_aes_256_gcm, - {.name = LN_aes_256_gcm, - .mode = EVP_CIPH_GCM_MODE, - .nid = NID_aes_256_gcm}}, - {EVP_aead_chacha20_poly1305, - {.name = LN_chacha20_poly1305, - .mode = EVP_CIPH_STREAM_CIPHER, - .nid = NID_chacha20_poly1305}}, - {EVP_aead_xchacha20_poly1305, - { - .name = "xchacha20-poly1305", - .mode = EVP_CIPH_STREAM_CIPHER, - }}, - {EVP_aead_aes_128_ctr_hmac_sha256, - { - .name = "aes-128-ctr-hmac-sha256", - .mode = EVP_CIPH_CTR_MODE, - }}, - {EVP_aead_aes_256_ctr_hmac_sha256, - { - .name = "aes-256-ctr-hmac-sha256", - .mode = EVP_CIPH_CTR_MODE, - }}, - {EVP_aead_aes_128_gcm_siv, - { - .name = "aes-128-gcm-siv", - .mode = EVP_CIPH_GCM_MODE, - }}, - {EVP_aead_aes_256_gcm_siv, - { - .name = "aes-256-gcm-siv", - .mode = EVP_CIPH_GCM_MODE, - }}, - {EVP_aead_aes_128_gcm_randnonce, - { - .name = "aes-128-gcm-randnonce", - .mode = EVP_CIPH_GCM_MODE, - }}, - {EVP_aead_aes_256_gcm_randnonce, - { - .name = "aes-256-gcm-randnonce", - .mode = EVP_CIPH_GCM_MODE, - }}, - {EVP_aead_aes_128_ccm_bluetooth, - { - .name = "aes-128-ccm-bluetooth", - .mode = EVP_CIPH_CCM_MODE, - }}, - {EVP_aead_aes_128_ccm_bluetooth_8, - { - .name = "aes-128-ccm-bluetooth-8", - .mode = EVP_CIPH_CCM_MODE, - }}, - {EVP_aead_aes_128_ccm_matter, - { - .name = "aes-128-ccm-matter", - .mode = EVP_CIPH_CCM_MODE, - }}, - {EVP_aead_aes_128_eax, - {.name = "aes-128-eax", - // BoringSSL does not define a mode constant for EAX. Using STREAM - // arbitrarily - .mode = EVP_CIPH_STREAM_CIPHER}}, - {EVP_aead_aes_256_eax, - {.name = "aes-256-eax", - // BoringSSL does not define a mode constant for EAX. Using STREAM - // arbitrarily - .mode = EVP_CIPH_STREAM_CIPHER}}, - }; - -void Aead::ForEach(AeadNameCallback callback) { - for (const auto& [_, info] : aeadIndex) { - callback(info.name); - } -} - -const Aead Aead::EMPTY = Aead(); -const Aead Aead::AES_128_GCM = Aead::FromConstructor(EVP_aead_aes_128_gcm); -const Aead Aead::AES_192_GCM = Aead::FromConstructor(EVP_aead_aes_192_gcm); -const Aead Aead::AES_256_GCM = Aead::FromConstructor(EVP_aead_aes_256_gcm); -const Aead Aead::CHACHA20_POLY1305 = - Aead::FromConstructor(EVP_aead_chacha20_poly1305); -const Aead Aead::XCHACHA20_POLY1305 = - Aead::FromConstructor(EVP_aead_xchacha20_poly1305); -const Aead Aead::AES_128_CTR_HMAC_SHA256 = - Aead::FromConstructor(EVP_aead_aes_128_ctr_hmac_sha256); -const Aead Aead::AES_256_CTR_HMAC_SHA256 = - Aead::FromConstructor(EVP_aead_aes_256_ctr_hmac_sha256); -const Aead Aead::AES_128_GCM_SIV = - Aead::FromConstructor(EVP_aead_aes_128_gcm_siv); -const Aead Aead::AES_256_GCM_SIV = - Aead::FromConstructor(EVP_aead_aes_256_gcm_siv); -const Aead Aead::AES_128_GCM_RANDNONCE = - Aead::FromConstructor(EVP_aead_aes_128_gcm_randnonce); -const Aead Aead::AES_256_GCM_RANDNONCE = - Aead::FromConstructor(EVP_aead_aes_256_gcm_randnonce); -const Aead Aead::AES_128_CCM_BLUETOOTH = - Aead::FromConstructor(EVP_aead_aes_128_ccm_bluetooth); -const Aead Aead::AES_128_CCM_BLUETOOTH_8 = - Aead::FromConstructor(EVP_aead_aes_128_ccm_bluetooth_8); -const Aead Aead::AES_128_CCM_MATTER = - Aead::FromConstructor(EVP_aead_aes_128_ccm_matter); -const Aead Aead::AES_128_EAX = Aead::FromConstructor(EVP_aead_aes_128_eax); -const Aead Aead::AES_256_EAX = Aead::FromConstructor(EVP_aead_aes_256_eax); - -template class ModeMixin; - -AeadCtxPointer AeadCtxPointer::New(const Aead& aead, - bool encrypt, - const unsigned char* key, - size_t keyLen, - size_t tagLen) { - // Note: In the EVP_AEAD API new always calls init - auto ret = AeadCtxPointer(EVP_AEAD_CTX_new(aead.get(), key, keyLen, tagLen)); - - if (!ret) { - return {}; - } - - return ret; -} - -AeadCtxPointer::AeadCtxPointer(EVP_AEAD_CTX* ctx) : ctx_(ctx) {} - -AeadCtxPointer::AeadCtxPointer(AeadCtxPointer&& other) noexcept - : ctx_(other.release()) {} - -AeadCtxPointer& AeadCtxPointer::operator=(AeadCtxPointer&& other) noexcept { - if (this == &other) return *this; - this->~AeadCtxPointer(); - return *new (this) AeadCtxPointer(std::move(other)); -} - -AeadCtxPointer::~AeadCtxPointer() { - reset(); -} - -void AeadCtxPointer::reset(EVP_AEAD_CTX* ctx) { - ctx_.reset(ctx); -} - -EVP_AEAD_CTX* AeadCtxPointer::release() { - return ctx_.release(); -} - -bool AeadCtxPointer::init(const Aead& aead, - bool encrypt, - const unsigned char* key, - size_t keyLen, - size_t tagLen) { - return EVP_AEAD_CTX_init_with_direction( - ctx_.get(), - aead, - key, - keyLen, - tagLen, - encrypt ? evp_aead_seal : evp_aead_open); -} - -bool AeadCtxPointer::encrypt(const Buffer& in, - Buffer& out, - Buffer& tag, - const Buffer& nonce, - const Buffer& aad) { - if (!ctx_) return false; - return EVP_AEAD_CTX_seal_scatter(ctx_.get(), - out.data, - tag.data, - &tag.len, - tag.len, - nonce.data, - nonce.len, - in.data, - in.len, - nullptr /* extra_in */, - 0 /* extra_in_len */, - aad.data, - aad.len) == 1; -} - -bool AeadCtxPointer::decrypt(const Buffer& in, - Buffer& out, - const Buffer& tag, - const Buffer& nonce, - const Buffer& aad) { - if (!ctx_) return false; - - return EVP_AEAD_CTX_open_gather(ctx_.get(), - out.data, - nonce.data, - nonce.len, - in.data, - in.len, - tag.data, - tag.len, - aad.data, - aad.len) == 1; -} -#endif } // namespace ncrypto - -// =========================================================================== -#ifdef NCRYPTO_BSSL_NEEDS_DH_PRIMES -// While newer versions of BoringSSL have these primes, older versions do not, -// in particular older versions that conform to fips. We conditionally add -// them here only if the NCRYPTO_BSSL_NEEDS_DH_PRIMES define is set. Their -// implementations are defined here to prevent duplicating the symbols. -extern "C" int bn_set_words(BIGNUM* bn, const BN_ULONG* words, size_t num); - -// Backporting primes that may not be supported in earlier boringssl versions. -// Intentionally keeping the existing C-style formatting. - -#define OPENSSL_ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) - -#if defined(OPENSSL_64_BIT) -#define TOBN(hi, lo) ((BN_ULONG)(hi) << 32 | (lo)) -#elif defined(OPENSSL_32_BIT) -#define TOBN(hi, lo) (lo), (hi) -#else -#error "Must define either OPENSSL_32_BIT or OPENSSL_64_BIT" -#endif - -static BIGNUM* get_params(BIGNUM* ret, - const BN_ULONG* words, - size_t num_words) { - BIGNUM* alloc = nullptr; - if (ret == nullptr) { - alloc = BN_new(); - if (alloc == nullptr) { - return nullptr; - } - ret = alloc; - } - - if (!bn_set_words(ret, words, num_words)) { - BN_free(alloc); - return nullptr; - } - - return ret; -} - -BIGNUM* BN_get_rfc3526_prime_2048(BIGNUM* ret) { - static const BN_ULONG kWords[] = { - TOBN(0xffffffff, 0xffffffff), TOBN(0x15728e5a, 0x8aacaa68), - TOBN(0x15d22618, 0x98fa0510), TOBN(0x3995497c, 0xea956ae5), - TOBN(0xde2bcbf6, 0x95581718), TOBN(0xb5c55df0, 0x6f4c52c9), - TOBN(0x9b2783a2, 0xec07a28f), TOBN(0xe39e772c, 0x180e8603), - TOBN(0x32905e46, 0x2e36ce3b), TOBN(0xf1746c08, 0xca18217c), - TOBN(0x670c354e, 0x4abc9804), TOBN(0x9ed52907, 0x7096966d), - TOBN(0x1c62f356, 0x208552bb), TOBN(0x83655d23, 0xdca3ad96), - TOBN(0x69163fa8, 0xfd24cf5f), TOBN(0x98da4836, 0x1c55d39a), - TOBN(0xc2007cb8, 0xa163bf05), TOBN(0x49286651, 0xece45b3d), - TOBN(0xae9f2411, 0x7c4b1fe6), TOBN(0xee386bfb, 0x5a899fa5), - TOBN(0x0bff5cb6, 0xf406b7ed), TOBN(0xf44c42e9, 0xa637ed6b), - TOBN(0xe485b576, 0x625e7ec6), TOBN(0x4fe1356d, 0x6d51c245), - TOBN(0x302b0a6d, 0xf25f1437), TOBN(0xef9519b3, 0xcd3a431b), - TOBN(0x514a0879, 0x8e3404dd), TOBN(0x020bbea6, 0x3b139b22), - TOBN(0x29024e08, 0x8a67cc74), TOBN(0xc4c6628b, 0x80dc1cd1), - TOBN(0xc90fdaa2, 0x2168c234), TOBN(0xffffffff, 0xffffffff), - }; - return get_params(ret, kWords, OPENSSL_ARRAY_SIZE(kWords)); -} - -BIGNUM* BN_get_rfc3526_prime_3072(BIGNUM* ret) { - static const BN_ULONG kWords[] = { - TOBN(0xffffffff, 0xffffffff), TOBN(0x4b82d120, 0xa93ad2ca), - TOBN(0x43db5bfc, 0xe0fd108e), TOBN(0x08e24fa0, 0x74e5ab31), - TOBN(0x770988c0, 0xbad946e2), TOBN(0xbbe11757, 0x7a615d6c), - TOBN(0x521f2b18, 0x177b200c), TOBN(0xd8760273, 0x3ec86a64), - TOBN(0xf12ffa06, 0xd98a0864), TOBN(0xcee3d226, 0x1ad2ee6b), - TOBN(0x1e8c94e0, 0x4a25619d), TOBN(0xabf5ae8c, 0xdb0933d7), - TOBN(0xb3970f85, 0xa6e1e4c7), TOBN(0x8aea7157, 0x5d060c7d), - TOBN(0xecfb8504, 0x58dbef0a), TOBN(0xa85521ab, 0xdf1cba64), - TOBN(0xad33170d, 0x04507a33), TOBN(0x15728e5a, 0x8aaac42d), - TOBN(0x15d22618, 0x98fa0510), TOBN(0x3995497c, 0xea956ae5), - TOBN(0xde2bcbf6, 0x95581718), TOBN(0xb5c55df0, 0x6f4c52c9), - TOBN(0x9b2783a2, 0xec07a28f), TOBN(0xe39e772c, 0x180e8603), - TOBN(0x32905e46, 0x2e36ce3b), TOBN(0xf1746c08, 0xca18217c), - TOBN(0x670c354e, 0x4abc9804), TOBN(0x9ed52907, 0x7096966d), - TOBN(0x1c62f356, 0x208552bb), TOBN(0x83655d23, 0xdca3ad96), - TOBN(0x69163fa8, 0xfd24cf5f), TOBN(0x98da4836, 0x1c55d39a), - TOBN(0xc2007cb8, 0xa163bf05), TOBN(0x49286651, 0xece45b3d), - TOBN(0xae9f2411, 0x7c4b1fe6), TOBN(0xee386bfb, 0x5a899fa5), - TOBN(0x0bff5cb6, 0xf406b7ed), TOBN(0xf44c42e9, 0xa637ed6b), - TOBN(0xe485b576, 0x625e7ec6), TOBN(0x4fe1356d, 0x6d51c245), - TOBN(0x302b0a6d, 0xf25f1437), TOBN(0xef9519b3, 0xcd3a431b), - TOBN(0x514a0879, 0x8e3404dd), TOBN(0x020bbea6, 0x3b139b22), - TOBN(0x29024e08, 0x8a67cc74), TOBN(0xc4c6628b, 0x80dc1cd1), - TOBN(0xc90fdaa2, 0x2168c234), TOBN(0xffffffff, 0xffffffff), - }; - return get_params(ret, kWords, OPENSSL_ARRAY_SIZE(kWords)); -} - -BIGNUM* BN_get_rfc3526_prime_4096(BIGNUM* ret) { - static const BN_ULONG kWords[] = { - TOBN(0xffffffff, 0xffffffff), TOBN(0x4df435c9, 0x34063199), - TOBN(0x86ffb7dc, 0x90a6c08f), TOBN(0x93b4ea98, 0x8d8fddc1), - TOBN(0xd0069127, 0xd5b05aa9), TOBN(0xb81bdd76, 0x2170481c), - TOBN(0x1f612970, 0xcee2d7af), TOBN(0x233ba186, 0x515be7ed), - TOBN(0x99b2964f, 0xa090c3a2), TOBN(0x287c5947, 0x4e6bc05d), - TOBN(0x2e8efc14, 0x1fbecaa6), TOBN(0xdbbbc2db, 0x04de8ef9), - TOBN(0x2583e9ca, 0x2ad44ce8), TOBN(0x1a946834, 0xb6150bda), - TOBN(0x99c32718, 0x6af4e23c), TOBN(0x88719a10, 0xbdba5b26), - TOBN(0x1a723c12, 0xa787e6d7), TOBN(0x4b82d120, 0xa9210801), - TOBN(0x43db5bfc, 0xe0fd108e), TOBN(0x08e24fa0, 0x74e5ab31), - TOBN(0x770988c0, 0xbad946e2), TOBN(0xbbe11757, 0x7a615d6c), - TOBN(0x521f2b18, 0x177b200c), TOBN(0xd8760273, 0x3ec86a64), - TOBN(0xf12ffa06, 0xd98a0864), TOBN(0xcee3d226, 0x1ad2ee6b), - TOBN(0x1e8c94e0, 0x4a25619d), TOBN(0xabf5ae8c, 0xdb0933d7), - TOBN(0xb3970f85, 0xa6e1e4c7), TOBN(0x8aea7157, 0x5d060c7d), - TOBN(0xecfb8504, 0x58dbef0a), TOBN(0xa85521ab, 0xdf1cba64), - TOBN(0xad33170d, 0x04507a33), TOBN(0x15728e5a, 0x8aaac42d), - TOBN(0x15d22618, 0x98fa0510), TOBN(0x3995497c, 0xea956ae5), - TOBN(0xde2bcbf6, 0x95581718), TOBN(0xb5c55df0, 0x6f4c52c9), - TOBN(0x9b2783a2, 0xec07a28f), TOBN(0xe39e772c, 0x180e8603), - TOBN(0x32905e46, 0x2e36ce3b), TOBN(0xf1746c08, 0xca18217c), - TOBN(0x670c354e, 0x4abc9804), TOBN(0x9ed52907, 0x7096966d), - TOBN(0x1c62f356, 0x208552bb), TOBN(0x83655d23, 0xdca3ad96), - TOBN(0x69163fa8, 0xfd24cf5f), TOBN(0x98da4836, 0x1c55d39a), - TOBN(0xc2007cb8, 0xa163bf05), TOBN(0x49286651, 0xece45b3d), - TOBN(0xae9f2411, 0x7c4b1fe6), TOBN(0xee386bfb, 0x5a899fa5), - TOBN(0x0bff5cb6, 0xf406b7ed), TOBN(0xf44c42e9, 0xa637ed6b), - TOBN(0xe485b576, 0x625e7ec6), TOBN(0x4fe1356d, 0x6d51c245), - TOBN(0x302b0a6d, 0xf25f1437), TOBN(0xef9519b3, 0xcd3a431b), - TOBN(0x514a0879, 0x8e3404dd), TOBN(0x020bbea6, 0x3b139b22), - TOBN(0x29024e08, 0x8a67cc74), TOBN(0xc4c6628b, 0x80dc1cd1), - TOBN(0xc90fdaa2, 0x2168c234), TOBN(0xffffffff, 0xffffffff), - }; - return get_params(ret, kWords, OPENSSL_ARRAY_SIZE(kWords)); -} - -BIGNUM* BN_get_rfc3526_prime_6144(BIGNUM* ret) { - static const BN_ULONG kWords[] = { - TOBN(0xffffffff, 0xffffffff), TOBN(0xe694f91e, 0x6dcc4024), - TOBN(0x12bf2d5b, 0x0b7474d6), TOBN(0x043e8f66, 0x3f4860ee), - TOBN(0x387fe8d7, 0x6e3c0468), TOBN(0xda56c9ec, 0x2ef29632), - TOBN(0xeb19ccb1, 0xa313d55c), TOBN(0xf550aa3d, 0x8a1fbff0), - TOBN(0x06a1d58b, 0xb7c5da76), TOBN(0xa79715ee, 0xf29be328), - TOBN(0x14cc5ed2, 0x0f8037e0), TOBN(0xcc8f6d7e, 0xbf48e1d8), - TOBN(0x4bd407b2, 0x2b4154aa), TOBN(0x0f1d45b7, 0xff585ac5), - TOBN(0x23a97a7e, 0x36cc88be), TOBN(0x59e7c97f, 0xbec7e8f3), - TOBN(0xb5a84031, 0x900b1c9e), TOBN(0xd55e702f, 0x46980c82), - TOBN(0xf482d7ce, 0x6e74fef6), TOBN(0xf032ea15, 0xd1721d03), - TOBN(0x5983ca01, 0xc64b92ec), TOBN(0x6fb8f401, 0x378cd2bf), - TOBN(0x33205151, 0x2bd7af42), TOBN(0xdb7f1447, 0xe6cc254b), - TOBN(0x44ce6cba, 0xced4bb1b), TOBN(0xda3edbeb, 0xcf9b14ed), - TOBN(0x179727b0, 0x865a8918), TOBN(0xb06a53ed, 0x9027d831), - TOBN(0xe5db382f, 0x413001ae), TOBN(0xf8ff9406, 0xad9e530e), - TOBN(0xc9751e76, 0x3dba37bd), TOBN(0xc1d4dcb2, 0x602646de), - TOBN(0x36c3fab4, 0xd27c7026), TOBN(0x4df435c9, 0x34028492), - TOBN(0x86ffb7dc, 0x90a6c08f), TOBN(0x93b4ea98, 0x8d8fddc1), - TOBN(0xd0069127, 0xd5b05aa9), TOBN(0xb81bdd76, 0x2170481c), - TOBN(0x1f612970, 0xcee2d7af), TOBN(0x233ba186, 0x515be7ed), - TOBN(0x99b2964f, 0xa090c3a2), TOBN(0x287c5947, 0x4e6bc05d), - TOBN(0x2e8efc14, 0x1fbecaa6), TOBN(0xdbbbc2db, 0x04de8ef9), - TOBN(0x2583e9ca, 0x2ad44ce8), TOBN(0x1a946834, 0xb6150bda), - TOBN(0x99c32718, 0x6af4e23c), TOBN(0x88719a10, 0xbdba5b26), - TOBN(0x1a723c12, 0xa787e6d7), TOBN(0x4b82d120, 0xa9210801), - TOBN(0x43db5bfc, 0xe0fd108e), TOBN(0x08e24fa0, 0x74e5ab31), - TOBN(0x770988c0, 0xbad946e2), TOBN(0xbbe11757, 0x7a615d6c), - TOBN(0x521f2b18, 0x177b200c), TOBN(0xd8760273, 0x3ec86a64), - TOBN(0xf12ffa06, 0xd98a0864), TOBN(0xcee3d226, 0x1ad2ee6b), - TOBN(0x1e8c94e0, 0x4a25619d), TOBN(0xabf5ae8c, 0xdb0933d7), - TOBN(0xb3970f85, 0xa6e1e4c7), TOBN(0x8aea7157, 0x5d060c7d), - TOBN(0xecfb8504, 0x58dbef0a), TOBN(0xa85521ab, 0xdf1cba64), - TOBN(0xad33170d, 0x04507a33), TOBN(0x15728e5a, 0x8aaac42d), - TOBN(0x15d22618, 0x98fa0510), TOBN(0x3995497c, 0xea956ae5), - TOBN(0xde2bcbf6, 0x95581718), TOBN(0xb5c55df0, 0x6f4c52c9), - TOBN(0x9b2783a2, 0xec07a28f), TOBN(0xe39e772c, 0x180e8603), - TOBN(0x32905e46, 0x2e36ce3b), TOBN(0xf1746c08, 0xca18217c), - TOBN(0x670c354e, 0x4abc9804), TOBN(0x9ed52907, 0x7096966d), - TOBN(0x1c62f356, 0x208552bb), TOBN(0x83655d23, 0xdca3ad96), - TOBN(0x69163fa8, 0xfd24cf5f), TOBN(0x98da4836, 0x1c55d39a), - TOBN(0xc2007cb8, 0xa163bf05), TOBN(0x49286651, 0xece45b3d), - TOBN(0xae9f2411, 0x7c4b1fe6), TOBN(0xee386bfb, 0x5a899fa5), - TOBN(0x0bff5cb6, 0xf406b7ed), TOBN(0xf44c42e9, 0xa637ed6b), - TOBN(0xe485b576, 0x625e7ec6), TOBN(0x4fe1356d, 0x6d51c245), - TOBN(0x302b0a6d, 0xf25f1437), TOBN(0xef9519b3, 0xcd3a431b), - TOBN(0x514a0879, 0x8e3404dd), TOBN(0x020bbea6, 0x3b139b22), - TOBN(0x29024e08, 0x8a67cc74), TOBN(0xc4c6628b, 0x80dc1cd1), - TOBN(0xc90fdaa2, 0x2168c234), TOBN(0xffffffff, 0xffffffff), - }; - return get_params(ret, kWords, OPENSSL_ARRAY_SIZE(kWords)); -} - -BIGNUM* BN_get_rfc3526_prime_8192(BIGNUM* ret) { - static const BN_ULONG kWords[] = { - TOBN(0xffffffff, 0xffffffff), TOBN(0x60c980dd, 0x98edd3df), - TOBN(0xc81f56e8, 0x80b96e71), TOBN(0x9e3050e2, 0x765694df), - TOBN(0x9558e447, 0x5677e9aa), TOBN(0xc9190da6, 0xfc026e47), - TOBN(0x889a002e, 0xd5ee382b), TOBN(0x4009438b, 0x481c6cd7), - TOBN(0x359046f4, 0xeb879f92), TOBN(0xfaf36bc3, 0x1ecfa268), - TOBN(0xb1d510bd, 0x7ee74d73), TOBN(0xf9ab4819, 0x5ded7ea1), - TOBN(0x64f31cc5, 0x0846851d), TOBN(0x4597e899, 0xa0255dc1), - TOBN(0xdf310ee0, 0x74ab6a36), TOBN(0x6d2a13f8, 0x3f44f82d), - TOBN(0x062b3cf5, 0xb3a278a6), TOBN(0x79683303, 0xed5bdd3a), - TOBN(0xfa9d4b7f, 0xa2c087e8), TOBN(0x4bcbc886, 0x2f8385dd), - TOBN(0x3473fc64, 0x6cea306b), TOBN(0x13eb57a8, 0x1a23f0c7), - TOBN(0x22222e04, 0xa4037c07), TOBN(0xe3fdb8be, 0xfc848ad9), - TOBN(0x238f16cb, 0xe39d652d), TOBN(0x3423b474, 0x2bf1c978), - TOBN(0x3aab639c, 0x5ae4f568), TOBN(0x2576f693, 0x6ba42466), - TOBN(0x741fa7bf, 0x8afc47ed), TOBN(0x3bc832b6, 0x8d9dd300), - TOBN(0xd8bec4d0, 0x73b931ba), TOBN(0x38777cb6, 0xa932df8c), - TOBN(0x74a3926f, 0x12fee5e4), TOBN(0xe694f91e, 0x6dbe1159), - TOBN(0x12bf2d5b, 0x0b7474d6), TOBN(0x043e8f66, 0x3f4860ee), - TOBN(0x387fe8d7, 0x6e3c0468), TOBN(0xda56c9ec, 0x2ef29632), - TOBN(0xeb19ccb1, 0xa313d55c), TOBN(0xf550aa3d, 0x8a1fbff0), - TOBN(0x06a1d58b, 0xb7c5da76), TOBN(0xa79715ee, 0xf29be328), - TOBN(0x14cc5ed2, 0x0f8037e0), TOBN(0xcc8f6d7e, 0xbf48e1d8), - TOBN(0x4bd407b2, 0x2b4154aa), TOBN(0x0f1d45b7, 0xff585ac5), - TOBN(0x23a97a7e, 0x36cc88be), TOBN(0x59e7c97f, 0xbec7e8f3), - TOBN(0xb5a84031, 0x900b1c9e), TOBN(0xd55e702f, 0x46980c82), - TOBN(0xf482d7ce, 0x6e74fef6), TOBN(0xf032ea15, 0xd1721d03), - TOBN(0x5983ca01, 0xc64b92ec), TOBN(0x6fb8f401, 0x378cd2bf), - TOBN(0x33205151, 0x2bd7af42), TOBN(0xdb7f1447, 0xe6cc254b), - TOBN(0x44ce6cba, 0xced4bb1b), TOBN(0xda3edbeb, 0xcf9b14ed), - TOBN(0x179727b0, 0x865a8918), TOBN(0xb06a53ed, 0x9027d831), - TOBN(0xe5db382f, 0x413001ae), TOBN(0xf8ff9406, 0xad9e530e), - TOBN(0xc9751e76, 0x3dba37bd), TOBN(0xc1d4dcb2, 0x602646de), - TOBN(0x36c3fab4, 0xd27c7026), TOBN(0x4df435c9, 0x34028492), - TOBN(0x86ffb7dc, 0x90a6c08f), TOBN(0x93b4ea98, 0x8d8fddc1), - TOBN(0xd0069127, 0xd5b05aa9), TOBN(0xb81bdd76, 0x2170481c), - TOBN(0x1f612970, 0xcee2d7af), TOBN(0x233ba186, 0x515be7ed), - TOBN(0x99b2964f, 0xa090c3a2), TOBN(0x287c5947, 0x4e6bc05d), - TOBN(0x2e8efc14, 0x1fbecaa6), TOBN(0xdbbbc2db, 0x04de8ef9), - TOBN(0x2583e9ca, 0x2ad44ce8), TOBN(0x1a946834, 0xb6150bda), - TOBN(0x99c32718, 0x6af4e23c), TOBN(0x88719a10, 0xbdba5b26), - TOBN(0x1a723c12, 0xa787e6d7), TOBN(0x4b82d120, 0xa9210801), - TOBN(0x43db5bfc, 0xe0fd108e), TOBN(0x08e24fa0, 0x74e5ab31), - TOBN(0x770988c0, 0xbad946e2), TOBN(0xbbe11757, 0x7a615d6c), - TOBN(0x521f2b18, 0x177b200c), TOBN(0xd8760273, 0x3ec86a64), - TOBN(0xf12ffa06, 0xd98a0864), TOBN(0xcee3d226, 0x1ad2ee6b), - TOBN(0x1e8c94e0, 0x4a25619d), TOBN(0xabf5ae8c, 0xdb0933d7), - TOBN(0xb3970f85, 0xa6e1e4c7), TOBN(0x8aea7157, 0x5d060c7d), - TOBN(0xecfb8504, 0x58dbef0a), TOBN(0xa85521ab, 0xdf1cba64), - TOBN(0xad33170d, 0x04507a33), TOBN(0x15728e5a, 0x8aaac42d), - TOBN(0x15d22618, 0x98fa0510), TOBN(0x3995497c, 0xea956ae5), - TOBN(0xde2bcbf6, 0x95581718), TOBN(0xb5c55df0, 0x6f4c52c9), - TOBN(0x9b2783a2, 0xec07a28f), TOBN(0xe39e772c, 0x180e8603), - TOBN(0x32905e46, 0x2e36ce3b), TOBN(0xf1746c08, 0xca18217c), - TOBN(0x670c354e, 0x4abc9804), TOBN(0x9ed52907, 0x7096966d), - TOBN(0x1c62f356, 0x208552bb), TOBN(0x83655d23, 0xdca3ad96), - TOBN(0x69163fa8, 0xfd24cf5f), TOBN(0x98da4836, 0x1c55d39a), - TOBN(0xc2007cb8, 0xa163bf05), TOBN(0x49286651, 0xece45b3d), - TOBN(0xae9f2411, 0x7c4b1fe6), TOBN(0xee386bfb, 0x5a899fa5), - TOBN(0x0bff5cb6, 0xf406b7ed), TOBN(0xf44c42e9, 0xa637ed6b), - TOBN(0xe485b576, 0x625e7ec6), TOBN(0x4fe1356d, 0x6d51c245), - TOBN(0x302b0a6d, 0xf25f1437), TOBN(0xef9519b3, 0xcd3a431b), - TOBN(0x514a0879, 0x8e3404dd), TOBN(0x020bbea6, 0x3b139b22), - TOBN(0x29024e08, 0x8a67cc74), TOBN(0xc4c6628b, 0x80dc1cd1), - TOBN(0xc90fdaa2, 0x2168c234), TOBN(0xffffffff, 0xffffffff), - }; - return get_params(ret, kWords, OPENSSL_ARRAY_SIZE(kWords)); -} -#endif - -// =========================================================================== -#if NCRYPTO_BSSL_LIBDECREPIT_MISSING -// While BoringSSL implements EVP_CIPHER_do_all_sorted, it includes this -// inplementation in a library called "libdecrepit", which might not be included -// depending on how boringssl was built. In such cases, a copy of the function -// is provided here: - -extern "C" void EVP_CIPHER_do_all_sorted( - void (*callback)(const EVP_CIPHER* cipher, - const char* name, - const char* unused, - void* arg), - void* arg) { - callback(EVP_aes_128_cbc(), "AES-128-CBC", NULL, arg); - callback(EVP_aes_192_cbc(), "AES-192-CBC", NULL, arg); - callback(EVP_aes_256_cbc(), "AES-256-CBC", NULL, arg); - callback(EVP_aes_128_ctr(), "AES-128-CTR", NULL, arg); - callback(EVP_aes_192_ctr(), "AES-192-CTR", NULL, arg); - callback(EVP_aes_256_ctr(), "AES-256-CTR", NULL, arg); - callback(EVP_aes_128_ecb(), "AES-128-ECB", NULL, arg); - callback(EVP_aes_192_ecb(), "AES-192-ECB", NULL, arg); - callback(EVP_aes_256_ecb(), "AES-256-ECB", NULL, arg); - callback(EVP_aes_128_ofb(), "AES-128-OFB", NULL, arg); - callback(EVP_aes_192_ofb(), "AES-192-OFB", NULL, arg); - callback(EVP_aes_256_ofb(), "AES-256-OFB", NULL, arg); - callback(EVP_aes_128_gcm(), "AES-128-GCM", NULL, arg); - callback(EVP_aes_192_gcm(), "AES-192-GCM", NULL, arg); - callback(EVP_aes_256_gcm(), "AES-256-GCM", NULL, arg); - callback(EVP_des_cbc(), "DES-CBC", NULL, arg); - callback(EVP_des_ecb(), "DES-ECB", NULL, arg); - callback(EVP_des_ede(), "DES-EDE", NULL, arg); - callback(EVP_des_ede_cbc(), "DES-EDE-CBC", NULL, arg); - callback(EVP_des_ede3_cbc(), "DES-EDE3-CBC", NULL, arg); - callback(EVP_rc2_cbc(), "RC2-CBC", NULL, arg); - callback(EVP_rc4(), "RC4", NULL, arg); - - // OpenSSL returns everything twice, the second time in lower case. - callback(EVP_aes_128_cbc(), "aes-128-cbc", NULL, arg); - callback(EVP_aes_192_cbc(), "aes-192-cbc", NULL, arg); - callback(EVP_aes_256_cbc(), "aes-256-cbc", NULL, arg); - callback(EVP_aes_128_ctr(), "aes-128-ctr", NULL, arg); - callback(EVP_aes_192_ctr(), "aes-192-ctr", NULL, arg); - callback(EVP_aes_256_ctr(), "aes-256-ctr", NULL, arg); - callback(EVP_aes_128_ecb(), "aes-128-ecb", NULL, arg); - callback(EVP_aes_192_ecb(), "aes-192-ecb", NULL, arg); - callback(EVP_aes_256_ecb(), "aes-256-ecb", NULL, arg); - callback(EVP_aes_128_ofb(), "aes-128-ofb", NULL, arg); - callback(EVP_aes_192_ofb(), "aes-192-ofb", NULL, arg); - callback(EVP_aes_256_ofb(), "aes-256-ofb", NULL, arg); - callback(EVP_aes_128_gcm(), "aes-128-gcm", NULL, arg); - callback(EVP_aes_192_gcm(), "aes-192-gcm", NULL, arg); - callback(EVP_aes_256_gcm(), "aes-256-gcm", NULL, arg); - callback(EVP_des_cbc(), "des-cbc", NULL, arg); - callback(EVP_des_ecb(), "des-ecb", NULL, arg); - callback(EVP_des_ede(), "des-ede", NULL, arg); - callback(EVP_des_ede_cbc(), "des-ede-cbc", NULL, arg); - callback(EVP_des_ede3_cbc(), "des-ede3-cbc", NULL, arg); - callback(EVP_rc2_cbc(), "rc2-cbc", NULL, arg); - callback(EVP_rc4(), "rc4", NULL, arg); -} -#endif From 6f2b8cb3a41ec2d7f5e7eab3b100be7870c038c4 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 11 Jan 2026 01:20:17 +0100 Subject: [PATCH 02/11] use shared libs instead --- CMakeLists.txt | 28 ++-------------------------- src/CMakeLists.txt | 9 +-------- 2 files changed, 3 insertions(+), 34 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1704cd3..b6eb71a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,37 +3,13 @@ project(ncrypto) include(CTest) include(GNUInstallDirs) -include(FetchContent) -include(cmake/ncrypto-flags.cmake) -if (NOT CMAKE_BUILD_TYPE) - message(STATUS "No build type selected, default to Release") - set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) -endif() - -include(cmake/CPM.cmake) - -CPMAddPackage( - NAME boringssl - VERSION 0.20250818.0 - GITHUB_REPOSITORY google/boringssl - GIT_TAG 0.20250818.0 - OPTIONS "BUILD_SHARED_LIBS OFF" "BUILD_TESTING OFF" -) +find_package(OpenSSL REQUIRED) add_subdirectory(src) add_library(ncrypto::ncrypto ALIAS ncrypto) -include_directories(${boringssl_SOURCE_DIR}/include) - if (NCRYPTO_TESTING) - CPMAddPackage( - NAME GTest - GITHUB_REPOSITORY google/googletest - VERSION 1.15.2 - OPTIONS "BUILD_GMOCK OFF" "INSTALL_GTEST OFF" - ) - # For Windows: Prevent overriding the parent project's compiler/linker settings - set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + find_package(GTest REQUIRED) enable_testing() add_subdirectory(tests) endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 87296cd..f73efb1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,12 +1,5 @@ add_library(ncrypto ncrypto.cpp engine.cpp) -target_link_libraries(ncrypto PUBLIC ssl crypto) - -if (NCRYPTO_BSSL_LIBDECREPIT_MISSING) - target_compile_definitions(ncrypto PUBLIC NCRYPTO_BSSL_LIBDECREPIT_MISSING=1) -else() - target_link_libraries(ncrypto PUBLIC decrepit) -endif() - +target_link_libraries(ncrypto PUBLIC OpenSSL::SSL OpenSSL::Crypto) target_include_directories(ncrypto PUBLIC $ From ff892d61980b17b2234f4600794ca81bdf394707 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 11 Jan 2026 01:26:18 +0100 Subject: [PATCH 03/11] chore: add pkgconfig support --- CMakeLists.txt | 14 +++++++++++++- ncrypto.pc.in | 10 ++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 ncrypto.pc.in diff --git a/CMakeLists.txt b/CMakeLists.txt index b6eb71a..783be02 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.28) -project(ncrypto) +project(ncrypto VERSION 1.0.1) include(CTest) include(GNUInstallDirs) @@ -29,3 +29,15 @@ install( ARCHIVE COMPONENT ncrypto_development INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" ) + +# Generate pkg-config file +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/ncrypto.pc.in + ${CMAKE_CURRENT_BINARY_DIR}/ncrypto.pc + @ONLY +) + +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/ncrypto.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig +) diff --git a/ncrypto.pc.in b/ncrypto.pc.in new file mode 100644 index 0000000..5178d55 --- /dev/null +++ b/ncrypto.pc.in @@ -0,0 +1,10 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ + +Name: ncrypto +Description: crypto functions for node:crypto +Version: @PROJECT_VERSION@ +Libs: -L${libdir} -lncrypto +Cflags: -I${includedir} From 0eb53ad4ccce264507d0e5ed4335ead0635186ab Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 11 Jan 2026 01:33:10 +0100 Subject: [PATCH 04/11] fix: set compiler to Cpp20 --- src/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f73efb1..862fc6c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,5 @@ add_library(ncrypto ncrypto.cpp engine.cpp) +set_target_properties(ncrypto PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED YES) target_link_libraries(ncrypto PUBLIC OpenSSL::SSL OpenSSL::Crypto) target_include_directories(ncrypto PUBLIC From fe09e4083f6b414f6f2d9a2c0b73f4de2d32d6a6 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 11 Jan 2026 01:54:19 +0100 Subject: [PATCH 05/11] fixup! fix: set compiler to Cpp20 --- tests/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 17866ff..6f11f07 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,6 @@ include(GoogleTest) add_executable(basic basic.cpp) +set_target_properties(basic PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED YES) target_link_libraries(basic PRIVATE ncrypto GTest::gtest_main) gtest_discover_tests(basic) if(MSVC OR MINGW) From f63c4496287db620cf9d3028ca926792609dbf6e Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 11 Jan 2026 09:56:02 +0100 Subject: [PATCH 06/11] fix tests --- tests/basic.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/basic.cpp b/tests/basic.cpp index 05744cd..f9c0791 100644 --- a/tests/basic.cpp +++ b/tests/basic.cpp @@ -37,8 +37,8 @@ TEST(basic, cipher_foreach) { // as that depends on openssl vs boringssl, versions, configuration, etc. // Instead, we look for a couple of very common ciphers that should always be // present. - ASSERT_TRUE(foundCiphers.count("AES-128-CTR")); - ASSERT_TRUE(foundCiphers.count("AES-256-CBC")); + ASSERT_TRUE(foundCiphers.count("aes-128-ctr") || foundCiphers.count("AES-128-CTR")); + ASSERT_TRUE(foundCiphers.count("aes-256-cbc") || foundCiphers.count("AES-256-CBC")); } #ifdef OPENSSL_IS_BORINGSSL From 1d6d395936b4642c0cea0ae390bbc3f228353f4a Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 11 Jan 2026 10:38:15 +0100 Subject: [PATCH 07/11] fixup! use shared libs instead --- CMakeLists.txt | 29 +++++++++++++++++++++++++++-- cmake/ncrypto-flags.cmake | 1 + 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 783be02..bacc669 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,12 +4,37 @@ project(ncrypto VERSION 1.0.1) include(CTest) include(GNUInstallDirs) -find_package(OpenSSL REQUIRED) +if (NCRYPTO_SHARED_LIBS) + find_package(OpenSSL REQUIRED) +else() + include(FetchContent) + include(cmake/CPM.cmake) + + CPMAddPackage( + NAME boringssl + VERSION 0.20250818.0 + GITHUB_REPOSITORY google/boringssl + GIT_TAG 0.20250818.0 + OPTIONS "BUILD_SHARED_LIBS OFF" "BUILD_TESTING OFF" + ) +endif() + add_subdirectory(src) add_library(ncrypto::ncrypto ALIAS ncrypto) if (NCRYPTO_TESTING) - find_package(GTest REQUIRED) + if (NCRYPTO_SHARED_LIBS) + find_package(GTest REQUIRED) + else() + CPMAddPackage( + NAME GTest + GITHUB_REPOSITORY google/googletest + VERSION 1.15.2 + OPTIONS "BUILD_GMOCK OFF" "INSTALL_GTEST OFF" + ) + # For Windows: Prevent overriding the parent project's compiler/linker settings + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + endif() enable_testing() add_subdirectory(tests) endif() diff --git a/cmake/ncrypto-flags.cmake b/cmake/ncrypto-flags.cmake index 3023f0a..1d332bc 100644 --- a/cmake/ncrypto-flags.cmake +++ b/cmake/ncrypto-flags.cmake @@ -1,5 +1,6 @@ option(NCRYPTO_DEVELOPMENT_CHECKS "development checks (useful for debugging)" OFF) option(NCRYPTO_TESTING "Build tests" ON) +option(NCRYPTO_SHARED_LIBS "Build (and test) using shared library" OFF) option(NCRYPTO_BSSL_LIBDECREPIT_MISSING "enable if boringssl is built without libdecrepit" OFF) set(CMAKE_POSITION_INDEPENDENT_CODE ON) From f96106291aa37348cb0a145ca5088976617c1b30 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 11 Jan 2026 12:48:13 +0100 Subject: [PATCH 08/11] fixup! fix: set compiler to Cpp20 --- CMakeLists.txt | 1 + src/CMakeLists.txt | 1 - tests/CMakeLists.txt | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bacc669..c7619f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,7 @@ project(ncrypto VERSION 1.0.1) include(CTest) include(GNUInstallDirs) +include(cmake/ncrypto-flags.cmake) if (NCRYPTO_SHARED_LIBS) find_package(OpenSSL REQUIRED) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 862fc6c..f73efb1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,4 @@ add_library(ncrypto ncrypto.cpp engine.cpp) -set_target_properties(ncrypto PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED YES) target_link_libraries(ncrypto PUBLIC OpenSSL::SSL OpenSSL::Crypto) target_include_directories(ncrypto PUBLIC diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6f11f07..17866ff 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,6 +1,5 @@ include(GoogleTest) add_executable(basic basic.cpp) -set_target_properties(basic PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED YES) target_link_libraries(basic PRIVATE ncrypto GTest::gtest_main) gtest_discover_tests(basic) if(MSVC OR MINGW) From f550da6aada36673cb3b735419a614c434f48546 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 11 Jan 2026 18:59:11 +0100 Subject: [PATCH 09/11] fixup! use shared libs instead --- src/CMakeLists.txt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f73efb1..61779aa 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,15 @@ add_library(ncrypto ncrypto.cpp engine.cpp) -target_link_libraries(ncrypto PUBLIC OpenSSL::SSL OpenSSL::Crypto) +if (NCRYPTO_SHARED_LIBS) + target_link_libraries(ncrypto PUBLIC OpenSSL::SSL OpenSSL::Crypto) +else() + target_link_libraries(ncrypto PUBLIC ssl crypto) + + if (NCRYPTO_BSSL_LIBDECREPIT_MISSING) + target_compile_definitions(ncrypto PUBLIC NCRYPTO_BSSL_LIBDECREPIT_MISSING=1) + else() + target_link_libraries(ncrypto PUBLIC decrepit) + endif() +endif() target_include_directories(ncrypto PUBLIC $ From de32942ca0011df225ee4a3757248900b4ce0661 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 11 Jan 2026 19:09:58 +0100 Subject: [PATCH 10/11] lint --- src/ncrypto.cpp | 4 ++-- tests/basic.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ncrypto.cpp b/src/ncrypto.cpp index 461819c..da5fa74 100644 --- a/src/ncrypto.cpp +++ b/src/ncrypto.cpp @@ -288,7 +288,7 @@ bool testFipsEnabled() { #else #ifdef OPENSSL_FIPS return FIPS_selftest(); -#else // OPENSSL_FIPS +#else // OPENSSL_FIPS return false; #endif // OPENSSL_FIPS #endif @@ -2769,7 +2769,7 @@ EVPKeyPointer::operator Dsa() const { bool EVPKeyPointer::validateDsaParameters() const { if (!pkey_) return false; - /* Validate DSA2 parameters from FIPS 186-4 */ + /* Validate DSA2 parameters from FIPS 186-4 */ #if OPENSSL_VERSION_MAJOR >= 3 if (EVP_default_properties_is_fips_enabled(nullptr) && EVP_PKEY_DSA == id()) { #else diff --git a/tests/basic.cpp b/tests/basic.cpp index f9c0791..a30ab3e 100644 --- a/tests/basic.cpp +++ b/tests/basic.cpp @@ -37,8 +37,10 @@ TEST(basic, cipher_foreach) { // as that depends on openssl vs boringssl, versions, configuration, etc. // Instead, we look for a couple of very common ciphers that should always be // present. - ASSERT_TRUE(foundCiphers.count("aes-128-ctr") || foundCiphers.count("AES-128-CTR")); - ASSERT_TRUE(foundCiphers.count("aes-256-cbc") || foundCiphers.count("AES-256-CBC")); + ASSERT_TRUE(foundCiphers.count("aes-128-ctr") || + foundCiphers.count("AES-128-CTR")); + ASSERT_TRUE(foundCiphers.count("aes-256-cbc") || + foundCiphers.count("AES-256-CBC")); } #ifdef OPENSSL_IS_BORINGSSL From f55658feae9cb93f0ab52ff7a44afc666fe478b9 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 11 Jan 2026 22:49:26 +0100 Subject: [PATCH 11/11] bump clang-format --- .github/workflows/linter.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 0241386..5546d0a 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -27,9 +27,9 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Run clang-format - uses: jidicula/clang-format-action@c74383674bf5f7c69f60ce562019c1c94bc1421a # v4.13.0 + uses: jidicula/clang-format-action@6cd220de46c89139a0365edae93eee8eb30ca8fe # v4.16.0 with: - clang-format-version: '17' + clang-format-version: '21' fallback-style: 'Google' - uses: chartboost/ruff-action@e18ae971ccee1b2d7bbef113930f00c670b78da4 # v1.0.0