-
Notifications
You must be signed in to change notification settings - Fork 552
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
base: master
Are you sure you want to change the base?
Add TPM2 RNG #4117
Changes from all commits
6109b2b
5250131
e613f07
c205858
bda3913
78f1bee
1cd900b
c6a3a02
d1244e3
9890bfa
ed26467
2ab6008
70be246
6e2af38
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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> { | ||
if(tcti_nameconf == nullptr) { | ||
return {}; | ||
} else { | ||
return std::string(tcti_nameconf); | ||
} | ||
}(); | ||
|
||
ctx->ctx = Botan::TPM2_Context::create(std::move(tcti)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If an exception is throwing here due to for instance no hardware being available at runtime, the caller will get There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe even just reusing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. I don't think reusing The exception that would be thrown here is |
||
*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 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<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:internal> | ||
tpm2_util.h | ||
</header:internal> | ||
|
||
<header:public> | ||
tpm2.h | ||
tpm2_rng.h | ||
</header:public> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/* | ||
* 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 <botan/internal/tpm2_util.h> | ||
|
||
#include <tss2/tss2_esys.h> | ||
#include <tss2/tss2_tcti.h> | ||
#include <tss2/tss2_tctildr.h> | ||
|
||
namespace Botan { | ||
|
||
TPM2_Error::TPM2_Error(std::string_view location, uint32_t 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); | ||
} | ||
|
||
struct TPM2_Context::Impl { | ||
TSS2_TCTI_CONTEXT* m_tcti_ctx; | ||
ESYS_CONTEXT* m_ctx; | ||
}; | ||
|
||
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; | ||
} | ||
}(); | ||
// We cannot std::make_shared as the constructor is private | ||
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) : m_impl(std::make_unique<Impl>()) { | ||
check_tss2_rc("TCTI Initialization", Tss2_TctiLdr_Initialize(tcti_nameconf, &m_impl->m_tcti_ctx)); | ||
check_tss2_rc("TPM2 Initialization", Esys_Initialize(&m_impl->m_ctx, m_impl->m_tcti_ctx, nullptr /* ABI version */)); | ||
} | ||
|
||
void* TPM2_Context::inner_context_object() { | ||
return m_impl->m_ctx; | ||
} | ||
|
||
TPM2_Context::~TPM2_Context() { | ||
Esys_Finalize(&m_impl->m_ctx); | ||
Tss2_TctiLdr_Finalize(&m_impl->m_tcti_ctx); | ||
} | ||
|
||
} // namespace Botan |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/* | ||
* 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
|
||
|
||
namespace Botan { | ||
class BOTAN_PUBLIC_API(3, 6) TPM2_Error final : public Exception { | ||
public: | ||
TPM2_Error(std::string_view location, uint32_t rc); | ||
|
||
ErrorType error_type() const noexcept override { return ErrorType::TPMError; } | ||
|
||
uint32_t code() const { return m_rc; } | ||
|
||
int error_code() const noexcept override { | ||
// RC is uint32 but the maximum value is within int32 range as per tss2_common.h | ||
return static_cast<int>(m_rc); | ||
} | ||
|
||
std::string error_message() const; | ||
|
||
private: | ||
uint32_t m_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 = default; | ||
~TPM2_Context(); | ||
|
||
TPM2_Context& operator=(const TPM2_Context&) = delete; | ||
TPM2_Context& operator=(TPM2_Context&& ctx) noexcept = default; | ||
|
||
/// @return an ESYS_CONTEXT* for use in other TPM2 functions. | ||
void* inner_context_object(); | ||
|
||
private: | ||
TPM2_Context(const char* tcti_nameconf); | ||
|
||
private: | ||
struct Impl; // PImpl to avoid TPM2-TSS includes in this header | ||
std::unique_ptr<Impl> m_impl; | ||
}; | ||
|
||
} // namespace Botan | ||
|
||
#endif |
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 <botan/internal/tpm2_util.h> | ||
|
||
#include <source_location> | ||
#include <tss2/tss2_esys.h> | ||
|
||
namespace Botan { | ||
|
||
void TPM2_RNG::fill_bytes_with_input(std::span<uint8_t> output, std::span<const uint8_t> input) { | ||
constexpr size_t MAX_STIR_RANDOM_SIZE = 128; // From specification of tpm2-tool's tpm2_stirrandom | ||
|
||
BufferSlicer in(input); | ||
|
||
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(inner(m_ctx), ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, &data)); | ||
} | ||
|
||
BufferStuffer out(output); | ||
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(inner(m_ctx), ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, requested_bytes, &digest)); | ||
|
||
// Ensure Esys_Free(digest) is called even if assertions fail and we leave this block | ||
auto clean_buffer = scoped_cleanup([&digest] { Esys_Free(digest); }); | ||
|
||
BOTAN_ASSERT_NOMSG(digest->size == requested_bytes); | ||
out.append({digest->buffer, digest->size}); | ||
} | ||
} | ||
|
||
} // namespace Botan |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... we've been specific basically everywhere. I think we should always state TPM2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #4117 (comment) - this FFI error translates the
ErrorType::TPMError
, which is also the error type forTPM2_Error
. Or should we just introduce a new TPM2ErrorType
and keep them separated entirely?