xref: /openbsd-src/sys/crypto/crypto.c (revision 3a3fbb3f2e2521ab7c4a56b7ff7462ebd9095ec5)
1 /*	$OpenBSD: crypto.c,v 1.30 2001/11/13 18:54:32 deraadt Exp $	*/
2 /*
3  * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu)
4  *
5  * This code was written by Angelos D. Keromytis in Athens, Greece, in
6  * February 2000. Network Security Technologies Inc. (NSTI) kindly
7  * supported the development of this code.
8  *
9  * Copyright (c) 2000, 2001 Angelos D. Keromytis
10  *
11  * Permission to use, copy, and modify this software with or without fee
12  * is hereby granted, provided that this entire notice is included in
13  * all source code copies of any software which is or includes a copy or
14  * modification of this software.
15  *
16  * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
17  * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
18  * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
19  * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
20  * PURPOSE.
21  */
22 
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/malloc.h>
26 #include <sys/proc.h>
27 #include <sys/pool.h>
28 #include <crypto/cryptodev.h>
29 
30 struct cryptocap *crypto_drivers = NULL;
31 int crypto_drivers_num = 0;
32 
33 struct pool cryptop_pool;
34 struct pool cryptodesc_pool;
35 int crypto_pool_initialized = 0;
36 
37 struct cryptop *crp_req_queue = NULL;
38 struct cryptop **crp_req_queue_tail = NULL;
39 
40 /*
41  * Create a new session.
42  */
43 int
44 crypto_newsession(u_int64_t *sid, struct cryptoini *cri, int hard)
45 {
46 	struct cryptoini *cr;
47 	u_int32_t hid, lid;
48 	int err, s;
49 
50 	if (crypto_drivers == NULL)
51 		return EINVAL;
52 
53 	s = splimp();
54 
55 	/*
56 	 * The algorithm we use here is pretty stupid; just use the
57 	 * first driver that supports all the algorithms we need.
58 	 *
59 	 * XXX We need more smarts here (in real life too, but that's
60 	 * XXX another story altogether).
61 	 */
62 
63 	for (hid = 0; hid < crypto_drivers_num; hid++) {
64 		/*
65 		 * If it's not initialized or has remaining sessions
66 		 * referencing it, skip.
67 		 */
68 		if (crypto_drivers[hid].cc_newsession == NULL ||
69 		    (crypto_drivers[hid].cc_flags & CRYPTOCAP_F_CLEANUP))
70 			continue;
71 
72 		/* Hardware requested -- ignore software drivers. */
73 		if (hard &&
74 		    (crypto_drivers[hid].cc_flags & CRYPTOCAP_F_SOFTWARE))
75 			continue;
76 
77 		/* See if all the algorithms are supported. */
78 		for (cr = cri; cr; cr = cr->cri_next)
79 			if (crypto_drivers[hid].cc_alg[cr->cri_alg] == 0)
80 				break;
81 
82 		/* Ok, all algorithms are supported. */
83 		if (cr == NULL)
84 			break;
85 	}
86 
87 	/*
88 	 * Can't do everything in one session.
89 	 *
90 	 * XXX Fix this. We need to inject a "virtual" session layer right
91 	 * XXX about here.
92 	 */
93 
94 	if (hid == crypto_drivers_num) {
95 		splx(s);
96 		return EINVAL;
97 	}
98 
99 	/* Call the driver initialization routine. */
100 	lid = hid; /* Pass the driver ID. */
101 	err = crypto_drivers[hid].cc_newsession(&lid, cri);
102 	if (err == 0) {
103 		(*sid) = hid;
104 		(*sid) <<= 32;
105 		(*sid) |= (lid & 0xffffffff);
106 		crypto_drivers[hid].cc_sessions++;
107 	}
108 
109 	splx(s);
110 	return err;
111 }
112 
113 /*
114  * Delete an existing session (or a reserved session on an unregistered
115  * driver).
116  */
117 int
118 crypto_freesession(u_int64_t sid)
119 {
120 	int err = 0, s;
121 	u_int32_t hid;
122 
123 	if (crypto_drivers == NULL)
124 		return EINVAL;
125 
126 	/* Determine two IDs. */
127 	hid = (sid >> 32) & 0xffffffff;
128 
129 	if (hid >= crypto_drivers_num)
130 		return ENOENT;
131 
132 	s = splimp();
133 
134 	if (crypto_drivers[hid].cc_sessions)
135 		crypto_drivers[hid].cc_sessions--;
136 
137 	/* Call the driver cleanup routine, if available. */
138 	if (crypto_drivers[hid].cc_freesession)
139 		err = crypto_drivers[hid].cc_freesession(sid);
140 
141 	/*
142 	 * If this was the last session of a driver marked as invalid,
143 	 * make the entry available for reuse.
144 	 */
145 	if ((crypto_drivers[hid].cc_flags & CRYPTOCAP_F_CLEANUP) &&
146 	    crypto_drivers[hid].cc_sessions == 0)
147 		bzero(&crypto_drivers[hid], sizeof(struct cryptocap));
148 
149 	splx(s);
150 	return err;
151 }
152 
153 /*
154  * Find an empty slot.
155  */
156 int32_t
157 crypto_get_driverid(u_int8_t flags)
158 {
159 	struct cryptocap *newdrv;
160 	int i, s = splimp();
161 
162 	if (crypto_drivers_num == 0) {
163 		crypto_drivers_num = CRYPTO_DRIVERS_INITIAL;
164 		crypto_drivers = malloc(crypto_drivers_num *
165 		    sizeof(struct cryptocap), M_CRYPTO_DATA, M_NOWAIT);
166 		if (crypto_drivers == NULL) {
167 			splx(s);
168 			crypto_drivers_num = 0;
169 			return -1;
170 		}
171 
172 		bzero(crypto_drivers, crypto_drivers_num *
173 		    sizeof(struct cryptocap));
174 	}
175 
176 	for (i = 0; i < crypto_drivers_num; i++) {
177 		if (crypto_drivers[i].cc_process == NULL &&
178 		    !(crypto_drivers[i].cc_flags & CRYPTOCAP_F_CLEANUP) &&
179 		    crypto_drivers[i].cc_sessions == 0) {
180 			crypto_drivers[i].cc_sessions = 1; /* Mark */
181 			crypto_drivers[i].cc_flags = flags;
182 			splx(s);
183 			return i;
184 		}
185 	}
186 
187 	/* Out of entries, allocate some more. */
188 	if (i == crypto_drivers_num) {
189 		/* Be careful about wrap-around. */
190 		if (2 * crypto_drivers_num <= crypto_drivers_num) {
191 			splx(s);
192 			return -1;
193 		}
194 
195 		newdrv = malloc(2 * crypto_drivers_num *
196 		    sizeof(struct cryptocap), M_CRYPTO_DATA, M_NOWAIT);
197 		if (newdrv == NULL) {
198 			splx(s);
199 			return -1;
200 		}
201 
202 		bcopy(crypto_drivers, newdrv,
203 		    crypto_drivers_num * sizeof(struct cryptocap));
204 		bzero(&newdrv[crypto_drivers_num],
205 		    crypto_drivers_num * sizeof(struct cryptocap));
206 
207 		newdrv[i].cc_sessions = 1; /* Mark */
208 		newdrv[i].cc_flags = flags;
209 		crypto_drivers_num *= 2;
210 
211 		free(crypto_drivers, M_CRYPTO_DATA);
212 		crypto_drivers = newdrv;
213 		splx(s);
214 		return i;
215 	}
216 
217 	/* Shouldn't really get here... */
218 	splx(s);
219 	return -1;
220 }
221 
222 /*
223  * Register a crypto driver. It should be called once for each algorithm
224  * supported by the driver.
225  */
226 int
227 crypto_register(u_int32_t driverid, int alg, u_int16_t maxoplen,
228     u_int32_t flags,
229     int (*newses)(u_int32_t *, struct cryptoini *),
230     int (*freeses)(u_int64_t), int (*process)(struct cryptop *))
231 {
232 	int s;
233 
234 	if (driverid >= crypto_drivers_num || alg <= 0 ||
235 	    alg > CRYPTO_ALGORITHM_MAX || crypto_drivers == NULL)
236 		return EINVAL;
237 
238 	s = splimp();
239 
240 	/*
241 	 * XXX Do some performance testing to determine placing.
242 	 * XXX We probably need an auxiliary data structure that describes
243 	 * XXX relative performances.
244 	 */
245 
246 	crypto_drivers[driverid].cc_alg[alg] =
247 	    flags | CRYPTO_ALG_FLAG_SUPPORTED;
248 
249 	crypto_drivers[driverid].cc_max_op_len[alg] = maxoplen;
250 
251 	if (crypto_drivers[driverid].cc_process == NULL) {
252 		crypto_drivers[driverid].cc_newsession = newses;
253 		crypto_drivers[driverid].cc_process = process;
254 		crypto_drivers[driverid].cc_freesession = freeses;
255 		crypto_drivers[driverid].cc_sessions = 0; /* Unmark */
256 	}
257 
258 	splx(s);
259 	return 0;
260 }
261 
262 /*
263  * Unregister a crypto driver. If there are pending sessions using it,
264  * leave enough information around so that subsequent calls using those
265  * sessions will correctly detect the driver being unregistered and reroute
266  * the request.
267  */
268 int
269 crypto_unregister(u_int32_t driverid, int alg)
270 {
271 	int i, s = splimp();
272 	u_int32_t ses;
273 
274 	/* Sanity checks */
275 	if (driverid >= crypto_drivers_num || alg <= 0 ||
276 	    alg > CRYPTO_ALGORITHM_MAX || crypto_drivers == NULL ||
277 	    crypto_drivers[driverid].cc_alg[alg] == 0) {
278 		splx(s);
279 		return EINVAL;
280 	}
281 
282 	crypto_drivers[driverid].cc_alg[alg] = 0;
283 	crypto_drivers[driverid].cc_max_op_len[alg] = 0;
284 
285 	/* Was this the last algorithm ? */
286 	for (i = 1; i <= CRYPTO_ALGORITHM_MAX; i++)
287 		if (crypto_drivers[driverid].cc_alg[i] != 0)
288 			break;
289 
290 	if (i == CRYPTO_ALGORITHM_MAX + 1) {
291 		ses = crypto_drivers[driverid].cc_sessions;
292 		bzero(&crypto_drivers[driverid], sizeof(struct cryptocap));
293 		if (ses != 0) {
294 			/*
295 			 * If there are pending sessions, just mark as invalid.
296 			 */
297 			crypto_drivers[driverid].cc_flags |= CRYPTOCAP_F_CLEANUP;
298 			crypto_drivers[driverid].cc_sessions = ses;
299 		}
300 	}
301 	splx(s);
302 	return 0;
303 }
304 
305 /*
306  * Add crypto request to a queue, to be processed by a kernel thread.
307  */
308 int
309 crypto_dispatch(struct cryptop *crp)
310 {
311 	int s = splimp();
312 
313 	if (crp_req_queue == NULL) {
314 		crp_req_queue = crp;
315 		crp_req_queue_tail = &(crp->crp_next);
316 		splx(s);
317 		wakeup((caddr_t) &crp_req_queue);
318 	} else {
319 		*crp_req_queue_tail = crp;
320 		crp_req_queue_tail = &(crp->crp_next);
321 		splx(s);
322 	}
323 	return 0;
324 }
325 
326 /*
327  * Dispatch a crypto request to the appropriate crypto devices.
328  */
329 int
330 crypto_invoke(struct cryptop *crp)
331 {
332 	struct cryptodesc *crd;
333 	u_int64_t nid;
334 	u_int32_t hid;
335 
336 	/* Sanity checks. */
337 	if (crp == NULL || crp->crp_callback == NULL)
338 		return EINVAL;
339 
340 	if (crp->crp_desc == NULL || crypto_drivers == NULL) {
341 		crp->crp_etype = EINVAL;
342 		crypto_done(crp);
343 		return 0;
344 	}
345 
346 	hid = (crp->crp_sid >> 32) & 0xffffffff;
347 	if (hid >= crypto_drivers_num) {
348 		/* Migrate session. */
349 		for (crd = crp->crp_desc; crd->crd_next; crd = crd->crd_next)
350 			crd->CRD_INI.cri_next = &(crd->crd_next->CRD_INI);
351 
352 		if (crypto_newsession(&nid, &(crp->crp_desc->CRD_INI), 0) == 0)
353 			crp->crp_sid = nid;
354 
355 		crp->crp_etype = EAGAIN;
356 		crypto_done(crp);
357 		return 0;
358 	}
359 
360 	if (crypto_drivers[hid].cc_flags & CRYPTOCAP_F_CLEANUP)
361 		crypto_freesession(crp->crp_sid);
362 
363 	if (crypto_drivers[hid].cc_process == NULL) {
364 		/* Migrate session. */
365 		for (crd = crp->crp_desc; crd->crd_next; crd = crd->crd_next)
366 			crd->CRD_INI.cri_next = &(crd->crd_next->CRD_INI);
367 
368 		if (crypto_newsession(&nid, &(crp->crp_desc->CRD_INI), 0) == 0)
369 			crp->crp_sid = nid;
370 
371 		crp->crp_etype = EAGAIN;
372 		crypto_done(crp);
373 		return 0;
374 	}
375 
376 	crypto_drivers[hid].cc_process(crp);
377 	return 0;
378 }
379 
380 /*
381  * Release a set of crypto descriptors.
382  */
383 void
384 crypto_freereq(struct cryptop *crp)
385 {
386 	struct cryptodesc *crd;
387 	int s;
388 
389 	if (crp == NULL)
390 		return;
391 
392 	s = splimp();
393 
394 	while ((crd = crp->crp_desc) != NULL) {
395 		crp->crp_desc = crd->crd_next;
396 		pool_put(&cryptodesc_pool, crd);
397 	}
398 
399 	pool_put(&cryptop_pool, crp);
400 	splx(s);
401 }
402 
403 /*
404  * Acquire a set of crypto descriptors.
405  */
406 struct cryptop *
407 crypto_getreq(int num)
408 {
409 	struct cryptodesc *crd;
410 	struct cryptop *crp;
411 	int s = splimp();
412 
413 	if (crypto_pool_initialized == 0) {
414 		pool_init(&cryptop_pool, sizeof(struct cryptop), 0, 0,
415 		    PR_FREEHEADER, "cryptop", 0, NULL, NULL, M_CRYPTO_OPS);
416 		pool_init(&cryptodesc_pool, sizeof(struct cryptodesc), 0, 0,
417 		    PR_FREEHEADER, "cryptodesc", 0, NULL, NULL, M_CRYPTO_OPS);
418 		crypto_pool_initialized = 1;
419 	}
420 
421 	crp = pool_get(&cryptop_pool, 0);
422 	if (crp == NULL) {
423 		splx(s);
424 		return NULL;
425 	}
426 	bzero(crp, sizeof(struct cryptop));
427 
428 	while (num--) {
429 		crd = pool_get(&cryptodesc_pool, 0);
430 		if (crd == NULL) {
431 			splx(s);
432 			crypto_freereq(crp);
433 			return NULL;
434 		}
435 
436 		bzero(crd, sizeof(struct cryptodesc));
437 		crd->crd_next = crp->crp_desc;
438 		crp->crp_desc = crd;
439 	}
440 
441 	splx(s);
442 	return crp;
443 }
444 
445 /*
446  * Crypto thread, runs as a kernel thread to process crypto requests.
447  */
448 void
449 crypto_thread(void)
450 {
451 	struct cryptop *crp;
452 	int s;
453 
454 	s = splimp();
455 
456 	for (;;) {
457 		crp = crp_req_queue;
458 		if (crp == NULL) {
459 			(void) tsleep(&crp_req_queue, PLOCK, "crypto_wait", 0);
460 			continue;
461 		}
462 
463 		/* Remove from the queue. */
464 		crp_req_queue = crp->crp_next;
465 		crypto_invoke(crp);
466 	}
467 }
468 
469 /*
470  * Invoke the callback on behalf of the driver.
471  */
472 void
473 crypto_done(struct cryptop *crp)
474 {
475 	crp->crp_callback(crp);
476 }
477 
478 /*
479  * Return SYMMETRIC or PUBLIC_KEY, depending on the algorithm type.
480  */
481 int
482 crypto_check_alg(struct cryptoini *cri)
483 {
484 	switch (cri->cri_alg)
485 	{
486 	case CRYPTO_DES_CBC:
487 	case CRYPTO_3DES_CBC:
488 	case CRYPTO_BLF_CBC:
489 	case CRYPTO_CAST_CBC:
490 	case CRYPTO_SKIPJACK_CBC:
491 	case CRYPTO_RIJNDAEL128_CBC:
492 	case CRYPTO_ARC4:
493 		return SYMMETRIC;
494 	case CRYPTO_DH_SEND:
495 	case CRYPTO_DH_RECEIVE:
496 	case CRYPTO_RSA_ENCRYPT:
497 	case CRYPTO_RSA_DECRYPT:
498 	case CRYPTO_DSA_SIGN:
499 	case CRYPTO_DSA_VERIFY:
500 		return PUBLIC_KEY;
501 	}
502 
503 #ifdef DIAGNOSTIC
504 	panic("crypto_check_alg: unknown algorithm %d", cri->cri_alg);
505 #endif
506 	return -1;
507 }
508