diff mbox series

[RFC,04/10] KEYS: asymmetric: Introduce the user asymmetric key parser

Message ID 20230706144225.1046544-5-roberto.sassu@huaweicloud.com (mailing list archive)
State Handled Elsewhere
Headers show
Series KEYS: Introduce user asymmetric keys and signatures | expand

Commit Message

Roberto Sassu July 6, 2023, 2:42 p.m. UTC
From: Roberto Sassu <roberto.sassu@huawei.com>

Introduce a new parser for user asymmetric keys, in TLV format. User space
tools are expected to convert keys from their original format to the TLV
format.

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 crypto/asymmetric_keys/Kconfig            |   1 +
 crypto/asymmetric_keys/Makefile           |   3 +-
 crypto/asymmetric_keys/asymmetric_type.c  |   3 +-
 crypto/asymmetric_keys/uasym_key_parser.c | 229 ++++++++++++++++++++++
 crypto/asymmetric_keys/uasym_parser.h     |   5 +
 include/keys/asymmetric-type.h            |   1 +
 include/uapi/linux/uasym_parser.h         |   7 +
 7 files changed, 247 insertions(+), 2 deletions(-)
 create mode 100644 crypto/asymmetric_keys/uasym_key_parser.c
diff mbox series

Patch

diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index 4f86fe78efd..d4b8f52a126 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -88,6 +88,7 @@  config FIPS_SIGNATURE_SELFTEST
 config UASYM_KEYS_SIGS
 	tristate "User asymmetric keys and signatures"
 	depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+	select CRYPTO_PUB_KEY_INFO
 	help
 	  This option enables user asymmetric keys and signatures. They are
 	  keys and signatures converted in user space from their native
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index ac3955d834f..6708a9e81ed 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -82,4 +82,5 @@  $(obj)/mscode.asn1.o: $(obj)/mscode.asn1.c $(obj)/mscode.asn1.h
 #
 obj-$(CONFIG_UASYM_KEYS_SIGS) += uasym_keys_sigs.o
 uasym_keys_sigs-y := \
-	uasym_parser.o
+	uasym_parser.o \
+	uasym_key_parser.o
diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c
index a5da8ccd353..53d0fc26eac 100644
--- a/crypto/asymmetric_keys/asymmetric_type.c
+++ b/crypto/asymmetric_keys/asymmetric_type.c
@@ -430,7 +430,7 @@  static int asymmetric_key_preparse(struct key_preparsed_payload *prep)
 /*
  * Clean up the key ID list
  */
-static void asymmetric_key_free_kids(struct asymmetric_key_ids *kids)
+void asymmetric_key_free_kids(struct asymmetric_key_ids *kids)
 {
 	int i;
 
@@ -440,6 +440,7 @@  static void asymmetric_key_free_kids(struct asymmetric_key_ids *kids)
 		kfree(kids);
 	}
 }
+EXPORT_SYMBOL_GPL(asymmetric_key_free_kids);
 
 /*
  * Clean up the preparse data
diff --git a/crypto/asymmetric_keys/uasym_key_parser.c b/crypto/asymmetric_keys/uasym_key_parser.c
new file mode 100644
index 00000000000..2de3f9afa64
--- /dev/null
+++ b/crypto/asymmetric_keys/uasym_key_parser.c
@@ -0,0 +1,229 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * Implement the user asymmetric key parser.
+ */
+
+#define pr_fmt(fmt) "UASYM KEY: "fmt
+#include <linux/module.h>
+#include <crypto/public_key.h>
+#include <crypto/pub_key_info.h>
+
+#include "uasym_parser.h"
+
+static int parse_key_pub(struct public_key *pub, enum fields field,
+			 const u8 *field_data, u32 field_data_len)
+{
+	int ret = 0;
+
+	kenter(",%u,%u", field, field_data_len);
+
+	pub->key = kmemdup(field_data, field_data_len, GFP_KERNEL);
+	if (!pub->key) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	pub->keylen = field_data_len;
+	pr_debug("Key length in bytes: %d\n", pub->keylen);
+out:
+	kleave(" = %d", ret);
+	return ret;
+}
+
+int parse_key_algo(const char **pkey_algo, enum fields field,
+		   const u8 *field_data, u32 field_data_len)
+{
+	u8 algo;
+	int ret = 0;
+
+	kenter(",%u,%u", field, field_data_len);
+
+	if (field_data_len != sizeof(u8)) {
+		pr_debug("Unexpected data length %u, expected %lu\n",
+			 field_data_len, sizeof(u8));
+		ret = -EBADMSG;
+		goto out;
+	}
+
+	algo = *field_data;
+
+	if (algo >= PKEY_ALGO__LAST) {
+		pr_debug("Unexpected public key algo %u\n", algo);
+		ret = -EBADMSG;
+		goto out;
+	}
+
+	*pkey_algo = pub_key_algo_name[algo];
+	pr_debug("Public key algo: %s\n", *pkey_algo);
+out:
+	kleave(" = %d", ret);
+	return ret;
+}
+
+int parse_key_kid(struct asymmetric_key_id **id, enum fields field,
+		  const u8 *field_data, u32 field_data_len)
+{
+	int ret = 0;
+
+	kenter(",%u,%u", field, field_data_len);
+
+	*id = asymmetric_key_generate_id(field_data, field_data_len, NULL, 0);
+	if (!*id) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	pr_debug("Key/auth identifier: %*phN\n", (*id)->len, (*id)->data);
+out:
+	kleave(" = %d", ret);
+	return ret;
+}
+
+static int parse_key_desc(struct key_preparsed_payload *prep, enum fields field,
+			  const u8 *field_data, u32 field_data_len)
+{
+	int ret = 0;
+
+	kenter(",%u,%u", field, field_data_len);
+
+	if (field_data[field_data_len - 1] != '\0') {
+		pr_err("Non-terminated string\n");
+		ret = -EBADMSG;
+		goto out;
+	}
+
+	prep->description = kstrndup(field_data, field_data_len, GFP_KERNEL);
+	if (!prep->description) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	pr_debug("Key description: %s\n", prep->description);
+out:
+	kleave(" = %d", ret);
+	return ret;
+}
+
+struct callback_struct {
+	struct public_key *pub;
+	struct asymmetric_key_ids *kids;
+	struct key_preparsed_payload *prep;
+};
+
+static int key_callback(void *callback_data, enum fields field,
+			const u8 *field_data, u32 field_data_len)
+{
+	struct callback_struct *cb_s = (struct callback_struct *)callback_data;
+	struct asymmetric_key_id **id;
+	int ret;
+
+	switch (field) {
+	case KEY_PUB:
+		ret = parse_key_pub(cb_s->pub, field, field_data,
+				    field_data_len);
+		break;
+	case KEY_ALGO:
+		ret = parse_key_algo(&cb_s->pub->pkey_algo, field, field_data,
+				     field_data_len);
+		break;
+	case KEY_KID0:
+		id = (struct asymmetric_key_id **)&cb_s->kids->id[0];
+		ret = parse_key_kid(id, field, field_data, field_data_len);
+		break;
+	case KEY_KID1:
+		id = (struct asymmetric_key_id **)&cb_s->kids->id[1];
+		ret = parse_key_kid(id, field, field_data, field_data_len);
+		break;
+	case KEY_KID2:
+		id = (struct asymmetric_key_id **)&cb_s->kids->id[2];
+		ret = parse_key_kid(id, field, field_data, field_data_len);
+		break;
+	case KEY_DESC:
+		ret = parse_key_desc(cb_s->prep, field, field_data,
+				     field_data_len);
+		break;
+	default:
+		/* Just ignore non-relevant fields. */
+		ret = 0;
+		break;
+	}
+
+	return ret;
+}
+
+static int uasym_key_parse(struct key_preparsed_payload *prep)
+{
+	struct callback_struct cb_s;
+	int ret;
+
+	kenter("");
+
+	cb_s.pub = kzalloc(sizeof(*cb_s.pub), GFP_KERNEL);
+	if (!cb_s.pub) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	cb_s.pub->id_type = "UASYM_KEY";
+
+	cb_s.kids = kzalloc(sizeof(*cb_s.kids), GFP_KERNEL);
+	if (!cb_s.kids) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	cb_s.prep = prep;
+
+	ret = uasym_parse(TYPE_KEY, key_callback, &cb_s, prep->data,
+			  prep->datalen);
+	if (ret < 0)
+		goto out;
+
+	if (!cb_s.pub->key || !cb_s.pub->pkey_algo ||
+	    (!cb_s.kids->id[0] && !cb_s.kids->id[1] && !cb_s.kids->id[2])) {
+		pr_debug("Incomplete data\n");
+		ret = -ENOENT;
+		goto out;
+	}
+
+	/* We're pinning the module by being linked against it */
+	__module_get(public_key_subtype.owner);
+	prep->payload.data[asym_subtype] = &public_key_subtype;
+	prep->payload.data[asym_key_ids] = cb_s.kids;
+	prep->payload.data[asym_crypto] = cb_s.pub;
+	prep->quotalen = 100;
+out:
+	kleave(" = %d", ret);
+
+	if (ret < 0) {
+		public_key_free(cb_s.pub);
+		asymmetric_key_free_kids(cb_s.kids);
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct asymmetric_key_parser uasym_key_parser = {
+	.owner = THIS_MODULE,
+	.name = "uasym_key",
+	.parse = uasym_key_parse
+};
+
+static int __init uasym_key_init(void)
+{
+	return register_asymmetric_key_parser(&uasym_key_parser);
+}
+
+static void __exit uasym_key_exit(void)
+{
+	unregister_asymmetric_key_parser(&uasym_key_parser);
+}
+
+module_init(uasym_key_init);
+module_exit(uasym_key_exit);
+MODULE_LICENSE("GPL");
diff --git a/crypto/asymmetric_keys/uasym_parser.h b/crypto/asymmetric_keys/uasym_parser.h
index 985dda6aad3..0f629fb7a9b 100644
--- a/crypto/asymmetric_keys/uasym_parser.h
+++ b/crypto/asymmetric_keys/uasym_parser.h
@@ -28,3 +28,8 @@  int uasym_parse_data(parse_callback callback, void *callback_data,
 		     u16 num_fields, const u8 *data, size_t data_len);
 int uasym_parse(enum data_types expected_data_type, parse_callback callback,
 		void *callback_data, const u8 *data, size_t data_len);
+
+int parse_key_algo(const char **pkey_algo, enum fields field,
+		   const u8 *field_data, u32 field_data_len);
+int parse_key_kid(struct asymmetric_key_id **id, enum fields field,
+		  const u8 *data, u32 data_len);
diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h
index 69a13e1e5b2..acbb8c805f6 100644
--- a/include/keys/asymmetric-type.h
+++ b/include/keys/asymmetric-type.h
@@ -66,6 +66,7 @@  extern struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1,
 							    size_t len_1,
 							    const void *val_2,
 							    size_t len_2);
+void asymmetric_key_free_kids(struct asymmetric_key_ids *kids);
 static inline
 const struct asymmetric_key_ids *asymmetric_key_ids(const struct key *key)
 {
diff --git a/include/uapi/linux/uasym_parser.h b/include/uapi/linux/uasym_parser.h
index 8f0bc235492..42e0087ac2b 100644
--- a/include/uapi/linux/uasym_parser.h
+++ b/include/uapi/linux/uasym_parser.h
@@ -62,9 +62,16 @@  struct uasym_entry {
 } __packed;
 
 #define FOR_EACH_DATA_TYPE(DATA_TYPE) \
+	DATA_TYPE(TYPE_KEY) \
 	DATA_TYPE(TYPE__LAST)
 
 #define FOR_EACH_FIELD(FIELD) \
+	FIELD(KEY_PUB) \
+	FIELD(KEY_ALGO) \
+	FIELD(KEY_KID0) \
+	FIELD(KEY_KID1) \
+	FIELD(KEY_KID2) \
+	FIELD(KEY_DESC) \
 	FIELD(FIELD__LAST)
 
 #define GENERATE_ENUM(ENUM) ENUM,