summaryrefslogtreecommitdiffstats
path: root/recipes-kernel/cryptodev/sdk_patches/0007-add-support-for-RSA-public-and-private-key-operation.patch
diff options
context:
space:
mode:
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.patch440
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 @@
1From 6213ae5228a2ff0bb3521474ae37effda95a5d46 Mon Sep 17 00:00:00 2001
2From: Cristian Stoica <cristian.stoica@nxp.com>
3Date: Fri, 12 May 2017 17:04:40 +0300
4Subject: [PATCH 7/9] add support for RSA public and private key operations
5
6Only form 1 support is added with this patch. To maintain
7compatibility with OpenBSD we need to reverse bignum buffers before
8giving them to the kernel. This adds an artificial performance
9penalty that can be resolved only with a CIOCKEY extension in
10cryptodev API.
11
12As of Linux kernel 4.12 it is not possible to give to the kernel
13directly a pointer to a RSA key structure and must resort to a BER
14encoding scheme.
15
16Support for private keys in form 3 (CRT) must wait for updates and
17fixes in Linux kernel crypto API.
18
19Known issue:
20Kernels <= v4.7 strip leading zeros from the result and we get padding
21errors from Openssl: RSA_EAY_PUBLIC_DECRYPT: padding check failed
22(Fixed with kernel commit "crypto: rsa - Generate fixed-length output"
239b45b7bba3d22de52e09df63c50f390a193a3f53)
24
25Signed-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
34diff --git a/cryptlib.c b/cryptlib.c
35index 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
283diff --git a/cryptlib.h b/cryptlib.h
284index 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
297diff --git a/cryptodev_int.h b/cryptodev_int.h
298index 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;
339diff --git a/ioctl.c b/ioctl.c
340index 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");
381diff --git a/main.c b/main.c
382index 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--
4392.7.4
440