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