diff mbox series

[RFC,03/10] KEYS: asymmetric: Introduce a parser for user asymmetric keys and sigs

Message ID 20230706144225.1046544-4-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 6, 2023, 2:42 p.m. UTC
From: Roberto Sassu <roberto.sassu@huawei.com>

Introduce the common parser for user asymmetric keys and signatures. The
data format is TLV-based, and consists of a header and the data.

Key and signature blobs can be parsed with the new function uasym_parse().
Each caller of that function should provide a callback function,
responsible to parse their fields, and an opaque data pointer to be used by
the callback function to store the parsed data.

The same data format will be used to store both keys and signatures, albeit
with different fields.

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 crypto/asymmetric_keys/Kconfig        |  12 ++
 crypto/asymmetric_keys/Makefile       |   7 +
 crypto/asymmetric_keys/uasym_parser.c | 201 ++++++++++++++++++++++++++
 crypto/asymmetric_keys/uasym_parser.h |  30 ++++
 include/uapi/linux/uasym_parser.h     |  91 ++++++++++++
 5 files changed, 341 insertions(+)
 create mode 100644 crypto/asymmetric_keys/uasym_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..4f86fe78efd 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -85,4 +85,16 @@  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
+	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..ac3955d834f 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -76,3 +76,10 @@  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_parser.o
diff --git a/crypto/asymmetric_keys/uasym_parser.c b/crypto/asymmetric_keys/uasym_parser.c
new file mode 100644
index 00000000000..e207f350c40
--- /dev/null
+++ b/crypto/asymmetric_keys/uasym_parser.c
@@ -0,0 +1,201 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * Implement the user asymmetric keys and signature parser.
+ */
+
+#define pr_fmt(fmt) "UASYM PARSER: "fmt
+
+#include "uasym_parser.h"
+
+const char *data_types_str[] = {
+	FOR_EACH_DATA_TYPE(GENERATE_STRING)
+};
+
+const char *fields_str[] = {
+	FOR_EACH_FIELD(GENERATE_STRING)
+};
+
+/**
+ * uasym_parse_hdr - Parse a user asymmetric key or signature header
+ * @data: Data to parse (updated)
+ * @data_len: Length of @data (updated)
+ * @data_type: Data type (updated)
+ * @num_fields: Data fields (updated)
+ * @total_len: Length of key or signature, excluding the header (updated)
+ *
+ * Parse the header of a user asymmetric key or signature, update the data
+ * pointer and length, and provide the data type, number of fields and the
+ * length of that element.
+ *
+ * Return: Zero on success, a negative value on error.
+ */
+int uasym_parse_hdr(const u8 **data, size_t *data_len, u8 *data_type,
+		    u16 *num_fields, u64 *total_len)
+{
+	struct uasym_hdr *hdr;
+
+	if (*data_len < sizeof(*hdr)) {
+		pr_debug("Data blob too short, %lu bytes, expected %lu\n",
+			 *data_len, sizeof(*hdr));
+		return -EBADMSG;
+	}
+
+	hdr = (struct uasym_hdr *)*data;
+
+	*data += sizeof(*hdr);
+	*data_len -= sizeof(*hdr);
+
+	*data_type = hdr->data_type;
+	if (*data_type >= TYPE__LAST) {
+		pr_debug("Invalid data type %u\n", *data_type);
+		return -EBADMSG;
+	}
+
+	if (hdr->_reserved0 != 0) {
+		pr_debug("_reserved0 must be zero\n");
+		return -EBADMSG;
+	}
+
+	*num_fields = be16_to_cpu(hdr->num_fields);
+	if (*num_fields >= FIELD__LAST) {
+		pr_debug("Too many fields %u, max: %u\n", *num_fields,
+			 FIELD__LAST);
+		return -EBADMSG;
+	}
+
+	if (hdr->_reserved1 != 0) {
+		pr_debug("_reserved1 must be zero\n");
+		return -EBADMSG;
+	}
+
+	*total_len = be64_to_cpu(hdr->total_len);
+	if (*total_len > *data_len) {
+		pr_debug("Invalid total length %llu, expected: %lu\n",
+			 *total_len, *data_len);
+		return -EBADMSG;
+	}
+
+	pr_debug("Header: type: %s, num fields: %d, total len: %lld\n",
+		 data_types_str[hdr->data_type], *num_fields, *total_len);
+
+	return 0;
+}
+
+/**
+ * uasym_parse_data - Parse a user asymmetric key or signature data
+ * @callback: Callback function to call to parse the fields
+ * @callback_data: Opaque data to supply to the callback function
+ * @num_fields: Data fields
+ * @data: Data to parse
+ * @data_len: Length of @data
+ *
+ * Parse the data part of a user asymmetric key or signature and call the
+ * supplied callback function for each data field, passing also the opaque
+ * data pointer.
+ *
+ * Return: Zero on success, a negative value on error.
+ */
+int uasym_parse_data(parse_callback callback, void *callback_data,
+		     u16 num_fields, const u8 *data, size_t data_len)
+{
+	const u8 *data_ptr = data;
+	struct uasym_entry *entry;
+	u16 field;
+	u32 len;
+	int ret, i;
+
+	for (i = 0; i < num_fields; i++) {
+		if (data_len < sizeof(*entry))
+			return -EBADMSG;
+
+		entry = (struct uasym_entry *)data_ptr;
+		data_ptr += sizeof(*entry);
+		data_len -= sizeof(*entry);
+
+		field = be16_to_cpu(entry->field);
+		len = be32_to_cpu(entry->length);
+
+		if (data_len < len)
+			return -EBADMSG;
+
+		pr_debug("Data: field: %s, len: %d\n", fields_str[field], len);
+
+		if (!len)
+			continue;
+
+		ret = callback(callback_data, field, data_ptr, len);
+		if (ret < 0) {
+			pr_debug("Parsing of field %s failed, ret: %d\n",
+				 fields_str[field], ret);
+			return -EBADMSG;
+		}
+
+		data_ptr += len;
+		data_len -= len;
+	}
+
+	if (data_len) {
+		pr_debug("Excess data: %ld bytes\n", data_len);
+		return -EBADMSG;
+	}
+
+	return 0;
+}
+
+/**
+ * uasym_parse - Parse a user asymmetric key or signature
+ * @expected_data_type: Desired data type
+ * @callback: Callback function to call to parse the fields
+ * @callback_data: Opaque data to supply to the callback function
+ * @data: Data to parse
+ * @data_len: Length of @data
+ *
+ * Parse a user asymmetric key or signature and call the supplied callback
+ * function for each data field, passing also the opaque data pointer.
+ *
+ * Return: Zero on success, a negative value on error.
+ */
+int uasym_parse(enum data_types expected_data_type, parse_callback callback,
+		void *callback_data, const u8 *data, size_t data_len)
+{
+	u8 data_type;
+	u16 num_fields;
+	u64 total_len;
+	int ret = 0;
+
+	pr_debug("Start parsing data blob, size: %ld, expected data type: %s\n",
+		 data_len, data_types_str[expected_data_type]);
+
+	while (data_len) {
+		ret = uasym_parse_hdr(&data, &data_len, &data_type, &num_fields,
+				      &total_len);
+		if (ret < 0)
+			goto out;
+
+		if (data_type == expected_data_type)
+			break;
+
+		/*
+		 * uasym_parse_hdr() already checked that total_len <= data_len.
+		 */
+		data += total_len;
+		data_len -= total_len;
+	}
+
+	if (!data_len) {
+		pr_debug("Data type %s not found\n",
+			 data_types_str[expected_data_type]);
+		ret = -ENOENT;
+		goto out;
+	}
+
+	ret = uasym_parse_data(callback, callback_data, num_fields, data,
+			       total_len);
+out:
+	pr_debug("End of parsing data blob, ret: %d\n", ret);
+	return ret;
+}
diff --git a/crypto/asymmetric_keys/uasym_parser.h b/crypto/asymmetric_keys/uasym_parser.h
new file mode 100644
index 00000000000..985dda6aad3
--- /dev/null
+++ b/crypto/asymmetric_keys/uasym_parser.h
@@ -0,0 +1,30 @@ 
+/* 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__)
+
+typedef int (*parse_callback)(void *, enum fields, const u8 *, u32);
+
+extern const char *data_types_str[];
+extern const char *fields_str[];
+
+int uasym_parse_hdr(const u8 **data, size_t *data_len, u8 *data_type,
+		    u16 *num_fields, u64 *total_len);
+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);
diff --git a/include/uapi/linux/uasym_parser.h b/include/uapi/linux/uasym_parser.h
new file mode 100644
index 00000000000..8f0bc235492
--- /dev/null
+++ b/include/uapi/linux/uasym_parser.h
@@ -0,0 +1,91 @@ 
+/* 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>
+
+/*
+ * User asymmmetric key and signature format:
+ *
+ * +----------------+-----------------+-----------------+
+ * | data type (u8) | num fields (u16)| total len (u64) |
+ * +--------------+-+----------+------+-----------+-----+
+ * | field1 (u16) | len1 (u32) | value1 (u8 len1) |
+ * +--------------+------------+------------------+
+ * |     ...      |    ...     |        ...       |
+ * +--------------+------------+------------------+
+ * | fieldN (u16) | lenN (u32) | valueN (u8 lenN) |
+ * +--------------+------------+------------------+
+ */
+
+/**
+ * struct uasym_hdr - Header of user asymmetric keys and signatures
+ * @data_type: Type of data to parse
+ * @_reserved0: Reserved for future use
+ * @num_fields: Number of fields provided
+ * @_reserved1: Reserved for future use
+ * @total_len: Total length of the data blob, excluding the header
+ *
+ * This structure represents the header of the user asymmetric keys and
+ * signatures format.
+ */
+struct uasym_hdr {
+	__u8 data_type;
+	__u8 _reserved0;
+	__u16 num_fields;
+	__u32 _reserved1;
+	__u64 total_len;
+} __packed;
+
+/**
+ * struct uasym_entry - Data entry of user asymmetric keys and signatures
+ * @field: Data field identifier
+ * @length: Data length
+ * @data: Data
+ *
+ * This structure represents a TLV entry of the data part of the user
+ * asymmetric keys and signatures format.
+ */
+struct uasym_entry {
+	__u16 field;
+	__u32 length;
+	__u8 data[];
+} __packed;
+
+#define FOR_EACH_DATA_TYPE(DATA_TYPE) \
+	DATA_TYPE(TYPE__LAST)
+
+#define FOR_EACH_FIELD(FIELD) \
+	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 */