1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2020 Netflix, Inc
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer,
11 *    without modification.
12 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
13 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
14 *    redistribution must be conditioned upon including a substantially
15 *    similar Disclaimer requirement for further binary redistribution.
16 *
17 * NO WARRANTY
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
21 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
23 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
26 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28 * THE POSSIBILITY OF SUCH DAMAGES.
29 */
30
31/*
32 * A driver for the OpenCrypto framework which uses assembly routines
33 * from OpenSSL.
34 */
35
36#include <sys/cdefs.h>
37__FBSDID("$FreeBSD$");
38
39#include <sys/types.h>
40#include <sys/bus.h>
41#include <sys/kernel.h>
42#include <sys/malloc.h>
43#include <sys/module.h>
44
45#include <machine/fpu.h>
46
47#include <opencrypto/cryptodev.h>
48#include <opencrypto/xform_auth.h>
49
50#include <crypto/openssl/ossl.h>
51
52#include "cryptodev_if.h"
53
54struct ossl_softc {
55	int32_t sc_cid;
56};
57
58struct ossl_session_hash {
59	struct ossl_hash_context ictx;
60	struct ossl_hash_context octx;
61	struct auth_hash *axf;
62	u_int mlen;
63};
64
65struct ossl_session {
66	struct ossl_session_hash hash;
67};
68
69static MALLOC_DEFINE(M_OSSL, "ossl", "OpenSSL crypto");
70
71static void
72ossl_identify(driver_t *driver, device_t parent)
73{
74
75	if (device_find_child(parent, "ossl", -1) == NULL)
76		BUS_ADD_CHILD(parent, 10, "ossl", -1);
77}
78
79static int
80ossl_probe(device_t dev)
81{
82
83	device_set_desc(dev, "OpenSSL crypto");
84	return (BUS_PROBE_DEFAULT);
85}
86
87static int
88ossl_attach(device_t dev)
89{
90	struct ossl_softc *sc;
91
92	sc = device_get_softc(dev);
93
94	ossl_cpuid();
95	sc->sc_cid = crypto_get_driverid(dev, sizeof(struct ossl_session),
96	    CRYPTOCAP_F_SOFTWARE | CRYPTOCAP_F_SYNC |
97	    CRYPTOCAP_F_ACCEL_SOFTWARE);
98	if (sc->sc_cid < 0) {
99		device_printf(dev, "failed to allocate crypto driver id\n");
100		return (ENXIO);
101	}
102
103	return (0);
104}
105
106static int
107ossl_detach(device_t dev)
108{
109	struct ossl_softc *sc;
110
111	sc = device_get_softc(dev);
112
113	crypto_unregister_all(sc->sc_cid);
114
115	return (0);
116}
117
118static struct auth_hash *
119ossl_lookup_hash(const struct crypto_session_params *csp)
120{
121
122	switch (csp->csp_auth_alg) {
123	case CRYPTO_SHA1:
124	case CRYPTO_SHA1_HMAC:
125		return (&ossl_hash_sha1);
126	case CRYPTO_SHA2_224:
127	case CRYPTO_SHA2_224_HMAC:
128		return (&ossl_hash_sha224);
129	case CRYPTO_SHA2_256:
130	case CRYPTO_SHA2_256_HMAC:
131		return (&ossl_hash_sha256);
132	case CRYPTO_SHA2_384:
133	case CRYPTO_SHA2_384_HMAC:
134		return (&ossl_hash_sha384);
135	case CRYPTO_SHA2_512:
136	case CRYPTO_SHA2_512_HMAC:
137		return (&ossl_hash_sha512);
138	default:
139		return (NULL);
140	}
141}
142
143static int
144ossl_probesession(device_t dev, const struct crypto_session_params *csp)
145{
146
147	if ((csp->csp_flags & ~(CSP_F_SEPARATE_OUTPUT | CSP_F_SEPARATE_AAD)) !=
148	    0)
149		return (EINVAL);
150	switch (csp->csp_mode) {
151	case CSP_MODE_DIGEST:
152		if (ossl_lookup_hash(csp) == NULL)
153			return (EINVAL);
154		break;
155	default:
156		return (EINVAL);
157	}
158
159	return (CRYPTODEV_PROBE_ACCEL_SOFTWARE);
160}
161
162static void
163ossl_setkey_hmac(struct ossl_session *s, const void *key, int klen)
164{
165
166	hmac_init_ipad(s->hash.axf, key, klen, &s->hash.ictx);
167	hmac_init_opad(s->hash.axf, key, klen, &s->hash.octx);
168}
169
170static int
171ossl_newsession(device_t dev, crypto_session_t cses,
172    const struct crypto_session_params *csp)
173{
174	struct ossl_session *s;
175	struct auth_hash *axf;
176
177	s = crypto_get_driver_session(cses);
178
179	axf = ossl_lookup_hash(csp);
180	s->hash.axf = axf;
181	if (csp->csp_auth_mlen == 0)
182		s->hash.mlen = axf->hashsize;
183	else
184		s->hash.mlen = csp->csp_auth_mlen;
185
186	if (csp->csp_auth_klen == 0) {
187		axf->Init(&s->hash.ictx);
188	} else {
189		if (csp->csp_auth_key != NULL) {
190			fpu_kern_enter(curthread, NULL, FPU_KERN_NOCTX);
191			ossl_setkey_hmac(s, csp->csp_auth_key,
192			    csp->csp_auth_klen);
193			fpu_kern_leave(curthread, NULL);
194		}
195	}
196	return (0);
197}
198
199static int
200ossl_process(device_t dev, struct cryptop *crp, int hint)
201{
202	struct ossl_hash_context ctx;
203	char digest[HASH_MAX_LEN];
204	const struct crypto_session_params *csp;
205	struct ossl_session *s;
206	struct auth_hash *axf;
207	int error;
208	bool fpu_entered;
209
210	s = crypto_get_driver_session(crp->crp_session);
211	csp = crypto_get_params(crp->crp_session);
212	axf = s->hash.axf;
213
214	if (is_fpu_kern_thread(0)) {
215		fpu_entered = false;
216	} else {
217		fpu_kern_enter(curthread, NULL, FPU_KERN_NOCTX);
218		fpu_entered = true;
219	}
220
221	if (crp->crp_auth_key != NULL)
222		ossl_setkey_hmac(s, crp->crp_auth_key, csp->csp_auth_klen);
223
224	ctx = s->hash.ictx;
225
226	if (crp->crp_aad != NULL)
227		error = axf->Update(&ctx, crp->crp_aad, crp->crp_aad_length);
228	else
229		error = crypto_apply(crp, crp->crp_aad_start,
230		    crp->crp_aad_length, axf->Update, &ctx);
231	if (error)
232		goto out;
233
234	error = crypto_apply(crp, crp->crp_payload_start,
235	    crp->crp_payload_length, axf->Update, &ctx);
236	if (error)
237		goto out;
238
239	axf->Final(digest, &ctx);
240
241	if (csp->csp_auth_klen != 0) {
242		ctx = s->hash.octx;
243		axf->Update(&ctx, digest, axf->hashsize);
244		axf->Final(digest, &ctx);
245	}
246
247	if (crp->crp_op & CRYPTO_OP_VERIFY_DIGEST) {
248		char digest2[HASH_MAX_LEN];
249
250		crypto_copydata(crp, crp->crp_digest_start, s->hash.mlen,
251		    digest2);
252		if (timingsafe_bcmp(digest, digest2, s->hash.mlen) != 0)
253			error = EBADMSG;
254		explicit_bzero(digest2, sizeof(digest2));
255	} else {
256		crypto_copyback(crp, crp->crp_digest_start, s->hash.mlen,
257		    digest);
258	}
259	explicit_bzero(digest, sizeof(digest));
260
261out:
262	if (fpu_entered)
263		fpu_kern_leave(curthread, NULL);
264
265	crp->crp_etype = error;
266	crypto_done(crp);
267
268	explicit_bzero(&ctx, sizeof(ctx));
269	return (0);
270}
271
272static device_method_t ossl_methods[] = {
273	DEVMETHOD(device_identify,	ossl_identify),
274	DEVMETHOD(device_probe,		ossl_probe),
275	DEVMETHOD(device_attach,	ossl_attach),
276	DEVMETHOD(device_detach,	ossl_detach),
277
278	DEVMETHOD(cryptodev_probesession, ossl_probesession),
279	DEVMETHOD(cryptodev_newsession,	ossl_newsession),
280	DEVMETHOD(cryptodev_process,	ossl_process),
281
282	DEVMETHOD_END
283};
284
285static driver_t ossl_driver = {
286	"ossl",
287	ossl_methods,
288	sizeof(struct ossl_softc)
289};
290
291static devclass_t ossl_devclass;
292
293DRIVER_MODULE(ossl, nexus, ossl_driver, ossl_devclass, NULL, NULL);
294MODULE_VERSION(ossl, 1);
295MODULE_DEPEND(ossl, crypto, 1, 1, 1);
296