Search code examples
boostopensslfips

Enable OpenSSL FIPS mode for Boost C++


Question

Looking thru these various OpenSSL 3.0 docs

I've managed to piece together a solution to enable FIPS mode in OpenSSL with the following goal in mind:

The emphasis is on the fact that once FIPS mode is enabled in OpenSSL, all cryptographic operations performed through OpenSSL, including those used by C++ Boost.Asio for SSL/TLS connections, will utilize FIPS-approved algorithms, ensuring compliance with FIPS standards throughout the Boost C++ application.

Is this a valid function enableFIPS() to enable OpenSSL FIPS mode for Boost C++?

Example

main.cpp

#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <openssl/provider.h>

bool PrintErrorSSL(const std::string& err)
{
   std::cerr << err << std::endl;
   unsigned long err_code;
   while ((err_code = ERR_get_error()) != 0)
   {
      char err_msg[256];
      ERR_error_string_n(err_code, err_msg, sizeof(err_msg));
      std::cerr << "OpenSSL error: " << err_msg << std::endl;
   }
   // FIPS mode not enabled.
   return false;
}

bool enableFIPS()
{
   std::string ConfigPath;
   std::string TestPath = "C:/Projects/sys-openssl-fips/Test/";
   
#ifdef _WIN32
    ConfigPath = TestPath + "openssl-fips.cnf";
#else
    ConfigPath = TestPath + "openssl-fips-linux.cnf";
#endif

   if (!OSSL_PROVIDER_set_default_search_path(nullptr, TestPath.c_str()))
   {
      return PrintErrorSSL("OSSL_PROVIDER_set_default_search_path() Failed");
   }

   if (!OSSL_LIB_CTX_load_config(nullptr, ConfigPath.c_str()))
   {
      return PrintErrorSSL("OSSL_LIB_CTX_load_config() Failed");
   }

   if (!OSSL_PROVIDER_available(nullptr, "fips") || !OSSL_PROVIDER_available(nullptr, "base"))
   {
      return PrintErrorSSL("OSSL_PROVIDER_available() Failed");
   }

   if (!EVP_default_properties_is_fips_enabled(nullptr))
   {
      return PrintErrorSSL("EVP_default_properties_is_fips_enabled() Failed");
   }

   // FIPS mode enabled.
   return true;
}

int main(int argc, char* argv[])
{
   if (enableFIPS())
   {
      using boost::asio::ip::tcp;
      boost::asio::io_context io_context;
      boost::asio::ssl::context ssl_context(boost::asio::ssl::context::tlsv12);
      boost::asio::ssl::stream<tcp::socket> socket(io_context, ssl_context);
      tcp::resolver resolver(io_context);
      auto endpoints = resolver.resolve("www.google.com", "443");
      boost::asio::connect(socket.lowest_layer(), endpoints);
      socket.handshake(boost::asio::ssl::stream_base::client);
      std::string request = "GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n";
      boost::asio::write(socket, boost::asio::buffer(request));
      boost::asio::streambuf response;
      boost::system::error_code ec;
      boost::asio::read_until(socket, response, "\r\n", ec);
      std::istream response_stream(&response);
      std::string http_status;
      std::getline(response_stream, http_status);
      if (http_status.find("200 OK") != std::string::npos)
         return 0;
   }

   return 1;
}

openssl-fips.cnf

config_diagnostics = 1
openssl_conf = openssl_init

[fips_sect]
activate = 1
conditional-errors = 1
security-checks = 1
module-mac = D9:AB:CC:37:8A:4C:06:BF:E9:E3:A8:F7:B9:B5:02:48:58:71:76:EB:5E:71:8A:0F:87:AA:52:46:7D:60:B0:EB

[openssl_init]
providers = provider_sect
alg_section = algorithm_sect

[provider_sect]
fips = fips_sect
base = base_sect

[base_sect]
activate = 1

[algorithm_sect]
default_properties = fips=yes

Output (Windows)

OpenSSL FIPS mode: OFF

Projects will utilize non FIPS-approved algorithms provided by these openssl static libraries

  • libssl - Implements the Transport Layer Security (TLS) protocol.
  • libcrypto - Implements cryptographic algorithms.

OpenSSL FIPS mode: ON

Projects will utilize FIPS-approved algorithms provided by these openssl shared libraries

  • fips.so (on Linux)
  • fips.dll (on Windows)

Using Process Explorer, the fips.dll is loaded:

sys-fips


Solution

  • It looks close.

    I'd say you have to be careful with using the particular constructor for asio::ssl::context. It looks like enableFIPS mostly sets defaults, so perhaps to be safe you could also manually instantiate an SSL_CTX* and construct asio::ssl::context from it using https://live.boost.org/doc/libs/1_84_0/doc/html/boost_asio/reference/ssl__context/context/overload2.html

    Beyond that, I'd always vouch for compliance tests. Of course you could start with some in-house unit/integration tests that check that improper configurations are actively rejected.