summaryrefslogtreecommitdiff
path: root/crypto/asymmetric_keys/pkcs8_parser.c
blob: 5f6a7ecc9765e91934b2ede0e056088109525a0f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/* PKCS#8 Private Key parser [RFC 5208].
 *
 * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public Licence
 * as published by the Free Software Foundation; either version
 * 2 of the Licence, or (at your option) any later version.
 */

#define pr_fmt(fmt) "PKCS8: "fmt
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/oid_registry.h>
#include <keys/asymmetric-subtype.h>
#include <keys/asymmetric-parser.h>
#include <crypto/public_key.h>
#include "pkcs8.asn1.h"

struct pkcs8_parse_context {
	struct public_key *pub;
	unsigned long	data;			/* Start of data */
	enum OID	last_oid;		/* Last OID encountered */
	enum OID	algo_oid;		/* Algorithm OID */
	u32		key_size;
	const void	*key;
};

/*
 * Note an OID when we find one for later processing when we know how to
 * interpret it.
 */
int pkcs8_note_OID(void *context, size_t hdrlen,
		   unsigned char tag,
		   const void *value, size_t vlen)
{
	struct pkcs8_parse_context *ctx = context;

	ctx->last_oid = look_up_OID(value, vlen);
	if (ctx->last_oid == OID__NR) {
		char buffer[50];

		sprint_oid(value, vlen, buffer, sizeof(buffer));
		pr_info("Unknown OID: [%lu] %s\n",
			(unsigned long)value - ctx->data, buffer);
	}
	return 0;
}

/*
 * Note the version number of the ASN.1 blob.
 */
int pkcs8_note_version(void *context, size_t hdrlen,
		       unsigned char tag,
		       const void *value, size_t vlen)
{
	if (vlen != 1 || ((const u8 *)value)[0] != 0) {
		pr_warn("Unsupported PKCS#8 version\n");
		return -EBADMSG;
	}
	return 0;
}

/*
 * Note the public algorithm.
 */
int pkcs8_note_algo(void *context, size_t hdrlen,
		    unsigned char tag,
		    const void *value, size_t vlen)
{
	struct pkcs8_parse_context *ctx = context;

	if (ctx->last_oid != OID_rsaEncryption)
		return -ENOPKG;

	ctx->pub->pkey_algo = "rsa";
	return 0;
}

/*
 * Note the key data of the ASN.1 blob.
 */
int pkcs8_note_key(void *context, size_t hdrlen,
		   unsigned char tag,
		   const void *value, size_t vlen)
{
	struct pkcs8_parse_context *ctx = context;

	ctx->key = value;
	ctx->key_size = vlen;
	return 0;
}

/*
 * Parse a PKCS#8 private key blob.
 */
static struct public_key *pkcs8_parse(const void *data, size_t datalen)
{
	struct pkcs8_parse_context ctx;
	struct public_key *pub;
	long ret;

	memset(&ctx, 0, sizeof(ctx));

	ret = -ENOMEM;
	ctx.pub = kzalloc(sizeof(struct public_key), GFP_KERNEL);
	if (!ctx.pub)
		goto error;

	ctx.data = (unsigned long)data;

	/* Attempt to decode the private key */
	ret = asn1_ber_decoder(&pkcs8_decoder, &ctx, data, datalen);
	if (ret < 0)
		goto error_decode;

	ret = -ENOMEM;
	pub = ctx.pub;
	pub->key = kmemdup(ctx.key, ctx.key_size, GFP_KERNEL);
	if (!pub->key)
		goto error_decode;

	pub->keylen = ctx.key_size;
	pub->key_is_private = true;
	return pub;

error_decode:
	kfree(ctx.pub);
error:
	return ERR_PTR(ret);
}

/*
 * Attempt to parse a data blob for a key as a PKCS#8 private key.
 */
static int pkcs8_key_preparse(struct key_preparsed_payload *prep)
{
	struct public_key *pub;

	pub = pkcs8_parse(prep->data, prep->datalen);
	if (IS_ERR(pub))
		return PTR_ERR(pub);

	pr_devel("Cert Key Algo: %s\n", pub->pkey_algo);
	pub->id_type = "PKCS8";

	/* 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] = NULL;
	prep->payload.data[asym_crypto] = pub;
	prep->payload.data[asym_auth] = NULL;
	prep->quotalen = 100;
	return 0;
}

static struct asymmetric_key_parser pkcs8_key_parser = {
	.owner	= THIS_MODULE,
	.name	= "pkcs8",
	.parse	= pkcs8_key_preparse,
};

/*
 * Module stuff
 */
static int __init pkcs8_key_init(void)
{
	return register_asymmetric_key_parser(&pkcs8_key_parser);
}

static void __exit pkcs8_key_exit(void)
{
	unregister_asymmetric_key_parser(&pkcs8_key_parser);
}

module_init(pkcs8_key_init);
module_exit(pkcs8_key_exit);

MODULE_DESCRIPTION("PKCS#8 certificate parser");
MODULE_LICENSE("GPL");