Skip to content

Commit

Permalink
feat: OpenSSL provider implementation now default for OpenSSL v3
Browse files Browse the repository at this point in the history
feat: OpenSSL tpm2 provider load on first use, configured via
      property strings
feat: OpenSSL property strings configurable via cmake
feat: unit tests run for OpenSSL v1 and v3
feat: additional unit tests for tpm2 provider (OpenSSL v3 only)

provider handling changed following testing on an embedded system.
Loading and unloading providers was proving unreliable. Approach changed
to load providers early and use the property string to control
which provider is used.

A mutex has been added so that another call cann't change the
provider configuration whilst in use.

OpenSSL v3 uses providers. OpenSSL v1 uses previous code.

Note: the tpm2-abrmd daemon needs to be running for tpm2 where
      TLS is being used.

Signed-off-by: James Chapman <[email protected]>
  • Loading branch information
james-ctc committed Feb 14, 2024
1 parent 6f45efd commit 4077a63
Show file tree
Hide file tree
Showing 12 changed files with 1,087 additions and 171 deletions.
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ find_package(everest-cmake 0.1 REQUIRED
option(${PROJECT_NAME}_BUILD_TESTING "Build unit tests, used if included as dependency" OFF)
option(BUILD_TESTING "Build unit tests, used if standalone project" OFF)
option(EVSE_SECURITY_INSTALL "Install the library (shared data might be installed anyway)" ${EVC_MAIN_PROJECT})
option(USING_TPM2 "Include code for using OpenSSL 3 and the tpm2 provider" OFF)

if(USING_TPM2)
# OpenSSL property string when using the default provider
set(PROPQUERY_DEFAULT "provider!=tpm2")
# OpenSSL property string when using the tpm2 provider
set(PROPQUERY_TPM2 "?provider=tpm2,tpm2.digest!=yes,tpm2.cipher!=yes")
endif()


# dependencies
if (NOT DISABLE_EDM)
Expand Down
161 changes: 161 additions & 0 deletions include/evse_security/crypto/openssl/openssl_tpm.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest

#ifndef OPENSSL_TPM_HPP
#define OPENSSL_TPM_HPP

#include <cstdint>
#include <fstream>
#include <mutex>
#include <string>

// opaque types (from OpenSSL)
struct ossl_lib_ctx_st; // OpenSSL OSSL_LIB_CTX;
struct ossl_provider_st; // OpenSSL OSSL_PROVIDER

namespace evse_security {

/// @brief determine if the PEM string is a TSS2 private key
/// @param private_key_pem string containing the PEM encoded key
/// @return true when "-----BEGIN TSS2 PRIVATE KEY-----" found
/// @note works irrespective of OpenSSL version
bool is_tpm_key_string(const std::string& private_key_pem);

/// @brief determine if the PEM file contains a TSS2 private key
/// @param private_key_file_pem filename of the PEM file
/// @return true when file starts "-----BEGIN TSS2 PRIVATE KEY-----"
/// @note works irrespective of OpenSSL version
bool is_tpm_key_filename(const std::string& private_key_file_pem);

/// @brief Manage the loading and configuring of OpenSSL providers
///
/// There are two providers considered:
/// - 'default'
/// - 'tpm2' for working with TSS2 keys (protected by a TPM)
///
/// There are two contexts:
/// - 'global' for general use
/// - 'tls' for TLS connections
///
/// The class also acts as a scoped mutex to prevent changes in the
/// provider configuration during crypto operations
///
/// @note OpenSSL SSL_CTX caches the propquery so updates via
/// this class may not be effective. See SSL_CTX_new_ex()
///
/// This code provides a null implementation when OpenSSL 3 or later isn't
/// used. The null implementation is also used when -DUSING_TPM2=OFF is
/// set with cmake.
///
/// @note the tpm2-abrmd daemon is needed to support openssl-tpm2 for TLS
class OpenSSLProvider {
public:
/// @brief supported propquery strings
enum class mode_t {
default_provider,
tpm2_provider,
};

private:
typedef std::uint8_t flags_underlying_t;
enum class flags_t : flags_underlying_t {
initialised,
tpm2_available,
global_tpm2,
tls_tpm2,
};

static std::mutex s_mux;
static flags_underlying_t s_flags;

static struct ossl_provider_st* s_global_prov_default_p;
static struct ossl_provider_st* s_global_prov_tpm_p;
static struct ossl_provider_st* s_tls_prov_default_p;
static struct ossl_provider_st* s_tls_prov_tpm_p;
static struct ossl_lib_ctx_st* s_tls_libctx_p;

static inline void reset(flags_t f) {
s_flags &= ~(1 << static_cast<flags_underlying_t>(f));
}

static inline void set(flags_t f) {
s_flags |= 1 << static_cast<flags_underlying_t>(f);
}

static inline bool is_set(flags_t f) {
return (s_flags & (1 << static_cast<flags_underlying_t>(f))) != 0;
}

static inline bool is_reset(flags_t f) {
return !is_set(f);
}

/// @brief uodate the flag
/// @param f - flag to update
/// @param val - whether to set or reset the flag
/// @return true when the flag was changed
static inline bool update(flags_t f, bool val) {
bool bResult = val != is_set(f);
if (val) {
set(f);
} else {
reset(f);
}
return bResult;
}

bool load(struct ossl_provider_st*& default_p, struct ossl_provider_st*& tpm2_p, struct ossl_lib_ctx_st* libctx_p,
mode_t mode);
inline bool load_global(mode_t mode) {
return load(s_global_prov_default_p, s_global_prov_tpm_p, nullptr, mode);
}
inline bool load_tls(mode_t mode) {
return load(s_tls_prov_default_p, s_tls_prov_tpm_p, s_tls_libctx_p, mode);
}

bool set_propstr(struct ossl_lib_ctx_st* libctx, mode_t mode);
bool set_mode(struct ossl_lib_ctx_st* libctx, mode_t mode);

public:
OpenSSLProvider();
~OpenSSLProvider();

inline void set_global_mode(mode_t mode) {
set_mode(nullptr, mode);
}

inline void set_tls_mode(mode_t mode) {
set_mode(s_tls_libctx_p, mode);
}

const char* propquery(mode_t mode) const;

inline mode_t propquery_global() const {
return (is_set(flags_t::global_tpm2)) ? mode_t::tpm2_provider : mode_t::default_provider;
}
inline mode_t propquery_tls() const {
return (is_set(flags_t::tls_tpm2)) ? mode_t::tpm2_provider : mode_t::default_provider;
}

inline const char* propquery_global_str() const {
return propquery(propquery_global());
}
inline const char* propquery_tls_str() const {
return propquery(propquery_tls());
}

/// @brief return the TLS OSSL library context
inline operator struct ossl_lib_ctx_st *() {
return s_tls_libctx_p;
}

static inline bool supports_tpm() {
return is_set(flags_t::tpm2_available);
}

static void cleanup();
};

} // namespace evse_security

#endif // OPENSSL_TPM_HPP
9 changes: 9 additions & 0 deletions lib/evse_security/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ target_sources(evse_security

crypto/interface/crypto_supplier.cpp
crypto/openssl/openssl_supplier.cpp
crypto/openssl/openssl_tpm.cpp
)

target_include_directories(evse_security
Expand Down Expand Up @@ -71,4 +72,12 @@ if(LIBEVSE_CRYPTO_SUPPLIER_OPENSSL)
add_compile_definitions(LIBEVSE_CRYPTO_SUPPLIER_OPENSSL)
endif()

if(USING_TPM2)
target_compile_definitions(evse_security PRIVATE
USING_TPM2
PROPQUERY_DEFAULT="${PROPQUERY_DEFAULT}"
PROPQUERY_TPM2="${PROPQUERY_TPM2}"
)
endif()

target_compile_features(evse_security PUBLIC cxx_std_17)
Loading

0 comments on commit 4077a63

Please sign in to comment.