@@ -22,15 +22,23 @@
#define MAX_BLOB_SIZE 512
#define MAX_PCRINFO_SIZE 64
#define MAX_DIGEST_SIZE 64
+#define MAX_CREATION_DATA 412
+#define MAX_TK 76
struct trusted_key_payload {
struct rcu_head rcu;
unsigned int key_len;
unsigned int blob_len;
+ unsigned int creation_len;
+ unsigned int creation_hash_len;
+ unsigned int tk_len;
unsigned char migratable;
unsigned char old_format;
unsigned char key[MAX_KEY_SIZE + 1];
unsigned char blob[MAX_BLOB_SIZE];
+ unsigned char *creation;
+ unsigned char *creation_hash;
+ unsigned char *tk;
};
struct trusted_key_options {
@@ -7,5 +7,18 @@ TPMKey ::= SEQUENCE {
emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL,
parent INTEGER ({tpm2_key_parent}),
pubkey OCTET STRING ({tpm2_key_pub}),
- privkey OCTET STRING ({tpm2_key_priv})
+ privkey OCTET STRING ({tpm2_key_priv}),
+ ---
+ --- A TPM2B_CREATION_DATA struct as returned from the TPM2_Create command.
+ ---
+ creationData [1] EXPLICIT OCTET STRING OPTIONAL ({tpm2_key_creation_data}),
+ ---
+ --- A TPM2B_DIGEST of the creationHash as returned from the TPM2_Create
+ --- command.
+ ---
+ creationHash [2] EXPLICIT OCTET STRING OPTIONAL ({tpm2_key_creation_hash}),
+ ---
+ --- A TPMT_TK_CREATION ticket as returned from the TPM2_Create command.
+ ---
+ creationTk [3] EXPLICIT OCTET STRING OPTIONAL ({tpm2_key_creation_tk})
}
@@ -33,19 +33,54 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
u8 *src, u32 len)
{
const int SCRATCH_SIZE = PAGE_SIZE;
+ u8 *end = src + len;
u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
u8 *work = scratch, *work1;
u8 *end_work = scratch + SCRATCH_SIZE;
u8 *priv, *pub;
+ u8 *creation_data = NULL, *creation_hash = NULL, *creation_tk = NULL;
+ u16 creation_data_len, creation_hash_len = 0, creation_tk_len = 0;
u16 priv_len, pub_len;
+ int rc;
+
+ if (src + 2 > end)
+ return -EINVAL;
priv_len = get_unaligned_be16(src) + 2;
priv = src;
src += priv_len;
-
+ if (src + 2 > end)
+ return -EINVAL;
pub_len = get_unaligned_be16(src) + 2;
pub = src;
+ src += pub_len;
+ if (src + 2 > end)
+ return -EINVAL;
+ creation_data_len = get_unaligned_be16(src);
+ if (creation_data_len) {
+ creation_data_len += 2;
+ creation_data = src;
+ src += creation_data_len;
+ if (src + 2 > end)
+ return -EINVAL;
+
+ creation_hash_len = get_unaligned_be16(src) + 2;
+ creation_hash = src;
+ src += creation_hash_len;
+ if (src + 2 > end)
+ return -EINVAL;
+
+ /*
+ * The creation ticket (TPMT_TK_CREATION) consists of a 2 byte
+ * tag, 4 byte handle, and then a TPM2B_DIGEST, which is a 2
+ * byte length followed by data.
+ */
+ creation_tk_len = get_unaligned_be16(src + 6) + 8;
+ creation_tk = src;
+ if (creation_tk + creation_tk_len > end)
+ return -EINVAL;
+ }
if (!scratch)
return -ENOMEM;
@@ -63,26 +98,81 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
}
/*
- * Assume both octet strings will encode to a 2 byte definite length
+ * Assume each octet string will encode to a 2 byte definite length.
+ * Each optional octet string consumes one extra byte.
*
- * Note: For a well behaved TPM, this warning should never
- * trigger, so if it does there's something nefarious going on
+ * Note: For a well behaved TPM, this warning should never trigger, so
+ * if it does there's something nefarious going on
*/
- if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE,
- "BUG: scratch buffer is too small"))
- return -EINVAL;
+ if (WARN(work - scratch + pub_len + priv_len + creation_data_len +
+ creation_hash_len + creation_tk_len + (7 * 5) + 3 >
+ SCRATCH_SIZE,
+ "BUG: scratch buffer is too small")) {
+ rc = -EINVAL;
+ goto err;
+ }
work = asn1_encode_integer(work, end_work, options->keyhandle);
work = asn1_encode_octet_string(work, end_work, pub, pub_len);
work = asn1_encode_octet_string(work, end_work, priv, priv_len);
+ if (creation_data_len) {
+ u8 *scratch2 = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
+ u8 *work2;
+ u8 *end_work2 = scratch2 + SCRATCH_SIZE;
+
+ if (!scratch2) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ work2 = asn1_encode_octet_string(scratch2,
+ end_work2,
+ creation_data,
+ creation_data_len);
+
+ work = asn1_encode_tag(work,
+ end_work,
+ 1,
+ scratch2,
+ work2 - scratch2);
+
+ work2 = asn1_encode_octet_string(scratch2,
+ end_work2,
+ creation_hash,
+ creation_hash_len);
+
+ work = asn1_encode_tag(work,
+ end_work,
+ 2,
+ scratch2,
+ work2 - scratch2);
+
+ work2 = asn1_encode_octet_string(scratch2,
+ end_work2,
+ creation_tk,
+ creation_tk_len);
+
+ work = asn1_encode_tag(work,
+ end_work,
+ 3,
+ scratch2,
+ work2 - scratch2);
+
+ kfree(scratch2);
+ }
work1 = payload->blob;
work1 = asn1_encode_sequence(work1, work1 + sizeof(payload->blob),
scratch, work - scratch);
- if (WARN(IS_ERR(work1), "BUG: ASN.1 encoder failed"))
- return PTR_ERR(work1);
+ if (WARN(IS_ERR(work1), "BUG: ASN.1 encoder failed")) {
+ rc = PTR_ERR(work1);
+ goto err;
+ }
return work1 - payload->blob;
+err:
+ kfree(scratch);
+ return rc;
}
struct tpm2_key_context {
@@ -91,15 +181,21 @@ struct tpm2_key_context {
u32 pub_len;
const u8 *priv;
u32 priv_len;
+ const u8 *creation_data;
+ u32 creation_data_len;
+ const u8 *creation_hash;
+ u32 creation_hash_len;
+ const u8 *creation_tk;
+ u32 creation_tk_len;
};
static int tpm2_key_decode(struct trusted_key_payload *payload,
- struct trusted_key_options *options,
- u8 **buf)
+ struct trusted_key_options *options)
{
+ u64 data_len;
int ret;
struct tpm2_key_context ctx;
- u8 *blob;
+ u8 *blob, *buf;
memset(&ctx, 0, sizeof(ctx));
@@ -108,21 +204,57 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
if (ret < 0)
return ret;
- if (ctx.priv_len + ctx.pub_len > MAX_BLOB_SIZE)
+ data_len = ctx.priv_len + ctx.pub_len + ctx.creation_data_len +
+ ctx.creation_hash_len + ctx.creation_tk_len;
+
+ if (data_len > MAX_BLOB_SIZE)
return -EINVAL;
- blob = kmalloc(ctx.priv_len + ctx.pub_len + 4, GFP_KERNEL);
- if (!blob)
+ buf = kmalloc(data_len + 4, GFP_KERNEL);
+ if (!buf)
return -ENOMEM;
- *buf = blob;
+ blob = buf;
options->keyhandle = ctx.parent;
memcpy(blob, ctx.priv, ctx.priv_len);
blob += ctx.priv_len;
memcpy(blob, ctx.pub, ctx.pub_len);
+ blob += ctx.pub_len;
+ if (ctx.creation_data_len) {
+ memcpy(blob, ctx.creation_data, ctx.creation_data_len);
+ blob += ctx.creation_data_len;
+ }
+
+ if (ctx.creation_hash_len) {
+ memcpy(blob, ctx.creation_hash, ctx.creation_hash_len);
+ blob += ctx.creation_hash_len;
+ }
+
+ if (ctx.creation_tk_len) {
+ memcpy(blob, ctx.creation_tk, ctx.creation_tk_len);
+ blob += ctx.creation_tk_len;
+ }
+
+ /*
+ * Copy the buffer back into the payload blob since the creation
+ * info will be used after loading.
+ */
+ payload->blob_len = blob - buf;
+ memcpy(payload->blob, buf, payload->blob_len);
+ if (ctx.creation_data_len) {
+ payload->creation = payload->blob + ctx.priv_len + ctx.pub_len;
+ payload->creation_len = ctx.creation_data_len;
+ payload->creation_hash = payload->creation + ctx.creation_data_len;
+ payload->creation_hash_len = ctx.creation_hash_len;
+ payload->tk = payload->creation_hash +
+ payload->creation_hash_len;
+
+ payload->tk_len = ctx.creation_tk_len;
+ }
+ kfree(buf);
return 0;
}
@@ -185,6 +317,42 @@ int tpm2_key_priv(void *context, size_t hdrlen,
return 0;
}
+int tpm2_key_creation_data(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct tpm2_key_context *ctx = context;
+
+ ctx->creation_data = value;
+ ctx->creation_data_len = vlen;
+
+ return 0;
+}
+
+int tpm2_key_creation_hash(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct tpm2_key_context *ctx = context;
+
+ ctx->creation_hash = value;
+ ctx->creation_hash_len = vlen;
+
+ return 0;
+}
+
+int tpm2_key_creation_tk(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct tpm2_key_context *ctx = context;
+
+ ctx->creation_tk = value;
+ ctx->creation_tk_len = vlen;
+
+ return 0;
+}
+
/**
* tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
*
@@ -229,6 +397,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
struct trusted_key_options *options)
{
int blob_len = 0;
+ unsigned int offset;
struct tpm_buf buf;
u32 hash;
u32 flags;
@@ -317,13 +486,14 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
rc = -E2BIG;
goto out;
}
- if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 4 + blob_len) {
+ offset = TPM_HEADER_SIZE + 4;
+ if (tpm_buf_length(&buf) < offset + blob_len) {
rc = -EFAULT;
goto out;
}
blob_len = tpm2_key_encode(payload, options,
- &buf.data[TPM_HEADER_SIZE + 4],
+ &buf.data[offset],
blob_len);
out:
@@ -370,13 +540,11 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
int rc;
u32 attrs;
- rc = tpm2_key_decode(payload, options, &blob);
- if (rc) {
- /* old form */
- blob = payload->blob;
+ rc = tpm2_key_decode(payload, options);
+ if (rc)
payload->old_format = 1;
- }
+ blob = payload->blob;
/* new format carries keyhandle but old format doesn't */
if (!options->keyhandle)
return -EINVAL;
@@ -433,8 +601,6 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
(__be32 *) &buf.data[TPM_HEADER_SIZE]);
out:
- if (blob != payload->blob)
- kfree(blob);
tpm_buf_destroy(&buf);
if (rc > 0)