LCOV - code coverage report
Current view: top level - DCPS/security/SSL - PrivateKey.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 80 115 69.6 %
Date: 2023-04-30 01:32:43 Functions: 10 12 83.3 %

          Line data    Source code
       1             : /*
       2             :  * Distributed under the OpenDDS License.
       3             :  * See: http://www.OpenDDS.org/license.html
       4             :  */
       5             : 
       6             : #include "PrivateKey.h"
       7             : #include "dds/DCPS/security/CommonUtilities.h"
       8             : #include "Err.h"
       9             : 
      10             : #include <openssl/pem.h>
      11             : #include "../OpenSSL_legacy.h"  // Must come after all other OpenSSL includes
      12             : 
      13             : #include <cstring>
      14             : #include <sstream>
      15             : 
      16             : OPENDDS_BEGIN_VERSIONED_NAMESPACE_DECL
      17             : 
      18             : namespace OpenDDS {
      19             : namespace Security {
      20             : namespace SSL {
      21             : 
      22          37 : PrivateKey::PrivateKey(const std::string& uri, const std::string& password)
      23          37 :   : k_(0)
      24             : {
      25          37 :   load(uri, password);
      26          37 : }
      27             : 
      28           0 : PrivateKey::PrivateKey()
      29           0 :   : k_(0)
      30             : {
      31           0 : }
      32             : 
      33          59 : PrivateKey::~PrivateKey()
      34             : {
      35          37 :   if (k_) {
      36          37 :     EVP_PKEY_free(k_);
      37             :   }
      38          59 : }
      39             : 
      40          37 : void PrivateKey::load(const std::string& uri, const std::string& password)
      41             : {
      42             :   using namespace CommonUtilities;
      43             : 
      44          37 :   if (k_) return;
      45             : 
      46          37 :   URI uri_info(uri);
      47             : 
      48          37 :   switch (uri_info.scheme) {
      49          32 :   case URI::URI_FILE:
      50          32 :     k_ = EVP_PKEY_from_pem(uri_info.everything_else, password);
      51          32 :     break;
      52             : 
      53           5 :   case URI::URI_DATA:
      54           5 :     k_ = EVP_PKEY_from_pem_data(uri_info.everything_else, password);
      55           5 :     break;
      56             : 
      57           0 :   case URI::URI_PKCS11:
      58             :   case URI::URI_UNKNOWN:
      59             :   default:
      60           0 :     ACE_ERROR((LM_WARNING,
      61             :                ACE_TEXT("(%P|%t) SSL::PrivateKey::load: WARNING: Unsupported URI scheme in cert path '%C'\n"),
      62             :                uri.c_str()));
      63           0 :     break;
      64             :   }
      65          37 : }
      66             : 
      67             : class sign_implementation
      68             : {
      69             : public:
      70          17 :   explicit sign_implementation(EVP_PKEY* pkey)
      71          17 :     : private_key(pkey), md_ctx(0), pkey_ctx(0)
      72             :   {
      73          17 :   }
      74             : 
      75          17 :   ~sign_implementation()
      76             :   {
      77          17 :     if (md_ctx) {
      78          17 :       EVP_MD_CTX_free(md_ctx);
      79             :     }
      80          17 :   }
      81             : 
      82          17 :   int operator()(const std::vector<const DDS::OctetSeq*>& src,
      83             :                  DDS::OctetSeq& dst)
      84             :   {
      85          17 :     if (!private_key) return 1;
      86             : 
      87          17 :     std::vector<const DDS::OctetSeq*>::const_iterator i, n;
      88          17 :     size_t len = 0u;
      89             : 
      90          17 :     md_ctx = EVP_MD_CTX_new();
      91          17 :     if (!md_ctx) {
      92           0 :       OPENDDS_SSL_LOG_ERR("EVP_MD_CTX_new failed");
      93           0 :       return 1;
      94             :     }
      95             : 
      96          17 :     EVP_MD_CTX_init(md_ctx);
      97             : 
      98          17 :     if (1 != EVP_DigestSignInit(md_ctx, &pkey_ctx, EVP_sha256(), 0,
      99             :                                 private_key)) {
     100           0 :       OPENDDS_SSL_LOG_ERR("EVP_DigestSignInit failed");
     101           0 :       return 1;
     102             :     }
     103             : 
     104             :     // Determine which signature type is being signed
     105          17 :     int pk_id = EVP_PKEY_id(private_key);
     106             : 
     107          17 :     if (pk_id == EVP_PKEY_RSA) {
     108          16 :       if (1 !=
     109          16 :           EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING)) {
     110           0 :         OPENDDS_SSL_LOG_ERR("EVP_PKEY_CTX_set_rsa_padding failed");
     111           0 :         return 1;
     112             :       }
     113             : 
     114          16 :       if (1 != EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, EVP_sha256())) {
     115           0 :         OPENDDS_SSL_LOG_ERR("EVP_PKEY_CTX_set_rsa_mgf1_md failed");
     116           0 :         return 1;
     117             :       }
     118             :     }
     119             : 
     120          17 :     n = src.end();
     121          39 :     for (i = src.begin(); i != n; ++i) {
     122          22 :       if ((*i)->length() > 0) {
     123          21 :         if (1 != EVP_DigestSignUpdate(md_ctx, (*i)->get_buffer(),
     124          21 :                                       (*i)->length())) {
     125           0 :           OPENDDS_SSL_LOG_ERR("EVP_DigestSignUpdate failed");
     126           0 :           return 1;
     127             :         }
     128             :       }
     129             :     }
     130             : 
     131             :     // First call with 0 to extract size
     132          17 :     if (1 != EVP_DigestSignFinal(md_ctx, 0, &len)) {
     133           0 :       OPENDDS_SSL_LOG_ERR("EVP_DigestSignFinal failed");
     134           0 :       return 1;
     135             :     }
     136             : 
     137             :     // Second call to extract the data
     138          17 :     dst.length(static_cast<unsigned int>(len));
     139          17 :     if (1 != EVP_DigestSignFinal(md_ctx, dst.get_buffer(), &len)) {
     140           0 :       OPENDDS_SSL_LOG_ERR("EVP_DigestSignFinal failed");
     141           0 :       return 1;
     142             :     }
     143             : 
     144             :     // The last call to EVP_DigestSignFinal can change the value of len so
     145             :     // reassign the value to len to dst.length.  This happens when using EC
     146          17 :     dst.length(static_cast<unsigned int>(len));
     147             : 
     148          17 :     return 0;
     149             :   }
     150             : 
     151             : private:
     152             :   EVP_PKEY* private_key;
     153             :   EVP_MD_CTX* md_ctx;
     154             :   EVP_PKEY_CTX* pkey_ctx;
     155             : };
     156             : 
     157          17 : int PrivateKey::sign(const std::vector<const DDS::OctetSeq*>& src,
     158             :                      DDS::OctetSeq& dst) const
     159             : {
     160          17 :   sign_implementation sign(k_);
     161          34 :   return sign(src, dst);
     162          17 : }
     163             : 
     164          32 : EVP_PKEY* PrivateKey::EVP_PKEY_from_pem(const std::string& path,
     165             :                                         const std::string& password)
     166             : {
     167          32 :   EVP_PKEY* result = 0;
     168             : 
     169          32 :   BIO* filebuf = BIO_new_file(path.c_str(), "r");
     170          32 :   if (filebuf) {
     171          32 :     result = PEM_read_bio_PrivateKey(filebuf, 0, 0,
     172          32 :                                      password.empty() ? 0 : (void*)password.c_str());
     173          32 :     if (!result) {
     174           0 :       OPENDDS_SSL_LOG_ERR("PEM_read_bio_PrivateKey failed");
     175             :     }
     176             : 
     177          32 :     BIO_free(filebuf);
     178             : 
     179             :   } else {
     180           0 :     std::stringstream errmsg;
     181           0 :     errmsg << "failed to read file '" << path << "' using BIO_new_file";
     182           0 :     OPENDDS_SSL_LOG_ERR(errmsg.str().c_str());
     183           0 :   }
     184             : 
     185          32 :   return result;
     186             : }
     187             : 
     188           5 : EVP_PKEY* PrivateKey::EVP_PKEY_from_pem_data(const std::string& data,
     189             :                                              const std::string& password)
     190             : {
     191           5 :   DDS::OctetSeq original_bytes;
     192             : 
     193             :   // The minus 1 is because path contains a comma in element 0 and that
     194             :   // comma is not included in the cert string
     195           5 :   original_bytes.length(static_cast<unsigned int>(data.size() - 1));
     196           5 :   std::memcpy(original_bytes.get_buffer(), &data[1],
     197           5 :               original_bytes.length());
     198             : 
     199             :   // To appease the other DDS security implementations which
     200             :   // append a null byte at the end of the cert.
     201           5 :   original_bytes.length(original_bytes.length() + 1);
     202           5 :   original_bytes[original_bytes.length() - 1] = 0;
     203             : 
     204           5 :   EVP_PKEY* result = 0;
     205           5 :   BIO* filebuf = BIO_new(BIO_s_mem());
     206             : 
     207           5 :   if (filebuf) {
     208           5 :     if (0 >= BIO_write(filebuf, original_bytes.get_buffer(),
     209           5 :                        original_bytes.length())) {
     210           0 :       OPENDDS_SSL_LOG_ERR("BIO_write failed");
     211             :     }
     212             : 
     213           5 :     result = PEM_read_bio_PrivateKey(filebuf, 0, 0,
     214           5 :                                      password.empty() ? 0 : (void*)password.c_str());
     215             : 
     216           5 :     if (!result) {
     217           0 :       OPENDDS_SSL_LOG_ERR("PEM_read_bio_PrivateKey failed");
     218             :     }
     219             : 
     220           5 :     BIO_free(filebuf);
     221             : 
     222             :   } else {
     223           0 :     std::stringstream errmsg;
     224           0 :     errmsg << "failed to create data '" << data << "' using BIO_new";
     225           0 :     OPENDDS_SSL_LOG_ERR(errmsg.str().c_str());
     226           0 :   }
     227             : 
     228           5 :   return result;
     229           5 : }
     230             : 
     231           0 : bool operator==(const PrivateKey& lhs, const PrivateKey& rhs)
     232             : {
     233           0 :   if (lhs.k_ && rhs.k_) {
     234             : #ifdef OPENSSL_V_3_0
     235           0 :     return 1 == EVP_PKEY_eq(lhs.k_, rhs.k_);
     236             : #else
     237             :     return 1 == EVP_PKEY_cmp(lhs.k_, rhs.k_);
     238             : #endif
     239             :   }
     240           0 :   return lhs.k_ == rhs.k_;
     241             : }
     242             : 
     243             : }  // namespace SSL
     244             : }  // namespace Security
     245             : }  // namespace OpenDDS
     246             : 
     247             : OPENDDS_END_VERSIONED_NAMESPACE_DECL

Generated by: LCOV version 1.16