aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorDmitry Kasatkin <dmitry.kasatkin@intel.com>2011-10-14 08:25:16 -0400
committerDmitry Kasatkin <dmitry.kasatkin@intel.com>2011-11-09 05:10:37 -0500
commit051dbb918c7fb7da8e64a2cd0d804ba73399709f (patch)
tree34d547e74ef6edb7feeda4a8291b221cc016c393 /lib
parent7e8dec918ef8e0f68b4937c3c50fa57002077a4d (diff)
crypto: digital signature verification support
This patch implements RSA digital signature verification using GnuPG library. The format of the signature and the public key is defined by their respective headers. The signature header contains version information, algorithm, and keyid, which was used to generate the signature. The key header contains version and algorythim type. The payload of the signature and the key are multi-precision integers. The signing and key management utilities evm-utils provide functionality to generate signatures and load keys into the kernel keyring. When the key is added to the kernel keyring, the keyid defines the name of the key. Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com> Acked-by: Mimi Zohar <zohar@us.ibm.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/Kconfig8
-rw-r--r--lib/Makefile1
-rw-r--r--lib/digsig.c284
3 files changed, 293 insertions, 0 deletions
diff --git a/lib/Kconfig b/lib/Kconfig
index d411de53be4f..c1a89185fe6b 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -293,4 +293,12 @@ config MPILIB_EXTRA
293 This code in unnecessary for RSA digital signature verification, 293 This code in unnecessary for RSA digital signature verification,
294 and can be compiled if needed. 294 and can be compiled if needed.
295 295
296config DIGSIG
297 tristate "In-kernel signature checker"
298 depends on CRYPTO
299 select MPILIB
300 help
301 Digital signature verification. Currently only RSA is supported.
302 Implementation is done using GnuPG MPI library
303
296endmenu 304endmenu
diff --git a/lib/Makefile b/lib/Makefile
index 6ba8cbf4c72f..0f5cff267aff 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -116,6 +116,7 @@ obj-$(CONFIG_CPU_RMAP) += cpu_rmap.o
116obj-$(CONFIG_CORDIC) += cordic.o 116obj-$(CONFIG_CORDIC) += cordic.o
117 117
118obj-$(CONFIG_MPILIB) += mpi/ 118obj-$(CONFIG_MPILIB) += mpi/
119obj-$(CONFIG_DIGSIG) += digsig.o
119 120
120hostprogs-y := gen_crc32table 121hostprogs-y := gen_crc32table
121clean-files := crc32table.h 122clean-files := crc32table.h
diff --git a/lib/digsig.c b/lib/digsig.c
new file mode 100644
index 000000000000..fd2402f67f89
--- /dev/null
+++ b/lib/digsig.c
@@ -0,0 +1,284 @@
1/*
2 * Copyright (C) 2011 Nokia Corporation
3 * Copyright (C) 2011 Intel Corporation
4 *
5 * Author:
6 * Dmitry Kasatkin <dmitry.kasatkin@nokia.com>
7 * <dmitry.kasatkin@intel.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, version 2 of the License.
12 *
13 * File: sign.c
14 * implements signature (RSA) verification
15 * pkcs decoding is based on LibTomCrypt code
16 */
17
18#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19
20#include <linux/err.h>
21#include <linux/module.h>
22#include <linux/slab.h>
23#include <linux/key.h>
24#include <linux/crypto.h>
25#include <crypto/hash.h>
26#include <crypto/sha.h>
27#include <keys/user-type.h>
28#include <linux/mpi.h>
29#include <linux/digsig.h>
30
31static struct crypto_shash *shash;
32
33static int pkcs_1_v1_5_decode_emsa(const unsigned char *msg,
34 unsigned long msglen,
35 unsigned long modulus_bitlen,
36 unsigned char *out,
37 unsigned long *outlen,
38 int *is_valid)
39{
40 unsigned long modulus_len, ps_len, i;
41 int result;
42
43 /* default to invalid packet */
44 *is_valid = 0;
45
46 modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0);
47
48 /* test message size */
49 if ((msglen > modulus_len) || (modulus_len < 11))
50 return -EINVAL;
51
52 /* separate encoded message */
53 if ((msg[0] != 0x00) || (msg[1] != (unsigned char)1)) {
54 result = -EINVAL;
55 goto bail;
56 }
57
58 for (i = 2; i < modulus_len - 1; i++)
59 if (msg[i] != 0xFF)
60 break;
61
62 /* separator check */
63 if (msg[i] != 0) {
64 /* There was no octet with hexadecimal value 0x00
65 to separate ps from m. */
66 result = -EINVAL;
67 goto bail;
68 }
69
70 ps_len = i - 2;
71
72 if (*outlen < (msglen - (2 + ps_len + 1))) {
73 *outlen = msglen - (2 + ps_len + 1);
74 result = -EOVERFLOW;
75 goto bail;
76 }
77
78 *outlen = (msglen - (2 + ps_len + 1));
79 memcpy(out, &msg[2 + ps_len + 1], *outlen);
80
81 /* valid packet */
82 *is_valid = 1;
83 result = 0;
84bail:
85 return result;
86}
87
88/*
89 * RSA Signature verification with public key
90 */
91static int digsig_verify_rsa(struct key *key,
92 const char *sig, int siglen,
93 const char *h, int hlen)
94{
95 int err = -EINVAL;
96 unsigned long len;
97 unsigned long mlen, mblen;
98 unsigned nret, l;
99 int valid, head, i;
100 unsigned char *out1 = NULL, *out2 = NULL;
101 MPI in = NULL, res = NULL, pkey[2];
102 uint8_t *p, *datap, *endp;
103 struct user_key_payload *ukp;
104 struct pubkey_hdr *pkh;
105
106 down_read(&key->sem);
107 ukp = key->payload.data;
108 pkh = (struct pubkey_hdr *)ukp->data;
109
110 if (pkh->version != 1)
111 goto err1;
112
113 if (pkh->algo != PUBKEY_ALGO_RSA)
114 goto err1;
115
116 if (pkh->nmpi != 2)
117 goto err1;
118
119 datap = pkh->mpi;
120 endp = datap + ukp->datalen;
121
122 for (i = 0; i < pkh->nmpi; i++) {
123 unsigned int remaining = endp - datap;
124 pkey[i] = mpi_read_from_buffer(datap, &remaining);
125 datap += remaining;
126 }
127
128 mblen = mpi_get_nbits(pkey[0]);
129 mlen = (mblen + 7)/8;
130
131 err = -ENOMEM;
132
133 out1 = kzalloc(mlen, GFP_KERNEL);
134 if (!out1)
135 goto err;
136
137 out2 = kzalloc(mlen, GFP_KERNEL);
138 if (!out2)
139 goto err;
140
141 nret = siglen;
142 in = mpi_read_from_buffer(sig, &nret);
143 if (!in)
144 goto err;
145
146 res = mpi_alloc(mpi_get_nlimbs(in) * 2);
147 if (!res)
148 goto err;
149
150 err = mpi_powm(res, in, pkey[1], pkey[0]);
151 if (err)
152 goto err;
153
154 if (mpi_get_nlimbs(res) * BYTES_PER_MPI_LIMB > mlen) {
155 err = -EINVAL;
156 goto err;
157 }
158
159 p = mpi_get_buffer(res, &l, NULL);
160 if (!p) {
161 err = -EINVAL;
162 goto err;
163 }
164
165 len = mlen;
166 head = len - l;
167 memset(out1, 0, head);
168 memcpy(out1 + head, p, l);
169
170 err = -EINVAL;
171 pkcs_1_v1_5_decode_emsa(out1, len, mblen, out2, &len, &valid);
172
173 if (valid && len == hlen)
174 err = memcmp(out2, h, hlen);
175
176err:
177 mpi_free(in);
178 mpi_free(res);
179 kfree(out1);
180 kfree(out2);
181 mpi_free(pkey[0]);
182 mpi_free(pkey[1]);
183err1:
184 up_read(&key->sem);
185
186 return err;
187}
188
189/**
190 * digsig_verify() - digital signature verification with public key
191 * @keyring: keyring to search key in
192 * @sig: digital signature
193 * @sigen: length of the signature
194 * @data: data
195 * @datalen: length of the data
196 * @return: 0 on success, -EINVAL otherwise
197 *
198 * Verifies data integrity against digital signature.
199 * Currently only RSA is supported.
200 * Normally hash of the content is used as a data for this function.
201 *
202 */
203int digsig_verify(struct key *keyring, const char *sig, int siglen,
204 const char *data, int datalen)
205{
206 int err = -ENOMEM;
207 struct signature_hdr *sh = (struct signature_hdr *)sig;
208 struct shash_desc *desc = NULL;
209 unsigned char hash[SHA1_DIGEST_SIZE];
210 struct key *key;
211 char name[20];
212
213 if (siglen < sizeof(*sh) + 2)
214 return -EINVAL;
215
216 if (sh->algo != PUBKEY_ALGO_RSA)
217 return -ENOTSUPP;
218
219 sprintf(name, "%llX", __be64_to_cpup((uint64_t *)sh->keyid));
220
221 if (keyring) {
222 /* search in specific keyring */
223 key_ref_t kref;
224 kref = keyring_search(make_key_ref(keyring, 1UL),
225 &key_type_user, name);
226 if (IS_ERR(kref))
227 key = ERR_PTR(PTR_ERR(kref));
228 else
229 key = key_ref_to_ptr(kref);
230 } else {
231 key = request_key(&key_type_user, name, NULL);
232 }
233 if (IS_ERR(key)) {
234 pr_err("key not found, id: %s\n", name);
235 return PTR_ERR(key);
236 }
237
238 desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(shash),
239 GFP_KERNEL);
240 if (!desc)
241 goto err;
242
243 desc->tfm = shash;
244 desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
245
246 crypto_shash_init(desc);
247 crypto_shash_update(desc, data, datalen);
248 crypto_shash_update(desc, sig, sizeof(*sh));
249 crypto_shash_final(desc, hash);
250
251 kfree(desc);
252
253 /* pass signature mpis address */
254 err = digsig_verify_rsa(key, sig + sizeof(*sh), siglen - sizeof(*sh),
255 hash, sizeof(hash));
256
257err:
258 key_put(key);
259
260 return err ? -EINVAL : 0;
261}
262EXPORT_SYMBOL_GPL(digsig_verify);
263
264static int __init digsig_init(void)
265{
266 shash = crypto_alloc_shash("sha1", 0, 0);
267 if (IS_ERR(shash)) {
268 pr_err("shash allocation failed\n");
269 return PTR_ERR(shash);
270 }
271
272 return 0;
273
274}
275
276static void __exit digsig_cleanup(void)
277{
278 crypto_free_shash(shash);
279}
280
281module_init(digsig_init);
282module_exit(digsig_cleanup);
283
284MODULE_LICENSE("GPL");