Botan 2.1.0 Crypto and TLS for C++11

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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