Botan 3.7.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/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 a TPM2 buffer of a given type and @p length.
141template <tpm2_buffer T>
142constexpr T init_with_size(size_t length) {
143 T result;
144 BOTAN_ASSERT_NOMSG(length <= sizeof(result.buffer));
145 result.size = static_cast<decltype(result.size)>(length);
146 clear_bytes(result.buffer, length);
147 return result;
148}
149
150/// Create an empty TPM2 buffer of the given type.
151template <tpm2_buffer T>
152constexpr T init_empty() {
153 return init_with_size<T>(0);
154}
155
157 void operator()(void* handle) { Esys_Free(handle); }
158};
159
160/// A unique pointer type for ESYS handles that automatically frees the handle.
161template <typename T>
162using unique_esys_ptr = std::unique_ptr<T, esys_liberator>;
163
169
171 std::optional<TPM2_HANDLE> persistent = std::nullopt;
172 ESYS_TR transient = ESYS_TR_NONE;
173};
174
175/**
176 * Helper type setting a TPM2_HANDLE or ESYS_TR on a given instance of Object
177 * from a TSS2 library function's out parameter.
178 *
179 * This is not used directly, but through the out_transient_handle() and
180 * out_persistent_handle() respectively.
181 */
183 public:
184 constexpr ObjectSetter(Object& object, bool persistent = false) :
185 m_object(object), m_persistent(persistent), m_handle(persistent ? 0 : ESYS_TR_NONE) {}
186
187 constexpr ~ObjectSetter() noexcept {
188 if(!was_written()) {
189 return;
190 }
191
192 if(m_persistent) {
193 m_object.handles().persistent = m_handle;
194 } else {
195 m_object.handles().transient = m_handle;
196 }
197 }
198
199 ObjectSetter(const ObjectSetter&) = delete;
203
204 [[nodiscard]] constexpr operator uint32_t*() && noexcept { return &m_handle; }
205
206 private:
207 constexpr bool was_written() const { return m_handle != (m_persistent ? 0 : ESYS_TR_NONE); }
208
209 private:
210 Object& m_object;
211 bool m_persistent;
212 uint32_t m_handle; /// TPM2_HANDLE or ESYS_TR, both are typedefs to uint32_t
213};
214
215/// Helper to set the transient handle of an object from a TSS2 library
216/// function's out parameter.
217constexpr auto out_transient_handle(Object& object) {
218 return ObjectSetter{object, false};
219}
220
221/// Helper to set the persistent handle of an object from a TSS2 library
222/// function's out parameter.
223constexpr auto out_persistent_handle(Object& object) {
224 return ObjectSetter{object, true};
225}
226
227/// Helper for the AttributeWrapper to define mappings between
228/// boolean members of a struct and the corresponding bit masks
229template <typename FieldPointerT, std::unsigned_integral MaskT>
230 requires std::is_member_object_pointer_v<FieldPointerT>
231struct PropMap {
232 FieldPointerT field;
233 MaskT mask;
234
235 /// Access the boolean member 'field' from the given @p object
236 [[nodiscard]] constexpr bool& operator()(auto& object) const noexcept { return object.*field; }
237
238 /// Read-only access the boolean member 'field' from the given @p object
239 [[nodiscard]] constexpr bool operator()(const auto& object) const noexcept { return object.*field; }
240};
241
242/// Deduction guide to simplify the creation of PropMap instances
243template <typename MaskT, typename FieldPointerT>
244PropMap(MaskT, FieldPointerT) -> PropMap<MaskT, FieldPointerT>;
245
246/**
247 * This is an internal helper structure to wrap TPMA_* attribute bit fields.
248 *
249 * @tparam UnderlyingT the TPMA_* bit field type
250 * @tparam AttributeWrapperT the C++ struct type that wraps the TPMA_* bit field
251 * @tparam props a bunch of std::pair mappping boolean members of
252 * AttributeWrapperT to the bit masks of the TPMA_* type
253 */
254template <std::unsigned_integral UnderlyingT,
255 typename AttributeWrapperT,
258 private:
259 template <std::invocable<const PropMap<bool AttributeWrapperT::*, UnderlyingT>&> FnT>
260 static constexpr void for_all(FnT&& fn) {
261 (fn(props), ...);
262 }
263
264 static consteval bool all_single_bit_bitmasks() {
265 bool result = true;
266 for_all([&](auto&& prop) { result = result && (std::popcount(prop.mask) == 1); });
267 return result;
268 }
269
270 static_assert(all_single_bit_bitmasks(), "props... must contain single-bit flags only");
271
272 public:
273 static constexpr UnderlyingT render(AttributeWrapperT attributes) {
274 UnderlyingT result = 0;
275 for_all([&](auto&& prop) {
276 if(prop(attributes)) {
277 result |= prop.mask;
278 }
279 });
280 return result;
281 }
282
283 static constexpr AttributeWrapperT read(UnderlyingT attributes) {
284 AttributeWrapperT result;
285 for_all([&](auto&& prop) { prop(result) = (attributes & prop.mask) != 0; });
286 return result;
287 }
288};
289
290} // namespace Botan::TPM2
291
292#endif
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
static constexpr AttributeWrapperT read(UnderlyingT attributes)
Definition tpm2_util.h:283
static constexpr UnderlyingT render(AttributeWrapperT attributes)
Definition tpm2_util.h:273
ObjectSetter(const ObjectSetter &)=delete
constexpr ObjectSetter(Object &object, bool persistent=false)
Definition tpm2_util.h:184
ObjectSetter & operator=(ObjectSetter &&)=delete
ObjectSetter(ObjectSetter &&)=delete
constexpr ~ObjectSetter() noexcept
Definition tpm2_util.h:187
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:152
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:223
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
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:142
constexpr auto out_transient_handle(Object &object)
Definition tpm2_util.h:217
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 clear_bytes(void *ptr, size_t bytes)
Definition mem_ops.h:104
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:147
std::optional< TPM2_HANDLE > persistent
Definition tpm2_util.h:171
constexpr bool operator()(const auto &object) const noexcept
Read-only access the boolean member 'field' from the given object.
Definition tpm2_util.h:239
FieldPointerT field
Definition tpm2_util.h:232
constexpr bool & operator()(auto &object) const noexcept
Access the boolean member 'field' from the given object.
Definition tpm2_util.h:236
unique_esys_ptr< TPM2B_PUBLIC > pub
Definition tpm2_util.h:165
unique_esys_ptr< TPM2B_NAME > qualified_name
Definition tpm2_util.h:167
unique_esys_ptr< TPM2B_NAME > name
Definition tpm2_util.h:166
void operator()(void *handle)
Definition tpm2_util.h:157
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