• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 13262741994

11 Feb 2025 12:19PM UTC coverage: 91.656% (-0.003%) from 91.659%
13262741994

Pull #4647

github

web-flow
Merge 0b8e56724 into f372b5a9e
Pull Request #4647: Avoid using mem_ops.h or assert.h in public headers

94864 of 103500 relevant lines covered (91.66%)

11330304.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

5.73
/src/lib/prov/tpm/tpm.cpp
1
/*
2
* TPM 1.2 interface
3
* (C) 2015 Jack Lloyd
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include <botan/tpm.h>
9

10
#include <botan/der_enc.h>
11
#include <botan/hash.h>
12
#include <botan/mem_ops.h>
13
#include <botan/pk_ops.h>
14
#include <botan/rsa.h>
15
#include <botan/internal/fmt.h>
16
#include <botan/internal/hash_id.h>
17
#include <botan/internal/workfactor.h>
18
#include <limits>
19

20
#include <trousers/trousers.h>
21
#include <tss/platform.h>
22
#include <tss/tspi.h>
23

24
// TODO: dynamically load the TPM libraries?
25

26
namespace Botan {
27

28
namespace {
29

30
void tss_error(TSS_RESULT res, const char* expr, const char* file, int line) {
1✔
31
   std::string err = fmt("TPM error {} in layer {} executing {} at {}:{}",
1✔
32
                         Trspi_Error_String(res),
1✔
33
                         Trspi_Error_Layer(res),
1✔
34
                         expr,
35
                         line,
36
                         file);
1✔
37

38
   throw TPM_Error(err);
1✔
39
}
1✔
40

41
TSS_FLAG bit_flag(size_t bits) {
×
42
   switch(bits) {
×
43
      // 512 supported, but ignored and rejected here
44
      case 1024:
45
         return TSS_KEY_SIZE_1024;
46
      case 2048:
×
47
         return TSS_KEY_SIZE_2048;
×
48

49
      // Most? v1.2 TPMs only support 1024 and 2048 bit keys ...
50
      case 4096:
×
51
         return TSS_KEY_SIZE_4096;
×
52
      case 8192:
×
53
         return TSS_KEY_SIZE_8192;
×
54
      case 16384:
×
55
         return TSS_KEY_SIZE_16384;
×
56
      default:
×
57
         throw Invalid_Argument("Unsupported TPM key size " + std::to_string(bits));
×
58
   }
59
}
60

61
template <typename T>
62
uint32_t to_uint32(T v) {
×
63
   BOTAN_ARG_CHECK(v > std::numeric_limits<uint32_t>::max(), "Value too large for 32bit unsigned integer");
×
64
   return static_cast<uint32_t>(v);
×
65
}
66

67
#define TSPI_CHECK_SUCCESS(expr)                    \
68
   do {                                             \
69
      TSS_RESULT res = expr;                        \
70
      if(res != TSS_SUCCESS)                        \
71
         tss_error(res, #expr, __FILE__, __LINE__); \
72
   } while(0)
73

74
std::vector<uint8_t> get_obj_attr(TSS_HCONTEXT ctx, TSS_HOBJECT obj, TSS_FLAG flag, TSS_FLAG sub_flag) {
×
75
   BYTE* data = nullptr;
×
76
   UINT32 data_len = 0;
×
77
   TSPI_CHECK_SUCCESS(::Tspi_GetAttribData(obj, flag, sub_flag, &data_len, &data));
×
78

79
   std::vector<uint8_t> r(data, data + data_len);
×
80

81
   TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(ctx, data));
×
82

83
   return r;
×
84
}
×
85

86
void set_policy_secret(TSS_HPOLICY policy, const char* secret) {
×
87
   if(secret) {
×
88
      BYTE* as_b = const_cast<BYTE*>(reinterpret_cast<const BYTE*>(secret));
×
89
      TSPI_CHECK_SUCCESS(::Tspi_Policy_SetSecret(policy, TSS_SECRET_MODE_PLAIN, to_uint32(std::strlen(secret)), as_b));
×
90
   } else {
91
      static const uint8_t nullpass[20] = {0};
×
92

93
      TSPI_CHECK_SUCCESS(
×
94
         ::Tspi_Policy_SetSecret(policy, TSS_SECRET_MODE_SHA1, sizeof(nullpass), const_cast<BYTE*>(nullpass)));
95
   }
96
}
×
97

98
TSS_UUID to_tss_uuid(const UUID& uuid) {
×
99
   static_assert(sizeof(TSS_UUID) == 16, "Expected size of packed UUID");
×
100

101
   TSS_UUID tss_uuid;
×
102
   typecast_copy(tss_uuid, uuid.binary_value().data());
×
103
   return tss_uuid;
×
104
}
105

106
UUID from_tss_uuid(const TSS_UUID& tss_uuid) {
×
107
   static_assert(sizeof(TSS_UUID) == 16, "Expected size of packed UUID");
×
108

109
   std::vector<uint8_t> mem(16);
×
110
   typecast_copy(mem.data(), tss_uuid);
×
111
   UUID uuid(std::move(mem));
×
112
   return uuid;
×
113
}
×
114

115
TPM_Storage_Type storage_type_from_tss_flag(TSS_FLAG flag) {
×
116
   if(flag == TSS_PS_TYPE_USER)
×
117
      return TPM_Storage_Type::User;
118
   else if(flag == TSS_PS_TYPE_SYSTEM)
×
119
      return TPM_Storage_Type::System;
120
   else
121
      throw TPM_Error("Invalid storage flag " + std::to_string(flag));
×
122
}
123

124
std::string format_url(const UUID& uuid, TPM_Storage_Type storage) {
×
125
   std::string storage_str = (storage == TPM_Storage_Type::User) ? "user" : "system";
×
126
   return "tpmkey:uuid=" + uuid.to_string() + ";storage=" + storage_str;
×
127
}
×
128

129
std::string format_url(const TSS_UUID& tss_uuid, TSS_FLAG store_type) {
×
130
   UUID uuid = from_tss_uuid(tss_uuid);
×
131

132
   return format_url(from_tss_uuid(tss_uuid), storage_type_from_tss_flag(store_type));
×
133
}
×
134

135
}  // namespace
136

137
TPM_Context::TPM_Context(pin_cb cb, const char* srk_password) : m_pin_cb(cb), m_srk_policy(0) {
1✔
138
   TSPI_CHECK_SUCCESS(::Tspi_Context_Create(&m_ctx));
1✔
139
   TSPI_CHECK_SUCCESS(::Tspi_Context_Connect(m_ctx, nullptr));
1✔
140

141
   TSPI_CHECK_SUCCESS(::Tspi_Context_GetTpmObject(m_ctx, &m_tpm));
×
142

143
   const TSS_UUID SRK_UUID = TSS_UUID_SRK;
×
144

145
   TSPI_CHECK_SUCCESS(::Tspi_Context_LoadKeyByUUID(m_ctx, TSS_PS_TYPE_SYSTEM, SRK_UUID, &m_srk));
×
146

147
   TSPI_CHECK_SUCCESS(::Tspi_GetPolicyObject(m_srk, TSS_POLICY_USAGE, &m_srk_policy));
×
148
   set_policy_secret(m_srk_policy, srk_password);
×
149

150
   // TODO: do we have to cache it?
151
   // TODO: try to use SRK with null, if it fails call the pin cb?
152
}
1✔
153

154
TPM_Context::~TPM_Context() {
×
155
   TSPI_CHECK_SUCCESS(::Tspi_Context_CloseObject(m_ctx, m_srk));
×
156
   //TSPI_CHECK_SUCCESS(::Tspi_Context_CloseObject(m_ctx, m_tpm));
157
   TSPI_CHECK_SUCCESS(::Tspi_Context_Close(m_srk_policy));
×
158
   TSPI_CHECK_SUCCESS(::Tspi_Context_Close(m_ctx));
×
159
}
×
160

161
uint32_t TPM_Context::current_counter() {
×
162
   uint32_t r = 0;
×
163
   TSPI_CHECK_SUCCESS(::Tspi_TPM_ReadCounter(m_tpm, &r));
×
164
   return r;
×
165
}
166

167
void TPM_Context::gen_random(uint8_t out[], size_t out_len) {
×
168
   BYTE* mem;
×
169
   TSPI_CHECK_SUCCESS(::Tspi_TPM_GetRandom(m_tpm, to_uint32(out_len), &mem));
×
170
   copy_mem(out, reinterpret_cast<const uint8_t*>(mem), out_len);
×
171
   TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(m_ctx, mem));
×
172
}
×
173

174
void TPM_Context::stir_random(const uint8_t in[], size_t in_len) {
×
175
   TSPI_CHECK_SUCCESS(::Tspi_TPM_StirRandom(m_tpm, to_uint32(in_len), const_cast<BYTE*>(in)));
×
176
}
×
177

178
TPM_PrivateKey::TPM_PrivateKey(TPM_Context& ctx, size_t bits, const char* key_password) : m_ctx(ctx) {
×
179
   // TODO: can also do OAEP decryption via binding keys
180
   // TODO: offer signing, binding (decrypt), or legacy (sign + decrypt) keys?
181

182
   TSS_FLAG key_flags = bit_flag(bits) | TSS_KEY_VOLATILE | TSS_KEY_TYPE_SIGNING;
×
183

184
   TSS_HKEY key;
×
185
   TSPI_CHECK_SUCCESS(::Tspi_Context_CreateObject(m_ctx.handle(), TSS_OBJECT_TYPE_RSAKEY, key_flags, &key));
×
186

187
   TSPI_CHECK_SUCCESS(
×
188
      ::Tspi_SetAttribUint32(key, TSS_TSPATTRIB_KEY_INFO, TSS_TSPATTRIB_KEYINFO_SIGSCHEME, TSS_SS_RSASSAPKCS1V15_DER));
189

190
   TSS_HPOLICY policy;
×
191
   TSPI_CHECK_SUCCESS(::Tspi_Context_CreateObject(m_ctx.handle(), TSS_OBJECT_TYPE_POLICY, TSS_POLICY_USAGE, &policy));
×
192
   set_policy_secret(policy, key_password);
×
193
   TSPI_CHECK_SUCCESS(::Tspi_Policy_AssignToObject(policy, key));
×
194

195
   TSPI_CHECK_SUCCESS(::Tspi_Key_CreateKey(key, ctx.srk(), 0));
×
196
   m_key = key;
×
197
}
×
198

199
// reference a registered TPM key
200
TPM_PrivateKey::TPM_PrivateKey(TPM_Context& ctx, std::string_view uuid_str, TPM_Storage_Type storage_type) :
×
201
      m_ctx(ctx), m_uuid(uuid_str), m_storage(storage_type) {
×
202
   const TSS_FLAG key_ps_type = (m_storage == TPM_Storage_Type::User) ? TSS_PS_TYPE_USER : TSS_PS_TYPE_SYSTEM;
×
203

204
   TSPI_CHECK_SUCCESS(::Tspi_Context_LoadKeyByUUID(m_ctx.handle(), key_ps_type, to_tss_uuid(m_uuid), &m_key));
×
205
}
×
206

207
TPM_PrivateKey::TPM_PrivateKey(TPM_Context& ctx, const std::vector<uint8_t>& blob) : m_ctx(ctx) {
×
208
   TSPI_CHECK_SUCCESS(::Tspi_Context_LoadKeyByBlob(
×
209
      m_ctx.handle(), m_ctx.srk(), to_uint32(blob.size()), const_cast<uint8_t*>(blob.data()), &m_key));
210

211
   //TSPI_CHECK_SUCCESS(::Tspi_Key_LoadKey(m_key, m_ctx.srk()));
212
}
×
213

214
std::string TPM_PrivateKey::register_key(TPM_Storage_Type storage_type) {
×
215
   if(!m_uuid.is_valid()) {
×
216
      TPM_RNG rng(ctx());  // use system_rng or arg RNG& instead?
×
217
      m_uuid = UUID(rng);
×
218
      m_storage = storage_type;
×
219

220
      const TSS_UUID key_uuid = to_tss_uuid(m_uuid);
×
221
      const TSS_FLAG key_ps_type = (storage_type == TPM_Storage_Type::User) ? TSS_PS_TYPE_USER : TSS_PS_TYPE_SYSTEM;
×
222

223
      const TSS_UUID srk_uuid = TSS_UUID_SRK;
×
224

225
      TSPI_CHECK_SUCCESS(
×
226
         ::Tspi_Context_RegisterKey(m_ctx.handle(), m_key, key_ps_type, key_uuid, TSS_PS_TYPE_SYSTEM, srk_uuid));
227
   }
×
228

229
   // Presumably we could re-register in the other store and same UUID
230
   // Doesn't seem like what is desired most of the time here
231
   if(storage_type != m_storage) {
×
232
      throw TPM_Error("TPM key " + m_uuid.to_string() + " already registered with different storage type");
×
233
   }
234

235
   return format_url(m_uuid, m_storage);
×
236
}
237

238
std::vector<std::string> TPM_PrivateKey::registered_keys(TPM_Context& ctx) {
×
239
   TSS_KM_KEYINFO2* key_info;
×
240
   UINT32 key_info_size;
×
241

242
   // TODO: does the PS type matter here at all?
243
   TSPI_CHECK_SUCCESS(
×
244
      ::Tspi_Context_GetRegisteredKeysByUUID2(ctx.handle(), TSS_PS_TYPE_SYSTEM, nullptr, &key_info_size, &key_info));
245

246
   std::vector<std::string> r(key_info_size);
×
247

248
   for(size_t i = 0; i != key_info_size; ++i) {
×
249
      r[i] = format_url(key_info[i].keyUUID, key_info[i].persistentStorageType);
×
250
   }
251

252
   // TODO: are we supposed to free this memory and if so how?
253
   //TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(ctx.handle(), key_info));
254

255
   return r;
×
256
}
×
257

258
BigInt TPM_PrivateKey::get_n() const {
×
259
   if(m_n == 0) {
×
260
      m_n = BigInt::from_bytes(
×
261
         get_obj_attr(m_ctx.handle(), m_key, TSS_TSPATTRIB_RSAKEY_INFO, TSS_TSPATTRIB_KEYINFO_RSA_MODULUS));
×
262
   }
263

264
   return m_n;
×
265
}
266

267
BigInt TPM_PrivateKey::get_e() const {
×
268
   if(m_e == 0) {
×
269
      m_e = BigInt::from_bytes(
×
270
         get_obj_attr(m_ctx.handle(), m_key, TSS_TSPATTRIB_RSAKEY_INFO, TSS_TSPATTRIB_KEYINFO_RSA_EXPONENT));
×
271
   }
272

273
   return m_e;
×
274
}
275

276
size_t TPM_PrivateKey::estimated_strength() const {
×
277
   return if_work_factor(key_length());
×
278
}
279

280
size_t TPM_PrivateKey::key_length() const {
×
281
   return get_n().bits();
×
282
}
283

284
AlgorithmIdentifier TPM_PrivateKey::algorithm_identifier() const {
×
285
   return AlgorithmIdentifier(object_identifier(), AlgorithmIdentifier::USE_NULL_PARAM);
×
286
}
287

288
std::vector<uint8_t> TPM_PrivateKey::public_key_bits() const {
×
289
   std::vector<uint8_t> bits;
×
290
   DER_Encoder(bits).start_sequence().encode(get_n()).encode(get_e()).end_cons();
×
291
   return bits;
×
292
}
×
293

294
std::vector<uint8_t> TPM_PrivateKey::raw_public_key_bits() const {
×
295
   throw TPM_Error("Raw public key export not supported for RSA TPM keys");
×
296
}
297

298
secure_vector<uint8_t> TPM_PrivateKey::private_key_bits() const {
×
299
   throw TPM_Error("Private key export not supported for TPM keys");
×
300
}
301

302
std::vector<uint8_t> TPM_PrivateKey::export_blob() const {
×
303
   return get_obj_attr(m_ctx.handle(), m_key, TSS_TSPATTRIB_KEY_BLOB, TSS_TSPATTRIB_KEYBLOB_BLOB);
×
304
}
305

306
std::unique_ptr<Public_Key> TPM_PrivateKey::public_key() const {
×
307
   return std::make_unique<RSA_PublicKey>(get_n(), get_e());
×
308
}
309

310
bool TPM_PrivateKey::check_key(RandomNumberGenerator&, bool) const {
×
311
   return true;  // TODO do a kat or pairwise check
×
312
}
313

314
namespace {
315

316
class TPM_Signing_Operation final : public PK_Ops::Signature {
×
317
   public:
318
      TPM_Signing_Operation(const TPM_PrivateKey& key, std::string_view hash_name) :
×
319
            m_key(key), m_hash(HashFunction::create_or_throw(hash_name)), m_hash_id(pkcs_hash_id(hash_name)) {}
×
320

321
      std::string hash_function() const override { return m_hash->name(); }
×
322

323
      size_t signature_length() const override { return m_key.get_n().bytes(); }
×
324

325
      void update(std::span<const uint8_t> msg) override { m_hash->update(msg); }
×
326

327
      AlgorithmIdentifier algorithm_identifier() const override {
×
328
         const std::string full_name = "RSA/EMSA3(" + m_hash->name() + ")";
×
329
         const OID oid = OID::from_string(full_name);
×
330
         return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_EMPTY_PARAM);
×
331
      }
×
332

333
      std::vector<uint8_t> sign(RandomNumberGenerator&) override {
×
334
         /*
335
         * v1.2 TPMs will only sign with PKCS #1 v1.5 padding. SHA-1 is built
336
         * in, all other hash inputs (TSS_HASH_OTHER) are treated as the
337
         * concatenation of the hash OID and hash value and signed with just the
338
         * 01FFFF... prefix. Even when using SHA-1 we compute the hash locally
339
         * since it is going to be much faster than pushing data over the LPC bus.
340
         */
341
         secure_vector<uint8_t> msg_hash = m_hash->final();
×
342

343
         std::vector<uint8_t> id_and_msg;
×
344
         id_and_msg.reserve(m_hash_id.size() + msg_hash.size());
×
345
         id_and_msg.insert(id_and_msg.end(), m_hash_id.begin(), m_hash_id.end());
×
346
         id_and_msg.insert(id_and_msg.end(), msg_hash.begin(), msg_hash.end());
×
347

348
         TSS_HCONTEXT ctx = m_key.ctx().handle();
×
349
         TSS_HHASH tpm_hash;
×
350
         TSPI_CHECK_SUCCESS(::Tspi_Context_CreateObject(ctx, TSS_OBJECT_TYPE_HASH, TSS_HASH_OTHER, &tpm_hash));
×
351
         TSPI_CHECK_SUCCESS(::Tspi_Hash_SetHashValue(tpm_hash, to_uint32(id_and_msg.size()), id_and_msg.data()));
×
352

353
         BYTE* sig_bytes = nullptr;
×
354
         UINT32 sig_len = 0;
×
355
         TSPI_CHECK_SUCCESS(::Tspi_Hash_Sign(tpm_hash, m_key.handle(), &sig_len, &sig_bytes));
×
356
         std::vector<uint8_t> sig(sig_bytes, sig_bytes + sig_len);
×
357

358
         // TODO: RAII for Context_FreeMemory
359
         TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(ctx, sig_bytes));
×
360

361
         // TODO: RAII for Context_CloseObject
362
         TSPI_CHECK_SUCCESS(::Tspi_Context_CloseObject(ctx, tpm_hash));
×
363

364
         return sig;
×
365
      }
×
366

367
   private:
368
      const TPM_PrivateKey m_key;
369
      std::unique_ptr<HashFunction> m_hash;
370
      std::vector<uint8_t> m_hash_id;
371
};
372

373
}  // namespace
374

375
std::unique_ptr<PK_Ops::Signature> TPM_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
×
376
                                                                       std::string_view params,
377
                                                                       std::string_view /*provider*/) const {
378
   return std::make_unique<TPM_Signing_Operation>(*this, params);
×
379
}
380

381
}  // namespace Botan
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc