diff mbox series

[RFC,v3,4/9] KEYS: asymmetric: Introduce the user asymmetric key parser

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

Commit Message

Roberto Sassu July 20, 2023, 3:32 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            |  13 ++
 crypto/asymmetric_keys/Makefile           |   6 +
 crypto/asymmetric_keys/asymmetric_type.c  |   3 +-
 crypto/asymmetric_keys/uasym_key_parser.c | 240 ++++++++++++++++++++++
 crypto/asymmetric_keys/uasym_parser.h     |  26 +++
 include/keys/asymmetric-type.h            |   1 +
 include/uapi/linux/uasym_parser.h         |  50 +++++
 7 files changed, 338 insertions(+), 1 deletion(-)
 create mode 100644 crypto/asymmetric_keys/uasym_key_parser.c
 create mode 100644 crypto/asymmetric_keys/uasym_parser.h
 create mode 100644 include/uapi/linux/uasym_parser.h
diff mbox series

Patch

diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index 1ef3b46d6f6..4a5f66511d4 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -85,4 +85,17 @@  config FIPS_SIGNATURE_SELFTEST
 	depends on ASYMMETRIC_KEY_TYPE
 	depends on PKCS7_MESSAGE_PARSER=X509_CERTIFICATE_PARSER
 
+config UASYM_KEYS_SIGS
+	tristate "User asymmetric keys and signatures"
+	depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+	select CRYPTO_PUB_KEY_INFO
+	select TLV_PARSER
+	help
+	  This option enables user asymmetric keys and signatures. They are
+	  keys and signatures converted in user space from their native
+	  format (e.g. PGP), to the TLV format (Type-Length-Value) understood
+	  by the kernel.
+
+	  Key and signature-specific fields are defined in the UAPI interface,
+	  so that user space converters can reference them.
 endif # ASYMMETRIC_KEY_TYPE
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index 0d1fa1b692c..5ba30cca09e 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -76,3 +76,9 @@  verify_signed_pefile-y := \
 
 $(obj)/mscode_parser.o: $(obj)/mscode.asn1.h $(obj)/mscode.asn1.h
 $(obj)/mscode.asn1.o: $(obj)/mscode.asn1.c $(obj)/mscode.asn1.h
+
+#
+# User asymmetric keys and signatures
+#
+obj-$(CONFIG_UASYM_KEYS_SIGS) += uasym_keys_sigs.o
+uasym_keys_sigs-y := 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..36a5faa2706
--- /dev/null
+++ b/crypto/asymmetric_keys/uasym_key_parser.c
@@ -0,0 +1,240 @@ 
+// 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 <linux/tlv_parser.h>
+#include <crypto/public_key.h>
+#include <crypto/pub_key_info.h>
+
+#include "uasym_parser.h"
+
+const char *data_types_str[] = {
+	FOR_EACH_DATA_TYPE(GENERATE_STRING)
+};
+
+const char *fields_str[] = {
+	FOR_EACH_FIELD(GENERATE_STRING)
+};
+
+static int parse_key_pub(struct public_key *pub, enum fields field,
+			 const u8 *field_data, u64 field_data_len)
+{
+	int ret = 0;
+
+	kenter(",%u,%llu", 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, u64 field_data_len)
+{
+	u8 algo;
+	int ret = 0;
+
+	kenter(",%u,%llu", field, field_data_len);
+
+	if (field_data_len != sizeof(u8)) {
+		pr_debug("Unexpected data length %llu, 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, u64 field_data_len)
+{
+	int ret = 0;
+
+	kenter(",%u,%llu", 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, u64 field_data_len)
+{
+	int ret = 0;
+
+	kenter(",%u,%llu", 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, u64 field, const u8 *field_data,
+			u64 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:
+		pr_debug("Unhandled field %llu\n", field);
+		/* 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 = tlv_parse(TYPE_KEY, key_callback, &cb_s, prep->data,
+			prep->datalen, data_types_str, TYPE__LAST, fields_str,
+			FIELD__LAST);
+	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
new file mode 100644
index 00000000000..cd8bc934d38
--- /dev/null
+++ b/crypto/asymmetric_keys/uasym_parser.h
@@ -0,0 +1,26 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * Header file of user asymmetric keys and signatures.
+ */
+
+#include <keys/asymmetric-subtype.h>
+#include <keys/asymmetric-parser.h>
+
+#include <uapi/linux/uasym_parser.h>
+
+#define kenter(FMT, ...) \
+	pr_debug("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
+#define kleave(FMT, ...) \
+	pr_debug("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
+
+extern const char *data_types_str[];
+extern const char *fields_str[];
+
+int parse_key_algo(const char **pkey_algo, enum fields field,
+		   const u8 *field_data, u64 field_data_len);
+int parse_key_kid(struct asymmetric_key_id **id, enum fields field,
+		  const u8 *field_data, u64 field_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
new file mode 100644
index 00000000000..25c2995b3d4
--- /dev/null
+++ b/include/uapi/linux/uasym_parser.h
@@ -0,0 +1,50 @@ 
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2023 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * Implement the user space interface for user asymmetric keys and signatures.
+ */
+
+#ifndef _UAPI_LINUX_UASYM_PARSER_H
+#define _UAPI_LINUX_UASYM_PARSER_H
+
+#include <linux/types.h>
+#include <linux/pub_key_info.h>
+
+#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,
+#define GENERATE_STRING(STRING) #STRING,
+
+/**
+ * enum data_types - Type of data to parse
+ *
+ * Enumerates the type of data to parse.
+ */
+enum data_types {
+	FOR_EACH_DATA_TYPE(GENERATE_ENUM)
+};
+
+/**
+ * enum fields - Data fields
+ *
+ * Enumerates the data fields. Some belongs to keys, some to signatures.
+ */
+enum fields {
+	FOR_EACH_FIELD(GENERATE_ENUM)
+};
+
+#endif /* _UAPI_LINUX_UASYM_PARSER_H */