lkml.org 
[lkml]   [2011]   [Nov]   [2]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH 2/2] trusted-key: added support for loading a key blob in the TPM
Date
The new functions 'tpm_loadkey2', 'tpm_evictkey' and 'tpm_flushspecific'
allow to load/unload a TPM key whose blob is provided from the userspace
interface and to use it for sealing or unsealing the symmetric key.

Signed-off-by: Roberto Sassu <roberto.sassu@polito.it>
---
Documentation/security/keys-trusted-encrypted.txt | 6 +-
include/linux/tpm_command.h | 6 +
security/keys/trusted.c | 203 ++++++++++++++++++++-
security/keys/trusted.h | 27 +++-
4 files changed, 234 insertions(+), 8 deletions(-)

diff --git a/Documentation/security/keys-trusted-encrypted.txt b/Documentation/security/keys-trusted-encrypted.txt
index 5f50cca..afebb58 100644
--- a/Documentation/security/keys-trusted-encrypted.txt
+++ b/Documentation/security/keys-trusted-encrypted.txt
@@ -27,8 +27,10 @@ Usage:
keyctl print keyid

options:
- keyhandle= ascii hex value of sealing key default 0x40000000 (SRK)
- keyauth= ascii hex auth for sealing key default 0x00...i
+ keyhandle= ascii hex value of sealing key handle default 0x40000000 (SRK)
+ keyblob= ascii hex value of sealing key blob (no default)
+ srkauth= ascii hex auth for SRK key default 0x00...
+ keyauth= ascii hex auth for sealing key (not SRK) default 0x00...
(40 ascii zeros)
blobauth= ascii hex auth for sealed data default 0x00...
(40 ascii zeros)
diff --git a/include/linux/tpm_command.h b/include/linux/tpm_command.h
index 727512e..e3348b7 100644
--- a/include/linux/tpm_command.h
+++ b/include/linux/tpm_command.h
@@ -15,7 +15,10 @@
#define TPM_TAG_RSP_AUTH2_COMMAND 198

/* Command Ordinals */
+#define TPM_ORD_EVICTKEY 34
+#define TPM_ORD_FLUSHSPECIFIC 186
#define TPM_ORD_GETRANDOM 70
+#define TPM_ORD_LOADKEY2 65
#define TPM_ORD_OSAP 11
#define TPM_ORD_OIAP 10
#define TPM_ORD_SEAL 23
@@ -24,5 +27,8 @@
/* Other constants */
#define SRKHANDLE 0x40000000
#define TPM_NONCE_SIZE 20
+#define TPM_RT_KEY 0x00000001
+#define TPM_TAG_KEY12 0x0028
+#define TPM_BAD_ORDINAL 10

#endif
diff --git a/security/keys/trusted.c b/security/keys/trusted.c
index 8777015..c332e3b 100644
--- a/security/keys/trusted.c
+++ b/security/keys/trusted.c
@@ -688,12 +688,118 @@ static int tpm_unseal(struct tpm_buf *tb,
}

/*
+ * Load a TPM key from the blob provided by userspace
+ */
+static int tpm_loadkey2(struct tpm_buf *tb,
+ uint32_t keyhandle, unsigned char *keyauth,
+ const unsigned char *keyblob, int keybloblen,
+ uint32_t *newhandle)
+{
+ unsigned char nonceodd[TPM_NONCE_SIZE];
+ unsigned char enonce[TPM_NONCE_SIZE];
+ unsigned char authdata[SHA1_DIGEST_SIZE];
+ uint32_t authhandle = 0;
+ unsigned char cont = 0;
+ uint32_t ordinal;
+ int ret;
+
+ ordinal = htonl(TPM_ORD_LOADKEY2);
+
+ /* session for loading the key */
+ ret = oiap(tb, &authhandle, enonce);
+ if (ret < 0) {
+ pr_info("trusted_key: oiap failed (%d)\n", ret);
+ return ret;
+ }
+
+ /* generate odd nonce */
+ ret = tpm_get_random(tb, nonceodd, TPM_NONCE_SIZE);
+ if (ret < 0) {
+ pr_info("trusted_key: tpm_get_random failed (%d)\n", ret);
+ return ret;
+ }
+
+ /* calculate authorization HMAC value */
+ ret = TSS_authhmac(authdata, keyauth, SHA1_DIGEST_SIZE, enonce,
+ nonceodd, cont, sizeof(uint32_t), &ordinal,
+ keybloblen, keyblob, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ /* build the request buffer */
+ INIT_BUF(tb);
+ store16(tb, TPM_TAG_RQU_AUTH1_COMMAND);
+ store32(tb, TPM_LOADKEY2_SIZE + keybloblen);
+ store32(tb, TPM_ORD_LOADKEY2);
+ store32(tb, keyhandle);
+ storebytes(tb, keyblob, keybloblen);
+ store32(tb, authhandle);
+ storebytes(tb, nonceodd, TPM_NONCE_SIZE);
+ store8(tb, cont);
+ storebytes(tb, authdata, SHA1_DIGEST_SIZE);
+
+ ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE);
+ if (ret < 0) {
+ pr_info("trusted_key: authhmac failed (%d)\n", ret);
+ return ret;
+ }
+
+ ret = TSS_checkhmac1(tb->data, ordinal, nonceodd, keyauth,
+ SHA1_DIGEST_SIZE, 0, 0);
+ if (ret < 0) {
+ pr_info("trusted_key: TSS_checkhmac1 failed (%d)\n", ret);
+ return ret;
+ }
+
+ *newhandle = LOAD32(tb->data, TPM_DATA_OFFSET);
+ return 0;
+}
+
+/*
+ * Execute the FlushSpecific TPM command
+ */
+uint32_t tpm_flushspecific(struct tpm_buf *tb, uint32_t handle,
+ uint32_t resourcetype)
+{
+ INIT_BUF(tb);
+ store16(tb, TPM_TAG_RQU_COMMAND);
+ store32(tb, TPM_FLUSHSPECIFIC_SIZE);
+ store32(tb, TPM_ORD_FLUSHSPECIFIC);
+ store32(tb, handle);
+ store32(tb, resourcetype);
+
+ return trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE);
+}
+
+/*
+ * Evict a key from the TPM
+ */
+uint32_t tpm_evictkey(struct tpm_buf *tb, uint32_t keyhandle)
+{
+ int ret;
+
+ INIT_BUF(tb);
+ store16(tb, TPM_TAG_RQU_COMMAND);
+ store32(tb, TPM_EVICTKEY_SIZE);
+ store32(tb, TPM_ORD_EVICTKEY);
+ store32(tb, keyhandle);
+
+ ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, sizeof tb->data);
+ if (ret < 0)
+ ret = tpm_flushspecific(tb, keyhandle, TPM_RT_KEY);
+
+ return ret;
+}
+
+/*
* Have the TPM seal(encrypt) the symmetric key
*/
static int key_seal(struct trusted_key_payload *p,
struct trusted_key_options *o)
{
struct tpm_buf *tb;
+ uint32_t keyhandle;
+ unsigned char *parentauth;
int ret;

tb = kzalloc(sizeof *tb, GFP_KERNEL);
@@ -703,12 +809,40 @@ static int key_seal(struct trusted_key_payload *p,
/* include migratable flag at end of sealed key */
p->key[p->key_len] = p->migratable;

- ret = tpm_seal(tb, o->keytype, o->keyhandle, o->keyauth,
+ /* set default values */
+ keyhandle = o->keyhandle;
+ parentauth = o->srkauth;
+
+ if (o->keytype == SEAL_keytype) {
+ parentauth = o->keyauth;
+ if (o->keyblob_len > 0) {
+ ret = tpm_loadkey2(tb, SRKHANDLE, o->srkauth,
+ o->keyblob, o->keyblob_len,
+ &keyhandle);
+ if (ret < 0) {
+ pr_info("trusted_key: loadkey2 failed (%d)\n",
+ ret);
+ goto out;
+ }
+
+ dump_tpm_key12_handle(keyhandle);
+ }
+ }
+
+ ret = tpm_seal(tb, o->keytype, keyhandle, parentauth,
p->key, p->key_len + 1, p->blob, &p->blob_len,
o->blobauth, o->pcrinfo, o->pcrinfo_len);
if (ret < 0)
pr_info("trusted_key: srkseal failed (%d)\n", ret);

+ if (o->keyblob_len > 0) {
+ int evictret = tpm_evictkey(tb, keyhandle);
+
+ if (evictret < 0)
+ pr_info("trusted_key: evictkey failed (%d)\n",
+ evictret);
+ }
+out:
kfree(tb);
return ret;
}
@@ -720,13 +854,33 @@ static int key_unseal(struct trusted_key_payload *p,
struct trusted_key_options *o)
{
struct tpm_buf *tb;
+ uint32_t keyhandle;
+ unsigned char *parentauth;
int ret;

tb = kzalloc(sizeof *tb, GFP_KERNEL);
if (!tb)
return -ENOMEM;

- ret = tpm_unseal(tb, o->keyhandle, o->keyauth, p->blob, p->blob_len,
+ /* set default values */
+ keyhandle = o->keyhandle;
+ parentauth = o->srkauth;
+
+ if (o->keytype == SEAL_keytype) {
+ parentauth = o->keyauth;
+ if (o->keyblob_len > 0) {
+ ret = tpm_loadkey2(tb, SRKHANDLE, o->srkauth,
+ o->keyblob, o->keyblob_len,
+ &keyhandle);
+ if (ret < 0) {
+ pr_info("trusted_key: loadkey2 failed (%d)\n",
+ ret);
+ goto out;
+ }
+ }
+ }
+
+ ret = tpm_unseal(tb, keyhandle, parentauth, p->blob, p->blob_len,
o->blobauth, p->key, &p->key_len);
if (ret < 0)
pr_info("trusted_key: srkunseal failed (%d)\n", ret);
@@ -734,14 +888,22 @@ static int key_unseal(struct trusted_key_payload *p,
/* pull migratable flag out of sealed key */
p->migratable = p->key[--p->key_len];

+ if (o->keyblob_len > 0) {
+ int evictret = tpm_evictkey(tb, keyhandle);
+
+ if (evictret < 0)
+ pr_info("trusted_key: evictkey failed (%d)\n",
+ evictret);
+ }
+out:
kfree(tb);
return ret;
}

enum {
Opt_err = -1,
- Opt_new, Opt_load, Opt_update,
- Opt_keyhandle, Opt_keyauth, Opt_blobauth,
+ Opt_new, Opt_load, Opt_update, Opt_srkauth,
+ Opt_keyhandle, Opt_keyblob, Opt_keyauth, Opt_blobauth,
Opt_pcrinfo, Opt_pcrlock, Opt_migratable
};

@@ -749,7 +911,9 @@ static const match_table_t key_tokens = {
{Opt_new, "new"},
{Opt_load, "load"},
{Opt_update, "update"},
+ {Opt_srkauth, "srkauth=%s"},
{Opt_keyhandle, "keyhandle=%s"},
+ {Opt_keyblob, "keyblob=%s"},
{Opt_keyauth, "keyauth=%s"},
{Opt_blobauth, "blobauth=%s"},
{Opt_pcrinfo, "pcrinfo=%s"},
@@ -768,6 +932,8 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
int res;
unsigned long handle;
unsigned long lock;
+ uint16_t tpm_key_tag;
+ uint32_t value;

while ((p = strsep(&c, " \t"))) {
if (*p == '\0' || *p == ' ' || *p == '\t')
@@ -788,6 +954,35 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
opt->keytype = SEAL_keytype;
opt->keyhandle = handle;
break;
+ case Opt_keyblob:
+ if (strlen(args[0].from) >= MAX_KEYBLOB_SIZE * 2)
+ return -EINVAL;
+ hex2bin(opt->keyblob, args[0].from, MAX_KEYBLOB_SIZE);
+ tpm_key_tag = LOAD16(opt->keyblob, 0);
+ if (tpm_key_tag != TPM_TAG_KEY12)
+ return -EINVAL;
+ opt->keytype = SEAL_keytype;
+ opt->keyblob_len = TPM_KEY12_EXPSIZE_OFFSET;
+ /* key exponent size */
+ value = LOAD32(opt->keyblob, opt->keyblob_len);
+ opt->keyblob_len += sizeof(uint32_t) + value;
+ /* PCRINFO size */
+ value = LOAD32(opt->keyblob, opt->keyblob_len);
+ opt->keyblob_len += sizeof(uint32_t) + value;
+ /* key length */
+ value = LOAD32(opt->keyblob, opt->keyblob_len);
+ opt->keyblob_len += sizeof(uint32_t) + value;
+ /* enc data size */
+ value = LOAD32(opt->keyblob, opt->keyblob_len);
+ opt->keyblob_len += sizeof(uint32_t) + value;
+ if (opt->keyblob_len >= MAX_KEYBLOB_SIZE)
+ return -EINVAL;
+ break;
+ case Opt_srkauth:
+ if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE)
+ return -EINVAL;
+ hex2bin(opt->srkauth, args[0].from, SHA1_DIGEST_SIZE);
+ break;
case Opt_keyauth:
if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE)
return -EINVAL;
diff --git a/security/keys/trusted.h b/security/keys/trusted.h
index 3249fbd..6a9f373 100644
--- a/security/keys/trusted.h
+++ b/security/keys/trusted.h
@@ -3,12 +3,16 @@

/* implementation specific TPM constants */
#define MAX_PCRINFO_SIZE 64
-#define MAX_BUF_SIZE 512
+#define MAX_BUF_SIZE 1024
+#define MAX_KEYBLOB_SIZE 1024
#define TPM_GETRANDOM_SIZE 14
#define TPM_OSAP_SIZE 36
#define TPM_OIAP_SIZE 10
#define TPM_SEAL_SIZE 87
#define TPM_UNSEAL_SIZE 104
+#define TPM_LOADKEY2_SIZE 59
+#define TPM_EVICTKEY_SIZE 14
+#define TPM_FLUSHSPECIFIC_SIZE 18
#define TPM_SIZE_OFFSET 2
#define TPM_RETURN_OFFSET 6
#define TPM_DATA_OFFSET 10
@@ -17,6 +21,8 @@
#define LOAD32N(buffer, offset) (*(uint32_t *)&buffer[offset])
#define LOAD16(buffer, offset) (ntohs(*(uint16_t *)&buffer[offset]))

+#define TPM_KEY12_EXPSIZE_OFFSET 31
+
struct tpm_buf {
int len;
unsigned char data[MAX_BUF_SIZE];
@@ -39,6 +45,9 @@ enum {
struct trusted_key_options {
uint16_t keytype;
uint32_t keyhandle;
+ uint32_t keyblob_len;
+ unsigned char keyblob[MAX_KEYBLOB_SIZE];
+ unsigned char srkauth[SHA1_DIGEST_SIZE];
unsigned char keyauth[SHA1_DIGEST_SIZE];
unsigned char blobauth[SHA1_DIGEST_SIZE];
uint32_t pcrinfo_len;
@@ -52,7 +61,12 @@ struct trusted_key_options {
static inline void dump_options(struct trusted_key_options *o)
{
pr_info("trusted_key: sealing key type %d\n", o->keytype);
- pr_info("trusted_key: sealing key handle %0X\n", o->keyhandle);
+ if (o->keyblob_len > 0) {
+ pr_info("trusted_key: sealing key blob %d\n", o->keyblob_len);
+ print_hex_dump(KERN_INFO, "keyblob ", DUMP_PREFIX_NONE,
+ 16, 1, o->keyblob, o->keyblob_len, 0);
+ } else
+ pr_info("trusted_key: sealing key handle %0X\n", o->keyhandle);
pr_info("trusted_key: pcrlock %d\n", o->pcrlock);
pr_info("trusted_key: pcrinfo %d\n", o->pcrinfo_len);
print_hex_dump(KERN_INFO, "pcrinfo ", DUMP_PREFIX_NONE,
@@ -90,6 +104,11 @@ static inline void dump_tpm_buf(unsigned char *buf)
len = LOAD32(buf, TPM_SIZE_OFFSET);
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, buf, len, 0);
}
+static inline void dump_tpm_key12_handle(uint32_t handle)
+{
+ print_hex_dump(KERN_INFO, "trusted-key: key handle ", DUMP_PREFIX_NONE,
+ 16, 1, &handle, 4, 0);
+}
#else
static inline void dump_options(struct trusted_key_options *o)
{
@@ -106,6 +125,10 @@ static inline void dump_sess(struct osapsess *s)
static inline void dump_tpm_buf(unsigned char *buf)
{
}
+
+static inline void dump_tpm_key12_handle(uint32_t handle)
+{
+}
#endif

static inline void store8(struct tpm_buf *buf, const unsigned char value)
--
1.7.6.4
[unhandled content-type:application/x-pkcs7-signature]
\
 
 \ /
  Last update: 2011-11-02 13:45    [W:0.073 / U:0.060 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site