Botan 3.0.0-alpha0
Crypto and TLS for C&
tpm.cpp
Go to the documentation of this file.
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#include <botan/rsa.h>
10#include <botan/hash.h>
11#include <botan/internal/hash_id.h>
12#include <botan/der_enc.h>
13#include <botan/internal/workfactor.h>
14#include <botan/internal/pk_ops.h>
15#include <sstream>
16#include <limits>
17
18#include <tss/platform.h>
19#include <tss/tspi.h>
20#include <trousers/trousers.h>
21
22// TODO: dynamically load the TPM libraries?
23
24namespace Botan {
25
26namespace {
27
28void tss_error(TSS_RESULT res, const char* expr, const char* file, int line)
29 {
30 std::ostringstream err;
31 err << "TPM error " << Trspi_Error_String(res)
32 << " layer " << Trspi_Error_Layer(res)
33 << " in " << expr << " at " << file << ":" << line;
34
35 throw TPM_Error(err.str());
36 }
37
38TSS_FLAG bit_flag(size_t bits)
39 {
40 switch(bits)
41 {
42 // 512 supported, but ignored and rejected here
43 case 1024:
44 return TSS_KEY_SIZE_1024;
45 case 2048:
46 return TSS_KEY_SIZE_2048;
47
48 // Most? v1.2 TPMs only support 1024 and 2048 bit keys ...
49 case 4096:
50 return TSS_KEY_SIZE_4096;
51 case 8192:
52 return TSS_KEY_SIZE_8192;
53 case 16384:
54 return TSS_KEY_SIZE_16384;
55 default:
56 throw Invalid_Argument("Unsupported TPM key size " + std::to_string(bits));
57 }
58 }
59
60template <typename T>
61uint32_t to_uint32(T v)
62 {
63 BOTAN_ARG_CHECK(v > std::numeric_limits<uint32_t>::max(),
64 "Value too large for 32bit unsigned integer");
65 return static_cast<uint32_t>(v);
66 }
67
68#if 0
69bool is_srk_uuid(const UUID& uuid)
70 {
71 static const uint8_t srk[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
72 const std::vector<uint8_t>& b = uuid.binary_value();
73 return (b.size() == 16 && same_mem(b.data(), srk, 16));
74 }
75#endif
76
77#define TSPI_CHECK_SUCCESS(expr) do { \
78 TSS_RESULT res = expr; \
79 if(res != TSS_SUCCESS) \
80 tss_error(res, #expr, __FILE__, __LINE__); \
81 } while(0)
82
83std::vector<uint8_t> get_obj_attr(TSS_HCONTEXT ctx,
84 TSS_HOBJECT obj,
85 TSS_FLAG flag,
86 TSS_FLAG sub_flag)
87 {
88 BYTE *data = nullptr;
89 UINT32 data_len = 0;
90 TSPI_CHECK_SUCCESS(::Tspi_GetAttribData(obj, flag, sub_flag, &data_len, &data));
91
92 std::vector<uint8_t> r(data, data + data_len);
93
94 TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(ctx, data));
95
96 return r;
97 }
98
99void set_policy_secret(TSS_HPOLICY policy, const char* secret)
100 {
101 if(secret)
102 {
103 BYTE* as_b = const_cast<BYTE*>(reinterpret_cast<const BYTE*>(secret));
104 TSPI_CHECK_SUCCESS(::Tspi_Policy_SetSecret(policy,
105 TSS_SECRET_MODE_PLAIN,
106 to_uint32(std::strlen(secret)),
107 as_b));
108 }
109 else
110 {
111 static const uint8_t nullpass[20] = { 0 };
112
113 TSPI_CHECK_SUCCESS(::Tspi_Policy_SetSecret(policy,
114 TSS_SECRET_MODE_SHA1,
115 sizeof(nullpass),
116 const_cast<BYTE*>(nullpass)));
117 }
118 }
119
120TSS_UUID to_tss_uuid(const UUID& uuid)
121 {
122 static_assert(sizeof(TSS_UUID) == 16, "Expected size of packed UUID");
123
124 TSS_UUID tss_uuid;
125 typecast_copy(tss_uuid, uuid.binary_value().data());
126 return tss_uuid;
127 }
128
129UUID from_tss_uuid(const TSS_UUID& tss_uuid)
130 {
131 static_assert(sizeof(TSS_UUID) == 16, "Expected size of packed UUID");
132
133 std::vector<uint8_t> mem(16);
134 typecast_copy(mem.data(), tss_uuid);
135 UUID uuid(std::move(mem));
136 return uuid;
137 }
138
139TPM_Storage_Type storage_type_from_tss_flag(TSS_FLAG flag)
140 {
141 if(flag == TSS_PS_TYPE_USER)
143 else if(flag == TSS_PS_TYPE_SYSTEM)
145 else
146 throw TPM_Error("Invalid storage flag " + std::to_string(flag));
147 }
148
149std::string format_url(const UUID& uuid, TPM_Storage_Type storage)
150 {
151 std::string storage_str = (storage == TPM_Storage_Type::User) ? "user" : "system";
152 return "tpmkey:uuid=" + uuid.to_string() + ";storage=" + storage_str;
153 }
154
155std::string format_url(const TSS_UUID& tss_uuid, TSS_FLAG store_type)
156 {
157 UUID uuid = from_tss_uuid(tss_uuid);
158
159 return format_url(from_tss_uuid(tss_uuid),
160 storage_type_from_tss_flag(store_type));
161 }
162
163}
164
165TPM_Context::TPM_Context(pin_cb cb, const char* srk_password) :
166 m_pin_cb(cb),
167 m_srk_policy(0)
168 {
169 TSPI_CHECK_SUCCESS(::Tspi_Context_Create(&m_ctx));
170 TSPI_CHECK_SUCCESS(::Tspi_Context_Connect(m_ctx, nullptr));
171
172 TSPI_CHECK_SUCCESS(::Tspi_Context_GetTpmObject(m_ctx, &m_tpm));
173
174 const TSS_UUID SRK_UUID = TSS_UUID_SRK;
175
176 TSPI_CHECK_SUCCESS(::Tspi_Context_LoadKeyByUUID(m_ctx, TSS_PS_TYPE_SYSTEM, SRK_UUID, &m_srk));
177
178 TSPI_CHECK_SUCCESS(::Tspi_GetPolicyObject(m_srk, TSS_POLICY_USAGE, &m_srk_policy));
179 set_policy_secret(m_srk_policy, srk_password);
180
181 // TODO: do we have to cache it?
182 // TODO: try to use SRK with null, if it fails call the pin cb?
183 }
184
186 {
187 TSPI_CHECK_SUCCESS(::Tspi_Context_CloseObject(m_ctx, m_srk));
188 //TSPI_CHECK_SUCCESS(::Tspi_Context_CloseObject(m_ctx, m_tpm));
189 TSPI_CHECK_SUCCESS(::Tspi_Context_Close(m_srk_policy));
190 TSPI_CHECK_SUCCESS(::Tspi_Context_Close(m_ctx));
191 }
192
194 {
195 uint32_t r = 0;
196 TSPI_CHECK_SUCCESS(::Tspi_TPM_ReadCounter(m_tpm, &r));
197 return r;
198 }
199
200void TPM_Context::gen_random(uint8_t out[], size_t out_len)
201 {
202 BYTE* mem;
203 TSPI_CHECK_SUCCESS(::Tspi_TPM_GetRandom(m_tpm, to_uint32(out_len), &mem));
204 copy_mem(out, reinterpret_cast<const uint8_t*>(mem), out_len);
205 TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(m_ctx, mem));
206 }
207
208void TPM_Context::stir_random(const uint8_t in[], size_t in_len)
209 {
210 TSPI_CHECK_SUCCESS(::Tspi_TPM_StirRandom(m_tpm, to_uint32(in_len), const_cast<BYTE*>(in)));
211 }
212
214 const char* key_password) : m_ctx(ctx)
215 {
216 // TODO: can also do OAEP decryption via binding keys
217 // TODO: offer signing, binding (decrypt), or legacy (sign + decrypt) keys?
218
219 TSS_FLAG key_flags = bit_flag(bits) | TSS_KEY_VOLATILE | TSS_KEY_TYPE_SIGNING;
220
221 TSS_HKEY key;
222 TSPI_CHECK_SUCCESS(::Tspi_Context_CreateObject(m_ctx.handle(), TSS_OBJECT_TYPE_RSAKEY, key_flags, &key));
223
224 TSPI_CHECK_SUCCESS(::Tspi_SetAttribUint32(key, TSS_TSPATTRIB_KEY_INFO,
225 TSS_TSPATTRIB_KEYINFO_SIGSCHEME,
226 TSS_SS_RSASSAPKCS1V15_DER));
227
228 TSS_HPOLICY policy;
229 TSPI_CHECK_SUCCESS(::Tspi_Context_CreateObject(m_ctx.handle(), TSS_OBJECT_TYPE_POLICY, TSS_POLICY_USAGE, &policy));
230 set_policy_secret(policy, key_password);
231 TSPI_CHECK_SUCCESS(::Tspi_Policy_AssignToObject(policy, key));
232
233 TSPI_CHECK_SUCCESS(::Tspi_Key_CreateKey(key, ctx.srk(), 0));
234 m_key = key;
235 }
236
237// reference a registered TPM key
238TPM_PrivateKey::TPM_PrivateKey(TPM_Context& ctx, const std::string& uuid_str,
239 TPM_Storage_Type storage_type) :
240 m_ctx(ctx),
241 m_uuid(uuid_str),
242 m_storage(storage_type)
243 {
244 const TSS_FLAG key_ps_type =
245 (m_storage == TPM_Storage_Type::User) ? TSS_PS_TYPE_USER : TSS_PS_TYPE_SYSTEM;
246
247 TSPI_CHECK_SUCCESS(::Tspi_Context_LoadKeyByUUID(m_ctx.handle(),
248 key_ps_type,
249 to_tss_uuid(m_uuid),
250 &m_key));
251 }
252
254 const std::vector<uint8_t>& blob) : m_ctx(ctx)
255 {
256 TSPI_CHECK_SUCCESS(::Tspi_Context_LoadKeyByBlob(m_ctx.handle(), m_ctx.srk(), to_uint32(blob.size()),
257 const_cast<uint8_t*>(blob.data()),
258 &m_key));
259
260 //TSPI_CHECK_SUCCESS(::Tspi_Key_LoadKey(m_key, m_ctx.srk()));
261 }
262
264 {
265 if(!m_uuid.is_valid())
266 {
267 TPM_RNG rng(ctx()); // use system_rng or arg RNG& instead?
268 m_uuid = UUID(rng);
269 m_storage = storage_type;
270
271 const TSS_UUID key_uuid = to_tss_uuid(m_uuid);
272 const TSS_FLAG key_ps_type =
273 (storage_type == TPM_Storage_Type::User) ? TSS_PS_TYPE_USER : TSS_PS_TYPE_SYSTEM;
274
275 const TSS_UUID srk_uuid = TSS_UUID_SRK;
276
277 TSPI_CHECK_SUCCESS(::Tspi_Context_RegisterKey(m_ctx.handle(),
278 m_key,
279 key_ps_type,
280 key_uuid,
281 TSS_PS_TYPE_SYSTEM,
282 srk_uuid));
283
284 }
285
286 // Presumably we could re-register in the other store and same UUID
287 // Doesn't seem like what is desired most of the time here
288 if(storage_type != m_storage)
289 {
290 throw TPM_Error("TPM key " + m_uuid.to_string() +
291 " already registered with different storage type");
292 }
293
294 return format_url(m_uuid, m_storage);
295 }
296
297std::vector<std::string> TPM_PrivateKey::registered_keys(TPM_Context& ctx)
298 {
299 TSS_KM_KEYINFO2* key_info;
300 UINT32 key_info_size;
301
302 // TODO: does the PS type matter here at all?
303 TSPI_CHECK_SUCCESS(::Tspi_Context_GetRegisteredKeysByUUID2(ctx.handle(),
304 TSS_PS_TYPE_SYSTEM,
305 nullptr,
306 &key_info_size,
307 &key_info));
308
309 std::vector<std::string> r(key_info_size);
310
311 for(size_t i = 0; i != key_info_size; ++i)
312 {
313 r[i] = format_url(key_info[i].keyUUID, key_info[i].persistentStorageType);
314 }
315
316 // TODO: are we supposed to free this memory and if so how?
317 //TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(ctx.handle(), key_info));
318
319 return r;
320 }
321
323 {
324 if(m_n == 0)
325 {
326 m_n = BigInt::decode(get_obj_attr(m_ctx.handle(), m_key,
327 TSS_TSPATTRIB_RSAKEY_INFO,
328 TSS_TSPATTRIB_KEYINFO_RSA_MODULUS));
329 }
330
331 return m_n;
332 }
333
335 {
336 if(m_e == 0)
337 {
338 m_e = BigInt::decode(get_obj_attr(m_ctx.handle(), m_key,
339 TSS_TSPATTRIB_RSAKEY_INFO,
340 TSS_TSPATTRIB_KEYINFO_RSA_EXPONENT));
341 }
342
343 return m_e;
344 }
345
347 {
348 return if_work_factor(key_length());
349 }
350
352 {
353 return get_n().bits();
354 }
355
357 {
360 }
361
362std::vector<uint8_t> TPM_PrivateKey::public_key_bits() const
363 {
364 std::vector<uint8_t> bits;
365 DER_Encoder(bits)
367 .encode(get_n())
368 .encode(get_e())
369 .end_cons();
370 return bits;
371 }
372
374 {
375 throw TPM_Error("Private key export not supported for TPM keys");
376 }
377
378std::vector<uint8_t> TPM_PrivateKey::export_blob() const
379 {
380 return get_obj_attr(m_ctx.handle(), m_key,
381 TSS_TSPATTRIB_KEY_BLOB,
382 TSS_TSPATTRIB_KEYBLOB_BLOB);
383 }
384
385std::unique_ptr<Public_Key> TPM_PrivateKey::public_key() const
386 {
387 return std::make_unique<RSA_PublicKey>(get_n(), get_e());
388 }
389
391 {
392 return true; // TODO do a kat or pairwise check
393 }
394
395namespace {
396
397class TPM_Signing_Operation final : public PK_Ops::Signature
398 {
399 public:
400 TPM_Signing_Operation(const TPM_PrivateKey& key,
401 const std::string& hash_name) :
402 m_key(key),
403 m_hash(HashFunction::create(hash_name)),
404 m_hash_id(pkcs_hash_id(hash_name))
405 {
406 }
407
408 size_t signature_length() const override
409 {
410 return m_key.get_n().bytes();
411 }
412
413 void update(const uint8_t msg[], size_t msg_len) override
414 {
415 m_hash->update(msg, msg_len);
416 }
417
418 secure_vector<uint8_t> sign(RandomNumberGenerator&) override
419 {
420 /*
421 * v1.2 TPMs will only sign with PKCS #1 v1.5 padding. SHA-1 is built
422 * in, all other hash inputs (TSS_HASH_OTHER) are treated as the
423 * concatenation of the hash OID and hash value and signed with just the
424 * 01FFFF... prefix. Even when using SHA-1 we compute the hash locally
425 * since it is going to be much faster than pushing data over the LPC bus.
426 */
427 secure_vector<uint8_t> msg_hash = m_hash->final();
428
429 std::vector<uint8_t> id_and_msg;
430 id_and_msg.reserve(m_hash_id.size() + msg_hash.size());
431 id_and_msg.insert(id_and_msg.end(), m_hash_id.begin(), m_hash_id.end());
432 id_and_msg.insert(id_and_msg.end(), msg_hash.begin(), msg_hash.end());
433
434 TSS_HCONTEXT ctx = m_key.ctx().handle();
435 TSS_HHASH tpm_hash;
436 TSPI_CHECK_SUCCESS(::Tspi_Context_CreateObject(ctx, TSS_OBJECT_TYPE_HASH, TSS_HASH_OTHER, &tpm_hash));
437 TSPI_CHECK_SUCCESS(::Tspi_Hash_SetHashValue(tpm_hash, to_uint32(id_and_msg.size()), id_and_msg.data()));
438
439 BYTE* sig_bytes = nullptr;
440 UINT32 sig_len = 0;
441 TSPI_CHECK_SUCCESS(::Tspi_Hash_Sign(tpm_hash, m_key.handle(), &sig_len, &sig_bytes));
442 secure_vector<uint8_t> sig(sig_bytes, sig_bytes + sig_len);
443
444 // TODO: RAII for Context_FreeMemory
445 TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(ctx, sig_bytes));
446
447 // TODO: RAII for Context_CloseObject
448 TSPI_CHECK_SUCCESS(::Tspi_Context_CloseObject(ctx, tpm_hash));
449
450 return sig;
451 }
452
453 private:
454 const TPM_PrivateKey& m_key;
455 std::unique_ptr<HashFunction> m_hash;
456 std::vector<uint8_t> m_hash_id;
457 };
458
459}
460
461std::unique_ptr<PK_Ops::Signature>
463 const std::string& params,
464 const std::string& /*provider*/) const
465 {
466 return std::make_unique<TPM_Signing_Operation>(*this, params);
467 }
468
469}
#define BOTAN_ARG_CHECK(expr, msg)
Definition: assert.h:36
static BigInt decode(const uint8_t buf[], size_t length)
Definition: bigint.h:790
size_t bits() const
Definition: bigint.cpp:309
DER_Encoder & start_sequence()
Definition: der_enc.h:66
DER_Encoder & end_cons()
Definition: der_enc.cpp:194
DER_Encoder & encode(bool b)
Definition: der_enc.cpp:288
virtual OID get_oid() const
Definition: pk_keys.cpp:53
TSS_HCONTEXT handle() const
Definition: tpm.h:67
TPM_Context(pin_cb cb, const char *srk_password)
Definition: tpm.cpp:165
TSS_HKEY srk() const
Definition: tpm.h:68
void gen_random(uint8_t out[], size_t out_len)
Definition: tpm.cpp:200
uint32_t current_counter()
Definition: tpm.cpp:193
std::function< std::string(std::string)> pin_cb
Definition: tpm.h:48
void stir_random(const uint8_t in[], size_t in_len)
Definition: tpm.cpp:208
std::vector< uint8_t > export_blob() const
Definition: tpm.cpp:378
std::string register_key(TPM_Storage_Type storage_type)
Definition: tpm.cpp:263
TPM_PrivateKey(TPM_Context &ctx, size_t bits, const char *key_password)
Definition: tpm.cpp:213
secure_vector< uint8_t > private_key_bits() const override
Definition: tpm.cpp:373
std::unique_ptr< PK_Ops::Signature > create_signature_op(RandomNumberGenerator &rng, const std::string &params, const std::string &provider) const override
Definition: tpm.cpp:462
static std::vector< std::string > registered_keys(TPM_Context &ctx)
Definition: tpm.cpp:297
bool check_key(RandomNumberGenerator &rng, bool) const override
Definition: tpm.cpp:390
TPM_Context & ctx() const
Definition: tpm.h:145
BigInt get_e() const
Definition: tpm.cpp:334
size_t key_length() const override
Definition: tpm.cpp:351
std::vector< uint8_t > public_key_bits() const override
Definition: tpm.cpp:362
size_t estimated_strength() const override
Definition: tpm.cpp:346
std::unique_ptr< Public_Key > public_key() const override
Definition: tpm.cpp:385
BigInt get_n() const
Definition: tpm.cpp:322
AlgorithmIdentifier algorithm_identifier() const override
Definition: tpm.cpp:356
bool is_valid() const
Definition: uuid.h:61
std::string to_string() const
Definition: uuid.cpp:65
int(* update)(CTX *, const void *, CC_LONG len)
int(* final)(unsigned char *, CTX *)
fe T
Definition: ge.cpp:36
Polynomial v
Definition: kyber.cpp:822
PolynomialVector b
Definition: kyber.cpp:821
std::string to_string(const BER_Object &obj)
Definition: asn1_obj.cpp:209
Definition: alg_id.cpp:13
std::vector< uint8_t > pkcs_hash_id(const std::string &name)
Definition: hash_id.cpp:73
TPM_Storage_Type
Definition: tpm.h:103
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition: mem_ops.h:126
constexpr void typecast_copy(uint8_t out[], T in[], size_t N)
Definition: mem_ops.h:138
bool same_mem(const T *p1, const T *p2, size_t n)
Definition: mem_ops.h:210
size_t if_work_factor(size_t bits)
Definition: workfactor.cpp:38
std::vector< T, secure_allocator< T > > secure_vector
Definition: secmem.h:65
#define TSPI_CHECK_SUCCESS(expr)
Definition: tpm.cpp:77