@@ -85,4 +85,15 @@ config FIPS_SIGNATURE_SELFTEST
depends on ASYMMETRIC_KEY_TYPE
depends on PKCS7_MESSAGE_PARSER=X509_CERTIFICATE_PARSER
+config UMD_KEY_PARSER
+ bool "UMD-based parser for the asymmetric key type"
+ depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+ select USERMODE_DRIVER
+ help
+ This option introduces a new parser for the asymmetric key type.
+ The parser forwards the request to a User Mode Driver (UMD) handler,
+ which replies with the result.
+
+ On success, the parser fills the key from the UMD handler response.
+
endif # ASYMMETRIC_KEY_TYPE
@@ -76,3 +76,8 @@ 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
+
+#
+# UMD asymmetric key parser
+#
+obj-$(CONFIG_UMD_KEY_PARSER) += umd_key_parser.o
@@ -431,7 +431,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;
@@ -441,6 +441,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,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * Header file of the UMD asymmetric key parser.
+ */
+
+#include <linux/usermode_driver_mgmt.h>
+#include <keys/asymmetric-subtype.h>
+#include <keys/asymmetric-parser.h>
+
+#include "umd_key_sig_umh.h"
+
+extern struct umd_mgmt key_ops;
+extern const char *pub_key_algos[PUBKEY_ALGO__LAST];
+
+int umd_get_kids(struct umd_asymmetric_key_ids *umd_kids,
+ struct asymmetric_key_id *id[3]);
new file mode 100644
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * Implement the UMD asymmetric key parser.
+ */
+
+#include <linux/module.h>
+#include <crypto/public_key.h>
+
+#include "umd_key.h"
+
+const char *pub_key_algos[PUBKEY_ALGO__LAST] = {
+ [PUBKEY_ALGO_RSA] = "rsa",
+ [PUBKEY_ALGO_ECDSA] = "ecdsa",
+ [PUBKEY_ALGO_ECDSA_NIST_P192] = "ecdsa-nist-p192",
+ [PUBKEY_ALGO_ECDSA_NIST_P256] = "ecdsa-nist-p256",
+ [PUBKEY_ALGO_ECDSA_NIST_P384] = "ecdsa-nist-p384",
+};
+
+struct umd_mgmt key_ops = {
+ .info.driver_name = "umd_key_sig_umh",
+ .kmod = "umd_key_sig_user",
+ .lock = __MUTEX_INITIALIZER(key_ops.lock),
+};
+EXPORT_SYMBOL_GPL(key_ops);
+
+static struct public_key *get_public_key(struct msg_out *out)
+{
+ struct public_key *pub;
+
+ if (out->key.pkey_algo >= PUBKEY_ALGO__LAST) {
+ pr_err("Unexpected key algo %d\n", out->key.pkey_algo);
+ return ERR_PTR(-EBADMSG);
+ }
+
+ if (!out->key.pub_key_len) {
+ pr_err("Unexpected zero-length for public key\n");
+ return ERR_PTR(-EBADMSG);
+ }
+
+ if (out->key.pub_key_len > sizeof(out->key.pub_key)) {
+ pr_err("Public key length %ld greater than expected %ld\n",
+ out->key.pub_key_len, sizeof(out->key.pub_key));
+ return ERR_PTR(-EBADMSG);
+ }
+
+ pub = kzalloc(sizeof(*pub), GFP_KERNEL);
+ if (!pub)
+ return ERR_PTR(-ENOMEM);
+
+ pub->id_type = "UMD";
+ pub->pkey_algo = pub_key_algos[out->key.pkey_algo];
+
+ pub->key = kmemdup(out->key.pub_key, out->key.pub_key_len, GFP_KERNEL);
+ if (!pub->key) {
+ kfree(pub);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ pub->keylen = out->key.pub_key_len;
+ return pub;
+}
+
+int umd_get_kids(struct umd_asymmetric_key_ids *umd_kids,
+ struct asymmetric_key_id **id)
+{
+ int ret = 0, i, j;
+
+ for (i = 0; i < ARRAY_SIZE(umd_kids->kid1_len); i++) {
+ if (!umd_kids->kid1_len[i] && !umd_kids->kid2_len[i])
+ continue;
+
+ if (umd_kids->kid1_len[i] > sizeof(umd_kids->kid1[0])) {
+ pr_err("Key ID 1 length %ld greater than expected %ld\n",
+ umd_kids->kid1_len[i],
+ sizeof(umd_kids->kid1[0]));
+ ret = -EBADMSG;
+ break;
+ }
+
+ if (umd_kids->kid2_len[i] > sizeof(umd_kids->kid2[0])) {
+ pr_err("Key ID 2 length %ld greater than expected %ld\n",
+ umd_kids->kid2_len[i],
+ sizeof(umd_kids->kid2[0]));
+ ret = -EBADMSG;
+ break;
+ }
+
+ id[i] = asymmetric_key_generate_id(umd_kids->kid1[i],
+ umd_kids->kid1_len[i],
+ umd_kids->kid2[i],
+ umd_kids->kid2_len[i]);
+ if (!id[i]) {
+ ret = -ENOMEM;
+ break;
+ }
+ }
+
+ if (ret) {
+ for (j = 0; j < i; j++)
+ kfree(id[j]);
+ }
+
+ return ret;
+}
+
+static int umd_key_parse(struct key_preparsed_payload *prep)
+{
+ struct msg_in *in = NULL;
+ struct msg_out *out = NULL;
+ struct asymmetric_key_ids *kids = NULL;
+ struct public_key *pub = NULL;
+ int ret = -ENOMEM;
+
+ in = kzalloc(sizeof(*in), GFP_KERNEL);
+ if (!in)
+ goto out;
+
+ out = kzalloc(sizeof(*out), GFP_KERNEL);
+ if (!out)
+ goto out;
+
+ in->cmd = CMD_KEY;
+ in->data_len = prep->datalen;
+ /* Truncate the input, there might be multiple keys in the same blob. */
+ memcpy(in->data, prep->data, min(prep->datalen, sizeof(in->data)));
+
+ out->ret = -EINVAL;
+
+ ret = umd_mgmt_send_recv(&key_ops, in, sizeof(*in), out, sizeof(*out));
+ if (ret)
+ goto out;
+
+ if (out->ret) {
+ ret = out->ret;
+ goto out;
+ }
+
+ pub = get_public_key(out);
+ if (IS_ERR(pub)) {
+ ret = PTR_ERR(pub);
+ pub = NULL;
+ goto out;
+ }
+
+ kids = kzalloc(sizeof(*kids), GFP_KERNEL);
+ if (!kids) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = umd_get_kids(&out->key.kids,
+ (struct asymmetric_key_id **)kids->id);
+ if (ret)
+ goto out;
+
+ if (strlen(out->key.key_desc)) {
+ prep->description = kstrdup(out->key.key_desc, GFP_KERNEL);
+ if (!prep->description)
+ ret = -ENOMEM;
+ }
+
+out:
+ kfree(in);
+ kfree(out);
+
+ if (ret) {
+ public_key_free(pub);
+ asymmetric_key_free_kids(kids);
+ return ret;
+ }
+
+ /* 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] = kids;
+ prep->payload.data[asym_crypto] = pub;
+ prep->quotalen = 100;
+ return 0;
+}
+
+static struct asymmetric_key_parser umd_key_parser = {
+ .owner = THIS_MODULE,
+ .name = "umd_key",
+ .parse = umd_key_parse
+};
+
+static int __init umd_key_init(void)
+{
+ return register_asymmetric_key_parser(&umd_key_parser);
+}
+
+static void __exit umd_key_exit(void)
+{
+ unregister_asymmetric_key_parser(&umd_key_parser);
+}
+
+module_init(umd_key_init);
+module_exit(umd_key_exit);
+MODULE_LICENSE("GPL");
new file mode 100644
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * Header file for communication between the kernel and the UMD handler.
+ */
+
+#define MAX_KEY_SIZE 1024
+#define MAX_KEY_DESC_SIZE 256
+#define MAX_PAYLOAD_SIZE 8192
+#define MAX_KID_SIZE 256
+
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
+enum cmds { CMD_KEY, CMD__LAST };
+
+/* Public key algorithms that the kernel supports. */
+enum pub_key_algos { PUBKEY_ALGO_RSA, PUBKEY_ALGO_ECDSA,
+ PUBKEY_ALGO_ECDSA_NIST_P192, PUBKEY_ALGO_ECDSA_NIST_P256,
+ PUBKEY_ALGO_ECDSA_NIST_P384, PUBKEY_ALGO__LAST };
+
+struct msg_in {
+ enum cmds cmd;
+ size_t data_len;
+ unsigned char data[MAX_PAYLOAD_SIZE];
+} __packed;
+
+struct umd_asymmetric_key_ids {
+ size_t kid1_len[3];
+ unsigned char kid1[3][MAX_KID_SIZE];
+ size_t kid2_len[3];
+ unsigned char kid2[3][MAX_KID_SIZE];
+} __packed;
+
+struct umd_key_msg_out {
+ size_t pub_key_len;
+ unsigned char pub_key[MAX_KEY_SIZE];
+ enum pub_key_algos pkey_algo;
+ struct umd_asymmetric_key_ids kids;
+ char key_desc[MAX_KEY_DESC_SIZE];
+} __packed;
+
+struct msg_out {
+ int ret;
+ union {
+ struct umd_key_msg_out key;
+ };
+} __packed;
@@ -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)
{