[PATCH 23/23] TPM: Add an asymmetric key subtype for handling TPM-based keys

David Howells
2018-08-21T19:20:11Z

Add an asymmetric key subtype for handling keys that have to be loaded into
the TPM to be used.

A key can be created by something like:

keyctl add asymmetric "a" "tpm_create parent=40000000,095c2a76085f6aa9327c62f72a3d1348f62b99db keyauth=095c2a76085f6aa9327c62f72a3d1348f62b99db" @s

where "parent=<parent_key_handle>,<parent_key_secret>" and
"keyauth=<new_key_secret>".

The above will ask the TPM to create a key and return the TPM_KEY struct as a
blob with the private key encrypted by the parent key (in the above case, the
SRK).

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

crypto/asymmetric_keys/Kconfig | 7 +
crypto/asymmetric_keys/Makefile | 1
crypto/asymmetric_keys/tpm_key.c | 73 +++++++++++
crypto/asymmetric_keys/tpm_key.h | 19 +++
crypto/asymmetric_keys/tpm_key_parser.c | 212 +++++++++++++++++++++++++++++++
5 files changed, 312 insertions(+)
create mode 100644 crypto/asymmetric_keys/tpm_key.c
create mode 100644 crypto/asymmetric_keys/tpm_key.h
create mode 100644 crypto/asymmetric_keys/tpm_key_parser.c

diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index 4870f28403f5..97d1bb714617 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -67,4 +67,11 @@ config SIGNED_PE_FILE_VERIFICATION
This option provides support for verifying the signature(s) on a
signed PE binary.

+config ASYMMETRIC_TPM_KEY_SUBTYPE
+ tristate "Asymmetric TPM-based public-key crypto algorithm subtype"
+ depends on TCG_TPM
+ help
+ This option provides support for TPM hardware-based asymmetric public
+ key type handling.
+
endif # ASYMMETRIC_KEY_TYPE
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index e47fcd9ac5e8..690c16a517a9 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o
asymmetric_keys-y := asymmetric_type.o signature.o

obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
+obj-$(CONFIG_ASYMMETRIC_TPM_KEY_SUBTYPE) += tpm_key.o tpm_key_parser.o
obj-$(CONFIG_PUBLIC_KEY_ALGO_RSA) += rsa.o

#
diff --git a/crypto/asymmetric_keys/tpm_key.c b/crypto/asymmetric_keys/tpm_key.c
new file mode 100644
index 000000000000..bfddedc9db32
--- /dev/null
+++ b/crypto/asymmetric_keys/tpm_key.c
@@ -0,0 +1,73 @@
+/* In-TPM asymmetric public-key crypto subtype
+ *
+ * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "TPK: "fmt
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/tpm.h>
+#include <keys/asymmetric-subtype.h>
+#include "tpm_key.h"
+
+MODULE_LICENSE("GPL");
+
+/*
+ * Provide a part of a description of the key for /proc/keys.
+ */
+static void tpm_key_describe(const struct key *asymmetric_key,
+ struct seq_file *m)
+{
+ struct tpm_asymmetric_key *key = asymmetric_key->payload.data;
+ struct tpm_wrapped_key *wrap;
+
+ if (key && key->wrapped_key) {
+ wrap = key->wrapped_key;
+ seq_printf(m, "TPM.RSA %*phN",
+ wrap->pubkey_len, wrap->data + wrap->pubkey_offset);
+ }
+}
+
+/*
+ * Destroy a TPM-based key.
+ */
+static void tpm_key_destroy(void *payload)
+{
+ struct tpm_asymmetric_key *key = payload;
+
+ if (key) {
+ kfree(key->wrapped_key);
+ kfree(key);
+ tpm_library_unuse();
+ }
+}
+
+/*
+ * Verify a signature using a TPM-based key.
+ */
+static int tpm_key_verify_signature(const struct key *key,
+ const struct public_key_signature *sig)
+{
+ return -EOPNOTSUPP;
+}
+
+/*
+ * Public key algorithm asymmetric key subtype
+ */
+struct asymmetric_key_subtype tpm_key_subtype = {
+ .owner = THIS_MODULE,
+ .name = "tpm_key",
+ .describe = tpm_key_describe,
+ .destroy = tpm_key_destroy,
+ .verify_signature = tpm_key_verify_signature,
+};
+EXPORT_SYMBOL_GPL(tpm_key_subtype);
diff --git a/crypto/asymmetric_keys/tpm_key.h b/crypto/asymmetric_keys/tpm_key.h
new file mode 100644
index 000000000000..712221fee874
--- /dev/null
+++ b/crypto/asymmetric_keys/tpm_key.h
@@ -0,0 +1,19 @@
+/* TPM-based public key algorithm internals
+ *
+ * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+struct tpm_asymmetric_key {
+ struct tpm_wrapped_key *wrapped_key;
+ u32 parent_tpm_handle;
+ u8 parent_authdata[TPM_DIGEST_SIZE];
+ u8 key_authdata[TPM_DIGEST_SIZE];
+};
+
+extern struct asymmetric_key_subtype tpm_key_subtype;
diff --git a/crypto/asymmetric_keys/tpm_key_parser.c b/crypto/asymmetric_keys/tpm_key_parser.c
new file mode 100644
index 000000000000..efb53172b43d
--- /dev/null
+++ b/crypto/asymmetric_keys/tpm_key_parser.c
@@ -0,0 +1,212 @@
+/* Instantiate a TPM key.
+ *
+ * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#define DEBUG
+#define pr_fmt(fmt) "TPKP: "fmt
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/tpm.h>
+#include <linux/parser.h>
+#include <keys/asymmetric-subtype.h>
+#include <keys/asymmetric-parser.h>
+#include "asymmetric_keys.h"
+#include "tpm_key.h"
+
+
+enum tpm_key_create_token {
+ opt_crt_err = -1,
+ opt_crt_parent,
+ opt_crt_keyauth,
+};
+
+static const match_table_t tpm_key_create_tokens = {
+ { opt_crt_parent, "parent=%x,%s"},
+ { opt_crt_keyauth, "keyauth=%s"},
+ { opt_crt_err, NULL}
+};
+
+/*
+ * Attempt to parse a key creation request.
+ */
+static int tpm_key_create(struct tpm_asymmetric_key *key, char *data)
+{
+ enum tpm_key_create_token token;
+ struct tpm_chip *chip;
+ unsigned long tmp, got = 0;
+ substring_t args[MAX_OPT_ARGS];
+ uint32_t key_handle;
+ char *p;
+ int ret;
+
+ pr_devel("==>%s(,%s)\n", __func__, data);
+
+ while ((p = strsep(&data, " \t"))) {
+ if (*p == '\0' || *p == ' ' || *p == '\t')
+ continue;
+ token = match_token(p, tpm_key_create_tokens, args);
+ switch (token) {
+ case opt_crt_parent:
+ pr_devel("parent %ld %ld\n",
+ args[0].to - args[0].from,
+ args[1].to - args[1].from);
+ *args[0].to = 0;
+ ret = kstrtoul(args[0].from, 16, &tmp);
+ if (ret < 0) {
+ pr_devel("bad parent handle\n");
+ return -EINVAL;
+ }
+ key->parent_tpm_handle = tmp;
+ if (args[1].to - args[1].from != TPM_DIGEST_SIZE * 2) {
+ pr_devel("parent auth wrong size\n");
+ return -EINVAL;
+ }
+ if (hex2bin(key->parent_authdata, args[1].from,
+ TPM_DIGEST_SIZE) < 0) {
+ pr_devel("parent auth bad hex\n");
+ return -EINVAL;
+ }
+ break;
+
+ case opt_crt_keyauth:
+ pr_devel("keyauth\n");
+ if (args[1].to - args[1].from != TPM_DIGEST_SIZE * 2)
+ return -EINVAL;
+ if (hex2bin(key->parent_authdata, args[1].from,
+ TPM_DIGEST_SIZE) < 0)
+ return -EINVAL;
+ break;
+
+ case opt_crt_err:
+ pr_devel("Unknown token %s\n", p);
+ return -EINVAL;
+ }
+ got |= 1 << token;
+ }
+
+ if ((got & 3) != 3) {
+ pr_devel("Missing mandatory args\n");
+ return -EINVAL;
+ }
+
+ chip = tpm_chip_find_get(TPM_ANY_NUM);
+ if (!chip)
+ return -ENODEV;
+
+ /* Create a key and retrieve the partially encrypted blob. */
+ ret = tpm_create_wrap_key(chip, TPM_ET_SRK, key->parent_tpm_handle,
+ key->parent_authdata,
+ key->key_authdata,
+ NULL,
+ &key->wrapped_key);
+ if (ret == -EBADMSG)
+ ret = -EIO;
+
+ /* Attempt to load the key back as a check */
+ ret = tpm_load_key2(chip, TPM_ET_SRK, key->parent_tpm_handle,
+ key->parent_authdata, key->wrapped_key,
+ &key_handle);
+ if (ret != 0) {
+ pr_devel("Couldn't load key back\n");
+ goto out;
+ }
+
+ ret = tpm_flush_specific(chip, key_handle, TPM_RT_KEY);
+ if (ret != 0)
+ pr_devel("Couldn't flush key handle\n");
+
+out:
+ tpm_chip_put(chip);
+ pr_devel("<==%s() = %d\n", __func__, ret);
+ return ret;
+}
+
+/*
+ * Attempt to parse a data blob for a key as a TPM key specification.
+ *
+ * We expect one of the following in the prep data:
+ *
+ * tpm_create parent=<key>,<auth> keyauth=<hex> [options...]
+ * tpm_load parent=<key>,<auth> data=<hex> [options...]
+ */
+static int tpm_key_preparse(struct key_preparsed_payload *prep)
+{
+ struct tpm_asymmetric_key *key;
+ char *data;
+ int ret;
+
+ pr_devel("==>%s()\n", __func__);
+
+ ret = tpm_library_use();
+ if (ret < 0)
+ goto out;
+
+ ret = -ENOMEM;
+ key = kzalloc(sizeof(*key), GFP_KERNEL);
+ if (!key)
+ goto out_free_tpmlib;
+ data = kmalloc(prep->datalen + 1, GFP_KERNEL);
+ if (!data)
+ goto out_free_key;
+
+ memcpy(data, prep->data, prep->datalen);
+ data[prep->datalen] = 0;
+ if (memcmp(data, "tpm_create ", 11) == 0) {
+ ret = tpm_key_create(key, data + 11);
+ } else {
+ ret = -EBADMSG;
+ goto out_free_data;
+ }
+
+ /* We're pinning the module by being linked against it */
+ __module_get(tpm_key_subtype.owner);
+ prep->type_data[0] = &tpm_key_subtype;
+ //prep->type_data[1] = kids;
+ prep->payload[0] = key;
+ //prep->description = desc;
+ prep->quotalen = 100;
+ key = NULL;
+ tpm_library_use();
+
+out_free_data:
+ kfree(data);
+out_free_key:
+ kfree(key);
+out_free_tpmlib:
+ tpm_library_unuse();
+out:
+ return ret;
+}
+
+static struct asymmetric_key_parser tpm_key_parser = {
+ .owner = THIS_MODULE,
+ .name = "tpm",
+ .parse = tpm_key_preparse,
+};
+
+/*
+ * Module stuff
+ */
+static int __init tpm_key_init(void)
+{
+ return register_asymmetric_key_parser(&tpm_key_parser);
+}
+
+static void __exit tpm_key_exit(void)
+{
+ unregister_asymmetric_key_parser(&tpm_key_parser);
+}
+
+module_init(tpm_key_init);
+module_exit(tpm_key_exit);
+
+MODULE_DESCRIPTION("TPM key parser");
+MODULE_LICENSE("GPL");