@@ -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
@@ -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
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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);
new file mode 100644
@@ -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 */