diff mbox series

[4/8] security: keys: trusted: use ASN.1 tpm2 key format for the blobs

Message ID 1575781831.14069.13.camel@HansenPartnership.com (mailing list archive)
State New, archived
Headers show
Series Fix TPM 2.0 trusted keys | expand

Commit Message

James Bottomley Dec. 8, 2019, 5:10 a.m. UTC
Modify the tpm2 key format blob output to export and import in the
ASN.1 form for tpm2 sealed object keys.  For compatibility with prior
trusted keys, the importer will also accept two tpm2b quantities
representing the public and private parts of the key.  However, the
export via keyctl pipe will only output the ASN.1 format.

The benefit of the ASN.1 format is that it's a standard and thus the
exported key can be used by userspace tools.  The format includes
policy specifications, thus it gets us out of having to construct
policy handles in userspace and the format includes the parent meaning
you don't have to keep passing it in each time.

This patch only implements basic handling for the ASN.1 format, so
keys with passwords but no policy.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
---
 security/keys/trusted-keys/Makefile       |   2 +-
 security/keys/trusted-keys/tpm2key.asn1   |  23 ++++
 security/keys/trusted-keys/trusted_tpm1.c |   2 +-
 security/keys/trusted-keys/trusted_tpm2.c | 170 +++++++++++++++++++++++++++++-
 4 files changed, 190 insertions(+), 7 deletions(-)
 create mode 100644 security/keys/trusted-keys/tpm2key.asn1

Comments

David Woodhouse Dec. 9, 2019, 10:04 a.m. UTC | #1
On Sat, 2019-12-07 at 21:10 -0800, James Bottomley wrote:
> Modify the tpm2 key format blob output to export and import in the
> ASN.1 form for tpm2 sealed object keys.  For compatibility with prior
> trusted keys, the importer will also accept two tpm2b quantities
> representing the public and private parts of the key.  However, the
> export via keyctl pipe will only output the ASN.1 format.

You still have a tpm2_key_encode() function which spits out the raw
private/public blobs each prefixed with a length word. What's that
still used for?

> The benefit of the ASN.1 format is that it's a standard

We should probably make that true. Did we even get as far as writing up
an RFC-style description of the ASN.1? 

>  and thus the
> exported key can be used by userspace tools.  The format includes
> policy specifications, thus it gets us out of having to construct
> policy handles in userspace and the format includes the parent meaning
> you don't have to keep passing it in each time.
> 
> This patch only implements basic handling for the ASN.1 format, so
> keys with passwords but no policy.

... but doesn't bail out with an error when it sees something it
doesn't yet understand? Including the 'secret' field which is only
relevant for importable keys, etc.

> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
> ---
>  security/keys/trusted-keys/Makefile       |   2 +-
>  security/keys/trusted-keys/tpm2key.asn1   |  23 ++++
>  security/keys/trusted-keys/trusted_tpm1.c |   2 +-
>  security/keys/trusted-keys/trusted_tpm2.c | 170 +++++++++++++++++++++++++++++-
>  4 files changed, 190 insertions(+), 7 deletions(-)
>  create mode 100644 security/keys/trusted-keys/tpm2key.asn1
> 
> diff --git a/security/keys/trusted-keys/Makefile b/security/keys/trusted-keys/Makefile
> index 7b73cebbb378..e0198641eff2 100644
> --- a/security/keys/trusted-keys/Makefile
> +++ b/security/keys/trusted-keys/Makefile
> @@ -5,4 +5,4 @@
>  
>  obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
>  trusted-y += trusted_tpm1.o
> -trusted-y += trusted_tpm2.o
> +trusted-y += trusted_tpm2.o tpm2key.asn1.o
> diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1
> new file mode 100644
> index 000000000000..1851b7c80f08
> --- /dev/null
> +++ b/security/keys/trusted-keys/tpm2key.asn1
> @@ -0,0 +1,23 @@
> +---
> +--- Note: This isn't quite the definition in the standard
> +---       However, the Linux asn.1 parser doesn't understand
> +---       [2] EXPLICIT SEQUENCE OF OPTIONAL
> +---       So there's an extra intermediate TPMPolicySequence
> +---       definition to work around this

At the very least we should prod David with a pointy stick on that
topic, rather than quietly working around it.


> +
> +TPMKey ::= SEQUENCE {
> +	type		OBJECT IDENTIFIER ({tpmkey_type}),
> +	emptyAuth	[0] EXPLICIT BOOLEAN OPTIONAL,
> +	policy		[1] EXPLICIT TPMPolicySequence OPTIONAL,
> +	secret		[2] EXPLICIT OCTET STRING OPTIONAL,
> +	parent		INTEGER ({tpmkey_parent}),
> +	pubkey		OCTET STRING ({tpmkey_pub}),
> +	privkey		OCTET STRING ({tpmkey_priv})
> +	}
> +
> +TPMPolicySequence ::= SEQUENCE OF TPMPolicy
> +
> +TPMPolicy ::= SEQUENCE {
> +	commandCode		[0] EXPLICIT INTEGER,
> +	commandPolicy		[1] EXPLICIT OCTET STRING
> +	}
> diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
> index d2c5ec1e040b..d744a0d1cb89 100644
> --- a/security/keys/trusted-keys/trusted_tpm1.c
> +++ b/security/keys/trusted-keys/trusted_tpm1.c
> @@ -991,7 +991,7 @@ static int trusted_instantiate(struct key *key,
>  		goto out;
>  	}
>  
> -	if (!options->keyhandle) {
> +	if (!options->keyhandle && !tpm2) {
>  		ret = -EINVAL;
>  		goto out;
>  	}
> diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
> index 08ec7f48f01d..4efc7b64d1cd 100644
> --- a/security/keys/trusted-keys/trusted_tpm2.c
> +++ b/security/keys/trusted-keys/trusted_tpm2.c
> @@ -4,6 +4,8 @@
>   * Copyright (C) 2014 Intel Corporation
>   */
>  
> +#include <linux/asn1_encoder.h>
> +#include <linux/oid_registry.h>
>  #include <linux/string.h>
>  #include <linux/err.h>
>  #include <linux/tpm.h>
> @@ -12,6 +14,10 @@
>  #include <keys/trusted-type.h>
>  #include <keys/trusted_tpm.h>
>  
> +#include <asm/unaligned.h>
> +
> +#include "tpm2key.asn1.h"
> +
>  static struct tpm2_hash tpm2_hash_map[] = {
>  	{HASH_ALGO_SHA1, TPM_ALG_SHA1},
>  	{HASH_ALGO_SHA256, TPM_ALG_SHA256},
> @@ -20,6 +26,141 @@ static struct tpm2_hash tpm2_hash_map[] = {
>  	{HASH_ALGO_SM3_256, TPM_ALG_SM3_256},
>  };
>  
> +static u32 tpm2key_oid[] = { 2,23,133,10,1,5 };
> +
> +static int tpm2_key_encode(struct trusted_key_payload *payload,
> +			   struct trusted_key_options *options,
> +			   u8 *src, u32 len)
> +{
> +	u8 *scratch = kmalloc(PAGE_SIZE, GFP_KERNEL);
> +	u8 *work = scratch, *work1;
> +	u8 *priv, *pub;
> +	u16 priv_len, pub_len;
> +
> +	priv_len = get_unaligned_be16(src);
> +	src += 2;
> +	priv = src;
> +	src += priv_len;
> +	pub_len = get_unaligned_be16(src);
> +	src += 2;
> +	pub = src;
> +
> +	if (!scratch)
> +		return -ENOMEM;
> +
> +	asn1_encode_oid(&work, tpm2key_oid, asn1_oid_len(tpm2key_oid));
> +	if (options->blobauth[0] == 0) {
> +		unsigned char bool[3], *w = bool;
> +		/* tag 0 is emptyAuth */
> +		asn1_encode_boolean(&w, true);
> +		asn1_encode_tag(&work, 0, bool, w - bool);
> +	}
> +	asn1_encode_integer(&work, options->keyhandle);
> +	asn1_encode_octet_string(&work, pub, pub_len);
> +	asn1_encode_octet_string(&work, priv, priv_len);
> +
> +	work1 = payload->blob;
> +	asn1_encode_sequence(&work1, scratch, work - scratch);
> +
> +	return work1 - payload->blob;
> +}

I still don't like the lack of overflow protection here, one layer up
from the underlying encoding APIs I already commented on.


> +struct tpm2key_context {
> +	u32 parent;
> +	const u8 *pub;
> +	u32 pub_len;
> +	const u8 *priv;
> +	u32 priv_len;
> +};
> +
> +static int tpm2_key_decode(struct trusted_key_payload *payload,
> +			   struct trusted_key_options *options,
> +			   u8 **buf)
> +{
> +	int ret;
> +	struct tpm2key_context ctx;
> +	u8 *blob;
> +
> +	ret = asn1_ber_decoder(&tpm2key_decoder, &ctx, payload->blob,
> +			       payload->blob_len);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (ctx.priv_len + ctx.pub_len > MAX_BLOB_SIZE)
> +		return -EINVAL;
> +
> +	blob = kmalloc(ctx.priv_len + ctx.pub_len + 4, GFP_KERNEL);
> +	if (!blob)
> +		return -ENOMEM;
> +
> +	*buf = blob;
> +	options->keyhandle = ctx.parent;
> +	put_unaligned_be16(ctx.priv_len, blob);
> +	blob += 2;
> +	memcpy(blob, ctx.priv, ctx.priv_len);
> +	blob += ctx.priv_len;
> +	put_unaligned_be16(ctx.pub_len, blob);
> +	blob += 2;
> +	memcpy(blob, ctx.pub, ctx.pub_len);
> 

Hm, do we really have to create this legacy form here and pass it
around? Can't we change whatever consumes this?

> +	return 0;
> +}
> +
> +int tpmkey_parent(void *context, size_t hdrlen,
> +		  unsigned char tag,
> +		  const void *value, size_t vlen)
> +{
> +	struct tpm2key_context *ctx = context;
> +	const u8 *v = value;
> +	int i;
> +
> +	ctx->parent = 0;
> +	for (i = 0; i < vlen; i++) {
> +		ctx->parent <<= 8;
> +		ctx->parent |= v[i];
> +	}
> +	return 0;
> +}
> +
> +int tpmkey_type(void *context, size_t hdrlen,
> +		unsigned char tag,
> +		const void *value, size_t vlen)
> +{
> +	enum OID oid = look_up_OID(value, vlen);
> +
> +	if (oid != OID_TPMSealedData) {
> +		char buffer[50];
> +
> +		sprint_oid(value, vlen, buffer, sizeof(buffer));
> +		pr_debug("OID is \"%s\" which is not TPMSealedData\n",
> +			 buffer);
> +		return -EINVAL;
> +	}
> +	return 0;
> +}
> +
> +int tpmkey_pub(void *context, size_t hdrlen,
> +	       unsigned char tag,
> +	       const void *value, size_t vlen)
> +{
> +	struct tpm2key_context *ctx = context;
> +
> +	ctx->pub = value;
> +	ctx->pub_len = vlen;
> +	return 0;
> +}
> +
> +int tpmkey_priv(void *context, size_t hdrlen,
> +		unsigned char tag,
> +		const void *value, size_t vlen)
> +{
> +	struct tpm2key_context *ctx = context;
> +
> +	ctx->priv = value;
> +	ctx->priv_len = vlen;
> +	return 0;
> +}
> +
>  /**
>   * tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
>   *
> @@ -79,6 +220,9 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
>  	if (i == ARRAY_SIZE(tpm2_hash_map))
>  		return -EINVAL;
>  
> +	if (!options->keyhandle)
> +		return -EINVAL;
> +
>  	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
>  	if (rc)
>  		return rc;
> @@ -144,8 +288,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
>  		goto out;
>  	}
>  
> -	memcpy(payload->blob, &buf.data[TPM_HEADER_SIZE + 4], blob_len);
> -	payload->blob_len = blob_len;
> +	payload->blob_len =
> +		tpm2_key_encode(payload, options,
> +				&buf.data[TPM_HEADER_SIZE + 4],
> +				blob_len);
>  
>  out:
>  	tpm_buf_destroy(&buf);
> @@ -156,6 +302,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
>  		else
>  			rc = -EPERM;
>  	}
> +	if (payload->blob_len < 0)
> +		return payload->blob_len;
>  
>  	return rc;
>  }
> @@ -182,13 +330,23 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
>  	unsigned int private_len;
>  	unsigned int public_len;
>  	unsigned int blob_len;
> +	u8 *blob;
>  	int rc;
>  
> -	private_len = be16_to_cpup((__be16 *) &payload->blob[0]);
> +	rc = tpm2_key_decode(payload, options, &blob);
> +	if (rc)
> +		/* old form */
> +		blob = payload->blob;
> +
> +	/* new format carries keyhandle but old format doesn't */
> +	if (!options->keyhandle)
> +		return -EINVAL;
> +
> +	private_len = be16_to_cpup((__be16 *) &blob[0]);
>  	if (private_len > (payload->blob_len - 2))
>  		return -E2BIG;
>  
> -	public_len = be16_to_cpup((__be16 *) &payload->blob[2 + private_len]);
> +	public_len = be16_to_cpup((__be16 *) &blob[2 + private_len]);
>  	blob_len = private_len + public_len + 4;
>  	if (blob_len > payload->blob_len)
>  		return -E2BIG;
> @@ -204,7 +362,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
>  			     options->keyauth /* hmac */,
>  			     TPM_DIGEST_SIZE);
>  
> -	tpm_buf_append(&buf, payload->blob, blob_len);
> +	tpm_buf_append(&buf, blob, blob_len);
>  
>  	if (buf.flags & TPM_BUF_OVERFLOW) {
>  		rc = -E2BIG;
> @@ -217,6 +375,8 @@ 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)
James Bottomley Dec. 9, 2019, 4:31 p.m. UTC | #2
On Mon, 2019-12-09 at 10:04 +0000, David Woodhouse wrote:
> On Sat, 2019-12-07 at 21:10 -0800, James Bottomley wrote:
> > Modify the tpm2 key format blob output to export and import in the
> > ASN.1 form for tpm2 sealed object keys.  For compatibility with
> > prior trusted keys, the importer will also accept two tpm2b
> > quantities representing the public and private parts of the
> > key.  However, the export via keyctl pipe will only output the
> > ASN.1 format.
> 
> You still have a tpm2_key_encode() function which spits out the raw
> private/public blobs each prefixed with a length word. What's that
> still used for?

It's the TPM2B format for input of the keys to the TPM.

> > The benefit of the ASN.1 format is that it's a standard
> 
> We should probably make that true. Did we even get as far as writing
> up an RFC-style description of the ASN.1? 

No ... I was going to ask you if you'd made a start.  I take it linux-
integrity is as good a list as any to begin this on.  I'll cc the
openssl_tpm2_engine list ... are there any others?

> >  and thus the
> > exported key can be used by userspace tools.  The format includes
> > policy specifications, thus it gets us out of having to construct
> > policy handles in userspace and the format includes the parent
> > meaning you don't have to keep passing it in each time.
> > 
> > This patch only implements basic handling for the ASN.1 format, so
> > keys with passwords but no policy.
> 
> ... but doesn't bail out with an error when it sees something it
> doesn't yet understand? Including the 'secret' field which is only
> relevant for importable keys, etc.

I hadn't actually got around to defining importable blobs ... but I
think keeping the same OID and adding the shared import secret in [2]
would be good enough?  In which case the TPM will error out for us
because it won't be able to unwrap the private blob.  If keyctl were
capable of returning information about the source of the problem it
might be worth checking, but while it can't, I think getting -EINVAL
from the TPM is as good as putting a check returning -EINVAL.

> > Signed-off-by: James Bottomley
> > <James.Bottomley@HansenPartnership.com>
> > ---
> >  security/keys/trusted-keys/Makefile       |   2 +-
> >  security/keys/trusted-keys/tpm2key.asn1   |  23 ++++
> >  security/keys/trusted-keys/trusted_tpm1.c |   2 +-
> >  security/keys/trusted-keys/trusted_tpm2.c | 170
> > +++++++++++++++++++++++++++++-
> >  4 files changed, 190 insertions(+), 7 deletions(-)
> >  create mode 100644 security/keys/trusted-keys/tpm2key.asn1
> > 
> > diff --git a/security/keys/trusted-keys/Makefile
> > b/security/keys/trusted-keys/Makefile
> > index 7b73cebbb378..e0198641eff2 100644
> > --- a/security/keys/trusted-keys/Makefile
> > +++ b/security/keys/trusted-keys/Makefile
> > @@ -5,4 +5,4 @@
> >  
> >  obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
> >  trusted-y += trusted_tpm1.o
> > -trusted-y += trusted_tpm2.o
> > +trusted-y += trusted_tpm2.o tpm2key.asn1.o
> > diff --git a/security/keys/trusted-keys/tpm2key.asn1
> > b/security/keys/trusted-keys/tpm2key.asn1
> > new file mode 100644
> > index 000000000000..1851b7c80f08
> > --- /dev/null
> > +++ b/security/keys/trusted-keys/tpm2key.asn1
> > @@ -0,0 +1,23 @@
> > +---
> > +--- Note: This isn't quite the definition in the standard
> > +---       However, the Linux asn.1 parser doesn't understand
> > +---       [2] EXPLICIT SEQUENCE OF OPTIONAL
> > +---       So there's an extra intermediate TPMPolicySequence
> > +---       definition to work around this
> 
> At the very least we should prod David with a pointy stick on that
> topic, rather than quietly working around it.

OK, I'll add him to the cc list on this one.

[...]
> > +static int tpm2_key_encode(struct trusted_key_payload *payload,
> > +			   struct trusted_key_options *options,
> > +			   u8 *src, u32 len)
> > +{
> > +	u8 *scratch = kmalloc(PAGE_SIZE, GFP_KERNEL);
> > +	u8 *work = scratch, *work1;
> > +	u8 *priv, *pub;
> > +	u16 priv_len, pub_len;
> > +
> > +	priv_len = get_unaligned_be16(src);
> > +	src += 2;
> > +	priv = src;
> > +	src += priv_len;
> > +	pub_len = get_unaligned_be16(src);
> > +	src += 2;
> > +	pub = src;
> > +
> > +	if (!scratch)
> > +		return -ENOMEM;
> > +
> > +	asn1_encode_oid(&work, tpm2key_oid,
> > asn1_oid_len(tpm2key_oid));
> > +	if (options->blobauth[0] == 0) {
> > +		unsigned char bool[3], *w = bool;
> > +		/* tag 0 is emptyAuth */
> > +		asn1_encode_boolean(&w, true);
> > +		asn1_encode_tag(&work, 0, bool, w - bool);
> > +	}
> > +	asn1_encode_integer(&work, options->keyhandle);
> > +	asn1_encode_octet_string(&work, pub, pub_len);
> > +	asn1_encode_octet_string(&work, priv, priv_len);
> > +
> > +	work1 = payload->blob;
> > +	asn1_encode_sequence(&work1, scratch, work - scratch);
> > +
> > +	return work1 - payload->blob;
> > +}
> 
> I still don't like the lack of overflow protection here, one layer up
> from the underlying encoding APIs I already commented on.

Fixed.

> > +struct tpm2key_context {
> > +	u32 parent;
> > +	const u8 *pub;
> > +	u32 pub_len;
> > +	const u8 *priv;
> > +	u32 priv_len;
> > +};
> > +
> > +static int tpm2_key_decode(struct trusted_key_payload *payload,
> > +			   struct trusted_key_options *options,
> > +			   u8 **buf)
> > +{
> > +	int ret;
> > +	struct tpm2key_context ctx;
> > +	u8 *blob;
> > +
> > +	ret = asn1_ber_decoder(&tpm2key_decoder, &ctx, payload-
> > >blob,
> > +			       payload->blob_len);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	if (ctx.priv_len + ctx.pub_len > MAX_BLOB_SIZE)
> > +		return -EINVAL;
> > +
> > +	blob = kmalloc(ctx.priv_len + ctx.pub_len + 4,
> > GFP_KERNEL);
> > +	if (!blob)
> > +		return -ENOMEM;
> > +
> > +	*buf = blob;
> > +	options->keyhandle = ctx.parent;
> > +	put_unaligned_be16(ctx.priv_len, blob);
> > +	blob += 2;
> > +	memcpy(blob, ctx.priv, ctx.priv_len);
> > +	blob += ctx.priv_len;
> > +	put_unaligned_be16(ctx.pub_len, blob);
> > +	blob += 2;
> > +	memcpy(blob, ctx.pub, ctx.pub_len);
> > 
> 
> Hm, do we really have to create this legacy form here and pass it
> around? Can't we change whatever consumes this?

It's not a legacy form ... it's the intrinsic TPM2B form the TPM 2.0
uses so it's the natural destination form for the conversion.

James
diff mbox series

Patch

diff --git a/security/keys/trusted-keys/Makefile b/security/keys/trusted-keys/Makefile
index 7b73cebbb378..e0198641eff2 100644
--- a/security/keys/trusted-keys/Makefile
+++ b/security/keys/trusted-keys/Makefile
@@ -5,4 +5,4 @@ 
 
 obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
 trusted-y += trusted_tpm1.o
-trusted-y += trusted_tpm2.o
+trusted-y += trusted_tpm2.o tpm2key.asn1.o
diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1
new file mode 100644
index 000000000000..1851b7c80f08
--- /dev/null
+++ b/security/keys/trusted-keys/tpm2key.asn1
@@ -0,0 +1,23 @@ 
+---
+--- Note: This isn't quite the definition in the standard
+---       However, the Linux asn.1 parser doesn't understand
+---       [2] EXPLICIT SEQUENCE OF OPTIONAL
+---       So there's an extra intermediate TPMPolicySequence
+---       definition to work around this
+
+TPMKey ::= SEQUENCE {
+	type		OBJECT IDENTIFIER ({tpmkey_type}),
+	emptyAuth	[0] EXPLICIT BOOLEAN OPTIONAL,
+	policy		[1] EXPLICIT TPMPolicySequence OPTIONAL,
+	secret		[2] EXPLICIT OCTET STRING OPTIONAL,
+	parent		INTEGER ({tpmkey_parent}),
+	pubkey		OCTET STRING ({tpmkey_pub}),
+	privkey		OCTET STRING ({tpmkey_priv})
+	}
+
+TPMPolicySequence ::= SEQUENCE OF TPMPolicy
+
+TPMPolicy ::= SEQUENCE {
+	commandCode		[0] EXPLICIT INTEGER,
+	commandPolicy		[1] EXPLICIT OCTET STRING
+	}
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index d2c5ec1e040b..d744a0d1cb89 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -991,7 +991,7 @@  static int trusted_instantiate(struct key *key,
 		goto out;
 	}
 
-	if (!options->keyhandle) {
+	if (!options->keyhandle && !tpm2) {
 		ret = -EINVAL;
 		goto out;
 	}
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 08ec7f48f01d..4efc7b64d1cd 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -4,6 +4,8 @@ 
  * Copyright (C) 2014 Intel Corporation
  */
 
+#include <linux/asn1_encoder.h>
+#include <linux/oid_registry.h>
 #include <linux/string.h>
 #include <linux/err.h>
 #include <linux/tpm.h>
@@ -12,6 +14,10 @@ 
 #include <keys/trusted-type.h>
 #include <keys/trusted_tpm.h>
 
+#include <asm/unaligned.h>
+
+#include "tpm2key.asn1.h"
+
 static struct tpm2_hash tpm2_hash_map[] = {
 	{HASH_ALGO_SHA1, TPM_ALG_SHA1},
 	{HASH_ALGO_SHA256, TPM_ALG_SHA256},
@@ -20,6 +26,141 @@  static struct tpm2_hash tpm2_hash_map[] = {
 	{HASH_ALGO_SM3_256, TPM_ALG_SM3_256},
 };
 
+static u32 tpm2key_oid[] = { 2,23,133,10,1,5 };
+
+static int tpm2_key_encode(struct trusted_key_payload *payload,
+			   struct trusted_key_options *options,
+			   u8 *src, u32 len)
+{
+	u8 *scratch = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	u8 *work = scratch, *work1;
+	u8 *priv, *pub;
+	u16 priv_len, pub_len;
+
+	priv_len = get_unaligned_be16(src);
+	src += 2;
+	priv = src;
+	src += priv_len;
+	pub_len = get_unaligned_be16(src);
+	src += 2;
+	pub = src;
+
+	if (!scratch)
+		return -ENOMEM;
+
+	asn1_encode_oid(&work, tpm2key_oid, asn1_oid_len(tpm2key_oid));
+	if (options->blobauth[0] == 0) {
+		unsigned char bool[3], *w = bool;
+		/* tag 0 is emptyAuth */
+		asn1_encode_boolean(&w, true);
+		asn1_encode_tag(&work, 0, bool, w - bool);
+	}
+	asn1_encode_integer(&work, options->keyhandle);
+	asn1_encode_octet_string(&work, pub, pub_len);
+	asn1_encode_octet_string(&work, priv, priv_len);
+
+	work1 = payload->blob;
+	asn1_encode_sequence(&work1, scratch, work - scratch);
+
+	return work1 - payload->blob;
+}
+
+struct tpm2key_context {
+	u32 parent;
+	const u8 *pub;
+	u32 pub_len;
+	const u8 *priv;
+	u32 priv_len;
+};
+
+static int tpm2_key_decode(struct trusted_key_payload *payload,
+			   struct trusted_key_options *options,
+			   u8 **buf)
+{
+	int ret;
+	struct tpm2key_context ctx;
+	u8 *blob;
+
+	ret = asn1_ber_decoder(&tpm2key_decoder, &ctx, payload->blob,
+			       payload->blob_len);
+	if (ret < 0)
+		return ret;
+
+	if (ctx.priv_len + ctx.pub_len > MAX_BLOB_SIZE)
+		return -EINVAL;
+
+	blob = kmalloc(ctx.priv_len + ctx.pub_len + 4, GFP_KERNEL);
+	if (!blob)
+		return -ENOMEM;
+
+	*buf = blob;
+	options->keyhandle = ctx.parent;
+	put_unaligned_be16(ctx.priv_len, blob);
+	blob += 2;
+	memcpy(blob, ctx.priv, ctx.priv_len);
+	blob += ctx.priv_len;
+	put_unaligned_be16(ctx.pub_len, blob);
+	blob += 2;
+	memcpy(blob, ctx.pub, ctx.pub_len);
+
+	return 0;
+}
+
+int tpmkey_parent(void *context, size_t hdrlen,
+		  unsigned char tag,
+		  const void *value, size_t vlen)
+{
+	struct tpm2key_context *ctx = context;
+	const u8 *v = value;
+	int i;
+
+	ctx->parent = 0;
+	for (i = 0; i < vlen; i++) {
+		ctx->parent <<= 8;
+		ctx->parent |= v[i];
+	}
+	return 0;
+}
+
+int tpmkey_type(void *context, size_t hdrlen,
+		unsigned char tag,
+		const void *value, size_t vlen)
+{
+	enum OID oid = look_up_OID(value, vlen);
+
+	if (oid != OID_TPMSealedData) {
+		char buffer[50];
+
+		sprint_oid(value, vlen, buffer, sizeof(buffer));
+		pr_debug("OID is \"%s\" which is not TPMSealedData\n",
+			 buffer);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+int tpmkey_pub(void *context, size_t hdrlen,
+	       unsigned char tag,
+	       const void *value, size_t vlen)
+{
+	struct tpm2key_context *ctx = context;
+
+	ctx->pub = value;
+	ctx->pub_len = vlen;
+	return 0;
+}
+
+int tpmkey_priv(void *context, size_t hdrlen,
+		unsigned char tag,
+		const void *value, size_t vlen)
+{
+	struct tpm2key_context *ctx = context;
+
+	ctx->priv = value;
+	ctx->priv_len = vlen;
+	return 0;
+}
+
 /**
  * tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
  *
@@ -79,6 +220,9 @@  int tpm2_seal_trusted(struct tpm_chip *chip,
 	if (i == ARRAY_SIZE(tpm2_hash_map))
 		return -EINVAL;
 
+	if (!options->keyhandle)
+		return -EINVAL;
+
 	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
 	if (rc)
 		return rc;
@@ -144,8 +288,10 @@  int tpm2_seal_trusted(struct tpm_chip *chip,
 		goto out;
 	}
 
-	memcpy(payload->blob, &buf.data[TPM_HEADER_SIZE + 4], blob_len);
-	payload->blob_len = blob_len;
+	payload->blob_len =
+		tpm2_key_encode(payload, options,
+				&buf.data[TPM_HEADER_SIZE + 4],
+				blob_len);
 
 out:
 	tpm_buf_destroy(&buf);
@@ -156,6 +302,8 @@  int tpm2_seal_trusted(struct tpm_chip *chip,
 		else
 			rc = -EPERM;
 	}
+	if (payload->blob_len < 0)
+		return payload->blob_len;
 
 	return rc;
 }
@@ -182,13 +330,23 @@  static int tpm2_load_cmd(struct tpm_chip *chip,
 	unsigned int private_len;
 	unsigned int public_len;
 	unsigned int blob_len;
+	u8 *blob;
 	int rc;
 
-	private_len = be16_to_cpup((__be16 *) &payload->blob[0]);
+	rc = tpm2_key_decode(payload, options, &blob);
+	if (rc)
+		/* old form */
+		blob = payload->blob;
+
+	/* new format carries keyhandle but old format doesn't */
+	if (!options->keyhandle)
+		return -EINVAL;
+
+	private_len = be16_to_cpup((__be16 *) &blob[0]);
 	if (private_len > (payload->blob_len - 2))
 		return -E2BIG;
 
-	public_len = be16_to_cpup((__be16 *) &payload->blob[2 + private_len]);
+	public_len = be16_to_cpup((__be16 *) &blob[2 + private_len]);
 	blob_len = private_len + public_len + 4;
 	if (blob_len > payload->blob_len)
 		return -E2BIG;
@@ -204,7 +362,7 @@  static int tpm2_load_cmd(struct tpm_chip *chip,
 			     options->keyauth /* hmac */,
 			     TPM_DIGEST_SIZE);
 
-	tpm_buf_append(&buf, payload->blob, blob_len);
+	tpm_buf_append(&buf, blob, blob_len);
 
 	if (buf.flags & TPM_BUF_OVERFLOW) {
 		rc = -E2BIG;
@@ -217,6 +375,8 @@  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)