Added Mike Hamburg's SSSE3 AES implementation.
[gnutls:gnutls.git] / lib / accelerated / cryptodev-gcm.c
1 /*
2  * Copyright (C) 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
44 #ifdef CIOCAUTHCRYPT
45
46 #define GCM_BLOCK_SIZE 16
47
48 struct cryptodev_gcm_ctx {
49         struct session_op sess;
50         struct crypt_auth_op cryp;
51         uint8_t iv[GCM_BLOCK_SIZE];
52         uint8_t tag[GCM_BLOCK_SIZE];
53
54         void *auth_data;
55         unsigned int auth_data_size;
56
57         int op;                 /* whether encryption op has been executed */
58
59         int cfd;
60 };
61
62 static void aes_gcm_deinit(void *_ctx)
63 {
64         struct cryptodev_gcm_ctx *ctx = _ctx;
65
66         ioctl(ctx->cfd, CIOCFSESSION, &ctx->sess.ses);
67         gnutls_free(ctx);
68 }
69
70 static const int cipher_map[] = {
71         [GNUTLS_CIPHER_AES_128_GCM] = CRYPTO_AES_GCM,
72         [GNUTLS_CIPHER_AES_256_GCM] = CRYPTO_AES_GCM,
73 };
74
75 static int
76 aes_gcm_cipher_init(gnutls_cipher_algorithm_t algorithm, void **_ctx,
77                     int enc)
78 {
79         struct cryptodev_gcm_ctx *ctx;
80
81         *_ctx = gnutls_calloc(1, sizeof(struct cryptodev_gcm_ctx));
82         if (*_ctx == NULL) {
83                 gnutls_assert();
84                 return GNUTLS_E_MEMORY_ERROR;
85         }
86
87
88         ctx = *_ctx;
89
90         ctx->cfd = _gnutls_cryptodev_fd;
91         ctx->sess.cipher = cipher_map[algorithm];
92         ctx->cryp.iv = ctx->iv;
93
94         return 0;
95 }
96
97 static int
98 aes_gcm_cipher_setkey(void *_ctx, const void *userkey, size_t keysize)
99 {
100         struct cryptodev_gcm_ctx *ctx = _ctx;
101
102         ctx->sess.keylen = keysize;
103         ctx->sess.key = (void *) userkey;
104
105         if (ioctl(ctx->cfd, CIOCGSESSION, &ctx->sess)) {
106                 gnutls_assert();
107                 return GNUTLS_E_CRYPTODEV_IOCTL_ERROR;
108         }
109         ctx->cryp.ses = ctx->sess.ses;
110
111         return 0;
112 }
113
114 static int aes_gcm_setiv(void *_ctx, const void *iv, size_t iv_size)
115 {
116         struct cryptodev_gcm_ctx *ctx = _ctx;
117
118         if (iv_size != GCM_BLOCK_SIZE - 4)
119                 return GNUTLS_E_INVALID_REQUEST;
120
121         memcpy(ctx->iv, iv, GCM_BLOCK_SIZE - 4);
122
123         ctx->cryp.iv = (void *) ctx->iv;
124
125         return 0;
126 }
127
128 static int
129 aes_gcm_encrypt(void *_ctx, const void *src, size_t src_size,
130                 void *dst, size_t dst_size)
131 {
132         struct cryptodev_gcm_ctx *ctx = _ctx;
133
134         /* the GCM in kernel will place the tag after the
135          * encrypted data.
136          */
137         if (dst_size < src_size + GCM_BLOCK_SIZE)
138                 return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
139
140         ctx->cryp.len = src_size;
141         ctx->cryp.src = (void *) src;
142         ctx->cryp.dst = dst;
143         ctx->cryp.op = COP_ENCRYPT;
144
145         ctx->cryp.auth_len = ctx->auth_data_size;
146         ctx->cryp.auth_src = ctx->auth_data;
147
148         if (ioctl(ctx->cfd, CIOCAUTHCRYPT, &ctx->cryp)) {
149                 gnutls_assert();
150                 return GNUTLS_E_CRYPTODEV_IOCTL_ERROR;
151         }
152
153         ctx->cryp.auth_len = 0;
154         ctx->op = 1;
155         memcpy(ctx->tag, &((uint8_t *) dst)[src_size], GCM_BLOCK_SIZE);
156         return 0;
157 }
158
159 static int
160 aes_gcm_decrypt(void *_ctx, const void *src, size_t src_size,
161                 void *dst, size_t dst_size)
162 {
163         struct cryptodev_gcm_ctx *ctx = _ctx;
164
165         /* the GCM in kernel will place the tag after the
166          * encrypted data.
167          */
168         ctx->cryp.len = src_size + GCM_BLOCK_SIZE;
169         ctx->cryp.src = (void *) src;
170         ctx->cryp.dst = dst;
171         ctx->cryp.op = COP_DECRYPT;
172
173         ctx->cryp.auth_len = ctx->auth_data_size;
174         ctx->cryp.auth_src = ctx->auth_data;
175
176         if (ioctl(ctx->cfd, CIOCAUTHCRYPT, &ctx->cryp)) {
177                 gnutls_assert();
178                 return GNUTLS_E_CRYPTODEV_IOCTL_ERROR;
179         }
180
181         ctx->cryp.auth_len = 0;
182         ctx->op = 1;
183         memcpy(ctx->tag, &((uint8_t *) dst)[src_size], GCM_BLOCK_SIZE);
184         return 0;
185 }
186
187 static int aes_gcm_auth(void *_ctx, const void *src, size_t src_size)
188 {
189         struct cryptodev_gcm_ctx *ctx = _ctx;
190
191         ctx->op = 0;
192         ctx->auth_data = (void *) src;
193         ctx->auth_data_size = src_size;
194
195         return 0;
196 }
197
198 static void aes_gcm_tag(void *_ctx, void *tag, size_t tagsize)
199 {
200         struct cryptodev_gcm_ctx *ctx = _ctx;
201
202         if (ctx->op == 0) {
203                 ctx->cryp.len = 0;
204                 ctx->cryp.src = NULL;
205                 ctx->cryp.dst = ctx->tag;
206                 ctx->cryp.op = COP_ENCRYPT;
207
208                 ctx->cryp.auth_len = ctx->auth_data_size;
209                 ctx->cryp.auth_src = ctx->auth_data;
210
211                 if (ioctl(ctx->cfd, CIOCAUTHCRYPT, &ctx->cryp)) {
212                         gnutls_assert();
213                         return;
214                 }
215         }
216
217         memcpy(tag, ctx->tag, tagsize);
218         ctx->op = 0;
219 }
220
221 static const gnutls_crypto_cipher_st cipher_struct = {
222         .init = aes_gcm_cipher_init,
223         .setkey = aes_gcm_cipher_setkey,
224         .setiv = aes_gcm_setiv,
225         .encrypt = aes_gcm_encrypt,
226         .decrypt = aes_gcm_decrypt,
227         .deinit = aes_gcm_deinit,
228         .tag = aes_gcm_tag,
229         .auth = aes_gcm_auth,
230 };
231
232 int _cryptodev_register_gcm_crypto(int cfd)
233 {
234         struct session_op sess;
235         uint8_t fake_key[CRYPTO_CIPHER_MAX_KEY_LEN];
236         unsigned int i;
237         int ret;
238 #ifdef CIOCGSESSINFO
239         struct session_info_op siop;
240
241         memset(&siop, 0, sizeof(siop));
242 #endif
243
244         memset(&sess, 0, sizeof(sess));
245
246         for (i = 0; i < sizeof(cipher_map) / sizeof(cipher_map[0]); i++) {
247                 if (cipher_map[i] == 0)
248                         continue;
249
250                 /* test if a cipher is support it and if yes register it */
251                 sess.cipher = cipher_map[i];
252                 sess.keylen = gnutls_cipher_get_key_size(i);
253                 sess.key = fake_key;
254
255                 if (ioctl(cfd, CIOCGSESSION, &sess)) {
256                         continue;
257                 }
258 #ifdef CIOCGSESSINFO
259                 siop.ses = sess.ses;    /* do not register ciphers that are not hw accelerated */
260                 if (ioctl(cfd, CIOCGSESSINFO, &siop) == 0) {
261                         if (!(siop.flags & SIOP_FLAG_KERNEL_DRIVER_ONLY)) {
262                                 ioctl(cfd, CIOCFSESSION, &sess.ses);
263                                 continue;
264                         }
265                 }
266 #endif
267
268                 ioctl(cfd, CIOCFSESSION, &sess.ses);
269
270                 _gnutls_debug_log("/dev/crypto: registering: %s\n",
271                                   gnutls_cipher_get_name(i));
272                 ret =
273                     gnutls_crypto_single_cipher_register(i, 90,
274                                                          &cipher_struct);
275                 if (ret < 0) {
276                         gnutls_assert();
277                         return ret;
278                 }
279
280         }
281
282         return 0;
283 }
284
285 #endif                          /* CIOCAUTHCRYPT */
286
287 #endif                          /* ENABLE_CRYPTODEV */