Linux-Integrity Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH v5 0/6] TPM 2.0 trusted keys with attached policy
@ 2020-01-30 10:18 James Bottomley
  2020-01-30 10:18 ` [PATCH v5 1/6] lib: add ASN.1 encoder James Bottomley
                   ` (6 more replies)
  0 siblings, 7 replies; 25+ messages in thread
From: James Bottomley @ 2020-01-30 10:18 UTC (permalink / raw)
  To: linux-integrity; +Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings

This is mainly a respin to add more spacing as Jarkko requested.
However, I also added the seal/unseal operations to the
openssl_tpm2_engine (next branch):

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

With the result that the kernel code completely failed the
interoperability checks because the ASN.1 format requires the TPM2B
length prepended to the public and private blobs.  I corrected this in
patch 4 and now all the interoperability tests are passing.

General cover letter:

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).  It also simplifies the use of policy with keys because
the ASN.1 format is designed to carry a description of how to
construct the policy, with the result that simple policies (like
authorization and PCR locking) can now be constructed and used in the
kernel, bringing the TPM 2.0 policy use into line with how TPM 1.2
works.

James

---

James Bottomley (6):
  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: add ability to specify arbitrary policy
  security: keys: trusted: implement counter/timer policy

 Documentation/security/keys/trusted-encrypted.rst |  64 ++-
 include/keys/trusted-type.h                       |   7 +-
 include/linux/asn1_encoder.h                      |  32 ++
 include/linux/oid_registry.h                      |   5 +
 include/linux/tpm.h                               |   8 +
 lib/Makefile                                      |   2 +-
 lib/asn1_encoder.c                                | 431 ++++++++++++++++++++
 security/keys/Kconfig                             |   2 +
 security/keys/trusted-keys/Makefile               |   2 +-
 security/keys/trusted-keys/tpm2-policy.c          | 463 ++++++++++++++++++++++
 security/keys/trusted-keys/tpm2-policy.h          |  31 ++
 security/keys/trusted-keys/tpm2key.asn1           |  23 ++
 security/keys/trusted-keys/trusted_tpm1.c         |  50 ++-
 security/keys/trusted-keys/trusted_tpm2.c         | 370 +++++++++++++++--
 14 files changed, 1454 insertions(+), 36 deletions(-)
 create mode 100644 include/linux/asn1_encoder.h
 create mode 100644 lib/asn1_encoder.c
 create mode 100644 security/keys/trusted-keys/tpm2-policy.c
 create mode 100644 security/keys/trusted-keys/tpm2-policy.h
 create mode 100644 security/keys/trusted-keys/tpm2key.asn1

-- 
2.16.4


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

* [PATCH v5 1/6] lib: add ASN.1 encoder
  2020-01-30 10:18 [PATCH v5 0/6] TPM 2.0 trusted keys with attached policy James Bottomley
@ 2020-01-30 10:18 ` James Bottomley
  2020-01-30 10:18 ` [PATCH v5 2/6] oid_registry: Add TCG defined OIDS for TPM keys James Bottomley
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 25+ messages in thread
From: James Bottomley @ 2020-01-30 10:18 UTC (permalink / raw)
  To: linux-integrity; +Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings

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>

---

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
---
 include/linux/asn1_encoder.h |  32 ++++
 lib/Makefile                 |   2 +-
 lib/asn1_encoder.c           | 431 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 464 insertions(+), 1 deletion(-)
 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/Makefile b/lib/Makefile
index c20b1debe9b4..0858fbdf41dc 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -233,7 +233,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) += asn1_decoder.o 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..c7493667656e
--- /dev/null
+++ b/lib/asn1_encoder.c
@@ -0,0 +1,431 @@
+// 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)
+{
+	unsigned char *d = &data[2];
+	int i;
+	bool found = false;
+	int data_len = end_data - data;
+
+	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)
+{
+	int start = 7 + 7 + 7 + 7;
+	unsigned char *data = *_data;
+	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)
+{
+	unsigned char *d = data + 2;
+	int i, ret;
+	int data_len = end_data - data;
+
+	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);
+
+static int asn1_encode_length(unsigned char **data, int *data_len, int len)
+{
+	if (*data_len < 1)
+		return -EINVAL;
+
+	if (len < 0) {
+		*((*data)++) = ASN1_INDEFINITE_LENGTH;
+		(*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.  To encode
+ * in place pass a NULL @string and -1 for @len; all this will do is
+ * add an indefinite length tag and update the data pointer to the
+ * place where the tag contents should be placed.  After the data is
+ * placed, 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 placed it.
+ */
+unsigned char *
+asn1_encode_tag(unsigned char *data, const unsigned char *end_data,
+		u32 tag, const unsigned char *string, int len)
+{
+	int ret;
+	int data_len = end_data - data;
+
+	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 ret;
+	int data_len = end_data - data;
+
+	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 ret;
+	int data_len = end_data - data;
+
+	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);
-- 
2.16.4


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

* [PATCH v5 2/6] oid_registry: Add TCG defined OIDS for TPM keys
  2020-01-30 10:18 [PATCH v5 0/6] TPM 2.0 trusted keys with attached policy James Bottomley
  2020-01-30 10:18 ` [PATCH v5 1/6] lib: add ASN.1 encoder James Bottomley
@ 2020-01-30 10:18 ` James Bottomley
  2020-01-30 10:18 ` [PATCH v5 3/6] security: keys: trusted fix tpm2 authorizations James Bottomley
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 25+ messages in thread
From: James Bottomley @ 2020-01-30 10:18 UTC (permalink / raw)
  To: linux-integrity; +Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings

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>

---

v3: correct OID_TPMImportableKey name
---
 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 657d6bf2c064..f6e2276e5f30 100644
--- a/include/linux/oid_registry.h
+++ b/include/linux/oid_registry.h
@@ -107,6 +107,11 @@ enum OID {
 	OID_gostTC26Sign512B,		/* 1.2.643.7.1.2.1.2.2 */
 	OID_gostTC26Sign512C,		/* 1.2.643.7.1.2.1.2.3 */
 
+	/* 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.16.4


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

* [PATCH v5 3/6] security: keys: trusted fix tpm2 authorizations
  2020-01-30 10:18 [PATCH v5 0/6] TPM 2.0 trusted keys with attached policy James Bottomley
  2020-01-30 10:18 ` [PATCH v5 1/6] lib: add ASN.1 encoder James Bottomley
  2020-01-30 10:18 ` [PATCH v5 2/6] oid_registry: Add TCG defined OIDS for TPM keys James Bottomley
@ 2020-01-30 10:18 ` James Bottomley
  2020-02-25 16:48   ` Jarkko Sakkinen
  2020-01-30 10:18 ` [PATCH v5 4/6] security: keys: trusted: use ASN.1 TPM2 key format for the blobs James Bottomley
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 25+ messages in thread
From: James Bottomley @ 2020-01-30 10:18 UTC (permalink / raw)
  To: linux-integrity; +Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings

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=f572d396fae9206628714fb2ce00f72e94f2258f"

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"

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.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
---
 include/keys/trusted-type.h               |  1 +
 security/keys/trusted-keys/trusted_tpm1.c | 26 +++++++++++++++++++++-----
 security/keys/trusted-keys/trusted_tpm2.c | 10 ++++++----
 3 files changed, 28 insertions(+), 9 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 d2c5ec1e040b..3f33d3f74d3c 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -781,12 +781,28 @@ 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)
+			/*
+			 * 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;
+			} else if (tpm2 &&
+				   opt->blobauth_len <= sizeof(opt->blobauth)) {
+				memcpy(opt->blobauth, args[0].from,
+				       opt->blobauth_len);
+			} else {
 				return -EINVAL;
+			}
+
 			break;
 		case Opt_migratable:
 			if (*args[0].from == '0')
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 08ec7f48f01d..b4a5058107c2 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.16.4


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

* [PATCH v5 4/6] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
  2020-01-30 10:18 [PATCH v5 0/6] TPM 2.0 trusted keys with attached policy James Bottomley
                   ` (2 preceding siblings ...)
  2020-01-30 10:18 ` [PATCH v5 3/6] security: keys: trusted fix tpm2 authorizations James Bottomley
@ 2020-01-30 10:18 ` James Bottomley
  2020-02-03 16:54   ` James Prestwood
  2020-01-30 10:18 ` [PATCH v5 5/6] security: keys: trusted: add ability to specify arbitrary policy James Bottomley
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 25+ messages in thread
From: James Bottomley @ 2020-01-30 10:18 UTC (permalink / raw)
  To: linux-integrity; +Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings

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>

v2: Updated encode API, added length checks
v5: correct export format after doing interoperability checks
---
 Documentation/security/keys/trusted-encrypted.rst |  19 +-
 include/keys/trusted-type.h                       |   6 +-
 include/linux/tpm.h                               |   7 +
 security/keys/Kconfig                             |   2 +
 security/keys/trusted-keys/Makefile               |   2 +-
 security/keys/trusted-keys/tpm2-policy.c          | 372 ++++++++++++++++++++++
 security/keys/trusted-keys/tpm2-policy.h          |  30 ++
 security/keys/trusted-keys/tpm2key.asn1           |  23 ++
 security/keys/trusted-keys/trusted_tpm1.c         |   9 +-
 security/keys/trusted-keys/trusted_tpm2.c         | 328 +++++++++++++++++--
 10 files changed, 771 insertions(+), 27 deletions(-)
 create mode 100644 security/keys/trusted-keys/tpm2-policy.c
 create mode 100644 security/keys/trusted-keys/tpm2-policy.h
 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 50ac8bcd6970..053344c4df5b 100644
--- a/Documentation/security/keys/trusted-encrypted.rst
+++ b/Documentation/security/keys/trusted-encrypted.rst
@@ -60,7 +60,10 @@ Usage::
                      (40 ascii zeros)
        blobauth=     ascii hex auth for sealed data default 0x00...
                      (40 ascii zeros)
-       pcrinfo=	     ascii hex of PCR_INFO or PCR_INFO_LONG (no default)
+       pcrinfo=      ascii hex of PCR_INFO or PCR_INFO_LONG (no
+                     default) on TPM 1.2 and a TPMS_PCR_SELECTION
+                     coupled with a hash of all the selected PCRs on
+                     TPM 2.0 using the selected hash.
        pcrlock=	     pcr number to be extended to "lock" blob
        migratable=   0|1 indicating permission to reseal to new PCR values,
                      default 1 (resealing allowed)
@@ -151,6 +154,20 @@ Load a trusted key from the saved blob::
     f1f8fff03ad0acb083725535636addb08d73dedb9832da198081e5deae84bfaf0409c22b
     e4a8aea2b607ec96931e6f4d4fe563ba
 
+Create a trusted key on TPM 2.0 using an all zero value of PCR16 and
+using the NV storage root 81000001 as the parent::
+
+    $ keyctl add trusted kmk "new 32 keyhandle=0x81000001 hash=sha1 pcrinfo=030000016768033e216468247bd031a0a2d9876d79818f8f" @u
+
+Note the TPMS_PCR_SELECT value for PCR 16 is 03000001 because all
+current TPMs have 24 PCRs, so the initial 03 says there are three
+following bytes of selection and then because the bytes are big
+endian, 16 is bit zero of byte 2. the hash is the sha1 sum of all
+zeros (the value of PCR 16)::
+
+    $ dd if=/dev/zero bs=1 count=20 2>/dev/null|sha1sum
+    6768033e216468247bd031a0a2d9876d79818f8f
+
 Reseal a trusted key under new pcr values::
 
     $ keyctl update 268728824 "update pcrinfo=`cat pcr.blob`"
diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
index b2ed3481c6a0..c117bf598230 100644
--- a/include/keys/trusted-type.h
+++ b/include/keys/trusted-type.h
@@ -14,16 +14,20 @@
 #define MIN_KEY_SIZE			32
 #define MAX_KEY_SIZE			128
 #define MAX_BLOB_SIZE			512
-#define MAX_PCRINFO_SIZE		64
+#define MAX_PCRINFO_SIZE		128
 #define MAX_DIGEST_SIZE			64
 
+#define TPM2_MAX_POLICIES		16
+
 struct trusted_key_payload {
 	struct rcu_head rcu;
 	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];
+	struct tpm2_policies *policies;
 };
 
 struct trusted_key_options {
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 03e9b184411b..e32e9728adce 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -222,10 +222,14 @@ enum tpm2_command_codes {
 	TPM2_CC_CONTEXT_LOAD	        = 0x0161,
 	TPM2_CC_CONTEXT_SAVE	        = 0x0162,
 	TPM2_CC_FLUSH_CONTEXT	        = 0x0165,
+	TPM2_CC_POLICY_AUTHVALUE	= 0x016B,
+	TPM2_CC_POLICY_COUNTER_TIMER	= 0x016D,
+	TPM2_CC_START_AUTH_SESS		= 0x0176,
 	TPM2_CC_VERIFY_SIGNATURE        = 0x0177,
 	TPM2_CC_GET_CAPABILITY	        = 0x017A,
 	TPM2_CC_GET_RANDOM	        = 0x017B,
 	TPM2_CC_PCR_READ	        = 0x017E,
+	TPM2_CC_POLICY_PCR		= 0x017F,
 	TPM2_CC_PCR_EXTEND	        = 0x0182,
 	TPM2_CC_EVENT_SEQUENCE_COMPLETE = 0x0185,
 	TPM2_CC_HASH_SEQUENCE_START     = 0x0186,
@@ -234,6 +238,7 @@ enum tpm2_command_codes {
 };
 
 enum tpm2_permanent_handles {
+	TPM2_RH_NULL		= 0x40000007,
 	TPM2_RS_PW		= 0x40000009,
 };
 
@@ -297,6 +302,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/Kconfig b/security/keys/Kconfig
index 47c041563d41..d65f7614709a 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -76,6 +76,8 @@ config TRUSTED_KEYS
 	select CRYPTO
 	select CRYPTO_HMAC
 	select CRYPTO_SHA1
+	select CRYPTO_SHA256
+	select CRYPTO_SHA512
 	select CRYPTO_HASH_INFO
 	help
 	  This option provides support for creating, sealing, and unsealing
diff --git a/security/keys/trusted-keys/Makefile b/security/keys/trusted-keys/Makefile
index 7b73cebbb378..194febacf362 100644
--- a/security/keys/trusted-keys/Makefile
+++ b/security/keys/trusted-keys/Makefile
@@ -5,4 +5,4 @@
 
 obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
 trusted-y += trusted_tpm1.o
-trusted-y += trusted_tpm2.o
+trusted-y += trusted_tpm2.o tpm2key.asn1.o tpm2-policy.o
diff --git a/security/keys/trusted-keys/tpm2-policy.c b/security/keys/trusted-keys/tpm2-policy.c
new file mode 100644
index 000000000000..de07e576c9f4
--- /dev/null
+++ b/security/keys/trusted-keys/tpm2-policy.c
@@ -0,0 +1,372 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2019 James.Bottomley@HansenPartnership.com
+ */
+
+#include <linux/asn1_encoder.h>
+#include <linux/err.h>
+#include <linux/types.h>
+#include <linux/printk.h>
+#include <linux/string.h>
+#include <linux/tpm.h>
+
+#include <asm/unaligned.h>
+
+#include <crypto/hash.h>
+
+#include <keys/trusted-type.h>
+#include <keys/trusted_tpm.h>
+
+#include "tpm2key.asn1.h"
+#include "tpm2-policy.h"
+
+int tpmkey_code(void *context, size_t hdrlen,
+		unsigned char tag,
+		const void *value, size_t vlen)
+{
+	struct tpm2key_context *ctx = context;
+	u32 code = 0;
+	const u8 *v = value;
+	int i;
+
+	for (i = 0; i < vlen; i++) {
+		code <<= 8;
+		code |= v[i];
+	}
+
+	ctx->policy_code[ctx->policy_count] = code;
+
+	return 0;
+}
+
+int tpmkey_policy(void *context, size_t hdrlen,
+		  unsigned char tag,
+		  const void *value, size_t vlen)
+{
+	struct tpm2key_context *ctx = context;
+
+	ctx->policies[ctx->policy_count] = value;
+	ctx->policy_len[ctx->policy_count++] = vlen;
+
+	return 0;
+}
+
+/* we only support a limited number of policy statement so
+ * make sure we don't have anything we can't support
+ */
+static int tpm2_validate_policy(struct tpm2_policies *pols)
+{
+	int i;
+
+	if (pols->count == 0)
+		return 0;
+
+	for (i = 0; i < pols->count; i++) {
+		switch (pols->code[i]) {
+		case TPM2_CC_POLICY_COUNTER_TIMER:
+		case TPM2_CC_POLICY_PCR:
+		case TPM2_CC_POLICY_AUTHVALUE:
+			break;
+		default:
+			printk(KERN_INFO "tpm2 policy 0x%x is unsupported",
+			       pols->code[i]);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * tpmkey_process_policy - collect the policty from the context
+ * @ctx: the context to collect from
+ * @payload: the payload structure to place it in
+ *
+ * THis function sizes the policy statements and allocates space
+ * within the payload to receive them before copying them over.  It
+ * should be used after the ber decoder has completed successfully
+ */
+int tpmkey_policy_process(struct tpm2key_context *ctx,
+			  struct trusted_key_payload *payload)
+{
+	int tot_len = 0;
+	u8 *buf;
+	int i, ret, len = 0;
+	struct tpm2_policies *pols;
+
+	if (ctx->policy_count == 0)
+		return 0;
+
+	for (i = 0; i < ctx->policy_count; i++)
+		tot_len += ctx->policy_len[i];
+	tot_len += sizeof(*pols);
+
+	pols = kmalloc(tot_len, GFP_KERNEL);
+	if (!pols)
+		return -ENOMEM;
+
+	payload->policies = pols;
+	buf = (u8 *)(pols + 1);
+
+	for (i = 0; i < ctx->policy_count; i++) {
+		pols->policies[i] = &buf[len];
+		pols->len[i] = ctx->policy_len[i];
+		pols->code[i] = ctx->policy_code[i];
+		if (pols->len[i])
+			memcpy(pols->policies[i], ctx->policies[i],
+			       ctx->policy_len[i]);
+		len += ctx->policy_len[i];
+	}
+	pols->count = ctx->policy_count;
+
+	ret = tpm2_validate_policy(pols);
+	if (ret) {
+		kfree(pols);
+		payload->policies = NULL;
+	}
+
+	/* capture the hash and size */
+
+	/* the hash is the second algorithm */
+	pols->hash = get_unaligned_be16(&ctx->pub[2]);
+	/* and the digest appears after the attributes */
+	pols->hash_size = get_unaligned_be16(&ctx->pub[8]);
+
+	return ret;
+}
+
+int tpm2_generate_policy_digest(struct tpm2_policies *pols,
+				u32 hash, u8 *policydigest, u32 *plen)
+{
+	int i;
+	struct crypto_shash *tfm;
+	int rc;
+
+	if (pols->count == 0)
+		return 0;
+
+	tfm = crypto_alloc_shash(hash_algo_name[hash], 0, 0);
+	if (IS_ERR(tfm))
+		return PTR_ERR(tfm);
+
+	rc = crypto_shash_digestsize(tfm);
+	if (WARN(rc > MAX_DIGEST_SIZE,
+		 "BUG: trusted key code has alg %s with digest too large (%d)",
+		 hash_algo_name[hash], rc)) {
+		rc = -EINVAL;
+		goto err;
+	}
+
+	pols->hash = hash;
+	pols->hash_size = rc;
+	*plen = rc;
+
+	/* policy digests always start out all zeros */
+	memset(policydigest, 0, rc);
+
+	for (i = 0; i < pols->count; i++) {
+		u8 *policy = pols->policies[i];
+		int len = pols->len[i];
+		u32 cmd = pols->code[i];
+		u8 digest[MAX_DIGEST_SIZE];
+		u8 code[4];
+		SHASH_DESC_ON_STACK(sdesc, tfm);
+
+		sdesc->tfm = tfm;
+		rc = crypto_shash_init(sdesc);
+		if (rc)
+			goto err;
+
+		/* first hash the previous digest */
+		crypto_shash_update(sdesc, policydigest, *plen);
+
+		/* then hash the command code */
+		put_unaligned_be32(cmd, code);
+		crypto_shash_update(sdesc, code, 4);
+
+		/* commands that need special handling */
+		if (cmd == TPM2_CC_POLICY_COUNTER_TIMER) {
+			SHASH_DESC_ON_STACK(sdesc1, tfm);
+
+			sdesc1->tfm = tfm;
+
+			/* counter timer policies are double hashed */
+			crypto_shash_digest(sdesc1, policy, len,
+					    digest);
+			policy = digest;
+			len = *plen;
+		}
+
+		crypto_shash_update(sdesc, policy, len);
+
+		/* now output the intermediate to the policydigest */
+		crypto_shash_final(sdesc, policydigest);
+
+	}
+	rc = 0;
+
+ err:
+	crypto_free_shash(tfm);
+	return rc;
+}
+
+int tpm2_encode_policy(struct tpm2_policies *pols, u8 **data, u32 *len)
+{
+	const int SCRATCH_SIZE = PAGE_SIZE;
+	u8 *buf = kmalloc(2 * SCRATCH_SIZE, GFP_KERNEL);
+	u8 *work = buf + SCRATCH_SIZE;
+	u8 *ptr;
+	u8 *end_work = work + SCRATCH_SIZE;
+	int i, ret;
+
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < pols->count; i++) {
+		u8 *seq, *tag;
+		u32 cmd = pols->code[i];
+
+		if (WARN(work - buf + 14 + pols->len[i] > 2 * SCRATCH_SIZE,
+			 "BUG: scratch buffer is too small"))
+			return -EINVAL;
+
+		work = asn1_encode_sequence(work, end_work, NULL, -1);
+		seq = work;
+
+		work = asn1_encode_tag(work, end_work, 0, NULL, -1);
+		tag = work;
+
+		work = asn1_encode_integer(work, end_work, cmd);
+		asn1_encode_tag(tag, end_work, 0, NULL, work - tag);
+
+		work = asn1_encode_tag(work, end_work, 1, NULL, -1);
+		tag = work;
+
+		work = asn1_encode_octet_string(work, end_work,
+						pols->policies[i],
+						pols->len[i]);
+
+		asn1_encode_tag(tag, end_work, 1, NULL, work - tag);
+
+		seq = asn1_encode_sequence(seq, end_work, NULL, work - seq);
+		if (IS_ERR(seq)) {
+			ret = PTR_ERR(seq);
+			goto err;
+		}
+	}
+	ptr = asn1_encode_sequence(buf, buf + SCRATCH_SIZE, buf + PAGE_SIZE,
+				   work - buf - PAGE_SIZE);
+	if (IS_ERR(ptr)) {
+		ret = PTR_ERR(ptr);
+		goto err;
+	}
+
+	*data = buf;
+	*len = ptr - buf;
+
+	return 0;
+
+ err:
+	kfree(buf);
+	return ret;
+}
+
+int tpm2_start_policy_session(struct tpm_chip *chip, u16 hash, u32 *handle)
+{
+	struct tpm_buf buf;
+	int rc;
+	int i;
+
+	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_START_AUTH_SESS);
+	if (rc)
+		return rc;
+
+	/* NULL salt key handle */
+	tpm_buf_append_u32(&buf, TPM2_RH_NULL);
+
+	/* NULL bind key handle */
+	tpm_buf_append_u32(&buf, TPM2_RH_NULL);
+
+	/* empty nonce caller */
+	tpm_buf_append_u16(&buf, 20);
+	for (i = 0; i < 20; i++)
+		tpm_buf_append_u8(&buf, 0);
+
+	/* empty auth */
+	tpm_buf_append_u16(&buf, 0);
+
+	/* session type policy */
+	tpm_buf_append_u8(&buf, 0x01);
+
+	/* symmetric encryption parameters */
+
+	/* symmetric algorithm  */
+	tpm_buf_append_u16(&buf, TPM_ALG_NULL);
+
+	/* hash algorithm for session */
+	tpm_buf_append_u16(&buf, hash);
+
+	rc = tpm_send(chip, buf.data, tpm_buf_length(&buf));
+	if (rc)
+		goto out;
+
+	*handle = get_unaligned_be32(buf.data + TPM_HEADER_SIZE);
+
+ out:
+	tpm_buf_destroy(&buf);
+
+	return rc <= 0 ? rc : -EPERM;
+}
+
+int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols,
+			    u32 *handle)
+{
+	int i, rc;
+	const char *failure;
+
+	rc = tpm2_start_policy_session(chip, pols->hash, handle);
+	if (rc)
+		return rc;
+
+	for (i = 0; i < pols->count; i++) {
+		u32 cmd = pols->code[i];
+		struct tpm_buf buf;
+
+		rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, cmd);
+		if (rc)
+			return rc;
+
+		tpm_buf_append_u32(&buf, *handle);
+
+		switch (cmd) {
+		case TPM2_CC_POLICY_PCR:
+			failure = "PCR";
+			/*
+			 * for reasons best known to the TCG we have
+			 * to reverse the two arguments to send to the
+			 * policy command
+			 */
+			tpm_buf_append_u16(&buf, pols->hash_size);
+			tpm_buf_append(&buf, pols->policies[i] + pols->len[i] -
+				       pols->hash_size, pols->hash_size);
+			tpm_buf_append(&buf, pols->policies[i],
+				       pols->len[i] - pols->hash_size);
+			break;
+		default:
+			failure = "unknown policy";
+			break;
+		}
+
+		rc = tpm_send(chip, buf.data, tpm_buf_length(&buf));
+		tpm_buf_destroy(&buf);
+		if (rc) {
+			printk(KERN_NOTICE "TPM policy %s failed, rc=%d\n",
+			       failure, rc);
+			tpm2_flush_context(chip, *handle);
+			*handle = 0;
+			return -EPERM;
+		}
+	}
+
+	return 0;
+}
diff --git a/security/keys/trusted-keys/tpm2-policy.h b/security/keys/trusted-keys/tpm2-policy.h
new file mode 100644
index 000000000000..152c948743f3
--- /dev/null
+++ b/security/keys/trusted-keys/tpm2-policy.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+struct tpm2key_context {
+	u32 parent;
+	const u8 *pub;
+	u32 pub_len;
+	const u8 *priv;
+	u32 priv_len;
+	const u8 *policies[TPM2_MAX_POLICIES];
+	u32 policy_code[TPM2_MAX_POLICIES];
+	u16 policy_len[TPM2_MAX_POLICIES];
+	u8 policy_count;
+};
+
+struct tpm2_policies {
+	u32 code[TPM2_MAX_POLICIES];
+	u8 *policies[TPM2_MAX_POLICIES];
+	u16 len[TPM2_MAX_POLICIES];
+	u8 count;
+	u16 hash;
+	u16 hash_size;
+};
+
+int tpmkey_policy_process(struct tpm2key_context *ctx,
+			  struct trusted_key_payload *payload);
+int tpm2_generate_policy_digest(struct tpm2_policies *pols, u32 hash,
+				u8 *policydigest, u32 *plen);
+int tpm2_encode_policy(struct tpm2_policies *pols, u8 **data, u32 *len);
+int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols,
+			    u32 *handle);
diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1
new file mode 100644
index 000000000000..f930fd812db3
--- /dev/null
+++ b/security/keys/trusted-keys/tpm2key.asn1
@@ -0,0 +1,23 @@
+---
+--- Note: This isn't quite the definition in the standard
+---       However, the Linux asn.1 parser doesn't understand
+---       [2] EXPLICIT SEQUENCE OF OPTIONAL
+---       So there's an extra intermediate TPMPolicySequence
+---       definition to work around this
+
+TPMKey ::= SEQUENCE {
+	type		OBJECT IDENTIFIER ({tpmkey_type}),
+	emptyAuth	[0] EXPLICIT BOOLEAN OPTIONAL,
+	policy		[1] EXPLICIT TPMPolicySequence OPTIONAL,
+	secret		[2] EXPLICIT OCTET STRING OPTIONAL,
+	parent		INTEGER ({tpmkey_parent}),
+	pubkey		OCTET STRING ({tpmkey_pub}),
+	privkey		OCTET STRING ({tpmkey_priv})
+	}
+
+TPMPolicySequence ::= SEQUENCE OF TPMPolicy
+
+TPMPolicy ::= SEQUENCE {
+	commandCode		[0] EXPLICIT INTEGER ({tpmkey_code}),
+	commandPolicy		[1] EXPLICIT OCTET STRING ({tpmkey_policy})
+	}
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index 3f33d3f74d3c..8c3ce175f14f 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -1007,7 +1007,7 @@ static int trusted_instantiate(struct key *key,
 		goto out;
 	}
 
-	if (!options->keyhandle) {
+	if (!options->keyhandle && !tpm2) {
 		ret = -EINVAL;
 		goto out;
 	}
@@ -1061,6 +1061,7 @@ static void trusted_rcu_free(struct rcu_head *rcu)
 	struct trusted_key_payload *p;
 
 	p = container_of(rcu, struct trusted_key_payload, rcu);
+	kzfree(p->policies);
 	kzfree(p);
 }
 
@@ -1180,7 +1181,11 @@ static long trusted_read(const struct key *key, char __user *buffer,
  */
 static void trusted_destroy(struct key *key)
 {
-	kzfree(key->payload.data[0]);
+	struct trusted_key_payload *p;
+
+	p = key->payload.data[0];
+	kzfree(p->policies);
+	kzfree(p);
 }
 
 struct key_type key_type_trusted = {
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index b4a5058107c2..293db0aaada6 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,11 @@
 #include <keys/trusted-type.h>
 #include <keys/trusted_tpm.h>
 
+#include <asm/unaligned.h>
+
+#include "tpm2key.asn1.h"
+#include "tpm2-policy.h"
+
 static struct tpm2_hash tpm2_hash_map[] = {
 	{HASH_ALGO_SHA1, TPM_ALG_SHA1},
 	{HASH_ALGO_SHA256, TPM_ALG_SHA256},
@@ -20,6 +27,178 @@ 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);
+	}
+
+	if (payload->policies) {
+		u8 *encoded_pols;
+		u32 encoded_pol_len;
+		int ret;
+
+		ret = tpm2_encode_policy(payload->policies, &encoded_pols,
+					 &encoded_pol_len);
+		if (ret)
+			return ret;
+
+		work = asn1_encode_tag(work, end_work, 1, encoded_pols,
+				       encoded_pol_len);
+		kfree(encoded_pols);
+	}
+
+	/*
+	 * 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;
+}
+
+static int tpm2_key_decode(struct trusted_key_payload *payload,
+			   struct trusted_key_options *options,
+			   u8 **buf)
+{
+	int ret;
+	struct tpm2key_context ctx;
+	u8 *blob;
+
+	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;
+
+	ret = tpmkey_policy_process(&ctx, payload);
+	if (ret) {
+		kfree(blob);
+		return ret;
+	}
+
+	*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 tpmkey_parent(void *context, size_t hdrlen,
+		  unsigned char tag,
+		  const void *value, size_t vlen)
+{
+	struct tpm2key_context *ctx = context;
+	const u8 *v = value;
+	int i;
+
+	ctx->parent = 0;
+	for (i = 0; i < vlen; i++) {
+		ctx->parent <<= 8;
+		ctx->parent |= v[i];
+	}
+
+	return 0;
+}
+
+int tpmkey_type(void *context, size_t hdrlen,
+		unsigned char tag,
+		const void *value, size_t vlen)
+{
+	enum OID oid = look_up_OID(value, vlen);
+
+	if (oid != OID_TPMSealedData) {
+		char buffer[50];
+
+		sprint_oid(value, vlen, buffer, sizeof(buffer));
+		pr_debug("OID is \"%s\" which is not TPMSealedData\n",
+			 buffer);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int tpmkey_pub(void *context, size_t hdrlen,
+	       unsigned char tag,
+	       const void *value, size_t vlen)
+{
+	struct tpm2key_context *ctx = context;
+
+	ctx->pub = value;
+	ctx->pub_len = vlen;
+
+	return 0;
+}
+
+int tpmkey_priv(void *context, size_t hdrlen,
+		unsigned char tag,
+		const void *value, size_t vlen)
+{
+	struct tpm2key_context *ctx = context;
+
+	ctx->priv = value;
+	ctx->priv_len = vlen;
+
+	return 0;
+}
+
 /**
  * tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
  *
@@ -66,6 +245,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;
 
@@ -79,6 +259,45 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 	if (i == ARRAY_SIZE(tpm2_hash_map))
 		return -EINVAL;
 
+	if (!options->keyhandle)
+		return -EINVAL;
+
+	if (options->pcrinfo_len != 0) {
+		struct tpm2_policies *pols;
+		static u8 *scratch;
+		/* 4 array len, 2 hash alg */
+		const int len = 4 + 2 + options->pcrinfo_len;
+
+		pols = kmalloc(sizeof(*pols) + len, GFP_KERNEL);
+		if (!pols)
+			return -ENOMEM;
+
+		pols->count = 1;
+		pols->len[0] = len;
+		scratch = (u8 *)(pols + 1);
+		pols->policies[0] = scratch;
+		pols->code[0] = TPM2_CC_POLICY_PCR;
+
+		put_unaligned_be32(1, &scratch[0]);
+		put_unaligned_be16(hash, &scratch[4]);
+		memcpy(&scratch[6], options->pcrinfo, options->pcrinfo_len);
+		payload->policies = pols;
+	}
+
+	if (options->policydigest_len != 0 && payload->policies) {
+		/* can't specify both a digest and a policy */
+		return -EINVAL;
+	}
+
+	if (payload->policies) {
+		rc = tpm2_generate_policy_digest(payload->policies,
+						 options->hash,
+						 options->policydigest,
+						 &options->policydigest_len);
+		if (rc)
+			return rc;
+	}
+
 	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
 	if (rc)
 		return rc;
@@ -91,31 +310,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);
@@ -146,8 +366,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 +380,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 +408,45 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 	unsigned int private_len;
 	unsigned int public_len;
 	unsigned int blob_len;
+	u8 *blob, *pub;
 	int rc;
+	u32 attrs;
+
+	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);
 
-	private_len = be16_to_cpup((__be16 *) &payload->blob[0]);
-	if (private_len > (payload->blob_len - 2))
+	/* must be big enough for following public_len */
+	if (private_len + 2 + 2 > (payload->blob_len))
 		return -E2BIG;
 
-	public_len = be16_to_cpup((__be16 *) &payload->blob[2 + private_len]);
+	public_len = get_unaligned_be16(blob + 2 + private_len);
+	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;
@@ -206,7 +462,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 +475,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)
@@ -248,28 +506,44 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 	u16 data_len;
 	u8 *data;
 	int rc;
+	u32 policyhandle;
+
+	if (payload->policies && options->policyhandle)
+		/* can't have both a passed in policy and a key resident one */
+		return -EINVAL;
 
 	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
 	if (rc)
 		return rc;
 
+	if (payload->policies) {
+		rc = tpm2_get_policy_session(chip, payload->policies,
+					     &policyhandle);
+		if (rc)
+			return rc;
+	} else {
+		policyhandle = options->policyhandle;
+	}
+
 	tpm_buf_append_u32(&buf, blob_handle);
 	tpm2_buf_append_auth(&buf,
-			     options->policyhandle ?
-			     options->policyhandle : TPM2_RS_PW,
+			     policyhandle ?
+			     policyhandle : TPM2_RS_PW,
 			     NULL /* nonce */, 0,
 			     TPM2_SA_CONTINUE_SESSION,
 			     options->blobauth /* hmac */,
 			     options->blobauth_len);
 
 	rc = tpm_send(chip, buf.data, tpm_buf_length(&buf));
+	if (payload->policies)
+		tpm2_flush_context(chip, policyhandle);
 	if (rc > 0)
 		rc = -EPERM;
 
 	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;
 		}
@@ -280,9 +554,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.16.4


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

* [PATCH v5 5/6] security: keys: trusted: add ability to specify arbitrary policy
  2020-01-30 10:18 [PATCH v5 0/6] TPM 2.0 trusted keys with attached policy James Bottomley
                   ` (3 preceding siblings ...)
  2020-01-30 10:18 ` [PATCH v5 4/6] security: keys: trusted: use ASN.1 TPM2 key format for the blobs James Bottomley
@ 2020-01-30 10:18 ` James Bottomley
  2020-01-30 10:18 ` [PATCH v5 6/6] security: keys: trusted: implement counter/timer policy James Bottomley
  2020-02-20 20:17 ` [PATCH v5 0/6] TPM 2.0 trusted keys with attached policy Jarkko Sakkinen
  6 siblings, 0 replies; 25+ messages in thread
From: James Bottomley @ 2020-01-30 10:18 UTC (permalink / raw)
  To: linux-integrity; +Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings

This patch adds a policy= argument to key creation.  The policy is the
standard tss policymaker format and each separate policy line must
have a newline after it.

Thus to construct a policy requiring authorized value and pcr 16
locking using a sha256 hash, the policy (policy.txt) file would be two
lines:

0000017F00000001000B03000001303095B49BE85E381E5B20E557E46363EF55B0F43B132C2D8E3DE9AC436656F2
0000016b

This can be inserted into the key with

keyctl add trusted kmk "new 32 policy=`cat policy.txt` keyhandle=0x81000001 hash=sha256" @u

Note that although a few policies work like this, most require special
handling which must be added to the kernel policy construction
routine.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
---
 Documentation/security/keys/trusted-encrypted.rst | 16 +++++++
 security/keys/trusted-keys/tpm2-policy.c          | 53 +++++++++++++++++++++++
 security/keys/trusted-keys/tpm2-policy.h          |  1 +
 security/keys/trusted-keys/trusted_tpm1.c         | 15 +++++++
 4 files changed, 85 insertions(+)

diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst
index 053344c4df5b..b68d3eb73f00 100644
--- a/Documentation/security/keys/trusted-encrypted.rst
+++ b/Documentation/security/keys/trusted-encrypted.rst
@@ -76,6 +76,9 @@ Usage::
        policyhandle= handle to an authorization policy session that defines the
                      same policy and with the same hash algorithm as was used to
                      seal the key.
+       policy=       specify an arbitrary set of policies.  These must
+                     be in policymaker format with each separate
+                     policy line newline terminated.
 
 "keyctl print" returns an ascii hex copy of the sealed key, which is in standard
 TPM_STORED_DATA format.  The key length for new keys are always in bytes.
@@ -168,6 +171,19 @@ zeros (the value of PCR 16)::
     $ dd if=/dev/zero bs=1 count=20 2>/dev/null|sha1sum
     6768033e216468247bd031a0a2d9876d79818f8f
 
+You can also specify arbitrary policy in policymaker format, so a two
+value policy (the pcr example above and authvalue) would look like
+this in policymaker format::
+
+    0000017F000000010004030000016768033e216468247bd031a0a2d9876d79818f8f
+    0000016b
+
+This can be placed in a file (say policy.txt) and then added to the key as::
+
+    $ keyctl add trusted kmk "new 32 keyhandle=0x81000001 hash=sha1 policy=`cat policy.txt`" @u
+
+The newlines in the file policy.txt will be automatically processed.
+
 Reseal a trusted key under new pcr values::
 
     $ keyctl update 268728824 "update pcrinfo=`cat pcr.blob`"
diff --git a/security/keys/trusted-keys/tpm2-policy.c b/security/keys/trusted-keys/tpm2-policy.c
index de07e576c9f4..4cc478feaeb1 100644
--- a/security/keys/trusted-keys/tpm2-policy.c
+++ b/security/keys/trusted-keys/tpm2-policy.c
@@ -370,3 +370,56 @@ int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols,
 
 	return 0;
 }
+
+int tpm2_parse_policies(struct tpm2_policies **ppols, char *str)
+{
+	struct tpm2_policies *pols;
+	char *p;
+	u8 *ptr;
+	int i = 0, left = PAGE_SIZE, res;
+
+	pols = kmalloc(left, GFP_KERNEL);
+	if (!pols)
+		return -ENOMEM;
+
+	ptr = (u8 *)(pols + 1);
+	left -= ptr - (u8 *)pols;
+
+	while ((p = strsep(&str, "\n"))) {
+		if (*p == '\0' || *p == '\n')
+			continue;
+
+		pols->len[i] = strlen(p)/2;
+		if (pols->len[i] > left) {
+			res = -E2BIG;
+			goto err;
+		}
+
+		res = hex2bin(ptr, p, pols->len[i]);
+		if (res)
+			goto err;
+
+		/* get command code and skip past */
+		pols->code[i] = get_unaligned_be32(ptr);
+		pols->policies[i] = ptr + 4;
+		ptr += pols->len[i];
+		left -= pols->len[i];
+		pols->len[i] -= 4;
+
+		/*
+		 * FIXME: this does leave the code embedded in dead
+		 * regions of the memory, but it's easier than
+		 * hexdumping to a temporary or copying over
+		 */
+		i++;
+	}
+
+	pols->count = i;
+	*ppols = pols;
+
+	return 0;
+
+ err:
+	kfree(pols);
+	return res;
+}
diff --git a/security/keys/trusted-keys/tpm2-policy.h b/security/keys/trusted-keys/tpm2-policy.h
index 152c948743f3..cb804a544ced 100644
--- a/security/keys/trusted-keys/tpm2-policy.h
+++ b/security/keys/trusted-keys/tpm2-policy.h
@@ -28,3 +28,4 @@ int tpm2_generate_policy_digest(struct tpm2_policies *pols, u32 hash,
 int tpm2_encode_policy(struct tpm2_policies *pols, u8 **data, u32 *len);
 int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols,
 			    u32 *handle);
+int tpm2_parse_policies(struct tpm2_policies **ppols, char *str);
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index 8c3ce175f14f..e519d31c9bbf 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -29,6 +29,8 @@
 
 #include <keys/trusted_tpm.h>
 
+#include "tpm2-policy.h"
+
 static const char hmac_alg[] = "hmac(sha1)";
 static const char hash_alg[] = "sha1";
 static struct tpm_chip *chip;
@@ -709,6 +711,7 @@ enum {
 	Opt_hash,
 	Opt_policydigest,
 	Opt_policyhandle,
+	Opt_policy,
 };
 
 static const match_table_t key_tokens = {
@@ -724,6 +727,7 @@ static const match_table_t key_tokens = {
 	{Opt_hash, "hash=%s"},
 	{Opt_policydigest, "policydigest=%s"},
 	{Opt_policyhandle, "policyhandle=%s"},
+	{Opt_policy, "policy=%s"},
 	{Opt_err, NULL}
 };
 
@@ -850,6 +854,17 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
 				return -EINVAL;
 			opt->policyhandle = handle;
 			break;
+
+		case Opt_policy:
+			if (pay->policies)
+				return -EINVAL;
+			if (!tpm2)
+				return -EINVAL;
+			res = tpm2_parse_policies(&pay->policies, args[0].from);
+			if (res)
+				return res;
+			break;
+
 		default:
 			return -EINVAL;
 		}
-- 
2.16.4


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

* [PATCH v5 6/6] security: keys: trusted: implement counter/timer policy
  2020-01-30 10:18 [PATCH v5 0/6] TPM 2.0 trusted keys with attached policy James Bottomley
                   ` (4 preceding siblings ...)
  2020-01-30 10:18 ` [PATCH v5 5/6] security: keys: trusted: add ability to specify arbitrary policy James Bottomley
@ 2020-01-30 10:18 ` James Bottomley
  2020-02-20 20:17 ` [PATCH v5 0/6] TPM 2.0 trusted keys with attached policy Jarkko Sakkinen
  6 siblings, 0 replies; 25+ messages in thread
From: James Bottomley @ 2020-01-30 10:18 UTC (permalink / raw)
  To: linux-integrity; +Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings

This is actually a generic policy allowing a range of comparisons
against any value set in the TPM Clock, which includes things like the
reset count, a monotonic millisecond count and the restart count.  The
most useful comparison is against the millisecond count for expiring
keys.  However, you have to remember that currently Linux doesn't try
to sync the epoch timer with the TPM, so the expiration is actually
measured in how long the TPM itself has been powered on ... the TPM
timer doesn't count while the system is powered down.  The millisecond
counter is a u64 quantity found at offset 8 in the timer structure,
and the <= comparision operand is 9, so a policy set to expire after the
TPM has been up for 100 seconds would look like

0000016d00000000000f424000080009

Where 0x16d is the counter timer policy code and 0xf4240 is 100 000 in
hex.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
---
 Documentation/security/keys/trusted-encrypted.rst | 29 ++++++++++++++++
 include/linux/tpm.h                               |  1 +
 security/keys/trusted-keys/tpm2-policy.c          | 40 ++++++++++++++++++++++-
 security/keys/trusted-keys/trusted_tpm2.c         | 36 +++++++++++++++++++-
 4 files changed, 104 insertions(+), 2 deletions(-)

diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst
index b68d3eb73f00..53a6196c7df9 100644
--- a/Documentation/security/keys/trusted-encrypted.rst
+++ b/Documentation/security/keys/trusted-encrypted.rst
@@ -241,3 +241,32 @@ 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.
+
+Appendix
+--------
+
+TPM 2.0 Policies
+----------------
+
+The current TPM supports PCR lock policies as documented above and
+CounterTimer policies which can be used to create expiring keys.  One
+caveat with expiring keys is that the TPM millisecond counter does not
+update while a system is powered off and Linux does not sync the TPM
+millisecond count with its internal clock, so the best you can expire
+in is in terms of how long any given TPM has been powered on.  (FIXME:
+Linux should simply update the millisecond clock to the current number
+of seconds past the epoch on boot).
+
+A CounterTimer policy is expressed in terms of length and offset
+against the TPM clock structure (TPMS_TIME_INFO), which looks like the
+packed structure::
+
+    struct tpms_time_info {
+            u64 uptime;       /* time in ms since last start or reset */
+	    u64 clock;        /* cumulative uptime in ms */
+	    u32 resetcount;   /* numer of times the TPM has been reset */
+	    u32 restartcount; /* number of times the TPM has been restarted */
+	    u8  safe          /* time was safely loaded from NVRam */
+    };
+
+The usual comparison for expiring keys is against clock, at offset 8.
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index e32e9728adce..5026a06977e1 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -233,6 +233,7 @@ enum tpm2_command_codes {
 	TPM2_CC_PCR_EXTEND	        = 0x0182,
 	TPM2_CC_EVENT_SEQUENCE_COMPLETE = 0x0185,
 	TPM2_CC_HASH_SEQUENCE_START     = 0x0186,
+	TPM2_CC_POLICY_PASSWORD		= 0x018c,
 	TPM2_CC_CREATE_LOADED           = 0x0191,
 	TPM2_CC_LAST		        = 0x0193, /* Spec 1.36 */
 };
diff --git a/security/keys/trusted-keys/tpm2-policy.c b/security/keys/trusted-keys/tpm2-policy.c
index 4cc478feaeb1..90da9fa4ca02 100644
--- a/security/keys/trusted-keys/tpm2-policy.c
+++ b/security/keys/trusted-keys/tpm2-policy.c
@@ -197,7 +197,8 @@ int tpm2_generate_policy_digest(struct tpm2_policies *pols,
 			len = *plen;
 		}
 
-		crypto_shash_update(sdesc, policy, len);
+		if (len)
+			crypto_shash_update(sdesc, policy, len);
 
 		/* now output the intermediate to the policydigest */
 		crypto_shash_final(sdesc, policydigest);
@@ -332,6 +333,16 @@ int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols,
 		u32 cmd = pols->code[i];
 		struct tpm_buf buf;
 
+		if (cmd == TPM2_CC_POLICY_AUTHVALUE)
+			/*
+			 * both PolicyAuthValue and PolicyPassword
+			 * hash to the same thing, but one triggers
+			 * HMAC authentication and the other simple
+			 * authentication.  Since we have no HMAC
+			 * code, we're choosing the simple
+			 */
+			cmd = TPM2_CC_POLICY_PASSWORD;
+
 		rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, cmd);
 		if (rc)
 			return rc;
@@ -352,8 +363,35 @@ int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols,
 			tpm_buf_append(&buf, pols->policies[i],
 				       pols->len[i] - pols->hash_size);
 			break;
+
+		case TPM2_CC_POLICY_COUNTER_TIMER: {
+			/*
+			 * the format of this is the last two u16
+			 * quantities are the offset and operation
+			 * respectively.  The rest is operandB which
+			 * must be zero padded in a hash digest
+			 */
+			u16 opb_len = pols->len[i] - 4;
+
+			if (opb_len > pols->hash_size)
+				return -EINVAL;
+
+			tpm_buf_append_u16(&buf, opb_len);
+			tpm_buf_append(&buf, pols->policies[i], opb_len);
+
+			/* offset and operand*/
+			tpm_buf_append(&buf, pols->policies[i] + opb_len, 4);
+			failure = "Counter Timer";
+
+			break;
+		}
+
 		default:
 			failure = "unknown policy";
+			if (pols->len[i])
+				tpm_buf_append(&buf, pols->policies[i],
+					       pols->len[i]);
+
 			break;
 		}
 
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 293db0aaada6..63b0ff1d3385 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -248,6 +248,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 	u32 flags;
 	int i;
 	int rc;
+	static const int POLICY_SIZE = 2 * PAGE_SIZE;
 
 	for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++) {
 		if (options->hash == tpm2_hash_map[i].crypto_id) {
@@ -268,7 +269,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		/* 4 array len, 2 hash alg */
 		const int len = 4 + 2 + options->pcrinfo_len;
 
-		pols = kmalloc(sizeof(*pols) + len, GFP_KERNEL);
+		pols = kmalloc(POLICY_SIZE, GFP_KERNEL);
 		if (!pols)
 			return -ENOMEM;
 
@@ -289,6 +290,39 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		return -EINVAL;
 	}
 
+	/*
+	 * if we already have a policy, we have to add authorization
+	 * to it.  If we don't, we can simply follow the usual
+	 * non-policy route.
+	 */
+	if (options->blobauth_len != 0 && payload->policies) {
+		struct tpm2_policies *pols;
+		static u8 *scratch;
+		int i;
+		bool found = false;
+
+		pols = payload->policies;
+
+		/* make sure it's not already in policy */
+		for (i = 0; i < pols->count; i++) {
+			if (pols->code[i] == TPM2_CC_POLICY_AUTHVALUE) {
+				found = true;
+
+				break;
+			}
+		}
+
+		if (!found) {
+			i = pols->count++;
+			scratch = pols->policies[i - 1] + pols->len[i - 1];
+
+			/* the TPM2_PolicyPassword command has no payload */
+			pols->policies[i] = scratch;
+			pols->len[i] = 0;
+			pols->code[i] = TPM2_CC_POLICY_AUTHVALUE;
+		}
+	}
+
 	if (payload->policies) {
 		rc = tpm2_generate_policy_digest(payload->policies,
 						 options->hash,
-- 
2.16.4


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

* Re: [PATCH v5 4/6] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
  2020-01-30 10:18 ` [PATCH v5 4/6] security: keys: trusted: use ASN.1 TPM2 key format for the blobs James Bottomley
@ 2020-02-03 16:54   ` James Prestwood
  2020-02-27  0:02     ` James Bottomley
  0 siblings, 1 reply; 25+ messages in thread
From: James Prestwood @ 2020-02-03 16:54 UTC (permalink / raw)
  To: James Bottomley, linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings

Hi James,

<snip>

> diff --git a/security/keys/trusted-keys/tpm2key.asn1
> b/security/keys/trusted-keys/tpm2key.asn1
> new file mode 100644
> index 000000000000..f930fd812db3
> --- /dev/null
> +++ b/security/keys/trusted-keys/tpm2key.asn1
> @@ -0,0 +1,23 @@
> +---
> +--- Note: This isn't quite the definition in the standard
> +---       However, the Linux asn.1 parser doesn't understand
> +---       [2] EXPLICIT SEQUENCE OF OPTIONAL
> +---       So there's an extra intermediate TPMPolicySequence
> +---       definition to work around this
> +
> +TPMKey ::= SEQUENCE {
> +	type		OBJECT IDENTIFIER ({tpmkey_type}),
> +	emptyAuth	[0] EXPLICIT BOOLEAN OPTIONAL,
> +	policy		[1] EXPLICIT TPMPolicySequence OPTIONAL,
> +	secret		[2] EXPLICIT OCTET STRING OPTIONAL,
> +	parent		INTEGER ({tpmkey_parent}),
> +	pubkey		OCTET STRING ({tpmkey_pub}),
> +	privkey		OCTET STRING ({tpmkey_priv})
> +	}
> +
> +TPMPolicySequence ::= SEQUENCE OF TPMPolicy
> +
> +TPMPolicy ::= SEQUENCE {
> +	commandCode		[0] EXPLICIT INTEGER ({tpmkey_code}),
> +	commandPolicy		[1] EXPLICIT OCTET STRING
> ({tpmkey_policy})
> +	}

I have been using your set of patches in order to get this ASN.1
parser/definition. I am implementing an asymmetric key parser/type TPM2
keys for enc/dec/sign/verify using keyctl. Note that this
implementation goes in crypto/asymmetric_keys/, and your patches sit in
security/keys/trusted-keys/.

Currently I am just including "../../security/keys/trusted-
keys/{tpm2key.asn1.h,tpm2-policy.h}" in order to use the ASN.1 parser
to verify my keys, but this obviously isn't going to fly.

Do you (or anyone) have any ideas as to how both trusted keys and
asymmetric keys could share this ASN.1 parser/definition? Some common
area that both security and crypto could include? Or maybe there is
some common way the kernel does things like this?

Thanks,
James



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

* Re: [PATCH v5 0/6] TPM 2.0 trusted keys with attached policy
  2020-01-30 10:18 [PATCH v5 0/6] TPM 2.0 trusted keys with attached policy James Bottomley
                   ` (5 preceding siblings ...)
  2020-01-30 10:18 ` [PATCH v5 6/6] security: keys: trusted: implement counter/timer policy James Bottomley
@ 2020-02-20 20:17 ` Jarkko Sakkinen
  6 siblings, 0 replies; 25+ messages in thread
From: Jarkko Sakkinen @ 2020-02-20 20:17 UTC (permalink / raw)
  To: James Bottomley; +Cc: linux-integrity, Mimi Zohar, David Woodhouse, keyrings

On Thu, Jan 30, 2020 at 11:18:06AM +0100, James Bottomley wrote:
> This is mainly a respin to add more spacing as Jarkko requested.
> However, I also added the seal/unseal operations to the
> openssl_tpm2_engine (next branch):
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl_tpm2_engine.git/
> 
> With the result that the kernel code completely failed the
> interoperability checks because the ASN.1 format requires the TPM2B
> length prepended to the public and private blobs.  I corrected this in
> patch 4 and now all the interoperability tests are passing.
> 
> General cover letter:
> 
> 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).  It also simplifies the use of policy with keys because
> the ASN.1 format is designed to carry a description of how to
> construct the policy, with the result that simple policies (like
> authorization and PCR locking) can now be constructed and used in the
> kernel, bringing the TPM 2.0 policy use into line with how TPM 1.2
> works.
> 
> James
> 
> ---
> 
> James Bottomley (6):
>   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: add ability to specify arbitrary policy
>   security: keys: trusted: implement counter/timer policy
> 
>  Documentation/security/keys/trusted-encrypted.rst |  64 ++-
>  include/keys/trusted-type.h                       |   7 +-
>  include/linux/asn1_encoder.h                      |  32 ++
>  include/linux/oid_registry.h                      |   5 +
>  include/linux/tpm.h                               |   8 +
>  lib/Makefile                                      |   2 +-
>  lib/asn1_encoder.c                                | 431 ++++++++++++++++++++
>  security/keys/Kconfig                             |   2 +
>  security/keys/trusted-keys/Makefile               |   2 +-
>  security/keys/trusted-keys/tpm2-policy.c          | 463 ++++++++++++++++++++++
>  security/keys/trusted-keys/tpm2-policy.h          |  31 ++
>  security/keys/trusted-keys/tpm2key.asn1           |  23 ++
>  security/keys/trusted-keys/trusted_tpm1.c         |  50 ++-
>  security/keys/trusted-keys/trusted_tpm2.c         | 370 +++++++++++++++--
>  14 files changed, 1454 insertions(+), 36 deletions(-)
>  create mode 100644 include/linux/asn1_encoder.h
>  create mode 100644 lib/asn1_encoder.c
>  create mode 100644 security/keys/trusted-keys/tpm2-policy.c
>  create mode 100644 security/keys/trusted-keys/tpm2-policy.h
>  create mode 100644 security/keys/trusted-keys/tpm2key.asn1
> 
> -- 
> 2.16.4

Somehow managed to drown this to my emails. Looking into next week.

/Jarkko
> 

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

* Re: [PATCH v5 3/6] security: keys: trusted fix tpm2 authorizations
  2020-01-30 10:18 ` [PATCH v5 3/6] security: keys: trusted fix tpm2 authorizations James Bottomley
@ 2020-02-25 16:48   ` Jarkko Sakkinen
  2020-02-26 15:15     ` Jarkko Sakkinen
  2020-02-27  0:58     ` James Bottomley
  0 siblings, 2 replies; 25+ messages in thread
From: Jarkko Sakkinen @ 2020-02-25 16:48 UTC (permalink / raw)
  To: James Bottomley; +Cc: linux-integrity, Mimi Zohar, David Woodhouse, keyrings

On Thu, Jan 30, 2020 at 11:18:09AM +0100, James Bottomley wrote:
> 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=f572d396fae9206628714fb2ce00f72e94f2258f"
> 
> 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"
> 
> 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.
> 
> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>

Should have a fixes tag.

> ---
>  include/keys/trusted-type.h               |  1 +
>  security/keys/trusted-keys/trusted_tpm1.c | 26 +++++++++++++++++++++-----
>  security/keys/trusted-keys/trusted_tpm2.c | 10 ++++++----
>  3 files changed, 28 insertions(+), 9 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 d2c5ec1e040b..3f33d3f74d3c 100644
> --- a/security/keys/trusted-keys/trusted_tpm1.c
> +++ b/security/keys/trusted-keys/trusted_tpm1.c
> @@ -781,12 +781,28 @@ 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)
> +			/*
> +			 * 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)

Justify to the 80 character line length.

> +			 */
> +			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;
> +			} else if (tpm2 &&
> +				   opt->blobauth_len <= sizeof(opt->blobauth)) {
> +				memcpy(opt->blobauth, args[0].from,
> +				       opt->blobauth_len);
> +			} else {
>  				return -EINVAL;
> +			}

This starts to be unnecessarily complicated.

This is what I would suggest:

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;
	return 0;
}

if (tpm2 && opt->blobauth_len <= sizeof(opt->blobauth)) {
	memcpy(opt->blobauth, args[0].from,
	       opt->blobauth_len);
	return 0;
}

return -EINVAL;

Easier to see quickly "when happens what".

/Jarkko

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

* Re: [PATCH v5 3/6] security: keys: trusted fix tpm2 authorizations
  2020-02-25 16:48   ` Jarkko Sakkinen
@ 2020-02-26 15:15     ` Jarkko Sakkinen
  2020-02-27  0:58     ` James Bottomley
  1 sibling, 0 replies; 25+ messages in thread
From: Jarkko Sakkinen @ 2020-02-26 15:15 UTC (permalink / raw)
  To: James Bottomley; +Cc: linux-integrity, Mimi Zohar, David Woodhouse, keyrings

On Tue, Feb 25, 2020 at 06:48:50PM +0200, Jarkko Sakkinen wrote:
> On Thu, Jan 30, 2020 at 11:18:09AM +0100, James Bottomley wrote:
> > 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=f572d396fae9206628714fb2ce00f72e94f2258f"
> > 
> > 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"
> > 
> > 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.
> > 
> > Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
> 
> Should have a fixes tag.
> 
> > ---
> >  include/keys/trusted-type.h               |  1 +
> >  security/keys/trusted-keys/trusted_tpm1.c | 26 +++++++++++++++++++++-----
> >  security/keys/trusted-keys/trusted_tpm2.c | 10 ++++++----
> >  3 files changed, 28 insertions(+), 9 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 d2c5ec1e040b..3f33d3f74d3c 100644
> > --- a/security/keys/trusted-keys/trusted_tpm1.c
> > +++ b/security/keys/trusted-keys/trusted_tpm1.c
> > @@ -781,12 +781,28 @@ 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)
> > +			/*
> > +			 * 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)
> 
> Justify to the 80 character line length.
> 
> > +			 */
> > +			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;
> > +			} else if (tpm2 &&
> > +				   opt->blobauth_len <= sizeof(opt->blobauth)) {
> > +				memcpy(opt->blobauth, args[0].from,
> > +				       opt->blobauth_len);
> > +			} else {
> >  				return -EINVAL;
> > +			}
> 
> This starts to be unnecessarily complicated.
> 
> This is what I would suggest:
> 
> 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;
> 	return 0;
> }
> 
> if (tpm2 && opt->blobauth_len <= sizeof(opt->blobauth)) {
> 	memcpy(opt->blobauth, args[0].from,
> 	       opt->blobauth_len);
> 	return 0;
> }
> 
> return -EINVAL;
> 
> Easier to see quickly "when happens what".
> 
> /Jarkko

And in short summary "TPM2" instead of tpm2.

/Jarkko

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

* Re: [PATCH v5 4/6] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
  2020-02-03 16:54   ` James Prestwood
@ 2020-02-27  0:02     ` James Bottomley
  2020-02-27  0:20       ` James Prestwood
  0 siblings, 1 reply; 25+ messages in thread
From: James Bottomley @ 2020-02-27  0:02 UTC (permalink / raw)
  To: James Prestwood, linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings

On Mon, 2020-02-03 at 08:54 -0800, James Prestwood wrote:
> Hi James,
> 
> <snip>
> 
> > diff --git a/security/keys/trusted-keys/tpm2key.asn1
> > b/security/keys/trusted-keys/tpm2key.asn1
> > new file mode 100644
> > index 000000000000..f930fd812db3
> > --- /dev/null
> > +++ b/security/keys/trusted-keys/tpm2key.asn1
> > @@ -0,0 +1,23 @@
> > +---
> > +--- Note: This isn't quite the definition in the standard
> > +---       However, the Linux asn.1 parser doesn't understand
> > +---       [2] EXPLICIT SEQUENCE OF OPTIONAL
> > +---       So there's an extra intermediate TPMPolicySequence
> > +---       definition to work around this
> > +
> > +TPMKey ::= SEQUENCE {
> > +	type		OBJECT IDENTIFIER ({tpmkey_type}),
> > +	emptyAuth	[0] EXPLICIT BOOLEAN OPTIONAL,
> > +	policy		[1] EXPLICIT TPMPolicySequence
> > OPTIONAL,
> > +	secret		[2] EXPLICIT OCTET STRING OPTIONAL,
> > +	parent		INTEGER ({tpmkey_parent}),
> > +	pubkey		OCTET STRING ({tpmkey_pub}),
> > +	privkey		OCTET STRING ({tpmkey_priv})
> > +	}
> > +
> > +TPMPolicySequence ::= SEQUENCE OF TPMPolicy
> > +
> > +TPMPolicy ::= SEQUENCE {
> > +	commandCode		[0] EXPLICIT INTEGER
> > ({tpmkey_code}),
> > +	commandPolicy		[1] EXPLICIT OCTET STRING
> > ({tpmkey_policy})
> > +	}
> 
> I have been using your set of patches in order to get this ASN.1
> parser/definition. I am implementing an asymmetric key parser/type
> TPM2
> keys for enc/dec/sign/verify using keyctl. Note that this
> implementation goes in crypto/asymmetric_keys/, and your patches sit
> in
> security/keys/trusted-keys/.
> 
> Currently I am just including "../../security/keys/trusted-
> keys/{tpm2key.asn1.h,tpm2-policy.h}" in order to use the ASN.1 parser
> to verify my keys, but this obviously isn't going to fly.
> 
> Do you (or anyone) have any ideas as to how both trusted keys and
> asymmetric keys could share this ASN.1 parser/definition? Some common
> area that both security and crypto could include? Or maybe there is
> some common way the kernel does things like this?

Actually TPM2 asymmetric keys was also on my list.  I was going to use
the existing template and simply move it somewhere everyone could use. 
I also think you need the policy parser pieces because at least one
implementation we'd need to be compatible with supports key policy.

Regards,

James


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

* Re: [PATCH v5 4/6] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
  2020-02-27  0:02     ` James Bottomley
@ 2020-02-27  0:20       ` James Prestwood
  2020-02-27  0:54         ` James Bottomley
  0 siblings, 1 reply; 25+ messages in thread
From: James Prestwood @ 2020-02-27  0:20 UTC (permalink / raw)
  To: James Bottomley, linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings

> > I have been using your set of patches in order to get this ASN.1
> > parser/definition. I am implementing an asymmetric key parser/type
> > TPM2
> > keys for enc/dec/sign/verify using keyctl. Note that this
> > implementation goes in crypto/asymmetric_keys/, and your patches
> > sit
> > in
> > security/keys/trusted-keys/.
> > 
> > Currently I am just including "../../security/keys/trusted-
> > keys/{tpm2key.asn1.h,tpm2-policy.h}" in order to use the ASN.1
> > parser
> > to verify my keys, but this obviously isn't going to fly.
> > 
> > Do you (or anyone) have any ideas as to how both trusted keys and
> > asymmetric keys could share this ASN.1 parser/definition? Some
> > common
> > area that both security and crypto could include? Or maybe there is
> > some common way the kernel does things like this?
> 
> Actually TPM2 asymmetric keys was also on my list.  I was going to
> use
> the existing template and simply move it somewhere everyone could
> use. 
> I also think you need the policy parser pieces because at least one
> implementation we'd need to be compatible with supports key policy.

In terms of policy, I haven't looked into that at all for asymmetric
keys. I do already have enc/dec/sign/verify asymmetric key operations
all working, and used your ASN1 template for parsing (just copied it
into asymmetric_keys for now). Since the asymmetric operations use HMAC
sessions I didn't see much carry over from your patches (but this could
change if policy stuff gets introduced).

This will go in the eventual RFC soon but while I have you here:

I also implemented key wrapping. Exposing this as a keyctl API may take
some rework, hopefully with some help from others in this subsystem. As
it stand now you have to padd a key pair, then do a (new) pkey_wrap
operation on it. This returns a DER with the wrapped TPM2 key. This
required modifying the public_key type, which I really did not like
since it now depends on TPM. Not sure if the route I went is gonna fly
without tweaking, but this is all new to me :) Again, some guidance for
how this should be is needed.

Before I send these patches I need to get some testing done on real
TPM2 hardware. So far its just been emulation. But these patches should
be coming very soon.

Thanks,
James


 

> 
> Regards,
> 
> James
> 


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

* Re: [PATCH v5 4/6] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
  2020-02-27  0:20       ` James Prestwood
@ 2020-02-27  0:54         ` James Bottomley
  2020-02-27 17:19           ` James Prestwood
  0 siblings, 1 reply; 25+ messages in thread
From: James Bottomley @ 2020-02-27  0:54 UTC (permalink / raw)
  To: James Prestwood, linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings

On Wed, 2020-02-26 at 16:20 -0800, James Prestwood wrote:
> > > I have been using your set of patches in order to get this ASN.1
> > > parser/definition. I am implementing an asymmetric key
> > > parser/type TPM2 keys for enc/dec/sign/verify using keyctl. Note
> > > that this implementation goes in crypto/asymmetric_keys/, and
> > > your patches sit in security/keys/trusted-keys/.
> > > 
> > > Currently I am just including "../../security/keys/trusted-
> > > keys/{tpm2key.asn1.h,tpm2-policy.h}" in order to use the ASN.1
> > > parser to verify my keys, but this obviously isn't going to fly.
> > > 
> > > Do you (or anyone) have any ideas as to how both trusted keys and
> > > asymmetric keys could share this ASN.1 parser/definition? Some
> > > common area that both security and crypto could include? Or maybe
> > > there is some common way the kernel does things like this?
> > 
> > Actually TPM2 asymmetric keys was also on my list.  I was going to
> > use the existing template and simply move it somewhere everyone
> > could use.  I also think you need the policy parser pieces because
> > at least one implementation we'd need to be compatible with
> > supports key policy.
> 
> In terms of policy, I haven't looked into that at all for asymmetric
> keys. I do already have enc/dec/sign/verify asymmetric key operations
> all working, and used your ASN1 template for parsing (just copied it
> into asymmetric_keys for now). Since the asymmetric operations use
> HMAC sessions I didn't see much carry over from your patches (but
> this could change if policy stuff gets introduced).

There's a related patch that introduces HMAC and encryption sessions
for pretty much everything in the TPM:

https://lore.kernel.org/r/1568031408.6613.29.camel@HansenPartnership.com

I didn't resend this time around because of patch overload, and anyway,
the last patch needs updating for the current policy c

> This will go in the eventual RFC soon but while I have you here:
> 
> I also implemented key wrapping. Exposing this as a keyctl API may
> take some rework, hopefully with some help from others in this
> subsystem.

Wrapping for what?  The output privkey in the ASN.1 is wrapped by the
TPM using its internal AES key.  The ASN.1 also defines ECDH wrapping,
that's what the secret element of the sequence is for, but you'd only
use that for creating a wrapped key to pass in to the TPM knowing the
parent.  The way current TPM crypto systems use this is they generate
an EC parent from the storage primary seed on the NIST P256 curve.

It's on my todo list to accept bare primary identifiers as parents in
the kernel code and create the EC primary on the fly, but it's not in
this patch set.

There's also another policy problem in that generating an RSA2048 key
can lock the TPM up for ages, so there should likely be some type of
block on someone doing this.  I was thinking that an unprivileged user
should be allowed to create EC keys but not RSA ones.

> As it stand now you have to padd a key pair, then do a (new)
> pkey_wrap operation on it. This returns a DER with the wrapped TPM2
> key. This required modifying the public_key type, which I really did
> not like since it now depends on TPM. Not sure if the route I went is
> gonna fly without tweaking, but this is all new to me :) Again, some
> guidance for how this should be is needed.

The way it's defined to be done using the ASN.1 secret parameter is
simply the way the TPM2 command manual defines duplication with an
outer wrapper.  The TPM2 manual even has a coded example in section 4
and the secret is simply a TPM2B_ENCRYPTED_SECRET.

> Before I send these patches I need to get some testing done on real
> TPM2 hardware. So far its just been emulation. But these patches
> should be coming very soon.

Sure thing, but you may want to look at some of the existing code that
this will need to interoperate with.  The most complete is the openssl
engine, but there's also the intel version of that and openconnect
which all use the same key format:

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

Regards,

James


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

* Re: [PATCH v5 3/6] security: keys: trusted fix tpm2 authorizations
  2020-02-25 16:48   ` Jarkko Sakkinen
  2020-02-26 15:15     ` Jarkko Sakkinen
@ 2020-02-27  0:58     ` James Bottomley
  2020-02-27 16:19       ` Jarkko Sakkinen
  1 sibling, 1 reply; 25+ messages in thread
From: James Bottomley @ 2020-02-27  0:58 UTC (permalink / raw)
  To: Jarkko Sakkinen; +Cc: linux-integrity, Mimi Zohar, David Woodhouse, keyrings

On Tue, 2020-02-25 at 18:48 +0200, Jarkko Sakkinen wrote:
> On Thu, Jan 30, 2020 at 11:18:09AM +0100, James Bottomley wrote:
> > 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=f572d396fae9206628714fb2ce00f72e94f2258f"
> > 
> > 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"
> > 
> > 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.
> > 
> > Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.c
> > om>
> 
> Should have a fixes tag.

I made all the other changes, but I'm not sure what to identify in the
fixes tag.  The problem is the code I updated was simply carried over
unaltered from TPM 1.2

You could certainly argue that commit

commit 0fe5480303a1657b328a0a389f8d99249d9961f5
Author: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Date:   Fri Jun 26 22:28:26 2015 +0300

    keys, trusted: seal/unseal with TPM 2.0 chips

Should have updated the blobauth handling ... is that the one you'd
like fixes: to identify?

James


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

* Re: [PATCH v5 3/6] security: keys: trusted fix tpm2 authorizations
  2020-02-27  0:58     ` James Bottomley
@ 2020-02-27 16:19       ` Jarkko Sakkinen
  2020-02-27 16:21         ` James Bottomley
  0 siblings, 1 reply; 25+ messages in thread
From: Jarkko Sakkinen @ 2020-02-27 16:19 UTC (permalink / raw)
  To: James Bottomley; +Cc: linux-integrity, Mimi Zohar, David Woodhouse, keyrings

On Wed, Feb 26, 2020 at 04:58:11PM -0800, James Bottomley wrote:
> On Tue, 2020-02-25 at 18:48 +0200, Jarkko Sakkinen wrote:
> > On Thu, Jan 30, 2020 at 11:18:09AM +0100, James Bottomley wrote:
> > > 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=f572d396fae9206628714fb2ce00f72e94f2258f"
> > > 
> > > 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"
> > > 
> > > 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.
> > > 
> > > Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.c
> > > om>
> > 
> > Should have a fixes tag.
> 
> I made all the other changes, but I'm not sure what to identify in the
> fixes tag.  The problem is the code I updated was simply carried over
> unaltered from TPM 1.2
> 
> You could certainly argue that commit
> 
> commit 0fe5480303a1657b328a0a389f8d99249d9961f5
> Author: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> Date:   Fri Jun 26 22:28:26 2015 +0300
> 
>     keys, trusted: seal/unseal with TPM 2.0 chips
> 
> Should have updated the blobauth handling ... is that the one you'd
> like fixes: to identify?

What I'm thinking is to have fixes tag w/o cc to stable. I'm not
sure at this point whether we want to backport this but it still
makes sense to tag it.

/Jarkko

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

* Re: [PATCH v5 3/6] security: keys: trusted fix tpm2 authorizations
  2020-02-27 16:19       ` Jarkko Sakkinen
@ 2020-02-27 16:21         ` James Bottomley
  2020-02-27 17:49           ` James Bottomley
  0 siblings, 1 reply; 25+ messages in thread
From: James Bottomley @ 2020-02-27 16:21 UTC (permalink / raw)
  To: Jarkko Sakkinen; +Cc: linux-integrity, Mimi Zohar, David Woodhouse, keyrings

On Thu, 2020-02-27 at 18:19 +0200, Jarkko Sakkinen wrote:
> On Wed, Feb 26, 2020 at 04:58:11PM -0800, James Bottomley wrote:
> > On Tue, 2020-02-25 at 18:48 +0200, Jarkko Sakkinen wrote:
> > > On Thu, Jan 30, 2020 at 11:18:09AM +0100, James Bottomley wrote:
> > > > 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=f572d396fae9206628714fb2ce00f72e94f2258f"
> > > > 
> > > > 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"
> > > > 
> > > > 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.
> > > > 
> > > > Signed-off-by: James Bottomley <James.Bottomley@HansenPartnersh
> > > > ip.c
> > > > om>
> > > 
> > > Should have a fixes tag.
> > 
> > I made all the other changes, but I'm not sure what to identify in
> > the
> > fixes tag.  The problem is the code I updated was simply carried
> > over
> > unaltered from TPM 1.2
> > 
> > You could certainly argue that commit
> > 
> > commit 0fe5480303a1657b328a0a389f8d99249d9961f5
> > Author: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> > Date:   Fri Jun 26 22:28:26 2015 +0300
> > 
> >     keys, trusted: seal/unseal with TPM 2.0 chips
> > 
> > Should have updated the blobauth handling ... is that the one you'd
> > like fixes: to identify?
> 
> What I'm thinking is to have fixes tag w/o cc to stable. I'm not
> sure at this point whether we want to backport this but it still
> makes sense to tag it.

Ok, I'll add that commit as the fixes; it certainly makes no sense to
backport this change before the above commit.

James


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

* Re: [PATCH v5 4/6] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
  2020-02-27  0:54         ` James Bottomley
@ 2020-02-27 17:19           ` James Prestwood
  2020-02-27 20:19             ` James Bottomley
  0 siblings, 1 reply; 25+ messages in thread
From: James Prestwood @ 2020-02-27 17:19 UTC (permalink / raw)
  To: James Bottomley, linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings

On Wed, 2020-02-26 at 16:54 -0800, James Bottomley wrote:
> On Wed, 2020-02-26 at 16:20 -0800, James Prestwood wrote:
> > > > I have been using your set of patches in order to get this
> > > > ASN.1
> > > > parser/definition. I am implementing an asymmetric key
> > > > parser/type TPM2 keys for enc/dec/sign/verify using keyctl.
> > > > Note
> > > > that this implementation goes in crypto/asymmetric_keys/, and
> > > > your patches sit in security/keys/trusted-keys/.
> > > > 
> > > > Currently I am just including "../../security/keys/trusted-
> > > > keys/{tpm2key.asn1.h,tpm2-policy.h}" in order to use the ASN.1
> > > > parser to verify my keys, but this obviously isn't going to
> > > > fly.
> > > > 
> > > > Do you (or anyone) have any ideas as to how both trusted keys
> > > > and
> > > > asymmetric keys could share this ASN.1 parser/definition? Some
> > > > common area that both security and crypto could include? Or
> > > > maybe
> > > > there is some common way the kernel does things like this?
> > > 
> > > Actually TPM2 asymmetric keys was also on my list.  I was going
> > > to
> > > use the existing template and simply move it somewhere everyone
> > > could use.  I also think you need the policy parser pieces
> > > because
> > > at least one implementation we'd need to be compatible with
> > > supports key policy.
> > 
> > In terms of policy, I haven't looked into that at all for
> > asymmetric
> > keys. I do already have enc/dec/sign/verify asymmetric key
> > operations
> > all working, and used your ASN1 template for parsing (just copied
> > it
> > into asymmetric_keys for now). Since the asymmetric operations use
> > HMAC sessions I didn't see much carry over from your patches (but
> > this could change if policy stuff gets introduced).
> 
> There's a related patch that introduces HMAC and encryption sessions
> for pretty much everything in the TPM:
> 
> 
https://lore.kernel.org/r/1568031408.6613.29.camel@HansenPartnership.com
> 
> I didn't resend this time around because of patch overload, and
> anyway,
> the last patch needs updating for the current policy c

Well... I sure duplicated a lot of work. I haven't been on these lists
long enough to see that come through. I am still reading through these
patches, but noticed some differences already with how the session is
started. I use the parent key handle for "salt key handle" rather than
the null key. Also I used RSA/OAEP for encrypting the salt value rather
than ECC. I hadn't read into the null key thing, but I will now. I
would be more than happy to rip out the OAEP code though. I was just
modeling everything how libtpms did it, which used OAEP.

Obviously we don't want a bunch of duplicated code, but I am somewhat
concerned about going right in and using these patches as they have
been sitting around quite a while (plus you said they will need
updating). Seems like the best route is get these merged, then
update/send my patches.

> 
> > This will go in the eventual RFC soon but while I have you here:
> > 
> > I also implemented key wrapping. Exposing this as a keyctl API may
> > take some rework, hopefully with some help from others in this
> > subsystem.
> 
> Wrapping for what?  The output privkey in the ASN.1 is wrapped by the
> TPM using its internal AES key.  The ASN.1 also defines ECDH
> wrapping,
> that's what the secret element of the sequence is for, but you'd only
> use that for creating a wrapped key to pass in to the TPM knowing the
> parent.  The way current TPM crypto systems use this is they generate
> an EC parent from the storage primary seed on the NIST P256 curve.

I implemented CC_Import(). You generate the private key yourself
(openssl or however) and import it into the TPM. Then the result of
that is the TPM wrapped key that can be loaded later on. And yes this
depends on knowing the parent handle.

I basically just implemented:

create_tpm2_key -w privkey.pem -p <handle> privkey.tpm

My reasoning for this was because I had issues with the
openssl_tpm2_engine, and just the whole TPM2 on Linux support as it
stands now. I was able to get everything working on Debian, but then I
went to test on real TPM hardware, which happened to be a Fedora box.
This was a complete disaster; openssl_tpm2_engine did not compile due
to (I think) a library versioning issue and build warnings. I ignored
warnings, and manually built my own version of libtpms but this just
resulted in create_tpm2_key to segfault. At this point I just thought
it would be more worth my time to implement Import() myself.

I think this was all a result of bad packaging on Fedora's part, but
still, the experience didn't sit well with me and I felt it would be
worth while to add support for this in keyctl.

> 
> It's on my todo list to accept bare primary identifiers as parents in
> the kernel code and create the EC primary on the fly, but it's not in
> this patch set.
> 
> There's also another policy problem in that generating an RSA2048 key
> can lock the TPM up for ages, so there should likely be some type of
> block on someone doing this.  I was thinking that an unprivileged
> user
> should be allowed to create EC keys but not RSA ones.

I didn't have any plans for RSA key generation inside the TPM itself,
just wrapping/asym operations.

> 
> > As it stand now you have to padd a key pair, then do a (new)
> > pkey_wrap operation on it. This returns a DER with the wrapped TPM2
> > key. This required modifying the public_key type, which I really
> > did
> > not like since it now depends on TPM. Not sure if the route I went
> > is
> > gonna fly without tweaking, but this is all new to me :) Again,
> > some
> > guidance for how this should be is needed.
> 
> The way it's defined to be done using the ASN.1 secret parameter is
> simply the way the TPM2 command manual defines duplication with an
> outer wrapper.  The TPM2 manual even has a coded example in section 4
> and the secret is simply a TPM2B_ENCRYPTED_SECRET.

I actually didn't do any inner/outer encryption when sending the key to
the TPM (if this isn't what your talking about disregard). I just sent
the private key over plainly. Maybe bus snooping is a concern, but as a
first pass I just punted on this.

> 
> > Before I send these patches I need to get some testing done on real
> > TPM2 hardware. So far its just been emulation. But these patches
> > should be coming very soon.
> 
> Sure thing, but you may want to look at some of the existing code
> that
> this will need to interoperate with.  The most complete is the
> openssl
> engine, but there's also the intel version of that and openconnect
> which all use the same key format:
> 
> 
https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl_tpm2_engine.git/

Yes, as far as wrapping/enc/dec/sign/verify, these all inter-operate
with openssl_tpm2_engine. I have not tried openconnect or the intel
tools but I'll check those out to verify.

Thanks,
James

> 
> Regards,
> 
> James
> 


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

* Re: [PATCH v5 3/6] security: keys: trusted fix tpm2 authorizations
  2020-02-27 16:21         ` James Bottomley
@ 2020-02-27 17:49           ` James Bottomley
  2020-03-02 11:08             ` Jarkko Sakkinen
  0 siblings, 1 reply; 25+ messages in thread
From: James Bottomley @ 2020-02-27 17:49 UTC (permalink / raw)
  To: Jarkko Sakkinen; +Cc: linux-integrity, Mimi Zohar, David Woodhouse, keyrings

On Thu, 2020-02-27 at 08:21 -0800, James Bottomley wrote:
> On Thu, 2020-02-27 at 18:19 +0200, Jarkko Sakkinen wrote:
[...]
> Ok, I'll add that commit as the fixes; it certainly makes no sense to
> backport this change before the above commit.

This is what I currently have.  Do you want me to resend the whole
series?

James

----8>8>8>-----

From: James Bottomley <James.Bottomley@HansenPartnership.com>
Subject: [PATCH v6 3/6] security: keys: trusted: fix TPM2 authorizations

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=f572d396fae9206628714fb2ce00f72e94f2258f"

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"

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.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Fixes: 0fe5480303a1 ("keys, trusted: seal/unseal with TPM 2.0 chips")

---

v6: change comment, eliminate else clauses and add fixes tag

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 d2c5ec1e040b..add9f071d818 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;
+				return 0;
+			}
+
+			if (tpm2 && opt->blobauth_len <= sizeof(opt->blobauth)) {
+				memcpy(opt->blobauth, args[0].from,
+				       opt->blobauth_len);
+				return 0;
+			}
+
+			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 08ec7f48f01d..b4a5058107c2 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)


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

* Re: [PATCH v5 4/6] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
  2020-02-27 17:19           ` James Prestwood
@ 2020-02-27 20:19             ` James Bottomley
  2020-02-27 20:26               ` James Bottomley
                                 ` (2 more replies)
  0 siblings, 3 replies; 25+ messages in thread
From: James Bottomley @ 2020-02-27 20:19 UTC (permalink / raw)
  To: James Prestwood, linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings

On Thu, 2020-02-27 at 09:19 -0800, James Prestwood wrote:
> On Wed, 2020-02-26 at 16:54 -0800, James Bottomley wrote:
> > On Wed, 2020-02-26 at 16:20 -0800, James Prestwood wrote:
> > > > > I have been using your set of patches in order to get this
> > > > > ASN.1 parser/definition. I am implementing an asymmetric key
> > > > > parser/type TPM2 keys for enc/dec/sign/verify using keyctl.
> > > > > Note that this implementation goes in
> > > > > crypto/asymmetric_keys/, and your patches sit in
> > > > > security/keys/trusted-keys/.
> > > > > 
> > > > > Currently I am just including "../../security/keys/trusted-
> > > > > keys/{tpm2key.asn1.h,tpm2-policy.h}" in order to use the
> > > > > ASN.1 parser to verify my keys, but this obviously isn't
> > > > > going to fly.
> > > > > 
> > > > > Do you (or anyone) have any ideas as to how both trusted keys
> > > > > and asymmetric keys could share this ASN.1 parser/definition?
> > > > > Some common area that both security and crypto could include?
> > > > > Or maybe there is some common way the kernel does things like
> > > > > this?
> > > > 
> > > > Actually TPM2 asymmetric keys was also on my list.  I was going
> > > > to use the existing template and simply move it somewhere
> > > > everyone could use.  I also think you need the policy parser
> > > > pieces because at least one implementation we'd need to be
> > > > compatible with supports key policy.
> > > 
> > > In terms of policy, I haven't looked into that at all for
> > > asymmetric keys. I do already have enc/dec/sign/verify asymmetric
> > > key operations all working, and used your ASN1 template for
> > > parsing (just copied it into asymmetric_keys for now). Since the
> > > asymmetric operations use HMAC sessions I didn't see much carry
> > > over from your patches (but this could change if policy stuff
> > > gets introduced).
> > 
> > There's a related patch that introduces HMAC and encryption
> > sessions for pretty much everything in the TPM:
> > 
> > 
> 
> https://lore.kernel.org/r/1568031408.6613.29.camel@HansenPartnership.
> com
> > 
> > I didn't resend this time around because of patch overload, and
> > anyway, the last patch needs updating for the current policy c
> 
> Well... I sure duplicated a lot of work. I haven't been on these
> lists long enough to see that come through. I am still reading
> through these patches, but noticed some differences already with how
> the session is started. I use the parent key handle for "salt key
> handle" ratherthan the null key.

That's a minor detail.  The routines could be updated to use anything
for the parent.  The null seed is just convenient and has nice security
properties.

>  Also I used RSA/OAEP for encrypting the salt value rather than ECC.
> I hadn't read into the null key thing, but I will now. I would be
> more than happy to rip out the OAEP code though. I was just modeling
> everything how libtpms did it, which used OAEP.

There's not much the IBM and Intel TSS teams agree on, but we do agree
that RSA is a bad choice for parents and that EC keys are much better. 
The main reason is that most TPM 2's are much worse at RSA operations
than EC ones ... you're looking at factors of 10x to 100x simply
because of the huge bignum complexity of RSA, which really slows
everything down when you use RSA parents for crypto operations.  Then,
as you found, no-one really does the padding with OAEP either.

> Obviously we don't want a bunch of duplicated code, but I am somewhat
> concerned about going right in and using these patches as they have
> been sitting around quite a while (plus you said they will need
> updating). Seems like the best route is get these merged, then
> update/send my patches.

So we figure out the correct precursor patches and have a couple of
sets ... Jarkko likes stuff done this way anyway.

> > > This will go in the eventual RFC soon but while I have you here:
> > >  I also implemented key wrapping. Exposing this as a keyctl API
> > > may take some rework, hopefully with some help from others in
> > > this subsystem.
> > 
> > Wrapping for what?  The output privkey in the ASN.1 is wrapped by
> > the TPM using its internal AES key.  The ASN.1 also defines ECDH
> > wrapping, that's what the secret element of the sequence is for,
> > but you'd only use that for creating a wrapped key to pass in to
> > the TPM knowing the parent.  The way current TPM crypto systems use
> > this is they generate an EC parent from the storage primary seed on
> > the NIST P256 curve.
> 
> I implemented CC_Import(). You generate the private key yourself
> (openssl or however) and import it into the TPM. Then the result of
> that is the TPM wrapped key that can be loaded later on. And yes this
> depends on knowing the parent handle.

Right, that's what the key wrapping code of the engine does as well,
except that we use EC parents and ECDH wrapping.

> I basically just implemented:
> 
> create_tpm2_key -w privkey.pem -p <handle> privkey.tpm
> 
> My reasoning for this was because I had issues with the
> openssl_tpm2_engine, and just the whole TPM2 on Linux support as it
> stands now. I was able to get everything working on Debian, but then
> I went to test on real TPM hardware, which happened to be a Fedora
> box.  This was a complete disaster; openssl_tpm2_engine did not
> compile due to (I think) a library versioning issue and build
> warnings. I ignored warnings, and manually built my own version
> ofopenssl libtpms but this just resulted in create_tpm2_key to
> segfault. At this point I just thought it would be more worth my time
> to implement Import() myself.
> 
> I think this was all a result of bad packaging on Fedora's part, but
> still, the experience didn't sit well with me and I felt it would be
> worth while to add support for this in keyctl.

Well there's a list you can report problems to and get help:

openssl-tpm2-engine@groups.io

I've got to confess I develop on openSUSE and debian, so Fedora doesn't
get much testing.

> > It's on my todo list to accept bare primary identifiers as parents
> > in the kernel code and create the EC primary on the fly, but it's
> > not in this patch set.
> > 
> > There's also another policy problem in that generating an RSA2048
> > key can lock the TPM up for ages, so there should likely be some
> > type of block on someone doing this.  I was thinking that an
> > unprivileged user should be allowed to create EC keys but not RSA
> > ones.
> 
> I didn't have any plans for RSA key generation inside the TPM itself,
> just wrapping/asym operations.

Well as long as we're interoperable with create_tpm2_key, the consumer
can generate a TPM resident key outside the kernel if they want and
then simply pass it in.

> > > As it stand now you have to padd a key pair, then do a (new)
> > > pkey_wrap operation on it. This returns a DER with the wrapped
> > > TPM2 key. This required modifying the public_key type, which I
> > > really did not like since it now depends on TPM. Not sure if the
> > > route I went is gonna fly without tweaking, but this is all new
> > > to me :) Again, some guidance for how this should be is needed.
> > 
> > The way it's defined to be done using the ASN.1 secret parameter is
> > simply the way the TPM2 command manual defines duplication with an
> > outer wrapper.  The TPM2 manual even has a coded example in section
> > 4 and the secret is simply a TPM2B_ENCRYPTED_SECRET.
> 
> I actually didn't do any inner/outer encryption when sending the key
> to the TPM (if this isn't what your talking about disregard). I just
> sent the private key over plainly. Maybe bus snooping is a concern,
> but as a first pass I just punted on this.

Well to get TPM2_Import to work with an encrypted secret *is* what the
manuals call outer wrapping, you just used an RSA encrypted secret
instead of an ECDH protected one.  It's the same sequence of operations
as for duplication.

Regards,

James

> > > Before I send these patches I need to get some testing done on
> > > real TPM2 hardware. So far its just been emulation. But these
> > > patches should be coming very soon.
> > 
> > Sure thing, but you may want to look at some of the existing code
> > that this will need to interoperate with.  The most complete is the
> > openssl engine, but there's also the intel version of that and
> > openconnect which all use the same key format:
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl_tpm2_eng
> ine.git/
> 
> Yes, as far as wrapping/enc/dec/sign/verify, these all inter-operate
> with openssl_tpm2_engine. I have not tried openconnect or the intel
> tools but I'll check those out to verify.
> 
> Thanks,
> James


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

* Re: [PATCH v5 4/6] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
  2020-02-27 20:19             ` James Bottomley
@ 2020-02-27 20:26               ` James Bottomley
  2020-02-27 20:44                 ` James Prestwood
  2020-02-27 20:57               ` James Prestwood
  2020-03-02 19:00               ` James Prestwood
  2 siblings, 1 reply; 25+ messages in thread
From: James Bottomley @ 2020-02-27 20:26 UTC (permalink / raw)
  To: James Prestwood, linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings

On Thu, 2020-02-27 at 12:19 -0800, James Bottomley wrote:
> On Thu, 2020-02-27 at 09:19 -0800, James Prestwood wrote:
[...]
> > I think this was all a result of bad packaging on Fedora's part,
> > but still, the experience didn't sit well with me and I felt it
> > would be worth while to add support for this in keyctl.
> 
> Well there's a list you can report problems to and get help:
> 
> openssl-tpm2-engine@groups.io
> 
> I've got to confess I develop on openSUSE and debian, so Fedora
> doesn't get much testing.

I should add that even though I don't test on fedora, the opensuse
build service does in my TPM build environment:

https://build.opensuse.org/package/show/home:jejb1:TPM/openssl_tpm2_engine

It says the builds for Fedora 26, 29 and Rawhide all succeeded.  The
build service does both building and testing with the swtpm, so the
engine on fedora gets a pretty extensive workout.

James


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

* Re: [PATCH v5 4/6] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
  2020-02-27 20:26               ` James Bottomley
@ 2020-02-27 20:44                 ` James Prestwood
  0 siblings, 0 replies; 25+ messages in thread
From: James Prestwood @ 2020-02-27 20:44 UTC (permalink / raw)
  To: James Bottomley, linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings

On Thu, 2020-02-27 at 12:26 -0800, James Bottomley wrote:
> On Thu, 2020-02-27 at 12:19 -0800, James Bottomley wrote:
> > On Thu, 2020-02-27 at 09:19 -0800, James Prestwood wrote:
> 
> [...]
> > > I think this was all a result of bad packaging on Fedora's part,
> > > but still, the experience didn't sit well with me and I felt it
> > > would be worth while to add support for this in keyctl.
> > 
> > Well there's a list you can report problems to and get help:
> > 
> > openssl-tpm2-engine@groups.io
> > 
> > I've got to confess I develop on openSUSE and debian, so Fedora
> > doesn't get much testing.
> 
> I should add that even though I don't test on fedora, the opensuse
> build service does in my TPM build environment:
> 
> 
https://build.opensuse.org/package/show/home:jejb1:TPM/openssl_tpm2_engine
> 
> It says the builds for Fedora 26, 29 and Rawhide all succeeded.  The
> build service does both building and testing with the swtpm, so the
> engine on fedora gets a pretty extensive workout.

Hmm, ok I will be trying this again then. Thanks.
> 
> James
> 


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

* Re: [PATCH v5 4/6] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
  2020-02-27 20:19             ` James Bottomley
  2020-02-27 20:26               ` James Bottomley
@ 2020-02-27 20:57               ` James Prestwood
  2020-03-02 19:00               ` James Prestwood
  2 siblings, 0 replies; 25+ messages in thread
From: James Prestwood @ 2020-02-27 20:57 UTC (permalink / raw)
  To: James Bottomley, linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings

On Thu, 2020-02-27 at 12:19 -0800, James Bottomley wrote:
> On Thu, 2020-02-27 at 09:19 -0800, James Prestwood wrote:
> > On Wed, 2020-02-26 at 16:54 -0800, James Bottomley wrote:
> > > On Wed, 2020-02-26 at 16:20 -0800, James Prestwood wrote:
> > > > > > I have been using your set of patches in order to get this
> > > > > > ASN.1 parser/definition. I am implementing an asymmetric
> > > > > > key
> > > > > > parser/type TPM2 keys for enc/dec/sign/verify using keyctl.
> > > > > > Note that this implementation goes in
> > > > > > crypto/asymmetric_keys/, and your patches sit in
> > > > > > security/keys/trusted-keys/.
> > > > > > 
> > > > > > Currently I am just including "../../security/keys/trusted-
> > > > > > keys/{tpm2key.asn1.h,tpm2-policy.h}" in order to use the
> > > > > > ASN.1 parser to verify my keys, but this obviously isn't
> > > > > > going to fly.
> > > > > > 
> > > > > > Do you (or anyone) have any ideas as to how both trusted
> > > > > > keys
> > > > > > and asymmetric keys could share this ASN.1
> > > > > > parser/definition?
> > > > > > Some common area that both security and crypto could
> > > > > > include?
> > > > > > Or maybe there is some common way the kernel does things
> > > > > > like
> > > > > > this?
> > > > > 
> > > > > Actually TPM2 asymmetric keys was also on my list.  I was
> > > > > going
> > > > > to use the existing template and simply move it somewhere
> > > > > everyone could use.  I also think you need the policy parser
> > > > > pieces because at least one implementation we'd need to be
> > > > > compatible with supports key policy.
> > > > 
> > > > In terms of policy, I haven't looked into that at all for
> > > > asymmetric keys. I do already have enc/dec/sign/verify
> > > > asymmetric
> > > > key operations all working, and used your ASN1 template for
> > > > parsing (just copied it into asymmetric_keys for now). Since
> > > > the
> > > > asymmetric operations use HMAC sessions I didn't see much carry
> > > > over from your patches (but this could change if policy stuff
> > > > gets introduced).
> > > 
> > > There's a related patch that introduces HMAC and encryption
> > > sessions for pretty much everything in the TPM:
> > > 
> > > 
> > 
> > 
https://lore.kernel.org/r/1568031408.6613.29.camel@HansenPartnership
> > .
> > com
> > > 
> > > I didn't resend this time around because of patch overload, and
> > > anyway, the last patch needs updating for the current policy c
> > 
> > Well... I sure duplicated a lot of work. I haven't been on these
> > lists long enough to see that come through. I am still reading
> > through these patches, but noticed some differences already with
> > how
> > the session is started. I use the parent key handle for "salt key
> > handle" ratherthan the null key.
> 
> That's a minor detail.  The routines could be updated to use anything
> for the parent.  The null seed is just convenient and has nice
> security
> properties.
> 
> >  Also I used RSA/OAEP for encrypting the salt value rather than
> > ECC.
> > I hadn't read into the null key thing, but I will now. I would be
> > more than happy to rip out the OAEP code though. I was just
> > modeling
> > everything how libtpms did it, which used OAEP.
> 
> There's not much the IBM and Intel TSS teams agree on, but we do
> agree
> that RSA is a bad choice for parents and that EC keys are much
> better. 
> The main reason is that most TPM 2's are much worse at RSA operations
> than EC ones ... you're looking at factors of 10x to 100x simply
> because of the huge bignum complexity of RSA, which really slows
> everything down when you use RSA parents for crypto
> operations.  Then,
> as you found, no-one really does the padding with OAEP either.

I am learning lots from this discussion, so thank you. I had assumed
that the parent key crypto had to match the child key, RSA vs EC, but
sounds like that is not the case. And yes, this sounds like a much
better way to go now that I have a bit more info on it.

> 
> > Obviously we don't want a bunch of duplicated code, but I am
> > somewhat
> > concerned about going right in and using these patches as they have
> > been sitting around quite a while (plus you said they will need
> > updating). Seems like the best route is get these merged, then
> > update/send my patches.
> 
> So we figure out the correct precursor patches and have a couple of
> sets ... Jarkko likes stuff done this way anyway.

Ok, I'll figure out exactly what I need. Its looking like the only
duplication is starting the session and all the HMAC helpers.

> 
> > > > This will go in the eventual RFC soon but while I have you
> > > > here:
> > > >  I also implemented key wrapping. Exposing this as a keyctl API
> > > > may take some rework, hopefully with some help from others in
> > > > this subsystem.
> > > 
> > > Wrapping for what?  The output privkey in the ASN.1 is wrapped by
> > > the TPM using its internal AES key.  The ASN.1 also defines ECDH
> > > wrapping, that's what the secret element of the sequence is for,
> > > but you'd only use that for creating a wrapped key to pass in to
> > > the TPM knowing the parent.  The way current TPM crypto systems
> > > use
> > > this is they generate an EC parent from the storage primary seed
> > > on
> > > the NIST P256 curve.
> > 
> > I implemented CC_Import(). You generate the private key yourself
> > (openssl or however) and import it into the TPM. Then the result of
> > that is the TPM wrapped key that can be loaded later on. And yes
> > this
> > depends on knowing the parent handle.
> 
> Right, that's what the key wrapping code of the engine does as well,
> except that we use EC parents and ECDH wrapping.
> 
> > I basically just implemented:
> > 
> > create_tpm2_key -w privkey.pem -p <handle> privkey.tpm
> > 
> > My reasoning for this was because I had issues with the
> > openssl_tpm2_engine, and just the whole TPM2 on Linux support as it
> > stands now. I was able to get everything working on Debian, but
> > then
> > I went to test on real TPM hardware, which happened to be a Fedora
> > box.  This was a complete disaster; openssl_tpm2_engine did not
> > compile due to (I think) a library versioning issue and build
> > warnings. I ignored warnings, and manually built my own version
> > ofopenssl libtpms but this just resulted in create_tpm2_key to
> > segfault. At this point I just thought it would be more worth my
> > time
> > to implement Import() myself.
> > 
> > I think this was all a result of bad packaging on Fedora's part,
> > but
> > still, the experience didn't sit well with me and I felt it would
> > be
> > worth while to add support for this in keyctl.
> 
> Well there's a list you can report problems to and get help:
> 
> openssl-tpm2-engine@groups.io
> 
> I've got to confess I develop on openSUSE and debian, so Fedora
> doesn't
> get much testing.
> 
> > > It's on my todo list to accept bare primary identifiers as
> > > parents
> > > in the kernel code and create the EC primary on the fly, but it's
> > > not in this patch set.
> > > 
> > > There's also another policy problem in that generating an RSA2048
> > > key can lock the TPM up for ages, so there should likely be some
> > > type of block on someone doing this.  I was thinking that an
> > > unprivileged user should be allowed to create EC keys but not RSA
> > > ones.
> > 
> > I didn't have any plans for RSA key generation inside the TPM
> > itself,
> > just wrapping/asym operations.
> 
> Well as long as we're interoperable with create_tpm2_key, the
> consumer
> can generate a TPM resident key outside the kernel if they want and
> then simply pass it in.
> 
> > > > As it stand now you have to padd a key pair, then do a (new)
> > > > pkey_wrap operation on it. This returns a DER with the wrapped
> > > > TPM2 key. This required modifying the public_key type, which I
> > > > really did not like since it now depends on TPM. Not sure if
> > > > the
> > > > route I went is gonna fly without tweaking, but this is all new
> > > > to me :) Again, some guidance for how this should be is needed.
> > > 
> > > The way it's defined to be done using the ASN.1 secret parameter
> > > is
> > > simply the way the TPM2 command manual defines duplication with
> > > an
> > > outer wrapper.  The TPM2 manual even has a coded example in
> > > section
> > > 4 and the secret is simply a TPM2B_ENCRYPTED_SECRET.
> > 
> > I actually didn't do any inner/outer encryption when sending the
> > key
> > to the TPM (if this isn't what your talking about disregard). I
> > just
> > sent the private key over plainly. Maybe bus snooping is a concern,
> > but as a first pass I just punted on this.
> 
> Well to get TPM2_Import to work with an encrypted secret *is* what
> the
> manuals call outer wrapping, you just used an RSA encrypted secret
> instead of an ECDH protected one.  It's the same sequence of
> operations
> as for duplication.

Ok this makes more sense now.

Thanks,
James


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

* Re: [PATCH v5 3/6] security: keys: trusted fix tpm2 authorizations
  2020-02-27 17:49           ` James Bottomley
@ 2020-03-02 11:08             ` Jarkko Sakkinen
  0 siblings, 0 replies; 25+ messages in thread
From: Jarkko Sakkinen @ 2020-03-02 11:08 UTC (permalink / raw)
  To: James Bottomley; +Cc: linux-integrity, Mimi Zohar, David Woodhouse, keyrings

On Thu, Feb 27, 2020 at 09:49:29AM -0800, James Bottomley wrote:
> On Thu, 2020-02-27 at 08:21 -0800, James Bottomley wrote:
> > On Thu, 2020-02-27 at 18:19 +0200, Jarkko Sakkinen wrote:
> [...]
> > Ok, I'll add that commit as the fixes; it certainly makes no sense to
> > backport this change before the above commit.
> 
> This is what I currently have.  Do you want me to resend the whole
> series?

I prefer to review full snapshots of the series.

/Jarkko

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

* Re: [PATCH v5 4/6] security: keys: trusted: use ASN.1 TPM2 key format for the blobs
  2020-02-27 20:19             ` James Bottomley
  2020-02-27 20:26               ` James Bottomley
  2020-02-27 20:57               ` James Prestwood
@ 2020-03-02 19:00               ` James Prestwood
  2 siblings, 0 replies; 25+ messages in thread
From: James Prestwood @ 2020-03-02 19:00 UTC (permalink / raw)
  To: James Bottomley, linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings

Hi James,

> > > 
> > > There's a related patch that introduces HMAC and encryption
> > > sessions for pretty much everything in the TPM:
> > > 
> > > 
> > 
> > 
https://lore.kernel.org/r/1568031408.6613.29.camel@HansenPartnership
> > .
> > com
> > > 
> > > I didn't resend this time around because of patch overload, and
> > > anyway, the last patch needs updating for the current policy c
> > 

You had mentioned the need for updating, but these (or at least patch
1) failed to apply to v5.5. Looks like some headers had been shifted
around since then. Could you rebase these when you get a chance? That
way I can refactor my patches to use your session stuff.

Its hard to to be completely certain but I think all I need is patch
6/12. If that could be made to be a standalone patch and not depend on
the previous sets that could work too.

Thanks,
James


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

end of thread, back to index

Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-01-30 10:18 [PATCH v5 0/6] TPM 2.0 trusted keys with attached policy James Bottomley
2020-01-30 10:18 ` [PATCH v5 1/6] lib: add ASN.1 encoder James Bottomley
2020-01-30 10:18 ` [PATCH v5 2/6] oid_registry: Add TCG defined OIDS for TPM keys James Bottomley
2020-01-30 10:18 ` [PATCH v5 3/6] security: keys: trusted fix tpm2 authorizations James Bottomley
2020-02-25 16:48   ` Jarkko Sakkinen
2020-02-26 15:15     ` Jarkko Sakkinen
2020-02-27  0:58     ` James Bottomley
2020-02-27 16:19       ` Jarkko Sakkinen
2020-02-27 16:21         ` James Bottomley
2020-02-27 17:49           ` James Bottomley
2020-03-02 11:08             ` Jarkko Sakkinen
2020-01-30 10:18 ` [PATCH v5 4/6] security: keys: trusted: use ASN.1 TPM2 key format for the blobs James Bottomley
2020-02-03 16:54   ` James Prestwood
2020-02-27  0:02     ` James Bottomley
2020-02-27  0:20       ` James Prestwood
2020-02-27  0:54         ` James Bottomley
2020-02-27 17:19           ` James Prestwood
2020-02-27 20:19             ` James Bottomley
2020-02-27 20:26               ` James Bottomley
2020-02-27 20:44                 ` James Prestwood
2020-02-27 20:57               ` James Prestwood
2020-03-02 19:00               ` James Prestwood
2020-01-30 10:18 ` [PATCH v5 5/6] security: keys: trusted: add ability to specify arbitrary policy James Bottomley
2020-01-30 10:18 ` [PATCH v5 6/6] security: keys: trusted: implement counter/timer policy James Bottomley
2020-02-20 20:17 ` [PATCH v5 0/6] TPM 2.0 trusted keys with attached policy Jarkko Sakkinen

Linux-Integrity Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-integrity/0 linux-integrity/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-integrity linux-integrity/ https://lore.kernel.org/linux-integrity \
		linux-integrity@vger.kernel.org
	public-inbox-index linux-integrity

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-integrity


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git