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