tpm: seal/unseal for TPM 2.0
authorJarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Sat, 30 May 2015 05:09:04 +0000 (08:09 +0300)
committerPeter Huewe <peterhuewe@gmx.de>
Sun, 18 Oct 2015 23:01:21 +0000 (01:01 +0200)
Added tpm_trusted_seal() and tpm_trusted_unseal() API for sealing
trusted keys.

This patch implements basic sealing and unsealing functionality for
TPM 2.0:

* Seal with a parent key using a 20 byte auth value.
* Unseal with a parent key using a 20 byte auth value.

Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Signed-off-by: Peter Huewe <peterhuewe@gmx.de>
drivers/char/tpm/tpm-interface.c
drivers/char/tpm/tpm.h
drivers/char/tpm/tpm2-cmd.c
include/keys/trusted-type.h
include/linux/tpm.h

index e85d3416d89918e4050eeec8dd32233f2589dc44..c50637db3a8a9336f002d44046135bdb49fa7088 100644 (file)
@@ -665,6 +665,30 @@ int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
        return rc;
 }
 
+/**
+ * tpm_is_tpm2 - is the chip a TPM2 chip?
+ * @chip_num:  tpm idx # or ANY
+ *
+ * Returns < 0 on error, and 1 or 0 on success depending whether the chip
+ * is a TPM2 chip.
+ */
+int tpm_is_tpm2(u32 chip_num)
+{
+       struct tpm_chip *chip;
+       int rc;
+
+       chip = tpm_chip_find_get(chip_num);
+       if (chip == NULL)
+               return -ENODEV;
+
+       rc = (chip->flags & TPM_CHIP_FLAG_TPM2) != 0;
+
+       tpm_chip_put(chip);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_is_tpm2);
+
 /**
  * tpm_pcr_read - read a pcr value
  * @chip_num:  tpm idx # or ANY
@@ -1021,6 +1045,58 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max)
 }
 EXPORT_SYMBOL_GPL(tpm_get_random);
 
+/**
+ * tpm_seal_trusted() - seal a trusted key
+ * @chip_num: A specific chip number for the request or TPM_ANY_NUM
+ * @options: authentication values and other options
+ * @payload: the key data in clear and encrypted form
+ *
+ * Returns < 0 on error and 0 on success. At the moment, only TPM 2.0 chips
+ * are supported.
+ */
+int tpm_seal_trusted(u32 chip_num, struct trusted_key_payload *payload,
+                    struct trusted_key_options *options)
+{
+       struct tpm_chip *chip;
+       int rc;
+
+       chip = tpm_chip_find_get(chip_num);
+       if (chip == NULL || !(chip->flags & TPM_CHIP_FLAG_TPM2))
+               return -ENODEV;
+
+       rc = tpm2_seal_trusted(chip, payload, options);
+
+       tpm_chip_put(chip);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_seal_trusted);
+
+/**
+ * tpm_unseal_trusted() - unseal a trusted key
+ * @chip_num: A specific chip number for the request or TPM_ANY_NUM
+ * @options: authentication values and other options
+ * @payload: the key data in clear and encrypted form
+ *
+ * Returns < 0 on error and 0 on success. At the moment, only TPM 2.0 chips
+ * are supported.
+ */
+int tpm_unseal_trusted(u32 chip_num, struct trusted_key_payload *payload,
+                      struct trusted_key_options *options)
+{
+       struct tpm_chip *chip;
+       int rc;
+
+       chip = tpm_chip_find_get(chip_num);
+       if (chip == NULL || !(chip->flags & TPM_CHIP_FLAG_TPM2))
+               return -ENODEV;
+
+       rc = tpm2_unseal_trusted(chip, payload, options);
+
+       tpm_chip_put(chip);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_unseal_trusted);
+
 static int __init tpm_init(void)
 {
        int rc;
index cb46f6267af2560dedb10c18622fbbca1f4d3f42..a4257a32964f40c189f7a1dc54bd252efa48532a 100644 (file)
@@ -90,6 +90,9 @@ enum tpm2_return_codes {
 
 enum tpm2_algorithms {
        TPM2_ALG_SHA1           = 0x0004,
+       TPM2_ALG_KEYEDHASH      = 0x0008,
+       TPM2_ALG_SHA256         = 0x000B,
+       TPM2_ALG_NULL           = 0x0010
 };
 
 enum tpm2_command_codes {
@@ -97,6 +100,10 @@ enum tpm2_command_codes {
        TPM2_CC_SELF_TEST       = 0x0143,
        TPM2_CC_STARTUP         = 0x0144,
        TPM2_CC_SHUTDOWN        = 0x0145,
+       TPM2_CC_CREATE          = 0x0153,
+       TPM2_CC_LOAD            = 0x0157,
+       TPM2_CC_UNSEAL          = 0x015E,
+       TPM2_CC_FLUSH_CONTEXT   = 0x0165,
        TPM2_CC_GET_CAPABILITY  = 0x017A,
        TPM2_CC_GET_RANDOM      = 0x017B,
        TPM2_CC_PCR_READ        = 0x017E,
@@ -407,7 +414,7 @@ struct tpm_buf {
        u8 *data;
 };
 
-static inline void tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal)
+static inline int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal)
 {
        struct tpm_input_header *head;
 
@@ -527,6 +534,12 @@ static inline void tpm_add_ppi(struct tpm_chip *chip)
 int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf);
 int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash);
 int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max);
+int tpm2_seal_trusted(struct tpm_chip *chip,
+                     struct trusted_key_payload *payload,
+                     struct trusted_key_options *options);
+int tpm2_unseal_trusted(struct tpm_chip *chip,
+                       struct trusted_key_payload *payload,
+                       struct trusted_key_options *options);
 ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,
                        u32 *value, const char *desc);
 
index 011909a9be9627632bd1d1ec19230d5945d86719..bd7039fafa8aa7c34326e7a2270cd09e42837d81 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Intel Corporation
+ * Copyright (C) 2014, 2015 Intel Corporation
  *
  * Authors:
  * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
  */
 
 #include "tpm.h"
+#include <keys/trusted-type.h>
+
+enum tpm2_object_attributes {
+       TPM2_ATTR_USER_WITH_AUTH        = BIT(6),
+};
 
 struct tpm2_startup_in {
        __be16  startup_type;
@@ -380,6 +385,249 @@ static const struct tpm_input_header tpm2_get_tpm_pt_header = {
        .ordinal = cpu_to_be32(TPM2_CC_GET_CAPABILITY)
 };
 
+/**
+ * Append TPMS_AUTH_COMMAND to the buffer. The buffer must be allocated with
+ * tpm_buf_alloc().
+ *
+ * @param buf: an allocated tpm_buf instance
+ * @param nonce: the session nonce, may be NULL if not used
+ * @param nonce_len: the session nonce length, may be 0 if not used
+ * @param attributes: the session attributes
+ * @param hmac: the session HMAC or password, may be NULL if not used
+ * @param hmac_len: the session HMAC or password length, maybe 0 if not used
+ */
+static void tpm2_buf_append_auth(struct tpm_buf *buf, u32 session_handle,
+                                const u8 *nonce, u16 nonce_len,
+                                u8 attributes,
+                                const u8 *hmac, u16 hmac_len)
+{
+       tpm_buf_append_u32(buf, 9 + nonce_len + hmac_len);
+       tpm_buf_append_u32(buf, session_handle);
+       tpm_buf_append_u16(buf, nonce_len);
+
+       if (nonce && nonce_len)
+               tpm_buf_append(buf, nonce, nonce_len);
+
+       tpm_buf_append_u8(buf, attributes);
+       tpm_buf_append_u16(buf, hmac_len);
+
+       if (hmac && hmac_len)
+               tpm_buf_append(buf, hmac, hmac_len);
+}
+
+/**
+ * tpm2_seal_trusted() - seal a trusted key
+ * @chip_num: A specific chip number for the request or TPM_ANY_NUM
+ * @options: authentication values and other options
+ * @payload: the key data in clear and encrypted form
+ *
+ * Returns < 0 on error and 0 on success.
+ */
+int tpm2_seal_trusted(struct tpm_chip *chip,
+                     struct trusted_key_payload *payload,
+                     struct trusted_key_options *options)
+{
+       unsigned int blob_len;
+       struct tpm_buf buf;
+       int rc;
+
+       rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
+       if (rc)
+               return rc;
+
+       tpm_buf_append_u32(&buf, options->keyhandle);
+       tpm2_buf_append_auth(&buf, TPM2_RS_PW,
+                            NULL /* nonce */, 0,
+                            0 /* session_attributes */,
+                            options->keyauth /* hmac */,
+                            TPM_DIGEST_SIZE);
+
+       /* sensitive */
+       tpm_buf_append_u16(&buf, 4 + TPM_DIGEST_SIZE + payload->key_len);
+
+       tpm_buf_append_u16(&buf, TPM_DIGEST_SIZE);
+       tpm_buf_append(&buf, options->blobauth, TPM_DIGEST_SIZE);
+       tpm_buf_append_u16(&buf, payload->key_len);
+       tpm_buf_append(&buf, payload->key, payload->key_len);
+
+       /* public */
+       tpm_buf_append_u16(&buf, 14);
+
+       tpm_buf_append_u16(&buf, TPM2_ALG_KEYEDHASH);
+       tpm_buf_append_u16(&buf, TPM2_ALG_SHA256);
+       tpm_buf_append_u32(&buf, TPM2_ATTR_USER_WITH_AUTH);
+       tpm_buf_append_u16(&buf, 0); /* policy digest size */
+       tpm_buf_append_u16(&buf, TPM2_ALG_NULL);
+       tpm_buf_append_u16(&buf, 0);
+
+       /* outside info */
+       tpm_buf_append_u16(&buf, 0);
+
+       /* creation PCR */
+       tpm_buf_append_u32(&buf, 0);
+
+       if (buf.flags & TPM_BUF_OVERFLOW) {
+               rc = -E2BIG;
+               goto out;
+       }
+
+       rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "sealing data");
+       if (rc)
+               goto out;
+
+       blob_len = be32_to_cpup((__be32 *) &buf.data[TPM_HEADER_SIZE]);
+       if (blob_len > MAX_BLOB_SIZE) {
+               rc = -E2BIG;
+               goto out;
+       }
+
+       memcpy(payload->blob, &buf.data[TPM_HEADER_SIZE + 4], blob_len);
+       payload->blob_len = blob_len;
+
+out:
+       tpm_buf_destroy(&buf);
+
+       if (rc > 0)
+               rc = -EPERM;
+
+       return rc;
+}
+
+static int tpm2_load(struct tpm_chip *chip,
+                    struct trusted_key_payload *payload,
+                    struct trusted_key_options *options,
+                    u32 *blob_handle)
+{
+       struct tpm_buf buf;
+       unsigned int private_len;
+       unsigned int public_len;
+       unsigned int blob_len;
+       int rc;
+
+       private_len = be16_to_cpup((__be16 *) &payload->blob[0]);
+       if (private_len > (payload->blob_len - 2))
+               return -E2BIG;
+
+       public_len = be16_to_cpup((__be16 *) &payload->blob[2 + private_len]);
+       blob_len = private_len + public_len + 4;
+       if (blob_len > payload->blob_len)
+               return -E2BIG;
+
+       rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
+       if (rc)
+               return rc;
+
+       tpm_buf_append_u32(&buf, options->keyhandle);
+       tpm2_buf_append_auth(&buf, TPM2_RS_PW,
+                            NULL /* nonce */, 0,
+                            0 /* session_attributes */,
+                            options->keyauth /* hmac */,
+                            TPM_DIGEST_SIZE);
+
+       tpm_buf_append(&buf, payload->blob, blob_len);
+
+       if (buf.flags & TPM_BUF_OVERFLOW) {
+               rc = -E2BIG;
+               goto out;
+       }
+
+       rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "loading blob");
+       if (!rc)
+               *blob_handle = be32_to_cpup(
+                       (__be32 *) &buf.data[TPM_HEADER_SIZE]);
+
+out:
+       tpm_buf_destroy(&buf);
+
+       if (rc > 0)
+               rc = -EPERM;
+
+       return rc;
+}
+
+static void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
+{
+       struct tpm_buf buf;
+       int rc;
+
+       rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
+       if (rc) {
+               dev_warn(chip->pdev, "0x%08x was not flushed, out of memory\n",
+                        handle);
+               return;
+       }
+
+       tpm_buf_append_u32(&buf, handle);
+
+       rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "flushing context");
+       if (rc)
+               dev_warn(chip->pdev, "0x%08x was not flushed, rc=%d\n", handle,
+                        rc);
+
+       tpm_buf_destroy(&buf);
+}
+
+static int tpm2_unseal(struct tpm_chip *chip,
+                      struct trusted_key_payload *payload,
+                      struct trusted_key_options *options,
+                      u32 blob_handle)
+{
+       struct tpm_buf buf;
+       int rc;
+
+       rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
+       if (rc)
+               return rc;
+
+       tpm_buf_append_u32(&buf, blob_handle);
+       tpm2_buf_append_auth(&buf, TPM2_RS_PW,
+                            NULL /* nonce */, 0,
+                            0 /* session_attributes */,
+                            options->blobauth /* hmac */,
+                            TPM_DIGEST_SIZE);
+
+       rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "unsealing");
+       if (rc > 0)
+               rc = -EPERM;
+
+       if (!rc) {
+               payload->key_len = be16_to_cpup(
+                       (__be16 *) &buf.data[TPM_HEADER_SIZE + 4]);
+
+               memcpy(payload->key, &buf.data[TPM_HEADER_SIZE + 6],
+                      payload->key_len);
+       }
+
+       tpm_buf_destroy(&buf);
+       return rc;
+}
+
+/**
+ * tpm_unseal_trusted() - unseal a trusted key
+ * @chip_num: A specific chip number for the request or TPM_ANY_NUM
+ * @options: authentication values and other options
+ * @payload: the key data in clear and encrypted form
+ *
+ * Returns < 0 on error and 0 on success.
+ */
+int tpm2_unseal_trusted(struct tpm_chip *chip,
+                       struct trusted_key_payload *payload,
+                       struct trusted_key_options *options)
+{
+       u32 blob_handle;
+       int rc;
+
+       rc = tpm2_load(chip, payload, options, &blob_handle);
+       if (rc)
+               return rc;
+
+       rc = tpm2_unseal(chip, payload, options, blob_handle);
+
+       tpm2_flush_context(chip, blob_handle);
+
+       return rc;
+}
+
 /**
  * tpm2_get_tpm_pt() - get value of a TPM_CAP_TPM_PROPERTIES type property
  * @chip:              TPM chip to use.
index c91651f91687c1354d6031771d6f715eaa150745..f91ecd9d1bb19c2c241af39137521432492bff48 100644 (file)
@@ -16,7 +16,7 @@
 
 #define MIN_KEY_SIZE                   32
 #define MAX_KEY_SIZE                   128
-#define MAX_BLOB_SIZE                  320
+#define MAX_BLOB_SIZE                  512
 #define MAX_PCRINFO_SIZE               64
 
 struct trusted_key_payload {
index 8350c538b486cd217da6a067f4d008cbd3cdd8f2..706e63eea0800123d50dae79cfae327f6dacaa51 100644 (file)
@@ -30,6 +30,8 @@
 #define        TPM_ANY_NUM 0xFFFF
 
 struct tpm_chip;
+struct trusted_key_payload;
+struct trusted_key_options;
 
 struct tpm_class_ops {
        const u8 req_complete_mask;
@@ -46,11 +48,22 @@ struct tpm_class_ops {
 
 #if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE)
 
+extern int tpm_is_tpm2(u32 chip_num);
 extern int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf);
 extern int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash);
 extern int tpm_send(u32 chip_num, void *cmd, size_t buflen);
 extern int tpm_get_random(u32 chip_num, u8 *data, size_t max);
+extern int tpm_seal_trusted(u32 chip_num,
+                           struct trusted_key_payload *payload,
+                           struct trusted_key_options *options);
+extern int tpm_unseal_trusted(u32 chip_num,
+                             struct trusted_key_payload *payload,
+                             struct trusted_key_options *options);
 #else
+static inline int tpm_is_tpm2(u32 chip_num)
+{
+       return -ENODEV;
+}
 static inline int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) {
        return -ENODEV;
 }
@@ -63,5 +76,18 @@ static inline int tpm_send(u32 chip_num, void *cmd, size_t buflen) {
 static inline int tpm_get_random(u32 chip_num, u8 *data, size_t max) {
        return -ENODEV;
 }
+
+static inline int tpm_seal_trusted(u32 chip_num,
+                                  struct trusted_key_payload *payload,
+                                  struct trusted_key_options *options)
+{
+       return -ENODEV;
+}
+static inline int tpm_unseal_trusted(u32 chip_num,
+                                    struct trusted_key_payload *payload,
+                                    struct trusted_key_options *options)
+{
+       return -ENODEV;
+}
 #endif
 #endif