xref: /openbsd-src/sys/crypto/crypto.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: crypto.c,v 1.26 2001/08/05 09:36:38 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(void)
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 			splx(s);
182 			return i;
183 		}
184 	}
185 
186 	/* Out of entries, allocate some more. */
187 	if (i == crypto_drivers_num) {
188 		/* Be careful about wrap-around. */
189 		if (2 * crypto_drivers_num <= crypto_drivers_num) {
190 			splx(s);
191 			return -1;
192 		}
193 
194 		newdrv = malloc(2 * crypto_drivers_num *
195 		    sizeof(struct cryptocap), M_CRYPTO_DATA, M_NOWAIT);
196 		if (newdrv == NULL) {
197 			splx(s);
198 			return -1;
199 		}
200 
201 		bcopy(crypto_drivers, newdrv,
202 		    crypto_drivers_num * sizeof(struct cryptocap));
203 		bzero(&newdrv[crypto_drivers_num],
204 		    crypto_drivers_num * sizeof(struct cryptocap));
205 
206 		newdrv[i].cc_sessions = 1; /* Mark */
207 		crypto_drivers_num *= 2;
208 
209 		free(crypto_drivers, M_CRYPTO_DATA);
210 		crypto_drivers = newdrv;
211 		splx(s);
212 		return i;
213 	}
214 
215 	/* Shouldn't really get here... */
216 	splx(s);
217 	return -1;
218 }
219 
220 /*
221  * Register a crypto driver. It should be called once for each algorithm
222  * supported by the driver.
223  */
224 int
225 crypto_register(u_int32_t driverid, int alg, u_int16_t maxoplen,
226     u_int32_t flags,
227     int (*newses)(u_int32_t *, struct cryptoini *),
228     int (*freeses)(u_int64_t), int (*process)(struct cryptop *))
229 {
230 	int s;
231 
232 	if (driverid >= crypto_drivers_num || alg <= 0 ||
233 	    alg > CRYPTO_ALGORITHM_MAX || crypto_drivers == NULL)
234 		return EINVAL;
235 
236 	s = splimp();
237 
238 	/*
239 	 * XXX Do some performance testing to determine placing.
240 	 * XXX We probably need an auxiliary data structure that describes
241 	 * XXX relative performances.
242 	 */
243 
244 	crypto_drivers[driverid].cc_alg[alg] =
245 	    flags | CRYPTO_ALG_FLAG_SUPPORTED;
246 
247 	crypto_drivers[driverid].cc_max_op_len[alg] = maxoplen;
248 
249 	if (crypto_drivers[driverid].cc_process == NULL) {
250 		crypto_drivers[driverid].cc_newsession = newses;
251 		crypto_drivers[driverid].cc_process = process;
252 		crypto_drivers[driverid].cc_freesession = freeses;
253 		crypto_drivers[driverid].cc_sessions = 0; /* Unmark */
254 	}
255 
256 	splx(s);
257 	return 0;
258 }
259 
260 /*
261  * Unregister a crypto driver. If there are pending sessions using it,
262  * leave enough information around so that subsequent calls using those
263  * sessions will correctly detect the driver being unregistered and reroute
264  * the request.
265  */
266 int
267 crypto_unregister(u_int32_t driverid, int alg)
268 {
269 	int i, s = splimp();
270 	u_int32_t ses;
271 
272 	/* Sanity checks */
273 	if (driverid >= crypto_drivers_num || alg <= 0 ||
274 	    alg > CRYPTO_ALGORITHM_MAX || crypto_drivers == NULL ||
275 	    crypto_drivers[driverid].cc_alg[alg] == 0) {
276 		splx(s);
277 		return EINVAL;
278 	}
279 
280 	crypto_drivers[driverid].cc_alg[alg] = 0;
281 	crypto_drivers[driverid].cc_max_op_len[alg] = 0;
282 
283 	/* Was this the last algorithm ? */
284 	for (i = 1; i <= CRYPTO_ALGORITHM_MAX; i++)
285 		if (crypto_drivers[driverid].cc_alg[i] != 0)
286 			break;
287 
288 	if (i == CRYPTO_ALGORITHM_MAX + 1) {
289 		ses = crypto_drivers[driverid].cc_sessions;
290 		bzero(&crypto_drivers[driverid], sizeof(struct cryptocap));
291 		if (ses != 0) {
292 			/*
293 			 * If there are pending sessions, just mark as invalid.
294 			 */
295 			crypto_drivers[driverid].cc_flags |= CRYPTOCAP_F_CLEANUP;
296 			crypto_drivers[driverid].cc_sessions = ses;
297 		}
298 	}
299 	splx(s);
300 	return 0;
301 }
302 
303 /*
304  * Add crypto request to a queue, to be processed by a kernel thread.
305  */
306 int
307 crypto_dispatch(struct cryptop *crp)
308 {
309 	int s = splimp();
310 
311 	if (crp_req_queue == NULL) {
312 		crp_req_queue = crp;
313 		crp_req_queue_tail = &(crp->crp_next);
314 		splx(s);
315 		wakeup((caddr_t) &crp_req_queue);
316 	} else {
317 		*crp_req_queue_tail = crp;
318 		crp_req_queue_tail = &(crp->crp_next);
319 		splx(s);
320 	}
321 	return 0;
322 }
323 
324 /*
325  * Dispatch a crypto request to the appropriate crypto devices.
326  */
327 int
328 crypto_invoke(struct cryptop *crp)
329 {
330 	struct cryptodesc *crd;
331 	u_int64_t nid;
332 	u_int32_t hid;
333 
334 	/* Sanity checks. */
335 	if (crp == NULL || crp->crp_callback == NULL)
336 		return EINVAL;
337 
338 	if (crp->crp_desc == NULL || crypto_drivers == NULL) {
339 		crp->crp_etype = EINVAL;
340 		crypto_done(crp);
341 		return 0;
342 	}
343 
344 	hid = (crp->crp_sid >> 32) & 0xffffffff;
345 	if (hid >= crypto_drivers_num) {
346 		/* Migrate session. */
347 		for (crd = crp->crp_desc; crd->crd_next; crd = crd->crd_next)
348 			crd->CRD_INI.cri_next = &(crd->crd_next->CRD_INI);
349 
350 		if (crypto_newsession(&nid, &(crp->crp_desc->CRD_INI), 0) == 0)
351 			crp->crp_sid = nid;
352 
353 		crp->crp_etype = EAGAIN;
354 		crypto_done(crp);
355 		return 0;
356 	}
357 
358 	if (crypto_drivers[hid].cc_flags & CRYPTOCAP_F_CLEANUP)
359 		crypto_freesession(crp->crp_sid);
360 
361 	if (crypto_drivers[hid].cc_process == NULL) {
362 		/* Migrate session. */
363 		for (crd = crp->crp_desc; crd->crd_next; crd = crd->crd_next)
364 			crd->CRD_INI.cri_next = &(crd->crd_next->CRD_INI);
365 
366 		if (crypto_newsession(&nid, &(crp->crp_desc->CRD_INI), 0) == 0)
367 			crp->crp_sid = nid;
368 
369 		crp->crp_etype = EAGAIN;
370 		crypto_done(crp);
371 		return 0;
372 	}
373 
374 	crypto_drivers[hid].cc_process(crp);
375 	return 0;
376 }
377 
378 /*
379  * Release a set of crypto descriptors.
380  */
381 void
382 crypto_freereq(struct cryptop *crp)
383 {
384 	struct cryptodesc *crd;
385 	int s;
386 
387 	if (crp == NULL)
388 		return;
389 
390 	s = splimp();
391 
392 	while ((crd = crp->crp_desc) != NULL) {
393 		crp->crp_desc = crd->crd_next;
394 		pool_put(&cryptodesc_pool, crd);
395 	}
396 
397 	pool_put(&cryptop_pool, crp);
398 	splx(s);
399 }
400 
401 /*
402  * Acquire a set of crypto descriptors.
403  */
404 struct cryptop *
405 crypto_getreq(int num)
406 {
407 	struct cryptodesc *crd;
408 	struct cryptop *crp;
409 	int s = splimp();
410 
411 	if (crypto_pool_initialized == 0) {
412 		pool_init(&cryptop_pool, sizeof(struct cryptop), 0, 0,
413 		    PR_FREEHEADER, "cryptop", 0, NULL, NULL, M_CRYPTO_OPS);
414 		pool_init(&cryptodesc_pool, sizeof(struct cryptodesc), 0, 0,
415 		    PR_FREEHEADER, "cryptodesc", 0, NULL, NULL, M_CRYPTO_OPS);
416 		crypto_pool_initialized = 1;
417 	}
418 
419 	crp = pool_get(&cryptop_pool, 0);
420 	if (crp == NULL) {
421 		splx(s);
422 		return NULL;
423 	}
424 	bzero(crp, sizeof(struct cryptop));
425 
426 	while (num--) {
427 		crd = pool_get(&cryptodesc_pool, 0);
428 		if (crd == NULL) {
429 			splx(s);
430 			crypto_freereq(crp);
431 			return NULL;
432 		}
433 
434 		bzero(crd, sizeof(struct cryptodesc));
435 		crd->crd_next = crp->crp_desc;
436 		crp->crp_desc = crd;
437 	}
438 
439 	splx(s);
440 	return crp;
441 }
442 
443 /*
444  * Crypto thread, runs as a kernel thread to process crypto requests.
445  */
446 void
447 crypto_thread(void)
448 {
449 	struct cryptop *crp;
450 	int s;
451 
452 	s = splimp();
453 
454 	for (;;) {
455 		crp = crp_req_queue;
456 		if (crp == NULL) {
457 			(void) tsleep(&crp_req_queue, PLOCK, "crypto_wait", 0);
458 			continue;
459 		}
460 
461 		/* Remove from the queue. */
462 		crp_req_queue = crp->crp_next;
463 		crypto_invoke(crp);
464 	}
465 }
466 
467 /*
468  * Invoke the callback on behalf of the driver.
469  */
470 void
471 crypto_done(struct cryptop *crp)
472 {
473 	crp->crp_callback(crp);
474 }
475 
476 /*
477  * Return SYMMETRIC or PUBLIC_KEY, depending on the algorithm type.
478  */
479 int
480 crypto_check_alg(struct cryptoini *cri)
481 {
482 	switch (cri->cri_alg)
483 	{
484 	case CRYPTO_DES_CBC:
485 	case CRYPTO_3DES_CBC:
486 	case CRYPTO_BLF_CBC:
487 	case CRYPTO_CAST_CBC:
488 	case CRYPTO_SKIPJACK_CBC:
489 	case CRYPTO_RIJNDAEL128_CBC:
490 	case CRYPTO_ARC4:
491 		return SYMMETRIC;
492 	case CRYPTO_DH_SEND:
493 	case CRYPTO_DH_RECEIVE:
494 	case CRYPTO_RSA_ENCRYPT:
495 	case CRYPTO_RSA_DECRYPT:
496 	case CRYPTO_DSA_SIGN:
497 	case CRYPTO_DSA_VERIFY:
498 		return PUBLIC_KEY;
499 	}
500 
501 #ifdef DIAGNOSTIC
502 	panic("crypto_check_alg: unknown algorithm %d", cri->cri_alg);
503 #endif
504 	return -1;
505 }
506