Botan 3.7.1
Crypto and TLS for C&
tpm2_context.cpp
Go to the documentation of this file.
1/*
2* TPM 2 interface
3* (C) 2024 Jack Lloyd
4* (C) 2024 René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity GmbH, financed by LANCOM Systems GmbH
5*
6* Botan is released under the Simplified BSD License (see license.txt)
7*/
8
9#include <botan/tpm2_context.h>
10
11#include <botan/tpm2_key.h>
12#include <botan/tpm2_session.h>
13
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>
20
21#include <tss2/tss2_esys.h>
22#include <tss2/tss2_tcti.h>
23#include <tss2/tss2_tctildr.h>
24
25#if defined(BOTAN_HAS_TPM2_CRYPTO_BACKEND)
26 #include <botan/tpm2_crypto_backend.h>
27 #include <botan/internal/tpm2_crypto_backend_impl.h>
28#endif
29
30namespace Botan::TPM2 {
31
32namespace {
33
34constexpr TPM2_HANDLE storage_root_key_handle = TPM2_HR_PERSISTENT + 1;
35
36} // namespace
37
38struct Context::Impl {
39 ESYS_CONTEXT* m_ctx; /// m_ctx may be owned by the library user (see m_external)
40 bool m_external;
41
42#if defined(BOTAN_HAS_TPM2_CRYPTO_BACKEND)
43 std::unique_ptr<CryptoCallbackState> m_crypto_callback_state;
44#endif
45};
46
48#if defined(BOTAN_HAS_TPM2_CRYPTO_BACKEND)
50#else
51 return false;
52#endif
53}
54
55std::shared_ptr<Context> Context::create(const std::string& tcti_nameconf) {
56 const auto nameconf_ptr = tcti_nameconf.c_str();
57
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));
61 BOTAN_ASSERT_NONNULL(tcti_ctx);
62 check_rc("TPM2 Initialization", Esys_Initialize(&esys_ctx, tcti_ctx, nullptr /* ABI version */));
63 BOTAN_ASSERT_NONNULL(esys_ctx);
64
65 // We cannot std::make_shared as the constructor is private
66 return std::shared_ptr<Context>(new Context(esys_ctx, false /* context is managed by us */));
67}
68
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;
72
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));
76 BOTAN_ASSERT_NONNULL(tcti_ctx);
77 check_rc("TPM2 Initialization", Esys_Initialize(&esys_ctx, tcti_ctx, nullptr /* ABI version */));
78 BOTAN_ASSERT_NONNULL(esys_ctx);
79
80 // We cannot std::make_shared as the constructor is private
81 return std::shared_ptr<Context>(new Context(esys_ctx, false /* context is managed by us */));
82}
83
84std::shared_ptr<Context> Context::create(ESYS_CONTEXT* esys_ctx) {
85 BOTAN_ARG_CHECK(esys_ctx != nullptr, "provided esys_ctx must not be null");
86
87 // We cannot std::make_shared as the constructor is private
88 return std::shared_ptr<Context>(new Context(esys_ctx, true /* context is managed externally */));
89}
90
91Context::Context(ESYS_CONTEXT* ctx, bool external) : m_impl(std::make_unique<Impl>()) {
92 m_impl->m_ctx = ctx;
93 m_impl->m_external = external;
94 BOTAN_ASSERT_NONNULL(m_impl->m_ctx);
95}
96
97Context::Context(Context&&) noexcept = default;
98Context& Context::operator=(Context&&) noexcept = default;
99
100void Context::use_botan_crypto_backend(const std::shared_ptr<Botan::RandomNumberGenerator>& rng) {
101#if defined(BOTAN_HAS_TPM2_CRYPTO_BACKEND)
102 BOTAN_STATE_CHECK(!uses_botan_crypto_backend());
103 m_impl->m_crypto_callback_state = Botan::TPM2::use_botan_crypto_backend(esys_context(), rng);
104#else
105 BOTAN_UNUSED(rng);
106 throw Not_Implemented("This build of botan does not provide the TPM2 crypto backend");
107#endif
108}
109
111#if defined(BOTAN_HAS_TPM2_CRYPTO_BACKEND)
112 return m_impl->m_crypto_callback_state != nullptr;
113#else
114 return false;
115#endif
116}
117
118#if defined(BOTAN_HAS_TPM2_CRYPTO_BACKEND)
119CryptoCallbackState& Context::crypto_callback_state() {
120 BOTAN_ASSERT_NONNULL(m_impl->m_crypto_callback_state);
121 return *m_impl->m_crypto_callback_state;
122}
123#endif
124
125ESYS_CONTEXT* Context::esys_context() noexcept {
126 return m_impl->m_ctx;
127}
128
129namespace {
130
131uint32_t get_tpm_property(ESYS_CONTEXT* ctx, TPM2_PT property) {
132 // We expect to retrieve a single piece of information, not a list.
133 constexpr uint32_t property_count = 1;
134 constexpr TPM2_CAP capability = TPM2_CAP_TPM_PROPERTIES;
135
137 check_rc("Esys_GetCapability",
138 Esys_GetCapability(ctx,
139 ESYS_TR_NONE,
140 ESYS_TR_NONE,
141 ESYS_TR_NONE,
142 capability,
143 property,
144 property_count,
145 nullptr /* more data? - we don't care here */,
146 out_ptr(capability_data)));
147 BOTAN_ASSERT_NONNULL(capability_data);
148 BOTAN_ASSERT_NOMSG(capability_data->capability == capability);
149 BOTAN_ASSERT_NOMSG(capability_data->data.tpmProperties.count == property_count);
150 BOTAN_ASSERT_NOMSG(capability_data->data.tpmProperties.tpmProperty[0].property == property);
151
152 return capability_data->data.tpmProperties.tpmProperty[0].value;
153}
154
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]);
164 }
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) {
169 // TODO: This also contains an algProperties.algProperties bitfield
170 // that defines some characteristics of the algorithm.
171 // Currently, we don't need that information and ignore it.
172 result.push_back(caps.algorithms.algProperties[i].alg);
173 }
174 } else {
175 // TODO: support reading other capability types as needed
176 static_assert(capability != TPM2_CAP_HANDLES, "Unsupported capability");
177 }
178 return result;
179 };
180
181 TPMI_YES_NO more_data = TPM2_YES;
182 std::vector<ReturnT> properties;
183 while(more_data == TPM2_YES && count > 0) {
185 check_rc("Esys_GetCapability",
186 Esys_GetCapability(ctx,
187 ESYS_TR_NONE,
188 ESYS_TR_NONE,
189 ESYS_TR_NONE,
190 capability,
191 property,
192 count,
193 &more_data,
194 out_ptr(capability_data)));
195 BOTAN_ASSERT_NONNULL(capability_data);
196 BOTAN_ASSERT_NOMSG(capability_data->capability == capability);
197
198 const auto new_properties = extract(capability_data->data, count);
199 BOTAN_ASSERT_NOMSG(new_properties.size() <= count);
200 properties.insert(properties.end(), new_properties.begin(), new_properties.end());
201 count -= checked_cast_to<uint32_t>(new_properties.size());
202 }
203
204 return properties;
205}
206
207} // namespace
208
209std::string Context::vendor() const {
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 /* ensure zero-termination */> vendor_string{};
213
214 BufferStuffer bs(vendor_string);
215
216 // The vendor name is transported in several uint32_t fields that are
217 // loaded as big-endian bytes and concatenated to form the vendor string.
218 for(auto prop : properties) {
219 bs.append(store_be(get_tpm_property(m_impl->m_ctx, prop)));
220 }
221
222 BOTAN_ASSERT_NOMSG(bs.remaining_capacity() == 1); // the ensured zero-termination
223 return std::string(cast_uint8_ptr_to_char(vendor_string.data()));
224}
225
226std::string Context::manufacturer() const {
227 std::array<uint8_t, 4 + 1 /* ensure zero termination */> manufacturer_data{};
228 store_be(std::span{manufacturer_data}.first<4>(), get_tpm_property(m_impl->m_ctx, TPM2_PT_MANUFACTURER));
229 return std::string(cast_uint8_ptr_to_char(manufacturer_data.data()));
230}
231
232bool Context::supports_algorithm(std::string_view algo_name) const {
233 // Go through all the string mappings we have available and check if we
234 // can find the algorithm name in any of them. If we do, we can check if
235 // the TPM supports the required algorithms.
236 const auto required_alg_ids = [&]() -> std::vector<TPM2_ALG_ID> {
237 std::vector<TPM2_ALG_ID> result;
238 if(auto algo_id = asymmetric_algorithm_botan_to_tss2(algo_name)) {
239 result.push_back(algo_id.value());
240 }
241
242 if(auto hash_id = hash_algo_botan_to_tss2(algo_name)) {
243 result.push_back(hash_id.value());
244 }
245
246 if(auto block_id = block_cipher_botan_to_tss2(algo_name)) {
247 result.push_back(block_id->first);
248 }
249
250 if(auto cipher_mode_id = cipher_mode_botan_to_tss2(algo_name)) {
251 result.push_back(cipher_mode_id.value());
252 }
253
254 if(auto cipher_spec = cipher_botan_to_tss2(algo_name)) {
255 result.push_back(cipher_spec->algorithm);
256 result.push_back(cipher_spec->mode.sym);
257 }
258
259 if(auto sig_padding = rsa_signature_padding_botan_to_tss2(algo_name)) {
260 result.push_back(sig_padding.value());
261 }
262
263 if(auto sig = rsa_signature_scheme_botan_to_tss2(algo_name)) {
264 result.push_back(sig->scheme);
265 result.push_back(sig->details.any.hashAlg);
266 }
267
268 if(auto enc_scheme = rsa_encryption_scheme_botan_to_tss2(algo_name)) {
269 result.push_back(enc_scheme->scheme);
270 if(enc_scheme->scheme == TPM2_ALG_OAEP) {
271 result.push_back(enc_scheme->details.oaep.hashAlg);
272 }
273 }
274
275 if(auto enc_id = rsa_encryption_padding_botan_to_tss2(algo_name)) {
276 result.push_back(enc_id.value());
277 }
278
279 return result;
280 }();
281
282 if(required_alg_ids.empty()) {
283 // The algorithm name is not known to us, so we cannot check for support.
284 return false;
285 }
286
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);
289
290 return std::all_of(
291 required_alg_ids.begin(), required_alg_ids.end(), [&](TPM2_ALG_ID id) { return value_exists(algo_caps, id); });
292}
293
295 return get_tpm_property(m_impl->m_ctx, TPM2_PT_MAX_DIGEST);
296}
297
298std::unique_ptr<TPM2::PrivateKey> Context::storage_root_key(std::span<const uint8_t> auth_value,
299 const SessionBundle& sessions) {
300 return TPM2::PrivateKey::load_persistent(shared_from_this(), storage_root_key_handle, auth_value, sessions);
301}
302
303std::vector<ESYS_TR> Context::transient_handles() const {
304 return get_tpm_property_list<TPM2_CAP_HANDLES, ESYS_TR>(m_impl->m_ctx, TPM2_TRANSIENT_FIRST, TPM2_MAX_CAP_HANDLES);
305}
306
307std::optional<TPM2_HANDLE> Context::find_free_persistent_handle() const {
308 const auto occupied_handles = persistent_handles();
309
310 // This is modeled after the implementation in tpm2-tools, which also takes
311 // "platform persistent" handles into account. We don't do that here, but
312 // we might need to in the future.
313 //
314 // See: https://github.com/tpm2-software/tpm2-tools/blob/bd832d3f79/lib/tpm2_capability.c#L143-L196
315
316 // all persistent handles are occupied
317 if(occupied_handles.size() >= TPM2_MAX_CAP_HANDLES) {
318 return std::nullopt;
319 }
320
321 // find the lowest handle that is not occupied
322 for(TPM2_HANDLE i = TPM2_PERSISTENT_FIRST; i < TPM2_PERSISTENT_LAST; ++i) {
323 if(!value_exists(occupied_handles, i)) {
324 return i;
325 }
326 }
327
329}
330
331std::vector<TPM2_HANDLE> Context::persistent_handles() const {
332 return get_tpm_property_list<TPM2_CAP_HANDLES, TPM2_HANDLE>(
333 m_impl->m_ctx, TPM2_PERSISTENT_FIRST, TPM2_MAX_CAP_HANDLES);
334}
335
337 const SessionBundle& sessions,
338 std::span<const uint8_t> auth_value,
339 std::optional<TPM2_HANDLE> persistent_handle) {
340 auto& handles = key.handles();
341
342 BOTAN_ARG_CHECK(!persistent_handle || !value_exists(persistent_handles(), persistent_handle.value()),
343 "Persistent handle already in use");
344 BOTAN_ARG_CHECK(!handles.has_persistent_handle(), "Key already has a persistent handle assigned");
345
346 // 1. Decide on the location to persist the key to.
347 // This uses either the handle provided by the caller or a free handle.
348 const TPMI_DH_PERSISTENT new_persistent_handle = [&] {
349 if(persistent_handle.has_value()) {
350 return persistent_handle.value();
351 } else {
352 const auto free_persistent_handle = find_free_persistent_handle();
353 BOTAN_STATE_CHECK(free_persistent_handle.has_value());
354 return free_persistent_handle.value();
355 }
356 }();
357
358 // 2. Persist the transient key in the TPM's NV storage
359 // This will flush the transient key handle and replace it with a new
360 // transient handle that references the persisted key.
361 check_rc("Esys_EvictControl",
362 Esys_EvictControl(m_impl->m_ctx,
363 ESYS_TR_RH_OWNER /*TODO: hierarchy*/,
364 handles.transient_handle(),
365 sessions[0],
366 sessions[1],
367 sessions[2],
368 new_persistent_handle,
369 out_transient_handle(handles)));
370 BOTAN_ASSERT_NOMSG(handles.has_transient_handle());
371
372 // 3. Reset the auth value of the key object
373 // This is necessary to ensure that the key object remains usable after
374 // the transient handle was recreated inside Esys_EvictControl().
375 if(!auth_value.empty()) {
376 const auto user_auth = copy_into<TPM2B_AUTH>(auth_value);
377 check_rc("Esys_TR_SetAuth", Esys_TR_SetAuth(m_impl->m_ctx, handles.transient_handle(), &user_auth));
378 }
379
380 // 4. Update the key object with the new persistent handle
381 // This double-checks that the key was persisted at the correct location,
382 // but also brings the key object into a consistent state.
383 check_rc("Esys_TR_GetTpmHandle",
384 Esys_TR_GetTpmHandle(m_impl->m_ctx, handles.transient_handle(), out_persistent_handle(handles)));
385
386 BOTAN_ASSERT_NOMSG(handles.has_persistent_handle());
387 BOTAN_ASSERT_EQUAL(new_persistent_handle, handles.persistent_handle(), "key was persisted at the correct location");
388
389 return new_persistent_handle;
390}
391
392void Context::evict(std::unique_ptr<TPM2::PrivateKey> key, const SessionBundle& sessions) {
394
395 auto& handles = key->handles();
396 BOTAN_ARG_CHECK(handles.has_persistent_handle(), "Key does not have a persistent handle assigned");
397
398 // 1. Evict the key from the TPM's NV storage
399 // This will free the persistent handle, but the transient handle will
400 // still be valid.
401 ESYS_TR no_new_handle = ESYS_TR_NONE;
402 check_rc("Esys_EvictControl",
403 Esys_EvictControl(m_impl->m_ctx,
404 ESYS_TR_RH_OWNER /*TODO: hierarchy*/,
405 handles.transient_handle(),
406 sessions[0],
407 sessions[1],
408 sessions[2],
409 0,
410 &no_new_handle));
411 BOTAN_ASSERT(no_new_handle == ESYS_TR_NONE, "When deleting a key, no new handle is returned");
412
413 // 2. The persistent key was deleted and the transient key was flushed by
414 // Esys_EvictControl().
415 handles._disengage();
416}
417
419 if(!m_impl) {
420 return;
421 }
422
423#if defined(BOTAN_HAS_TPM2_CRYPTO_BACKEND)
424 // If this object manages a crypto backend state object and the ESYS context
425 // will live on, because it was externally provided, we have to de-register
426 // this state object from the crypto callbacks.
427 //
428 // This will prevent the crypto backend callbacks from using a dangling
429 // pointer and cause graceful errors if the externally provided ESYS context
430 // is used for any action that would still need the crypto backend state.
431 //
432 // We deliberately do not just disable the crypto backend silently, as that
433 // might give users the false impression that they continue to benefit from
434 // the crypto backend while in fact they're back to the TSS' default.
435 if(m_impl->m_external && uses_botan_crypto_backend()) {
436 try {
437 set_crypto_callbacks(esys_context(), nullptr /* reset callback state */);
438 } catch(...) {
439 // ignore errors in destructor
440 }
441 m_impl->m_crypto_callback_state.reset();
442 }
443#endif
444
445 // We don't finalize contexts that were provided externally. Those are
446 // expected to be handled by the library users' applications.
447 if(!m_impl->m_external) {
448 // If the TCTI context was initialized explicitly, Esys_GetTcti() will
449 // return a pointer to the TCTI context that then has to be finalized
450 // explicitly. See ESAPI Specification Section 6.3 "Esys_GetTcti".
451 TSS2_TCTI_CONTEXT* tcti_ctx = nullptr;
452 Esys_GetTcti(m_impl->m_ctx, &tcti_ctx); // ignore error in destructor
453 if(tcti_ctx != nullptr) {
454 Tss2_TctiLdr_Finalize(&tcti_ctx);
455 }
456
457 Esys_Finalize(&m_impl->m_ctx);
458 }
459}
460
461} // namespace Botan::TPM2
#define BOTAN_UNUSED
Definition assert.h:118
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:41
#define BOTAN_ASSERT_EQUAL(expr1, expr2, assertion_made)
Definition assert.h:68
#define BOTAN_ASSERT_NONNULL(ptr)
Definition assert.h:86
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:29
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:50
#define BOTAN_ASSERT_UNREACHABLE()
Definition assert.h:137
Helper class to ease in-place marshalling of concatenated fixed-length values.
Definition stl_util.h:142
constexpr void append(std::span< const uint8_t > buffer)
Definition stl_util.h:177
constexpr size_t remaining_capacity() const
Definition stl_util.h:189
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)
Definition tpm2_key.cpp:177
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)
Definition tpm2_util.h:54
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)
Definition tpm2_util.h:223
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.
Definition tpm2_util.h:162
std::optional< TPMI_ALG_HASH > hash_algo_botan_to_tss2(std::string_view hash_name) noexcept
constexpr auto out_transient_handle(Object &object)
Definition tpm2_util.h:217
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)
Definition tpm2_util.h:117
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
Definition stl_util.h:420
constexpr RT checked_cast_to(AT i)
Definition int_utils.h:74
const char * cast_uint8_ptr_to_char(const uint8_t *b)
Definition mem_ops.h:278
bool value_exists(const std::vector< T > &vec, const OT &val)
Definition stl_util.h:60
constexpr auto store_be(ParamTs &&... params)
Definition loadstor.h:773
uint32_t ESYS_TR
Forward declaration of TSS2 type for convenience.
uint32_t TPM2_HANDLE
Forward declaration of TSS2 type for convenience.