]> code.ossystems Code Review - meta-freescale.git/blob
803b90ad263a4766edb9085ee9256c1f2916f98e
[meta-freescale.git] /
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