162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* PKCS#8 Private Key parser [RFC 5208].
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define pr_fmt(fmt) "PKCS8: "fmt
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/export.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/err.h>
1462306a36Sopenharmony_ci#include <linux/oid_registry.h>
1562306a36Sopenharmony_ci#include <keys/asymmetric-subtype.h>
1662306a36Sopenharmony_ci#include <keys/asymmetric-parser.h>
1762306a36Sopenharmony_ci#include <crypto/public_key.h>
1862306a36Sopenharmony_ci#include "pkcs8.asn1.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistruct pkcs8_parse_context {
2162306a36Sopenharmony_ci	struct public_key *pub;
2262306a36Sopenharmony_ci	unsigned long	data;			/* Start of data */
2362306a36Sopenharmony_ci	enum OID	last_oid;		/* Last OID encountered */
2462306a36Sopenharmony_ci	enum OID	algo_oid;		/* Algorithm OID */
2562306a36Sopenharmony_ci	u32		key_size;
2662306a36Sopenharmony_ci	const void	*key;
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/*
3062306a36Sopenharmony_ci * Note an OID when we find one for later processing when we know how to
3162306a36Sopenharmony_ci * interpret it.
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ciint pkcs8_note_OID(void *context, size_t hdrlen,
3462306a36Sopenharmony_ci		   unsigned char tag,
3562306a36Sopenharmony_ci		   const void *value, size_t vlen)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	struct pkcs8_parse_context *ctx = context;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	ctx->last_oid = look_up_OID(value, vlen);
4062306a36Sopenharmony_ci	if (ctx->last_oid == OID__NR) {
4162306a36Sopenharmony_ci		char buffer[50];
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci		sprint_oid(value, vlen, buffer, sizeof(buffer));
4462306a36Sopenharmony_ci		pr_info("Unknown OID: [%lu] %s\n",
4562306a36Sopenharmony_ci			(unsigned long)value - ctx->data, buffer);
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci	return 0;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/*
5162306a36Sopenharmony_ci * Note the version number of the ASN.1 blob.
5262306a36Sopenharmony_ci */
5362306a36Sopenharmony_ciint pkcs8_note_version(void *context, size_t hdrlen,
5462306a36Sopenharmony_ci		       unsigned char tag,
5562306a36Sopenharmony_ci		       const void *value, size_t vlen)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	if (vlen != 1 || ((const u8 *)value)[0] != 0) {
5862306a36Sopenharmony_ci		pr_warn("Unsupported PKCS#8 version\n");
5962306a36Sopenharmony_ci		return -EBADMSG;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci	return 0;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/*
6562306a36Sopenharmony_ci * Note the public algorithm.
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_ciint pkcs8_note_algo(void *context, size_t hdrlen,
6862306a36Sopenharmony_ci		    unsigned char tag,
6962306a36Sopenharmony_ci		    const void *value, size_t vlen)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	struct pkcs8_parse_context *ctx = context;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (ctx->last_oid != OID_rsaEncryption)
7462306a36Sopenharmony_ci		return -ENOPKG;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	ctx->pub->pkey_algo = "rsa";
7762306a36Sopenharmony_ci	return 0;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/*
8162306a36Sopenharmony_ci * Note the key data of the ASN.1 blob.
8262306a36Sopenharmony_ci */
8362306a36Sopenharmony_ciint pkcs8_note_key(void *context, size_t hdrlen,
8462306a36Sopenharmony_ci		   unsigned char tag,
8562306a36Sopenharmony_ci		   const void *value, size_t vlen)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct pkcs8_parse_context *ctx = context;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	ctx->key = value;
9062306a36Sopenharmony_ci	ctx->key_size = vlen;
9162306a36Sopenharmony_ci	return 0;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/*
9562306a36Sopenharmony_ci * Parse a PKCS#8 private key blob.
9662306a36Sopenharmony_ci */
9762306a36Sopenharmony_cistatic struct public_key *pkcs8_parse(const void *data, size_t datalen)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	struct pkcs8_parse_context ctx;
10062306a36Sopenharmony_ci	struct public_key *pub;
10162306a36Sopenharmony_ci	long ret;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	memset(&ctx, 0, sizeof(ctx));
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	ret = -ENOMEM;
10662306a36Sopenharmony_ci	ctx.pub = kzalloc(sizeof(struct public_key), GFP_KERNEL);
10762306a36Sopenharmony_ci	if (!ctx.pub)
10862306a36Sopenharmony_ci		goto error;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	ctx.data = (unsigned long)data;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	/* Attempt to decode the private key */
11362306a36Sopenharmony_ci	ret = asn1_ber_decoder(&pkcs8_decoder, &ctx, data, datalen);
11462306a36Sopenharmony_ci	if (ret < 0)
11562306a36Sopenharmony_ci		goto error_decode;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	ret = -ENOMEM;
11862306a36Sopenharmony_ci	pub = ctx.pub;
11962306a36Sopenharmony_ci	pub->key = kmemdup(ctx.key, ctx.key_size, GFP_KERNEL);
12062306a36Sopenharmony_ci	if (!pub->key)
12162306a36Sopenharmony_ci		goto error_decode;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	pub->keylen = ctx.key_size;
12462306a36Sopenharmony_ci	pub->key_is_private = true;
12562306a36Sopenharmony_ci	return pub;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cierror_decode:
12862306a36Sopenharmony_ci	kfree(ctx.pub);
12962306a36Sopenharmony_cierror:
13062306a36Sopenharmony_ci	return ERR_PTR(ret);
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci/*
13462306a36Sopenharmony_ci * Attempt to parse a data blob for a key as a PKCS#8 private key.
13562306a36Sopenharmony_ci */
13662306a36Sopenharmony_cistatic int pkcs8_key_preparse(struct key_preparsed_payload *prep)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	struct public_key *pub;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	pub = pkcs8_parse(prep->data, prep->datalen);
14162306a36Sopenharmony_ci	if (IS_ERR(pub))
14262306a36Sopenharmony_ci		return PTR_ERR(pub);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	pr_devel("Cert Key Algo: %s\n", pub->pkey_algo);
14562306a36Sopenharmony_ci	pub->id_type = "PKCS8";
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	/* We're pinning the module by being linked against it */
14862306a36Sopenharmony_ci	__module_get(public_key_subtype.owner);
14962306a36Sopenharmony_ci	prep->payload.data[asym_subtype] = &public_key_subtype;
15062306a36Sopenharmony_ci	prep->payload.data[asym_key_ids] = NULL;
15162306a36Sopenharmony_ci	prep->payload.data[asym_crypto] = pub;
15262306a36Sopenharmony_ci	prep->payload.data[asym_auth] = NULL;
15362306a36Sopenharmony_ci	prep->quotalen = 100;
15462306a36Sopenharmony_ci	return 0;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic struct asymmetric_key_parser pkcs8_key_parser = {
15862306a36Sopenharmony_ci	.owner	= THIS_MODULE,
15962306a36Sopenharmony_ci	.name	= "pkcs8",
16062306a36Sopenharmony_ci	.parse	= pkcs8_key_preparse,
16162306a36Sopenharmony_ci};
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci/*
16462306a36Sopenharmony_ci * Module stuff
16562306a36Sopenharmony_ci */
16662306a36Sopenharmony_cistatic int __init pkcs8_key_init(void)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	return register_asymmetric_key_parser(&pkcs8_key_parser);
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic void __exit pkcs8_key_exit(void)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	unregister_asymmetric_key_parser(&pkcs8_key_parser);
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cimodule_init(pkcs8_key_init);
17762306a36Sopenharmony_cimodule_exit(pkcs8_key_exit);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ciMODULE_DESCRIPTION("PKCS#8 certificate parser");
18062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
181