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 59d9f6f
Show file tree
Hide file tree
Showing 13 changed files with 1,102 additions and 172 deletions.
10 changes: 10 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 All @@ -30,6 +39,7 @@ option(LIBEVSE_CRYPTO_SUPPLIER_OPENSSL "Default OpenSSL cryptography supplier" O

# dependencies
find_package(OpenSSL REQUIRED)
find_package(date)

add_subdirectory(lib)

Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,27 @@ cmake -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=./dist
make -j$(nproc) install
make test
```

## TPM

Check notice on line 39 in README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

README.md#L39

Expected: 1; Actual: 0; Below
There is a configuration option to configure OpenSSL for use with a TPM.<br>

Check notice on line 40 in README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

README.md#L40

Element: br
`cmake` ... `-DUSING_TPM2=ON`<br>

Check notice on line 41 in README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

README.md#L41

Element: br
Note OpenSSL providers are not available for OpenSSL v1, OpenSSL v3 is required.

The library will use the `UseTPM` flag and the PEM private key file to
configure whether to use the `default` provider or the `tpm2` provider.

Configuration is managed via propquery strings (see CMakeLists.txt)
- `PROPQUERY_DEFAULT` is the string to use when selecting the default provider

Check notice on line 48 in README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

README.md#L48

Lists should be surrounded by blank lines
- `PROPQUERY_TPM2` is the string to use when selecting the tpm2 provider

propquery|action
---------|------
"provider=default"|use the default provider
"provider=tpm2"|use the tpm2 provider
"provider!=tpm2"|don't use the tpm provider
"?provider=tpm2,tpm2.digest!=yes"|prefer the tpm2 provider but not for message digests

For more information see:
- [Provider for integration of TPM 2.0 to OpenSSL 3.x](https://github.com/tpm2-software/tpm2-openssl)
- [OpenSSL property](https://www.openssl.org/docs/man3.0/man7/property.html)
- [OpenSSL provider](https://www.openssl.org/docs/man3.0/man7/provider.html)
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 59d9f6f

Please sign in to comment.