diff --git a/include/ncrypto.h b/include/ncrypto.h index 263c4dc..970e766 100644 --- a/include/ncrypto.h +++ b/include/ncrypto.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -16,17 +17,13 @@ #include #endif -#include #include -#include #include #include #include #include #include #include -#include -#include #if NCRYPTO_DEVELOPMENT_CHECKS #include @@ -92,7 +89,10 @@ inline bool EqualNoCase(const std::string_view a, const std::string_view b) { #define NCRYPTO_STR(x) #x #define NCRYPTO_REQUIRE(EXPR) \ { \ - if (!(EXPR) { abort(); }) } + if (!(EXPR)) { \ + abort(); \ + } \ + } #define NCRYPTO_FAIL(MESSAGE) \ do { \ @@ -269,8 +269,6 @@ class ECKeyPointer; class Dsa; class Rsa; class Ec; -class Aead; -class AeadCtxPointer; struct StackOfXASN1Deleter { void operator()(STACK_OF(ASN1_OBJECT) * p) const { @@ -325,25 +323,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; @@ -352,10 +332,12 @@ 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 +#ifndef OPENSSL_IS_BORINGSSL + && EVP_CCM_TLS_TAG_LEN <= MAX_AUTH_TAG_LENGTH && + EVP_CHACHAPOLY_TLS_TAG_LEN <= MAX_AUTH_TAG_LENGTH +#endif + ); Cipher() = default; Cipher(const EVP_CIPHER* cipher) : cipher_(cipher) {} @@ -376,9 +358,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; @@ -471,78 +459,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: @@ -604,10 +522,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_; } @@ -617,16 +531,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 @@ -757,6 +663,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(); @@ -998,15 +976,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_; }; @@ -1635,19 +1610,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 @@ -1664,13 +1631,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, @@ -1679,15 +1639,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, @@ -1696,13 +1647,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, diff --git a/src/ncrypto.cpp b/src/ncrypto.cpp index bcd2536..3a32813 100644 --- a/src/ncrypto.cpp +++ b/src/ncrypto.cpp @@ -1,5 +1,4 @@ #include "ncrypto.h" - #include #include #include @@ -17,12 +16,8 @@ #include #include -#include -#include #include #include -#include - #if OPENSSL_VERSION_MAJOR >= 3 #include #include @@ -71,8 +66,6 @@ constexpr static PQCMapping pqc_mappings[] = { nullptr) #endif -// ============================================================================ - namespace ncrypto { namespace { using BignumCtxPointer = DeleteFnPtr; @@ -261,7 +254,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); @@ -354,15 +347,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()); } @@ -635,9 +623,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. @@ -655,11 +641,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, @@ -668,21 +652,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. @@ -695,44 +675,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 {}; } @@ -1406,16 +1354,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); @@ -1425,16 +1367,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); @@ -1859,28 +1795,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. @@ -1888,7 +1814,15 @@ 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 @@ -1907,41 +1841,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) { @@ -1949,36 +1865,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, @@ -1986,42 +1872,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, @@ -2029,11 +1900,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 {}; @@ -2236,12 +2119,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(); @@ -2590,7 +2467,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); @@ -2666,8 +2542,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( @@ -2726,8 +2605,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( @@ -2903,15 +2785,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 */ @@ -3213,36 +3086,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}"; -} // ============================================================================ @@ -3280,13 +3123,43 @@ const Cipher Cipher::AES_256_OCB = Cipher::FromNid(NID_aes_256_ocb); 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_); } @@ -3310,6 +3183,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 @@ -3340,76 +3242,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() { @@ -3486,11 +3318,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; @@ -3516,6 +3343,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, @@ -4216,14 +4048,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; } @@ -4308,18 +4135,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_); @@ -4349,22 +4237,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) {} @@ -4827,9 +4699,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)}; } // ============================================================================ diff --git a/tests/basic.cpp b/tests/basic.cpp index d8c7f11..6742e05 100644 --- a/tests/basic.cpp +++ b/tests/basic.cpp @@ -38,8 +38,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")); - 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