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

From: Roberto Sassu
Date: Thu Jul 06 2023 - 10:46:44 EST


From: Roberto Sassu <roberto.sassu@xxxxxxxxxx>

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@xxxxxxxxxx>
---
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 --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@xxxxxxxxxx>
+ *
+ * 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,
--
2.34.1