Botan 3.8.1
Crypto and TLS for C&
tpm2_util.h
Go to the documentation of this file.
1/*
2* TPM 2 internal utilities
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#ifndef BOTAN_TPM2_UTIL_H_
10#define BOTAN_TPM2_UTIL_H_
11
12#include <botan/bigint.h>
13#include <botan/concepts.h>
14#include <botan/mem_ops.h>
15#include <botan/tpm2_context.h>
16#include <botan/tpm2_error.h>
17#include <botan/tpm2_object.h>
18
19#include <botan/internal/fmt.h>
20
21#include <bit>
22#include <memory>
23#include <span>
24
25#include <tss2/tss2_esys.h>
26#include <tss2/tss2_rc.h>
27
28// There's no obvious way to get the version of the TSS from its headers,
29// instead the existence of certain return code macro definitions is used
30// as sentinels to pinpoint the TSS' version. Namely:
31//
32// - TSS2_BASE_RC_CALLBACK_NULL -> 4.0.0 or later
33// - TPM2_RC_FW_LIMITED -> 4.1.0 or later
34
35#if defined(TSS2_BASE_RC_CALLBACK_NULL)
36 // The crypto callbacks were added in tpm2-tss 4.0.0.
37 #define BOTAN_TSS2_SUPPORTS_CRYPTO_CALLBACKS
38
39 // Error decoding was added in tpm2-tss 4.0.0
40 #define BOTAN_TSS2_SUPPORTS_ERROR_DECODING
41#endif
42
43#if defined(TPM2_RC_FW_LIMITED)
44 // The crypto callbacks for SM4 were added in tpm2-tss 4.1.0.
45 #define BOTAN_TSS2_SUPPORTS_SM4_IN_CRYPTO_CALLBACKS
46#endif
47
48namespace Botan::TPM2 {
49
50/**
51 * Check the return code and throw an exception if some error occured.
52 *
53 * @throws TPM2::Error if an error occured.
54 */
55constexpr void check_rc(std::string_view location, TSS2_RC rc) {
56 if(rc != TSS2_RC_SUCCESS) {
57 throw Error(location, rc);
58 }
59}
60
61/**
62 * Check the return code and throw an exception if an unexpected error occured.
63 *
64 * Errors that are listed in the `expected_errors` parameter are considered
65 * expected and will not cause an exception to be thrown. Instead the error
66 * code is decoded and returned to the caller for further processing.
67 *
68 * @throws TPM2::Error if an unexpected error occured.
69 * @returns TSS2_RC_SUCCESS or one of the expected error codes.
70 */
71template <TSS2_RC... expected_errors>
72 requires(sizeof...(expected_errors) > 0)
73[[nodiscard]] constexpr TSS2_RC check_rc_expecting(std::string_view location, TSS2_RC rc) {
74 // If the RC is success, we can return early and avoid the decoding.
75 if(rc == TSS2_RC_SUCCESS) {
76 return rc;
77 }
78
79 // An error occured, we need to decode it to check if it was expected.
80 const TSS2_RC decoded_rc = get_raw_rc(rc);
81
82 // Check if the error is one of the expected and return those to the caller.
83 const bool is_expected_by_caller = ((decoded_rc == expected_errors) || ...);
84 if(is_expected_by_caller) {
85 return decoded_rc;
86 }
87
88 // The error was not expected, so call the normal error handling which
89 // will throw an exception.
90 check_rc(location, rc);
91
92 // We know, rc is not 'success', so this won't ever be reached.
93 return rc;
94}
95
96template <typename T>
97concept tpm2_buffer = requires(T t) {
98 { t.buffer } -> std::convertible_to<const uint8_t*>;
99 { t.size } -> std::convertible_to<size_t>;
100};
101
102/// Construct a std::span as a view into a TPM2 buffer
103constexpr auto as_span(tpm2_buffer auto& data) {
104 return std::span{data.buffer, data.size};
105}
106
107/// Set the size of @p data to @p length and construct a std::span
108/// as a view into @p data
109constexpr auto as_span(tpm2_buffer auto& data, size_t length) {
110 BOTAN_ASSERT_NOMSG(length <= sizeof(data.buffer));
111 data.size = static_cast<decltype(data.size)>(length);
112 return as_span(data);
113}
114
115/// Copy the @p data into the TPM2 buffer @p dest, assuming that the
116/// provided @p data is not larger than the capacity of the buffer.
117template <tpm2_buffer T>
118constexpr void copy_into(T& dest, std::span<const uint8_t> data) {
119 copy_mem(as_span(dest, data.size()), data);
120}
121
122/// Create a TPM2 buffer from the provided @p data, assuming that the
123/// provided @p data is not larger than the capacity of the buffer type.
124template <tpm2_buffer T>
125constexpr T copy_into(std::span<const uint8_t> data) {
126 T result;
127 copy_into(result, data);
128 return result;
129}
130
131/// Copy the content of the TPM2 buffer @p data into a new resizable byte buffer
132/// of the user's choosing.
133template <concepts::resizable_byte_buffer OutT>
134constexpr OutT copy_into(const tpm2_buffer auto& data) {
135 OutT result;
136 result.resize(data.size);
137 copy_mem(result, as_span(data));
138 return result;
139}
140
141/// Create a TPM2 buffer of a given type and @p length.
142template <tpm2_buffer T>
143constexpr T init_with_size(size_t length) {
144 T result;
145 BOTAN_ASSERT_NOMSG(length <= sizeof(result.buffer));
146 result.size = static_cast<decltype(result.size)>(length);
147 clear_bytes(result.buffer, length);
148 return result;
149}
150
151/// Create an empty TPM2 buffer of the given type.
152template <tpm2_buffer T>
153constexpr T init_empty() {
154 return init_with_size<T>(0);
155}
156
158 void operator()(void* handle) { Esys_Free(handle); }
159};
160
161/// A unique pointer type for ESYS handles that automatically frees the handle.
162template <typename T>
163using unique_esys_ptr = std::unique_ptr<T, esys_liberator>;
164
170
172 std::optional<TPM2_HANDLE> persistent = std::nullopt;
173 ESYS_TR transient = ESYS_TR_NONE;
174};
175
176/**
177 * Helper type setting a TPM2_HANDLE or ESYS_TR on a given instance of Object
178 * from a TSS2 library function's out parameter.
179 *
180 * This is not used directly, but through the out_transient_handle() and
181 * out_persistent_handle() respectively.
182 */
184 public:
185 constexpr ObjectSetter(Object& object, bool persistent = false) :
186 m_object(object), m_persistent(persistent), m_handle(persistent ? 0 : ESYS_TR_NONE) {}
187
188 constexpr ~ObjectSetter() noexcept {
189 if(!was_written()) {
190 return;
191 }
192
193 if(m_persistent) {
194 m_object.handles().persistent = m_handle;
195 } else {
196 m_object.handles().transient = m_handle;
197 }
198 }
199
200 ObjectSetter(const ObjectSetter&) = delete;
204
205 [[nodiscard]] constexpr operator uint32_t*() && noexcept { return &m_handle; }
206
207 private:
208 constexpr bool was_written() const { return m_handle != (m_persistent ? 0 : ESYS_TR_NONE); }
209
210 private:
211 Object& m_object;
212 bool m_persistent;
213 uint32_t m_handle; /// TPM2_HANDLE or ESYS_TR, both are typedefs to uint32_t
214};
215
216/// Helper to set the transient handle of an object from a TSS2 library
217/// function's out parameter.
218constexpr auto out_transient_handle(Object& object) {
219 return ObjectSetter{object, false};
220}
221
222/// Helper to set the persistent handle of an object from a TSS2 library
223/// function's out parameter.
224constexpr auto out_persistent_handle(Object& object) {
225 return ObjectSetter{object, true};
226}
227
228/// Helper for the AttributeWrapper to define mappings between
229/// boolean members of a struct and the corresponding bit masks
230template <typename FieldPointerT, std::unsigned_integral MaskT>
231 requires std::is_member_object_pointer_v<FieldPointerT>
232struct PropMap {
233 FieldPointerT field;
234 MaskT mask;
235
236 /// Access the boolean member 'field' from the given @p object
237 [[nodiscard]] constexpr bool& operator()(auto& object) const noexcept { return object.*field; }
238
239 /// Read-only access the boolean member 'field' from the given @p object
240 [[nodiscard]] constexpr bool operator()(const auto& object) const noexcept { return object.*field; }
241};
242
243/// Deduction guide to simplify the creation of PropMap instances
244template <typename MaskT, typename FieldPointerT>
245PropMap(MaskT, FieldPointerT) -> PropMap<MaskT, FieldPointerT>;
246
247/**
248 * This is an internal helper structure to wrap TPMA_* attribute bit fields.
249 *
250 * @tparam UnderlyingT the TPMA_* bit field type
251 * @tparam AttributeWrapperT the C++ struct type that wraps the TPMA_* bit field
252 * @tparam props a bunch of std::pair mappping boolean members of
253 * AttributeWrapperT to the bit masks of the TPMA_* type
254 */
255template <std::unsigned_integral UnderlyingT,
256 typename AttributeWrapperT,
259 private:
260 template <std::invocable<const PropMap<bool AttributeWrapperT::*, UnderlyingT>&> FnT>
261 static constexpr void for_all(FnT&& fn) {
262 (fn(props), ...);
263 }
264
265 static consteval bool all_single_bit_bitmasks() {
266 bool result = true;
267 for_all([&](auto&& prop) { result = result && (std::popcount(prop.mask) == 1); });
268 return result;
269 }
270
271 static_assert(all_single_bit_bitmasks(), "props... must contain single-bit flags only");
272
273 public:
274 static constexpr UnderlyingT render(AttributeWrapperT attributes) {
275 UnderlyingT result = 0;
276 for_all([&](auto&& prop) {
277 if(prop(attributes)) {
278 result |= prop.mask;
279 }
280 });
281 return result;
282 }
283
284 static constexpr AttributeWrapperT read(UnderlyingT attributes) {
285 AttributeWrapperT result;
286 for_all([&](auto&& prop) { prop(result) = (attributes & prop.mask) != 0; });
287 return result;
288 }
289};
290
291#if defined(BOTAN_HAS_RSA)
292
293/**
294 * This helper function transforms a @p public_blob in a TPM2B_PUBLIC* format
295 * into the functional components of an RSA public key. Namely, a pair of
296 * modulus and exponent as big integers.
297 *
298 * @param public_blob The public blob to decompose into RSA pubkey components
299 */
300inline std::pair<BigInt, BigInt> rsa_pubkey_components_from_tss2_public(const TPM2B_PUBLIC* public_blob) {
301 BOTAN_ASSERT_NONNULL(public_blob);
302 const auto& pub = public_blob->publicArea;
303 BOTAN_ARG_CHECK(pub.type == TPM2_ALG_RSA, "Public key is not an RSA key");
304
305 // TPM2 may report 0 when the exponent is 'the default' (2^16 + 1)
306 const auto exponent = (pub.parameters.rsaDetail.exponent == 0) ? 65537 : pub.parameters.rsaDetail.exponent;
307
308 return {BigInt(as_span(pub.unique.rsa)), exponent};
309}
310
311#endif
312
313} // namespace Botan::TPM2
314
315#endif
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:61
#define BOTAN_ASSERT_NONNULL(ptr)
Definition assert.h:88
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:31
static constexpr AttributeWrapperT read(UnderlyingT attributes)
Definition tpm2_util.h:284
static constexpr UnderlyingT render(AttributeWrapperT attributes)
Definition tpm2_util.h:274
ObjectSetter(const ObjectSetter &)=delete
constexpr ObjectSetter(Object &object, bool persistent=false)
Definition tpm2_util.h:185
ObjectSetter & operator=(ObjectSetter &&)=delete
ObjectSetter(ObjectSetter &&)=delete
constexpr ~ObjectSetter() noexcept
Definition tpm2_util.h:188
ObjectSetter & operator=(const ObjectSetter &)=delete
PropMap(MaskT, FieldPointerT) -> PropMap< MaskT, FieldPointerT >
Deduction guide to simplify the creation of PropMap instances.
constexpr T init_empty()
Create an empty TPM2 buffer of the given type.
Definition tpm2_util.h:153
constexpr void check_rc(std::string_view location, TSS2_RC rc)
Definition tpm2_util.h:55
constexpr auto out_persistent_handle(Object &object)
Definition tpm2_util.h:224
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:163
TSS2_RC get_raw_rc(TSS2_RC rc)
constexpr T init_with_size(size_t length)
Create a TPM2 buffer of a given type and length.
Definition tpm2_util.h:143
constexpr auto out_transient_handle(Object &object)
Definition tpm2_util.h:218
constexpr void copy_into(T &dest, std::span< const uint8_t > data)
Definition tpm2_util.h:118
constexpr auto as_span(tpm2_buffer auto &data)
Construct a std::span as a view into a TPM2 buffer.
Definition tpm2_util.h:103
constexpr TSS2_RC check_rc_expecting(std::string_view location, TSS2_RC rc)
Definition tpm2_util.h:73
constexpr void clear_bytes(void *ptr, size_t bytes)
Definition mem_ops.h:106
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:149
std::optional< TPM2_HANDLE > persistent
Definition tpm2_util.h:172
constexpr bool operator()(const auto &object) const noexcept
Read-only access the boolean member 'field' from the given object.
Definition tpm2_util.h:240
FieldPointerT field
Definition tpm2_util.h:233
constexpr bool & operator()(auto &object) const noexcept
Access the boolean member 'field' from the given object.
Definition tpm2_util.h:237
unique_esys_ptr< TPM2B_PUBLIC > pub
Definition tpm2_util.h:166
unique_esys_ptr< TPM2B_NAME > qualified_name
Definition tpm2_util.h:168
unique_esys_ptr< TPM2B_NAME > name
Definition tpm2_util.h:167
void operator()(void *handle)
Definition tpm2_util.h:158
uint32_t ESYS_TR
Forward declaration of TSS2 type for convenience.
uint32_t TSS2_RC
Forward declaration of TSS2 type for convenience.
Definition tpm2_error.h:15