9#include <botan/tpm2_context.h>
11#include <botan/tpm2_key.h>
12#include <botan/tpm2_session.h>
14#include <botan/internal/fmt.h>
15#include <botan/internal/int_utils.h>
16#include <botan/internal/loadstor.h>
17#include <botan/internal/stl_util.h>
18#include <botan/internal/tpm2_algo_mappings.h>
19#include <botan/internal/tpm2_util.h>
21#include <tss2/tss2_esys.h>
22#include <tss2/tss2_tcti.h>
23#include <tss2/tss2_tctildr.h>
25#if defined(BOTAN_HAS_TPM2_CRYPTO_BACKEND)
26 #include <botan/tpm2_crypto_backend.h>
27 #include <botan/internal/tpm2_crypto_backend_impl.h>
34constexpr TPM2_HANDLE storage_root_key_handle = TPM2_HR_PERSISTENT + 1;
42#if defined(BOTAN_HAS_TPM2_CRYPTO_BACKEND)
43 std::unique_ptr<CryptoCallbackState> m_crypto_callback_state;
48#if defined(BOTAN_HAS_TPM2_CRYPTO_BACKEND)
56 const auto nameconf_ptr = tcti_nameconf.c_str();
58 TSS2_TCTI_CONTEXT* tcti_ctx =
nullptr;
59 ESYS_CONTEXT* esys_ctx =
nullptr;
60 check_rc(
"TCTI Initialization", Tss2_TctiLdr_Initialize(nameconf_ptr, &tcti_ctx));
62 check_rc(
"TPM2 Initialization", Esys_Initialize(&esys_ctx, tcti_ctx,
nullptr ));
66 return std::shared_ptr<Context>(
new Context(esys_ctx,
false ));
69std::shared_ptr<Context>
Context::create(std::optional<std::string> tcti, std::optional<std::string> conf) {
70 const auto tcti_ptr = tcti.has_value() ? tcti->c_str() :
nullptr;
71 const auto conf_ptr = conf.has_value() ? conf->c_str() :
nullptr;
73 TSS2_TCTI_CONTEXT* tcti_ctx =
nullptr;
74 ESYS_CONTEXT* esys_ctx =
nullptr;
75 check_rc(
"TCTI Initialization", Tss2_TctiLdr_Initialize_Ex(tcti_ptr, conf_ptr, &tcti_ctx));
77 check_rc(
"TPM2 Initialization", Esys_Initialize(&esys_ctx, tcti_ctx,
nullptr ));
81 return std::shared_ptr<Context>(
new Context(esys_ctx,
false ));
69std::shared_ptr<Context>
Context::create(std::optional<std::string> tcti, std::optional<std::string> conf) {
…}
85 BOTAN_ARG_CHECK(esys_ctx !=
nullptr,
"provided esys_ctx must not be null");
88 return std::shared_ptr<Context>(
new Context(esys_ctx,
true ));
91Context::Context(ESYS_CONTEXT* ctx,
bool external) : m_impl(std::make_unique<Impl>()) {
93 m_impl->m_external = external;
98Context& Context::operator=(Context&&) noexcept = default;
101#if defined(BOTAN_HAS_TPM2_CRYPTO_BACKEND)
106 throw Not_Implemented(
"This build of botan does not provide the TPM2 crypto backend");
111#if defined(BOTAN_HAS_TPM2_CRYPTO_BACKEND)
112 return m_impl->m_crypto_callback_state !=
nullptr;
118#if defined(BOTAN_HAS_TPM2_CRYPTO_BACKEND)
121 return *m_impl->m_crypto_callback_state;
126 return m_impl->m_ctx;
131uint32_t get_tpm_property(ESYS_CONTEXT* ctx, TPM2_PT property) {
133 constexpr uint32_t property_count = 1;
134 constexpr TPM2_CAP capability = TPM2_CAP_TPM_PROPERTIES;
138 Esys_GetCapability(ctx,
150 BOTAN_ASSERT_NOMSG(capability_data->data.tpmProperties.tpmProperty[0].property == property);
152 return capability_data->data.tpmProperties.tpmProperty[0].value;
155template <TPM2_CAP capability,
typename ReturnT>
156[[nodiscard]] std::vector<ReturnT> get_tpm_property_list(ESYS_CONTEXT* ctx, TPM2_PT property, uint32_t count) {
157 auto extract = [](
const TPMU_CAPABILITIES& caps, uint32_t max_count) {
158 std::vector<ReturnT> result;
159 if constexpr(capability == TPM2_CAP_HANDLES) {
160 const auto to_read = std::min(caps.handles.count, max_count);
161 result.reserve(to_read);
162 for(
size_t i = 0; i < to_read; ++i) {
163 result.push_back(caps.handles.handle[i]);
165 }
else if constexpr(capability == TPM2_CAP_ALGS) {
166 const auto to_read = std::min(caps.algorithms.count, max_count);
167 result.reserve(to_read);
168 for(
size_t i = 0; i < to_read; ++i) {
172 result.push_back(caps.algorithms.algProperties[i].alg);
176 static_assert(capability != TPM2_CAP_HANDLES,
"Unsupported capability");
181 TPMI_YES_NO more_data = TPM2_YES;
182 std::vector<ReturnT> properties;
183 while(more_data == TPM2_YES && count > 0) {
186 Esys_GetCapability(ctx,
198 const auto new_properties = extract(capability_data->data, count);
200 properties.insert(properties.end(), new_properties.begin(), new_properties.end());
210 constexpr std::array properties = {
211 TPM2_PT_VENDOR_STRING_1, TPM2_PT_VENDOR_STRING_2, TPM2_PT_VENDOR_STRING_3, TPM2_PT_VENDOR_STRING_4};
212 std::array<uint8_t, properties.size() * 4 + 1 > vendor_string{};
218 for(
auto prop : properties) {
227 std::array<uint8_t, 4 + 1 > manufacturer_data{};
228 store_be(std::span{manufacturer_data}.first<4>(), get_tpm_property(m_impl->m_ctx, TPM2_PT_MANUFACTURER));
236 const auto required_alg_ids = [&]() -> std::vector<TPM2_ALG_ID> {
237 std::vector<TPM2_ALG_ID> result;
239 result.push_back(algo_id.value());
243 result.push_back(hash_id.value());
247 result.push_back(block_id->first);
251 result.push_back(cipher_mode_id.value());
255 result.push_back(cipher_spec->algorithm);
256 result.push_back(cipher_spec->mode.sym);
260 result.push_back(sig_padding.value());
264 result.push_back(sig->scheme);
265 result.push_back(sig->details.any.hashAlg);
269 result.push_back(enc_scheme->scheme);
270 if(enc_scheme->scheme == TPM2_ALG_OAEP) {
271 result.push_back(enc_scheme->details.oaep.hashAlg);
276 result.push_back(enc_id.value());
282 if(required_alg_ids.empty()) {
287 const auto algo_caps =
288 get_tpm_property_list<TPM2_CAP_ALGS, TPM2_ALG_ID>(m_impl->m_ctx, TPM2_ALG_FIRST, TPM2_MAX_CAP_ALGS);
291 required_alg_ids.begin(), required_alg_ids.end(), [&](TPM2_ALG_ID
id) { return value_exists(algo_caps, id); });
295 return get_tpm_property(m_impl->m_ctx, TPM2_PT_MAX_DIGEST);
304 return get_tpm_property_list<TPM2_CAP_HANDLES, ESYS_TR>(m_impl->m_ctx, TPM2_TRANSIENT_FIRST, TPM2_MAX_CAP_HANDLES);
317 if(occupied_handles.size() >= TPM2_MAX_CAP_HANDLES) {
322 for(
TPM2_HANDLE i = TPM2_PERSISTENT_FIRST; i < TPM2_PERSISTENT_LAST; ++i) {
332 return get_tpm_property_list<TPM2_CAP_HANDLES, TPM2_HANDLE>(
333 m_impl->m_ctx, TPM2_PERSISTENT_FIRST, TPM2_MAX_CAP_HANDLES);
338 std::span<const uint8_t> auth_value,
339 std::optional<TPM2_HANDLE> persistent_handle) {
343 "Persistent handle already in use");
344 BOTAN_ARG_CHECK(!handles.has_persistent_handle(),
"Key already has a persistent handle assigned");
348 const TPMI_DH_PERSISTENT new_persistent_handle = [&] {
349 if(persistent_handle.has_value()) {
350 return persistent_handle.value();
354 return free_persistent_handle.value();
362 Esys_EvictControl(m_impl->m_ctx,
364 handles.transient_handle(),
368 new_persistent_handle,
375 if(!auth_value.empty()) {
377 check_rc(
"Esys_TR_SetAuth", Esys_TR_SetAuth(m_impl->m_ctx, handles.transient_handle(), &user_auth));
387 BOTAN_ASSERT_EQUAL(new_persistent_handle, handles.persistent_handle(),
"key was persisted at the correct location");
389 return new_persistent_handle;
395 auto& handles = key->handles();
396 BOTAN_ARG_CHECK(handles.has_persistent_handle(),
"Key does not have a persistent handle assigned");
401 ESYS_TR no_new_handle = ESYS_TR_NONE;
403 Esys_EvictControl(m_impl->m_ctx,
405 handles.transient_handle(),
411 BOTAN_ASSERT(no_new_handle == ESYS_TR_NONE,
"When deleting a key, no new handle is returned");
415 handles._disengage();
423#if defined(BOTAN_HAS_TPM2_CRYPTO_BACKEND)
441 m_impl->m_crypto_callback_state.reset();
447 if(!m_impl->m_external) {
451 TSS2_TCTI_CONTEXT* tcti_ctx =
nullptr;
452 Esys_GetTcti(m_impl->m_ctx, &tcti_ctx);
453 if(tcti_ctx !=
nullptr) {
454 Tss2_TctiLdr_Finalize(&tcti_ctx);
457 Esys_Finalize(&m_impl->m_ctx);
#define BOTAN_ASSERT_NOMSG(expr)
#define BOTAN_STATE_CHECK(expr)
#define BOTAN_ASSERT_EQUAL(expr1, expr2, assertion_made)
#define BOTAN_ASSERT_NONNULL(ptr)
#define BOTAN_ARG_CHECK(expr, msg)
#define BOTAN_ASSERT(expr, assertion_made)
#define BOTAN_ASSERT_UNREACHABLE()
Helper class to ease in-place marshalling of concatenated fixed-length values.
constexpr void append(std::span< const uint8_t > buffer)
constexpr size_t remaining_capacity() const
std::optional< TPM2_HANDLE > find_free_persistent_handle() const
bool supports_algorithm(std::string_view algo_name) const
static bool supports_botan_crypto_backend() noexcept
std::vector< TPM2_HANDLE > persistent_handles() const
size_t max_random_bytes_per_request() const
std::vector< ESYS_TR > transient_handles() const
TPM2_HANDLE persist(TPM2::PrivateKey &key, const SessionBundle &sessions, std::span< const uint8_t > auth_value={}, std::optional< TPM2_HANDLE > persistent_handle=std::nullopt)
Makes key persistent at location persistent_handle or any free.
Context(const Context &)=delete
static std::shared_ptr< Context > create(const std::string &tcti_nameconf)
std::string manufacturer() const
std::string vendor() const
ESYS_CONTEXT * esys_context() noexcept
std::unique_ptr< TPM2::PrivateKey > storage_root_key(std::span< const uint8_t > auth_value, const SessionBundle &sessions)
void evict(std::unique_ptr< TPM2::PrivateKey > key, const SessionBundle &sessions)
Evicts a persistent key from the TPM. The key cannot be used after.
bool uses_botan_crypto_backend() const noexcept
static std::unique_ptr< PrivateKey > load_persistent(const std::shared_ptr< Context > &ctx, TPM2_HANDLE persistent_object_handle, std::span< const uint8_t > auth_value, const SessionBundle &sessions)
std::optional< TPMT_SIG_SCHEME > rsa_signature_scheme_botan_to_tss2(std::string_view name)
std::optional< TPMI_ALG_SIG_SCHEME > rsa_signature_padding_botan_to_tss2(std::string_view padding_name) noexcept
bool supports_botan_crypto_backend() noexcept
std::optional< TPMI_ALG_ASYM_SCHEME > rsa_encryption_padding_botan_to_tss2(std::string_view name) noexcept
constexpr void check_rc(std::string_view location, TSS2_RC rc)
std::optional< std::pair< TPMI_ALG_SYM, TPM2_KEY_BITS > > block_cipher_botan_to_tss2(std::string_view cipher_name) noexcept
constexpr auto out_persistent_handle(Object &object)
std::optional< TPMT_SYM_DEF > cipher_botan_to_tss2(std::string_view algo_name)
std::optional< TPMT_RSA_DECRYPT > rsa_encryption_scheme_botan_to_tss2(std::string_view padding)
std::unique_ptr< CryptoCallbackState > use_botan_crypto_backend(ESYS_CONTEXT *context, const std::shared_ptr< Botan::RandomNumberGenerator > &rng)
std::unique_ptr< T, esys_liberator > unique_esys_ptr
A unique pointer type for ESYS handles that automatically frees the handle.
std::optional< TPMI_ALG_HASH > hash_algo_botan_to_tss2(std::string_view hash_name) noexcept
constexpr auto out_transient_handle(Object &object)
std::optional< TPM2_ALG_ID > asymmetric_algorithm_botan_to_tss2(std::string_view algo_name) noexcept
constexpr void copy_into(T &dest, std::span< const uint8_t > data)
std::optional< TPMI_ALG_SYM_MODE > cipher_mode_botan_to_tss2(std::string_view mode_name) noexcept
void set_crypto_callbacks(ESYS_CONTEXT *ctx, void *callback_state)
constexpr auto out_ptr(T &outptr) noexcept
constexpr RT checked_cast_to(AT i)
const char * cast_uint8_ptr_to_char(const uint8_t *b)
bool value_exists(const std::vector< T > &vec, const OT &val)
constexpr auto store_be(ParamTs &&... params)
uint32_t ESYS_TR
Forward declaration of TSS2 type for convenience.
uint32_t TPM2_HANDLE
Forward declaration of TSS2 type for convenience.