aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2015-12-19 21:33:19 -0500
committerJack Lloyd <[email protected]>2015-12-19 21:33:19 -0500
commit1752f0d522eef9a4a703fccf702b4b026c1c1d01 (patch)
treebe21fdef523e4c894a199ec9ebad8004afb9e979
parent6da5bc56c45c7151f64f65cba93d4fa7ef0a574a (diff)
Add TPM v1.2 support (RSA keygen/signing, RNG)
-rwxr-xr-xconfigure.py2
-rw-r--r--doc/news.rst6
-rw-r--r--src/lib/prov/tpm/info.txt13
-rw-r--r--src/lib/prov/tpm/tpm.cpp458
-rw-r--r--src/lib/prov/tpm/tpm.h182
-rw-r--r--src/lib/prov/tpm/uuid.h101
6 files changed, 760 insertions, 2 deletions
diff --git a/configure.py b/configure.py
index 925f2d768..0eb5eaa56 100755
--- a/configure.py
+++ b/configure.py
@@ -364,7 +364,7 @@ def process_command_line(args):
help='minimize build')
# Should be derived from info.txt but this runs too early
- third_party = ['boost', 'bzip2', 'lzma', 'openssl', 'sqlite3', 'zlib']
+ third_party = ['boost', 'bzip2', 'lzma', 'openssl', 'sqlite3', 'zlib', 'tpm']
for mod in third_party:
mods_group.add_option('--with-%s' % (mod),
diff --git a/doc/news.rst b/doc/news.rst
index 1d6a8b87e..39753a752 100644
--- a/doc/news.rst
+++ b/doc/news.rst
@@ -15,7 +15,11 @@ Version 1.11.26, Not Yet Released
* Enable RdRand entropy source on Windows/MSVC. GH #364
-* Add Intel's RdSeed as entropy source
+* Add Intel's RdSeed as entropy source. GH #370
+
+* Add preliminary support for accessing TPM v1.2 devices. Currently
+ random number generation, RSA key generation, and signing are
+ supported. Tested using Trousers and an ST TPM
Version 1.11.25, 2015-12-07
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/lib/prov/tpm/info.txt b/src/lib/prov/tpm/info.txt
new file mode 100644
index 000000000..f0860f3ec
--- /dev/null
+++ b/src/lib/prov/tpm/info.txt
@@ -0,0 +1,13 @@
+define TPM 20151126
+
+load_on vendor
+
+<libs>
+all -> tspi
+</libs>
+
+<requires>
+hash_id
+rsa
+rng
+</requires>
diff --git a/src/lib/prov/tpm/tpm.cpp b/src/lib/prov/tpm/tpm.cpp
new file mode 100644
index 000000000..9a29be395
--- /dev/null
+++ b/src/lib/prov/tpm/tpm.cpp
@@ -0,0 +1,458 @@
+/*
+* TPM 1.2 interface
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tpm.h>
+#include <botan/rsa.h>
+#include <botan/hash.h>
+#include <botan/hash_id.h>
+#include <botan/der_enc.h>
+#include <botan/workfactor.h>
+#include <botan/internal/pk_utils.h>
+#include <sstream>
+
+#include <tss/platform.h>
+#include <tss/tspi.h>
+#include <trousers/trousers.h>
+
+// TODO: dynamically load the TPM libraries?
+
+namespace Botan {
+
+namespace {
+
+void tss_error(TSS_RESULT res, const char* expr, const char* file, int line)
+ {
+ std::ostringstream err;
+ err << "TPM error " << Trspi_Error_String(res)
+ << " layer " << Trspi_Error_Layer(res)
+ << " in " << expr << " at " << file << ":" << line;
+
+ throw TPM_Error(err.str());
+ }
+
+TSS_FLAG bit_flag(size_t bits)
+ {
+ switch(bits)
+ {
+ // 512 supported, but ignored and rejected here
+ case 1024:
+ return TSS_KEY_SIZE_1024;
+ case 2048:
+ return TSS_KEY_SIZE_2048;
+
+ // Most? v1.2 TPMs only support 1024 and 2048 bit keys ...
+ case 4096:
+ return TSS_KEY_SIZE_4096;
+ case 8192:
+ return TSS_KEY_SIZE_8192;
+ case 16384:
+ return TSS_KEY_SIZE_16384;
+ default:
+ throw Invalid_Argument("Unsupported TPM key size " + std::to_string(bits));
+ }
+ }
+
+bool is_srk_uuid(const UUID& uuid)
+ {
+ static const byte srk[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
+ const std::vector<uint8_t>& b = uuid.binary_value();
+ return (b.size() == 16 && same_mem(b.data(), srk, 16));
+ }
+
+
+#define TSPI_CHECK_SUCCESS(expr) do { \
+ TSS_RESULT res = expr; \
+ if(res != TSS_SUCCESS) \
+ tss_error(res, #expr, __FILE__, __LINE__); \
+ } while(0)
+
+std::vector<uint8_t> get_obj_attr(TSS_HCONTEXT ctx,
+ TSS_HOBJECT obj,
+ TSS_FLAG flag,
+ TSS_FLAG sub_flag)
+ {
+ BYTE *data = nullptr;
+ UINT32 data_len = 0;
+ TSPI_CHECK_SUCCESS(::Tspi_GetAttribData(obj, flag, sub_flag, &data_len, &data));
+
+ std::vector<uint8_t> r(data, data + data_len);
+
+ TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(ctx, data));
+
+ return r;
+ }
+
+void set_policy_secret(TSS_HPOLICY policy, const char* secret)
+ {
+ if(secret)
+ {
+ TSPI_CHECK_SUCCESS(::Tspi_Policy_SetSecret(policy,
+ TSS_SECRET_MODE_PLAIN,
+ std::strlen(secret),
+ (BYTE*)secret));
+ }
+ else
+ {
+ static const uint8_t nullpass[20] = { 0 };
+
+ TSPI_CHECK_SUCCESS(::Tspi_Policy_SetSecret(policy,
+ TSS_SECRET_MODE_SHA1,
+ sizeof(nullpass),
+ const_cast<BYTE*>(nullpass)));
+ }
+ }
+
+TSS_UUID to_tss_uuid(const UUID& uuid)
+ {
+ static_assert(sizeof(TSS_UUID) == 16, "Expected size of packed UUID");
+
+ TSS_UUID tss_uuid;
+ std::memcpy(&tss_uuid, uuid.binary_value().data(), 16);
+ return tss_uuid;
+ }
+
+UUID from_tss_uuid(const TSS_UUID& tss_uuid)
+ {
+ static_assert(sizeof(TSS_UUID) == 16, "Expected size of packed UUID");
+
+ std::vector<uint8_t> mem(16);
+ std::memcpy(mem.data(), &tss_uuid, 16);
+ UUID uuid(std::move(mem));
+ return uuid;
+ }
+
+TPM_Storage_Type storage_type_from_tss_flag(TSS_FLAG flag)
+ {
+ if(flag == TSS_PS_TYPE_USER)
+ return TPM_Storage_Type::User;
+ else if(flag == TSS_PS_TYPE_SYSTEM)
+ return TPM_Storage_Type::System;
+ else
+ throw TPM_Error("Invalid storage flag " + std::to_string(flag));
+ }
+
+std::string format_url(const UUID& uuid, TPM_Storage_Type storage)
+ {
+ std::string storage_str = (storage == TPM_Storage_Type::User) ? "user" : "system";
+ return "tpmkey:uuid=" + uuid.to_string() + ";storage=" + storage_str;
+ }
+
+std::string format_url(const TSS_UUID& tss_uuid, TSS_FLAG store_type)
+ {
+ UUID uuid = from_tss_uuid(tss_uuid);
+
+ return format_url(from_tss_uuid(tss_uuid),
+ storage_type_from_tss_flag(store_type));
+ }
+
+}
+
+TPM_Context::TPM_Context(pin_cb cb, const char* srk_password) : m_pin_cb(cb)
+ {
+ TSPI_CHECK_SUCCESS(::Tspi_Context_Create(&m_ctx));
+ TSPI_CHECK_SUCCESS(::Tspi_Context_Connect(m_ctx, nullptr));
+
+ TSPI_CHECK_SUCCESS(::Tspi_Context_GetTpmObject(m_ctx, &m_tpm));
+
+ const TSS_UUID SRK_UUID = TSS_UUID_SRK;
+
+ TSPI_CHECK_SUCCESS(::Tspi_Context_LoadKeyByUUID(m_ctx, TSS_PS_TYPE_SYSTEM, SRK_UUID, &m_srk));
+
+ TSS_HPOLICY srk_policy;
+ TSPI_CHECK_SUCCESS(::Tspi_GetPolicyObject(m_srk, TSS_POLICY_USAGE, &srk_policy));
+ set_policy_secret(srk_policy, srk_password);
+
+ // TODO: leaking policy object here?
+ // TODO: do we have to cache it?
+ // TODO: try to use SRK with null, if it fails call the pin cb?
+ }
+
+TPM_Context::~TPM_Context()
+ {
+ TSPI_CHECK_SUCCESS(::Tspi_Context_CloseObject(m_ctx, m_srk));
+ //TSPI_CHECK_SUCCESS(::Tspi_Context_CloseObject(m_ctx, m_tpm));
+ TSPI_CHECK_SUCCESS(::Tspi_Context_Close(m_ctx));
+ }
+
+uint32_t TPM_Context::current_counter()
+ {
+ uint32_t r = 0;
+ TSPI_CHECK_SUCCESS(::Tspi_TPM_ReadCounter(m_tpm, &r));
+ return r;
+ }
+
+void TPM_Context::gen_random(uint8_t out[], size_t out_len)
+ {
+ BYTE* mem;
+ TSPI_CHECK_SUCCESS(::Tspi_TPM_GetRandom(m_tpm, out_len, &mem));
+ std::memcpy(out, mem, out_len);
+ TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(m_ctx, mem));
+ }
+
+void TPM_Context::stir_random(const uint8_t in[], size_t in_len)
+ {
+ TSPI_CHECK_SUCCESS(::Tspi_TPM_StirRandom(m_tpm, in_len, const_cast<BYTE*>(in)));
+ }
+
+TPM_PrivateKey::TPM_PrivateKey(TPM_Context& ctx, size_t bits,
+ const char* key_password) : m_ctx(ctx)
+ {
+ // TODO: can also do OAEP decryption via binding keys
+ // TODO: offer signing, binding (decrypt), or legacy (sign + decrypt) keys?
+
+ TSS_FLAG key_flags = bit_flag(bits) | TSS_KEY_VOLATILE | TSS_KEY_TYPE_SIGNING;
+
+ TSS_HKEY key;
+ TSPI_CHECK_SUCCESS(::Tspi_Context_CreateObject(m_ctx.handle(), TSS_OBJECT_TYPE_RSAKEY, key_flags, &key));
+
+ TSPI_CHECK_SUCCESS(::Tspi_SetAttribUint32(key, TSS_TSPATTRIB_KEY_INFO,
+ TSS_TSPATTRIB_KEYINFO_SIGSCHEME,
+ TSS_SS_RSASSAPKCS1V15_DER));
+
+ TSS_HPOLICY policy;
+ TSPI_CHECK_SUCCESS(::Tspi_Context_CreateObject(m_ctx.handle(), TSS_OBJECT_TYPE_POLICY, TSS_POLICY_USAGE, &policy));
+ set_policy_secret(policy, key_password);
+ TSPI_CHECK_SUCCESS(::Tspi_Policy_AssignToObject(policy, key));
+
+ TSPI_CHECK_SUCCESS(::Tspi_Key_CreateKey(key, ctx.srk(), 0));
+ m_key = key;
+ }
+
+// reference a registered TPM key
+TPM_PrivateKey::TPM_PrivateKey(TPM_Context& ctx, const std::string& uuid_str,
+ TPM_Storage_Type storage_type) :
+ m_ctx(ctx),
+ m_uuid(uuid_str),
+ m_storage(storage_type)
+ {
+ const TSS_FLAG key_ps_type =
+ (m_storage == TPM_Storage_Type::User) ? TSS_PS_TYPE_USER : TSS_PS_TYPE_SYSTEM;
+
+ TSPI_CHECK_SUCCESS(::Tspi_Context_LoadKeyByUUID(m_ctx.handle(),
+ key_ps_type,
+ to_tss_uuid(m_uuid),
+ &m_key));
+ }
+
+TPM_PrivateKey::TPM_PrivateKey(TPM_Context& ctx,
+ const std::vector<uint8_t>& blob) : m_ctx(ctx)
+ {
+ TSPI_CHECK_SUCCESS(::Tspi_Context_LoadKeyByBlob(m_ctx.handle(), m_ctx.srk(), blob.size(),
+ const_cast<uint8_t*>(blob.data()),
+ &m_key));
+
+ //TSPI_CHECK_SUCCESS(::Tspi_Key_LoadKey(m_key, m_ctx.srk()));
+ }
+
+std::string TPM_PrivateKey::register_key(TPM_Storage_Type storage_type)
+ {
+ if(!m_uuid.is_valid())
+ {
+ TPM_RNG rng(ctx()); // use system_rng or arg RNG& instead?
+ m_uuid = UUID(rng);
+ m_storage = storage_type;
+
+ const TSS_UUID key_uuid = to_tss_uuid(m_uuid);
+ const TSS_FLAG key_ps_type =
+ (storage_type == TPM_Storage_Type::User) ? TSS_PS_TYPE_USER : TSS_PS_TYPE_SYSTEM;
+
+ const TSS_UUID srk_uuid = TSS_UUID_SRK;
+
+ TSPI_CHECK_SUCCESS(::Tspi_Context_RegisterKey(m_ctx.handle(),
+ m_key,
+ key_ps_type,
+ key_uuid,
+ TSS_PS_TYPE_SYSTEM,
+ srk_uuid));
+
+ }
+
+ // Presumably we could re-register in the other store and same UUID
+ // Doesn't seem like what is desired most of the time here
+ if(storage_type != m_storage)
+ {
+ throw TPM_Error("TPM key " + m_uuid.to_string() +
+ " already registered with different storage type");
+ }
+
+ return format_url(m_uuid, m_storage);
+ }
+
+std::vector<std::string> TPM_PrivateKey::registered_keys(TPM_Context& ctx)
+ {
+ TSS_KM_KEYINFO2* key_info;
+ UINT32 key_info_size;
+
+ // TODO: does the PS type matter here at all?
+ TSPI_CHECK_SUCCESS(::Tspi_Context_GetRegisteredKeysByUUID2(ctx.handle(),
+ TSS_PS_TYPE_SYSTEM,
+ nullptr,
+ &key_info_size,
+ &key_info));
+
+ std::vector<std::string> r(key_info_size);
+
+ for(size_t i = 0; i != key_info_size; ++i)
+ {
+ r[i] = format_url(key_info[i].keyUUID, key_info[i].persistentStorageType);
+ }
+
+ // TODO: are we supposed to free this memory and if so how?
+ //TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(ctx.handle(), key_info));
+
+ return r;
+ }
+
+BigInt TPM_PrivateKey::get_n() const
+ {
+ if(m_n == 0)
+ {
+ m_n = BigInt::decode(get_obj_attr(m_ctx.handle(), m_key,
+ TSS_TSPATTRIB_RSAKEY_INFO,
+ TSS_TSPATTRIB_KEYINFO_RSA_MODULUS));
+ }
+
+ return m_n;
+ }
+
+BigInt TPM_PrivateKey::get_e() const
+ {
+ if(m_e == 0)
+ {
+ m_e = BigInt::decode(get_obj_attr(m_ctx.handle(), m_key,
+ TSS_TSPATTRIB_RSAKEY_INFO,
+ TSS_TSPATTRIB_KEYINFO_RSA_EXPONENT));
+ }
+
+ return m_e;
+ }
+
+size_t TPM_PrivateKey::estimated_strength() const
+ {
+ return if_work_factor(get_n().bits());
+ }
+
+size_t TPM_PrivateKey::max_input_bits() const
+ {
+ return get_n().bits();
+ }
+
+AlgorithmIdentifier TPM_PrivateKey::algorithm_identifier() const
+ {
+ return AlgorithmIdentifier(get_oid(),
+ AlgorithmIdentifier::USE_NULL_PARAM);
+ }
+
+std::vector<byte> TPM_PrivateKey::x509_subject_public_key() const
+ {
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode(get_n())
+ .encode(get_e())
+ .end_cons()
+ .get_contents_unlocked();
+ }
+
+secure_vector<byte> TPM_PrivateKey::pkcs8_private_key() const
+ {
+ throw TPM_Error("PKCS #8 export not supported for TPM keys");
+ }
+
+std::vector<uint8_t> TPM_PrivateKey::export_blob() const
+ {
+ return get_obj_attr(m_ctx.handle(), m_key,
+ TSS_TSPATTRIB_KEY_BLOB,
+ TSS_TSPATTRIB_KEYBLOB_BLOB);
+ }
+
+std::unique_ptr<Public_Key> TPM_PrivateKey::public_key() const
+ {
+ return std::unique_ptr<Public_Key>(new RSA_PublicKey(get_n(), get_e()));
+ }
+
+bool TPM_PrivateKey::check_key(RandomNumberGenerator&, bool) const
+ {
+ return true; // TODO do a kat or pairwise check
+ }
+
+namespace {
+
+class TPM_Signing_Operation : public PK_Ops::Signature
+ {
+ public:
+ static TPM_Signing_Operation* make(const Spec& spec)
+ {
+ if(auto* key = dynamic_cast<const TPM_PrivateKey*>(&spec.key()))
+ {
+ const std::string padding = spec.padding();
+ const std::string hash = "SHA-256"; // TODO
+ return new TPM_Signing_Operation(*key, hash);
+ }
+
+ return nullptr;
+ }
+
+ TPM_Signing_Operation(const TPM_PrivateKey& key,
+ const std::string& hash_name) :
+ m_key(key),
+ m_hash(HashFunction::create(hash_name)),
+ m_hash_id(pkcs_hash_id(hash_name))
+ {
+ }
+
+ void update(const byte msg[], size_t msg_len) override
+ {
+ m_hash->update(msg, msg_len);
+ }
+
+ secure_vector<byte> sign(RandomNumberGenerator&) override
+ {
+ /*
+ * v1.2 TPMs will only sign with PKCS #1 v1.5 padding. SHA-1 is built
+ * in, all other hash inputs (TSS_HASH_OTHER) are treated as the
+ * concatenation of the hash OID and hash value and signed with just the
+ * 01FFFF... prefix. Even when using SHA-1 we compute the hash locally
+ * since it is going to be much faster than pushing data over the LPC bus.
+ */
+ secure_vector<byte> msg_hash = m_hash->final();
+
+ std::vector<uint8_t> id_and_msg;
+ id_and_msg.reserve(m_hash_id.size() + msg_hash.size());
+ id_and_msg.insert(id_and_msg.end(), m_hash_id.begin(), m_hash_id.end());
+ id_and_msg.insert(id_and_msg.end(), msg_hash.begin(), msg_hash.end());
+
+ TSS_HCONTEXT ctx = m_key.ctx().handle();
+ TSS_HHASH tpm_hash;
+ TSPI_CHECK_SUCCESS(::Tspi_Context_CreateObject(ctx, TSS_OBJECT_TYPE_HASH, TSS_HASH_OTHER, &tpm_hash));
+ TSPI_CHECK_SUCCESS(::Tspi_Hash_SetHashValue(tpm_hash, id_and_msg.size(), id_and_msg.data()));
+
+ BYTE* sig_bytes = nullptr;
+ UINT32 sig_len = 0;
+ TSPI_CHECK_SUCCESS(::Tspi_Hash_Sign(tpm_hash, m_key.handle(), &sig_len, &sig_bytes));
+ secure_vector<uint8_t> sig(sig_bytes, sig_bytes + sig_len);
+
+ // TODO: RAII for Context_FreeMemory
+ TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(ctx, sig_bytes));
+
+ // TODO: RAII for Context_CloseObject
+ TSPI_CHECK_SUCCESS(::Tspi_Context_CloseObject(ctx, tpm_hash));
+
+ return sig;
+ }
+
+ private:
+ const TPM_PrivateKey& m_key;
+ std::unique_ptr<HashFunction> m_hash;
+ std::vector<uint8_t> m_hash_id;
+ };
+
+}
+
+BOTAN_REGISTER_TYPE(PK_Ops::Signature, TPM_Signing_Operation, "RSA",
+ TPM_Signing_Operation::make, "tpm", 100);
+
+}
diff --git a/src/lib/prov/tpm/tpm.h b/src/lib/prov/tpm/tpm.h
new file mode 100644
index 000000000..ce5eabe1c
--- /dev/null
+++ b/src/lib/prov/tpm/tpm.h
@@ -0,0 +1,182 @@
+/*
+* TPM 1.2 interface
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/exceptn.h>
+#include <botan/pk_keys.h>
+#include <botan/bigint.h>
+#include <botan/rng.h>
+#include <botan/uuid.h>
+
+//TODO remove this
+#include <tss/tspi.h>
+
+namespace Botan {
+
+class TPM_Error : public Exception
+ {
+ public:
+ TPM_Error(const std::string& err) : Exception(err) {}
+ };
+
+/**
+* Creates a connection to the TPM. All other TPM types take and hold
+* a TPM_Context reference, so all other objects must be deallocated
+* before ~TPM_Context runs.
+*
+* Use nullptr for the srk_password to indicate the well known secret
+* (ie, an unencrypted SRK). This is usually what you want.
+*
+* TODO: handling owner password?
+*/
+class BOTAN_DLL TPM_Context
+ {
+ public:
+ /**
+ * User callback for getting the PIN. Will be passed the best available
+ * description of what we are attempting to load.
+ */
+ typedef std::function<std::string (std::string)> pin_cb;
+
+ TPM_Context(pin_cb cb, const char* srk_password);
+
+ ~TPM_Context();
+
+ // Get data from the TPM's RNG, whatever that is
+ void gen_random(uint8_t out[], size_t out_len);
+
+ // Uses Tspi_TPM_StirRandom to add data to TPM's internal pool
+ void stir_random(const uint8_t in[], size_t in_len);
+
+ std::string get_user_pin(const std::string& who)
+ {
+ return m_pin_cb(who);
+ }
+
+ uint32_t current_counter();
+
+ TSS_HCONTEXT handle() const { return m_ctx; }
+ TSS_HKEY srk() const { return m_srk; }
+
+ private:
+ std::function<std::string (std::string)> m_pin_cb;
+ TSS_HCONTEXT m_ctx;
+ TSS_HKEY m_srk;
+ TSS_HTPM m_tpm;
+ };
+
+class BOTAN_DLL TPM_RNG : public RandomNumberGenerator
+ {
+ public:
+ TPM_RNG(TPM_Context& ctx) : m_ctx(ctx) {}
+
+ void randomize(byte out[], size_t out_len) override
+ {
+ m_ctx.gen_random(out, out_len);
+ }
+
+ void clear() override {}
+
+ std::string name() const override { return "TPM_RNG"; }
+
+ size_t reseed_with_sources(Entropy_Sources&,
+ size_t,
+ std::chrono::milliseconds) override
+ {
+ // TODO: poll and stir
+ return 0;
+ }
+
+ void add_entropy(const byte in[], size_t in_len) override
+ {
+ m_ctx.stir_random(in, in_len);
+ }
+
+ bool is_seeded() const override { return true; }
+ private:
+ TPM_Context& m_ctx;
+};
+
+enum class TPM_Storage_Type { User, System };
+
+/*
+* Also implements the public interface, but does not have usable
+* TODO: derive from RSA_PublicKey???
+*/
+class BOTAN_DLL TPM_PrivateKey : public Private_Key
+ {
+ public:
+ // TODO: key import?
+
+ /*
+ * Create a new key on the TPM parented to the SRK
+ * @param bits must be 1024 or 2048
+ */
+ TPM_PrivateKey(TPM_Context& ctx, size_t bits, const char* key_password);
+
+ // reference an existing TPM key using URL syntax from GnuTLS
+ // "tpmkey:uuid=79f07ca9-73ac-478a-9093-11ca6702e774;storage=user"
+ //TPM_PrivateKey(TPM_Context& ctx, const std::string& tpm_url);
+
+ TPM_PrivateKey(TPM_Context& ctx,
+ const std::string& uuid,
+ TPM_Storage_Type storage_type);
+
+ TPM_PrivateKey(TPM_Context& ctx,
+ const std::vector<uint8_t>& blob);
+
+ /**
+ * If the key is not currently registered under a known UUID,
+ * generates a new random UUID and registers the key.
+ * Returns the access URL.
+ */
+ std::string register_key(TPM_Storage_Type storage_type);
+
+ /*
+ * Returns the list of all keys (in URL format) registered with the system
+ */
+ static std::vector<std::string> registered_keys(TPM_Context& ctx);
+
+ size_t estimated_strength() const;
+
+ size_t max_input_bits() const;
+
+ AlgorithmIdentifier algorithm_identifier() const;
+
+ std::vector<byte> x509_subject_public_key() const;
+
+ secure_vector<byte> pkcs8_private_key() const; // not implemented
+
+ std::unique_ptr<Public_Key> public_key() const;
+
+ bool check_key(RandomNumberGenerator& rng, bool) const override;
+
+ std::string algo_name() const { return "RSA"; } // ???
+
+ std::vector<uint8_t> export_blob() const;
+
+ TPM_Context& ctx() const { return m_ctx; }
+
+ TSS_HKEY handle() const { return m_key; }
+ private:
+ BigInt get_n() const;
+ BigInt get_e() const;
+
+ TPM_Context& m_ctx;
+ TSS_HKEY m_key;
+
+ // Only set for registered keys
+ UUID m_uuid;
+ TPM_Storage_Type m_storage;
+
+ // Lazily computed in get_n, get_e
+ mutable BigInt m_n, m_e;
+ };
+
+// TODO: NVRAM interface
+// TODO: PCR measurement, writing, key locking
+
+}
diff --git a/src/lib/prov/tpm/uuid.h b/src/lib/prov/tpm/uuid.h
new file mode 100644
index 000000000..3c35da1f4
--- /dev/null
+++ b/src/lib/prov/tpm/uuid.h
@@ -0,0 +1,101 @@
+/*
+* UUID type
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/secmem.h>
+#include <botan/hex.h>
+#include <sstream>
+
+namespace Botan {
+
+// TODO: move to util?
+class UUID
+ {
+ public:
+ // Represents an unassigned UUID object
+ UUID() : m_uuid(0) {}
+
+ UUID(RandomNumberGenerator& rng)
+ {
+ m_uuid.resize(16);
+ rng.randomize(m_uuid.data(), m_uuid.size());
+
+ // Mark as a random UUID (RFC 4122 sec 4.4)
+ m_uuid[6] = 0x40 | (m_uuid[6] & 0x0F);
+
+ // Set two reserved bits
+ m_uuid[8] = 0xC0 | (m_uuid[8] & 0x3F);
+ }
+
+ UUID(const std::vector<uint8_t>& blob)
+ {
+ if(blob.size() != 16)
+ {
+ throw Invalid_Argument("Bad UUID blob " + hex_encode(blob));
+ }
+
+ m_uuid = blob;
+ }
+
+ UUID(const std::string& uuid_str)
+ {
+ if(uuid_str.size() != 36 ||
+ uuid_str[8] != '-' ||
+ uuid_str[14] != '-' ||
+ uuid_str[19] != '-' ||
+ uuid_str[24] != '-')
+ {
+ throw Invalid_Argument("Bad UUID '" + uuid_str + "'");
+ }
+
+ std::string just_hex;
+ for(size_t i = 0; i != uuid_str.size(); ++i)
+ {
+ char c = uuid_str[i];
+
+ if(c == '-')
+ continue;
+
+ just_hex += c;
+ }
+
+ m_uuid = hex_decode(just_hex);
+
+ if(m_uuid.size() != 16)
+ {
+ throw Invalid_Argument("Bad UUID '" + uuid_str + "'");
+ }
+ }
+
+
+ std::string to_string() const
+ {
+ std::string h = hex_encode(m_uuid);
+
+ h.insert(8, "-");
+ h.insert(14, "-");
+ h.insert(19, "-");
+ h.insert(24, "-");
+
+ return h;
+ }
+
+ const std::vector<uint8_t> binary_value() const { return m_uuid; }
+
+ bool operator==(const UUID& other)
+ {
+ return m_uuid == other.m_uuid;
+ }
+
+ bool operator!=(const UUID& other) { return !(*this == other); }
+
+ bool is_valid() const { return m_uuid.size() == 16; }
+
+ private:
+ std::vector<uint8_t> m_uuid;
+ };
+
+}