Added Appro's SSSE3 SHA implementations
[gnutls:gnutls.git] / lib / accelerated / cryptodev.c
1 /*
2  * Copyright (C) 2009-2010, 2012 Free Software Foundation, Inc.
3  *
4  * Author: Nikos Mavrogiannopoulos
5  *
6  * This file is part of GnuTLS.
7  *
8  * The GnuTLS is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  */
22
23 #include <gnutls_errors.h>
24 #include <gnutls_int.h>
25 #include <gnutls/crypto.h>
26 #include <gnutls_errors.h>
27 #include <accelerated/cryptodev.h>
28
29 #ifdef ENABLE_CRYPTODEV
30
31 #include <fcntl.h>
32 #include <sys/ioctl.h>
33 #include <crypto/cryptodev.h>
34
35 #ifndef CRYPTO_CIPHER_MAX_KEY_LEN
36 #define CRYPTO_CIPHER_MAX_KEY_LEN 64
37 #endif
38
39 #ifndef EALG_MAX_BLOCK_LEN
40 #define EALG_MAX_BLOCK_LEN 16
41 #endif
42
43 int _gnutls_cryptodev_fd = -1;
44
45 static int register_mac_digest(int cfd);
46
47 struct cryptodev_ctx {
48         struct session_op sess;
49         struct crypt_op cryp;
50         uint8_t iv[EALG_MAX_BLOCK_LEN];
51
52         int cfd;
53 };
54
55 static const int gnutls_cipher_map[] = {
56         [GNUTLS_CIPHER_AES_128_CBC] = CRYPTO_AES_CBC,
57         [GNUTLS_CIPHER_AES_192_CBC] = CRYPTO_AES_CBC,
58         [GNUTLS_CIPHER_AES_256_CBC] = CRYPTO_AES_CBC,
59         [GNUTLS_CIPHER_3DES_CBC] = CRYPTO_3DES_CBC,
60         [GNUTLS_CIPHER_CAMELLIA_128_CBC] = CRYPTO_CAMELLIA_CBC,
61         [GNUTLS_CIPHER_CAMELLIA_192_CBC] = CRYPTO_CAMELLIA_CBC,
62         [GNUTLS_CIPHER_CAMELLIA_256_CBC] = CRYPTO_CAMELLIA_CBC,
63         [GNUTLS_CIPHER_DES_CBC] = CRYPTO_DES_CBC,
64 };
65
66 static int
67 cryptodev_cipher_init(gnutls_cipher_algorithm_t algorithm, void **_ctx,
68                       int enc)
69 {
70         struct cryptodev_ctx *ctx;
71         int cipher = gnutls_cipher_map[algorithm];
72
73         *_ctx = gnutls_calloc(1, sizeof(struct cryptodev_ctx));
74         if (*_ctx == NULL) {
75                 gnutls_assert();
76                 return GNUTLS_E_MEMORY_ERROR;
77         }
78
79         ctx = *_ctx;
80
81         ctx->cfd = _gnutls_cryptodev_fd;
82         ctx->sess.cipher = cipher;
83         ctx->cryp.iv = ctx->iv;
84
85         return 0;
86 }
87
88 static int
89 cryptodev_cipher_setkey(void *_ctx, const void *key, size_t keysize)
90 {
91         struct cryptodev_ctx *ctx = _ctx;
92
93         ctx->sess.keylen = keysize;
94         ctx->sess.key = (void *) key;
95
96         if (ioctl(ctx->cfd, CIOCGSESSION, &ctx->sess)) {
97                 gnutls_assert();
98                 return GNUTLS_E_CRYPTODEV_IOCTL_ERROR;
99         }
100         ctx->cryp.ses = ctx->sess.ses;
101
102         return 0;
103 }
104
105 static int cryptodev_setiv(void *_ctx, const void *iv, size_t iv_size)
106 {
107         struct cryptodev_ctx *ctx = _ctx;
108
109         memcpy(ctx->iv, iv, iv_size);
110
111         return 0;
112 }
113
114 static int
115 cryptodev_encrypt(void *_ctx, const void *src, size_t src_size,
116                   void *dst, size_t dst_size)
117 {
118         struct cryptodev_ctx *ctx = _ctx;
119         ctx->cryp.len = src_size;
120         ctx->cryp.src = (void *) src;
121         ctx->cryp.dst = dst;
122         ctx->cryp.op = COP_ENCRYPT;
123         ctx->cryp.flags = COP_FLAG_WRITE_IV;
124
125         if (ioctl(ctx->cfd, CIOCCRYPT, &ctx->cryp)) {
126                 gnutls_assert();
127                 return GNUTLS_E_CRYPTODEV_IOCTL_ERROR;
128         }
129
130         return 0;
131 }
132
133 static int
134 cryptodev_decrypt(void *_ctx, const void *src, size_t src_size,
135                   void *dst, size_t dst_size)
136 {
137         struct cryptodev_ctx *ctx = _ctx;
138
139         ctx->cryp.len = src_size;
140         ctx->cryp.src = (void *) src;
141         ctx->cryp.dst = dst;
142         ctx->cryp.op = COP_DECRYPT;
143         ctx->cryp.flags = COP_FLAG_WRITE_IV;
144
145         if (ioctl(ctx->cfd, CIOCCRYPT, &ctx->cryp)) {
146                 gnutls_assert();
147                 return GNUTLS_E_CRYPTODEV_IOCTL_ERROR;
148         }
149
150         return 0;
151 }
152
153 static void cryptodev_deinit(void *_ctx)
154 {
155         struct cryptodev_ctx *ctx = _ctx;
156
157         ioctl(ctx->cfd, CIOCFSESSION, &ctx->sess.ses);
158         gnutls_free(ctx);
159 }
160
161 static const gnutls_crypto_cipher_st cipher_struct = {
162         .init = cryptodev_cipher_init,
163         .setkey = cryptodev_cipher_setkey,
164         .setiv = cryptodev_setiv,
165         .encrypt = cryptodev_encrypt,
166         .decrypt = cryptodev_decrypt,
167         .deinit = cryptodev_deinit,
168 };
169
170 static int register_crypto(int cfd)
171 {
172         struct session_op sess;
173         uint8_t fake_key[CRYPTO_CIPHER_MAX_KEY_LEN];
174         unsigned int i;
175         int ret;
176 #ifdef CIOCGSESSINFO
177         struct session_info_op siop;
178 #endif
179
180         memset(&sess, 0, sizeof(sess));
181
182         for (i = 0;
183              i < sizeof(gnutls_cipher_map) / sizeof(gnutls_cipher_map[0]);
184              i++) {
185                 if (gnutls_cipher_map[i] == 0)
186                         continue;
187
188                 /* test if a cipher is supported and if yes register it */
189                 sess.cipher = gnutls_cipher_map[i];
190                 sess.keylen = gnutls_cipher_get_key_size(i);
191                 sess.key = fake_key;
192
193                 if (ioctl(cfd, CIOCGSESSION, &sess)) {
194                         continue;
195                 }
196 #ifdef CIOCGSESSINFO
197                 memset(&siop, 0, sizeof(siop));
198
199                 siop.ses = sess.ses;    /* do not register ciphers that are not hw accelerated */
200                 if (ioctl(cfd, CIOCGSESSINFO, &siop) == 0) {
201                         if (!(siop.flags & SIOP_FLAG_KERNEL_DRIVER_ONLY)) {
202                                 ioctl(cfd, CIOCFSESSION, &sess.ses);
203                                 continue;
204                         }
205                 }
206 #endif
207
208                 ioctl(cfd, CIOCFSESSION, &sess.ses);
209
210                 _gnutls_debug_log("/dev/crypto: registering: %s\n",
211                                   gnutls_cipher_get_name(i));
212                 ret =
213                     gnutls_crypto_single_cipher_register(i, 90,
214                                                          &cipher_struct);
215                 if (ret < 0) {
216                         gnutls_assert();
217                         return ret;
218                 }
219
220         }
221
222 #ifdef CIOCAUTHCRYPT
223         return _cryptodev_register_gcm_crypto(cfd);
224 #else
225         return 0;
226 #endif
227 }
228
229 int _gnutls_cryptodev_init(void)
230 {
231         int ret;
232
233         /* Open the crypto device */
234         _gnutls_cryptodev_fd = open("/dev/crypto", O_RDWR, 0);
235         if (_gnutls_cryptodev_fd < 0) {
236                 gnutls_assert();
237                 return GNUTLS_E_CRYPTODEV_DEVICE_ERROR;
238         }
239 #ifndef CRIOGET_NOT_NEEDED
240         {
241                 int cfd = -1;
242                 /* Clone file descriptor */
243                 if (ioctl(_gnutls_cryptodev_fd, CRIOGET, &cfd)) {
244                         gnutls_assert();
245                         return GNUTLS_E_CRYPTODEV_IOCTL_ERROR;
246                 }
247
248                 /* Set close-on-exec (not really neede here) */
249                 if (fcntl(cfd, F_SETFD, 1) == -1) {
250                         gnutls_assert();
251                         return GNUTLS_E_CRYPTODEV_IOCTL_ERROR;
252                 }
253
254                 close(_gnutls_cryptodev_fd);
255                 _gnutls_cryptodev_fd = cfd;
256         }
257 #endif
258
259         ret = register_crypto(_gnutls_cryptodev_fd);
260         if (ret < 0)
261                 gnutls_assert();
262
263         if (ret >= 0) {
264                 ret = register_mac_digest(_gnutls_cryptodev_fd);
265                 if (ret < 0)
266                         gnutls_assert();
267         }
268
269         if (ret < 0) {
270                 gnutls_assert();
271                 close(_gnutls_cryptodev_fd);
272         }
273
274         return ret;
275 }
276
277 void _gnutls_cryptodev_deinit(void)
278 {
279         if (_gnutls_cryptodev_fd != -1)
280                 close(_gnutls_cryptodev_fd);
281 }
282
283 /* MAC and digest stuff */
284
285 /* if we are using linux /dev/crypto
286  */
287 #if defined(COP_FLAG_UPDATE) && defined(COP_FLAG_RESET)
288
289 static const int gnutls_mac_map[] = {
290         [GNUTLS_MAC_MD5] = CRYPTO_MD5_HMAC,
291         [GNUTLS_MAC_SHA1] = CRYPTO_SHA1_HMAC,
292         [GNUTLS_MAC_SHA256] = CRYPTO_SHA2_256_HMAC,
293         [GNUTLS_MAC_SHA384] = CRYPTO_SHA2_384_HMAC,
294         [GNUTLS_MAC_SHA512] = CRYPTO_SHA2_512_HMAC,
295 };
296
297 static int
298 cryptodev_mac_fast(gnutls_mac_algorithm_t algo,
299                    const void *key, size_t key_size, const void *text,
300                    size_t text_size, void *digest)
301 {
302         struct cryptodev_ctx ctx;
303         int ret;
304
305         memset(&ctx, 0, sizeof(ctx));
306         ctx.cfd = _gnutls_cryptodev_fd;
307         ctx.sess.mac = gnutls_mac_map[algo];
308
309         ctx.sess.mackeylen = key_size;
310         ctx.sess.mackey = (void *) key;
311
312         if (ioctl(ctx.cfd, CIOCGSESSION, &ctx.sess))
313                 return gnutls_assert_val(GNUTLS_E_CRYPTODEV_IOCTL_ERROR);
314
315         ctx.cryp.ses = ctx.sess.ses;
316
317         ctx.cryp.len = text_size;
318         ctx.cryp.src = (void *) text;
319         ctx.cryp.dst = NULL;
320         ctx.cryp.op = COP_ENCRYPT;
321         ctx.cryp.mac = digest;
322
323         ret = ioctl(ctx.cfd, CIOCCRYPT, &ctx.cryp);
324
325         ioctl(_gnutls_cryptodev_fd, CIOCFSESSION, &ctx.sess.ses);
326         if (ret != 0)
327                 return gnutls_assert_val(GNUTLS_E_CRYPTODEV_IOCTL_ERROR);
328
329         return 0;
330 }
331
332 #define cryptodev_mac_deinit cryptodev_deinit
333
334 static const gnutls_crypto_mac_st mac_struct = {
335         .init = NULL,
336         .setkey = NULL,
337         .setnonce = NULL,
338         .hash = NULL,
339         .output = NULL,
340         .deinit = NULL,
341         .fast = cryptodev_mac_fast
342 };
343
344 /* Digest algorithms */
345
346 static const int gnutls_digest_map[] = {
347         [GNUTLS_DIG_MD5] = CRYPTO_MD5,
348         [GNUTLS_DIG_SHA1] = CRYPTO_SHA1,
349         [GNUTLS_DIG_SHA256] = CRYPTO_SHA2_256,
350         [GNUTLS_DIG_SHA384] = CRYPTO_SHA2_384,
351         [GNUTLS_DIG_SHA512] = CRYPTO_SHA2_512,
352 };
353
354 static int
355 cryptodev_digest_fast(gnutls_digest_algorithm_t algo,
356                       const void *text, size_t text_size, void *digest)
357 {
358         struct cryptodev_ctx ctx;
359         int ret;
360
361         memset(&ctx, 0, sizeof(ctx));
362         ctx.cfd = _gnutls_cryptodev_fd;
363         ctx.sess.mac = gnutls_digest_map[algo];
364
365         if (ioctl(ctx.cfd, CIOCGSESSION, &ctx.sess))
366                 return gnutls_assert_val(GNUTLS_E_CRYPTODEV_IOCTL_ERROR);
367
368         ctx.cryp.ses = ctx.sess.ses;
369
370         ctx.cryp.len = text_size;
371         ctx.cryp.src = (void *) text;
372         ctx.cryp.dst = NULL;
373         ctx.cryp.op = COP_ENCRYPT;
374         ctx.cryp.mac = digest;
375
376         ret = ioctl(ctx.cfd, CIOCCRYPT, &ctx.cryp);
377
378         ioctl(_gnutls_cryptodev_fd, CIOCFSESSION, &ctx.sess.ses);
379         if (ret != 0)
380                 return gnutls_assert_val(GNUTLS_E_CRYPTODEV_IOCTL_ERROR);
381
382         return 0;
383 }
384
385 static const gnutls_crypto_digest_st digest_struct = {
386         .init = NULL,
387         .hash = NULL,
388         .output = NULL,
389         .deinit = NULL,
390         .fast = cryptodev_digest_fast
391 };
392
393 static int register_mac_digest(int cfd)
394 {
395         struct session_op sess;
396         uint8_t fake_key[CRYPTO_CIPHER_MAX_KEY_LEN];
397         unsigned int i;
398         int ret;
399 #ifdef CIOCGSESSINFO
400         struct session_info_op siop;
401 #endif
402
403         memset(&sess, 0, sizeof(sess));
404         for (i = 0; i < sizeof(gnutls_mac_map) / sizeof(gnutls_mac_map[0]);
405              i++) {
406                 if (gnutls_mac_map[i] == 0)
407                         continue;
408
409                 sess.mac = gnutls_mac_map[i];
410                 sess.mackeylen = 8;
411                 sess.mackey = fake_key;
412
413                 if (ioctl(cfd, CIOCGSESSION, &sess)) {
414                         continue;
415                 }
416 #ifdef CIOCGSESSINFO
417                 memset(&siop, 0, sizeof(siop));
418
419                 siop.ses = sess.ses;    /* do not register ciphers that are not hw accelerated */
420                 if (ioctl(cfd, CIOCGSESSINFO, &siop) == 0) {
421                         if (!(siop.flags & SIOP_FLAG_KERNEL_DRIVER_ONLY)) {
422                                 ioctl(cfd, CIOCFSESSION, &sess.ses);
423                                 continue;
424                         }
425                 }
426 #endif
427                 _gnutls_debug_log("/dev/crypto: registering: HMAC-%s\n",
428                                   gnutls_mac_get_name(i));
429
430                 ioctl(cfd, CIOCFSESSION, &sess.ses);
431
432                 ret =
433                     gnutls_crypto_single_mac_register(i, 90, &mac_struct);
434                 if (ret < 0) {
435                         gnutls_assert();
436                         return ret;
437                 }
438         }
439
440         memset(&sess, 0, sizeof(sess));
441         for (i = 0;
442              i < sizeof(gnutls_digest_map) / sizeof(gnutls_digest_map[0]);
443              i++) {
444                 if (gnutls_digest_map[i] == 0)
445                         continue;
446
447                 sess.mac = gnutls_digest_map[i];
448
449                 if (ioctl(cfd, CIOCGSESSION, &sess)) {
450                         continue;
451                 }
452 #ifdef CIOCGSESSINFO
453                 memset(&siop, 0, sizeof(siop));
454
455                 siop.ses = sess.ses;
456                 if (ioctl(cfd, CIOCGSESSINFO, &siop) == 0) {
457                         if (!(siop.flags & SIOP_FLAG_KERNEL_DRIVER_ONLY)) {
458                                 ioctl(cfd, CIOCFSESSION, &sess.ses);
459                                 continue;
460                         }
461                 }
462 #endif
463
464                 ioctl(cfd, CIOCFSESSION, &sess.ses);
465
466                 _gnutls_debug_log("/dev/crypto: registering: %s\n",
467                                   gnutls_mac_get_name(i));
468                 ret =
469                     gnutls_crypto_single_digest_register(i, 90,
470                                                          &digest_struct);
471                 if (ret < 0) {
472                         gnutls_assert();
473                         return ret;
474                 }
475         }
476
477         return 0;
478 }
479
480 #else
481 static int register_mac_digest(int cfd)
482 {
483         return 0;
484 }
485
486 #endif                          /* defined(COP_FLAG_UPDATE) */
487
488 #else                           /* ENABLE_CRYPTODEV */
489 int _gnutls_cryptodev_init(void)
490 {
491         return 0;
492 }
493
494 void _gnutls_cryptodev_deinit(void)
495 {
496         return;
497 }
498 #endif                          /* ENABLE_CRYPTODEV */