Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TPM2 RNG #4117

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ def add_enable_disable_pair(group, what, default, msg=optparse.SUPPRESS_HELP):
'disable building of deprecated features and modules')

# Should be derived from info.txt but this runs too early
third_party = ['boost', 'bzip2', 'lzma', 'commoncrypto', 'sqlite3', 'zlib', 'tpm']
third_party = ['boost', 'bzip2', 'lzma', 'commoncrypto', 'sqlite3', 'zlib', 'tpm', 'tpm2']

for mod in third_party:
mods_group.add_option('--with-%s' % (mod),
Expand Down
27 changes: 27 additions & 0 deletions src/lib/ffi/ffi.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,33 @@ BOTAN_FFI_EXPORT(2, 3) int botan_base64_decode(const char* base64_str, size_t in
*/
typedef struct botan_rng_struct* botan_rng_t;

/**
atreiber94 marked this conversation as resolved.
Show resolved Hide resolved
* TPM2 context
*/
typedef struct botan_tpm2_ctx_struct* botan_tpm2_ctx_t;

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

/**
* 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
*/
BOTAN_FFI_EXPORT(3, 6) int botan_tpm2_rng_init(botan_rng_t* rng_out, botan_tpm2_ctx_t ctx);

/**
* Initialize a random number generator object
* @param rng rng object
Expand Down
89 changes: 89 additions & 0 deletions src/lib/ffi/ffi_tpm2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* (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_rng.h>
#include <botan/internal/ffi_util.h>

#if defined(BOTAN_HAS_TPM2)
#include <botan/tpm2.h>
#include <botan/tpm2_rng.h>
#endif

extern "C" {

using namespace Botan_FFI;

#if defined(BOTAN_HAS_TPM2)
/**
* This wrapper is required since BOTAN_FFI_DECLARE_STRUCT internally produces a unique pointer,
* but the TPM2_Context is meant to be used as a shared pointer.
*/
struct botan_tpm2_ctx_wrapper {
std::shared_ptr<Botan::TPM2_Context> ctx;
};

BOTAN_FFI_DECLARE_STRUCT(botan_tpm2_ctx_struct, botan_tpm2_ctx_wrapper, 0xD2B95E15);
#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> {
randombit marked this conversation as resolved.
Show resolved Hide resolved
if(tcti_nameconf == nullptr) {
return {};
} else {
return std::string(tcti_nameconf);
}
}();

ctx->ctx = Botan::TPM2_Context::create(std::move(tcti));
randombit marked this conversation as resolved.
Show resolved Hide resolved
*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
}

/**
* 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) {
#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_RNG>(ctx_wrapper.ctx));
return BOTAN_FFI_SUCCESS;
});
#else
BOTAN_UNUSED(rng_out, ctx);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
}
23 changes: 23 additions & 0 deletions src/lib/prov/tpm2/info.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<defines>
TPM2 -> 20240610
</defines>

<module_info>
name -> "TPM2"
brief -> "Wrappers and Utilites to interact with TPM2"
</module_info>

load_on vendor

<libs>
all -> tss2-esys,tss2-rc,tss2-tctildr
</libs>

<requires>
rng
</requires>

<header:public>
tpm2.h
tpm2_rng.h
</header:public>
59 changes: 59 additions & 0 deletions src/lib/prov/tpm2/tpm2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* TPM 2 interface
* (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/tpm2.h>

#include <botan/internal/fmt.h>

#include <tss2/tss2_esys.h>
#include <tss2/tss2_tctildr.h>

namespace Botan {

TPM2_Error::TPM2_Error(std::string_view location, TSS2_RC rc) :
Exception(fmt("TPM2 Exception in {}: Code {} ({})", location, rc, Tss2_RC_Decode(rc))), m_rc(rc) {}

std::string TPM2_Error::error_message() const {
return Tss2_RC_Decode(m_rc);
}

std::shared_ptr<TPM2_Context> TPM2_Context::create(std::optional<std::string> tcti_nameconf) {
const auto tcti_nameconf_ptr = [&]() -> const char* {
if(tcti_nameconf.has_value()) {
return tcti_nameconf->c_str();
} else {
return nullptr;
}
}();
return std::shared_ptr<TPM2_Context>(new TPM2_Context(tcti_nameconf_ptr));
atreiber94 marked this conversation as resolved.
Show resolved Hide resolved
}

TPM2_Context::TPM2_Context(const char* tcti_nameconf) {
check_tss2_rc("TCTI Initialization", Tss2_TctiLdr_Initialize(tcti_nameconf, &m_tcti_ctx));
check_tss2_rc("TPM2 Initialization", Esys_Initialize(&m_ctx, m_tcti_ctx, nullptr /* ABI version */));
}

TPM2_Context::TPM2_Context(TPM2_Context&& ctx) noexcept : m_ctx(ctx.m_ctx) {
ctx.m_ctx = nullptr;
}

TPM2_Context& TPM2_Context::operator=(TPM2_Context&& ctx) noexcept {
if(this != &ctx) {
m_ctx = ctx.m_ctx;
ctx.m_ctx = nullptr;
}

return *this;
}

TPM2_Context::~TPM2_Context() {
Esys_Finalize(&m_ctx);
Tss2_TctiLdr_Finalize(&m_tcti_ctx);
}

} // namespace Botan
69 changes: 69 additions & 0 deletions src/lib/prov/tpm2/tpm2.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* TPM 2 interface
* (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)
*/

#ifndef BOTAN_TPM2_H_
#define BOTAN_TPM2_H_

#include <botan/exceptn.h>

#include <memory>
#include <optional>
atreiber94 marked this conversation as resolved.
Show resolved Hide resolved

#include <tss2/tss2_esys.h>
#include <tss2/tss2_rc.h>
#include <tss2/tss2_tctildr.h>
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing that most/all uses of TPM will just have a shared_ptr<TPM2_Context> that they invoke various functions from the library with. That being the case, if we could hide these few uses of the TPM2 types in this header, we likely could avoid pulling the TPM library includes into anything a user might include.


namespace Botan {
class BOTAN_PUBLIC_API(3, 6) TPM2_Error final : public Exception {
public:
TPM2_Error(std::string_view location, TSS2_RC rc);

ErrorType error_type() const noexcept override { return ErrorType::TPMError; }

TSS2_RC code() const { return m_rc; }
atreiber94 marked this conversation as resolved.
Show resolved Hide resolved

std::string error_message() const;

private:
TSS2_RC m_rc;
};

inline void check_tss2_rc(std::string_view location, TSS2_RC rc) {
atreiber94 marked this conversation as resolved.
Show resolved Hide resolved
if(rc != TSS2_RC_SUCCESS) {
throw TPM2_Error(location, rc);
}
}

class BOTAN_PUBLIC_API(3, 6) TPM2_Context final {
public:
/**
* @param tcti_nameconf if set this is passed to Tss2_TctiLdr_Initialize verbatim
* otherwise a nullptr is passed.
*/
static std::shared_ptr<TPM2_Context> create(std::optional<std::string> tcti_nameconf = {});

TPM2_Context(const TPM2_Context&) = delete;
TPM2_Context(TPM2_Context&& ctx) noexcept;
~TPM2_Context();

TPM2_Context& operator=(const TPM2_Context&) = delete;
TPM2_Context& operator=(TPM2_Context&& ctx) noexcept;
atreiber94 marked this conversation as resolved.
Show resolved Hide resolved

ESYS_CONTEXT* get() { return m_ctx; }

private:
TPM2_Context(const char* tcti_nameconf);

private:
TSS2_TCTI_CONTEXT* m_tcti_ctx;
ESYS_CONTEXT* m_ctx;
};

} // namespace Botan

#endif
47 changes: 47 additions & 0 deletions src/lib/prov/tpm2/tpm2_rng.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* TPM 2 RNG interface
* (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/tpm2_rng.h>

#include <botan/internal/stl_util.h>

#include <source_location>
#include <tss2/tss2_esys.h>

namespace Botan {

namespace {
constexpr size_t MAX_STIR_RANDOM_SIZE = 128; // From specification of tpm2-tool's tpm2_stirrandom
atreiber94 marked this conversation as resolved.
Show resolved Hide resolved
} // anonymous namespace

void TPM2_RNG::fill_bytes_with_input(std::span<uint8_t> output, std::span<const uint8_t> input) {
BufferSlicer in(input);
BufferStuffer out(output);
atreiber94 marked this conversation as resolved.
Show resolved Hide resolved

while(!in.empty()) {
TPM2B_SENSITIVE_DATA data;
data.size = std::min(in.remaining(), MAX_STIR_RANDOM_SIZE);
in.copy_into({data.buffer, data.size});

check_tss2_rc("StirRandom", Esys_StirRandom(m_ctx->get(), ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, &data));
}

while(!out.full()) {
TPM2B_DIGEST* digest = nullptr;
const auto requested_bytes = std::min(sizeof(digest->buffer), out.remaining_capacity());
check_tss2_rc("GetRandom",
Esys_GetRandom(m_ctx->get(), ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, requested_bytes, &digest));

BOTAN_ASSERT_NOMSG(digest->size == requested_bytes);
out.append({digest->buffer, digest->size});

Esys_Free(digest);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be a good idea to use scoped_cleanup (or some variant of it) for this. Its unlikely, but if either the assert or the append above throw, the digest won't be freed.

}
}

} // namespace Botan
36 changes: 36 additions & 0 deletions src/lib/prov/tpm2/tpm2_rng.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* TPM 2 RNG interface
* (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)
*/

#ifndef BOTAN_TPM2_RNG_H_
#define BOTAN_TPM2_RNG_H_

#include <botan/rng.h>

#include <botan/tpm2.h>

namespace Botan {
class BOTAN_PUBLIC_API(3, 6) TPM2_RNG final : public Hardware_RNG {
public:
TPM2_RNG(std::shared_ptr<TPM2_Context> ctx) : m_ctx(std::move(ctx)) {}

bool accepts_input() const override { return true; }

std::string name() const override { return "TPM2_RNG"; }

bool is_seeded() const override { return true; }

private:
void fill_bytes_with_input(std::span<uint8_t> output, std::span<const uint8_t> input) override;

private:
std::shared_ptr<TPM2_Context> m_ctx;
};

} // namespace Botan

#endif
Loading
Loading