Skip to content

Commit

Permalink
Basic support for TPM 2.0 in the FFI
Browse files Browse the repository at this point in the history
This merely exposes enough functionality to establish an RNG backed
by the TPM. Explicitly enabling the usage of Botan's crypto primitives
for the communication with the TPM is also included.
  • Loading branch information
reneme committed Oct 11, 2024
1 parent 579e2b6 commit 109d000
Show file tree
Hide file tree
Showing 5 changed files with 406 additions and 1 deletion.
59 changes: 59 additions & 0 deletions doc/api_ref/ffi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ The following enum values are defined in the FFI header:
calling :cpp:func:`botan_hash_destroy` on a ``botan_rng_t`` object will cause
this error.

.. cpp:enumerator:: BOTAN_FFI_TPM_ERROR = -78

An error occured when performing TPM2 interactions.

.. cpp:enumerator:: BOTAN_FFI_ERROR_UNKNOWN_ERROR = -100

Something bad happened, but we are not sure why or how.
Expand Down Expand Up @@ -1337,6 +1341,61 @@ Public Key Encapsulation

Destroy the operation, freeing memory


TPM 2.0 Functions
----------------------------------------

.. versionadded:: 3.6.0

.. cpp:type:: opaque* botan_tpm2_ctx_t

An opaque data type for a TPM 2.0 context object. Don't mess with it.

.. cpp:type:: opaque* botan_tpm2_session_t

An opaque data type for a TPM 2.0 session object. Don't mess with it.


.. cpp:function:: int botan_tpm2_supports_crypto_backend()

Returns 1 if the Botan-based TPM 2.0 crypto backend is available, 0 otherwise.

.. cpp:function:: int botan_tpm2_ctx_init(botan_tpm2_ctx_t* ctx_out, const char* tcti_nameconf)

Initialize a TPM 2.0 context object. The TCTI name and configuration are
mangled into a single string separated by a colon. for instance "device:/dev/tpm0".

.. cpp:function:: int botan_tpm2_ctx_init_ex(botan_tpm2_ctx_t* ctx_out, const char* tcti_name, const char* tcti_conf)

Initialize a TPM 2.0 context object. The TCTI name and configuration are
passed as separate strings.

.. cpp:function:: int botan_tpm2_ctx_enable_crypto_backend(botan_tpm2_ctx_t ctx, botan_rng_t rng)

Enable the Botan-based TPM 2.0 crypto backend. Note that the random number
generator passed to this function must not be dependent on the TPM itself.

.. cpp:function:: int botan_tpm2_unauthenticated_session_init(botan_tpm2_session_t* session_out, botan_tpm2_ctx_t ctx)

Initialize an unauthenticated session that can be used to encrypt the
communication between your application and the TPM.

.. cpp:function:: int botan_tpm2_rng_init(botan_rng_t* rng_out, \
botan_tpm2_ctx_t ctx, \
botan_tpm2_session_t s1, \
botan_tpm2_session_t s2, \
botan_tpm2_session_t s3)

Initialize a random number generator that uses the TPM as a source of entropy.

.. cpp:function:: int botan_tpm2_ctx_destroy(botan_tpm2_ctx_t ctx)

Destroy a TPM 2.0 context object.

.. cpp:function:: int botan_tpm2_session_destroy(botan_tpm2_session_t session)

Destroy a TPM 2.0 session object.

X.509 Certificates
----------------------------------------

Expand Down
4 changes: 3 additions & 1 deletion src/lib/ffi/ffi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,15 @@ int ffi_map_error_type(Botan::ErrorType err) {
case Botan::ErrorType::IoError:
case Botan::ErrorType::Pkcs11Error:
case Botan::ErrorType::CommonCryptoError:
case Botan::ErrorType::TPMError:
case Botan::ErrorType::ZlibError:
case Botan::ErrorType::Bzip2Error:
case Botan::ErrorType::LzmaError:
case Botan::ErrorType::DatabaseError:
return BOTAN_FFI_ERROR_SYSTEM_ERROR;

case Botan::ErrorType::TPMError:
return BOTAN_FFI_ERROR_TPM_ERROR;

case Botan::ErrorType::NotImplemented:
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
case Botan::ErrorType::OutOfMemory:
Expand Down
85 changes: 85 additions & 0 deletions src/lib/ffi/ffi.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ enum BOTAN_FFI_ERROR {
BOTAN_FFI_ERROR_TLS_ERROR = -75,
BOTAN_FFI_ERROR_HTTP_ERROR = -76,
BOTAN_FFI_ERROR_ROUGHTIME_ERROR = -77,
BOTAN_FFI_ERROR_TPM_ERROR = -78,

BOTAN_FFI_ERROR_UNKNOWN_ERROR = -100,
};
Expand Down Expand Up @@ -2201,6 +2202,90 @@ BOTAN_FFI_EXPORT(3, 0)
int botan_zfec_decode(
size_t K, size_t N, const size_t* indexes, uint8_t* const* inputs, size_t shareSize, uint8_t** outputs);

/**
* TPM2 context
*/
typedef struct botan_tpm2_ctx_struct* botan_tpm2_ctx_t;

/**
* TPM2 session
*/
typedef struct botan_tpm2_session_struct* botan_tpm2_session_t;

/**
* Checks if Botan's TSS2 crypto backend can be used in this build
* @returns 1 if the crypto backend can be enabled
*/
BOTAN_FFI_EXPORT(3, 6)
int botan_tpm2_supports_crypto_backend();

/**
* Initialize a TPM2 context
* @param ctx_out output TPM2 context
* @param tcti_nameconf TCTI config (may be nullptr)
* @return 0 on success
*/
BOTAN_FFI_EXPORT(3, 6) int botan_tpm2_ctx_init(botan_tpm2_ctx_t* ctx_out, const char* tcti_nameconf);

/**
* Initialize a TPM2 context
* @param ctx_out output TPM2 context
* @param tcti_name TCTI name (may be nullptr)
* @param tcti_conf TCTI config (may be nullptr)
* @return 0 on success
*/
BOTAN_FFI_EXPORT(3, 6)
int botan_tpm2_ctx_init_ex(botan_tpm2_ctx_t* ctx_out, const char* tcti_name, const char* tcti_conf);

/**
* Enable Botan's TSS2 crypto backend that replaces the cryptographic functions
* required for the communication with the TPM with implementations provided
* by Botan instead of using TSS' defaults OpenSSL or mbedTLS.
* Note that the provided @p rng should not be dependent on the TPM and the
* caller must ensure that it remains usable for the lifetime of the @p ctx.
* @param ctx TPM2 context
* @param rng random number generator to be used by the crypto backend
*/
BOTAN_FFI_EXPORT(3, 6)
int botan_tpm2_ctx_enable_crypto_backend(botan_tpm2_ctx_t ctx, botan_rng_t rng);

/**
* Frees all resouces of a TPM2 context
* @param ctx TPM2 context
* @return 0 on success
*/
BOTAN_FFI_EXPORT(3, 6) int botan_tpm2_ctx_destroy(botan_tpm2_ctx_t ctx);

/**
* Initialize a random number generator object via TPM2
* @param rng_out rng object to create
* @param ctx TPM2 context
* @param s1 the first session to use (optional, may be nullptr)
* @param s2 the second session to use (optional, may be nullptr)
* @param s3 the third session to use (optional, may be nullptr)
*/
BOTAN_FFI_EXPORT(3, 6)
int botan_tpm2_rng_init(botan_rng_t* rng_out,
botan_tpm2_ctx_t ctx,
botan_tpm2_session_t s1,
botan_tpm2_session_t s2,
botan_tpm2_session_t s3);

/**
* Create an unauthenticated session for use with TPM2
* @param session_out the session object to create
* @param ctx TPM2 context
*/
BOTAN_FFI_EXPORT(3, 6)
int botan_tpm2_unauthenticated_session_init(botan_tpm2_session_t* session_out, botan_tpm2_ctx_t ctx);

/**
* Create an unauthenticated session for use with TPM2
* @param session the session object to destroy
*/
BOTAN_FFI_EXPORT(3, 6)
int botan_tpm2_session_destroy(botan_tpm2_session_t session);

#ifdef __cplusplus
}
#endif
Expand Down
205 changes: 205 additions & 0 deletions src/lib/ffi/ffi_tpm2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/*
* (C) 2024 Jack Lloyd
* (C) 2024 René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity GmbH
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/ffi.h>

#include <botan/internal/ffi_pkey.h>
#include <botan/internal/ffi_rng.h>
#include <botan/internal/ffi_util.h>

#if defined(BOTAN_HAS_TPM2)
#include <botan/tpm2_context.h>
#include <botan/tpm2_key.h>
#include <botan/tpm2_rng.h>
#include <botan/tpm2_session.h>
#endif

extern "C" {

using namespace Botan_FFI;

#if defined(BOTAN_HAS_TPM2)

// These wrappers are required since BOTAN_FFI_DECLARE_STRUCT internally
// produces a unique pointer, but the TPM types are meant to be used as
// shared pointers.

struct botan_tpm2_ctx_wrapper {
std::shared_ptr<Botan::TPM2::Context> ctx;
};

struct botan_tpm2_session_wrapper {
std::shared_ptr<Botan::TPM2::Session> session;
};

BOTAN_FFI_DECLARE_STRUCT(botan_tpm2_ctx_struct, botan_tpm2_ctx_wrapper, 0xD2B95E15);
BOTAN_FFI_DECLARE_STRUCT(botan_tpm2_session_struct, botan_tpm2_session_wrapper, 0x9ACCAB52);

} // extern "C"

namespace {

Botan::TPM2::SessionBundle sessions(botan_tpm2_session_t s1, botan_tpm2_session_t s2, botan_tpm2_session_t s3) {
return Botan::TPM2::SessionBundle((s1 != nullptr) ? safe_get(s1).session : nullptr,
(s2 != nullptr) ? safe_get(s2).session : nullptr,
(s3 != nullptr) ? safe_get(s3).session : nullptr);
}

} // namespace

extern "C" {

#endif

int botan_tpm2_supports_crypto_backend() {
#if defined(BOTAN_HAS_TPM2)
return Botan::TPM2::Context::supports_botan_crypto_backend() ? 1 : 0;
#else
return 0;
#endif
}

int botan_tpm2_ctx_init(botan_tpm2_ctx_t* ctx_out, const char* tcti_nameconf) {
#if defined(BOTAN_HAS_TPM2)
return ffi_guard_thunk(__func__, [=]() -> int {
if(ctx_out == nullptr) {
return BOTAN_FFI_ERROR_NULL_POINTER;
}
auto ctx = std::make_unique<botan_tpm2_ctx_wrapper>();

auto tcti = [=]() -> std::optional<std::string> {
if(tcti_nameconf == nullptr) {
return {};
} else {
return std::string(tcti_nameconf);
}
}();

ctx->ctx = Botan::TPM2::Context::create(std::move(tcti));
*ctx_out = new botan_tpm2_ctx_struct(std::move(ctx));
return BOTAN_FFI_SUCCESS;
});
#else
BOTAN_UNUSED(ctx_out, tcti_nameconf);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}

int botan_tpm2_ctx_init_ex(botan_tpm2_ctx_t* ctx_out, const char* tcti_name, const char* tcti_conf) {
#if defined(BOTAN_HAS_TPM2)
return ffi_guard_thunk(__func__, [=]() -> int {
if(ctx_out == nullptr) {
return BOTAN_FFI_ERROR_NULL_POINTER;
}
auto ctx = std::make_unique<botan_tpm2_ctx_wrapper>();

auto tcti_name_str = [=]() -> std::optional<std::string> {
if(tcti_name == nullptr) {
return {};
} else {
return std::string(tcti_name);
}
}();

auto tcti_conf_str = [=]() -> std::optional<std::string> {
if(tcti_conf == nullptr) {
return {};
} else {
return std::string(tcti_conf);
}
}();

ctx->ctx = Botan::TPM2::Context::create(std::move(tcti_name_str), std::move(tcti_conf_str));
*ctx_out = new botan_tpm2_ctx_struct(std::move(ctx));
return BOTAN_FFI_SUCCESS;
});
#else
BOTAN_UNUSED(ctx_out, tcti_name, tcti_conf);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}

int botan_tpm2_ctx_enable_crypto_backend(botan_tpm2_ctx_t ctx, botan_rng_t rng) {
#if defined(BOTAN_HAS_TPM2)
return BOTAN_FFI_VISIT(ctx, [=](botan_tpm2_ctx_wrapper& ctx_wrapper) -> int {
Botan::RandomNumberGenerator& rng_ref = safe_get(rng);

// The lifetime of the RNG used for the crypto backend should be managed
// by the TPM2::Context. Here, we just need to trust the user that they
// keep the passed-in RNG instance intact for the lifetime of the context.
std::shared_ptr<Botan::RandomNumberGenerator> rng_ptr(&rng_ref, [](auto*) {});
ctx_wrapper.ctx->use_botan_crypto_backend(rng_ptr);
return BOTAN_FFI_SUCCESS;
});
#else
BOTAN_UNUSED(ctx, rng);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}

/**
* Frees all resouces of a TPM2 context
* @param ctx TPM2 context
* @return 0 on success
*/
int botan_tpm2_ctx_destroy(botan_tpm2_ctx_t ctx) {
#if defined(BOTAN_HAS_TPM2)
return BOTAN_FFI_CHECKED_DELETE(ctx);
#else
BOTAN_UNUSED(ctx);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}

int botan_tpm2_rng_init(botan_rng_t* rng_out,
botan_tpm2_ctx_t ctx,
botan_tpm2_session_t s1,
botan_tpm2_session_t s2,
botan_tpm2_session_t s3) {
#if defined(BOTAN_HAS_TPM2)
return BOTAN_FFI_VISIT(ctx, [=](botan_tpm2_ctx_wrapper& ctx_wrapper) -> int {
if(rng_out == nullptr) {
return BOTAN_FFI_ERROR_NULL_POINTER;
}

*rng_out = new botan_rng_struct(
std::make_unique<Botan::TPM2::RandomNumberGenerator>(ctx_wrapper.ctx, sessions(s1, s2, s3)));
return BOTAN_FFI_SUCCESS;
});
#else
BOTAN_UNUSED(rng_out, ctx, s1, s2, s3);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}

int botan_tpm2_unauthenticated_session_init(botan_tpm2_session_t* session_out, botan_tpm2_ctx_t ctx) {
#if defined(BOTAN_HAS_TPM2)
return BOTAN_FFI_VISIT(ctx, [=](botan_tpm2_ctx_wrapper& ctx_wrapper) -> int {
if(session_out == nullptr) {
return BOTAN_FFI_ERROR_NULL_POINTER;
}

auto session = std::make_unique<botan_tpm2_session_wrapper>();
session->session = Botan::TPM2::Session::unauthenticated_session(ctx_wrapper.ctx);
*session_out = new botan_tpm2_session_struct(std::move(session));
return BOTAN_FFI_SUCCESS;
});
#else
BOTAN_UNUSED(session_out, ctx);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}

int botan_tpm2_session_destroy(botan_tpm2_session_t session) {
#if defined(BOTAN_HAS_TPM2)
return BOTAN_FFI_CHECKED_DELETE(session);
#else
BOTAN_UNUSED(session);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
}
Loading

0 comments on commit 109d000

Please sign in to comment.