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
@@ -75,6 +77,17 @@ namespace {
7577using BignumCtxPointer = DeleteFnPtr<BN_CTX , BN_CTX_free>;
7678using BignumGenCallbackPointer = DeleteFnPtr<BN_GENCB , BN_GENCB_free>;
7779using NetscapeSPKIPointer = DeleteFnPtr<NETSCAPE_SPKI , NETSCAPE_SPKI_free>;
80+ using X509PubKeyPointer = DeleteFnPtr<X509_PUBKEY , X509_PUBKEY_free>;
81+
82+ #if OPENSSL_VERSION_MAJOR >= 3 && !defined(OPENSSL_IS_BORINGSSL)
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
7891
7992const EVP_CIPHER * GetCipherCtxCipher (const EVP_CIPHER_CTX * ctx) {
8093#if NCRYPTO_USE_OPENSSL3_PROVIDER
@@ -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
@@ -851,6 +864,26 @@ int PasswordCallback(char* buf, int size, int rwflag, void* u) {
851864 return -1 ;
852865}
853866
867+ struct StorePassphraseData {
868+ Buffer<char > passphrase{.data = nullptr , .len = 0 };
869+ bool has_passphrase = false ;
870+ bool missing_passphrase = false ;
871+ };
872+
873+ int StorePasswordCallback (char * buf, int size, int rwflag, void * u) {
874+ auto data = static_cast <StorePassphraseData*>(u);
875+ if (data == nullptr || !data->has_passphrase ) {
876+ if (data != nullptr ) data->missing_passphrase = true ;
877+ return -1 ;
878+ }
879+
880+ size_t buflen = static_cast <size_t >(size);
881+ size_t len = data->passphrase .len ;
882+ if (buflen < len) return -1 ;
883+ memcpy (buf, reinterpret_cast <const char *>(data->passphrase .data ), len);
884+ return len;
885+ }
886+
854887// Algorithm: http://howardhinnant.github.io/date_algorithms.html
855888constexpr int days_from_epoch (int y, unsigned m, unsigned d) {
856889 y -= m <= 2 ;
@@ -3584,7 +3617,7 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey(
35843617 const Buffer<const unsigned char >& buffer) {
35853618 static constexpr auto keyOrError = [](EVPKeyPointer pkey,
35863619 bool had_passphrase = false ) {
3587- if (int err = ERR_peek_error ()) {
3620+ if (unsigned long err = ERR_peek_error ()) { // NOLINT(runtime/int)
35883621 if (ERR_GET_LIB (err) == ERR_LIB_PEM &&
35893622 ERR_GET_REASON (err) == PEM_R_BAD_PASSWORD_READ && !had_passphrase) {
35903623 return ParseKeyResult (PKParseError::NEED_PASSPHRASE );
@@ -3644,6 +3677,97 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey(
36443677 };
36453678}
36463679
3680+ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryLoadPrivateKeyFromStore (
3681+ const StorePrivateKeyConfig& config) {
3682+ #if defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_MAJOR < 3
3683+ return ParseKeyResult (PKParseError::FAILED );
3684+ #else
3685+ // Note: the OpenSSL error queue is deliberately left populated on failure so
3686+ // that the caller can surface a `code` and an `opensslErrorStack`, matching
3687+ // TryParsePrivateKey(). It is cleared explicitly on success, because loaders
3688+ // that decline the URI (OSSL_STORE_open_ex() always probes the `file` loader
3689+ // first) leave their errors behind even when a later loader succeeds.
3690+ std::string uri_str (config.uri );
3691+ std::string properties_str;
3692+ const char * properties = nullptr ;
3693+ if (config.properties .has_value ()) {
3694+ properties_str.assign (config.properties ->data (), config.properties ->size ());
3695+ properties = properties_str.c_str ();
3696+ }
3697+
3698+ std::string passphrase_str;
3699+ Buffer<char > passbuf{.data = nullptr , .len = 0 };
3700+ if (config.passphrase .has_value ()) {
3701+ passphrase_str.assign (config.passphrase ->data , config.passphrase ->len );
3702+ passbuf.data = passphrase_str.data ();
3703+ passbuf.len = passphrase_str.size ();
3704+ }
3705+ StorePassphraseData passphrase_data{
3706+ .passphrase = passbuf,
3707+ .has_passphrase = config.passphrase .has_value (),
3708+ };
3709+ UIMethodPointer ui_method (
3710+ UI_UTIL_wrap_read_pem_callback (StorePasswordCallback, 0 ));
3711+ if (!ui_method) return ParseKeyResult (PKParseError::FAILED );
3712+
3713+ // A loader that declines the URI reports it through the error queue, so the
3714+ // most recent entry describes the loader that actually handled it. Peeking
3715+ // the oldest entry would instead report the `file` loader probe that
3716+ // OSSL_STORE_open_ex() always performs first for URIs without an authority.
3717+ const auto failed = [&](bool missing_passphrase) {
3718+ if (missing_passphrase)
3719+ return ParseKeyResult (PKParseError::NEED_PASSPHRASE );
3720+ return ParseKeyResult (PKParseError::FAILED , ERR_peek_last_error ());
3721+ };
3722+
3723+ const OSSL_PARAM store_params[] = {OSSL_PARAM_END };
3724+ StoreCtxPointer ctx (OSSL_STORE_open_ex (uri_str.c_str (),
3725+ nullptr ,
3726+ properties,
3727+ ui_method.get (),
3728+ &passphrase_data,
3729+ store_params,
3730+ nullptr ,
3731+ nullptr ));
3732+ if (!ctx) return failed (passphrase_data.missing_passphrase );
3733+
3734+ if (!OSSL_STORE_expect (ctx.get (), OSSL_STORE_INFO_PKEY )) {
3735+ return failed (passphrase_data.missing_passphrase );
3736+ }
3737+
3738+ EVPKeyPointer pkey;
3739+ bool store_error = false ;
3740+ while (!OSSL_STORE_eof (ctx.get ())) {
3741+ OSSL_STORE_INFO * info = OSSL_STORE_load (ctx.get ());
3742+ if (info == nullptr ) {
3743+ if (OSSL_STORE_error (ctx.get ())) {
3744+ store_error = true ;
3745+ break ;
3746+ }
3747+ continue ;
3748+ }
3749+ if (OSSL_STORE_INFO_get_type (info) == OSSL_STORE_INFO_PKEY ) {
3750+ EVP_PKEY * raw_pkey = OSSL_STORE_INFO_get1_PKEY (info);
3751+ if (raw_pkey != nullptr ) {
3752+ pkey = EVPKeyPointer (raw_pkey);
3753+ } else {
3754+ store_error = true ;
3755+ }
3756+ }
3757+ OSSL_STORE_INFO_free (info);
3758+ if (pkey || store_error) break ;
3759+ }
3760+
3761+ if (passphrase_data.missing_passphrase || store_error) {
3762+ return failed (passphrase_data.missing_passphrase );
3763+ }
3764+ if (!pkey) return ParseKeyResult (PKParseError::NOT_RECOGNIZED );
3765+
3766+ ERR_clear_error ();
3767+ return ParseKeyResult (std::move (pkey));
3768+ #endif
3769+ }
3770+
36473771Result<BIOPointer, bool > EVPKeyPointer::writePrivateKey (
36483772 const PrivateKeyEncodingConfig& config) const {
36493773 if (config.format == PKFormatType::JWK ) {
@@ -3685,6 +3809,8 @@ Result<BIOPointer, bool> EVPKeyPointer::writePrivateKey(
36853809#else
36863810 RSA * rsa = EVP_PKEY_get0_RSA (get ());
36873811#endif
3812+ if (rsa == nullptr ) return Result<BIOPointer, bool >(false );
3813+
36883814 switch (config.format ) {
36893815 case PKFormatType::PEM : {
36903816 err = PEM_write_bio_RSAPrivateKey (
@@ -3760,6 +3886,8 @@ Result<BIOPointer, bool> EVPKeyPointer::writePrivateKey(
37603886#else
37613887 EC_KEY * ec = EVP_PKEY_get0_EC_KEY (get ());
37623888#endif
3889+ if (ec == nullptr ) return Result<BIOPointer, bool >(false );
3890+
37633891 switch (config.format ) {
37643892 case PKFormatType::PEM : {
37653893 err = PEM_write_bio_ECPrivateKey (
@@ -3826,6 +3954,8 @@ Result<BIOPointer, bool> EVPKeyPointer::writePublicKey(
38263954#else
38273955 RSA * rsa = EVP_PKEY_get0_RSA (get ());
38283956#endif
3957+ if (rsa == nullptr ) return Result<BIOPointer, bool >(false );
3958+
38293959 if (config.format == ncrypto::EVPKeyPointer::PKFormatType::PEM ) {
38303960 // Encode PKCS#1 as PEM.
38313961 if (PEM_write_bio_RSAPublicKey (bio.get (), rsa) != 1 ) {
@@ -3854,10 +3984,28 @@ Result<BIOPointer, bool> EVPKeyPointer::writePublicKey(
38543984
38553985 if (config.format == ncrypto::EVPKeyPointer::PKFormatType::PEM ) {
38563986 // Encode SPKI as PEM.
3987+ #if OPENSSL_VERSION_MAJOR == 3
3988+ // Build the SubjectPublicKeyInfo wrapper explicitly before PEM encoding.
3989+ // Provider-backed keys can fail the direct PEM_write_bio_PUBKEY() path even
3990+ // when OpenSSL can materialize the public wrapper with X509_PUBKEY_set().
3991+ X509_PUBKEY * pubkey = nullptr ;
3992+ if (X509_PUBKEY_set (&pubkey, get ()) != 1 ) {
3993+ X509_PUBKEY_free (pubkey);
3994+ return Result<BIOPointer, bool >(false ,
3995+ mark_pop_error_on_return.peekError ());
3996+ }
3997+ X509PubKeyPointer pubkey_ptr (pubkey);
3998+ if (PEM_write_bio_X509_PUBKEY (bio.get (), pubkey_ptr.get ()) != 1 ) {
3999+ return Result<BIOPointer, bool >(false ,
4000+ mark_pop_error_on_return.peekError ());
4001+ }
4002+ #else
4003+ // Non-OpenSSL 3 builds do not all declare PEM_write_bio_X509_PUBKEY().
38574004 if (PEM_write_bio_PUBKEY (bio.get (), get ()) != 1 ) {
38584005 return Result<BIOPointer, bool >(false ,
38594006 mark_pop_error_on_return.peekError ());
38604007 }
4008+ #endif
38614009 return bio;
38624010 }
38634011
@@ -3928,21 +4076,37 @@ std::optional<uint32_t> EVPKeyPointer::getBytesOfRS() const {
39284076 bits = BignumPointer::GetBitCount (q.get ());
39294077#else
39304078 const DSA * dsa_key = EVP_PKEY_get0_DSA (get ());
4079+ bool has_bits = false ;
39314080 // 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));
4081+ if (dsa_key != nullptr ) {
4082+ const BIGNUM * q = DSA_get0_q (dsa_key);
4083+ if (q != nullptr ) {
4084+ bits = BignumPointer::GetBitCount (q);
4085+ has_bits = true ;
4086+ }
4087+ }
4088+ if (!has_bits) return std::nullopt ;
39334089#endif
39344090 } else if (id == EVP_PKEY_EC ) {
39354091#if NCRYPTO_USE_OPENSSL3_PROVIDER
39364092 Ec ec (get ());
39374093 if (!ec) return std::nullopt ;
3938- bits = EC_GROUP_order_bits (ec.getGroup ());
4094+ const EC_GROUP * group = ec.getGroup ();
4095+ if (group == nullptr ) return std::nullopt ;
4096+ bits = EC_GROUP_order_bits (group);
39394097#else
3940- bits = EC_GROUP_order_bits (ECKeyPointer::GetGroup (*this ));
4098+ const EC_KEY * ec_key = EVP_PKEY_get0_EC_KEY (get ());
4099+ if (ec_key == nullptr ) return std::nullopt ;
4100+ const EC_GROUP * group = ECKeyPointer::GetGroup (ec_key);
4101+ if (group == nullptr ) return std::nullopt ;
4102+ bits = EC_GROUP_order_bits (group);
39414103#endif
39424104 } else {
39434105 return std::nullopt ;
39444106 }
39454107
4108+ if (bits <= 0 ) return std::nullopt ;
4109+
39464110 return (bits + 7 ) / 8 ;
39474111}
39484112
@@ -3981,12 +4145,12 @@ EVPKeyPointer::operator Dsa() const {
39814145
39824146bool EVPKeyPointer::validateDsaParameters () const {
39834147 if (!pkey_) return false ;
3984- /* Validate DSA2 parameters from FIPS 186-4 */
39854148#if OPENSSL_VERSION_MAJOR >= 3
39864149 if (EVP_default_properties_is_fips_enabled (nullptr ) && EVP_PKEY_DSA == id ()) {
39874150#else
39884151 if (FIPS_mode () && EVP_PKEY_DSA == id ()) {
39894152#endif
4153+ // Validate DSA2 parameters from FIPS 186-4.
39904154#if NCRYPTO_USE_OPENSSL3_PROVIDER
39914155 DeleteFnPtr<BIGNUM , BN_free> p;
39924156 DeleteFnPtr<BIGNUM , BN_free> q;
@@ -3998,9 +4162,11 @@ bool EVPKeyPointer::validateDsaParameters() const {
39984162 const BIGNUM * q_value = q.get ();
39994163#else
40004164 const DSA * dsa = EVP_PKEY_get0_DSA (pkey_.get ());
4165+ if (dsa == nullptr ) return false ;
40014166 const BIGNUM * p;
40024167 const BIGNUM * q;
40034168 DSA_get0_pqg (dsa, &p, &q, nullptr );
4169+ if (p == nullptr || q == nullptr ) return false ;
40044170 const BIGNUM * p_value = p;
40054171 const BIGNUM * q_value = q;
40064172#endif
@@ -6445,9 +6611,14 @@ DataPointer EVPMDCtxPointer::sign(
64456611
64466612bool EVPMDCtxPointer::verify (const Buffer<const unsigned char >& buf,
64476613 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 ;
6614+ return verifyOneShot (buf, sig) == 1 ;
6615+ }
6616+
6617+ int EVPMDCtxPointer::verifyOneShot (
6618+ const Buffer<const unsigned char >& buf,
6619+ const Buffer<const unsigned char >& sig) const {
6620+ if (!ctx_) return -1 ;
6621+ return EVP_DigestVerify (ctx_.get (), sig.data , sig.len , buf.data , buf.len );
64516622}
64526623
64536624EVPMDCtxPointer EVPMDCtxPointer::New () {
0 commit comments