44#include < openssl/dh.h>
55#include < openssl/evp.h>
66#include < openssl/hmac.h>
7+ #include < openssl/pem.h>
78#include < openssl/pkcs12.h>
89#include < openssl/rand.h>
910#include < openssl/x509v3.h>
1011#if NCRYPTO_USE_BORINGSSL_EVP_DO_ALL_FALLBACK
1112#include < openssl/bytestring.h>
1213#include < openssl/cipher.h>
13- #include < openssl/pem.h>
1414#endif
1515#include < algorithm>
1616#include < array>
2121#include < openssl/core_names.h>
2222#include < openssl/params.h>
2323#include < openssl/provider.h>
24+ #include < openssl/store.h>
25+ #include < openssl/ui.h>
2426#if OPENSSL_WITH_ARGON2
2527#include < openssl/thread.h>
2628#endif
@@ -76,6 +78,17 @@ using BignumCtxPointer = DeleteFnPtr<BN_CTX, BN_CTX_free>;
7678using BignumGenCallbackPointer = DeleteFnPtr<BN_GENCB , BN_GENCB_free>;
7779using NetscapeSPKIPointer = DeleteFnPtr<NETSCAPE_SPKI , NETSCAPE_SPKI_free>;
7880
81+ #if NCRYPTO_USE_OPENSSL3_PROVIDER
82+ using X509PubKeyPointer = DeleteFnPtr<X509_PUBKEY , X509_PUBKEY_free>;
83+ // OSSL_STORE_close() returns int, so it needs a void-returning adapter to be
84+ // usable as a DeleteFnPtr deleter.
85+ void CloseStoreCtx (OSSL_STORE_CTX * ctx) {
86+ OSSL_STORE_close (ctx);
87+ }
88+ using StoreCtxPointer = DeleteFnPtr<OSSL_STORE_CTX , CloseStoreCtx>;
89+ using UIMethodPointer = DeleteFnPtr<UI_METHOD , UI_destroy_method>;
90+ #endif
91+
7992const EVP_CIPHER * GetCipherCtxCipher (const EVP_CIPHER_CTX * ctx) {
8093#if NCRYPTO_USE_OPENSSL3_PROVIDER
8194 return EVP_CIPHER_CTX_get0_cipher (ctx);
@@ -332,7 +345,7 @@ ClearErrorOnReturn::~ClearErrorOnReturn() {
332345 ERR_clear_error ();
333346}
334347
335- int ClearErrorOnReturn::peekError () {
348+ unsigned long ClearErrorOnReturn::peekError () { // NOLINT(runtime/int)
336349 return ERR_peek_error ();
337350}
338351
@@ -346,7 +359,7 @@ MarkPopErrorOnReturn::~MarkPopErrorOnReturn() {
346359 ERR_pop_to_mark ();
347360}
348361
349- int MarkPopErrorOnReturn::peekError () {
362+ unsigned long MarkPopErrorOnReturn::peekError () { // NOLINT(runtime/int)
350363 return ERR_peek_error ();
351364}
352365
@@ -840,6 +853,7 @@ int NoPasswordCallback(char* buf, int size, int rwflag, void* u) {
840853
841854int PasswordCallback (char * buf, int size, int rwflag, void * u) {
842855 auto passphrase = static_cast <const Buffer<char >*>(u);
856+ if (size <= 0 ) return -1 ;
843857 if (passphrase != nullptr ) {
844858 size_t buflen = static_cast <size_t >(size);
845859 size_t len = passphrase->len ;
@@ -851,6 +865,31 @@ int PasswordCallback(char* buf, int size, int rwflag, void* u) {
851865 return -1 ;
852866}
853867
868+ #if NCRYPTO_USE_OPENSSL3_PROVIDER
869+ namespace {
870+ struct StorePassphraseData {
871+ Buffer<char > passphrase{.data = nullptr , .len = 0 };
872+ bool has_passphrase = false ;
873+ bool missing_passphrase = false ;
874+ };
875+
876+ int StorePasswordCallback (char * buf, int size, int rwflag, void * u) {
877+ auto data = static_cast <StorePassphraseData*>(u);
878+ if (data == nullptr || !data->has_passphrase ) {
879+ if (data != nullptr ) data->missing_passphrase = true ;
880+ return -1 ;
881+ }
882+
883+ if (size <= 0 ) return -1 ;
884+ size_t buflen = static_cast <size_t >(size);
885+ size_t len = data->passphrase .len ;
886+ if (buflen < len) return -1 ;
887+ memcpy (buf, reinterpret_cast <const char *>(data->passphrase .data ), len);
888+ return len;
889+ }
890+ } // namespace
891+ #endif
892+
854893// Algorithm: http://howardhinnant.github.io/date_algorithms.html
855894constexpr int days_from_epoch (int y, unsigned m, unsigned d) {
856895 y -= m <= 2 ;
@@ -3585,7 +3624,7 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey(
35853624 const Buffer<const unsigned char >& buffer) {
35863625 static constexpr auto keyOrError = [](EVPKeyPointer pkey,
35873626 bool had_passphrase = false ) {
3588- if (int err = ERR_peek_error ()) {
3627+ if (unsigned long err = ERR_peek_error ()) { // NOLINT(runtime/int)
35893628 if (ERR_GET_LIB (err) == ERR_LIB_PEM &&
35903629 ERR_GET_REASON (err) == PEM_R_BAD_PASSWORD_READ && !had_passphrase) {
35913630 return ParseKeyResult (PKParseError::NEED_PASSPHRASE );
@@ -3645,6 +3684,99 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey(
36453684 };
36463685}
36473686
3687+ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryLoadPrivateKeyFromStore (
3688+ const StorePrivateKeyConfig& config) {
3689+ #if !NCRYPTO_USE_OPENSSL3_PROVIDER
3690+ return ParseKeyResult (PKParseError::FAILED );
3691+ #else
3692+ // The error queue is left populated on failure so the caller can surface a
3693+ // `code` and an `opensslErrorStack`, matching TryParsePrivateKey(), and is
3694+ // cleared on success because decoders leave entries behind either way.
3695+ std::string uri_str (config.uri );
3696+ std::string properties_str;
3697+ const char * properties = nullptr ;
3698+ if (config.properties .has_value () && !config.properties ->empty ()) {
3699+ properties_str.assign (config.properties ->data (), config.properties ->size ());
3700+ properties = properties_str.c_str ();
3701+ }
3702+
3703+ // config.passphrase outlives this call, so no copy is needed.
3704+ Buffer<char > passbuf{.data = nullptr , .len = 0 };
3705+ if (config.passphrase .has_value ()) {
3706+ passbuf.data = const_cast <char *>(config.passphrase ->data );
3707+ passbuf.len = config.passphrase ->len ;
3708+ }
3709+ StorePassphraseData passphrase_data{
3710+ .passphrase = passbuf,
3711+ .has_passphrase = config.passphrase .has_value (),
3712+ };
3713+ // Declared before ctx so that reverse destruction closes the store first;
3714+ // it holds both for its lifetime.
3715+ UIMethodPointer ui_method (
3716+ UI_UTIL_wrap_read_pem_callback (StorePasswordCallback, 0 ));
3717+ if (!ui_method) return ParseKeyResult (PKParseError::FAILED );
3718+
3719+ // Errors from loaders that declined the URI are retained oldest-first, so the
3720+ // newest entry is the loader that actually handled it. Must run before ctx is
3721+ // destroyed, since OSSL_STORE_close() can push errors of its own.
3722+ const auto failed = [&](bool missing_passphrase) {
3723+ if (missing_passphrase)
3724+ return ParseKeyResult (PKParseError::NEED_PASSPHRASE );
3725+ return ParseKeyResult (PKParseError::FAILED , ERR_peek_last_error ());
3726+ };
3727+
3728+ const OSSL_PARAM store_params[] = {OSSL_PARAM_END };
3729+ StoreCtxPointer ctx (OSSL_STORE_open_ex (uri_str.c_str (),
3730+ nullptr ,
3731+ properties,
3732+ ui_method.get (),
3733+ &passphrase_data,
3734+ store_params,
3735+ nullptr ,
3736+ nullptr ));
3737+ if (!ctx) return failed (passphrase_data.missing_passphrase );
3738+
3739+ if (!OSSL_STORE_expect (ctx.get (), OSSL_STORE_INFO_PKEY )) {
3740+ return failed (passphrase_data.missing_passphrase );
3741+ }
3742+
3743+ EVPKeyPointer pkey;
3744+ bool store_error = false ;
3745+ while (!OSSL_STORE_eof (ctx.get ())) {
3746+ OSSL_STORE_INFO * info = OSSL_STORE_load (ctx.get ());
3747+ if (info == nullptr ) {
3748+ if (OSSL_STORE_error (ctx.get ())) {
3749+ store_error = true ;
3750+ break ;
3751+ }
3752+ continue ;
3753+ }
3754+ if (OSSL_STORE_INFO_get_type (info) == OSSL_STORE_INFO_PKEY ) {
3755+ EVP_PKEY * raw_pkey = OSSL_STORE_INFO_get1_PKEY (info);
3756+ if (raw_pkey != nullptr ) {
3757+ pkey = EVPKeyPointer (raw_pkey);
3758+ } else {
3759+ store_error = true ;
3760+ }
3761+ }
3762+ OSSL_STORE_INFO_free (info);
3763+ if (pkey || store_error) break ;
3764+ }
3765+
3766+ // missing_passphrase is sticky, so a key that loaded anyway wins over it.
3767+ if (pkey) {
3768+ ctx.reset ();
3769+ ERR_clear_error ();
3770+ return ParseKeyResult (std::move (pkey));
3771+ }
3772+
3773+ if (passphrase_data.missing_passphrase || store_error) {
3774+ return failed (passphrase_data.missing_passphrase );
3775+ }
3776+ return ParseKeyResult (PKParseError::NOT_RECOGNIZED );
3777+ #endif
3778+ }
3779+
36483780Result<BIOPointer, bool > EVPKeyPointer::writePrivateKey (
36493781 const PrivateKeyEncodingConfig& config) const {
36503782 if (config.format == PKFormatType::JWK ) {
@@ -3686,6 +3818,8 @@ Result<BIOPointer, bool> EVPKeyPointer::writePrivateKey(
36863818#else
36873819 RSA * rsa = EVP_PKEY_get0_RSA (get ());
36883820#endif
3821+ if (rsa == nullptr ) return Result<BIOPointer, bool >(false );
3822+
36893823 switch (config.format ) {
36903824 case PKFormatType::PEM : {
36913825 err = PEM_write_bio_RSAPrivateKey (
@@ -3761,6 +3895,8 @@ Result<BIOPointer, bool> EVPKeyPointer::writePrivateKey(
37613895#else
37623896 EC_KEY * ec = EVP_PKEY_get0_EC_KEY (get ());
37633897#endif
3898+ if (ec == nullptr ) return Result<BIOPointer, bool >(false );
3899+
37643900 switch (config.format ) {
37653901 case PKFormatType::PEM : {
37663902 err = PEM_write_bio_ECPrivateKey (
@@ -3827,6 +3963,8 @@ Result<BIOPointer, bool> EVPKeyPointer::writePublicKey(
38273963#else
38283964 RSA * rsa = EVP_PKEY_get0_RSA (get ());
38293965#endif
3966+ if (rsa == nullptr ) return Result<BIOPointer, bool >(false );
3967+
38303968 if (config.format == ncrypto::EVPKeyPointer::PKFormatType::PEM ) {
38313969 // Encode PKCS#1 as PEM.
38323970 if (PEM_write_bio_RSAPublicKey (bio.get (), rsa) != 1 ) {
@@ -3855,10 +3993,28 @@ Result<BIOPointer, bool> EVPKeyPointer::writePublicKey(
38553993
38563994 if (config.format == ncrypto::EVPKeyPointer::PKFormatType::PEM ) {
38573995 // Encode SPKI as PEM.
3996+ #if NCRYPTO_USE_OPENSSL3_PROVIDER
3997+ // Build the SubjectPublicKeyInfo wrapper explicitly before PEM encoding.
3998+ // Provider-backed keys can fail the direct PEM_write_bio_PUBKEY() path even
3999+ // when OpenSSL can materialize the public wrapper with X509_PUBKEY_set().
4000+ X509_PUBKEY * pubkey = nullptr ;
4001+ if (X509_PUBKEY_set (&pubkey, get ()) != 1 ) {
4002+ X509_PUBKEY_free (pubkey);
4003+ return Result<BIOPointer, bool >(false ,
4004+ mark_pop_error_on_return.peekError ());
4005+ }
4006+ X509PubKeyPointer pubkey_ptr (pubkey);
4007+ if (PEM_write_bio_X509_PUBKEY (bio.get (), pubkey_ptr.get ()) != 1 ) {
4008+ return Result<BIOPointer, bool >(false ,
4009+ mark_pop_error_on_return.peekError ());
4010+ }
4011+ #else
4012+ // Non-OpenSSL >= 3 builds do not all declare PEM_write_bio_X509_PUBKEY().
38584013 if (PEM_write_bio_PUBKEY (bio.get (), get ()) != 1 ) {
38594014 return Result<BIOPointer, bool >(false ,
38604015 mark_pop_error_on_return.peekError ());
38614016 }
4017+ #endif
38624018 return bio;
38634019 }
38644020
@@ -3929,21 +4085,37 @@ std::optional<uint32_t> EVPKeyPointer::getBytesOfRS() const {
39294085 bits = BignumPointer::GetBitCount (q.get ());
39304086#else
39314087 const DSA * dsa_key = EVP_PKEY_get0_DSA (get ());
4088+ bool has_bits = false ;
39324089 // Both r and s are computed mod q, so their width is limited by that of q.
3933- bits = BignumPointer::GetBitCount (DSA_get0_q (dsa_key));
4090+ if (dsa_key != nullptr ) {
4091+ const BIGNUM * q = DSA_get0_q (dsa_key);
4092+ if (q != nullptr ) {
4093+ bits = BignumPointer::GetBitCount (q);
4094+ has_bits = true ;
4095+ }
4096+ }
4097+ if (!has_bits) return std::nullopt ;
39344098#endif
39354099 } else if (id == EVP_PKEY_EC ) {
39364100#if NCRYPTO_USE_OPENSSL3_PROVIDER
39374101 Ec ec (get ());
39384102 if (!ec) return std::nullopt ;
3939- bits = EC_GROUP_order_bits (ec.getGroup ());
4103+ const EC_GROUP * group = ec.getGroup ();
4104+ if (group == nullptr ) return std::nullopt ;
4105+ bits = EC_GROUP_order_bits (group);
39404106#else
3941- bits = EC_GROUP_order_bits (ECKeyPointer::GetGroup (*this ));
4107+ const EC_KEY * ec_key = EVP_PKEY_get0_EC_KEY (get ());
4108+ if (ec_key == nullptr ) return std::nullopt ;
4109+ const EC_GROUP * group = ECKeyPointer::GetGroup (ec_key);
4110+ if (group == nullptr ) return std::nullopt ;
4111+ bits = EC_GROUP_order_bits (group);
39424112#endif
39434113 } else {
39444114 return std::nullopt ;
39454115 }
39464116
4117+ if (bits <= 0 ) return std::nullopt ;
4118+
39474119 return (bits + 7 ) / 8 ;
39484120}
39494121
@@ -3982,12 +4154,12 @@ EVPKeyPointer::operator Dsa() const {
39824154
39834155bool EVPKeyPointer::validateDsaParameters () const {
39844156 if (!pkey_) return false ;
3985- /* Validate DSA2 parameters from FIPS 186-4 */
39864157#if OPENSSL_VERSION_MAJOR >= 3
39874158 if (EVP_default_properties_is_fips_enabled (nullptr ) && EVP_PKEY_DSA == id ()) {
39884159#else
39894160 if (FIPS_mode () && EVP_PKEY_DSA == id ()) {
39904161#endif
4162+ // Validate DSA2 parameters from FIPS 186-4.
39914163#if NCRYPTO_USE_OPENSSL3_PROVIDER
39924164 DeleteFnPtr<BIGNUM , BN_free> p;
39934165 DeleteFnPtr<BIGNUM , BN_free> q;
@@ -3999,9 +4171,11 @@ bool EVPKeyPointer::validateDsaParameters() const {
39994171 const BIGNUM * q_value = q.get ();
40004172#else
40014173 const DSA * dsa = EVP_PKEY_get0_DSA (pkey_.get ());
4174+ if (dsa == nullptr ) return false ;
40024175 const BIGNUM * p;
40034176 const BIGNUM * q;
40044177 DSA_get0_pqg (dsa, &p, &q, nullptr );
4178+ if (p == nullptr || q == nullptr ) return false ;
40054179 const BIGNUM * p_value = p;
40064180 const BIGNUM * q_value = q;
40074181#endif
@@ -6452,9 +6626,14 @@ DataPointer EVPMDCtxPointer::sign(
64526626
64536627bool EVPMDCtxPointer::verify (const Buffer<const unsigned char >& buf,
64546628 const Buffer<const unsigned char >& sig) const {
6455- if (!ctx_) return false ;
6456- int ret = EVP_DigestVerify (ctx_.get (), sig.data , sig.len , buf.data , buf.len );
6457- return ret == 1 ;
6629+ return verifyOneShot (buf, sig) == 1 ;
6630+ }
6631+
6632+ int EVPMDCtxPointer::verifyOneShot (
6633+ const Buffer<const unsigned char >& buf,
6634+ const Buffer<const unsigned char >& sig) const {
6635+ if (!ctx_) return -1 ;
6636+ return EVP_DigestVerify (ctx_.get (), sig.data , sig.len , buf.data , buf.len );
64586637}
64596638
64606639EVPMDCtxPointer EVPMDCtxPointer::New () {
0 commit comments