linux-integrity.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 0/5] KEYS: asymmetric: tpm2_key_rsa
@ 2024-05-23 21:25 Jarkko Sakkinen
  2024-05-23 21:25 ` [PATCH v5 1/5] crypto: rsa-pkcs1pad: export rsa1_asn_lookup() Jarkko Sakkinen
                   ` (5 more replies)
  0 siblings, 6 replies; 10+ messages in thread
From: Jarkko Sakkinen @ 2024-05-23 21:25 UTC (permalink / raw)
  To: Herbert Xu
  Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
	David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
	Jarkko Sakkinen, David S. Miller, open list

## Overview

Introduce tpm2_key_rsa module, which implements asymmetric TPM2 RSA key.
The feature can be enabled with the CONFIG_ASYMMETRIC_TPM2_KEY_RSA_SUBTYPE 
kconfig option. This feature allows the private key to be uploaded to
the TPM2 for signing, and software can use the public key to verify
the signatures.

The idea in the design is to over time to have submodule per key type
For instance, tpm2_key_ecdsa could be one potential future addition in
the future. Perhaps, it might sense to consider at that point also a
top-level tpm2_key module. The gist is that the naming convention is
free from potential future bottlencks.

### Testing

tpm2_createprimary --hierarchy o -G rsa2048 -c owner.txt
tpm2_evictcontrol -c owner.txt 0x81000001
tpm2_getcap handles-persistent
openssl genrsa -out private.pem 2048
tpm2_import -C 0x81000001 -G rsa -i private.pem -u key.pub -r key.priv
tpm2_encodeobject -C 0x81000001 -u key.pub -r key.priv -o key.priv.pem
openssl asn1parse -inform pem -in key.priv.pem -noout -out key.priv.der
serial=`cat key.priv.der | keyctl padd asymmetric tpm @u`
echo "abcdefg" > plaintext.txt
keyctl pkey_encrypt $serial 0 plaintext.txt enc=pkcs1 > encrypted.dat
keyctl pkey_decrypt $serial 0 encrypted.dat enc=pkcs1 > decrypted.dat
keyctl pkey_sign $serial 0 plaintext.txt enc=pkcs1 hash=sha256 > signed.dat
keyctl pkey_verify $serial 0 plaintext.txt signed.dat enc=pkcs1 hash=sha256

## References

- v4: https://lore.kernel.org/linux-integrity/20240522005252.17841-1-jarkko@kernel.org/
- v3: https://lore.kernel.org/linux-integrity/20240521152659.26438-1-jarkko@kernel.org/
- v2: https://lore.kernel.org/linux-integrity/336755.1716327854@warthog.procyon.org.uk/
- v1: https://lore.kernel.org/linux-integrity/20240520184727.22038-1-jarkko@kernel.org/
- Derived from https://lore.kernel.org/all/20200518172704.29608-1-prestwoj@gmail.com/

James Prestwood (1):
  keys: asymmetric: ASYMMETRIC_TPM2_KEY_RSA_SUBTYPE

Jarkko Sakkinen (4):
  crypto: rsa-pkcs1pad: export rsa1_asn_lookup()
  KEYS: trusted: Change -EINVAL to -E2BIG
  KEYS: trusted: Move tpm2_key_decode() to the TPM driver
  tpm: tpm2_key: Extend parser to TPM_LoadableKey

 crypto/asymmetric_keys/Kconfig                |  15 +
 crypto/asymmetric_keys/Makefile               |   1 +
 crypto/asymmetric_keys/tpm2_key_rsa.c         | 680 ++++++++++++++++++
 crypto/rsa-pkcs1pad.c                         |  16 +-
 drivers/char/tpm/Kconfig                      |   1 +
 drivers/char/tpm/Makefile                     |   5 +
 drivers/char/tpm/tpm2_key.c                   | 118 +++
 .../char/tpm}/tpm2key.asn1                    |   0
 include/crypto/rsa-pkcs1pad.h                 |  20 +
 include/crypto/tpm2_key.h                     |  35 +
 include/linux/tpm.h                           |   2 +
 security/keys/trusted-keys/Makefile           |   2 -
 security/keys/trusted-keys/trusted_tpm2.c     | 131 +---
 13 files changed, 908 insertions(+), 118 deletions(-)
 create mode 100644 crypto/asymmetric_keys/tpm2_key_rsa.c
 create mode 100644 drivers/char/tpm/tpm2_key.c
 rename {security/keys/trusted-keys => drivers/char/tpm}/tpm2key.asn1 (100%)
 create mode 100644 include/crypto/rsa-pkcs1pad.h
 create mode 100644 include/crypto/tpm2_key.h

-- 
2.45.1


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

* [PATCH v5 1/5] crypto: rsa-pkcs1pad: export rsa1_asn_lookup()
  2024-05-23 21:25 [PATCH v5 0/5] KEYS: asymmetric: tpm2_key_rsa Jarkko Sakkinen
@ 2024-05-23 21:25 ` Jarkko Sakkinen
  2024-05-23 21:25 ` [PATCH v5 2/5] KEYS: trusted: Change -EINVAL to -E2BIG Jarkko Sakkinen
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Jarkko Sakkinen @ 2024-05-23 21:25 UTC (permalink / raw)
  To: Herbert Xu
  Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
	David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
	Jarkko Sakkinen, David S. Miller, open list

ASN.1 template is required for TPM2 asymmetric keys, as it needs to be
piggy-packed with the input data before applying TPM2_RSA_Decrypt. This
patch prepares crypto subsystem for the addition of those keys.

Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
 crypto/rsa-pkcs1pad.c         | 16 ++++++++++------
 include/crypto/rsa-pkcs1pad.h | 20 ++++++++++++++++++++
 2 files changed, 30 insertions(+), 6 deletions(-)
 create mode 100644 include/crypto/rsa-pkcs1pad.h

diff --git a/crypto/rsa-pkcs1pad.c b/crypto/rsa-pkcs1pad.c
index cd501195f34a..00b6c14f861c 100644
--- a/crypto/rsa-pkcs1pad.c
+++ b/crypto/rsa-pkcs1pad.c
@@ -7,6 +7,7 @@
 
 #include <crypto/algapi.h>
 #include <crypto/akcipher.h>
+#include <crypto/rsa-pkcs1pad.h>
 #include <crypto/internal/akcipher.h>
 #include <crypto/internal/rsa.h>
 #include <linux/err.h>
@@ -79,11 +80,7 @@ static const u8 rsa_digest_info_sha3_512[] = {
 	0x05, 0x00, 0x04, 0x40
 };
 
-static const struct rsa_asn1_template {
-	const char	*name;
-	const u8	*data;
-	size_t		size;
-} rsa_asn1_templates[] = {
+static const struct rsa_asn1_template rsa_asn1_templates[] = {
 #define _(X) { #X, rsa_digest_info_##X, sizeof(rsa_digest_info_##X) }
 	_(md5),
 	_(sha1),
@@ -101,7 +98,13 @@ static const struct rsa_asn1_template {
 	{ NULL }
 };
 
-static const struct rsa_asn1_template *rsa_lookup_asn1(const char *name)
+/**
+ * rsa_lookup_asn1() - Lookup the ASN.1 digest info given the hash
+ * name:	hash algorithm name
+ *
+ * Returns the ASN.1 digest info on success, and NULL on failure.
+ */
+const struct rsa_asn1_template *rsa_lookup_asn1(const char *name)
 {
 	const struct rsa_asn1_template *p;
 
@@ -110,6 +113,7 @@ static const struct rsa_asn1_template *rsa_lookup_asn1(const char *name)
 			return p;
 	return NULL;
 }
+EXPORT_SYMBOL_GPL(rsa_lookup_asn1);
 
 struct pkcs1pad_ctx {
 	struct crypto_akcipher *child;
diff --git a/include/crypto/rsa-pkcs1pad.h b/include/crypto/rsa-pkcs1pad.h
new file mode 100644
index 000000000000..32c7453ff644
--- /dev/null
+++ b/include/crypto/rsa-pkcs1pad.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * RSA padding templates.
+ */
+
+#ifndef _CRYPTO_RSA_PKCS1PAD_H
+#define _CRYPTO_RSA_PKCS1PAD_H
+
+/*
+ * Hash algorithm name to ASN.1 template mapping.
+ */
+struct rsa_asn1_template {
+	const char *name;
+	const u8 *data;
+	size_t size;
+};
+
+const struct rsa_asn1_template *rsa_lookup_asn1(const char *name);
+
+#endif /* _CRYPTO_RSA_PKCS1PAD_H */
-- 
2.45.1


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

* [PATCH v5 2/5] KEYS: trusted: Change -EINVAL to -E2BIG
  2024-05-23 21:25 [PATCH v5 0/5] KEYS: asymmetric: tpm2_key_rsa Jarkko Sakkinen
  2024-05-23 21:25 ` [PATCH v5 1/5] crypto: rsa-pkcs1pad: export rsa1_asn_lookup() Jarkko Sakkinen
@ 2024-05-23 21:25 ` Jarkko Sakkinen
  2024-05-23 21:25 ` [PATCH v5 3/5] KEYS: trusted: Move tpm2_key_decode() to the TPM driver Jarkko Sakkinen
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Jarkko Sakkinen @ 2024-05-23 21:25 UTC (permalink / raw)
  To: Herbert Xu
  Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
	David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
	Jarkko Sakkinen, David S. Miller, open list, James Bottomley,
	Mimi Zohar, David Howells, Paul Moore, James Morris,
	Serge E. Hallyn, open list:SECURITY SUBSYSTEM

Report -E2BIG instead of -EINVAL when too large size for the key blob is
requested.

Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
 security/keys/trusted-keys/trusted_tpm2.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index d478096cb5c1..7317e07440c3 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -122,7 +122,7 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
 		return ret;
 
 	if (ctx.priv_len + ctx.pub_len > MAX_BLOB_SIZE)
-		return -EINVAL;
+		return -E2BIG;
 
 	blob = kmalloc(ctx.priv_len + ctx.pub_len + 4, GFP_KERNEL);
 	if (!blob)
-- 
2.45.1


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

* [PATCH v5 3/5] KEYS: trusted: Move tpm2_key_decode() to the TPM driver
  2024-05-23 21:25 [PATCH v5 0/5] KEYS: asymmetric: tpm2_key_rsa Jarkko Sakkinen
  2024-05-23 21:25 ` [PATCH v5 1/5] crypto: rsa-pkcs1pad: export rsa1_asn_lookup() Jarkko Sakkinen
  2024-05-23 21:25 ` [PATCH v5 2/5] KEYS: trusted: Change -EINVAL to -E2BIG Jarkko Sakkinen
@ 2024-05-23 21:25 ` Jarkko Sakkinen
  2024-05-23 21:25 ` [PATCH v5 4/5] tpm: tpm2_key: Extend parser to TPM_LoadableKey Jarkko Sakkinen
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Jarkko Sakkinen @ 2024-05-23 21:25 UTC (permalink / raw)
  To: Herbert Xu
  Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
	David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
	Jarkko Sakkinen, David S. Miller, open list, Peter Huewe,
	Jason Gunthorpe, James Bottomley, Mimi Zohar, David Howells,
	Paul Moore, James Morris, Serge E. Hallyn,
	open list:SECURITY SUBSYSTEM

Move tpm2_key_decode() to the TPM driver and export the symbols to make
them callable from trusted keys. It can re-used for asymmetric keys.

Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v2:
Do not allocate blob twice. Use the one inside struct tpm2_key.
---
 drivers/char/tpm/Kconfig                      |   1 +
 drivers/char/tpm/Makefile                     |   5 +
 drivers/char/tpm/tpm2_key.c                   | 111 +++++++++++++++
 .../char/tpm}/tpm2key.asn1                    |   0
 include/crypto/tpm2_key.h                     |  33 +++++
 security/keys/trusted-keys/Makefile           |   2 -
 security/keys/trusted-keys/trusted_tpm2.c     | 127 +++---------------
 7 files changed, 167 insertions(+), 112 deletions(-)
 create mode 100644 drivers/char/tpm/tpm2_key.c
 rename {security/keys/trusted-keys => drivers/char/tpm}/tpm2key.asn1 (100%)
 create mode 100644 include/crypto/tpm2_key.h

diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index e63a6a17793c..de2f4093c939 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -7,6 +7,7 @@ menuconfig TCG_TPM
 	tristate "TPM Hardware Support"
 	depends on HAS_IOMEM
 	imply SECURITYFS
+	select ASN1
 	select CRYPTO
 	select CRYPTO_HASH_INFO
 	help
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index 4c695b0388f3..071437058ef6 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -17,6 +17,11 @@ tpm-y += eventlog/tpm1.o
 tpm-y += eventlog/tpm2.o
 tpm-y += tpm-buf.o
 
+# TPM2 Asymmetric Key
+$(obj)/trusted_tpm2.o: $(obj)/tpm2key.asn1.h
+tpm-y += tpm2key.asn1.o
+tpm-y += tpm2_key.o
+
 tpm-$(CONFIG_TCG_TPM2_HMAC) += tpm2-sessions.o
 tpm-$(CONFIG_ACPI) += tpm_ppi.o eventlog/acpi.o
 tpm-$(CONFIG_EFI) += eventlog/efi.o
diff --git a/drivers/char/tpm/tpm2_key.c b/drivers/char/tpm/tpm2_key.c
new file mode 100644
index 000000000000..7662b2cb85bf
--- /dev/null
+++ b/drivers/char/tpm/tpm2_key.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/oid_registry.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <crypto/tpm2_key.h>
+#include <asm/unaligned.h>
+#include "tpm2key.asn1.h"
+
+#undef pr_fmt
+#define pr_fmt(fmt) "tpm2_key: "fmt
+
+int tpm2_key_parent(void *context, size_t hdrlen,
+		    unsigned char tag,
+		    const void *value, size_t vlen)
+{
+	struct tpm2_key *ctx = context;
+	const u8 *v = value;
+	int i;
+
+	ctx->parent = 0;
+	for (i = 0; i < vlen; i++) {
+		ctx->parent <<= 8;
+		ctx->parent |= v[i];
+	}
+
+	return 0;
+}
+
+int tpm2_key_type(void *context, size_t hdrlen,
+		  unsigned char tag,
+		  const void *value, size_t vlen)
+{
+	enum OID oid = look_up_OID(value, vlen);
+
+	if (oid != OID_TPMSealedData) {
+		char buffer[50];
+
+		sprint_oid(value, vlen, buffer, sizeof(buffer));
+		pr_debug("OID is \"%s\" which is not TPMSealedData\n",
+			 buffer);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int tpm2_key_pub(void *context, size_t hdrlen,
+		 unsigned char tag,
+		 const void *value, size_t vlen)
+{
+	struct tpm2_key *ctx = context;
+
+	ctx->pub = value;
+	ctx->pub_len = vlen;
+
+	return 0;
+}
+
+int tpm2_key_priv(void *context, size_t hdrlen,
+		  unsigned char tag,
+		  const void *value, size_t vlen)
+{
+	struct tpm2_key *ctx = context;
+
+	ctx->priv = value;
+	ctx->priv_len = vlen;
+
+	return 0;
+}
+
+/**
+ * tpm_key_decode() - Decode TPM2 ASN.1 key.
+ * @src:		ASN.1 source.
+ * @src_len:		ASN.1 source length.
+ * @key:		TPM2 asymmetric key.
+ * @max_key_len:	Cap the maximum length for the blob allocation.
+ *
+ * Decodes TPM2 ASN.1 key on success. Returns POSIX error code on failure.
+ */
+int tpm2_key_decode(const u8 *src, u32 src_len, struct tpm2_key *key,
+		    u32 max_key_len)
+{
+	struct tpm2_key ctx;
+	u32 blob_len;
+	int ret;
+
+	memset(&ctx, 0, sizeof(ctx));
+
+	ret = asn1_ber_decoder(&tpm2key_decoder, &ctx, src, src_len);
+	if (ret < 0)
+		return ret;
+
+	blob_len = ctx.priv_len + ctx.pub_len;
+	if (blob_len > max_key_len)
+		return -E2BIG;
+
+	ctx.blob_len = blob_len;
+	ctx.blob = kmalloc(blob_len, GFP_KERNEL);
+	if (!ctx.blob)
+		return -ENOMEM;
+
+	memcpy((void *)ctx.blob, ctx.priv, ctx.priv_len);
+	memcpy((void *)ctx.blob + ctx.priv_len, ctx.pub, ctx.pub_len);
+	ctx.priv = ctx.blob;
+	ctx.pub = ctx.blob + ctx.priv_len;
+
+	memcpy(key, &ctx, sizeof(ctx));
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tpm2_key_decode);
diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/drivers/char/tpm/tpm2key.asn1
similarity index 100%
rename from security/keys/trusted-keys/tpm2key.asn1
rename to drivers/char/tpm/tpm2key.asn1
diff --git a/include/crypto/tpm2_key.h b/include/crypto/tpm2_key.h
new file mode 100644
index 000000000000..acf41b2e0c92
--- /dev/null
+++ b/include/crypto/tpm2_key.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __LINUX_TPM2_KEY_H__
+#define __LINUX_TPM2_KEY_H__
+
+#include <linux/slab.h>
+
+/*
+ * TPM2 ASN.1 key
+ */
+struct tpm2_key {
+	u32 parent;
+	const u8 *blob;
+	u32 blob_len;
+	const u8 *pub;
+	u32 pub_len;
+	const u8 *priv;
+	u32 priv_len;
+};
+
+int tpm2_key_decode(const u8 *src, u32 src_len, struct tpm2_key *key,
+		    u32 max_key_len);
+
+/**
+ * tpm2_key_free() - Release TPM2 asymmetric key resources and reset values
+ * @key:	TPM2 asymmetric key.
+ */
+static inline void tpm2_key_destroy(struct tpm2_key *key)
+{
+	kfree(key->blob);
+	memset(key, 0, sizeof(*key));
+}
+
+#endif /* __LINUX_TPM2_KEY_H__ */
diff --git a/security/keys/trusted-keys/Makefile b/security/keys/trusted-keys/Makefile
index f0f3b27f688b..2674d5c10fc9 100644
--- a/security/keys/trusted-keys/Makefile
+++ b/security/keys/trusted-keys/Makefile
@@ -7,9 +7,7 @@ obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
 trusted-y += trusted_core.o
 trusted-$(CONFIG_TRUSTED_KEYS_TPM) += trusted_tpm1.o
 
-$(obj)/trusted_tpm2.o: $(obj)/tpm2key.asn1.h
 trusted-$(CONFIG_TRUSTED_KEYS_TPM) += trusted_tpm2.o
-trusted-$(CONFIG_TRUSTED_KEYS_TPM) += tpm2key.asn1.o
 
 trusted-$(CONFIG_TRUSTED_KEYS_TEE) += trusted_tee.o
 
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 7317e07440c3..e0bd1a2fc2ca 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -13,11 +13,10 @@
 
 #include <keys/trusted-type.h>
 #include <keys/trusted_tpm.h>
+#include <crypto/tpm2_key.h>
 
 #include <asm/unaligned.h>
 
-#include "tpm2key.asn1.h"
-
 static struct tpm2_hash tpm2_hash_map[] = {
 	{HASH_ALGO_SHA1, TPM_ALG_SHA1},
 	{HASH_ALGO_SHA256, TPM_ALG_SHA256},
@@ -28,9 +27,9 @@ static struct tpm2_hash tpm2_hash_map[] = {
 
 static const u8 OID_TPMSealedData_ASN1[] = {0x06, 0x06, 0x67, 0x81, 0x05, 0x0a, 0x01, 0x05};
 
-static int tpm2_key_encode(struct trusted_key_payload *payload,
-			   struct trusted_key_options *options,
-			   u8 *src, u32 len)
+static int tpm2_trusted_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);
@@ -98,106 +97,6 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
 	return ret;
 }
 
-struct tpm2_key_context {
-	u32 parent;
-	const u8 *pub;
-	u32 pub_len;
-	const u8 *priv;
-	u32 priv_len;
-};
-
-static int tpm2_key_decode(struct trusted_key_payload *payload,
-			   struct trusted_key_options *options,
-			   u8 **buf)
-{
-	int ret;
-	struct tpm2_key_context ctx;
-	u8 *blob;
-
-	memset(&ctx, 0, sizeof(ctx));
-
-	ret = asn1_ber_decoder(&tpm2key_decoder, &ctx, payload->blob,
-			       payload->blob_len);
-	if (ret < 0)
-		return ret;
-
-	if (ctx.priv_len + ctx.pub_len > MAX_BLOB_SIZE)
-		return -E2BIG;
-
-	blob = kmalloc(ctx.priv_len + ctx.pub_len + 4, GFP_KERNEL);
-	if (!blob)
-		return -ENOMEM;
-
-	*buf = blob;
-	options->keyhandle = ctx.parent;
-
-	memcpy(blob, ctx.priv, ctx.priv_len);
-	blob += ctx.priv_len;
-
-	memcpy(blob, ctx.pub, ctx.pub_len);
-
-	return 0;
-}
-
-int tpm2_key_parent(void *context, size_t hdrlen,
-		  unsigned char tag,
-		  const void *value, size_t vlen)
-{
-	struct tpm2_key_context *ctx = context;
-	const u8 *v = value;
-	int i;
-
-	ctx->parent = 0;
-	for (i = 0; i < vlen; i++) {
-		ctx->parent <<= 8;
-		ctx->parent |= v[i];
-	}
-
-	return 0;
-}
-
-int tpm2_key_type(void *context, size_t hdrlen,
-		unsigned char tag,
-		const void *value, size_t vlen)
-{
-	enum OID oid = look_up_OID(value, vlen);
-
-	if (oid != OID_TPMSealedData) {
-		char buffer[50];
-
-		sprint_oid(value, vlen, buffer, sizeof(buffer));
-		pr_debug("OID is \"%s\" which is not TPMSealedData\n",
-			 buffer);
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-int tpm2_key_pub(void *context, size_t hdrlen,
-	       unsigned char tag,
-	       const void *value, size_t vlen)
-{
-	struct tpm2_key_context *ctx = context;
-
-	ctx->pub = value;
-	ctx->pub_len = vlen;
-
-	return 0;
-}
-
-int tpm2_key_priv(void *context, size_t hdrlen,
-		unsigned char tag,
-		const void *value, size_t vlen)
-{
-	struct tpm2_key_context *ctx = context;
-
-	ctx->priv = value;
-	ctx->priv_len = vlen;
-
-	return 0;
-}
-
 /**
  * tpm2_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
  *
@@ -347,7 +246,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		goto out;
 	}
 
-	blob_len = tpm2_key_encode(payload, options, &buf.data[offset], blob_len);
+	blob_len = tpm2_trusted_key_encode(payload, options, &buf.data[offset],
+					   blob_len);
 
 out:
 	tpm_buf_destroy(&sized);
@@ -387,21 +287,27 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 			 struct trusted_key_options *options,
 			 u32 *blob_handle)
 {
-	struct tpm_buf buf;
 	unsigned int private_len;
 	unsigned int public_len;
 	unsigned int blob_len;
-	u8 *blob, *pub;
+	struct tpm2_key key;
+	struct tpm_buf buf;
+	const u8 *blob, *pub;
 	int rc;
 	u32 attrs;
 
-	rc = tpm2_key_decode(payload, options, &blob);
+	rc = tpm2_key_decode(payload->blob, payload->blob_len, &key, PAGE_SIZE);
 	if (rc) {
 		/* old form */
 		blob = payload->blob;
 		payload->old_format = 1;
+	} else {
+		blob = key.blob;
 	}
 
+	if (!blob)
+		return -ENOMEM;
+
 	/* new format carries keyhandle but old format doesn't */
 	if (!options->keyhandle)
 		return -EINVAL;
@@ -465,7 +371,8 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 
 out:
 	if (blob != payload->blob)
-		kfree(blob);
+		tpm2_key_destroy(&key);
+
 	tpm_buf_destroy(&buf);
 
 	if (rc > 0)
-- 
2.45.1


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

* [PATCH v5 4/5] tpm: tpm2_key: Extend parser to TPM_LoadableKey
  2024-05-23 21:25 [PATCH v5 0/5] KEYS: asymmetric: tpm2_key_rsa Jarkko Sakkinen
                   ` (2 preceding siblings ...)
  2024-05-23 21:25 ` [PATCH v5 3/5] KEYS: trusted: Move tpm2_key_decode() to the TPM driver Jarkko Sakkinen
@ 2024-05-23 21:25 ` Jarkko Sakkinen
  2024-05-23 21:25 ` [PATCH v5 5/5] keys: asymmetric: ASYMMETRIC_TPM2_KEY_RSA_SUBTYPE Jarkko Sakkinen
  2024-05-24  9:18 ` [PATCH v5 0/5] KEYS: asymmetric: tpm2_key_rsa Jarkko Sakkinen
  5 siblings, 0 replies; 10+ messages in thread
From: Jarkko Sakkinen @ 2024-05-23 21:25 UTC (permalink / raw)
  To: Herbert Xu
  Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
	David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
	Jarkko Sakkinen, David S. Miller, open list, Peter Huewe,
	Jason Gunthorpe, James Bottomley, Mimi Zohar, David Howells,
	Paul Moore, James Morris, Serge E. Hallyn,
	open list:SECURITY SUBSYSTEM

Extend parser to TPM_LoadableKey. Add field for oid to struct tpm2_key
so that callers can differentiate different key types.

Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v3:
* Fixup klog messages:
  https://lore.kernel.org/linux-integrity/SN7PR18MB53140F4341BC441C1C11586EE3EA2@SN7PR18MB5314.namprd18.prod.outlook.com/
---
 drivers/char/tpm/tpm2_key.c               | 17 ++++++++++++-----
 include/crypto/tpm2_key.h                 |  2 ++
 security/keys/trusted-keys/trusted_tpm2.c |  4 ++++
 3 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/drivers/char/tpm/tpm2_key.c b/drivers/char/tpm/tpm2_key.c
index 7662b2cb85bf..b5c07288eff5 100644
--- a/drivers/char/tpm/tpm2_key.c
+++ b/drivers/char/tpm/tpm2_key.c
@@ -32,16 +32,23 @@ int tpm2_key_type(void *context, size_t hdrlen,
 		  const void *value, size_t vlen)
 {
 	enum OID oid = look_up_OID(value, vlen);
-
-	if (oid != OID_TPMSealedData) {
+	struct tpm2_key *key = context;
+
+	switch (oid) {
+	case OID_TPMSealedData:
+		pr_debug("TPMSealedData\n");
+		break;
+	case OID_TPMLoadableKey:
+		pr_debug("TPMLodableKey\n");
+		break;
+	default:
 		char buffer[50];
-
 		sprint_oid(value, vlen, buffer, sizeof(buffer));
-		pr_debug("OID is \"%s\" which is not TPMSealedData\n",
-			 buffer);
+		pr_debug("Unknown OID \"%s\"\n", buffer);
 		return -EINVAL;
 	}
 
+	key->oid = oid;
 	return 0;
 }
 
diff --git a/include/crypto/tpm2_key.h b/include/crypto/tpm2_key.h
index acf41b2e0c92..2d2434233000 100644
--- a/include/crypto/tpm2_key.h
+++ b/include/crypto/tpm2_key.h
@@ -2,12 +2,14 @@
 #ifndef __LINUX_TPM2_KEY_H__
 #define __LINUX_TPM2_KEY_H__
 
+#include <linux/oid_registry.h>
 #include <linux/slab.h>
 
 /*
  * TPM2 ASN.1 key
  */
 struct tpm2_key {
+	enum OID oid;
 	u32 parent;
 	const u8 *blob;
 	u32 blob_len;
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index e0bd1a2fc2ca..5b4555dd13e5 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -303,6 +303,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 		payload->old_format = 1;
 	} else {
 		blob = key.blob;
+		if (key.oid != OID_TPMSealedData) {
+			tpm2_key_destroy(&key);
+			return -EINVAL;
+		}
 	}
 
 	if (!blob)
-- 
2.45.1


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

* [PATCH v5 5/5] keys: asymmetric: ASYMMETRIC_TPM2_KEY_RSA_SUBTYPE
  2024-05-23 21:25 [PATCH v5 0/5] KEYS: asymmetric: tpm2_key_rsa Jarkko Sakkinen
                   ` (3 preceding siblings ...)
  2024-05-23 21:25 ` [PATCH v5 4/5] tpm: tpm2_key: Extend parser to TPM_LoadableKey Jarkko Sakkinen
@ 2024-05-23 21:25 ` Jarkko Sakkinen
  2024-05-23 21:39   ` Jarkko Sakkinen
  2024-05-24  9:18 ` [PATCH v5 0/5] KEYS: asymmetric: tpm2_key_rsa Jarkko Sakkinen
  5 siblings, 1 reply; 10+ messages in thread
From: Jarkko Sakkinen @ 2024-05-23 21:25 UTC (permalink / raw)
  To: Herbert Xu
  Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
	David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
	Jarkko Sakkinen, David S. Miller, open list, David Howells,
	James Bottomley, Stefan Berger, Ard Biesheuvel, Mario Limonciello

From: James Prestwood <prestwoj@gmail.com>

Based on earlier work by James Prestwood.

Add ASN.1 compatible asymmetric TPM2 RSA key subtype:

1. Signing and decryption (with the private key) is handled by
   TPM2_RSA_Decrypt.
2. Encryption (with the public key) is handled by the kernel RSA
   implementation.

Signed-off-by: James Prestwood <prestwoj@gmail.com>
Co-developed-by: Jarkko Sakkinen <jarkko@kernel.org>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v5:
* akcipher has two *undocumented* parameters. Document this clearly.
* Remove unused variable.
v4:
* Just put the values to the buffer instead of encoding them.
* Adjust buffer sizes.
* Make tpm2_rsa_key_encode() not to allocate from heap and simplify
  the serialization.
v3:
* Drop the special case for null handle i.e. do not define policy.
* Remove extra empty line.
v2:
* Remove two spurios pr_info() messsages that I forgot to remove.
* Clean up padding functions and add additional checks for length
  also in tpm2_unpad_pcks1().
* Add the missing success check kzalloc() in tpm2_key_rsa_decrypt().
* Check that params->out_len for capacity before copying the result.
---
 crypto/asymmetric_keys/Kconfig        |  15 +
 crypto/asymmetric_keys/Makefile       |   1 +
 crypto/asymmetric_keys/tpm2_key_rsa.c | 680 ++++++++++++++++++++++++++
 include/linux/tpm.h                   |   2 +
 4 files changed, 698 insertions(+)
 create mode 100644 crypto/asymmetric_keys/tpm2_key_rsa.c

diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index e1345b8f39f1..71448c2f0a8f 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -15,6 +15,7 @@ config ASYMMETRIC_PUBLIC_KEY_SUBTYPE
 	select MPILIB
 	select CRYPTO_HASH_INFO
 	select CRYPTO_AKCIPHER
+	select CRYPTO_RSA
 	select CRYPTO_SIG
 	select CRYPTO_HASH
 	help
@@ -23,6 +24,20 @@ config ASYMMETRIC_PUBLIC_KEY_SUBTYPE
 	  appropriate hash algorithms (such as SHA-1) must be available.
 	  ENOPKG will be reported if the requisite algorithm is unavailable.
 
+config ASYMMETRIC_TPM2_KEY_RSA_SUBTYPE
+	tristate "Asymmetric TPM2 RSA crypto algorithm subtype"
+	depends on TCG_TPM
+	select TCG_TPM2_HMAC
+	select CRYPTO_RSA
+	select CRYPTO_SHA256
+	select CRYPTO_HASH_INFO
+	select ASN1
+	help
+	  This option provides support for asymmetric TPM2 key type handling.
+	  If signature generation and/or verification are to be used,
+	  appropriate hash algorithms (such as SHA-256) must be available.
+	  ENOPKG will be reported if the requisite algorithm is unavailable.
+
 config X509_CERTIFICATE_PARSER
 	tristate "X.509 certificate parser"
 	depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index bc65d3b98dcb..c6da84607824 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -11,6 +11,7 @@ asymmetric_keys-y := \
 	signature.o
 
 obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
+obj-$(CONFIG_ASYMMETRIC_TPM2_KEY_RSA_SUBTYPE) += tpm2_key_rsa.o
 
 #
 # X.509 Certificate handling
diff --git a/crypto/asymmetric_keys/tpm2_key_rsa.c b/crypto/asymmetric_keys/tpm2_key_rsa.c
new file mode 100644
index 000000000000..22e7cc0217d3
--- /dev/null
+++ b/crypto/asymmetric_keys/tpm2_key_rsa.c
@@ -0,0 +1,680 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* TPM2 asymmetric public-key crypto subtype
+ *
+ * See Documentation/crypto/asymmetric-keys.rst
+ *
+ * Copyright (c) 2020 Intel Corporation
+ */
+
+#include <asm/unaligned.h>
+#include <crypto/akcipher.h>
+#include <crypto/public_key.h>
+#include <crypto/rsa-pkcs1pad.h>
+#include <crypto/tpm2_key.h>
+#include <keys/asymmetric-parser.h>
+#include <keys/asymmetric-subtype.h>
+#include <keys/trusted-type.h>
+#include <linux/asn1_encoder.h>
+#include <linux/keyctl.h>
+#include <linux/module.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/tpm.h>
+
+#undef pr_fmt
+#define pr_fmt(fmt) "tpm2_key_rsa: "fmt
+
+/* 4096 bits */
+#define TPM2_KEY_RSA_PUB_SIZE		512
+#define TPM2_KEY_RSA_PUB_ENC_SIZE	(TPM2_KEY_RSA_PUB_SIZE + 16)
+/* 4x 4096bits */
+#define TPM2_KEY_RSA_BUFFER_SIZE	(TPM2_KEY_RSA_PUB_SIZE * 4)
+
+#define PKCS1_PAD_MIN_SIZE  11
+
+struct tpm2_key_rsa {
+	struct tpm2_key key;
+	const u8 *pub;
+	int pub_len;
+};
+
+/*
+ * Fill the data with PKCS#1 v1.5 padding.
+ */
+static int tpm2_pad_pkcs1(const u8 *in, int in_len, u8 *out, int out_len)
+{
+	unsigned int prefix_len = out_len - in_len - 3;
+
+	if (in_len > out_len - PKCS1_PAD_MIN_SIZE)
+		return -EBADMSG;
+
+	/* prefix */
+	out[0] = 0;
+	out[1] = 1;
+	memset(&out[2], 0xff, prefix_len);
+	out[2 + prefix_len] = 0;
+	/* payload */
+	memcpy(&out[2 + prefix_len + 1], in, in_len);
+
+	return 0;
+}
+
+/*
+ * RFC 3447 - Section 7.2.2
+ * Size of the input data should be checked against public key size by
+ * the caller.
+ */
+static const u8 *tpm2_unpad_pkcs1(const u8 *in, int in_len, int *out_len)
+{
+	int i;
+
+	if (in[0] != 0 || in[1] != 2)
+		return NULL;
+
+	i = 2;
+	while (in[i] != 0 && i < in_len)
+		i++;
+
+	if (i == in_len || i < (PKCS1_PAD_MIN_SIZE - 1))
+		return NULL;
+
+	*out_len = in_len - i - 1;
+	return in + i + 1;
+}
+
+/*
+ * Outputs the cipher algorithm name on success, and retuns -ENOPKG
+ * on failure.
+ */
+static int tpm2_key_get_akcipher(const char *encoding, const char *hash_algo,
+				 char *cipher)
+{
+	ssize_t ret;
+
+	if (strcmp(encoding, "pkcs1") == 0) {
+		if (!hash_algo) {
+			strcpy(cipher, "pkcs1pad(rsa)");
+			return 0;
+		}
+
+		ret = snprintf(cipher, CRYPTO_MAX_ALG_NAME,
+			       "pkcs1pad(rsa,%s)",
+			       hash_algo);
+		if (ret >= CRYPTO_MAX_ALG_NAME)
+			return -ENOPKG;
+
+		return 0;
+	}
+
+	if (strcmp(encoding, "raw") == 0) {
+		strcpy(cipher, "rsa");
+		return 0;
+	}
+
+	return -ENOPKG;
+}
+
+static int tpm2_key_rsa_extract_pub(struct tpm2_key_rsa *key_rsa)
+{
+	struct tpm2_key *key = &key_rsa->key;
+	struct tpm_buf buf;
+	off_t offset = 2;
+	u16 policy_len;
+	u32 attr;
+	u16 bits;
+	u16 type;
+	u16 len;
+	u16 alg;
+	u32 exp;
+
+	buf.flags = TPM_BUF_TPM2B;
+	buf.length = key->pub_len;
+	buf.data = (void *)key->pub;
+
+	if (get_unaligned_be16(key->pub) != buf.length - 2)
+		return -EINVAL;
+
+	type = tpm_buf_read_u16(&buf, &offset);
+	pr_debug("pub type: 0x%04x\n", type);
+	if (type != TPM_ALG_RSA)
+		return -EINVAL;
+
+	alg = tpm_buf_read_u16(&buf, &offset);
+	pr_debug("pub name alg: 0x%04x\n", alg);
+	attr = tpm_buf_read_u32(&buf, &offset);
+	pr_debug("pub attributes: 0x%08x\n", attr);
+	policy_len = tpm_buf_read_u16(&buf, &offset);
+	pr_debug("pub policy length: %u bytes\n", policy_len);
+	offset += policy_len;
+
+	alg = tpm_buf_read_u16(&buf, &offset);
+	pr_debug("pub symmetric: 0x%04x\n", alg);
+	if (alg != TPM_ALG_NULL)
+		return -EINVAL;
+
+	alg = tpm_buf_read_u16(&buf, &offset);
+	pr_debug("pub symmetric scheme: 0x%04x\n", alg);
+	if (alg != TPM_ALG_NULL)
+		return -EINVAL;
+
+	bits = tpm_buf_read_u16(&buf, &offset);
+	pr_debug("pub bits: %u\n", bits);
+
+	exp = tpm_buf_read_u32(&buf, &offset);
+	pr_debug("pub exponent: 0x%08x\n", exp);
+	if (exp != 0x00000000 && exp !=  0x00010001)
+		return -EINVAL;
+
+	len = tpm_buf_read_u16(&buf, &offset);
+	pr_debug("pub modulus: %u bytes\n", len);
+	key_rsa->pub = key->pub + offset;
+	key_rsa->pub_len = len;
+
+	return buf.flags & TPM_BUF_BOUNDARY_ERROR ? -EIO : 0;
+}
+
+static int tpm2_key_rsa_encode(const struct tpm2_key_rsa *key, u8 *buf)
+{
+	int pub_len = key->pub_len;
+	const u8 *pub = key->pub;
+	u8 *start = &buf[4];
+	u8 *work = &buf[4];
+	u32 seq_len;
+
+	work[0] = 0x02;			/* INTEGER */
+	work[1] = 0x82;			/* u16 */
+	work[2] = pub_len >> 8;
+	work[3] = pub_len & 0xff;
+	work = &work[4];
+	memcpy(work, pub, pub_len);
+	work = &work[pub_len];
+	work[0] = 0x02;			/* INTEGER */
+	work[1] = 3;			/* < 128 */
+	work[2] = 1;			/* 65537 */
+	work[3] = 0;
+	work[4]	= 1;
+	work = &work[5];
+	seq_len = work - start;
+	buf[0] = 0x30;			/* SEQUENCE */
+	buf[1] = 0x82;			/* u16 */
+	buf[2] = seq_len >> 8;
+	buf[3] = seq_len & 0xff;
+
+	/*
+	 * ABI requires this according include/crypto/akcipher.h, which says
+	 * that there is epilogue with algorithm OID and parameters length.
+	 * Neither size nor semantics is documented *anywhere*, and there's no
+	 * struct to hold them.
+	 *
+	 * So zeroing out the last eight bytes after the key blob seems like the
+	 * best bet, given no better (or any) information. The size of the
+	 * parameters (two u32's) was found from crypto/asymmetric/public_key.c.
+	 */
+	memset(work, 0, 8);
+
+	return seq_len + 4;
+}
+
+/*
+ * Encryption operation is performed with the public key.  Hence it is done
+ * in software
+ */
+static int tpm2_key_rsa_encrypt(struct tpm2_key_rsa *key,
+				struct kernel_pkey_params *params,
+				const void *in, void *out)
+{
+	u8 enc_pub_key[TPM2_KEY_RSA_PUB_ENC_SIZE];
+	char cipher[CRYPTO_MAX_ALG_NAME];
+	struct scatterlist in_sg, out_sg;
+	struct akcipher_request *req;
+	struct crypto_akcipher *tfm;
+	struct crypto_wait cwait;
+	int rc;
+
+	rc = tpm2_key_get_akcipher(params->encoding, params->hash_algo, cipher);
+	if (rc < 0)
+		return rc;
+
+	tfm = crypto_alloc_akcipher(cipher, 0, 0);
+	if (IS_ERR(tfm))
+		return PTR_ERR(tfm);
+
+	rc = tpm2_key_rsa_encode(key, enc_pub_key);
+	if (rc < 0)
+		goto err_tfm;
+
+	rc = crypto_akcipher_set_pub_key(tfm, enc_pub_key, rc);
+	if (rc < 0)
+		goto err_tfm;
+
+	req = akcipher_request_alloc(tfm, GFP_KERNEL);
+	if (!req) {
+		rc = -ENOMEM;
+		goto err_tfm;
+	}
+
+	sg_init_one(&in_sg, in, params->in_len);
+	sg_init_one(&out_sg, out, params->out_len);
+	akcipher_request_set_crypt(req, &in_sg, &out_sg, params->in_len,
+				   params->out_len);
+
+	crypto_init_wait(&cwait);
+	akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
+				      CRYPTO_TFM_REQ_MAY_SLEEP,
+				      crypto_req_done, &cwait);
+
+	rc = crypto_akcipher_encrypt(req);
+	rc = crypto_wait_req(rc, &cwait);
+	if (!rc)
+		rc = req->dst_len;
+
+	akcipher_request_free(req);
+
+err_tfm:
+	crypto_free_akcipher(tfm);
+
+	return rc;
+}
+
+static int __tpm2_key_rsa_decrypt(struct tpm_chip *chip,
+				  struct tpm2_key_rsa *key,
+				  struct kernel_pkey_params *params,
+				  const void *in, int in_len, void *out)
+{
+	u32 key_handle = 0;
+	struct tpm_buf buf;
+	u16 decrypted_len;
+	u32 parent;
+	u8 *pos;
+	int ret;
+
+	ret = tpm_try_get_ops(chip);
+	if (ret)
+		return ret;
+
+	ret = tpm2_start_auth_session(chip);
+	if (ret)
+		goto err_ops;
+
+	parent = key->key.parent;
+
+	ret = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
+	if (ret < 0)
+		goto err_auth;
+
+	tpm_buf_append_name(chip, &buf, parent, NULL);
+	tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_CONTINUE_SESSION |
+				    TPM2_SA_ENCRYPT, NULL, 0);
+	tpm_buf_append(&buf, key->key.blob, key->key.blob_len);
+	if (buf.flags & TPM_BUF_OVERFLOW) {
+		ret = -E2BIG;
+		goto err_buf;
+	}
+	tpm_buf_fill_hmac_session(chip, &buf);
+	ret = tpm_transmit_cmd(chip, &buf, 4, "RSA key loading");
+	ret = tpm_buf_check_hmac_response(chip, &buf, ret);
+	if (ret) {
+		ret = -EIO;
+		goto err_buf;
+	}
+	key_handle = be32_to_cpup((__be32 *)&buf.data[TPM_HEADER_SIZE]);
+
+	tpm_buf_reset(&buf, TPM2_ST_SESSIONS, TPM2_CC_RSA_DECRYPT);
+	tpm_buf_append_name(chip, &buf, key_handle, NULL);
+	tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT, NULL, 0);
+	tpm_buf_append_u16(&buf, in_len);
+	tpm_buf_append(&buf, in, in_len);
+	tpm_buf_append_u16(&buf, TPM_ALG_NULL);
+	tpm_buf_append_u16(&buf, 0);
+	tpm_buf_fill_hmac_session(chip, &buf);
+	ret = tpm_transmit_cmd(chip, &buf, 4, "RSA key decrypting");
+	ret = tpm_buf_check_hmac_response(chip, &buf, ret);
+	if (ret) {
+		ret = -EIO;
+		goto err_blob;
+	}
+
+	pos = buf.data + TPM_HEADER_SIZE + 4;
+	decrypted_len = be16_to_cpup((__be16 *)pos);
+	pos += 2;
+
+	if (params->out_len < decrypted_len) {
+		ret = -EMSGSIZE;
+		goto err_blob;
+	}
+
+	memcpy(out, pos, decrypted_len);
+	ret = decrypted_len;
+
+err_blob:
+	tpm2_flush_context(chip, key_handle);
+
+err_buf:
+	tpm_buf_destroy(&buf);
+
+err_auth:
+	if (ret < 0)
+		tpm2_end_auth_session(chip);
+
+err_ops:
+	tpm_put_ops(chip);
+	return ret;
+}
+
+static int tpm2_key_rsa_decrypt(struct tpm_chip *chip, struct tpm2_key_rsa *key,
+				struct kernel_pkey_params *params,
+				const void *in, void *out)
+{
+	const u8 *ptr;
+	int out_len;
+	u8 *work;
+	int ret;
+
+	work = kzalloc(params->out_len, GFP_KERNEL);
+	if (!work)
+		return -ENOMEM;
+
+	ret = __tpm2_key_rsa_decrypt(chip, key, params, in, params->in_len,
+				     work);
+	if (ret < 0)
+		goto err;
+
+	ptr = tpm2_unpad_pkcs1(work, ret, &out_len);
+	if (!ptr) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	if (out_len > params->out_len) {
+		ret = -EMSGSIZE;
+		goto err;
+	}
+
+	memcpy(out, ptr, out_len);
+	kfree(work);
+	return out_len;
+
+err:
+	kfree(work);
+	return ret;
+}
+
+/*
+ * Sign operation is an encryption using the TPM's private key. With RSA the
+ * only difference between encryption and decryption is where the padding goes.
+ * Since own padding can be used, TPM2_RSA_Decrypt can be repurposed to do
+ * encryption.
+ */
+static int tpm2_key_rsa_sign(struct tpm_chip *chip, struct tpm2_key_rsa *key,
+			     struct kernel_pkey_params *params,
+			     const void *in, void *out)
+{
+	const struct rsa_asn1_template *asn1;
+	u32 in_len = params->in_len;
+	void *asn1_wrapped = NULL;
+	int pub_len = key->pub_len;
+	u8 *padded;
+	int ret;
+
+	if (strcmp(params->encoding, "pkcs1") != 0) {
+		ret = -ENOPKG;
+		goto err;
+	}
+
+	if (params->hash_algo) {
+		asn1 = rsa_lookup_asn1(params->hash_algo);
+		if (!asn1) {
+			ret = -ENOPKG;
+			goto err;
+		}
+
+		/* Request enough space for the ASN.1 template + input hash */
+		asn1_wrapped = kzalloc(in_len + asn1->size, GFP_KERNEL);
+		if (!asn1_wrapped) {
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		/* Copy ASN.1 template, then the input */
+		memcpy(asn1_wrapped, asn1->data, asn1->size);
+		memcpy(asn1_wrapped + asn1->size, in, in_len);
+
+		in = asn1_wrapped;
+		in_len += asn1->size;
+	}
+
+	/* with padding: */
+	padded = kmalloc(pub_len, GFP_KERNEL);
+	tpm2_pad_pkcs1(in, in_len, padded, pub_len);
+	ret = __tpm2_key_rsa_decrypt(chip, key, params, padded, pub_len, out);
+	kfree(padded);
+
+err:
+	kfree(asn1_wrapped);
+	return ret;
+}
+
+static void tpm2_key_rsa_describe(const struct key *asymmetric_key,
+				  struct seq_file *m)
+{
+	struct tpm2_key_rsa *key = asymmetric_key->payload.data[asym_crypto];
+
+	if (!key) {
+		pr_err("key blob missing");
+		return;
+	}
+
+	seq_puts(m, "TPM2/RSA");
+}
+
+static void tpm2_key_rsa_destroy(void *payload0, void *payload3)
+{
+	struct tpm2_key *key = payload0;
+
+	if (!key)
+		return;
+
+	tpm2_key_destroy(key);
+	kfree(key);
+}
+
+static int tpm2_key_rsa_eds_op(struct kernel_pkey_params *params,
+			       const void *in, void *out)
+{
+	struct tpm2_key_rsa *key = params->key->payload.data[asym_crypto];
+	struct tpm_chip *chip = tpm_default_chip();
+
+	if (!chip)
+		return -ENODEV;
+
+	switch (params->op) {
+	case kernel_pkey_encrypt:
+		return tpm2_key_rsa_encrypt(key, params, in, out);
+	case kernel_pkey_decrypt:
+		return tpm2_key_rsa_decrypt(chip, key, params, in, out);
+	case kernel_pkey_sign:
+		return tpm2_key_rsa_sign(chip, key, params, in, out);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static int tpm2_key_rsa_verify(const struct key *key,
+			       const struct public_key_signature *sig)
+{
+	const struct tpm2_key_rsa *tpm2_key = key->payload.data[asym_crypto];
+	u8 enc_pub_key[TPM2_KEY_RSA_PUB_ENC_SIZE];
+	char alg_name[CRYPTO_MAX_ALG_NAME];
+	struct akcipher_request *req;
+	struct scatterlist src_sg[2];
+	struct crypto_akcipher *tfm;
+	struct crypto_wait cwait;
+	int rc;
+
+	if (!sig->digest)
+		return -ENOPKG;
+
+	rc = tpm2_key_get_akcipher(sig->encoding, sig->hash_algo, alg_name);
+	if (rc < 0)
+		return rc;
+
+	tfm = crypto_alloc_akcipher(alg_name, 0, 0);
+	if (IS_ERR(tfm))
+		return PTR_ERR(tfm);
+
+	rc = tpm2_key_rsa_encode(tpm2_key, enc_pub_key);
+	if (rc < 0)
+		goto err_tfm;
+
+	rc = crypto_akcipher_set_pub_key(tfm, enc_pub_key, rc);
+	if (rc < 0)
+		goto err_tfm;
+
+	rc = -ENOMEM;
+	req = akcipher_request_alloc(tfm, GFP_KERNEL);
+	if (!req)
+		goto err_tfm;
+
+	sg_init_table(src_sg, 2);
+	sg_set_buf(&src_sg[0], sig->s, sig->s_size);
+	sg_set_buf(&src_sg[1], sig->digest, sig->digest_size);
+	akcipher_request_set_crypt(req, src_sg, NULL, sig->s_size,
+				   sig->digest_size);
+	crypto_init_wait(&cwait);
+	akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
+				      CRYPTO_TFM_REQ_MAY_SLEEP,
+				      crypto_req_done, &cwait);
+	rc = crypto_wait_req(crypto_akcipher_verify(req), &cwait);
+
+	akcipher_request_free(req);
+
+err_tfm:
+	crypto_free_akcipher(tfm);
+	return rc;
+}
+
+static int tpm2_key_rsa_query(const struct kernel_pkey_params *params,
+			      struct kernel_pkey_query *info)
+{
+	struct tpm2_key_rsa *tk = params->key->payload.data[asym_crypto];
+	u8 enc_pub_key[TPM2_KEY_RSA_PUB_ENC_SIZE];
+	char alg_name[CRYPTO_MAX_ALG_NAME];
+	struct crypto_akcipher *tfm;
+	unsigned int len;
+	int ret;
+
+	ret = tpm2_key_get_akcipher(params->encoding, params->hash_algo, alg_name);
+	if (ret < 0)
+		return ret;
+
+	tfm = crypto_alloc_akcipher(alg_name, 0, 0);
+	if (IS_ERR(tfm))
+		return PTR_ERR(tfm);
+
+	ret = tpm2_key_rsa_encode(tk, enc_pub_key);
+	if (ret < 0)
+		goto err_tfm;
+
+	ret = crypto_akcipher_set_pub_key(tfm, enc_pub_key, ret);
+	if (ret < 0)
+		goto err_tfm;
+
+	len = crypto_akcipher_maxsize(tfm);
+
+	info->key_size = tk->pub_len * 8;
+	info->max_data_size = tk->pub_len;
+	info->max_sig_size = len;
+	info->max_enc_size = len;
+	info->max_dec_size = tk->pub_len;
+
+	info->supported_ops = KEYCTL_SUPPORTS_ENCRYPT |
+			      KEYCTL_SUPPORTS_DECRYPT |
+			      KEYCTL_SUPPORTS_VERIFY |
+			      KEYCTL_SUPPORTS_SIGN;
+
+err_tfm:
+	crypto_free_akcipher(tfm);
+	return ret;
+}
+
+/*
+ * Asymmetric TPM2 RSA key. Signs and decrypts with TPM.
+ */
+struct asymmetric_key_subtype tpm2_key_rsa_subtype = {
+	.owner			= THIS_MODULE,
+	.name			= "tpm2_key_rsa",
+	.name_len		= sizeof("tpm2_key_rsa") - 1,
+	.describe		= tpm2_key_rsa_describe,
+	.destroy		= tpm2_key_rsa_destroy,
+	.query			= tpm2_key_rsa_query,
+	.eds_op			= tpm2_key_rsa_eds_op,
+	.verify_signature	= tpm2_key_rsa_verify,
+};
+EXPORT_SYMBOL_GPL(tpm2_key_rsa_subtype);
+
+/*
+ * Attempt to parse a data blob for a key as a TPM private key blob.
+ */
+static int tpm2_key_preparse(struct key_preparsed_payload *prep)
+{
+	struct tpm2_key_rsa *key;
+	int ret;
+
+	key = kzalloc(sizeof(*key), GFP_KERNEL);
+	if (!key)
+		return -ENOMEM;
+
+	if (prep->datalen > TPM2_KEY_RSA_BUFFER_SIZE) {
+		kfree(key);
+		return -EMSGSIZE;
+	}
+
+	ret = tpm2_key_decode(prep->data, prep->datalen, &key->key,
+			      TPM2_KEY_RSA_BUFFER_SIZE);
+	if (ret) {
+		kfree(key);
+		return ret;
+	}
+
+	if (key->key.oid != OID_TPMLoadableKey) {
+		tpm2_key_destroy(&key->key);
+		kfree(key);
+		return -EINVAL;
+	}
+
+	ret = tpm2_key_rsa_extract_pub(key);
+	if (ret < 0) {
+		tpm2_key_destroy(&key->key);
+		kfree(key);
+		return ret;
+	}
+
+	prep->payload.data[asym_subtype] = &tpm2_key_rsa_subtype;
+	prep->payload.data[asym_key_ids] = NULL;
+	prep->payload.data[asym_crypto] = key;
+	prep->payload.data[asym_auth] = NULL;
+	prep->quotalen = 100;
+	return 0;
+}
+
+static struct asymmetric_key_parser tpm2_key_rsa_parser = {
+	.owner	= THIS_MODULE,
+	.name	= "tpm2_key_rsa_parser",
+	.parse	= tpm2_key_preparse,
+};
+
+static int __init tpm2_key_rsa_init(void)
+{
+	return register_asymmetric_key_parser(&tpm2_key_rsa_parser);
+}
+
+static void __exit tpm2_key_rsa_exit(void)
+{
+	unregister_asymmetric_key_parser(&tpm2_key_rsa_parser);
+}
+
+module_init(tpm2_key_rsa_init);
+module_exit(tpm2_key_rsa_exit);
+
+MODULE_DESCRIPTION("Asymmetric TPM2 RSA key");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index c17e4efbb2e5..040be2c75868 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -43,6 +43,7 @@ enum tpm2_session_types {
 /* if you add a new hash to this, increment TPM_MAX_HASHES below */
 enum tpm_algorithms {
 	TPM_ALG_ERROR		= 0x0000,
+	TPM_ALG_RSA		= 0x0001,
 	TPM_ALG_SHA1		= 0x0004,
 	TPM_ALG_AES		= 0x0006,
 	TPM_ALG_KEYEDHASH	= 0x0008,
@@ -271,6 +272,7 @@ enum tpm2_command_codes {
 	TPM2_CC_NV_READ                 = 0x014E,
 	TPM2_CC_CREATE		        = 0x0153,
 	TPM2_CC_LOAD		        = 0x0157,
+	TPM2_CC_RSA_DECRYPT	        = 0x0159,
 	TPM2_CC_SEQUENCE_UPDATE         = 0x015C,
 	TPM2_CC_UNSEAL		        = 0x015E,
 	TPM2_CC_CONTEXT_LOAD	        = 0x0161,
-- 
2.45.1


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

* Re: [PATCH v5 5/5] keys: asymmetric: ASYMMETRIC_TPM2_KEY_RSA_SUBTYPE
  2024-05-23 21:25 ` [PATCH v5 5/5] keys: asymmetric: ASYMMETRIC_TPM2_KEY_RSA_SUBTYPE Jarkko Sakkinen
@ 2024-05-23 21:39   ` Jarkko Sakkinen
  2024-05-23 21:52     ` Jarkko Sakkinen
  0 siblings, 1 reply; 10+ messages in thread
From: Jarkko Sakkinen @ 2024-05-23 21:39 UTC (permalink / raw)
  To: Jarkko Sakkinen, Herbert Xu
  Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
	David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
	David S. Miller, open list, David Howells, James Bottomley,
	Stefan Berger, Ard Biesheuvel, Mario Limonciello

On Fri May 24, 2024 at 12:25 AM EEST, Jarkko Sakkinen wrote:
> +	/*
> +	 * ABI requires this according include/crypto/akcipher.h, which says
> +	 * that there is epilogue with algorithm OID and parameters length.
> +	 * Neither size nor semantics is documented *anywhere*, and there's no
> +	 * struct to hold them.
> +	 *
> +	 * So zeroing out the last eight bytes after the key blob seems like the
> +	 * best bet, given no better (or any) information. The size of the
> +	 * parameters (two u32's) was found from crypto/asymmetric/public_key.c.
> +	 */
> +	memset(work, 0, 8);

This is a mystery (or nightmare).

BR, Jarkko

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

* Re: [PATCH v5 5/5] keys: asymmetric: ASYMMETRIC_TPM2_KEY_RSA_SUBTYPE
  2024-05-23 21:39   ` Jarkko Sakkinen
@ 2024-05-23 21:52     ` Jarkko Sakkinen
  2024-05-23 22:02       ` Jarkko Sakkinen
  0 siblings, 1 reply; 10+ messages in thread
From: Jarkko Sakkinen @ 2024-05-23 21:52 UTC (permalink / raw)
  To: Jarkko Sakkinen, Herbert Xu
  Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
	David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
	David S. Miller, open list, David Howells, James Bottomley,
	Stefan Berger, Ard Biesheuvel, Mario Limonciello

On Fri May 24, 2024 at 12:39 AM EEST, Jarkko Sakkinen wrote:
> On Fri May 24, 2024 at 12:25 AM EEST, Jarkko Sakkinen wrote:
> > +	/*
> > +	 * ABI requires this according include/crypto/akcipher.h, which says
> > +	 * that there is epilogue with algorithm OID and parameters length.
> > +	 * Neither size nor semantics is documented *anywhere*, and there's no
> > +	 * struct to hold them.
> > +	 *
> > +	 * So zeroing out the last eight bytes after the key blob seems like the
> > +	 * best bet, given no better (or any) information. The size of the
> > +	 * parameters (two u32's) was found from crypto/asymmetric/public_key.c.
> > +	 */
> > +	memset(work, 0, 8);
>
> This is a mystery (or nightmare).

This is from akchiper_alg documentation:

 * @set_pub_key: Function invokes the algorithm specific set public key
 *		function, which knows how to decode and interpret
 *		the BER encoded public key and parameters

No struct, no size information and no description what they are used for.

Can we get these properly documented? My documentation at the moment
is grep and kprobes, literally.

BR, Jarkko

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

* Re: [PATCH v5 5/5] keys: asymmetric: ASYMMETRIC_TPM2_KEY_RSA_SUBTYPE
  2024-05-23 21:52     ` Jarkko Sakkinen
@ 2024-05-23 22:02       ` Jarkko Sakkinen
  0 siblings, 0 replies; 10+ messages in thread
From: Jarkko Sakkinen @ 2024-05-23 22:02 UTC (permalink / raw)
  To: Jarkko Sakkinen, Herbert Xu
  Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
	David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
	David S. Miller, open list, David Howells, James Bottomley,
	Stefan Berger, Ard Biesheuvel, Mario Limonciello

On Fri May 24, 2024 at 12:52 AM EEST, Jarkko Sakkinen wrote:
> On Fri May 24, 2024 at 12:39 AM EEST, Jarkko Sakkinen wrote:
> > On Fri May 24, 2024 at 12:25 AM EEST, Jarkko Sakkinen wrote:
> > > +	/*
> > > +	 * ABI requires this according include/crypto/akcipher.h, which says
> > > +	 * that there is epilogue with algorithm OID and parameters length.
> > > +	 * Neither size nor semantics is documented *anywhere*, and there's no
> > > +	 * struct to hold them.
> > > +	 *
> > > +	 * So zeroing out the last eight bytes after the key blob seems like the
> > > +	 * best bet, given no better (or any) information. The size of the
> > > +	 * parameters (two u32's) was found from crypto/asymmetric/public_key.c.
> > > +	 */
> > > +	memset(work, 0, 8);
> >
> > This is a mystery (or nightmare).
>
> This is from akchiper_alg documentation:
>
>  * @set_pub_key: Function invokes the algorithm specific set public key
>  *		function, which knows how to decode and interpret
>  *		the BER encoded public key and parameters
>
> No struct, no size information and no description what they are used for.
>
> Can we get these properly documented? My documentation at the moment
> is grep and kprobes, literally.

That said, zero issues with the interface, just pointing out the
part that is not right, and should be fixed.

I mean I have three layers: this, rsa-pcks1 and rsa. How I can be
sure that either of two layers below never ever up until sun melts
will do any changes that would break, with the data that I put
there? Is this a contract that will hold forever?

This is concerning so I have to point this out.

BR, Jarkko

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

* Re: [PATCH v5 0/5] KEYS: asymmetric: tpm2_key_rsa
  2024-05-23 21:25 [PATCH v5 0/5] KEYS: asymmetric: tpm2_key_rsa Jarkko Sakkinen
                   ` (4 preceding siblings ...)
  2024-05-23 21:25 ` [PATCH v5 5/5] keys: asymmetric: ASYMMETRIC_TPM2_KEY_RSA_SUBTYPE Jarkko Sakkinen
@ 2024-05-24  9:18 ` Jarkko Sakkinen
  5 siblings, 0 replies; 10+ messages in thread
From: Jarkko Sakkinen @ 2024-05-24  9:18 UTC (permalink / raw)
  To: Jarkko Sakkinen, Herbert Xu
  Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
	David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
	David S. Miller, open list

On Fri May 24, 2024 at 12:25 AM EEST, Jarkko Sakkinen wrote:
> ## Overview
>
> Introduce tpm2_key_rsa module, which implements asymmetric TPM2 RSA key.
> The feature can be enabled with the CONFIG_ASYMMETRIC_TPM2_KEY_RSA_SUBTYPE 
> kconfig option. This feature allows the private key to be uploaded to
> the TPM2 for signing, and software can use the public key to verify
> the signatures.

Since barely v6.9 is out I wrote over night also tpm2_key_ecdsa i.e.
ECC/ECDSA based module :-)

It was a good idea. I realized e.g. actually documented in the API
fact that I should return -EBADMSG as legit undetected. Also found
a memory corruption bugs.

I renamed extract_pub to probe because that made me sort of realized
the role better too. Some of the code could later on put to up-level
struct tpm2_key but it is not a functional requirement.

I.e. top-level does raw parsing and then these modules check each
that if this is for them (e.g. ECDSA) then eat it. Otherwise, pass
over.

I did do some rudimentary testing and it seems to be quite good, and
my pattern seems to work. I.e. different modules for RSA and ECDSA
fit well how asymmetric keys are probed and allows to do as a sysadmin
appropriate configuration for the use case.

My biggest concern is undocumented parameters API in akcipher.

BR, Jarkko

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

end of thread, other threads:[~2024-05-24  9:18 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-05-23 21:25 [PATCH v5 0/5] KEYS: asymmetric: tpm2_key_rsa Jarkko Sakkinen
2024-05-23 21:25 ` [PATCH v5 1/5] crypto: rsa-pkcs1pad: export rsa1_asn_lookup() Jarkko Sakkinen
2024-05-23 21:25 ` [PATCH v5 2/5] KEYS: trusted: Change -EINVAL to -E2BIG Jarkko Sakkinen
2024-05-23 21:25 ` [PATCH v5 3/5] KEYS: trusted: Move tpm2_key_decode() to the TPM driver Jarkko Sakkinen
2024-05-23 21:25 ` [PATCH v5 4/5] tpm: tpm2_key: Extend parser to TPM_LoadableKey Jarkko Sakkinen
2024-05-23 21:25 ` [PATCH v5 5/5] keys: asymmetric: ASYMMETRIC_TPM2_KEY_RSA_SUBTYPE Jarkko Sakkinen
2024-05-23 21:39   ` Jarkko Sakkinen
2024-05-23 21:52     ` Jarkko Sakkinen
2024-05-23 22:02       ` Jarkko Sakkinen
2024-05-24  9:18 ` [PATCH v5 0/5] KEYS: asymmetric: tpm2_key_rsa Jarkko Sakkinen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).