Botan 3.6.0
Crypto and TLS for C&
tpm2_key.cpp
Go to the documentation of this file.
1/*
2* TPM 2.0 Key Wrappers' Base Class
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_key.h>
10
11#if defined(BOTAN_HAS_TPM2_RSA_ADAPTER)
12 #include <botan/tpm2_rsa.h>
13#endif
14#if defined(BOTAN_HAS_TPM2_ECC_ADAPTER)
15 #include <botan/tpm2_ecc.h>
16#endif
17
18#include <botan/internal/fmt.h>
19#include <botan/internal/stl_util.h>
20#include <botan/internal/tpm2_algo_mappings.h>
21#include <botan/internal/tpm2_hash.h>
22#include <botan/internal/tpm2_util.h>
23
24#include <tss2/tss2_esys.h>
25#include <tss2/tss2_mu.h>
26
27namespace Botan::TPM2 {
28
29#if defined(BOTAN_HAS_RSA)
30Botan::RSA_PublicKey rsa_pubkey_from_tss2_public(const TPM2B_PUBLIC* public_area) {
31 BOTAN_ASSERT_NONNULL(public_area);
32 const auto& pub = public_area->publicArea;
33 BOTAN_ARG_CHECK(pub.type == TPM2_ALG_RSA, "Public key is not an RSA key");
34
35 // TPM2 may report 0 when the exponent is 'the default' (2^16 + 1)
36 const auto exponent = (pub.parameters.rsaDetail.exponent == 0) ? 65537 : pub.parameters.rsaDetail.exponent;
37
38 return Botan::RSA_PublicKey(BigInt(as_span(pub.unique.rsa)), exponent);
39}
40#endif
41
42#if defined(BOTAN_HAS_ECC_GROUP)
43std::pair<EC_Group, EC_AffinePoint> ecc_pubkey_from_tss2_public(const TPM2B_PUBLIC* public_blob) {
44 BOTAN_ASSERT_NONNULL(public_blob);
45 BOTAN_ARG_CHECK(public_blob->publicArea.type == TPM2_ALG_ECC, "Public blob is not an ECC key");
46
47 const auto curve_id = public_blob->publicArea.parameters.eccDetail.curveID;
48 const auto curve_name = curve_id_tss2_to_botan(curve_id);
49 if(!curve_name) {
50 throw Invalid_Argument(Botan::fmt("Unsupported ECC curve: {}", curve_id));
51 }
52
53 auto curve = Botan::EC_Group::from_name(curve_name.value());
54 // Create an EC_AffinePoint from the x and y coordinates as SEC1 uncompressed point.
55 // According to the TPM2.0 specification Part 1 C.8, each coordinate is already padded to the curve's size.
56 auto point = EC_AffinePoint::deserialize(curve,
57 concat(std::vector<uint8_t>({0x04}),
58 as_span(public_blob->publicArea.unique.ecc.x),
59 as_span(public_blob->publicArea.unique.ecc.y)));
60 if(!point) {
61 throw Invalid_Argument("Invalid ECC Point");
62 }
63 return {std::move(curve), std::move(point.value())};
64}
65#endif
66
67namespace {
68
69Object load_persistent_object(const std::shared_ptr<Context>& ctx,
70 TPM2_HANDLE persistent_object_handle,
71 std::span<const uint8_t> auth_value,
72 const SessionBundle& sessions) {
74 TPM2_PERSISTENT_FIRST <= persistent_object_handle && persistent_object_handle <= TPM2_PERSISTENT_LAST,
75 "persistent_object_handle out of range");
77 const bool is_persistent = value_exists(ctx->persistent_handles(), persistent_object_handle);
78 BOTAN_STATE_CHECK(is_persistent);
79
80 Object object(ctx);
81
82 check_rc("Esys_TR_FromTPMPublic",
83 Esys_TR_FromTPMPublic(
84 *ctx, persistent_object_handle, sessions[0], sessions[1], sessions[2], out_transient_handle(object)));
85
86 if(!auth_value.empty()) {
87 const auto user_auth = copy_into<TPM2B_AUTH>(auth_value);
88 check_rc("Esys_TR_SetAuth", Esys_TR_SetAuth(*ctx, object.transient_handle(), &user_auth));
89 }
90
91 check_rc("Esys_TR_GetTpmHandle",
92 Esys_TR_GetTpmHandle(*ctx, object.transient_handle(), out_persistent_handle(object)));
93
94 const auto key_type = object._public_info(sessions).pub->publicArea.type;
95 BOTAN_ARG_CHECK(key_type == TPM2_ALG_RSA || key_type == TPM2_ALG_ECC,
96 "persistent object is neither RSA nor ECC public key");
97
98 return object;
99}
100
101std::vector<uint8_t> marshal_public_blob(const TPM2B_PUBLIC* public_data) {
102 size_t bytes_required = 0;
103 std::vector<uint8_t> marshalled_blob(sizeof(TPM2B_PUBLIC));
104 check_rc("Tss2_MU_TPM2B_PUBLIC_Marshal",
105 Tss2_MU_TPM2B_PUBLIC_Marshal(public_data, marshalled_blob.data(), marshalled_blob.size(), &bytes_required));
106 marshalled_blob.resize(bytes_required);
107 marshalled_blob.shrink_to_fit();
108 return marshalled_blob;
109}
110
111TPM2B_PUBLIC unmarshal_public_blob(std::span<const uint8_t> marshalled_blob) {
112 TPM2B_PUBLIC public_data{};
113 size_t offset = 0;
114 check_rc("Tss2_MU_TPM2B_PUBLIC_Unmarshal",
115 Tss2_MU_TPM2B_PUBLIC_Unmarshal(marshalled_blob.data(), marshalled_blob.size(), &offset, &public_data));
116 BOTAN_ASSERT_NOMSG(offset == marshalled_blob.size());
117 return public_data;
118}
119
120TPM2B_TEMPLATE marshal_template(const TPMT_PUBLIC& key_template) {
121 TPM2B_TEMPLATE result = {};
122 size_t offset = 0;
123 check_rc("Tss2_MU_TPMT_PUBLIC_Marshal",
124 Tss2_MU_TPMT_PUBLIC_Marshal(&key_template, result.buffer, sizeof(TPMT_PUBLIC), &offset));
125 result.size = offset;
126 return result;
127}
128
129} // namespace
130
131std::unique_ptr<PublicKey> PublicKey::load_persistent(const std::shared_ptr<Context>& ctx,
132 TPM2_HANDLE persistent_object_handle,
133 const SessionBundle& sessions) {
134 return create(load_persistent_object(ctx, persistent_object_handle, {}, sessions), sessions);
135}
136
137std::unique_ptr<PublicKey> PublicKey::load_transient(const std::shared_ptr<Context>& ctx,
138 std::span<const uint8_t> public_blob,
139 const SessionBundle& sessions) {
140 const auto public_data = unmarshal_public_blob(public_blob);
141
143
144 Object handle(ctx);
145 check_rc("Esys_LoadExternal",
146 Esys_LoadExternal(*ctx,
147 sessions[0],
148 sessions[1],
149 sessions[2],
150 nullptr /* no private data to be loaded */,
151 &public_data,
152 TPM2_RH_NULL,
153 out_transient_handle(handle)));
154 return create(std::move(handle), sessions);
155}
156
157std::vector<uint8_t> PublicKey::raw_public_key_bits() const {
158 return marshal_public_blob(m_handle._public_info(m_sessions).pub.get());
159}
160
161std::unique_ptr<PublicKey> PublicKey::create(Object handles, const SessionBundle& sessions) {
162 [[maybe_unused]] const auto* pubinfo = handles._public_info(sessions).pub.get();
163#if defined(BOTAN_HAS_TPM2_RSA_ADAPTER)
164 if(pubinfo->publicArea.type == TPM2_ALG_RSA) {
165 return std::unique_ptr<PublicKey>(new RSA_PublicKey(std::move(handles), sessions, pubinfo));
166 }
167#endif
168#if defined(BOTAN_HAS_TPM2_ECC_ADAPTER)
169 if(pubinfo->publicArea.type == TPM2_ALG_ECC) {
170 return std::unique_ptr<PublicKey>(new EC_PublicKey(std::move(handles), sessions, pubinfo));
171 }
172#endif
173
174 throw Not_Implemented(Botan::fmt("Loaded a {} public key of an unsupported type",
175 handles.has_persistent_handle() ? "persistent" : "transient"));
176}
177
178// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
179
180std::unique_ptr<PrivateKey> PrivateKey::load_persistent(const std::shared_ptr<Context>& ctx,
181 TPM2_HANDLE persistent_object_handle,
182 std::span<const uint8_t> auth_value,
183 const SessionBundle& sessions) {
184 return create(load_persistent_object(ctx, persistent_object_handle, auth_value, sessions),
185 sessions,
186 nullptr /* pull public info from handle */,
187 {} /* persistent keys don't have an encrypted private blob */);
188}
189
190std::unique_ptr<PrivateKey> PrivateKey::load_transient(const std::shared_ptr<Context>& ctx,
191 std::span<const uint8_t> auth_value,
192 const TPM2::PrivateKey& parent,
193 std::span<const uint8_t> public_blob,
194 std::span<const uint8_t> private_blob,
195 const SessionBundle& sessions) {
197 Object handle(ctx);
198
199 const auto public_data = unmarshal_public_blob(public_blob);
200 const auto private_data = copy_into<TPM2B_PRIVATE>(private_blob);
201
202 check_rc("Esys_Load",
203 Esys_Load(*ctx,
204 parent.handles().transient_handle(),
205 sessions[0],
206 sessions[1],
207 sessions[2],
208 &private_data,
209 &public_data,
210 out_transient_handle(handle)));
211
212 if(!auth_value.empty()) {
213 const auto user_auth = copy_into<TPM2B_AUTH>(auth_value);
214 check_rc("Esys_TR_SetAuth", Esys_TR_SetAuth(*ctx, handle.transient_handle(), &user_auth));
215 }
216
217 return create(std::move(handle), sessions, nullptr /* pull public info from handle */, private_blob);
218}
219
220std::unique_ptr<PrivateKey> PrivateKey::create_transient_from_template(const std::shared_ptr<Context>& ctx,
221 const SessionBundle& sessions,
222 ESYS_TR parent,
223 const TPMT_PUBLIC& key_template,
224 const TPM2B_SENSITIVE_CREATE& sensitive_data) {
226
227 switch(key_template.type) {
228 case TPM2_ALG_RSA:
229#if not defined(BOTAN_HAS_TPM2_RSA_ADAPTER)
230 throw Not_Implemented("TPM2-based RSA keys are not supported in this build");
231#endif
232 break;
233 case TPM2_ALG_ECC:
234#if not defined(BOTAN_HAS_TPM2_ECC_ADAPTER)
235 throw Not_Implemented("TPM2-based ECC keys are not supported in this build");
236#endif
237 break;
238 default:
239 throw Invalid_Argument("Unsupported key type");
240 }
241
242 const auto marshalled_template = marshal_template(key_template);
243
244 Object handle(ctx);
245 unique_esys_ptr<TPM2B_PRIVATE> private_bytes;
247
248 // Esys_CreateLoaded can create different object types depending on the type
249 // of the parent passed in. Namely, this will create a Primary object if the
250 // parent is referencing a Primary Seed; an Ordinary Object if the parent is
251 // referencing a Storage Parent; and a Derived Object if the parent is
252 // referencing a Derivation Parent.
253 //
254 // See the Architecture Document, Section 27.1.
255 check_rc("Esys_CreateLoaded",
256 Esys_CreateLoaded(*ctx,
257 parent,
258 sessions[0],
259 sessions[1],
260 sessions[2],
261 &sensitive_data,
262 &marshalled_template,
263 out_transient_handle(handle),
264 out_ptr(private_bytes),
265 out_ptr(public_info)));
266 BOTAN_ASSERT_NONNULL(private_bytes);
267 BOTAN_ASSERT_NOMSG(public_info->publicArea.type == key_template.type);
269
270 return create(std::move(handle), sessions, public_info.get(), as_span(*private_bytes));
271}
272
275 BOTAN_ASSERT_NOMSG(!m_private_blob.empty());
276 return Botan::lock(m_private_blob);
277}
278
279std::vector<uint8_t> PrivateKey::raw_public_key_bits() const {
280 return marshal_public_blob(m_handle._public_info(m_sessions).pub.get());
281}
282
284 // Architectural Document, Section 4.54
285 // any object with the decrypt and restricted attributes SET and the sign
286 // attribute CLEAR
287 const auto attrs = m_handle.attributes(m_sessions);
288 return attrs.decrypt && attrs.restricted && !attrs.sign_encrypt;
289}
290
291std::unique_ptr<PrivateKey> PrivateKey::create(Object handles,
292 [[maybe_unused]] const SessionBundle& sessions,
293 [[maybe_unused]] const TPM2B_PUBLIC* public_info,
294 [[maybe_unused]] std::span<const uint8_t> private_blob) {
295 if(!public_info) {
296 public_info = handles._public_info(sessions).pub.get();
297 }
298
299#if defined(BOTAN_HAS_TPM2_RSA_ADAPTER)
300 if(public_info->publicArea.type == TPM2_ALG_RSA) {
301 return std::unique_ptr<RSA_PrivateKey>(
302 new RSA_PrivateKey(std::move(handles), sessions, public_info, private_blob));
303 }
304#endif
305
306#if defined(BOTAN_HAS_TPM2_ECC_ADAPTER)
307 if(public_info->publicArea.type == TPM2_ALG_ECC) {
308 return std::unique_ptr<EC_PrivateKey>(new EC_PrivateKey(std::move(handles), sessions, public_info, private_blob));
309 }
310#endif
311
312 throw Not_Implemented(Botan::fmt("Loaded a {} private key of an unsupported type",
313 handles.has_persistent_handle() ? "persistent" : "transient"));
314}
315
316} // namespace Botan::TPM2
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:41
#define BOTAN_ASSERT_NONNULL(ptr)
Definition assert.h:86
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:29
static std::optional< EC_AffinePoint > deserialize(const EC_Group &group, std::span< const uint8_t > bytes)
Definition ec_apoint.cpp:97
static EC_Group from_name(std::string_view name)
Definition ec_group.cpp:307
bool has_transient_handle() const
bool has_persistent_handle() const
PublicInfo & _public_info(const SessionBundle &sessions, std::optional< TPMI_ALG_PUBLIC > expected_type={}) const
ObjectAttributes attributes(const SessionBundle &sessions) const
ESYS_TR transient_handle() const noexcept
static std::unique_ptr< PrivateKey > create_transient_from_template(const std::shared_ptr< Context > &ctx, const SessionBundle &sessions, ESYS_TR parent, const TPMT_PUBLIC &key_template, const TPM2B_SENSITIVE_CREATE &sensitive_data)
Definition tpm2_key.cpp:220
std::vector< uint8_t > raw_public_key_bits() const override
Definition tpm2_key.cpp:279
static std::unique_ptr< PrivateKey > create(Object handles, const SessionBundle &sessions, const TPM2B_PUBLIC *public_info, std::span< const uint8_t > private_blob)
Definition tpm2_key.cpp:291
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:180
static std::unique_ptr< PrivateKey > load_transient(const std::shared_ptr< Context > &ctx, std::span< const uint8_t > auth_value, const TPM2::PrivateKey &parent, std::span< const uint8_t > public_blob, std::span< const uint8_t > private_blob, const SessionBundle &sessions)
Definition tpm2_key.cpp:190
const SessionBundle & sessions() const
Definition tpm2_key.h:224
secure_vector< uint8_t > raw_private_key_bits() const override
Definition tpm2_key.cpp:273
static std::unique_ptr< PublicKey > create(Object handles, const SessionBundle &sessions)
Definition tpm2_key.cpp:161
std::vector< uint8_t > raw_public_key_bits() const override
Definition tpm2_key.cpp:157
static std::unique_ptr< PublicKey > load_persistent(const std::shared_ptr< Context > &ctx, TPM2_HANDLE persistent_object_handle, const SessionBundle &sessions={})
Definition tpm2_key.cpp:131
const Object & handles() const
Definition tpm2_key.h:99
const SessionBundle & sessions() const
Definition tpm2_key.h:101
static std::unique_ptr< PublicKey > load_transient(const std::shared_ptr< Context > &ctx, std::span< const uint8_t > public_blob, const SessionBundle &sessions)
Definition tpm2_key.cpp:137
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
std::optional< std::string > curve_id_tss2_to_botan(TPMI_ECC_CURVE mode_id)
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 auto out_ptr(T &outptr) noexcept
Definition stl_util.h:420
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
secure_vector< T > lock(const std::vector< T > &in)
Definition secmem.h:70
constexpr auto concat(Rs &&... ranges)
Definition stl_util.h:263
bool value_exists(const std::vector< T > &vec, const OT &val)
Definition stl_util.h:60
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:61
bool decrypt
The private portion of the key might be used for data decryption.
Definition tpm2_object.h:64
unique_esys_ptr< TPM2B_PUBLIC > pub
Definition tpm2_util.h:157
uint32_t ESYS_TR
Forward declaration of TSS2 type for convenience.
uint32_t TPM2_HANDLE
Forward declaration of TSS2 type for convenience.