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 ;
@@ -3584,7 +3623,7 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey(
35843623 const Buffer<const unsigned char >& buffer) {
35853624 static constexpr auto keyOrError = [](EVPKeyPointer pkey,
35863625 bool had_passphrase = false ) {
3587- if (int err = ERR_peek_error ()) {
3626+ if (unsigned long err = ERR_peek_error ()) { // NOLINT(runtime/int)
35883627 if (ERR_GET_LIB (err) == ERR_LIB_PEM &&
35893628 ERR_GET_REASON (err) == PEM_R_BAD_PASSWORD_READ && !had_passphrase) {
35903629 return ParseKeyResult (PKParseError::NEED_PASSPHRASE );
@@ -3644,6 +3683,98 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey(
36443683 };
36453684}
36463685
3686+ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryLoadPrivateKeyFromStore (
3687+ const StorePrivateKeyConfig& config) {
3688+ #if !NCRYPTO_USE_OPENSSL3_PROVIDER
3689+ return ParseKeyResult (PKParseError::FAILED );
3690+ #else
3691+ // The error queue is left populated on failure so the caller can surface a
3692+ // `code` and an `opensslErrorStack`, matching TryParsePrivateKey(), and is
3693+ // cleared on success because decoders leave entries behind either way.
3694+ std::string uri_str (config.uri );
3695+ std::string properties_str;
3696+ const char * properties = nullptr ;
3697+ if (config.properties .has_value () && !config.properties ->empty ()) {
3698+ properties_str.assign (config.properties ->data (), config.properties ->size ());
3699+ properties = properties_str.c_str ();
3700+ }
3701+
3702+ // config.passphrase outlives this call, so no copy is needed.
3703+ Buffer<char > passbuf{.data = nullptr , .len = 0 };
3704+ if (config.passphrase .has_value ()) {
3705+ passbuf.data = const_cast <char *>(config.passphrase ->data );
3706+ passbuf.len = config.passphrase ->len ;
3707+ }
3708+ StorePassphraseData passphrase_data{
3709+ .passphrase = passbuf,
3710+ .has_passphrase = config.passphrase .has_value (),
3711+ };
3712+ // Declared before ctx so that reverse destruction closes the store first;
3713+ // it holds both for its lifetime.
3714+ UIMethodPointer ui_method (
3715+ UI_UTIL_wrap_read_pem_callback (StorePasswordCallback, 0 ));
3716+ if (!ui_method) return ParseKeyResult (PKParseError::FAILED );
3717+
3718+ // Errors from loaders that declined the URI are retained oldest-first, so the
3719+ // newest entry is the loader that actually handled it. Must run before ctx is
3720+ // destroyed, since OSSL_STORE_close() can push errors of its own.
3721+ const auto failed = [&](bool missing_passphrase) {
3722+ if (missing_passphrase)
3723+ return ParseKeyResult (PKParseError::NEED_PASSPHRASE );
3724+ return ParseKeyResult (PKParseError::FAILED , ERR_peek_last_error ());
3725+ };
3726+
3727+ const OSSL_PARAM store_params[] = {OSSL_PARAM_END };
3728+ StoreCtxPointer ctx (OSSL_STORE_open_ex (uri_str.c_str (),
3729+ nullptr ,
3730+ properties,
3731+ ui_method.get (),
3732+ &passphrase_data,
3733+ store_params,
3734+ nullptr ,
3735+ nullptr ));
3736+ if (!ctx) return failed (passphrase_data.missing_passphrase );
3737+
3738+ if (!OSSL_STORE_expect (ctx.get (), OSSL_STORE_INFO_PKEY )) {
3739+ return failed (passphrase_data.missing_passphrase );
3740+ }
3741+
3742+ EVPKeyPointer pkey;
3743+ bool store_error = false ;
3744+ while (!OSSL_STORE_eof (ctx.get ())) {
3745+ OSSL_STORE_INFO * info = OSSL_STORE_load (ctx.get ());
3746+ if (info == nullptr ) {
3747+ if (OSSL_STORE_error (ctx.get ())) {
3748+ store_error = true ;
3749+ break ;
3750+ }
3751+ continue ;
3752+ }
3753+ if (OSSL_STORE_INFO_get_type (info) == OSSL_STORE_INFO_PKEY ) {
3754+ EVP_PKEY * raw_pkey = OSSL_STORE_INFO_get1_PKEY (info);
3755+ if (raw_pkey != nullptr ) {
3756+ pkey = EVPKeyPointer (raw_pkey);
3757+ } else {
3758+ store_error = true ;
3759+ }
3760+ }
3761+ OSSL_STORE_INFO_free (info);
3762+ if (pkey || store_error) break ;
3763+ }
3764+
3765+ // missing_passphrase is sticky, so a key that loaded anyway wins over it.
3766+ if (pkey) {
3767+ ERR_clear_error ();
3768+ return ParseKeyResult (std::move (pkey));
3769+ }
3770+
3771+ if (passphrase_data.missing_passphrase || store_error) {
3772+ return failed (passphrase_data.missing_passphrase );
3773+ }
3774+ return ParseKeyResult (PKParseError::NOT_RECOGNIZED );
3775+ #endif
3776+ }
3777+
36473778Result<BIOPointer, bool > EVPKeyPointer::writePrivateKey (
36483779 const PrivateKeyEncodingConfig& config) const {
36493780 if (config.format == PKFormatType::JWK ) {
@@ -3685,6 +3816,8 @@ Result<BIOPointer, bool> EVPKeyPointer::writePrivateKey(
36853816#else
36863817 RSA * rsa = EVP_PKEY_get0_RSA (get ());
36873818#endif
3819+ if (rsa == nullptr ) return Result<BIOPointer, bool >(false );
3820+
36883821 switch (config.format ) {
36893822 case PKFormatType::PEM : {
36903823 err = PEM_write_bio_RSAPrivateKey (
@@ -3760,6 +3893,8 @@ Result<BIOPointer, bool> EVPKeyPointer::writePrivateKey(
37603893#else
37613894 EC_KEY * ec = EVP_PKEY_get0_EC_KEY (get ());
37623895#endif
3896+ if (ec == nullptr ) return Result<BIOPointer, bool >(false );
3897+
37633898 switch (config.format ) {
37643899 case PKFormatType::PEM : {
37653900 err = PEM_write_bio_ECPrivateKey (
@@ -3826,6 +3961,8 @@ Result<BIOPointer, bool> EVPKeyPointer::writePublicKey(
38263961#else
38273962 RSA * rsa = EVP_PKEY_get0_RSA (get ());
38283963#endif
3964+ if (rsa == nullptr ) return Result<BIOPointer, bool >(false );
3965+
38293966 if (config.format == ncrypto::EVPKeyPointer::PKFormatType::PEM ) {
38303967 // Encode PKCS#1 as PEM.
38313968 if (PEM_write_bio_RSAPublicKey (bio.get (), rsa) != 1 ) {
@@ -3854,10 +3991,28 @@ Result<BIOPointer, bool> EVPKeyPointer::writePublicKey(
38543991
38553992 if (config.format == ncrypto::EVPKeyPointer::PKFormatType::PEM ) {
38563993 // Encode SPKI as PEM.
3994+ #if NCRYPTO_USE_OPENSSL3_PROVIDER
3995+ // Build the SubjectPublicKeyInfo wrapper explicitly before PEM encoding.
3996+ // Provider-backed keys can fail the direct PEM_write_bio_PUBKEY() path even
3997+ // when OpenSSL can materialize the public wrapper with X509_PUBKEY_set().
3998+ X509_PUBKEY * pubkey = nullptr ;
3999+ if (X509_PUBKEY_set (&pubkey, get ()) != 1 ) {
4000+ X509_PUBKEY_free (pubkey);
4001+ return Result<BIOPointer, bool >(false ,
4002+ mark_pop_error_on_return.peekError ());
4003+ }
4004+ X509PubKeyPointer pubkey_ptr (pubkey);
4005+ if (PEM_write_bio_X509_PUBKEY (bio.get (), pubkey_ptr.get ()) != 1 ) {
4006+ return Result<BIOPointer, bool >(false ,
4007+ mark_pop_error_on_return.peekError ());
4008+ }
4009+ #else
4010+ // Non-OpenSSL >= 3 builds do not all declare PEM_write_bio_X509_PUBKEY().
38574011 if (PEM_write_bio_PUBKEY (bio.get (), get ()) != 1 ) {
38584012 return Result<BIOPointer, bool >(false ,
38594013 mark_pop_error_on_return.peekError ());
38604014 }
4015+ #endif
38614016 return bio;
38624017 }
38634018
@@ -3928,21 +4083,37 @@ std::optional<uint32_t> EVPKeyPointer::getBytesOfRS() const {
39284083 bits = BignumPointer::GetBitCount (q.get ());
39294084#else
39304085 const DSA * dsa_key = EVP_PKEY_get0_DSA (get ());
4086+ bool has_bits = false ;
39314087 // Both r and s are computed mod q, so their width is limited by that of q.
3932- bits = BignumPointer::GetBitCount (DSA_get0_q (dsa_key));
4088+ if (dsa_key != nullptr ) {
4089+ const BIGNUM * q = DSA_get0_q (dsa_key);
4090+ if (q != nullptr ) {
4091+ bits = BignumPointer::GetBitCount (q);
4092+ has_bits = true ;
4093+ }
4094+ }
4095+ if (!has_bits) return std::nullopt ;
39334096#endif
39344097 } else if (id == EVP_PKEY_EC ) {
39354098#if NCRYPTO_USE_OPENSSL3_PROVIDER
39364099 Ec ec (get ());
39374100 if (!ec) return std::nullopt ;
3938- bits = EC_GROUP_order_bits (ec.getGroup ());
4101+ const EC_GROUP * group = ec.getGroup ();
4102+ if (group == nullptr ) return std::nullopt ;
4103+ bits = EC_GROUP_order_bits (group);
39394104#else
3940- bits = EC_GROUP_order_bits (ECKeyPointer::GetGroup (*this ));
4105+ const EC_KEY * ec_key = EVP_PKEY_get0_EC_KEY (get ());
4106+ if (ec_key == nullptr ) return std::nullopt ;
4107+ const EC_GROUP * group = ECKeyPointer::GetGroup (ec_key);
4108+ if (group == nullptr ) return std::nullopt ;
4109+ bits = EC_GROUP_order_bits (group);
39414110#endif
39424111 } else {
39434112 return std::nullopt ;
39444113 }
39454114
4115+ if (bits <= 0 ) return std::nullopt ;
4116+
39464117 return (bits + 7 ) / 8 ;
39474118}
39484119
@@ -3981,12 +4152,12 @@ EVPKeyPointer::operator Dsa() const {
39814152
39824153bool EVPKeyPointer::validateDsaParameters () const {
39834154 if (!pkey_) return false ;
3984- /* Validate DSA2 parameters from FIPS 186-4 */
39854155#if OPENSSL_VERSION_MAJOR >= 3
39864156 if (EVP_default_properties_is_fips_enabled (nullptr ) && EVP_PKEY_DSA == id ()) {
39874157#else
39884158 if (FIPS_mode () && EVP_PKEY_DSA == id ()) {
39894159#endif
4160+ // Validate DSA2 parameters from FIPS 186-4.
39904161#if NCRYPTO_USE_OPENSSL3_PROVIDER
39914162 DeleteFnPtr<BIGNUM , BN_free> p;
39924163 DeleteFnPtr<BIGNUM , BN_free> q;
@@ -3998,9 +4169,11 @@ bool EVPKeyPointer::validateDsaParameters() const {
39984169 const BIGNUM * q_value = q.get ();
39994170#else
40004171 const DSA * dsa = EVP_PKEY_get0_DSA (pkey_.get ());
4172+ if (dsa == nullptr ) return false ;
40014173 const BIGNUM * p;
40024174 const BIGNUM * q;
40034175 DSA_get0_pqg (dsa, &p, &q, nullptr );
4176+ if (p == nullptr || q == nullptr ) return false ;
40044177 const BIGNUM * p_value = p;
40054178 const BIGNUM * q_value = q;
40064179#endif
@@ -6445,9 +6618,14 @@ DataPointer EVPMDCtxPointer::sign(
64456618
64466619bool EVPMDCtxPointer::verify (const Buffer<const unsigned char >& buf,
64476620 const Buffer<const unsigned char >& sig) const {
6448- if (!ctx_) return false ;
6449- int ret = EVP_DigestVerify (ctx_.get (), sig.data , sig.len , buf.data , buf.len );
6450- return ret == 1 ;
6621+ return verifyOneShot (buf, sig) == 1 ;
6622+ }
6623+
6624+ int EVPMDCtxPointer::verifyOneShot (
6625+ const Buffer<const unsigned char >& buf,
6626+ const Buffer<const unsigned char >& sig) const {
6627+ if (!ctx_) return -1 ;
6628+ return EVP_DigestVerify (ctx_.get (), sig.data , sig.len , buf.data , buf.len );
64516629}
64526630
64536631EVPMDCtxPointer EVPMDCtxPointer::New () {
0 commit comments