18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* PKCS#8 Private Key parser [RFC 5208].
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "PKCS8: "fmt
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/export.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci#include <linux/err.h>
148c2ecf20Sopenharmony_ci#include <linux/oid_registry.h>
158c2ecf20Sopenharmony_ci#include <keys/asymmetric-subtype.h>
168c2ecf20Sopenharmony_ci#include <keys/asymmetric-parser.h>
178c2ecf20Sopenharmony_ci#include <crypto/public_key.h>
188c2ecf20Sopenharmony_ci#include "pkcs8.asn1.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct pkcs8_parse_context {
218c2ecf20Sopenharmony_ci	struct public_key *pub;
228c2ecf20Sopenharmony_ci	unsigned long	data;			/* Start of data */
238c2ecf20Sopenharmony_ci	enum OID	last_oid;		/* Last OID encountered */
248c2ecf20Sopenharmony_ci	enum OID	algo_oid;		/* Algorithm OID */
258c2ecf20Sopenharmony_ci	u32		key_size;
268c2ecf20Sopenharmony_ci	const void	*key;
278c2ecf20Sopenharmony_ci};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/*
308c2ecf20Sopenharmony_ci * Note an OID when we find one for later processing when we know how to
318c2ecf20Sopenharmony_ci * interpret it.
328c2ecf20Sopenharmony_ci */
338c2ecf20Sopenharmony_ciint pkcs8_note_OID(void *context, size_t hdrlen,
348c2ecf20Sopenharmony_ci		   unsigned char tag,
358c2ecf20Sopenharmony_ci		   const void *value, size_t vlen)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	struct pkcs8_parse_context *ctx = context;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	ctx->last_oid = look_up_OID(value, vlen);
408c2ecf20Sopenharmony_ci	if (ctx->last_oid == OID__NR) {
418c2ecf20Sopenharmony_ci		char buffer[50];
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci		sprint_oid(value, vlen, buffer, sizeof(buffer));
448c2ecf20Sopenharmony_ci		pr_info("Unknown OID: [%lu] %s\n",
458c2ecf20Sopenharmony_ci			(unsigned long)value - ctx->data, buffer);
468c2ecf20Sopenharmony_ci	}
478c2ecf20Sopenharmony_ci	return 0;
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/*
518c2ecf20Sopenharmony_ci * Note the version number of the ASN.1 blob.
528c2ecf20Sopenharmony_ci */
538c2ecf20Sopenharmony_ciint pkcs8_note_version(void *context, size_t hdrlen,
548c2ecf20Sopenharmony_ci		       unsigned char tag,
558c2ecf20Sopenharmony_ci		       const void *value, size_t vlen)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	if (vlen != 1 || ((const u8 *)value)[0] != 0) {
588c2ecf20Sopenharmony_ci		pr_warn("Unsupported PKCS#8 version\n");
598c2ecf20Sopenharmony_ci		return -EBADMSG;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci	return 0;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/*
658c2ecf20Sopenharmony_ci * Note the public algorithm.
668c2ecf20Sopenharmony_ci */
678c2ecf20Sopenharmony_ciint pkcs8_note_algo(void *context, size_t hdrlen,
688c2ecf20Sopenharmony_ci		    unsigned char tag,
698c2ecf20Sopenharmony_ci		    const void *value, size_t vlen)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	struct pkcs8_parse_context *ctx = context;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if (ctx->last_oid != OID_rsaEncryption)
748c2ecf20Sopenharmony_ci		return -ENOPKG;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	ctx->pub->pkey_algo = "rsa";
778c2ecf20Sopenharmony_ci	return 0;
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/*
818c2ecf20Sopenharmony_ci * Note the key data of the ASN.1 blob.
828c2ecf20Sopenharmony_ci */
838c2ecf20Sopenharmony_ciint pkcs8_note_key(void *context, size_t hdrlen,
848c2ecf20Sopenharmony_ci		   unsigned char tag,
858c2ecf20Sopenharmony_ci		   const void *value, size_t vlen)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct pkcs8_parse_context *ctx = context;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	ctx->key = value;
908c2ecf20Sopenharmony_ci	ctx->key_size = vlen;
918c2ecf20Sopenharmony_ci	return 0;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci/*
958c2ecf20Sopenharmony_ci * Parse a PKCS#8 private key blob.
968c2ecf20Sopenharmony_ci */
978c2ecf20Sopenharmony_cistatic struct public_key *pkcs8_parse(const void *data, size_t datalen)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct pkcs8_parse_context ctx;
1008c2ecf20Sopenharmony_ci	struct public_key *pub;
1018c2ecf20Sopenharmony_ci	long ret;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	memset(&ctx, 0, sizeof(ctx));
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	ret = -ENOMEM;
1068c2ecf20Sopenharmony_ci	ctx.pub = kzalloc(sizeof(struct public_key), GFP_KERNEL);
1078c2ecf20Sopenharmony_ci	if (!ctx.pub)
1088c2ecf20Sopenharmony_ci		goto error;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	ctx.data = (unsigned long)data;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	/* Attempt to decode the private key */
1138c2ecf20Sopenharmony_ci	ret = asn1_ber_decoder(&pkcs8_decoder, &ctx, data, datalen);
1148c2ecf20Sopenharmony_ci	if (ret < 0)
1158c2ecf20Sopenharmony_ci		goto error_decode;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	ret = -ENOMEM;
1188c2ecf20Sopenharmony_ci	pub = ctx.pub;
1198c2ecf20Sopenharmony_ci	pub->key = kmemdup(ctx.key, ctx.key_size, GFP_KERNEL);
1208c2ecf20Sopenharmony_ci	if (!pub->key)
1218c2ecf20Sopenharmony_ci		goto error_decode;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	pub->keylen = ctx.key_size;
1248c2ecf20Sopenharmony_ci	pub->key_is_private = true;
1258c2ecf20Sopenharmony_ci	return pub;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cierror_decode:
1288c2ecf20Sopenharmony_ci	kfree(ctx.pub);
1298c2ecf20Sopenharmony_cierror:
1308c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci/*
1348c2ecf20Sopenharmony_ci * Attempt to parse a data blob for a key as a PKCS#8 private key.
1358c2ecf20Sopenharmony_ci */
1368c2ecf20Sopenharmony_cistatic int pkcs8_key_preparse(struct key_preparsed_payload *prep)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	struct public_key *pub;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	pub = pkcs8_parse(prep->data, prep->datalen);
1418c2ecf20Sopenharmony_ci	if (IS_ERR(pub))
1428c2ecf20Sopenharmony_ci		return PTR_ERR(pub);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	pr_devel("Cert Key Algo: %s\n", pub->pkey_algo);
1458c2ecf20Sopenharmony_ci	pub->id_type = "PKCS8";
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	/* We're pinning the module by being linked against it */
1488c2ecf20Sopenharmony_ci	__module_get(public_key_subtype.owner);
1498c2ecf20Sopenharmony_ci	prep->payload.data[asym_subtype] = &public_key_subtype;
1508c2ecf20Sopenharmony_ci	prep->payload.data[asym_key_ids] = NULL;
1518c2ecf20Sopenharmony_ci	prep->payload.data[asym_crypto] = pub;
1528c2ecf20Sopenharmony_ci	prep->payload.data[asym_auth] = NULL;
1538c2ecf20Sopenharmony_ci	prep->quotalen = 100;
1548c2ecf20Sopenharmony_ci	return 0;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic struct asymmetric_key_parser pkcs8_key_parser = {
1588c2ecf20Sopenharmony_ci	.owner	= THIS_MODULE,
1598c2ecf20Sopenharmony_ci	.name	= "pkcs8",
1608c2ecf20Sopenharmony_ci	.parse	= pkcs8_key_preparse,
1618c2ecf20Sopenharmony_ci};
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci/*
1648c2ecf20Sopenharmony_ci * Module stuff
1658c2ecf20Sopenharmony_ci */
1668c2ecf20Sopenharmony_cistatic int __init pkcs8_key_init(void)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	return register_asymmetric_key_parser(&pkcs8_key_parser);
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic void __exit pkcs8_key_exit(void)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	unregister_asymmetric_key_parser(&pkcs8_key_parser);
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cimodule_init(pkcs8_key_init);
1778c2ecf20Sopenharmony_cimodule_exit(pkcs8_key_exit);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PKCS#8 certificate parser");
1808c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
181