Botan 3.4.0
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
10#include <botan/der_enc.h>
11#include <botan/hash.h>
12#include <botan/pk_ops.h>
13#include <botan/rsa.h>
14#include <botan/internal/fmt.h>
15#include <botan/internal/hash_id.h>
16#include <botan/internal/workfactor.h>
17#include <limits>
18
19#include <trousers/trousers.h>
20#include <tss/platform.h>
21#include <tss/tspi.h>
22
23// TODO: dynamically load the TPM libraries?
24
25namespace Botan {
26
27namespace {
28
29void tss_error(TSS_RESULT res, const char* expr, const char* file, int line) {
30 std::string err = fmt("TPM error {} in layer {} executing {} at {}:{}",
31 Trspi_Error_String(res),
32 Trspi_Error_Layer(res),
33 expr,
34 line,
35 file);
36
37 throw TPM_Error(err);
38}
39
40TSS_FLAG bit_flag(size_t bits) {
41 switch(bits) {
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 BOTAN_ARG_CHECK(v > std::numeric_limits<uint32_t>::max(), "Value too large for 32bit unsigned integer");
63 return static_cast<uint32_t>(v);
64}
65
66#define TSPI_CHECK_SUCCESS(expr) \
67 do { \
68 TSS_RESULT res = expr; \
69 if(res != TSS_SUCCESS) \
70 tss_error(res, #expr, __FILE__, __LINE__); \
71 } while(0)
72
73std::vector<uint8_t> get_obj_attr(TSS_HCONTEXT ctx, TSS_HOBJECT obj, TSS_FLAG flag, TSS_FLAG sub_flag) {
74 BYTE* data = nullptr;
75 UINT32 data_len = 0;
76 TSPI_CHECK_SUCCESS(::Tspi_GetAttribData(obj, flag, sub_flag, &data_len, &data));
77
78 std::vector<uint8_t> r(data, data + data_len);
79
80 TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(ctx, data));
81
82 return r;
83}
84
85void set_policy_secret(TSS_HPOLICY policy, const char* secret) {
86 if(secret) {
87 BYTE* as_b = const_cast<BYTE*>(reinterpret_cast<const BYTE*>(secret));
88 TSPI_CHECK_SUCCESS(::Tspi_Policy_SetSecret(policy, TSS_SECRET_MODE_PLAIN, to_uint32(std::strlen(secret)), as_b));
89 } else {
90 static const uint8_t nullpass[20] = {0};
91
93 ::Tspi_Policy_SetSecret(policy, TSS_SECRET_MODE_SHA1, sizeof(nullpass), const_cast<BYTE*>(nullpass)));
94 }
95}
96
97TSS_UUID to_tss_uuid(const UUID& uuid) {
98 static_assert(sizeof(TSS_UUID) == 16, "Expected size of packed UUID");
99
100 TSS_UUID tss_uuid;
101 typecast_copy(tss_uuid, uuid.binary_value().data());
102 return tss_uuid;
103}
104
105UUID from_tss_uuid(const TSS_UUID& tss_uuid) {
106 static_assert(sizeof(TSS_UUID) == 16, "Expected size of packed UUID");
107
108 std::vector<uint8_t> mem(16);
109 typecast_copy(mem.data(), tss_uuid);
110 UUID uuid(std::move(mem));
111 return uuid;
112}
113
114TPM_Storage_Type storage_type_from_tss_flag(TSS_FLAG flag) {
115 if(flag == TSS_PS_TYPE_USER)
117 else if(flag == TSS_PS_TYPE_SYSTEM)
119 else
120 throw TPM_Error("Invalid storage flag " + std::to_string(flag));
121}
122
123std::string format_url(const UUID& uuid, TPM_Storage_Type storage) {
124 std::string storage_str = (storage == TPM_Storage_Type::User) ? "user" : "system";
125 return "tpmkey:uuid=" + uuid.to_string() + ";storage=" + storage_str;
126}
127
128std::string format_url(const TSS_UUID& tss_uuid, TSS_FLAG store_type) {
129 UUID uuid = from_tss_uuid(tss_uuid);
130
131 return format_url(from_tss_uuid(tss_uuid), storage_type_from_tss_flag(store_type));
132}
133
134} // namespace
135
136TPM_Context::TPM_Context(pin_cb cb, const char* srk_password) : m_pin_cb(cb), m_srk_policy(0) {
137 TSPI_CHECK_SUCCESS(::Tspi_Context_Create(&m_ctx));
138 TSPI_CHECK_SUCCESS(::Tspi_Context_Connect(m_ctx, nullptr));
139
140 TSPI_CHECK_SUCCESS(::Tspi_Context_GetTpmObject(m_ctx, &m_tpm));
141
142 const TSS_UUID SRK_UUID = TSS_UUID_SRK;
143
144 TSPI_CHECK_SUCCESS(::Tspi_Context_LoadKeyByUUID(m_ctx, TSS_PS_TYPE_SYSTEM, SRK_UUID, &m_srk));
145
146 TSPI_CHECK_SUCCESS(::Tspi_GetPolicyObject(m_srk, TSS_POLICY_USAGE, &m_srk_policy));
147 set_policy_secret(m_srk_policy, srk_password);
148
149 // TODO: do we have to cache it?
150 // TODO: try to use SRK with null, if it fails call the pin cb?
151}
152
154 TSPI_CHECK_SUCCESS(::Tspi_Context_CloseObject(m_ctx, m_srk));
155 //TSPI_CHECK_SUCCESS(::Tspi_Context_CloseObject(m_ctx, m_tpm));
156 TSPI_CHECK_SUCCESS(::Tspi_Context_Close(m_srk_policy));
157 TSPI_CHECK_SUCCESS(::Tspi_Context_Close(m_ctx));
158}
159
161 uint32_t r = 0;
162 TSPI_CHECK_SUCCESS(::Tspi_TPM_ReadCounter(m_tpm, &r));
163 return r;
164}
165
166void TPM_Context::gen_random(uint8_t out[], size_t out_len) {
167 BYTE* mem;
168 TSPI_CHECK_SUCCESS(::Tspi_TPM_GetRandom(m_tpm, to_uint32(out_len), &mem));
169 copy_mem(out, reinterpret_cast<const uint8_t*>(mem), out_len);
170 TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(m_ctx, mem));
171}
172
173void TPM_Context::stir_random(const uint8_t in[], size_t in_len) {
174 TSPI_CHECK_SUCCESS(::Tspi_TPM_StirRandom(m_tpm, to_uint32(in_len), const_cast<BYTE*>(in)));
175}
176
177TPM_PrivateKey::TPM_PrivateKey(TPM_Context& ctx, size_t bits, const char* key_password) : m_ctx(ctx) {
178 // TODO: can also do OAEP decryption via binding keys
179 // TODO: offer signing, binding (decrypt), or legacy (sign + decrypt) keys?
180
181 TSS_FLAG key_flags = bit_flag(bits) | TSS_KEY_VOLATILE | TSS_KEY_TYPE_SIGNING;
182
183 TSS_HKEY key;
184 TSPI_CHECK_SUCCESS(::Tspi_Context_CreateObject(m_ctx.handle(), TSS_OBJECT_TYPE_RSAKEY, key_flags, &key));
185
187 ::Tspi_SetAttribUint32(key, TSS_TSPATTRIB_KEY_INFO, TSS_TSPATTRIB_KEYINFO_SIGSCHEME, TSS_SS_RSASSAPKCS1V15_DER));
188
189 TSS_HPOLICY policy;
190 TSPI_CHECK_SUCCESS(::Tspi_Context_CreateObject(m_ctx.handle(), TSS_OBJECT_TYPE_POLICY, TSS_POLICY_USAGE, &policy));
191 set_policy_secret(policy, key_password);
192 TSPI_CHECK_SUCCESS(::Tspi_Policy_AssignToObject(policy, key));
193
194 TSPI_CHECK_SUCCESS(::Tspi_Key_CreateKey(key, ctx.srk(), 0));
195 m_key = key;
196}
197
198// reference a registered TPM key
199TPM_PrivateKey::TPM_PrivateKey(TPM_Context& ctx, std::string_view uuid_str, TPM_Storage_Type storage_type) :
200 m_ctx(ctx), m_uuid(uuid_str), m_storage(storage_type) {
201 const TSS_FLAG key_ps_type = (m_storage == TPM_Storage_Type::User) ? TSS_PS_TYPE_USER : TSS_PS_TYPE_SYSTEM;
202
203 TSPI_CHECK_SUCCESS(::Tspi_Context_LoadKeyByUUID(m_ctx.handle(), key_ps_type, to_tss_uuid(m_uuid), &m_key));
204}
205
206TPM_PrivateKey::TPM_PrivateKey(TPM_Context& ctx, const std::vector<uint8_t>& blob) : m_ctx(ctx) {
207 TSPI_CHECK_SUCCESS(::Tspi_Context_LoadKeyByBlob(
208 m_ctx.handle(), m_ctx.srk(), to_uint32(blob.size()), const_cast<uint8_t*>(blob.data()), &m_key));
209
210 //TSPI_CHECK_SUCCESS(::Tspi_Key_LoadKey(m_key, m_ctx.srk()));
211}
212
214 if(!m_uuid.is_valid()) {
215 TPM_RNG rng(ctx()); // use system_rng or arg RNG& instead?
216 m_uuid = UUID(rng);
217 m_storage = storage_type;
218
219 const TSS_UUID key_uuid = to_tss_uuid(m_uuid);
220 const TSS_FLAG key_ps_type = (storage_type == TPM_Storage_Type::User) ? TSS_PS_TYPE_USER : TSS_PS_TYPE_SYSTEM;
221
222 const TSS_UUID srk_uuid = TSS_UUID_SRK;
223
225 ::Tspi_Context_RegisterKey(m_ctx.handle(), m_key, key_ps_type, key_uuid, TSS_PS_TYPE_SYSTEM, srk_uuid));
226 }
227
228 // Presumably we could re-register in the other store and same UUID
229 // Doesn't seem like what is desired most of the time here
230 if(storage_type != m_storage) {
231 throw TPM_Error("TPM key " + m_uuid.to_string() + " already registered with different storage type");
232 }
233
234 return format_url(m_uuid, m_storage);
235}
236
237std::vector<std::string> TPM_PrivateKey::registered_keys(TPM_Context& ctx) {
238 TSS_KM_KEYINFO2* key_info;
239 UINT32 key_info_size;
240
241 // TODO: does the PS type matter here at all?
243 ::Tspi_Context_GetRegisteredKeysByUUID2(ctx.handle(), TSS_PS_TYPE_SYSTEM, nullptr, &key_info_size, &key_info));
244
245 std::vector<std::string> r(key_info_size);
246
247 for(size_t i = 0; i != key_info_size; ++i) {
248 r[i] = format_url(key_info[i].keyUUID, key_info[i].persistentStorageType);
249 }
250
251 // TODO: are we supposed to free this memory and if so how?
252 //TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(ctx.handle(), key_info));
253
254 return r;
255}
256
258 if(m_n == 0) {
259 m_n = BigInt::decode(
260 get_obj_attr(m_ctx.handle(), m_key, TSS_TSPATTRIB_RSAKEY_INFO, TSS_TSPATTRIB_KEYINFO_RSA_MODULUS));
261 }
262
263 return m_n;
264}
265
267 if(m_e == 0) {
268 m_e = BigInt::decode(
269 get_obj_attr(m_ctx.handle(), m_key, TSS_TSPATTRIB_RSAKEY_INFO, TSS_TSPATTRIB_KEYINFO_RSA_EXPONENT));
270 }
271
272 return m_e;
273}
274
278
280 return get_n().bits();
281}
282
286
287std::vector<uint8_t> TPM_PrivateKey::public_key_bits() const {
288 std::vector<uint8_t> bits;
290 return bits;
291}
292
294 throw TPM_Error("Private key export not supported for TPM keys");
295}
296
297std::vector<uint8_t> TPM_PrivateKey::export_blob() const {
298 return get_obj_attr(m_ctx.handle(), m_key, TSS_TSPATTRIB_KEY_BLOB, TSS_TSPATTRIB_KEYBLOB_BLOB);
299}
300
301std::unique_ptr<Public_Key> TPM_PrivateKey::public_key() const {
302 return std::make_unique<RSA_PublicKey>(get_n(), get_e());
303}
304
306 return true; // TODO do a kat or pairwise check
307}
308
309namespace {
310
311class TPM_Signing_Operation final : public PK_Ops::Signature {
312 public:
313 TPM_Signing_Operation(const TPM_PrivateKey& key, std::string_view hash_name) :
314 m_key(key), m_hash(HashFunction::create_or_throw(hash_name)), m_hash_id(pkcs_hash_id(hash_name)) {}
315
316 std::string hash_function() const override { return m_hash->name(); }
317
318 size_t signature_length() const override { return m_key.get_n().bytes(); }
319
320 void update(const uint8_t msg[], size_t msg_len) override { m_hash->update(msg, msg_len); }
321
322 AlgorithmIdentifier algorithm_identifier() const override {
323 const std::string full_name = "RSA/EMSA3(" + m_hash->name() + ")";
324 const OID oid = OID::from_string(full_name);
325 return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_EMPTY_PARAM);
326 }
327
328 secure_vector<uint8_t> sign(RandomNumberGenerator&) override {
329 /*
330 * v1.2 TPMs will only sign with PKCS #1 v1.5 padding. SHA-1 is built
331 * in, all other hash inputs (TSS_HASH_OTHER) are treated as the
332 * concatenation of the hash OID and hash value and signed with just the
333 * 01FFFF... prefix. Even when using SHA-1 we compute the hash locally
334 * since it is going to be much faster than pushing data over the LPC bus.
335 */
336 secure_vector<uint8_t> msg_hash = m_hash->final();
337
338 std::vector<uint8_t> id_and_msg;
339 id_and_msg.reserve(m_hash_id.size() + msg_hash.size());
340 id_and_msg.insert(id_and_msg.end(), m_hash_id.begin(), m_hash_id.end());
341 id_and_msg.insert(id_and_msg.end(), msg_hash.begin(), msg_hash.end());
342
343 TSS_HCONTEXT ctx = m_key.ctx().handle();
344 TSS_HHASH tpm_hash;
345 TSPI_CHECK_SUCCESS(::Tspi_Context_CreateObject(ctx, TSS_OBJECT_TYPE_HASH, TSS_HASH_OTHER, &tpm_hash));
346 TSPI_CHECK_SUCCESS(::Tspi_Hash_SetHashValue(tpm_hash, to_uint32(id_and_msg.size()), id_and_msg.data()));
347
348 BYTE* sig_bytes = nullptr;
349 UINT32 sig_len = 0;
350 TSPI_CHECK_SUCCESS(::Tspi_Hash_Sign(tpm_hash, m_key.handle(), &sig_len, &sig_bytes));
351 secure_vector<uint8_t> sig(sig_bytes, sig_bytes + sig_len);
352
353 // TODO: RAII for Context_FreeMemory
354 TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(ctx, sig_bytes));
355
356 // TODO: RAII for Context_CloseObject
357 TSPI_CHECK_SUCCESS(::Tspi_Context_CloseObject(ctx, tpm_hash));
358
359 return sig;
360 }
361
362 private:
363 const TPM_PrivateKey m_key;
364 std::unique_ptr<HashFunction> m_hash;
365 std::vector<uint8_t> m_hash_id;
366};
367
368} // namespace
369
370std::unique_ptr<PK_Ops::Signature> TPM_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
371 std::string_view params,
372 std::string_view /*provider*/) const {
373 return std::make_unique<TPM_Signing_Operation>(*this, params);
374}
375
376} // namespace Botan
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:29
virtual OID object_identifier() const
Definition pk_keys.cpp:22
static BigInt decode(const uint8_t buf[], size_t length)
Definition bigint.h:773
size_t bits() const
Definition bigint.cpp:290
DER_Encoder & start_sequence()
Definition der_enc.h:65
DER_Encoder & end_cons()
Definition der_enc.cpp:171
DER_Encoder & encode(bool b)
Definition der_enc.cpp:250
static OID from_string(std::string_view str)
Definition asn1_oid.cpp:74
TSS_HCONTEXT handle() const
Definition tpm.h:63
TPM_Context(pin_cb cb, const char *srk_password)
Definition tpm.cpp:136
TSS_HKEY srk() const
Definition tpm.h:65
void gen_random(uint8_t out[], size_t out_len)
Definition tpm.cpp:166
uint32_t current_counter()
Definition tpm.cpp:160
std::function< std::string(std::string)> pin_cb
Definition tpm.h:47
void stir_random(const uint8_t in[], size_t in_len)
Definition tpm.cpp:173
std::vector< uint8_t > export_blob() const
Definition tpm.cpp:297
std::string register_key(TPM_Storage_Type storage_type)
Definition tpm.cpp:213
TPM_PrivateKey(TPM_Context &ctx, size_t bits, const char *key_password)
Definition tpm.cpp:177
secure_vector< uint8_t > private_key_bits() const override
Definition tpm.cpp:293
static std::vector< std::string > registered_keys(TPM_Context &ctx)
Definition tpm.cpp:237
bool check_key(RandomNumberGenerator &rng, bool) const override
Definition tpm.cpp:305
TPM_Context & ctx() const
Definition tpm.h:138
BigInt get_e() const
Definition tpm.cpp:266
std::unique_ptr< PK_Ops::Signature > create_signature_op(RandomNumberGenerator &rng, std::string_view params, std::string_view provider) const override
Definition tpm.cpp:370
size_t key_length() const override
Definition tpm.cpp:279
std::vector< uint8_t > public_key_bits() const override
Definition tpm.cpp:287
size_t estimated_strength() const override
Definition tpm.cpp:275
std::unique_ptr< Public_Key > public_key() const override
Definition tpm.cpp:301
BigInt get_n() const
Definition tpm.cpp:257
AlgorithmIdentifier algorithm_identifier() const override
Definition tpm.cpp:283
bool is_valid() const
Definition uuid.h:57
std::string to_string() const
Definition uuid.cpp:58
int(* update)(CTX *, const void *, CC_LONG len)
int(* final)(unsigned char *, CTX *)
FE_25519 T
Definition ge.cpp:34
TPM_Storage_Type
Definition tpm.h:100
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
constexpr void typecast_copy(ToR &&out, FromR &&in)
Definition mem_ops.h:178
std::vector< uint8_t > pkcs_hash_id(std::string_view name)
Definition hash_id.cpp:78
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:61
size_t if_work_factor(size_t bits)
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:146
#define TSPI_CHECK_SUCCESS(expr)
Definition tpm.cpp:66