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