Botan 3.9.0
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 explicit 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 // NOLINTNEXTLINE(*-explicit-conversions) FIXME
206 [[nodiscard]] constexpr operator uint32_t*() && noexcept { return &m_handle; }
207
208 private:
209 constexpr bool was_written() const { return m_handle != (m_persistent ? 0 : ESYS_TR_NONE); }
210
211 private:
212 Object& m_object;
213 bool m_persistent;
214 uint32_t m_handle; /// TPM2_HANDLE or ESYS_TR, both are typedefs to uint32_t
215};
216
217/// Helper to set the transient handle of an object from a TSS2 library
218/// function's out parameter.
219constexpr auto out_transient_handle(Object& object) {
220 return ObjectSetter{object, false};
221}
222
223/// Helper to set the persistent handle of an object from a TSS2 library
224/// function's out parameter.
225constexpr auto out_persistent_handle(Object& object) {
226 return ObjectSetter{object, true};
227}
228
229/// Helper for the AttributeWrapper to define mappings between
230/// boolean members of a struct and the corresponding bit masks
231template <typename FieldPointerT, std::unsigned_integral MaskT>
232 requires std::is_member_object_pointer_v<FieldPointerT>
233struct PropMap {
234 FieldPointerT field;
235 MaskT mask;
236
237 /// Access the boolean member 'field' from the given @p object
238 [[nodiscard]] constexpr bool& operator()(auto& object) const noexcept { return object.*field; }
239
240 /// Read-only access the boolean member 'field' from the given @p object
241 [[nodiscard]] constexpr bool operator()(const auto& object) const noexcept { return object.*field; }
242};
243
244/// Deduction guide to simplify the creation of PropMap instances
245template <typename MaskT, typename FieldPointerT>
246PropMap(MaskT, FieldPointerT) -> PropMap<MaskT, FieldPointerT>;
247
248/**
249 * This is an internal helper structure to wrap TPMA_* attribute bit fields.
250 *
251 * @tparam UnderlyingT the TPMA_* bit field type
252 * @tparam AttributeWrapperT the C++ struct type that wraps the TPMA_* bit field
253 * @tparam props a bunch of std::pair mappping boolean members of
254 * AttributeWrapperT to the bit masks of the TPMA_* type
255 */
256template <std::unsigned_integral UnderlyingT,
257 typename AttributeWrapperT,
260 private:
261 template <std::invocable<const PropMap<bool AttributeWrapperT::*, UnderlyingT>&> FnT>
262 static constexpr void for_all(const FnT& fn) {
263 (fn(props), ...);
264 }
265
266 static consteval bool all_single_bit_bitmasks() {
267 bool result = true;
268 for_all([&](auto&& prop) { result = result && (std::popcount(prop.mask) == 1); });
269 return result;
270 }
271
272 static_assert(all_single_bit_bitmasks(), "props... must contain single-bit flags only");
273
274 public:
275 static constexpr UnderlyingT render(AttributeWrapperT attributes) {
276 UnderlyingT result = 0;
277 for_all([&](auto&& prop) {
278 if(prop(attributes)) {
279 result |= prop.mask;
280 }
281 });
282 return result;
283 }
284
285 static constexpr AttributeWrapperT read(UnderlyingT attributes) {
286 AttributeWrapperT result;
287 for_all([&](auto&& prop) { prop(result) = (attributes & prop.mask) != 0; });
288 return result;
289 }
290};
291
292#if defined(BOTAN_HAS_RSA)
293
294/**
295 * This helper function transforms a @p public_blob in a TPM2B_PUBLIC* format
296 * into the functional components of an RSA public key. Namely, a pair of
297 * modulus and exponent as big integers.
298 *
299 * @param public_blob The public blob to decompose into RSA pubkey components
300 */
301inline std::pair<BigInt, BigInt> rsa_pubkey_components_from_tss2_public(const TPM2B_PUBLIC* public_blob) {
302 BOTAN_ASSERT_NONNULL(public_blob);
303 const auto& pub = public_blob->publicArea;
304 BOTAN_ARG_CHECK(pub.type == TPM2_ALG_RSA, "Public key is not an RSA key");
305
306 // TPM2 may report 0 when the exponent is 'the default' (2^16 + 1)
307 const auto exponent = (pub.parameters.rsaDetail.exponent == 0) ? 65537 : pub.parameters.rsaDetail.exponent;
308
309 return {BigInt(as_span(pub.unique.rsa)), exponent};
310}
311
312#endif
313
314} // namespace Botan::TPM2
315
316#endif
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_ASSERT_NONNULL(ptr)
Definition assert.h:114
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
static constexpr AttributeWrapperT read(UnderlyingT attributes)
Definition tpm2_util.h:285
static constexpr UnderlyingT render(AttributeWrapperT attributes)
Definition tpm2_util.h:275
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:225
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:219
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 copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:145
constexpr void clear_bytes(void *ptr, size_t bytes)
Definition mem_ops.h:102
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:241
FieldPointerT field
Definition tpm2_util.h:234
constexpr bool & operator()(auto &object) const noexcept
Access the boolean member 'field' from the given object.
Definition tpm2_util.h:238
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