All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v14 0/5] TPM 2.0 trusted key rework
@ 2020-11-29 22:19 James Bottomley
  2020-11-29 22:20 ` [PATCH v14 1/5] lib: add ASN.1 encoder James Bottomley
                   ` (6 more replies)
  0 siblings, 7 replies; 26+ messages in thread
From: James Bottomley @ 2020-11-29 22:19 UTC (permalink / raw)
  To: linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings, David Howells

v14: add tested by flags and fix one more 0day select problem

General cover letter minus policy bit:

This patch updates the trusted key code to export keys in the ASN.1
format used by current TPM key tools (openssl_tpm2_engine and
openconnect).  The current code will try to load keys containing
policy, but being unable to formulate the policy commands necessary to
load them, the unseal will always fail unless the policy is executed
in user space and a pre-formed policy session passed in.

The key format is designed to be compatible with our two openssl
engine implementations as well as with the format used by openconnect.
I've added seal/unseal to my engine so I can use it for
interoperability testing and I'll later use this for sealed symmetric
keys via engine:

https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl_tpm2_engine.git/

James

---

James Bottomley (5):
  lib: add ASN.1 encoder
  oid_registry: Add TCG defined OIDS for TPM keys
  security: keys: trusted: fix TPM2 authorizations
  security: keys: trusted: use ASN.1 TPM2 key format for the blobs
  security: keys: trusted: Make sealed key properly interoperable

 .../security/keys/trusted-encrypted.rst       |  58 +++
 include/keys/trusted-type.h                   |   2 +
 include/linux/asn1_encoder.h                  |  32 ++
 include/linux/oid_registry.h                  |   5 +
 include/linux/tpm.h                           |   2 +
 lib/Kconfig                                   |   3 +
 lib/Makefile                                  |   1 +
 lib/asn1_encoder.c                            | 454 ++++++++++++++++++
 security/keys/Kconfig                         |   3 +
 security/keys/trusted-keys/Makefile           |   4 +-
 security/keys/trusted-keys/tpm2key.asn1       |  11 +
 security/keys/trusted-keys/trusted_tpm1.c     |  34 +-
 security/keys/trusted-keys/trusted_tpm2.c     | 266 +++++++++-
 13 files changed, 844 insertions(+), 31 deletions(-)
 create mode 100644 include/linux/asn1_encoder.h
 create mode 100644 lib/asn1_encoder.c
 create mode 100644 security/keys/trusted-keys/tpm2key.asn1

-- 
2.26.2


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH v14 1/5] lib: add ASN.1 encoder
  2020-11-29 22:19 [PATCH v14 0/5] TPM 2.0 trusted key rework James Bottomley
@ 2020-11-29 22:20 ` James Bottomley
  2020-12-04  4:43   ` Jarkko Sakkinen
  2020-11-29 22:20 ` [PATCH v14 2/5] oid_registry: Add TCG defined OIDS for TPM keys James Bottomley
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 26+ messages in thread
From: James Bottomley @ 2020-11-29 22:20 UTC (permalink / raw)
  To: linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings, David Howells

We have a need in the TPM2 trusted keys to return the ASN.1 form of
the TPM key blob so it can be operated on by tools outside of the
kernel.  The specific tools are the openssl_tpm2_engine, openconnect
and the Intel tpm2-tss-engine.  To do that, we have to be able to read
and write the same binary key format the tools use.  The current ASN.1
decoder does fine for reading, but we need pieces of an ASN.1 encoder
to write the key blob in binary compatible form.

For backwards compatibility, the trusted key reader code will still
accept the two TPM2B quantities that it uses today, but the writer
will only output the ASN.1 form.

The current implementation only encodes the ASN.1 bits we actually need.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>

---

v2: updated API to use indefinite length, and made symbol exports gpl
v3: add data length error handling
v4: use end_data instead of data_len pointer
v5: mention tools and space out code
v7: reverse christmas tree variable definitions and better spacing
v9: update comments, remove indefinite length, make encoder config dependent
---
 include/linux/asn1_encoder.h |  32 +++
 lib/Kconfig                  |   3 +
 lib/Makefile                 |   1 +
 lib/asn1_encoder.c           | 454 +++++++++++++++++++++++++++++++++++
 4 files changed, 490 insertions(+)
 create mode 100644 include/linux/asn1_encoder.h
 create mode 100644 lib/asn1_encoder.c

diff --git a/include/linux/asn1_encoder.h b/include/linux/asn1_encoder.h
new file mode 100644
index 000000000000..08cd0c2ad34f
--- /dev/null
+++ b/include/linux/asn1_encoder.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _LINUX_ASN1_ENCODER_H
+#define _LINUX_ASN1_ENCODER_H
+
+#include <linux/types.h>
+#include <linux/asn1.h>
+#include <linux/asn1_ber_bytecode.h>
+#include <linux/bug.h>
+
+#define asn1_oid_len(oid) (sizeof(oid)/sizeof(u32))
+unsigned char *
+asn1_encode_integer(unsigned char *data, const unsigned char *end_data,
+		    s64 integer);
+unsigned char *
+asn1_encode_oid(unsigned char *data, const unsigned char *end_data,
+		u32 oid[], int oid_len);
+unsigned char *
+asn1_encode_tag(unsigned char *data, const unsigned char *end_data,
+		u32 tag, const unsigned char *string, int len);
+unsigned char *
+asn1_encode_octet_string(unsigned char *data,
+			 const unsigned char *end_data,
+			 const unsigned char *string, u32 len);
+unsigned char *
+asn1_encode_sequence(unsigned char *data, const unsigned char *end_data,
+		     const unsigned char *seq, int len);
+unsigned char *
+asn1_encode_boolean(unsigned char *data, const unsigned char *end_data,
+		    bool val);
+
+#endif
diff --git a/lib/Kconfig b/lib/Kconfig
index b46a9fd122c8..f7d7523a26b0 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -689,3 +689,6 @@ config GENERIC_LIB_UCMPDI2
 config PLDMFW
 	bool
 	default n
+
+config ASN1_ENCODER
+       tristate
diff --git a/lib/Makefile b/lib/Makefile
index ce45af50983a..05d2482cdadf 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -281,6 +281,7 @@ obj-$(CONFIG_INTERVAL_TREE_TEST) += interval_tree_test.o
 obj-$(CONFIG_PERCPU_TEST) += percpu_test.o
 
 obj-$(CONFIG_ASN1) += asn1_decoder.o
+obj-$(CONFIG_ASN1_ENCODER) += asn1_encoder.o
 
 obj-$(CONFIG_FONT_SUPPORT) += fonts/
 
diff --git a/lib/asn1_encoder.c b/lib/asn1_encoder.c
new file mode 100644
index 000000000000..41e71aae3ef6
--- /dev/null
+++ b/lib/asn1_encoder.c
@@ -0,0 +1,454 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Simple encoder primitives for ASN.1 BER/DER/CER
+ *
+ * Copyright (C) 2019 James.Bottomley@HansenPartnership.com
+ */
+
+#include <linux/asn1_encoder.h>
+#include <linux/bug.h>
+#include <linux/string.h>
+#include <linux/module.h>
+
+/**
+ * asn1_encode_integer() - encode positive integer to ASN.1
+ * @data:	pointer to the pointer to the data
+ * @end_data:	end of data pointer, points one beyond last usable byte in @data
+ * @integer:	integer to be encoded
+ *
+ * This is a simplified encoder: it only currently does
+ * positive integers, but it should be simple enough to add the
+ * negative case if a use comes along.
+ */
+unsigned char *
+asn1_encode_integer(unsigned char *data, const unsigned char *end_data,
+		    s64 integer)
+{
+	int data_len = end_data - data;
+	unsigned char *d = &data[2];
+	bool found = false;
+	int i;
+
+	if (WARN(integer < 0,
+		 "BUG: integer encode only supports positive integers"))
+		return ERR_PTR(-EINVAL);
+
+	if (IS_ERR(data))
+		return data;
+
+	/* need at least 3 bytes for tag, length and integer encoding */
+	if (data_len < 3)
+		return ERR_PTR(-EINVAL);
+
+	/* remaining length where at d (the start of the integer encoding) */
+	data_len -= 2;
+
+	data[0] = _tag(UNIV, PRIM, INT);
+	if (integer == 0) {
+		*d++ = 0;
+		goto out;
+	}
+
+	for (i = sizeof(integer); i > 0 ; i--) {
+		int byte = integer >> (8 * (i - 1));
+
+		if (!found && byte == 0)
+			continue;
+
+		/*
+		 * for a positive number the first byte must have bit
+		 * 7 clear in two's complement (otherwise it's a
+		 * negative number) so prepend a leading zero if
+		 * that's not the case
+		 */
+		if (!found && (byte & 0x80)) {
+			/*
+			 * no check needed here, we already know we
+			 * have len >= 1
+			 */
+			*d++ = 0;
+			data_len--;
+		}
+
+		found = true;
+		if (data_len == 0)
+			return ERR_PTR(-EINVAL);
+
+		*d++ = byte;
+		data_len--;
+	}
+
+ out:
+	data[1] = d - data - 2;
+
+	return d;
+}
+EXPORT_SYMBOL_GPL(asn1_encode_integer);
+
+/* calculate the base 128 digit values setting the top bit of the first octet */
+static int asn1_encode_oid_digit(unsigned char **_data, int *data_len, u32 oid)
+{
+	unsigned char *data = *_data;
+	int start = 7 + 7 + 7 + 7;
+	int ret = 0;
+
+	if (*data_len < 1)
+		return -EINVAL;
+
+	/* quick case */
+	if (oid == 0) {
+		*data++ = 0x80;
+		(*data_len)--;
+		goto out;
+	}
+
+	while (oid >> start == 0)
+		start -= 7;
+
+	while (start > 0 && *data_len > 0) {
+		u8 byte;
+
+		byte = oid >> start;
+		oid = oid - (byte << start);
+		start -= 7;
+		byte |= 0x80;
+		*data++ = byte;
+		(*data_len)--;
+	}
+
+	if (*data_len > 0) {
+		*data++ = oid;
+		(*data_len)--;
+	} else {
+		ret = -EINVAL;
+	}
+
+ out:
+	*_data = data;
+	return ret;
+}
+
+/**
+ * asn1_encode_oid() - encode an oid to ASN.1
+ * @data:	position to begin encoding at
+ * @end_data:	end of data pointer, points one beyond last usable byte in @data
+ * @oid:	array of oids
+ * @oid_len:	length of oid array
+ *
+ * this encodes an OID up to ASN.1 when presented as an array of OID values
+ */
+unsigned char *
+asn1_encode_oid(unsigned char *data, const unsigned char *end_data,
+		u32 oid[], int oid_len)
+{
+	int data_len = end_data - data;
+	unsigned char *d = data + 2;
+	int i, ret;
+
+	if (WARN(oid_len < 2, "OID must have at least two elements"))
+		return ERR_PTR(-EINVAL);
+
+	if (WARN(oid_len > 32, "OID is too large"))
+		return ERR_PTR(-EINVAL);
+
+	if (IS_ERR(data))
+		return data;
+
+
+	/* need at least 3 bytes for tag, length and OID encoding */
+	if (data_len < 3)
+		return ERR_PTR(-EINVAL);
+
+	data[0] = _tag(UNIV, PRIM, OID);
+	*d++ = oid[0] * 40 + oid[1];
+
+	data_len -= 3;
+
+	ret = 0;
+
+	for (i = 2; i < oid_len; i++) {
+		ret = asn1_encode_oid_digit(&d, &data_len, oid[i]);
+		if (ret < 0)
+			return ERR_PTR(ret);
+	}
+
+	data[1] = d - data - 2;
+
+	return d;
+}
+EXPORT_SYMBOL_GPL(asn1_encode_oid);
+
+/**
+ * asn1_encode_length() - encode a length to follow an ASN.1 tag
+ * @data: pointer to encode at
+ * @data_len: pointer to remaning length (adjusted by routine)
+ * @len: length to encode
+ *
+ * This routine can encode lengths up to 65535 using the ASN.1 rules.
+ * It will accept a negative length and place a zero length tag
+ * instead (to keep the ASN.1 valid).  This convention allows other
+ * encoder primitives to accept negative lengths as singalling the
+ * sequence will be re-encoded when the length is known.
+ */
+static int asn1_encode_length(unsigned char **data, int *data_len, int len)
+{
+	if (*data_len < 1)
+		return -EINVAL;
+
+	if (len < 0) {
+		*((*data)++) = 0;
+		(*data_len)--;
+		return 0;
+	}
+
+	if (len <= 0x7f) {
+		*((*data)++) = len;
+		(*data_len)--;
+		return 0;
+	}
+
+	if (*data_len < 2)
+		return -EINVAL;
+
+	if (len <= 0xff) {
+		*((*data)++) = 0x81;
+		*((*data)++) = len & 0xff;
+		*data_len -= 2;
+		return 0;
+	}
+
+	if (*data_len < 3)
+		return -EINVAL;
+
+	if (len <= 0xffff) {
+		*((*data)++) = 0x82;
+		*((*data)++) = (len >> 8) & 0xff;
+		*((*data)++) = len & 0xff;
+		*data_len -= 3;
+		return 0;
+	}
+
+	if (WARN(len > 0xffffff, "ASN.1 length can't be > 0xffffff"))
+		return -EINVAL;
+
+	if (*data_len < 4)
+		return -EINVAL;
+	*((*data)++) = 0x83;
+	*((*data)++) = (len >> 16) & 0xff;
+	*((*data)++) = (len >> 8) & 0xff;
+	*((*data)++) = len & 0xff;
+	*data_len -= 4;
+
+	return 0;
+}
+
+/**
+ * asn1_encode_tag() - add a tag for optional or explicit value
+ * @data:	pointer to place tag at
+ * @end_data:	end of data pointer, points one beyond last usable byte in @data
+ * @tag:	tag to be placed
+ * @string:	the data to be tagged
+ * @len:	the length of the data to be tagged
+ *
+ * Note this currently only handles short form tags < 31.
+ *
+ * Standard usage is to pass in a @tag, @string and @length and the
+ * @string will be ASN.1 encoded with @tag and placed into @data.  If
+ * the encoding would put data past @end_data then an error is
+ * returned, otherwise a pointer to a position one beyond the encoding
+ * is returned.
+ *
+ * To encode in place pass a NULL @string and -1 for @len and the
+ * maximum allowable beginning and end of the data; all this will do
+ * is add the current maximum length and update the data pointer to
+ * the place where the tag contents should be placed is returned.  The
+ * data should be copied in by the calling routine which should then
+ * repeat the prior statement but now with the known length.  In order
+ * to avoid having to keep both before and after pointers, the repeat
+ * expects to be called with @data pointing to where the first encode
+ * returned it and still NULL for @string but the real length in @len.
+ */
+unsigned char *
+asn1_encode_tag(unsigned char *data, const unsigned char *end_data,
+		u32 tag, const unsigned char *string, int len)
+{
+	int data_len = end_data - data;
+	int ret;
+
+	if (WARN(tag > 30, "ASN.1 tag can't be > 30"))
+		return ERR_PTR(-EINVAL);
+
+	if (!string && WARN(len > 127,
+			    "BUG: recode tag is too big (>127)"))
+		return ERR_PTR(-EINVAL);
+
+	if (IS_ERR(data))
+		return data;
+
+	if (!string && len > 0) {
+		/*
+		 * we're recoding, so move back to the start of the
+		 * tag and install a dummy length because the real
+		 * data_len should be NULL
+		 */
+		data -= 2;
+		data_len = 2;
+	}
+
+	if (data_len < 2)
+		return ERR_PTR(-EINVAL);
+
+	*(data++) = _tagn(CONT, CONS, tag);
+	data_len--;
+	ret = asn1_encode_length(&data, &data_len, len);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	if (!string)
+		return data;
+
+	if (data_len < len)
+		return ERR_PTR(-EINVAL);
+
+	memcpy(data, string, len);
+	data += len;
+
+	return data;
+}
+EXPORT_SYMBOL_GPL(asn1_encode_tag);
+
+/**
+ * asn1_encode_octet_string() - encode an ASN.1 OCTET STRING
+ * @data:	pointer to encode at
+ * @end_data:	end of data pointer, points one beyond last usable byte in @data
+ * @string:	string to be encoded
+ * @len:	length of string
+ *
+ * Note ASN.1 octet strings may contain zeros, so the length is obligatory.
+ */
+unsigned char *
+asn1_encode_octet_string(unsigned char *data,
+			 const unsigned char *end_data,
+			 const unsigned char *string, u32 len)
+{
+	int data_len = end_data - data;
+	int ret;
+
+	if (IS_ERR(data))
+		return data;
+
+	/* need minimum of 2 bytes for tag and length of zero length string */
+	if (data_len < 2)
+		return ERR_PTR(-EINVAL);
+
+	*(data++) = _tag(UNIV, PRIM, OTS);
+	data_len--;
+
+	ret = asn1_encode_length(&data, &data_len, len);
+	if (ret)
+		return ERR_PTR(ret);
+
+	if (data_len < len)
+		return ERR_PTR(-EINVAL);
+
+	memcpy(data, string, len);
+	data += len;
+
+	return data;
+}
+EXPORT_SYMBOL_GPL(asn1_encode_octet_string);
+
+/**
+ * asn1_encode_sequence() - wrap a byte stream in an ASN.1 SEQUENCE
+ * @data:	pointer to encode at
+ * @end_data:	end of data pointer, points one beyond last usable byte in @data
+ * @seq:	data to be encoded as a sequence
+ * @len:	length of the data to be encoded as a sequence
+ *
+ * Fill in a sequence.  To encode in place, pass NULL for @seq and -1
+ * for @len; then call again once the length is known (still with NULL
+ * for @seq). In order to avoid having to keep both before and after
+ * pointers, the repeat expects to be called with @data pointing to
+ * where the first encode placed it.
+ */
+unsigned char *
+asn1_encode_sequence(unsigned char *data, const unsigned char *end_data,
+		     const unsigned char *seq, int len)
+{
+	int data_len = end_data - data;
+	int ret;
+
+	if (!seq && WARN(len > 127,
+			 "BUG: recode sequence is too big (>127)"))
+		return ERR_PTR(-EINVAL);
+
+	if (IS_ERR(data))
+		return data;
+
+	if (!seq && len >= 0) {
+		/*
+		 * we're recoding, so move back to the start of the
+		 * sequence and install a dummy length because the
+		 * real length should be NULL
+		 */
+		data -= 2;
+		data_len = 2;
+	}
+
+	if (data_len < 2)
+		return ERR_PTR(-EINVAL);
+
+	*(data++) = _tag(UNIV, CONS, SEQ);
+	data_len--;
+
+	ret = asn1_encode_length(&data, &data_len, len);
+	if (ret)
+		return ERR_PTR(ret);
+
+	if (!seq)
+		return data;
+
+	if (data_len < len)
+		return ERR_PTR(-EINVAL);
+
+	memcpy(data, seq, len);
+	data += len;
+
+	return data;
+}
+EXPORT_SYMBOL_GPL(asn1_encode_sequence);
+
+/**
+ * asn1_encode_boolean() - encode a boolean value to ASN.1
+ * @data:	pointer to encode at
+ * @end_data:	end of data pointer, points one beyond last usable byte in @data
+ * @val:	the boolean true/false value
+ */
+unsigned char *
+asn1_encode_boolean(unsigned char *data, const unsigned char *end_data,
+		    bool val)
+{
+	int data_len = end_data - data;
+
+	if (IS_ERR(data))
+		return data;
+
+	/* booleans are 3 bytes: tag, length == 1 and value == 0 or 1 */
+	if (data_len < 3)
+		return ERR_PTR(-EINVAL);
+
+	*(data++) = _tag(UNIV, PRIM, BOOL);
+	data_len--;
+
+	asn1_encode_length(&data, &data_len, 1);
+
+	if (val)
+		*(data++) = 1;
+	else
+		*(data++) = 0;
+
+	return data;
+}
+EXPORT_SYMBOL_GPL(asn1_encode_boolean);
+
+MODULE_LICENSE("GPL");
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH v14 2/5] oid_registry: Add TCG defined OIDS for TPM keys
  2020-11-29 22:19 [PATCH v14 0/5] TPM 2.0 trusted key rework James Bottomley
  2020-11-29 22:20 ` [PATCH v14 1/5] lib: add ASN.1 encoder James Bottomley
@ 2020-11-29 22:20 ` James Bottomley
  2020-11-29 22:20 ` [PATCH v14 3/5] security: keys: trusted: fix TPM2 authorizations James Bottomley
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 26+ messages in thread
From: James Bottomley @ 2020-11-29 22:20 UTC (permalink / raw)
  To: linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings, David Howells

The TCG has defined an OID prefix "2.23.133.10.1" for the various TPM
key uses.  We've defined three of the available numbers:

2.23.133.10.1.3 TPM Loadable key.  This is an asymmetric key (Usually
		RSA2048 or Elliptic Curve) which can be imported by a
		TPM2_Load() operation.

2.23.133.10.1.4 TPM Importable Key.  This is an asymmetric key (Usually
		RSA2048 or Elliptic Curve) which can be imported by a
		TPM2_Import() operation.

Both loadable and importable keys are specific to a given TPM, the
difference is that a loadable key is wrapped with the symmetric
secret, so must have been created by the TPM itself.  An importable
key is wrapped with a DH shared secret, and may be created without
access to the TPM provided you know the public part of the parent key.

2.23.133.10.1.5 TPM Sealed Data.  This is a set of data (up to 128
		bytes) which is sealed by the TPM.  It usually
		represents a symmetric key and must be unsealed before
		use.

The ASN.1 binary key form starts of with this OID as the first element
of a sequence, giving the binary form a unique recognizable identity
marker regardless of encoding.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Acked-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Reviewed-by: David Howells <dhowells@redhat.com>

---

v3: correct OID_TPMImportableKey name
v7: add ack
v9: add review
---
 include/linux/oid_registry.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h
index 4462ed2c18cd..d06988d1565e 100644
--- a/include/linux/oid_registry.h
+++ b/include/linux/oid_registry.h
@@ -113,6 +113,11 @@ enum OID {
 	OID_SM2_with_SM3,		/* 1.2.156.10197.1.501 */
 	OID_sm3WithRSAEncryption,	/* 1.2.156.10197.1.504 */
 
+	/* TCG defined OIDS for TPM based keys */
+	OID_TPMLoadableKey,		/* 2.23.133.10.1.3 */
+	OID_TPMImportableKey,		/* 2.23.133.10.1.4 */
+	OID_TPMSealedData,		/* 2.23.133.10.1.5 */
+
 	OID__NR
 };
 
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH v14 3/5] security: keys: trusted: fix TPM2 authorizations
  2020-11-29 22:19 [PATCH v14 0/5] TPM 2.0 trusted key rework James Bottomley
  2020-11-29 22:20 ` [PATCH v14 1/5] lib: add ASN.1 encoder James Bottomley
  2020-11-29 22:20 ` [PATCH v14 2/5] oid_registry: Add TCG defined OIDS for TPM keys James Bottomley
@ 2020-11-29 22:20 ` James Bottomley
  2020-12-22 23:01   ` Ken Goldman
  2020-11-29 22:20 ` [PATCH v14 4/5] security: keys: trusted: use ASN.1 TPM2 key format for the blobs James Bottomley
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 26+ messages in thread
From: James Bottomley @ 2020-11-29 22:20 UTC (permalink / raw)
  To: linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings, David Howells

In TPM 1.2 an authorization was a 20 byte number.  The spec actually
recommended you to hash variable length passwords and use the sha1
hash as the authorization.  Because the spec doesn't require this
hashing, the current authorization for trusted keys is a 40 digit hex
number.  For TPM 2.0 the spec allows the passing in of variable length
passwords and passphrases directly, so we should allow that in trusted
keys for ease of use.  Update the 'blobauth' parameter to take this
into account, so we can now use plain text passwords for the keys.

so before

keyctl add trusted kmk "new 32 blobauth=f572d396fae9206628714fb2ce00f72e94f2258fkeyhandle=81000001" @u

after we will accept both the old hex sha1 form as well as a new
directly supplied password:

keyctl add trusted kmk "new 32 blobauth=hello keyhandle=81000001" @u

Since a sha1 hex code must be exactly 40 bytes long and a direct
password must be 20 or less, we use the length as the discriminator
for which form is input.

Note this is both and enhancement and a potential bug fix.  The TPM
2.0 spec requires us to strip leading zeros, meaning empyty
authorization is a zero length HMAC whereas we're currently passing in
20 bytes of zeros.  A lot of TPMs simply accept this as OK, but the
Microsoft TPM emulator rejects it with TPM_RC_BAD_AUTH, so this patch
makes the Microsoft TPM emulator work with trusted keys.

Fixes: 0fe5480303a1 ("keys, trusted: seal/unseal with TPM 2.0 chips")
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Tested-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>

---

v6: change comment, eliminate else clauses and add fixes tag
v7: fixes before signoff
v12: fix mismerge from v6 to make processing continue after blobauth
v14: add tested by

Merge with auth fix
---
 include/keys/trusted-type.h               |  1 +
 security/keys/trusted-keys/trusted_tpm1.c | 32 ++++++++++++++++++-----
 security/keys/trusted-keys/trusted_tpm2.c | 10 ++++---
 3 files changed, 33 insertions(+), 10 deletions(-)

diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
index a94c03a61d8f..b2ed3481c6a0 100644
--- a/include/keys/trusted-type.h
+++ b/include/keys/trusted-type.h
@@ -30,6 +30,7 @@ struct trusted_key_options {
 	uint16_t keytype;
 	uint32_t keyhandle;
 	unsigned char keyauth[TPM_DIGEST_SIZE];
+	uint32_t blobauth_len;
 	unsigned char blobauth[TPM_DIGEST_SIZE];
 	uint32_t pcrinfo_len;
 	unsigned char pcrinfo[MAX_PCRINFO_SIZE];
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index b9fe02e5f84f..eaa2e7ca136e 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -781,13 +781,33 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
 				return -EINVAL;
 			break;
 		case Opt_blobauth:
-			if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE)
-				return -EINVAL;
-			res = hex2bin(opt->blobauth, args[0].from,
-				      SHA1_DIGEST_SIZE);
-			if (res < 0)
-				return -EINVAL;
+			/*
+			 * TPM 1.2 authorizations are sha1 hashes passed in as
+			 * hex strings.  TPM 2.0 authorizations are simple
+			 * passwords (although it can take a hash as well)
+			 */
+			opt->blobauth_len = strlen(args[0].from);
+
+			if (opt->blobauth_len == 2 * TPM_DIGEST_SIZE) {
+				res = hex2bin(opt->blobauth, args[0].from,
+					      TPM_DIGEST_SIZE);
+				if (res < 0)
+					return -EINVAL;
+
+				opt->blobauth_len = TPM_DIGEST_SIZE;
+				break;
+			}
+
+			if (tpm2 && opt->blobauth_len <= sizeof(opt->blobauth)) {
+				memcpy(opt->blobauth, args[0].from,
+				       opt->blobauth_len);
+				break;
+			}
+
+			return -EINVAL;
+
 			break;
+
 		case Opt_migratable:
 			if (*args[0].from == '0')
 				pay->migratable = 0;
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 38bb33333cdf..6c6dd88d7bf6 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -91,10 +91,12 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 			     TPM_DIGEST_SIZE);
 
 	/* sensitive */
-	tpm_buf_append_u16(&buf, 4 + TPM_DIGEST_SIZE + payload->key_len + 1);
+	tpm_buf_append_u16(&buf, 4 + options->blobauth_len + payload->key_len + 1);
+
+	tpm_buf_append_u16(&buf, options->blobauth_len);
+	if (options->blobauth_len)
+		tpm_buf_append(&buf, options->blobauth, options->blobauth_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 + 1);
 	tpm_buf_append(&buf, payload->key, payload->key_len);
 	tpm_buf_append_u8(&buf, payload->migratable);
@@ -258,7 +260,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 			     NULL /* nonce */, 0,
 			     TPM2_SA_CONTINUE_SESSION,
 			     options->blobauth /* hmac */,
-			     TPM_DIGEST_SIZE);
+			     options->blobauth_len);
 
 	rc = tpm_send(chip, buf.data, tpm_buf_length(&buf));
 	if (rc > 0)
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH v14 4/5] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
  2020-11-29 22:19 [PATCH v14 0/5] TPM 2.0 trusted key rework James Bottomley
                   ` (2 preceding siblings ...)
  2020-11-29 22:20 ` [PATCH v14 3/5] security: keys: trusted: fix TPM2 authorizations James Bottomley
@ 2020-11-29 22:20 ` James Bottomley
  2020-11-30  2:10     ` kernel test robot
  2020-11-29 22:20 ` [PATCH v14 5/5] security: keys: trusted: Make sealed key properly interoperable James Bottomley
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 26+ messages in thread
From: James Bottomley @ 2020-11-29 22:20 UTC (permalink / raw)
  To: linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings, David Howells

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 (openssl_tpm2_engine,
openconnect and tpm2-tss-engine).  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>
Tested-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>

---
v2: Updated encode API, added length checks
v5: correct export format after doing interoperability checks
v7: use prefix tpm2_key_ instead of tpmkey_ for functions
v8: resplit commit
v9: select ASN1_ENCODER
v11: add ASN.1 format description
v13: fix ASN.1 compiler dependency
v14: select OID_REGISTRY add tested by
---
 .../security/keys/trusted-encrypted.rst       |  58 +++++
 include/keys/trusted-type.h                   |   1 +
 security/keys/Kconfig                         |   3 +
 security/keys/trusted-keys/Makefile           |   4 +-
 security/keys/trusted-keys/tpm2key.asn1       |  11 +
 security/keys/trusted-keys/trusted_tpm1.c     |   2 +-
 security/keys/trusted-keys/trusted_tpm2.c     | 207 +++++++++++++++++-
 7 files changed, 278 insertions(+), 8 deletions(-)
 create mode 100644 security/keys/trusted-keys/tpm2key.asn1

diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst
index 1da879a68640..549aa1308949 100644
--- a/Documentation/security/keys/trusted-encrypted.rst
+++ b/Documentation/security/keys/trusted-encrypted.rst
@@ -207,3 +207,61 @@ about the usage can be found in the file
 Another new format 'enc32' has been defined in order to support encrypted keys
 with payload size of 32 bytes. This will initially be used for nvdimm security
 but may expand to other usages that require 32 bytes payload.
+
+
+TPM 2.0 ASN.1 Key Format
+------------------------
+
+The TPM 2.0 ASN.1 key format is designed to be easily recognisable,
+even in binary form (fixing a problem we had with the TPM 1.2 ASN.1
+format) and to be extensible for additions like importable keys and
+policy::
+
+    TPMKey ::= SEQUENCE {
+        type		OBJECT IDENTIFIER
+        emptyAuth	[0] EXPLICIT BOOLEAN OPTIONAL
+        parent		INTEGER
+        pubkey		OCTET STRING
+        privkey		OCTET STRING
+    }
+
+type is what distinguishes the key even in binary form since the OID
+is provided by the TCG to be unique and thus forms a recognizable
+binary pattern at offset 3 in the key.  The OIDs currently made
+available are::
+
+    2.23.133.10.1.3 TPM Loadable key.  This is an asymmetric key (Usually
+                    RSA2048 or Elliptic Curve) which can be imported by a
+                    TPM2_Load() operation.
+
+    2.23.133.10.1.4 TPM Importable Key.  This is an asymmetric key (Usually
+                    RSA2048 or Elliptic Curve) which can be imported by a
+                    TPM2_Import() operation.
+
+    2.23.133.10.1.5 TPM Sealed Data.  This is a set of data (up to 128
+                    bytes) which is sealed by the TPM.  It usually
+                    represents a symmetric key and must be unsealed before
+                    use.
+
+The trusted key code only uses the TPM Sealed Data OID.
+
+emptyAuth is true if the key has well known authorization "".  If it
+is false or not present, the key requires an explicit authorization
+phrase.  This is used by most user space consumers to decide whether
+to prompt for a password.
+
+parent represents the parent key handle, either in the 0x81 MSO space,
+like 0x81000001 for the RSA primary storage key.  Userspace programmes
+also support specifying the primary handle in the 0x40 MSO space.  If
+this happens the Elliptic Curve variant of the primary key using the
+TCG defined template will be generated on the fly into a volatile
+object and used as the parent.  The current kernel code only supports
+the 0x81 MSO form.
+
+pubkey is the binary representation of TPM2B_PRIVATE excluding the
+initial TPM2B header, which can be reconstructed from the ASN.1 octet
+string length.
+
+privkey is the binary representation of TPM2B_PUBLIC excluding the
+initial TPM2B header which can be reconstructed from the ASN.1 octed
+string length.
diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
index b2ed3481c6a0..b2d87ad21714 100644
--- a/include/keys/trusted-type.h
+++ b/include/keys/trusted-type.h
@@ -22,6 +22,7 @@ struct trusted_key_payload {
 	unsigned int key_len;
 	unsigned int blob_len;
 	unsigned char migratable;
+	unsigned char old_format;
 	unsigned char key[MAX_KEY_SIZE + 1];
 	unsigned char blob[MAX_BLOB_SIZE];
 };
diff --git a/security/keys/Kconfig b/security/keys/Kconfig
index 83bc23409164..f0912692469b 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -75,6 +75,9 @@ config TRUSTED_KEYS
 	select CRYPTO_HMAC
 	select CRYPTO_SHA1
 	select CRYPTO_HASH_INFO
+	select ASN1_ENCODER
+	select OID_REGISTRY
+	select ASN1
 	help
 	  This option provides support for creating, sealing, and unsealing
 	  keys in the kernel. Trusted keys are random number symmetric keys,
diff --git a/security/keys/trusted-keys/Makefile b/security/keys/trusted-keys/Makefile
index 7b73cebbb378..f87c43f306d5 100644
--- a/security/keys/trusted-keys/Makefile
+++ b/security/keys/trusted-keys/Makefile
@@ -5,4 +5,6 @@
 
 obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
 trusted-y += trusted_tpm1.o
-trusted-y += trusted_tpm2.o
+
+$(obj)/trusted_tpm2.o: $(obj)/tpm2key.asn1.h
+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..3f6a9d01d1e5
--- /dev/null
+++ b/security/keys/trusted-keys/tpm2key.asn1
@@ -0,0 +1,11 @@
+---
+--- ASN.1 for for TPM 2.0 keys
+---
+
+TPMKey ::= SEQUENCE {
+	type		OBJECT IDENTIFIER ({tpm2_key_type}),
+	emptyAuth	[0] EXPLICIT BOOLEAN OPTIONAL,
+	parent		INTEGER ({tpm2_key_parent}),
+	pubkey		OCTET STRING ({tpm2_key_pub}),
+	privkey		OCTET STRING ({tpm2_key_priv})
+	}
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index eaa2e7ca136e..f235637865b9 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -1011,7 +1011,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 6c6dd88d7bf6..03dea445362c 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,165 @@ 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)
+{
+	const int SCRATCH_SIZE = PAGE_SIZE;
+	u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
+	u8 *work = scratch, *work1;
+	u8 *end_work = scratch + SCRATCH_SIZE;
+	u8 *priv, *pub;
+	u16 priv_len, pub_len;
+
+	priv_len = get_unaligned_be16(src) + 2;
+	priv = src;
+
+	src += priv_len;
+
+	pub_len = get_unaligned_be16(src) + 2;
+	pub = src;
+
+	if (!scratch)
+		return -ENOMEM;
+
+	work = asn1_encode_oid(work, end_work, tpm2key_oid,
+			       asn1_oid_len(tpm2key_oid));
+
+	if (options->blobauth_len == 0) {
+		unsigned char bool[3], *w = bool;
+		/* tag 0 is emptyAuth */
+		w = asn1_encode_boolean(w, w + sizeof(bool), true);
+		if (WARN(IS_ERR(w), "BUG: Boolean failed to encode"))
+			return PTR_ERR(w);
+		work = asn1_encode_tag(work, end_work, 0, bool, w - bool);
+	}
+
+	/*
+	 * Assume both octet strings will encode to a 2 byte definite length
+	 *
+	 * 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;
+
+	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);
+
+	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);
+
+	return work1 - payload->blob;
+}
+
+struct tpm2_key_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 tpm2_key_context ctx;
+	u8 *blob;
+
+	memset(&ctx, 0, sizeof(ctx));
+
+	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;
+
+	memcpy(blob, ctx.priv, ctx.priv_len);
+	blob += ctx.priv_len;
+
+	memcpy(blob, ctx.pub, ctx.pub_len);
+
+	return 0;
+}
+
+int tpm2_key_parent(void *context, size_t hdrlen,
+		  unsigned char tag,
+		  const void *value, size_t vlen)
+{
+	struct tpm2_key_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 tpm2_key_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 tpm2_key_pub(void *context, size_t hdrlen,
+	       unsigned char tag,
+	       const void *value, size_t vlen)
+{
+	struct tpm2_key_context *ctx = context;
+
+	ctx->pub = value;
+	ctx->pub_len = vlen;
+
+	return 0;
+}
+
+int tpm2_key_priv(void *context, size_t hdrlen,
+		unsigned char tag,
+		const void *value, size_t vlen)
+{
+	struct tpm2_key_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 +244,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;
@@ -146,8 +314,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);
@@ -158,6 +328,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		else
 			rc = -EPERM;
 	}
+	if (payload->blob_len < 0)
+		return payload->blob_len;
 
 	return rc;
 }
@@ -184,13 +356,34 @@ 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]);
-	if (private_len > (payload->blob_len - 2))
+	rc = tpm2_key_decode(payload, options, &blob);
+	if (rc) {
+		/* old form */
+		blob = payload->blob;
+		payload->old_format = 1;
+	}
+
+	/* new format carries keyhandle but old format doesn't */
+	if (!options->keyhandle)
+		return -EINVAL;
+
+	/* must be big enough for at least the two be16 size counts */
+	if (payload->blob_len < 4)
+		return -EINVAL;
+
+	private_len = get_unaligned_be16(blob);
+
+	/* must be big enough for following public_len */
+	if (private_len + 2 + 2 > (payload->blob_len))
+		return -E2BIG;
+
+	public_len = get_unaligned_be16(blob + 2 + private_len);
+	if (private_len + 2 + public_len + 2 > payload->blob_len)
 		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;
@@ -206,7 +399,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;
@@ -219,6 +412,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)
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH v14 5/5] security: keys: trusted: Make sealed key properly interoperable
  2020-11-29 22:19 [PATCH v14 0/5] TPM 2.0 trusted key rework James Bottomley
                   ` (3 preceding siblings ...)
  2020-11-29 22:20 ` [PATCH v14 4/5] security: keys: trusted: use ASN.1 TPM2 key format for the blobs James Bottomley
@ 2020-11-29 22:20 ` James Bottomley
  2020-12-04 13:40 ` [PATCH v14 1/5] lib: add ASN.1 encoder David Howells
  2020-12-04 13:44 ` [PATCH v14 2/5] oid_registry: Add TCG defined OIDS for TPM keys David Howells
  6 siblings, 0 replies; 26+ messages in thread
From: James Bottomley @ 2020-11-29 22:20 UTC (permalink / raw)
  To: linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings, David Howells

The current implementation appends a migratable flag to the end of a
key, meaning the format isn't exactly interoperable because the using
party needs to know to strip this extra byte.  However, all other
consumers of TPM sealed blobs expect the unseal to return exactly the
key.  Since TPM2 keys have a key property flag that corresponds to
migratable, use that flag instead and make the actual key the only
sealed quantity.  This is secure because the key properties are bound
to a hash in the private part, so if they're altered the key won't
load.

Backwards compatibility is implemented by detecting whether we're
loading a new format key or not and correctly setting migratable from
the last byte of old format keys.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Tested-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>

---

v2: added length checks to untrusted payload
v8: recover patch
v14: add tested by
---
 include/linux/tpm.h                       |  2 +
 security/keys/trusted-keys/trusted_tpm2.c | 53 ++++++++++++++++-------
 2 files changed, 40 insertions(+), 15 deletions(-)

diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index c4ca52138e8b..f1e32bc7d618 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -305,6 +305,8 @@ struct tpm_buf {
 };
 
 enum tpm2_object_attributes {
+	TPM2_OA_FIXED_TPM		= BIT(1),
+	TPM2_OA_FIXED_PARENT		= BIT(4),
 	TPM2_OA_USER_WITH_AUTH		= BIT(6),
 };
 
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 03dea445362c..7886b6d39d68 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -231,6 +231,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 	unsigned int blob_len;
 	struct tpm_buf buf;
 	u32 hash;
+	u32 flags;
 	int i;
 	int rc;
 
@@ -259,31 +260,32 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 			     TPM_DIGEST_SIZE);
 
 	/* sensitive */
-	tpm_buf_append_u16(&buf, 4 + options->blobauth_len + payload->key_len + 1);
+	tpm_buf_append_u16(&buf, 4 + options->blobauth_len + payload->key_len);
 
 	tpm_buf_append_u16(&buf, options->blobauth_len);
 	if (options->blobauth_len)
 		tpm_buf_append(&buf, options->blobauth, options->blobauth_len);
 
-	tpm_buf_append_u16(&buf, payload->key_len + 1);
+	tpm_buf_append_u16(&buf, payload->key_len);
 	tpm_buf_append(&buf, payload->key, payload->key_len);
-	tpm_buf_append_u8(&buf, payload->migratable);
 
 	/* public */
 	tpm_buf_append_u16(&buf, 14 + options->policydigest_len);
 	tpm_buf_append_u16(&buf, TPM_ALG_KEYEDHASH);
 	tpm_buf_append_u16(&buf, hash);
 
+	/* key properties */
+	flags = 0;
+	flags |= options->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH;
+	flags |= payload->migratable ? (TPM2_OA_FIXED_TPM |
+					TPM2_OA_FIXED_PARENT) : 0;
+	tpm_buf_append_u32(&buf, flags);
+
 	/* policy */
-	if (options->policydigest_len) {
-		tpm_buf_append_u32(&buf, 0);
-		tpm_buf_append_u16(&buf, options->policydigest_len);
+	tpm_buf_append_u16(&buf, options->policydigest_len);
+	if (options->policydigest_len)
 		tpm_buf_append(&buf, options->policydigest,
 			       options->policydigest_len);
-	} else {
-		tpm_buf_append_u32(&buf, TPM2_OA_USER_WITH_AUTH);
-		tpm_buf_append_u16(&buf, 0);
-	}
 
 	/* public parameters */
 	tpm_buf_append_u16(&buf, TPM_ALG_NULL);
@@ -356,8 +358,9 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 	unsigned int private_len;
 	unsigned int public_len;
 	unsigned int blob_len;
-	u8 *blob;
+	u8 *blob, *pub;
 	int rc;
+	u32 attrs;
 
 	rc = tpm2_key_decode(payload, options, &blob);
 	if (rc) {
@@ -384,6 +387,16 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 	if (private_len + 2 + public_len + 2 > payload->blob_len)
 		return -E2BIG;
 
+	pub = blob + 2 + private_len + 2;
+	/* key attributes are always at offset 4 */
+	attrs = get_unaligned_be32(pub + 4);
+
+	if ((attrs & (TPM2_OA_FIXED_TPM | TPM2_OA_FIXED_PARENT)) ==
+	    (TPM2_OA_FIXED_TPM | TPM2_OA_FIXED_PARENT))
+		payload->migratable = 0;
+	else
+		payload->migratable = 1;
+
 	blob_len = private_len + public_len + 4;
 	if (blob_len > payload->blob_len)
 		return -E2BIG;
@@ -464,7 +477,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 	if (!rc) {
 		data_len = be16_to_cpup(
 			(__be16 *) &buf.data[TPM_HEADER_SIZE + 4]);
-		if (data_len < MIN_KEY_SIZE ||  data_len > MAX_KEY_SIZE + 1) {
+		if (data_len < MIN_KEY_SIZE ||  data_len > MAX_KEY_SIZE) {
 			rc = -EFAULT;
 			goto out;
 		}
@@ -475,9 +488,19 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 		}
 		data = &buf.data[TPM_HEADER_SIZE + 6];
 
-		memcpy(payload->key, data, data_len - 1);
-		payload->key_len = data_len - 1;
-		payload->migratable = data[data_len - 1];
+		if (payload->old_format) {
+			/* migratable flag is at the end of the key */
+			memcpy(payload->key, data, data_len - 1);
+			payload->key_len = data_len - 1;
+			payload->migratable = data[data_len - 1];
+		} else {
+			/*
+			 * migratable flag already collected from key
+			 * attributes
+			 */
+			memcpy(payload->key, data, data_len);
+			payload->key_len = data_len;
+		}
 	}
 
 out:
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH v14 4/5] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
  2020-11-29 22:20 ` [PATCH v14 4/5] security: keys: trusted: use ASN.1 TPM2 key format for the blobs James Bottomley
@ 2020-11-30  2:10     ` kernel test robot
  0 siblings, 0 replies; 26+ messages in thread
From: kernel test robot @ 2020-11-30  2:10 UTC (permalink / raw)
  To: James Bottomley, linux-integrity
  Cc: kbuild-all, Mimi Zohar, Jarkko Sakkinen, David Woodhouse,
	keyrings, David Howells

[-- Attachment #1: Type: text/plain, Size: 4753 bytes --]

Hi James,

I love your patch! Perhaps something to improve:

[auto build test WARNING on integrity/next-integrity]
[also build test WARNING on linus/master v5.10-rc5 next-20201127]
[cannot apply to security/next-testing dhowells-fs/fscache-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/James-Bottomley/TPM-2-0-trusted-key-rework/20201130-063029
base:   https://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity.git next-integrity
config: x86_64-randconfig-m001-20201130 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-15) 9.3.0

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

smatch warnings:
security/keys/trusted-keys/trusted_tpm2.c:331 tpm2_seal_trusted() warn: unsigned 'payload->blob_len' is never less than zero.

vim +331 security/keys/trusted-keys/trusted_tpm2.c

   217	
   218	/**
   219	 * tpm2_seal_trusted() - seal the payload of a trusted key
   220	 *
   221	 * @chip: TPM chip to use
   222	 * @payload: the key data in clear and encrypted form
   223	 * @options: authentication values and other options
   224	 *
   225	 * Return: < 0 on error and 0 on success.
   226	 */
   227	int tpm2_seal_trusted(struct tpm_chip *chip,
   228			      struct trusted_key_payload *payload,
   229			      struct trusted_key_options *options)
   230	{
   231		unsigned int blob_len;
   232		struct tpm_buf buf;
   233		u32 hash;
   234		int i;
   235		int rc;
   236	
   237		for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++) {
   238			if (options->hash == tpm2_hash_map[i].crypto_id) {
   239				hash = tpm2_hash_map[i].tpm_id;
   240				break;
   241			}
   242		}
   243	
   244		if (i == ARRAY_SIZE(tpm2_hash_map))
   245			return -EINVAL;
   246	
   247		if (!options->keyhandle)
   248			return -EINVAL;
   249	
   250		rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
   251		if (rc)
   252			return rc;
   253	
   254		tpm_buf_append_u32(&buf, options->keyhandle);
   255		tpm2_buf_append_auth(&buf, TPM2_RS_PW,
   256				     NULL /* nonce */, 0,
   257				     0 /* session_attributes */,
   258				     options->keyauth /* hmac */,
   259				     TPM_DIGEST_SIZE);
   260	
   261		/* sensitive */
   262		tpm_buf_append_u16(&buf, 4 + options->blobauth_len + payload->key_len + 1);
   263	
   264		tpm_buf_append_u16(&buf, options->blobauth_len);
   265		if (options->blobauth_len)
   266			tpm_buf_append(&buf, options->blobauth, options->blobauth_len);
   267	
   268		tpm_buf_append_u16(&buf, payload->key_len + 1);
   269		tpm_buf_append(&buf, payload->key, payload->key_len);
   270		tpm_buf_append_u8(&buf, payload->migratable);
   271	
   272		/* public */
   273		tpm_buf_append_u16(&buf, 14 + options->policydigest_len);
   274		tpm_buf_append_u16(&buf, TPM_ALG_KEYEDHASH);
   275		tpm_buf_append_u16(&buf, hash);
   276	
   277		/* policy */
   278		if (options->policydigest_len) {
   279			tpm_buf_append_u32(&buf, 0);
   280			tpm_buf_append_u16(&buf, options->policydigest_len);
   281			tpm_buf_append(&buf, options->policydigest,
   282				       options->policydigest_len);
   283		} else {
   284			tpm_buf_append_u32(&buf, TPM2_OA_USER_WITH_AUTH);
   285			tpm_buf_append_u16(&buf, 0);
   286		}
   287	
   288		/* public parameters */
   289		tpm_buf_append_u16(&buf, TPM_ALG_NULL);
   290		tpm_buf_append_u16(&buf, 0);
   291	
   292		/* outside info */
   293		tpm_buf_append_u16(&buf, 0);
   294	
   295		/* creation PCR */
   296		tpm_buf_append_u32(&buf, 0);
   297	
   298		if (buf.flags & TPM_BUF_OVERFLOW) {
   299			rc = -E2BIG;
   300			goto out;
   301		}
   302	
   303		rc = tpm_send(chip, buf.data, tpm_buf_length(&buf));
   304		if (rc)
   305			goto out;
   306	
   307		blob_len = be32_to_cpup((__be32 *) &buf.data[TPM_HEADER_SIZE]);
   308		if (blob_len > MAX_BLOB_SIZE) {
   309			rc = -E2BIG;
   310			goto out;
   311		}
   312		if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 4 + blob_len) {
   313			rc = -EFAULT;
   314			goto out;
   315		}
   316	
   317		payload->blob_len =
   318			tpm2_key_encode(payload, options,
   319					&buf.data[TPM_HEADER_SIZE + 4],
   320					blob_len);
   321	
   322	out:
   323		tpm_buf_destroy(&buf);
   324	
   325		if (rc > 0) {
   326			if (tpm2_rc_value(rc) == TPM2_RC_HASH)
   327				rc = -EINVAL;
   328			else
   329				rc = -EPERM;
   330		}
 > 331		if (payload->blob_len < 0)
   332			return payload->blob_len;
   333	
   334		return rc;
   335	}
   336	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 31351 bytes --]

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v14 4/5] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
@ 2020-11-30  2:10     ` kernel test robot
  0 siblings, 0 replies; 26+ messages in thread
From: kernel test robot @ 2020-11-30  2:10 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 4903 bytes --]

Hi James,

I love your patch! Perhaps something to improve:

[auto build test WARNING on integrity/next-integrity]
[also build test WARNING on linus/master v5.10-rc5 next-20201127]
[cannot apply to security/next-testing dhowells-fs/fscache-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/James-Bottomley/TPM-2-0-trusted-key-rework/20201130-063029
base:   https://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity.git next-integrity
config: x86_64-randconfig-m001-20201130 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-15) 9.3.0

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

smatch warnings:
security/keys/trusted-keys/trusted_tpm2.c:331 tpm2_seal_trusted() warn: unsigned 'payload->blob_len' is never less than zero.

vim +331 security/keys/trusted-keys/trusted_tpm2.c

   217	
   218	/**
   219	 * tpm2_seal_trusted() - seal the payload of a trusted key
   220	 *
   221	 * @chip: TPM chip to use
   222	 * @payload: the key data in clear and encrypted form
   223	 * @options: authentication values and other options
   224	 *
   225	 * Return: < 0 on error and 0 on success.
   226	 */
   227	int tpm2_seal_trusted(struct tpm_chip *chip,
   228			      struct trusted_key_payload *payload,
   229			      struct trusted_key_options *options)
   230	{
   231		unsigned int blob_len;
   232		struct tpm_buf buf;
   233		u32 hash;
   234		int i;
   235		int rc;
   236	
   237		for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++) {
   238			if (options->hash == tpm2_hash_map[i].crypto_id) {
   239				hash = tpm2_hash_map[i].tpm_id;
   240				break;
   241			}
   242		}
   243	
   244		if (i == ARRAY_SIZE(tpm2_hash_map))
   245			return -EINVAL;
   246	
   247		if (!options->keyhandle)
   248			return -EINVAL;
   249	
   250		rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
   251		if (rc)
   252			return rc;
   253	
   254		tpm_buf_append_u32(&buf, options->keyhandle);
   255		tpm2_buf_append_auth(&buf, TPM2_RS_PW,
   256				     NULL /* nonce */, 0,
   257				     0 /* session_attributes */,
   258				     options->keyauth /* hmac */,
   259				     TPM_DIGEST_SIZE);
   260	
   261		/* sensitive */
   262		tpm_buf_append_u16(&buf, 4 + options->blobauth_len + payload->key_len + 1);
   263	
   264		tpm_buf_append_u16(&buf, options->blobauth_len);
   265		if (options->blobauth_len)
   266			tpm_buf_append(&buf, options->blobauth, options->blobauth_len);
   267	
   268		tpm_buf_append_u16(&buf, payload->key_len + 1);
   269		tpm_buf_append(&buf, payload->key, payload->key_len);
   270		tpm_buf_append_u8(&buf, payload->migratable);
   271	
   272		/* public */
   273		tpm_buf_append_u16(&buf, 14 + options->policydigest_len);
   274		tpm_buf_append_u16(&buf, TPM_ALG_KEYEDHASH);
   275		tpm_buf_append_u16(&buf, hash);
   276	
   277		/* policy */
   278		if (options->policydigest_len) {
   279			tpm_buf_append_u32(&buf, 0);
   280			tpm_buf_append_u16(&buf, options->policydigest_len);
   281			tpm_buf_append(&buf, options->policydigest,
   282				       options->policydigest_len);
   283		} else {
   284			tpm_buf_append_u32(&buf, TPM2_OA_USER_WITH_AUTH);
   285			tpm_buf_append_u16(&buf, 0);
   286		}
   287	
   288		/* public parameters */
   289		tpm_buf_append_u16(&buf, TPM_ALG_NULL);
   290		tpm_buf_append_u16(&buf, 0);
   291	
   292		/* outside info */
   293		tpm_buf_append_u16(&buf, 0);
   294	
   295		/* creation PCR */
   296		tpm_buf_append_u32(&buf, 0);
   297	
   298		if (buf.flags & TPM_BUF_OVERFLOW) {
   299			rc = -E2BIG;
   300			goto out;
   301		}
   302	
   303		rc = tpm_send(chip, buf.data, tpm_buf_length(&buf));
   304		if (rc)
   305			goto out;
   306	
   307		blob_len = be32_to_cpup((__be32 *) &buf.data[TPM_HEADER_SIZE]);
   308		if (blob_len > MAX_BLOB_SIZE) {
   309			rc = -E2BIG;
   310			goto out;
   311		}
   312		if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 4 + blob_len) {
   313			rc = -EFAULT;
   314			goto out;
   315		}
   316	
   317		payload->blob_len =
   318			tpm2_key_encode(payload, options,
   319					&buf.data[TPM_HEADER_SIZE + 4],
   320					blob_len);
   321	
   322	out:
   323		tpm_buf_destroy(&buf);
   324	
   325		if (rc > 0) {
   326			if (tpm2_rc_value(rc) == TPM2_RC_HASH)
   327				rc = -EINVAL;
   328			else
   329				rc = -EPERM;
   330		}
 > 331		if (payload->blob_len < 0)
   332			return payload->blob_len;
   333	
   334		return rc;
   335	}
   336	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

[-- Attachment #2: config.gz --]
[-- Type: application/gzip, Size: 31351 bytes --]

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v14 4/5] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
  2020-11-30  2:10     ` kernel test robot
@ 2020-11-30 19:58       ` James Bottomley
  -1 siblings, 0 replies; 26+ messages in thread
From: James Bottomley @ 2020-11-30 19:58 UTC (permalink / raw)
  To: kernel test robot, linux-integrity
  Cc: kbuild-all, Mimi Zohar, Jarkko Sakkinen, David Woodhouse,
	keyrings, David Howells

On Mon, 2020-11-30 at 10:10 +0800, kernel test robot wrote:
[...]
>  > 331		if (payload->blob_len < 0)
>    332			return payload->blob_len;

OK, I can rework this to use the signed version of blob len as below.

James

---

diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index e50563f58900..0d4c6f138b94 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -242,7 +242,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		      struct trusted_key_payload *payload,
 		      struct trusted_key_options *options)
 {
-	unsigned int blob_len;
+	int blob_len = 0;
 	struct tpm_buf buf;
 	u32 hash;
 	u32 flags;
@@ -400,10 +400,9 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		goto out;
 	}
 
-	payload->blob_len =
-		tpm2_key_encode(payload, options,
-				&buf.data[TPM_HEADER_SIZE + 4],
-				blob_len);
+	blob_len = tpm2_key_encode(payload, options,
+				   &buf.data[TPM_HEADER_SIZE + 4],
+				   blob_len);
 
 out:
 	tpm_buf_destroy(&buf);
@@ -414,8 +413,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		else
 			rc = -EPERM;
 	}
-	if (payload->blob_len < 0)
-		return payload->blob_len;
+	if (blob_len < 0)
+		return blob_len;
+
+	payload->blob_len = blob_len;
 
 	return rc;
 }


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH v14 4/5] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
@ 2020-11-30 19:58       ` James Bottomley
  0 siblings, 0 replies; 26+ messages in thread
From: James Bottomley @ 2020-11-30 19:58 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 1343 bytes --]

On Mon, 2020-11-30 at 10:10 +0800, kernel test robot wrote:
[...]
>  > 331		if (payload->blob_len < 0)
>    332			return payload->blob_len;

OK, I can rework this to use the signed version of blob len as below.

James

---

diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index e50563f58900..0d4c6f138b94 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -242,7 +242,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		      struct trusted_key_payload *payload,
 		      struct trusted_key_options *options)
 {
-	unsigned int blob_len;
+	int blob_len = 0;
 	struct tpm_buf buf;
 	u32 hash;
 	u32 flags;
@@ -400,10 +400,9 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		goto out;
 	}
 
-	payload->blob_len =
-		tpm2_key_encode(payload, options,
-				&buf.data[TPM_HEADER_SIZE + 4],
-				blob_len);
+	blob_len = tpm2_key_encode(payload, options,
+				   &buf.data[TPM_HEADER_SIZE + 4],
+				   blob_len);
 
 out:
 	tpm_buf_destroy(&buf);
@@ -414,8 +413,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		else
 			rc = -EPERM;
 	}
-	if (payload->blob_len < 0)
-		return payload->blob_len;
+	if (blob_len < 0)
+		return blob_len;
+
+	payload->blob_len = blob_len;
 
 	return rc;
 }

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH v14 1/5] lib: add ASN.1 encoder
  2020-11-29 22:20 ` [PATCH v14 1/5] lib: add ASN.1 encoder James Bottomley
@ 2020-12-04  4:43   ` Jarkko Sakkinen
  2020-12-04  4:44     ` Jarkko Sakkinen
  0 siblings, 1 reply; 26+ messages in thread
From: Jarkko Sakkinen @ 2020-12-04  4:43 UTC (permalink / raw)
  To: James Bottomley
  Cc: linux-integrity, Mimi Zohar, David Woodhouse, keyrings, David Howells

On Sun, Nov 29, 2020 at 02:20:00PM -0800, James Bottomley wrote:
> We have a need in the TPM2 trusted keys to return the ASN.1 form of
> the TPM key blob so it can be operated on by tools outside of the
> kernel.  The specific tools are the openssl_tpm2_engine, openconnect
> and the Intel tpm2-tss-engine.  To do that, we have to be able to read
> and write the same binary key format the tools use.  The current ASN.1
> decoder does fine for reading, but we need pieces of an ASN.1 encoder
> to write the key blob in binary compatible form.
> 
> For backwards compatibility, the trusted key reader code will still
> accept the two TPM2B quantities that it uses today, but the writer
> will only output the ASN.1 form.
> 
> The current implementation only encodes the ASN.1 bits we actually need.
> 
> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
> Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>

Also:

Tested-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>

I've successfully used this multiple times already.

/Jarkko

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v14 1/5] lib: add ASN.1 encoder
  2020-12-04  4:43   ` Jarkko Sakkinen
@ 2020-12-04  4:44     ` Jarkko Sakkinen
  0 siblings, 0 replies; 26+ messages in thread
From: Jarkko Sakkinen @ 2020-12-04  4:44 UTC (permalink / raw)
  To: James Bottomley
  Cc: linux-integrity, Mimi Zohar, David Woodhouse, keyrings, David Howells

On Fri, Dec 04, 2020 at 06:43:14AM +0200, Jarkko Sakkinen wrote:
> On Sun, Nov 29, 2020 at 02:20:00PM -0800, James Bottomley wrote:
> > We have a need in the TPM2 trusted keys to return the ASN.1 form of
> > the TPM key blob so it can be operated on by tools outside of the
> > kernel.  The specific tools are the openssl_tpm2_engine, openconnect
> > and the Intel tpm2-tss-engine.  To do that, we have to be able to read
> > and write the same binary key format the tools use.  The current ASN.1
> > decoder does fine for reading, but we need pieces of an ASN.1 encoder
> > to write the key blob in binary compatible form.
> > 
> > For backwards compatibility, the trusted key reader code will still
> > accept the two TPM2B quantities that it uses today, but the writer
> > will only output the ASN.1 form.
> > 
> > The current implementation only encodes the ASN.1 bits we actually need.
> > 
> > Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
> > Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> 
> Also:
> 
> Tested-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> 
> I've successfully used this multiple times already.

Hmm... Does this need ack from anyone outside of TPM space?

/Jarkko

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v14 4/5] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
  2020-11-30 19:58       ` James Bottomley
@ 2020-12-04  4:49         ` Jarkko Sakkinen
  -1 siblings, 0 replies; 26+ messages in thread
From: Jarkko Sakkinen @ 2020-12-04  4:49 UTC (permalink / raw)
  To: James Bottomley
  Cc: kernel test robot, linux-integrity, kbuild-all, Mimi Zohar,
	David Woodhouse, keyrings, David Howells

On Mon, Nov 30, 2020 at 11:58:43AM -0800, James Bottomley wrote:
> On Mon, 2020-11-30 at 10:10 +0800, kernel test robot wrote:
> [...]
> >  > 331		if (payload->blob_len < 0)
> >    332			return payload->blob_len;
> 
> OK, I can rework this to use the signed version of blob len as below.
> 
> James

Do you have the patches in a git branch somewhere with this fixed?

I can take from there and apply.

/Jarkko

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v14 4/5] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
@ 2020-12-04  4:49         ` Jarkko Sakkinen
  0 siblings, 0 replies; 26+ messages in thread
From: Jarkko Sakkinen @ 2020-12-04  4:49 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 423 bytes --]

On Mon, Nov 30, 2020 at 11:58:43AM -0800, James Bottomley wrote:
> On Mon, 2020-11-30 at 10:10 +0800, kernel test robot wrote:
> [...]
> >  > 331		if (payload->blob_len < 0)
> >    332			return payload->blob_len;
> 
> OK, I can rework this to use the signed version of blob len as below.
> 
> James

Do you have the patches in a git branch somewhere with this fixed?

I can take from there and apply.

/Jarkko

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v14 4/5] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
  2020-12-04  4:49         ` Jarkko Sakkinen
@ 2020-12-04  4:50           ` Jarkko Sakkinen
  -1 siblings, 0 replies; 26+ messages in thread
From: Jarkko Sakkinen @ 2020-12-04  4:50 UTC (permalink / raw)
  To: James Bottomley
  Cc: kernel test robot, linux-integrity, kbuild-all, Mimi Zohar,
	David Woodhouse, keyrings, David Howells, jarkko

On Fri, Dec 04, 2020 at 06:49:15AM +0200, Jarkko Sakkinen wrote:
> On Mon, Nov 30, 2020 at 11:58:43AM -0800, James Bottomley wrote:
> > On Mon, 2020-11-30 at 10:10 +0800, kernel test robot wrote:
> > [...]
> > >  > 331		if (payload->blob_len < 0)
> > >    332			return payload->blob_len;
> > 
> > OK, I can rework this to use the signed version of blob len as below.
> > 
> > James
> 
> Do you have the patches in a git branch somewhere with this fixed?
> 
> I can take from there and apply.

CC my korg address. Sorry for the latency, I have this LKML migration
going on (MAINTAINERS is already updated). Sometimes forgot to check
this inbox.

/Jarkko

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v14 4/5] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
@ 2020-12-04  4:50           ` Jarkko Sakkinen
  0 siblings, 0 replies; 26+ messages in thread
From: Jarkko Sakkinen @ 2020-12-04  4:50 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 671 bytes --]

On Fri, Dec 04, 2020 at 06:49:15AM +0200, Jarkko Sakkinen wrote:
> On Mon, Nov 30, 2020 at 11:58:43AM -0800, James Bottomley wrote:
> > On Mon, 2020-11-30 at 10:10 +0800, kernel test robot wrote:
> > [...]
> > >  > 331		if (payload->blob_len < 0)
> > >    332			return payload->blob_len;
> > 
> > OK, I can rework this to use the signed version of blob len as below.
> > 
> > James
> 
> Do you have the patches in a git branch somewhere with this fixed?
> 
> I can take from there and apply.

CC my korg address. Sorry for the latency, I have this LKML migration
going on (MAINTAINERS is already updated). Sometimes forgot to check
this inbox.

/Jarkko

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v14 1/5] lib: add ASN.1 encoder
  2020-11-29 22:19 [PATCH v14 0/5] TPM 2.0 trusted key rework James Bottomley
                   ` (4 preceding siblings ...)
  2020-11-29 22:20 ` [PATCH v14 5/5] security: keys: trusted: Make sealed key properly interoperable James Bottomley
@ 2020-12-04 13:40 ` David Howells
  2020-12-04 13:44 ` [PATCH v14 2/5] oid_registry: Add TCG defined OIDS for TPM keys David Howells
  6 siblings, 0 replies; 26+ messages in thread
From: David Howells @ 2020-12-04 13:40 UTC (permalink / raw)
  To: James Bottomley
  Cc: dhowells, linux-integrity, Mimi Zohar, Jarkko Sakkinen,
	David Woodhouse, keyrings

James Bottomley <James.Bottomley@HansenPartnership.com> wrote:

> +	*(data++) = _tagn(CONT, CONS, tag);

You might want to move the _tag() and _tagn() macros into linux/asn1.h so that
you don't need to include asn1_ber_bytecode.h.

But apart from that:

Reviewed-by: David Howells <dhowells@redhat.com>


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v14 2/5] oid_registry: Add TCG defined OIDS for TPM keys
  2020-11-29 22:19 [PATCH v14 0/5] TPM 2.0 trusted key rework James Bottomley
                   ` (5 preceding siblings ...)
  2020-12-04 13:40 ` [PATCH v14 1/5] lib: add ASN.1 encoder David Howells
@ 2020-12-04 13:44 ` David Howells
  2020-12-04 16:01   ` James Bottomley
  6 siblings, 1 reply; 26+ messages in thread
From: David Howells @ 2020-12-04 13:44 UTC (permalink / raw)
  To: James Bottomley
  Cc: dhowells, linux-integrity, Mimi Zohar, Jarkko Sakkinen,
	David Woodhouse, keyrings

James Bottomley <James.Bottomley@HansenPartnership.com> wrote:

> The TCG has defined an OID prefix "2.23.133.10.1" for the various TPM
> key uses.

Is this registered?  I've checked a couple of OID registry sites
(eg. www.oid-info.com) and it seems to be unknown.

David


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v14 2/5] oid_registry: Add TCG defined OIDS for TPM keys
  2020-12-04 13:44 ` [PATCH v14 2/5] oid_registry: Add TCG defined OIDS for TPM keys David Howells
@ 2020-12-04 16:01   ` James Bottomley
  0 siblings, 0 replies; 26+ messages in thread
From: James Bottomley @ 2020-12-04 16:01 UTC (permalink / raw)
  To: David Howells
  Cc: linux-integrity, Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings

On Fri, 2020-12-04 at 13:44 +0000, David Howells wrote:
> James Bottomley <James.Bottomley@HansenPartnership.com> wrote:
> 
> > The TCG has defined an OID prefix "2.23.133.10.1" for the various
> > TPM key uses.
> 
> Is this registered?  I've checked a couple of OID registry sites
> (eg. www.oid-info.com) and it seems to be unknown.

Yes, TCG owns 2.23.133, although I still don't think Monty has
published it yet:

https://lore.kernel.org/linux-integrity/26ED11907FC0F446BB0296B5357EEF0E316CDBB0@CINMBCNA02.e2k.ad.ge.com/

James



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v14 4/5] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
  2020-12-04  4:50           ` Jarkko Sakkinen
@ 2020-12-07 16:23             ` James Bottomley
  -1 siblings, 0 replies; 26+ messages in thread
From: James Bottomley @ 2020-12-07 16:23 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: kernel test robot, linux-integrity, kbuild-all, Mimi Zohar,
	David Woodhouse, keyrings, David Howells, jarkko

On Fri, 2020-12-04 at 06:50 +0200, Jarkko Sakkinen wrote:
> On Fri, Dec 04, 2020 at 06:49:15AM +0200, Jarkko Sakkinen wrote:
> > On Mon, Nov 30, 2020 at 11:58:43AM -0800, James Bottomley wrote:
> > > On Mon, 2020-11-30 at 10:10 +0800, kernel test robot wrote:
> > > [...]
> > > >  > 331		if (payload->blob_len < 0)
> > > >    332			return payload->blob_len;
> > > 
> > > OK, I can rework this to use the signed version of blob len as
> > > below.
> > > 
> > > James
> > 
> > Do you have the patches in a git branch somewhere with this fixed?
> > 
> > I can take from there and apply.
> 
> CC my korg address. Sorry for the latency, I have this LKML migration
> going on (MAINTAINERS is already updated). Sometimes forgot to check
> this inbox.

I'm preparing a v15 but this is basically the only code change.

James

---8>8>8><8<8<8---
From: James Bottomley <James.Bottomley@HansenPartnership.com>
Subject: [PATCH] security: keys: trusted: use ASN.1 TPM2 key format for the
 blobs

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 (openssl_tpm2_engine,
openconnect and tpm2-tss-engine).  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>
Tested-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>

---
v2: Updated encode API, added length checks
v5: correct export format after doing interoperability checks
v7: use prefix tpm2_key_ instead of tpmkey_ for functions
v8: resplit commit
v9: select ASN1_ENCODER
v11: add ASN.1 format description
v13: fix ASN.1 compiler dependency
v14: select OID_REGISTRY add tested by
v15: signed blob len
---
 .../security/keys/trusted-encrypted.rst       |  58 +++++
 include/keys/trusted-type.h                   |   1 +
 security/keys/Kconfig                         |   3 +
 security/keys/trusted-keys/Makefile           |   4 +-
 security/keys/trusted-keys/tpm2key.asn1       |  11 +
 security/keys/trusted-keys/trusted_tpm1.c     |   2 +-
 security/keys/trusted-keys/trusted_tpm2.c     | 210 +++++++++++++++++-
 7 files changed, 280 insertions(+), 9 deletions(-)
 create mode 100644 security/keys/trusted-keys/tpm2key.asn1

diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst
index 1da879a68640..549aa1308949 100644
--- a/Documentation/security/keys/trusted-encrypted.rst
+++ b/Documentation/security/keys/trusted-encrypted.rst
@@ -207,3 +207,61 @@ about the usage can be found in the file
 Another new format 'enc32' has been defined in order to support encrypted keys
 with payload size of 32 bytes. This will initially be used for nvdimm security
 but may expand to other usages that require 32 bytes payload.
+
+
+TPM 2.0 ASN.1 Key Format
+------------------------
+
+The TPM 2.0 ASN.1 key format is designed to be easily recognisable,
+even in binary form (fixing a problem we had with the TPM 1.2 ASN.1
+format) and to be extensible for additions like importable keys and
+policy::
+
+    TPMKey ::= SEQUENCE {
+        type		OBJECT IDENTIFIER
+        emptyAuth	[0] EXPLICIT BOOLEAN OPTIONAL
+        parent		INTEGER
+        pubkey		OCTET STRING
+        privkey		OCTET STRING
+    }
+
+type is what distinguishes the key even in binary form since the OID
+is provided by the TCG to be unique and thus forms a recognizable
+binary pattern at offset 3 in the key.  The OIDs currently made
+available are::
+
+    2.23.133.10.1.3 TPM Loadable key.  This is an asymmetric key (Usually
+                    RSA2048 or Elliptic Curve) which can be imported by a
+                    TPM2_Load() operation.
+
+    2.23.133.10.1.4 TPM Importable Key.  This is an asymmetric key (Usually
+                    RSA2048 or Elliptic Curve) which can be imported by a
+                    TPM2_Import() operation.
+
+    2.23.133.10.1.5 TPM Sealed Data.  This is a set of data (up to 128
+                    bytes) which is sealed by the TPM.  It usually
+                    represents a symmetric key and must be unsealed before
+                    use.
+
+The trusted key code only uses the TPM Sealed Data OID.
+
+emptyAuth is true if the key has well known authorization "".  If it
+is false or not present, the key requires an explicit authorization
+phrase.  This is used by most user space consumers to decide whether
+to prompt for a password.
+
+parent represents the parent key handle, either in the 0x81 MSO space,
+like 0x81000001 for the RSA primary storage key.  Userspace programmes
+also support specifying the primary handle in the 0x40 MSO space.  If
+this happens the Elliptic Curve variant of the primary key using the
+TCG defined template will be generated on the fly into a volatile
+object and used as the parent.  The current kernel code only supports
+the 0x81 MSO form.
+
+pubkey is the binary representation of TPM2B_PRIVATE excluding the
+initial TPM2B header, which can be reconstructed from the ASN.1 octet
+string length.
+
+privkey is the binary representation of TPM2B_PUBLIC excluding the
+initial TPM2B header which can be reconstructed from the ASN.1 octed
+string length.
diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
index b2ed3481c6a0..b2d87ad21714 100644
--- a/include/keys/trusted-type.h
+++ b/include/keys/trusted-type.h
@@ -22,6 +22,7 @@ struct trusted_key_payload {
 	unsigned int key_len;
 	unsigned int blob_len;
 	unsigned char migratable;
+	unsigned char old_format;
 	unsigned char key[MAX_KEY_SIZE + 1];
 	unsigned char blob[MAX_BLOB_SIZE];
 };
diff --git a/security/keys/Kconfig b/security/keys/Kconfig
index 83bc23409164..f0912692469b 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -75,6 +75,9 @@ config TRUSTED_KEYS
 	select CRYPTO_HMAC
 	select CRYPTO_SHA1
 	select CRYPTO_HASH_INFO
+	select ASN1_ENCODER
+	select OID_REGISTRY
+	select ASN1
 	help
 	  This option provides support for creating, sealing, and unsealing
 	  keys in the kernel. Trusted keys are random number symmetric keys,
diff --git a/security/keys/trusted-keys/Makefile b/security/keys/trusted-keys/Makefile
index 7b73cebbb378..f87c43f306d5 100644
--- a/security/keys/trusted-keys/Makefile
+++ b/security/keys/trusted-keys/Makefile
@@ -5,4 +5,6 @@
 
 obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
 trusted-y += trusted_tpm1.o
-trusted-y += trusted_tpm2.o
+
+$(obj)/trusted_tpm2.o: $(obj)/tpm2key.asn1.h
+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..3f6a9d01d1e5
--- /dev/null
+++ b/security/keys/trusted-keys/tpm2key.asn1
@@ -0,0 +1,11 @@
+---
+--- ASN.1 for for TPM 2.0 keys
+---
+
+TPMKey ::= SEQUENCE {
+	type		OBJECT IDENTIFIER ({tpm2_key_type}),
+	emptyAuth	[0] EXPLICIT BOOLEAN OPTIONAL,
+	parent		INTEGER ({tpm2_key_parent}),
+	pubkey		OCTET STRING ({tpm2_key_pub}),
+	privkey		OCTET STRING ({tpm2_key_priv})
+	}
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index eaa2e7ca136e..f235637865b9 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -1011,7 +1011,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 6c6dd88d7bf6..b8ef087528e9 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,165 @@ 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)
+{
+	const int SCRATCH_SIZE = PAGE_SIZE;
+	u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
+	u8 *work = scratch, *work1;
+	u8 *end_work = scratch + SCRATCH_SIZE;
+	u8 *priv, *pub;
+	u16 priv_len, pub_len;
+
+	priv_len = get_unaligned_be16(src) + 2;
+	priv = src;
+
+	src += priv_len;
+
+	pub_len = get_unaligned_be16(src) + 2;
+	pub = src;
+
+	if (!scratch)
+		return -ENOMEM;
+
+	work = asn1_encode_oid(work, end_work, tpm2key_oid,
+			       asn1_oid_len(tpm2key_oid));
+
+	if (options->blobauth_len == 0) {
+		unsigned char bool[3], *w = bool;
+		/* tag 0 is emptyAuth */
+		w = asn1_encode_boolean(w, w + sizeof(bool), true);
+		if (WARN(IS_ERR(w), "BUG: Boolean failed to encode"))
+			return PTR_ERR(w);
+		work = asn1_encode_tag(work, end_work, 0, bool, w - bool);
+	}
+
+	/*
+	 * Assume both octet strings will encode to a 2 byte definite length
+	 *
+	 * 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;
+
+	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);
+
+	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);
+
+	return work1 - payload->blob;
+}
+
+struct tpm2_key_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 tpm2_key_context ctx;
+	u8 *blob;
+
+	memset(&ctx, 0, sizeof(ctx));
+
+	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;
+
+	memcpy(blob, ctx.priv, ctx.priv_len);
+	blob += ctx.priv_len;
+
+	memcpy(blob, ctx.pub, ctx.pub_len);
+
+	return 0;
+}
+
+int tpm2_key_parent(void *context, size_t hdrlen,
+		  unsigned char tag,
+		  const void *value, size_t vlen)
+{
+	struct tpm2_key_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 tpm2_key_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 tpm2_key_pub(void *context, size_t hdrlen,
+	       unsigned char tag,
+	       const void *value, size_t vlen)
+{
+	struct tpm2_key_context *ctx = context;
+
+	ctx->pub = value;
+	ctx->pub_len = vlen;
+
+	return 0;
+}
+
+int tpm2_key_priv(void *context, size_t hdrlen,
+		unsigned char tag,
+		const void *value, size_t vlen)
+{
+	struct tpm2_key_context *ctx = context;
+
+	ctx->priv = value;
+	ctx->priv_len = vlen;
+
+	return 0;
+}
+
 /**
  * tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
  *
@@ -63,7 +228,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		      struct trusted_key_payload *payload,
 		      struct trusted_key_options *options)
 {
-	unsigned int blob_len;
+	int blob_len = 0;
 	struct tpm_buf buf;
 	u32 hash;
 	int i;
@@ -79,6 +244,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;
@@ -146,8 +314,9 @@ 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;
+	blob_len = tpm2_key_encode(payload, options,
+				   &buf.data[TPM_HEADER_SIZE + 4],
+				   blob_len);
 
 out:
 	tpm_buf_destroy(&buf);
@@ -158,6 +327,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		else
 			rc = -EPERM;
 	}
+	if (blob_len < 0)
+		return blob_len;
+
+	payload->blob_len = blob_len;
 
 	return rc;
 }
@@ -184,13 +357,34 @@ 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]);
-	if (private_len > (payload->blob_len - 2))
+	rc = tpm2_key_decode(payload, options, &blob);
+	if (rc) {
+		/* old form */
+		blob = payload->blob;
+		payload->old_format = 1;
+	}
+
+	/* new format carries keyhandle but old format doesn't */
+	if (!options->keyhandle)
+		return -EINVAL;
+
+	/* must be big enough for at least the two be16 size counts */
+	if (payload->blob_len < 4)
+		return -EINVAL;
+
+	private_len = get_unaligned_be16(blob);
+
+	/* must be big enough for following public_len */
+	if (private_len + 2 + 2 > (payload->blob_len))
+		return -E2BIG;
+
+	public_len = get_unaligned_be16(blob + 2 + private_len);
+	if (private_len + 2 + public_len + 2 > payload->blob_len)
 		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;
@@ -206,7 +400,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;
@@ -219,6 +413,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)
-- 
2.26.2



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH v14 4/5] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
@ 2020-12-07 16:23             ` James Bottomley
  0 siblings, 0 replies; 26+ messages in thread
From: James Bottomley @ 2020-12-07 16:23 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 15660 bytes --]

On Fri, 2020-12-04 at 06:50 +0200, Jarkko Sakkinen wrote:
> On Fri, Dec 04, 2020 at 06:49:15AM +0200, Jarkko Sakkinen wrote:
> > On Mon, Nov 30, 2020 at 11:58:43AM -0800, James Bottomley wrote:
> > > On Mon, 2020-11-30 at 10:10 +0800, kernel test robot wrote:
> > > [...]
> > > >  > 331		if (payload->blob_len < 0)
> > > >    332			return payload->blob_len;
> > > 
> > > OK, I can rework this to use the signed version of blob len as
> > > below.
> > > 
> > > James
> > 
> > Do you have the patches in a git branch somewhere with this fixed?
> > 
> > I can take from there and apply.
> 
> CC my korg address. Sorry for the latency, I have this LKML migration
> going on (MAINTAINERS is already updated). Sometimes forgot to check
> this inbox.

I'm preparing a v15 but this is basically the only code change.

James

---8>8>8><8<8<8---
From: James Bottomley <James.Bottomley@HansenPartnership.com>
Subject: [PATCH] security: keys: trusted: use ASN.1 TPM2 key format for the
 blobs

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 (openssl_tpm2_engine,
openconnect and tpm2-tss-engine).  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>
Tested-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>

---
v2: Updated encode API, added length checks
v5: correct export format after doing interoperability checks
v7: use prefix tpm2_key_ instead of tpmkey_ for functions
v8: resplit commit
v9: select ASN1_ENCODER
v11: add ASN.1 format description
v13: fix ASN.1 compiler dependency
v14: select OID_REGISTRY add tested by
v15: signed blob len
---
 .../security/keys/trusted-encrypted.rst       |  58 +++++
 include/keys/trusted-type.h                   |   1 +
 security/keys/Kconfig                         |   3 +
 security/keys/trusted-keys/Makefile           |   4 +-
 security/keys/trusted-keys/tpm2key.asn1       |  11 +
 security/keys/trusted-keys/trusted_tpm1.c     |   2 +-
 security/keys/trusted-keys/trusted_tpm2.c     | 210 +++++++++++++++++-
 7 files changed, 280 insertions(+), 9 deletions(-)
 create mode 100644 security/keys/trusted-keys/tpm2key.asn1

diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst
index 1da879a68640..549aa1308949 100644
--- a/Documentation/security/keys/trusted-encrypted.rst
+++ b/Documentation/security/keys/trusted-encrypted.rst
@@ -207,3 +207,61 @@ about the usage can be found in the file
 Another new format 'enc32' has been defined in order to support encrypted keys
 with payload size of 32 bytes. This will initially be used for nvdimm security
 but may expand to other usages that require 32 bytes payload.
+
+
+TPM 2.0 ASN.1 Key Format
+------------------------
+
+The TPM 2.0 ASN.1 key format is designed to be easily recognisable,
+even in binary form (fixing a problem we had with the TPM 1.2 ASN.1
+format) and to be extensible for additions like importable keys and
+policy::
+
+    TPMKey ::= SEQUENCE {
+        type		OBJECT IDENTIFIER
+        emptyAuth	[0] EXPLICIT BOOLEAN OPTIONAL
+        parent		INTEGER
+        pubkey		OCTET STRING
+        privkey		OCTET STRING
+    }
+
+type is what distinguishes the key even in binary form since the OID
+is provided by the TCG to be unique and thus forms a recognizable
+binary pattern at offset 3 in the key.  The OIDs currently made
+available are::
+
+    2.23.133.10.1.3 TPM Loadable key.  This is an asymmetric key (Usually
+                    RSA2048 or Elliptic Curve) which can be imported by a
+                    TPM2_Load() operation.
+
+    2.23.133.10.1.4 TPM Importable Key.  This is an asymmetric key (Usually
+                    RSA2048 or Elliptic Curve) which can be imported by a
+                    TPM2_Import() operation.
+
+    2.23.133.10.1.5 TPM Sealed Data.  This is a set of data (up to 128
+                    bytes) which is sealed by the TPM.  It usually
+                    represents a symmetric key and must be unsealed before
+                    use.
+
+The trusted key code only uses the TPM Sealed Data OID.
+
+emptyAuth is true if the key has well known authorization "".  If it
+is false or not present, the key requires an explicit authorization
+phrase.  This is used by most user space consumers to decide whether
+to prompt for a password.
+
+parent represents the parent key handle, either in the 0x81 MSO space,
+like 0x81000001 for the RSA primary storage key.  Userspace programmes
+also support specifying the primary handle in the 0x40 MSO space.  If
+this happens the Elliptic Curve variant of the primary key using the
+TCG defined template will be generated on the fly into a volatile
+object and used as the parent.  The current kernel code only supports
+the 0x81 MSO form.
+
+pubkey is the binary representation of TPM2B_PRIVATE excluding the
+initial TPM2B header, which can be reconstructed from the ASN.1 octet
+string length.
+
+privkey is the binary representation of TPM2B_PUBLIC excluding the
+initial TPM2B header which can be reconstructed from the ASN.1 octed
+string length.
diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
index b2ed3481c6a0..b2d87ad21714 100644
--- a/include/keys/trusted-type.h
+++ b/include/keys/trusted-type.h
@@ -22,6 +22,7 @@ struct trusted_key_payload {
 	unsigned int key_len;
 	unsigned int blob_len;
 	unsigned char migratable;
+	unsigned char old_format;
 	unsigned char key[MAX_KEY_SIZE + 1];
 	unsigned char blob[MAX_BLOB_SIZE];
 };
diff --git a/security/keys/Kconfig b/security/keys/Kconfig
index 83bc23409164..f0912692469b 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -75,6 +75,9 @@ config TRUSTED_KEYS
 	select CRYPTO_HMAC
 	select CRYPTO_SHA1
 	select CRYPTO_HASH_INFO
+	select ASN1_ENCODER
+	select OID_REGISTRY
+	select ASN1
 	help
 	  This option provides support for creating, sealing, and unsealing
 	  keys in the kernel. Trusted keys are random number symmetric keys,
diff --git a/security/keys/trusted-keys/Makefile b/security/keys/trusted-keys/Makefile
index 7b73cebbb378..f87c43f306d5 100644
--- a/security/keys/trusted-keys/Makefile
+++ b/security/keys/trusted-keys/Makefile
@@ -5,4 +5,6 @@
 
 obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
 trusted-y += trusted_tpm1.o
-trusted-y += trusted_tpm2.o
+
+$(obj)/trusted_tpm2.o: $(obj)/tpm2key.asn1.h
+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..3f6a9d01d1e5
--- /dev/null
+++ b/security/keys/trusted-keys/tpm2key.asn1
@@ -0,0 +1,11 @@
+---
+--- ASN.1 for for TPM 2.0 keys
+---
+
+TPMKey ::= SEQUENCE {
+	type		OBJECT IDENTIFIER ({tpm2_key_type}),
+	emptyAuth	[0] EXPLICIT BOOLEAN OPTIONAL,
+	parent		INTEGER ({tpm2_key_parent}),
+	pubkey		OCTET STRING ({tpm2_key_pub}),
+	privkey		OCTET STRING ({tpm2_key_priv})
+	}
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index eaa2e7ca136e..f235637865b9 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -1011,7 +1011,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 6c6dd88d7bf6..b8ef087528e9 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,165 @@ 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)
+{
+	const int SCRATCH_SIZE = PAGE_SIZE;
+	u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
+	u8 *work = scratch, *work1;
+	u8 *end_work = scratch + SCRATCH_SIZE;
+	u8 *priv, *pub;
+	u16 priv_len, pub_len;
+
+	priv_len = get_unaligned_be16(src) + 2;
+	priv = src;
+
+	src += priv_len;
+
+	pub_len = get_unaligned_be16(src) + 2;
+	pub = src;
+
+	if (!scratch)
+		return -ENOMEM;
+
+	work = asn1_encode_oid(work, end_work, tpm2key_oid,
+			       asn1_oid_len(tpm2key_oid));
+
+	if (options->blobauth_len == 0) {
+		unsigned char bool[3], *w = bool;
+		/* tag 0 is emptyAuth */
+		w = asn1_encode_boolean(w, w + sizeof(bool), true);
+		if (WARN(IS_ERR(w), "BUG: Boolean failed to encode"))
+			return PTR_ERR(w);
+		work = asn1_encode_tag(work, end_work, 0, bool, w - bool);
+	}
+
+	/*
+	 * Assume both octet strings will encode to a 2 byte definite length
+	 *
+	 * 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;
+
+	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);
+
+	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);
+
+	return work1 - payload->blob;
+}
+
+struct tpm2_key_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 tpm2_key_context ctx;
+	u8 *blob;
+
+	memset(&ctx, 0, sizeof(ctx));
+
+	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;
+
+	memcpy(blob, ctx.priv, ctx.priv_len);
+	blob += ctx.priv_len;
+
+	memcpy(blob, ctx.pub, ctx.pub_len);
+
+	return 0;
+}
+
+int tpm2_key_parent(void *context, size_t hdrlen,
+		  unsigned char tag,
+		  const void *value, size_t vlen)
+{
+	struct tpm2_key_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 tpm2_key_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 tpm2_key_pub(void *context, size_t hdrlen,
+	       unsigned char tag,
+	       const void *value, size_t vlen)
+{
+	struct tpm2_key_context *ctx = context;
+
+	ctx->pub = value;
+	ctx->pub_len = vlen;
+
+	return 0;
+}
+
+int tpm2_key_priv(void *context, size_t hdrlen,
+		unsigned char tag,
+		const void *value, size_t vlen)
+{
+	struct tpm2_key_context *ctx = context;
+
+	ctx->priv = value;
+	ctx->priv_len = vlen;
+
+	return 0;
+}
+
 /**
  * tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
  *
@@ -63,7 +228,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		      struct trusted_key_payload *payload,
 		      struct trusted_key_options *options)
 {
-	unsigned int blob_len;
+	int blob_len = 0;
 	struct tpm_buf buf;
 	u32 hash;
 	int i;
@@ -79,6 +244,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;
@@ -146,8 +314,9 @@ 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;
+	blob_len = tpm2_key_encode(payload, options,
+				   &buf.data[TPM_HEADER_SIZE + 4],
+				   blob_len);
 
 out:
 	tpm_buf_destroy(&buf);
@@ -158,6 +327,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		else
 			rc = -EPERM;
 	}
+	if (blob_len < 0)
+		return blob_len;
+
+	payload->blob_len = blob_len;
 
 	return rc;
 }
@@ -184,13 +357,34 @@ 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]);
-	if (private_len > (payload->blob_len - 2))
+	rc = tpm2_key_decode(payload, options, &blob);
+	if (rc) {
+		/* old form */
+		blob = payload->blob;
+		payload->old_format = 1;
+	}
+
+	/* new format carries keyhandle but old format doesn't */
+	if (!options->keyhandle)
+		return -EINVAL;
+
+	/* must be big enough for at least the two be16 size counts */
+	if (payload->blob_len < 4)
+		return -EINVAL;
+
+	private_len = get_unaligned_be16(blob);
+
+	/* must be big enough for following public_len */
+	if (private_len + 2 + 2 > (payload->blob_len))
+		return -E2BIG;
+
+	public_len = get_unaligned_be16(blob + 2 + private_len);
+	if (private_len + 2 + public_len + 2 > payload->blob_len)
 		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;
@@ -206,7 +400,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;
@@ -219,6 +413,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)
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH v14 4/5] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
  2020-12-07 16:23             ` James Bottomley
@ 2020-12-08 11:02               ` Jarkko Sakkinen
  -1 siblings, 0 replies; 26+ messages in thread
From: Jarkko Sakkinen @ 2020-12-08 11:02 UTC (permalink / raw)
  To: James Bottomley
  Cc: kernel test robot, linux-integrity, kbuild-all, Mimi Zohar,
	David Woodhouse, keyrings, David Howells, jarkko

On Mon, Dec 07, 2020 at 08:23:53AM -0800, James Bottomley wrote:
> On Fri, 2020-12-04 at 06:50 +0200, Jarkko Sakkinen wrote:
> > On Fri, Dec 04, 2020 at 06:49:15AM +0200, Jarkko Sakkinen wrote:
> > > On Mon, Nov 30, 2020 at 11:58:43AM -0800, James Bottomley wrote:
> > > > On Mon, 2020-11-30 at 10:10 +0800, kernel test robot wrote:
> > > > [...]
> > > > >  > 331		if (payload->blob_len < 0)
> > > > >    332			return payload->blob_len;
> > > > 
> > > > OK, I can rework this to use the signed version of blob len as
> > > > below.
> > > > 
> > > > James
> > > 
> > > Do you have the patches in a git branch somewhere with this fixed?
> > > 
> > > I can take from there and apply.
> > 
> > CC my korg address. Sorry for the latency, I have this LKML migration
> > going on (MAINTAINERS is already updated). Sometimes forgot to check
> > this inbox.
> 
> I'm preparing a v15 but this is basically the only code change.
> 
> James

Please actually do. It's always nice to have a lore reference to the
exact version that was merged.

/Jarkko

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v14 4/5] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
@ 2020-12-08 11:02               ` Jarkko Sakkinen
  0 siblings, 0 replies; 26+ messages in thread
From: Jarkko Sakkinen @ 2020-12-08 11:02 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 1067 bytes --]

On Mon, Dec 07, 2020 at 08:23:53AM -0800, James Bottomley wrote:
> On Fri, 2020-12-04 at 06:50 +0200, Jarkko Sakkinen wrote:
> > On Fri, Dec 04, 2020 at 06:49:15AM +0200, Jarkko Sakkinen wrote:
> > > On Mon, Nov 30, 2020 at 11:58:43AM -0800, James Bottomley wrote:
> > > > On Mon, 2020-11-30 at 10:10 +0800, kernel test robot wrote:
> > > > [...]
> > > > >  > 331		if (payload->blob_len < 0)
> > > > >    332			return payload->blob_len;
> > > > 
> > > > OK, I can rework this to use the signed version of blob len as
> > > > below.
> > > > 
> > > > James
> > > 
> > > Do you have the patches in a git branch somewhere with this fixed?
> > > 
> > > I can take from there and apply.
> > 
> > CC my korg address. Sorry for the latency, I have this LKML migration
> > going on (MAINTAINERS is already updated). Sometimes forgot to check
> > this inbox.
> 
> I'm preparing a v15 but this is basically the only code change.
> 
> James

Please actually do. It's always nice to have a lore reference to the
exact version that was merged.

/Jarkko

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v14 3/5] security: keys: trusted: fix TPM2 authorizations
  2020-11-29 22:20 ` [PATCH v14 3/5] security: keys: trusted: fix TPM2 authorizations James Bottomley
@ 2020-12-22 23:01   ` Ken Goldman
  2020-12-23 19:58     ` James Bottomley
  0 siblings, 1 reply; 26+ messages in thread
From: Ken Goldman @ 2020-12-22 23:01 UTC (permalink / raw)
  To: James Bottomley, linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings, David Howells

On 11/29/2020 5:20 PM, James Bottomley wrote:
> 
> Note this is both and enhancement and a potential bug fix.  The TPM
> 2.0 spec requires us to strip leading zeros, meaning empyty
> authorization is a zero length HMAC whereas we're currently passing in
> 20 bytes of zeros.  A lot of TPMs simply accept this as OK, but the
> Microsoft TPM emulator rejects it with TPM_RC_BAD_AUTH, so this patch
> makes the Microsoft TPM emulator work with trusted keys.

1 - To be precise, it strips trailing zeros, but 20 bytes of zero
results in an empty buffer either way.

"
Part 1 19.6.4.3	Authorization Size Convention

Trailing octets of zero are to be removed from any string before it is used as an authValue.
"


2 - If you have a test case for the MS simulator, post it and I'll give it a try.

I did a quick test, power cycle to set platform auth to empty, than
create primary with a parent password 20 bytes of zero, and the
SW TPM accepted it.

This was a password session, not an HMAC session.


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v14 3/5] security: keys: trusted: fix TPM2 authorizations
  2020-12-22 23:01   ` Ken Goldman
@ 2020-12-23 19:58     ` James Bottomley
  2021-01-04 21:56       ` Jarkko Sakkinen
  0 siblings, 1 reply; 26+ messages in thread
From: James Bottomley @ 2020-12-23 19:58 UTC (permalink / raw)
  To: Ken Goldman, linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings, David Howells

On Tue, 2020-12-22 at 18:01 -0500, Ken Goldman wrote:
> On 11/29/2020 5:20 PM, James Bottomley wrote:
> > Note this is both and enhancement and a potential bug fix.  The TPM
> > 2.0 spec requires us to strip leading zeros, meaning empyty
> > authorization is a zero length HMAC whereas we're currently passing
> > in 20 bytes of zeros.  A lot of TPMs simply accept this as OK, but
> > the Microsoft TPM emulator rejects it with TPM_RC_BAD_AUTH, so this
> > patch makes the Microsoft TPM emulator work with trusted keys.
> 
> 1 - To be precise, it strips trailing zeros, but 20 bytes of zero
> results in an empty buffer either way.
> 
> "
> Part 1 19.6.4.3	Authorization Size Convention
> 
> Trailing octets of zero are to be removed from any string before it
> is used as an authValue.
> "
> 
> 
> 2 - If you have a test case for the MS simulator, post it and I'll
> give it a try.
> 
> I did a quick test, power cycle to set platform auth to empty, than
> create primary with a parent password 20 bytes of zero, and the
> SW TPM accepted it.
> 
> This was a password session, not an HMAC session.

I reported it to Microsoft as soon as I found the problem, so, since
this patch set has been languishing for years, I'd hope it would be
fixed by now.  It is still, however, possible there still exist TPM
implementations based on the unfixed Microsoft reference platform.

James



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v14 3/5] security: keys: trusted: fix TPM2 authorizations
  2020-12-23 19:58     ` James Bottomley
@ 2021-01-04 21:56       ` Jarkko Sakkinen
  0 siblings, 0 replies; 26+ messages in thread
From: Jarkko Sakkinen @ 2021-01-04 21:56 UTC (permalink / raw)
  To: James Bottomley
  Cc: Ken Goldman, linux-integrity, Mimi Zohar, David Woodhouse,
	keyrings, David Howells

On Wed, Dec 23, 2020 at 11:58:17AM -0800, James Bottomley wrote:
> On Tue, 2020-12-22 at 18:01 -0500, Ken Goldman wrote:
> > On 11/29/2020 5:20 PM, James Bottomley wrote:
> > > Note this is both and enhancement and a potential bug fix.  The TPM
> > > 2.0 spec requires us to strip leading zeros, meaning empyty
> > > authorization is a zero length HMAC whereas we're currently passing
> > > in 20 bytes of zeros.  A lot of TPMs simply accept this as OK, but
> > > the Microsoft TPM emulator rejects it with TPM_RC_BAD_AUTH, so this
> > > patch makes the Microsoft TPM emulator work with trusted keys.
> > 
> > 1 - To be precise, it strips trailing zeros, but 20 bytes of zero
> > results in an empty buffer either way.
> > 
> > "
> > Part 1 19.6.4.3	Authorization Size Convention
> > 
> > Trailing octets of zero are to be removed from any string before it
> > is used as an authValue.
> > "
> > 
> > 
> > 2 - If you have a test case for the MS simulator, post it and I'll
> > give it a try.
> > 
> > I did a quick test, power cycle to set platform auth to empty, than
> > create primary with a parent password 20 bytes of zero, and the
> > SW TPM accepted it.
> > 
> > This was a password session, not an HMAC session.
> 
> I reported it to Microsoft as soon as I found the problem, so, since
> this patch set has been languishing for years, I'd hope it would be
> fixed by now.  It is still, however, possible there still exist TPM
> implementations based on the unfixed Microsoft reference platform.
> 
> James

One year :-) A bit over but by all practical means... [*]

BTW, can you use my kernel org address for v15? 

[*] https://lore.kernel.org/linux-integrity/1575781600.14069.8.camel@HansenPartnership.com/

/Jarkko

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2021-01-04 21:58 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-29 22:19 [PATCH v14 0/5] TPM 2.0 trusted key rework James Bottomley
2020-11-29 22:20 ` [PATCH v14 1/5] lib: add ASN.1 encoder James Bottomley
2020-12-04  4:43   ` Jarkko Sakkinen
2020-12-04  4:44     ` Jarkko Sakkinen
2020-11-29 22:20 ` [PATCH v14 2/5] oid_registry: Add TCG defined OIDS for TPM keys James Bottomley
2020-11-29 22:20 ` [PATCH v14 3/5] security: keys: trusted: fix TPM2 authorizations James Bottomley
2020-12-22 23:01   ` Ken Goldman
2020-12-23 19:58     ` James Bottomley
2021-01-04 21:56       ` Jarkko Sakkinen
2020-11-29 22:20 ` [PATCH v14 4/5] security: keys: trusted: use ASN.1 TPM2 key format for the blobs James Bottomley
2020-11-30  2:10   ` kernel test robot
2020-11-30  2:10     ` kernel test robot
2020-11-30 19:58     ` James Bottomley
2020-11-30 19:58       ` James Bottomley
2020-12-04  4:49       ` Jarkko Sakkinen
2020-12-04  4:49         ` Jarkko Sakkinen
2020-12-04  4:50         ` Jarkko Sakkinen
2020-12-04  4:50           ` Jarkko Sakkinen
2020-12-07 16:23           ` James Bottomley
2020-12-07 16:23             ` James Bottomley
2020-12-08 11:02             ` Jarkko Sakkinen
2020-12-08 11:02               ` Jarkko Sakkinen
2020-11-29 22:20 ` [PATCH v14 5/5] security: keys: trusted: Make sealed key properly interoperable James Bottomley
2020-12-04 13:40 ` [PATCH v14 1/5] lib: add ASN.1 encoder David Howells
2020-12-04 13:44 ` [PATCH v14 2/5] oid_registry: Add TCG defined OIDS for TPM keys David Howells
2020-12-04 16:01   ` James Bottomley

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.