aesni.c revision 275732
1/*-
2 * Copyright (c) 2005-2008 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3 * Copyright (c) 2010 Konstantin Belousov <kib@FreeBSD.org>
4 * Copyright (c) 2014 The FreeBSD Foundation
5 * All rights reserved.
6 *
7 * Portions of this software were developed by John-Mark Gurney
8 * under sponsorship of the FreeBSD Foundation and
9 * Rubicon Communications, LLC (Netgate).
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: head/sys/crypto/aesni/aesni.c 275732 2014-12-12 19:56:36Z jmg $");
35
36#include <sys/param.h>
37#include <sys/systm.h>
38#include <sys/kernel.h>
39#include <sys/kobj.h>
40#include <sys/libkern.h>
41#include <sys/lock.h>
42#include <sys/module.h>
43#include <sys/malloc.h>
44#include <sys/rwlock.h>
45#include <sys/bus.h>
46#include <sys/uio.h>
47#include <sys/mbuf.h>
48#include <crypto/aesni/aesni.h>
49#include <cryptodev_if.h>
50#include <opencrypto/gmac.h>
51
52struct aesni_softc {
53	int32_t cid;
54	uint32_t sid;
55	TAILQ_HEAD(aesni_sessions_head, aesni_session) sessions;
56	struct rwlock lock;
57};
58
59static int aesni_newsession(device_t, uint32_t *sidp, struct cryptoini *cri);
60static int aesni_freesession(device_t, uint64_t tid);
61static void aesni_freesession_locked(struct aesni_softc *sc,
62    struct aesni_session *ses);
63static int aesni_cipher_setup(struct aesni_session *ses,
64    struct cryptoini *encini);
65static int aesni_cipher_process(struct aesni_session *ses,
66    struct cryptodesc *enccrd, struct cryptodesc *authcrd, struct cryptop *crp);
67
68MALLOC_DEFINE(M_AESNI, "aesni_data", "AESNI Data");
69
70static void
71aesni_identify(driver_t *drv, device_t parent)
72{
73
74	/* NB: order 10 is so we get attached after h/w devices */
75	if (device_find_child(parent, "aesni", -1) == NULL &&
76	    BUS_ADD_CHILD(parent, 10, "aesni", -1) == 0)
77		panic("aesni: could not attach");
78}
79
80static int
81aesni_probe(device_t dev)
82{
83
84	if ((cpu_feature2 & CPUID2_AESNI) == 0) {
85		device_printf(dev, "No AESNI support.\n");
86		return (EINVAL);
87	}
88
89	if ((cpu_feature2 & CPUID2_SSE41) == 0) {
90		device_printf(dev, "No SSE4.1 support.\n");
91		return (EINVAL);
92	}
93
94	device_set_desc_copy(dev, "AES-CBC,AES-XTS,AES-GCM,AES-ICM");
95	return (0);
96}
97
98static int
99aesni_attach(device_t dev)
100{
101	struct aesni_softc *sc;
102
103	sc = device_get_softc(dev);
104	TAILQ_INIT(&sc->sessions);
105	sc->sid = 1;
106	sc->cid = crypto_get_driverid(dev, CRYPTOCAP_F_HARDWARE |
107	    CRYPTOCAP_F_SYNC);
108	if (sc->cid < 0) {
109		device_printf(dev, "Could not get crypto driver id.\n");
110		return (ENOMEM);
111	}
112
113	rw_init(&sc->lock, "aesni_lock");
114	crypto_register(sc->cid, CRYPTO_AES_CBC, 0, 0);
115	crypto_register(sc->cid, CRYPTO_AES_ICM, 0, 0);
116	crypto_register(sc->cid, CRYPTO_AES_NIST_GCM_16, 0, 0);
117	crypto_register(sc->cid, CRYPTO_AES_128_NIST_GMAC, 0, 0);
118	crypto_register(sc->cid, CRYPTO_AES_192_NIST_GMAC, 0, 0);
119	crypto_register(sc->cid, CRYPTO_AES_256_NIST_GMAC, 0, 0);
120	crypto_register(sc->cid, CRYPTO_AES_XTS, 0, 0);
121	return (0);
122}
123
124static int
125aesni_detach(device_t dev)
126{
127	struct aesni_softc *sc;
128	struct aesni_session *ses;
129
130	sc = device_get_softc(dev);
131	rw_wlock(&sc->lock);
132	TAILQ_FOREACH(ses, &sc->sessions, next) {
133		if (ses->used) {
134			rw_wunlock(&sc->lock);
135			device_printf(dev,
136			    "Cannot detach, sessions still active.\n");
137			return (EBUSY);
138		}
139	}
140	while ((ses = TAILQ_FIRST(&sc->sessions)) != NULL) {
141		TAILQ_REMOVE(&sc->sessions, ses, next);
142		fpu_kern_free_ctx(ses->fpu_ctx);
143		free(ses, M_AESNI);
144	}
145	rw_wunlock(&sc->lock);
146	rw_destroy(&sc->lock);
147	crypto_unregister_all(sc->cid);
148	return (0);
149}
150
151static int
152aesni_newsession(device_t dev, uint32_t *sidp, struct cryptoini *cri)
153{
154	struct aesni_softc *sc;
155	struct aesni_session *ses;
156	struct cryptoini *encini;
157	int error;
158
159	if (sidp == NULL || cri == NULL) {
160		CRYPTDEB("no sidp or cri");
161		return (EINVAL);
162	}
163
164	sc = device_get_softc(dev);
165	ses = NULL;
166	encini = NULL;
167	for (; cri != NULL; cri = cri->cri_next) {
168		switch (cri->cri_alg) {
169		case CRYPTO_AES_CBC:
170		case CRYPTO_AES_ICM:
171		case CRYPTO_AES_XTS:
172		case CRYPTO_AES_NIST_GCM_16:
173			if (encini != NULL) {
174				CRYPTDEB("encini already set");
175				return (EINVAL);
176			}
177			encini = cri;
178			break;
179		case CRYPTO_AES_128_NIST_GMAC:
180		case CRYPTO_AES_192_NIST_GMAC:
181		case CRYPTO_AES_256_NIST_GMAC:
182			/*
183			 * nothing to do here, maybe in the future cache some
184			 * values for GHASH
185			 */
186			break;
187		default:
188			CRYPTDEB("unhandled algorithm");
189			return (EINVAL);
190		}
191	}
192	if (encini == NULL) {
193		CRYPTDEB("no cipher");
194		return (EINVAL);
195	}
196
197	rw_wlock(&sc->lock);
198	/*
199	 * Free sessions goes first, so if first session is used, we need to
200	 * allocate one.
201	 */
202	ses = TAILQ_FIRST(&sc->sessions);
203	if (ses == NULL || ses->used) {
204		ses = malloc(sizeof(*ses), M_AESNI, M_NOWAIT | M_ZERO);
205		if (ses == NULL) {
206			rw_wunlock(&sc->lock);
207			return (ENOMEM);
208		}
209		ses->fpu_ctx = fpu_kern_alloc_ctx(FPU_KERN_NORMAL |
210		    FPU_KERN_NOWAIT);
211		if (ses->fpu_ctx == NULL) {
212			free(ses, M_AESNI);
213			rw_wunlock(&sc->lock);
214			return (ENOMEM);
215		}
216		ses->id = sc->sid++;
217	} else {
218		TAILQ_REMOVE(&sc->sessions, ses, next);
219	}
220	ses->used = 1;
221	TAILQ_INSERT_TAIL(&sc->sessions, ses, next);
222	rw_wunlock(&sc->lock);
223	ses->algo = encini->cri_alg;
224
225	error = aesni_cipher_setup(ses, encini);
226	if (error != 0) {
227		CRYPTDEB("setup failed");
228		rw_wlock(&sc->lock);
229		aesni_freesession_locked(sc, ses);
230		rw_wunlock(&sc->lock);
231		return (error);
232	}
233
234	*sidp = ses->id;
235	return (0);
236}
237
238static void
239aesni_freesession_locked(struct aesni_softc *sc, struct aesni_session *ses)
240{
241	struct fpu_kern_ctx *ctx;
242	uint32_t sid;
243
244	sid = ses->id;
245	TAILQ_REMOVE(&sc->sessions, ses, next);
246	ctx = ses->fpu_ctx;
247	*ses = (struct aesni_session){};
248	ses->id = sid;
249	ses->fpu_ctx = ctx;
250	TAILQ_INSERT_HEAD(&sc->sessions, ses, next);
251}
252
253static int
254aesni_freesession(device_t dev, uint64_t tid)
255{
256	struct aesni_softc *sc;
257	struct aesni_session *ses;
258	uint32_t sid;
259
260	sc = device_get_softc(dev);
261	sid = ((uint32_t)tid) & 0xffffffff;
262	rw_wlock(&sc->lock);
263	TAILQ_FOREACH_REVERSE(ses, &sc->sessions, aesni_sessions_head, next) {
264		if (ses->id == sid)
265			break;
266	}
267	if (ses == NULL) {
268		rw_wunlock(&sc->lock);
269		return (EINVAL);
270	}
271	aesni_freesession_locked(sc, ses);
272	rw_wunlock(&sc->lock);
273	return (0);
274}
275
276static int
277aesni_process(device_t dev, struct cryptop *crp, int hint __unused)
278{
279	struct aesni_softc *sc = device_get_softc(dev);
280	struct aesni_session *ses = NULL;
281	struct cryptodesc *crd, *enccrd, *authcrd;
282	int error, needauth;
283
284	error = 0;
285	enccrd = NULL;
286	authcrd = NULL;
287	needauth = 0;
288
289	/* Sanity check. */
290	if (crp == NULL)
291		return (EINVAL);
292
293	if (crp->crp_callback == NULL || crp->crp_desc == NULL) {
294		error = EINVAL;
295		goto out;
296	}
297
298	for (crd = crp->crp_desc; crd != NULL; crd = crd->crd_next) {
299		switch (crd->crd_alg) {
300		case CRYPTO_AES_CBC:
301		case CRYPTO_AES_ICM:
302		case CRYPTO_AES_XTS:
303			if (enccrd != NULL) {
304				error = EINVAL;
305				goto out;
306			}
307			enccrd = crd;
308			break;
309
310		case CRYPTO_AES_NIST_GCM_16:
311			if (enccrd != NULL) {
312				error = EINVAL;
313				goto out;
314			}
315			enccrd = crd;
316			needauth = 1;
317			break;
318
319		case CRYPTO_AES_128_NIST_GMAC:
320		case CRYPTO_AES_192_NIST_GMAC:
321		case CRYPTO_AES_256_NIST_GMAC:
322			if (authcrd != NULL) {
323				error = EINVAL;
324				goto out;
325			}
326			authcrd = crd;
327			needauth = 1;
328			break;
329
330		default:
331			error = EINVAL;
332			goto out;
333		}
334	}
335
336	if (enccrd == NULL || (needauth && authcrd == NULL)) {
337		error = EINVAL;
338		goto out;
339	}
340
341	/* CBC & XTS can only handle full blocks for now */
342	if ((enccrd->crd_alg == CRYPTO_AES_CBC || enccrd->crd_alg ==
343	    CRYPTO_AES_XTS) && (enccrd->crd_len % AES_BLOCK_LEN) != 0) {
344		error = EINVAL;
345		goto out;
346	}
347
348	rw_rlock(&sc->lock);
349	TAILQ_FOREACH_REVERSE(ses, &sc->sessions, aesni_sessions_head, next) {
350		if (ses->id == (crp->crp_sid & 0xffffffff))
351			break;
352	}
353	rw_runlock(&sc->lock);
354	if (ses == NULL) {
355		error = EINVAL;
356		goto out;
357	}
358
359	error = aesni_cipher_process(ses, enccrd, authcrd, crp);
360	if (error != 0)
361		goto out;
362
363out:
364	crp->crp_etype = error;
365	crypto_done(crp);
366	return (error);
367}
368
369uint8_t *
370aesni_cipher_alloc(struct cryptodesc *enccrd, struct cryptop *crp,
371    int *allocated)
372{
373	struct mbuf *m;
374	struct uio *uio;
375	struct iovec *iov;
376	uint8_t *addr;
377
378	if (crp->crp_flags & CRYPTO_F_IMBUF) {
379		m = (struct mbuf *)crp->crp_buf;
380		if (m->m_next != NULL)
381			goto alloc;
382		addr = mtod(m, uint8_t *);
383	} else if (crp->crp_flags & CRYPTO_F_IOV) {
384		uio = (struct uio *)crp->crp_buf;
385		if (uio->uio_iovcnt != 1)
386			goto alloc;
387		iov = uio->uio_iov;
388		addr = (uint8_t *)iov->iov_base;
389	} else
390		addr = (uint8_t *)crp->crp_buf;
391	*allocated = 0;
392	addr += enccrd->crd_skip;
393	return (addr);
394
395alloc:
396	addr = malloc(enccrd->crd_len, M_AESNI, M_NOWAIT);
397	if (addr != NULL) {
398		*allocated = 1;
399		crypto_copydata(crp->crp_flags, crp->crp_buf, enccrd->crd_skip,
400		    enccrd->crd_len, addr);
401	} else
402		*allocated = 0;
403	return (addr);
404}
405
406static device_method_t aesni_methods[] = {
407	DEVMETHOD(device_identify, aesni_identify),
408	DEVMETHOD(device_probe, aesni_probe),
409	DEVMETHOD(device_attach, aesni_attach),
410	DEVMETHOD(device_detach, aesni_detach),
411
412	DEVMETHOD(cryptodev_newsession, aesni_newsession),
413	DEVMETHOD(cryptodev_freesession, aesni_freesession),
414	DEVMETHOD(cryptodev_process, aesni_process),
415
416	{0, 0},
417};
418
419static driver_t aesni_driver = {
420	"aesni",
421	aesni_methods,
422	sizeof(struct aesni_softc),
423};
424static devclass_t aesni_devclass;
425
426DRIVER_MODULE(aesni, nexus, aesni_driver, aesni_devclass, 0, 0);
427MODULE_VERSION(aesni, 1);
428MODULE_DEPEND(aesni, crypto, 1, 1, 1);
429
430static int
431aesni_cipher_setup(struct aesni_session *ses, struct cryptoini *encini)
432{
433	struct thread *td;
434	int error;
435
436	td = curthread;
437	error = fpu_kern_enter(td, ses->fpu_ctx, FPU_KERN_NORMAL |
438	    FPU_KERN_KTHR);
439	if (error != 0)
440		return (error);
441	error = aesni_cipher_setup_common(ses, encini->cri_key,
442	    encini->cri_klen);
443	fpu_kern_leave(td, ses->fpu_ctx);
444	return (error);
445}
446
447/*
448 * authcrd contains the associated date.
449 */
450static int
451aesni_cipher_process(struct aesni_session *ses, struct cryptodesc *enccrd,
452    struct cryptodesc *authcrd, struct cryptop *crp)
453{
454	uint8_t tag[GMAC_DIGEST_LEN];
455	struct thread *td;
456	uint8_t *buf, *authbuf;
457	int error, allocated, authallocated;
458	int ivlen, encflag;
459
460	encflag = (enccrd->crd_flags & CRD_F_ENCRYPT) == CRD_F_ENCRYPT;
461
462	if ((enccrd->crd_alg == CRYPTO_AES_ICM ||
463	    enccrd->crd_alg == CRYPTO_AES_NIST_GCM_16) &&
464	    (enccrd->crd_flags & CRD_F_IV_EXPLICIT) == 0)
465		return (EINVAL);
466
467	buf = aesni_cipher_alloc(enccrd, crp, &allocated);
468	if (buf == NULL)
469		return (ENOMEM);
470
471	authbuf = NULL;
472	authallocated = 0;
473	if (authcrd != NULL) {
474		authbuf = aesni_cipher_alloc(authcrd, crp, &authallocated);
475		if (authbuf == NULL) {
476			error = ENOMEM;
477			goto out1;
478		}
479	}
480
481	td = curthread;
482	error = fpu_kern_enter(td, ses->fpu_ctx, FPU_KERN_NORMAL |
483	    FPU_KERN_KTHR);
484	if (error != 0)
485		goto out1;
486
487	if ((enccrd->crd_flags & CRD_F_KEY_EXPLICIT) != 0) {
488		error = aesni_cipher_setup_common(ses, enccrd->crd_key,
489		    enccrd->crd_klen);
490		if (error != 0)
491			goto out;
492	}
493
494	/* XXX - validate that enccrd and authcrd have/use same key? */
495	switch (enccrd->crd_alg) {
496	case CRYPTO_AES_CBC:
497	case CRYPTO_AES_ICM:
498		ivlen = AES_BLOCK_LEN;
499		break;
500	case CRYPTO_AES_XTS:
501		ivlen = 8;
502		break;
503	case CRYPTO_AES_NIST_GCM_16:
504		ivlen = 12;	/* should support arbitarily larger */
505		break;
506	}
507
508	/* Setup ses->iv */
509	bzero(ses->iv, sizeof ses->iv);
510	if ((enccrd->crd_flags & CRD_F_IV_EXPLICIT) != 0)
511		bcopy(enccrd->crd_iv, ses->iv, ivlen);
512	else if (encflag && ((enccrd->crd_flags & CRD_F_IV_PRESENT) != 0))
513		arc4rand(ses->iv, ivlen, 0);
514	else
515		crypto_copydata(crp->crp_flags, crp->crp_buf,
516		    enccrd->crd_inject, ivlen, ses->iv);
517
518	if (authcrd != NULL && !encflag)
519		crypto_copydata(crp->crp_flags, crp->crp_buf,
520		    authcrd->crd_inject, GMAC_DIGEST_LEN, tag);
521	else
522		bzero(tag, sizeof tag);
523
524	/* Do work */
525	switch (ses->algo) {
526	case CRYPTO_AES_CBC:
527		if (encflag)
528			aesni_encrypt_cbc(ses->rounds, ses->enc_schedule,
529			    enccrd->crd_len, buf, buf, ses->iv);
530		else
531			aesni_decrypt_cbc(ses->rounds, ses->dec_schedule,
532			    enccrd->crd_len, buf, ses->iv);
533		break;
534	case CRYPTO_AES_ICM:
535		/* encryption & decryption are the same */
536		aesni_encrypt_icm(ses->rounds, ses->enc_schedule,
537		    enccrd->crd_len, buf, buf, ses->iv);
538		break;
539	case CRYPTO_AES_XTS:
540		if (encflag)
541			aesni_encrypt_xts(ses->rounds, ses->enc_schedule,
542			    ses->xts_schedule, enccrd->crd_len, buf, buf,
543			    ses->iv);
544		else
545			aesni_decrypt_xts(ses->rounds, ses->dec_schedule,
546			    ses->xts_schedule, enccrd->crd_len, buf, buf,
547			    ses->iv);
548		break;
549	case CRYPTO_AES_NIST_GCM_16:
550		if (encflag)
551			AES_GCM_encrypt(buf, buf, authbuf, ses->iv, tag,
552			    enccrd->crd_len, authcrd->crd_len, ivlen,
553			    ses->enc_schedule, ses->rounds);
554		else {
555			if (!AES_GCM_decrypt(buf, buf, authbuf, ses->iv, tag,
556			    enccrd->crd_len, authcrd->crd_len, ivlen,
557			    ses->enc_schedule, ses->rounds))
558				error = EBADMSG;
559		}
560		break;
561	}
562
563	if (allocated)
564		crypto_copyback(crp->crp_flags, crp->crp_buf, enccrd->crd_skip,
565		    enccrd->crd_len, buf);
566
567	/*
568	 * OpenBSD doesn't copy this back.  This primes the IV for the next
569	 * chain.  Why do we not do it for decrypt?
570	 */
571	if (encflag && enccrd->crd_alg == CRYPTO_AES_CBC)
572		bcopy(buf + enccrd->crd_len - AES_BLOCK_LEN, ses->iv, AES_BLOCK_LEN);
573
574	if (!error && authcrd != NULL) {
575		crypto_copyback(crp->crp_flags, crp->crp_buf,
576		    authcrd->crd_inject, GMAC_DIGEST_LEN, tag);
577	}
578
579out:
580	fpu_kern_leave(td, ses->fpu_ctx);
581out1:
582	if (allocated) {
583		bzero(buf, enccrd->crd_len);
584		free(buf, M_AESNI);
585	}
586	if (authallocated)
587		free(authbuf, M_AESNI);
588	return (error);
589}
590