diff options
Diffstat (limited to 'recipes-kernel/cryptodev/sdk_patches/0007-add-support-for-RSA-public-and-private-key-operation.patch')
-rw-r--r-- | recipes-kernel/cryptodev/sdk_patches/0007-add-support-for-RSA-public-and-private-key-operation.patch | 440 |
1 files changed, 440 insertions, 0 deletions
diff --git a/recipes-kernel/cryptodev/sdk_patches/0007-add-support-for-RSA-public-and-private-key-operation.patch b/recipes-kernel/cryptodev/sdk_patches/0007-add-support-for-RSA-public-and-private-key-operation.patch new file mode 100644 index 00000000..803b90ad --- /dev/null +++ b/recipes-kernel/cryptodev/sdk_patches/0007-add-support-for-RSA-public-and-private-key-operation.patch | |||
@@ -0,0 +1,440 @@ | |||
1 | From 6213ae5228a2ff0bb3521474ae37effda95a5d46 Mon Sep 17 00:00:00 2001 | ||
2 | From: Cristian Stoica <cristian.stoica@nxp.com> | ||
3 | Date: Fri, 12 May 2017 17:04:40 +0300 | ||
4 | Subject: [PATCH 7/9] add support for RSA public and private key operations | ||
5 | |||
6 | Only form 1 support is added with this patch. To maintain | ||
7 | compatibility with OpenBSD we need to reverse bignum buffers before | ||
8 | giving them to the kernel. This adds an artificial performance | ||
9 | penalty that can be resolved only with a CIOCKEY extension in | ||
10 | cryptodev API. | ||
11 | |||
12 | As of Linux kernel 4.12 it is not possible to give to the kernel | ||
13 | directly a pointer to a RSA key structure and must resort to a BER | ||
14 | encoding scheme. | ||
15 | |||
16 | Support for private keys in form 3 (CRT) must wait for updates and | ||
17 | fixes in Linux kernel crypto API. | ||
18 | |||
19 | Known issue: | ||
20 | Kernels <= v4.7 strip leading zeros from the result and we get padding | ||
21 | errors from Openssl: RSA_EAY_PUBLIC_DECRYPT: padding check failed | ||
22 | (Fixed with kernel commit "crypto: rsa - Generate fixed-length output" | ||
23 | 9b45b7bba3d22de52e09df63c50f390a193a3f53) | ||
24 | |||
25 | Signed-off-by: Cristian Stoica <cristian.stoica@nxp.com> | ||
26 | --- | ||
27 | cryptlib.c | 234 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | ||
28 | cryptlib.h | 4 +- | ||
29 | cryptodev_int.h | 17 ++++ | ||
30 | ioctl.c | 17 +++- | ||
31 | main.c | 42 ++++++++++ | ||
32 | 5 files changed, 312 insertions(+), 2 deletions(-) | ||
33 | |||
34 | diff --git a/cryptlib.c b/cryptlib.c | ||
35 | index 2c6028e..1c044a4 100644 | ||
36 | --- a/cryptlib.c | ||
37 | +++ b/cryptlib.c | ||
38 | @@ -37,6 +37,10 @@ | ||
39 | #include <crypto/authenc.h> | ||
40 | #include "cryptodev_int.h" | ||
41 | #include "cipherapi.h" | ||
42 | +#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0)) | ||
43 | +#include <linux/asn1_ber_bytecode.h> | ||
44 | +#include <crypto/akcipher.h> | ||
45 | +#endif | ||
46 | |||
47 | extern const struct crypto_type crypto_givcipher_type; | ||
48 | |||
49 | @@ -435,3 +439,233 @@ int cryptodev_hash_final(struct hash_data *hdata, void *output) | ||
50 | return waitfor(&hdata->async.result, ret); | ||
51 | } | ||
52 | |||
53 | +#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0)) | ||
54 | +/* This function is necessary because the bignums in Linux kernel are MSB first | ||
55 | + * (big endian) as opposed to LSB first as OpenBSD crypto layer uses */ | ||
56 | +void reverse_buf(uint8_t *buf, size_t sz) | ||
57 | +{ | ||
58 | + int i; | ||
59 | + uint8_t *end; | ||
60 | + uint8_t tmp; | ||
61 | + | ||
62 | + end = buf + sz; | ||
63 | + | ||
64 | + for (i = 0; i < sz/2; i++) { | ||
65 | + end--; | ||
66 | + | ||
67 | + tmp = *buf; | ||
68 | + *buf = *end; | ||
69 | + *end = tmp; | ||
70 | + | ||
71 | + buf++; | ||
72 | + } | ||
73 | +} | ||
74 | + | ||
75 | +int ber_wr_tag(uint8_t **ber_ptr, uint8_t tag) | ||
76 | +{ | ||
77 | + **ber_ptr = tag; | ||
78 | + *ber_ptr += 1; | ||
79 | + | ||
80 | + return 0; | ||
81 | +} | ||
82 | + | ||
83 | +int ber_wr_len(uint8_t **ber_ptr, size_t len, size_t sz) | ||
84 | +{ | ||
85 | + if (len < 127) { | ||
86 | + **ber_ptr = len; | ||
87 | + *ber_ptr += 1; | ||
88 | + } else { | ||
89 | + size_t sz_save = sz; | ||
90 | + | ||
91 | + sz--; | ||
92 | + **ber_ptr = 0x80 | sz; | ||
93 | + | ||
94 | + while (sz > 0) { | ||
95 | + *(*ber_ptr + sz) = len & 0xff; | ||
96 | + len >>= 8; | ||
97 | + sz--; | ||
98 | + } | ||
99 | + *ber_ptr += sz_save; | ||
100 | + } | ||
101 | + | ||
102 | + return 0; | ||
103 | +} | ||
104 | + | ||
105 | +int ber_wr_int(uint8_t **ber_ptr, uint8_t *crp_p, size_t sz) | ||
106 | +{ | ||
107 | + int ret; | ||
108 | + | ||
109 | + ret = copy_from_user(*ber_ptr, crp_p, sz); | ||
110 | + reverse_buf(*ber_ptr, sz); | ||
111 | + | ||
112 | + *ber_ptr += sz; | ||
113 | + | ||
114 | + return ret; | ||
115 | +} | ||
116 | + | ||
117 | +/* calculate the size of the length field itself in BER encoding */ | ||
118 | +size_t ber_enc_len(size_t len) | ||
119 | +{ | ||
120 | + size_t sz; | ||
121 | + | ||
122 | + sz = 1; | ||
123 | + if (len > 127) { /* long encoding */ | ||
124 | + while (len != 0) { | ||
125 | + len >>= 8; | ||
126 | + sz++; | ||
127 | + } | ||
128 | + } | ||
129 | + | ||
130 | + return sz; | ||
131 | +} | ||
132 | + | ||
133 | +void *cryptodev_alloc_rsa_pub_key(struct kernel_crypt_pkop *pkop, | ||
134 | + uint32_t *key_len) | ||
135 | +{ | ||
136 | + struct crypt_kop *cop = &pkop->pkop; | ||
137 | + uint8_t *ber_key; | ||
138 | + uint8_t *ber_ptr; | ||
139 | + uint32_t ber_key_len; | ||
140 | + size_t s_sz; | ||
141 | + size_t e_sz; | ||
142 | + size_t n_sz; | ||
143 | + size_t s_enc_len; | ||
144 | + size_t e_enc_len; | ||
145 | + size_t n_enc_len; | ||
146 | + int err; | ||
147 | + | ||
148 | + /* BER public key format: | ||
149 | + * SEQUENCE TAG 1 byte | ||
150 | + * SEQUENCE LENGTH s_enc_len bytes | ||
151 | + * INTEGER TAG 1 byte | ||
152 | + * INTEGER LENGTH n_enc_len bytes | ||
153 | + * INTEGER (n modulus) n_sz bytes | ||
154 | + * INTEGER TAG 1 byte | ||
155 | + * INTEGER LENGTH e_enc_len bytes | ||
156 | + * INTEGER (e exponent) e_sz bytes | ||
157 | + */ | ||
158 | + | ||
159 | + e_sz = (cop->crk_param[1].crp_nbits + 7)/8; | ||
160 | + n_sz = (cop->crk_param[2].crp_nbits + 7)/8; | ||
161 | + | ||
162 | + e_enc_len = ber_enc_len(e_sz); | ||
163 | + n_enc_len = ber_enc_len(n_sz); | ||
164 | + | ||
165 | + /* | ||
166 | + * Sequence length is the size of all the fields following the sequence | ||
167 | + * tag, added together. The two added bytes account for the two INT | ||
168 | + * tags in the Public Key sequence | ||
169 | + */ | ||
170 | + s_sz = e_sz + e_enc_len + n_sz + n_enc_len + 2; | ||
171 | + s_enc_len = ber_enc_len(s_sz); | ||
172 | + | ||
173 | + /* The added byte accounts for the SEQ tag at the start of the key */ | ||
174 | + ber_key_len = s_sz + s_enc_len + 1; | ||
175 | + | ||
176 | + /* Linux asn1_ber_decoder doesn't like keys that are too large */ | ||
177 | + if (ber_key_len > 65535) { | ||
178 | + return NULL; | ||
179 | + } | ||
180 | + | ||
181 | + ber_key = kmalloc(ber_key_len, GFP_DMA); | ||
182 | + if (!ber_key) { | ||
183 | + return NULL; | ||
184 | + } | ||
185 | + | ||
186 | + ber_ptr = ber_key; | ||
187 | + | ||
188 | + err = ber_wr_tag(&ber_ptr, _tag(UNIV, CONS, SEQ)) || | ||
189 | + ber_wr_len(&ber_ptr, s_sz, s_enc_len) || | ||
190 | + ber_wr_tag(&ber_ptr, _tag(UNIV, PRIM, INT)) || | ||
191 | + ber_wr_len(&ber_ptr, n_sz, n_enc_len) || | ||
192 | + ber_wr_int(&ber_ptr, cop->crk_param[2].crp_p, n_sz) || | ||
193 | + ber_wr_tag(&ber_ptr, _tag(UNIV, PRIM, INT)) || | ||
194 | + ber_wr_len(&ber_ptr, e_sz, e_enc_len) || | ||
195 | + ber_wr_int(&ber_ptr, cop->crk_param[1].crp_p, e_sz); | ||
196 | + if (err != 0) { | ||
197 | + goto free_key; | ||
198 | + } | ||
199 | + | ||
200 | + *key_len = ber_key_len; | ||
201 | + return ber_key; | ||
202 | + | ||
203 | +free_key: | ||
204 | + kfree(ber_key); | ||
205 | + return NULL; | ||
206 | +} | ||
207 | + | ||
208 | +int crypto_bn_modexp(struct kernel_crypt_pkop *pkop) | ||
209 | +{ | ||
210 | + struct crypt_kop *cop = &pkop->pkop; | ||
211 | + uint8_t *ber_key; | ||
212 | + uint32_t ber_key_len; | ||
213 | + size_t m_sz; | ||
214 | + size_t c_sz; | ||
215 | + size_t c_sz_max; | ||
216 | + uint8_t *m_buf; | ||
217 | + uint8_t *c_buf; | ||
218 | + struct scatterlist src; | ||
219 | + struct scatterlist dst; | ||
220 | + int err; | ||
221 | + | ||
222 | + ber_key = cryptodev_alloc_rsa_pub_key(pkop, &ber_key_len); | ||
223 | + if (!ber_key) { | ||
224 | + return -ENOMEM; | ||
225 | + } | ||
226 | + | ||
227 | + err = crypto_akcipher_set_pub_key(pkop->s, ber_key, ber_key_len); | ||
228 | + if (err != 0) { | ||
229 | + goto free_key; | ||
230 | + } | ||
231 | + | ||
232 | + m_sz = (cop->crk_param[0].crp_nbits + 7)/8; | ||
233 | + c_sz = (cop->crk_param[3].crp_nbits + 7)/8; | ||
234 | + | ||
235 | + m_buf = kmalloc(m_sz, GFP_DMA); | ||
236 | + if (!m_buf) { | ||
237 | + err = -ENOMEM; | ||
238 | + goto free_key; | ||
239 | + } | ||
240 | + | ||
241 | + err = copy_from_user(m_buf, cop->crk_param[0].crp_p, m_sz); | ||
242 | + if (err != 0) { | ||
243 | + goto free_m_buf; | ||
244 | + } | ||
245 | + reverse_buf(m_buf, m_sz); | ||
246 | + | ||
247 | + c_sz_max = crypto_akcipher_maxsize(pkop->s); | ||
248 | + if (c_sz > c_sz_max) { | ||
249 | + err = -EINVAL; | ||
250 | + goto free_m_buf; | ||
251 | + } | ||
252 | + | ||
253 | + c_buf = kzalloc(c_sz_max, GFP_KERNEL); | ||
254 | + if (!c_buf) { | ||
255 | + goto free_m_buf; | ||
256 | + } | ||
257 | + | ||
258 | + sg_init_one(&src, m_buf, m_sz); | ||
259 | + sg_init_one(&dst, c_buf, c_sz); | ||
260 | + | ||
261 | + init_completion(&pkop->result.completion); | ||
262 | + akcipher_request_set_callback(pkop->req, 0, | ||
263 | + cryptodev_complete, &pkop->result); | ||
264 | + akcipher_request_set_crypt(pkop->req, &src, &dst, m_sz, c_sz); | ||
265 | + | ||
266 | + err = crypto_akcipher_encrypt(pkop->req); | ||
267 | + err = waitfor(&pkop->result, err); | ||
268 | + | ||
269 | + if (err == 0) { | ||
270 | + reverse_buf(c_buf, c_sz); | ||
271 | + err = copy_to_user(cop->crk_param[3].crp_p, c_buf, c_sz); | ||
272 | + } | ||
273 | + | ||
274 | + kfree(c_buf); | ||
275 | +free_m_buf: | ||
276 | + kfree(m_buf); | ||
277 | +free_key: | ||
278 | + kfree(ber_key); | ||
279 | + | ||
280 | + return err; | ||
281 | +} | ||
282 | +#endif | ||
283 | diff --git a/cryptlib.h b/cryptlib.h | ||
284 | index 48fe9bd..f909c34 100644 | ||
285 | --- a/cryptlib.h | ||
286 | +++ b/cryptlib.h | ||
287 | @@ -95,6 +95,8 @@ int cryptodev_hash_reset(struct hash_data *hdata); | ||
288 | void cryptodev_hash_deinit(struct hash_data *hdata); | ||
289 | int cryptodev_hash_init(struct hash_data *hdata, const char *alg_name, | ||
290 | int hmac_mode, void *mackey, size_t mackeylen); | ||
291 | - | ||
292 | +#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0)) | ||
293 | +int crypto_bn_modexp(struct kernel_crypt_pkop *pkop); | ||
294 | +#endif | ||
295 | |||
296 | #endif | ||
297 | diff --git a/cryptodev_int.h b/cryptodev_int.h | ||
298 | index c1879fd..7860c39 100644 | ||
299 | --- a/cryptodev_int.h | ||
300 | +++ b/cryptodev_int.h | ||
301 | @@ -19,6 +19,10 @@ | ||
302 | #include <linux/scatterlist.h> | ||
303 | #include <crypto/cryptodev.h> | ||
304 | #include <crypto/aead.h> | ||
305 | +#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0)) | ||
306 | +#include <crypto/internal/rsa.h> | ||
307 | +#endif | ||
308 | + | ||
309 | |||
310 | #define PFX "cryptodev: " | ||
311 | #define dprintk(level, severity, format, a...) \ | ||
312 | @@ -111,6 +115,18 @@ struct kernel_crypt_auth_op { | ||
313 | struct mm_struct *mm; | ||
314 | }; | ||
315 | |||
316 | +#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0)) | ||
317 | +struct kernel_crypt_pkop { | ||
318 | + struct crypt_kop pkop; | ||
319 | + | ||
320 | + struct crypto_akcipher *s; /* Transform pointer from CryptoAPI */ | ||
321 | + struct akcipher_request *req; /* PKC request allocated from CryptoAPI */ | ||
322 | + struct cryptodev_result result; /* updated by completion handler */ | ||
323 | +}; | ||
324 | + | ||
325 | +int crypto_run_asym(struct kernel_crypt_pkop *pkop); | ||
326 | +#endif | ||
327 | + | ||
328 | /* auth */ | ||
329 | |||
330 | int kcaop_from_user(struct kernel_crypt_auth_op *kcop, | ||
331 | @@ -122,6 +138,7 @@ int crypto_run(struct fcrypt *fcr, struct kernel_crypt_op *kcop); | ||
332 | |||
333 | #include <cryptlib.h> | ||
334 | |||
335 | + | ||
336 | /* other internal structs */ | ||
337 | struct csession { | ||
338 | struct list_head entry; | ||
339 | diff --git a/ioctl.c b/ioctl.c | ||
340 | index db7207a..8b0df4e 100644 | ||
341 | --- a/ioctl.c | ||
342 | +++ b/ioctl.c | ||
343 | @@ -810,6 +810,9 @@ cryptodev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg_) | ||
344 | struct session_op sop; | ||
345 | struct kernel_crypt_op kcop; | ||
346 | struct kernel_crypt_auth_op kcaop; | ||
347 | +#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0)) | ||
348 | + struct kernel_crypt_pkop pkop; | ||
349 | +#endif | ||
350 | struct crypt_priv *pcr = filp->private_data; | ||
351 | struct fcrypt *fcr; | ||
352 | struct session_info_op siop; | ||
353 | @@ -823,7 +826,11 @@ cryptodev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg_) | ||
354 | |||
355 | switch (cmd) { | ||
356 | case CIOCASYMFEAT: | ||
357 | - return put_user(0, p); | ||
358 | + ses = 0; | ||
359 | + if (crypto_has_alg("rsa", 0, 0)) { | ||
360 | + ses = CRF_MOD_EXP; | ||
361 | + } | ||
362 | + return put_user(ses, p); | ||
363 | case CRIOGET: | ||
364 | fd = clonefd(filp); | ||
365 | ret = put_user(fd, p); | ||
366 | @@ -859,6 +866,14 @@ cryptodev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg_) | ||
367 | if (unlikely(ret)) | ||
368 | return ret; | ||
369 | return copy_to_user(arg, &siop, sizeof(siop)); | ||
370 | +#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0)) | ||
371 | + case CIOCKEY: | ||
372 | + ret = copy_from_user(&pkop.pkop, arg, sizeof(struct crypt_kop)); | ||
373 | + if (ret == 0) { | ||
374 | + ret = crypto_run_asym(&pkop); | ||
375 | + } | ||
376 | + return ret; | ||
377 | +#endif | ||
378 | case CIOCCRYPT: | ||
379 | if (unlikely(ret = kcop_from_user(&kcop, fcr, arg))) { | ||
380 | dwarning(1, "Error copying from user"); | ||
381 | diff --git a/main.c b/main.c | ||
382 | index 57e5c38..2bfe6f0 100644 | ||
383 | --- a/main.c | ||
384 | +++ b/main.c | ||
385 | @@ -48,6 +48,9 @@ | ||
386 | #include "zc.h" | ||
387 | #include "cryptlib.h" | ||
388 | #include "version.h" | ||
389 | +#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0)) | ||
390 | +#include <crypto/akcipher.h> | ||
391 | +#endif | ||
392 | |||
393 | /* This file contains the traditional operations of encryption | ||
394 | * and hashing of /dev/crypto. | ||
395 | @@ -265,3 +268,42 @@ out_unlock: | ||
396 | crypto_put_session(ses_ptr); | ||
397 | return ret; | ||
398 | } | ||
399 | + | ||
400 | +#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0)) | ||
401 | +int crypto_run_asym(struct kernel_crypt_pkop *pkop) | ||
402 | +{ | ||
403 | + int err; | ||
404 | + | ||
405 | + pkop->s = crypto_alloc_akcipher("rsa", 0, 0); | ||
406 | + if (IS_ERR(pkop->s)) { | ||
407 | + return PTR_ERR(pkop->s); | ||
408 | + } | ||
409 | + | ||
410 | + pkop->req = akcipher_request_alloc(pkop->s, GFP_KERNEL); | ||
411 | + if (pkop->req == NULL) { | ||
412 | + err = -ENOMEM; | ||
413 | + goto out_free_tfm; | ||
414 | + } | ||
415 | + | ||
416 | + switch (pkop->pkop.crk_op) { | ||
417 | + case CRK_MOD_EXP: /* RSA_PUB or PRIV form 1 */ | ||
418 | + if (pkop->pkop.crk_iparams != 3 && pkop->pkop.crk_oparams != 1) { | ||
419 | + err = -EINVAL; | ||
420 | + goto out_free_req; | ||
421 | + } | ||
422 | + err = crypto_bn_modexp(pkop); | ||
423 | + break; | ||
424 | + default: | ||
425 | + err = -EINVAL; | ||
426 | + break; | ||
427 | + } | ||
428 | + | ||
429 | +out_free_req: | ||
430 | + kfree(pkop->req); | ||
431 | + | ||
432 | +out_free_tfm: | ||
433 | + crypto_free_akcipher(pkop->s); | ||
434 | + | ||
435 | + return err; | ||
436 | +} | ||
437 | +#endif | ||
438 | -- | ||
439 | 2.7.4 | ||
440 | |||