xref: /dragonfly/lib/libdmsg/crypto.c (revision aeaecd48)
1 /*
2  * Copyright (c) 2011-2012 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@dragonflybsd.org>
6  * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org>
7  * by Alex Hornung <alexh@dragonflybsd.org>
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  * 3. Neither the name of The DragonFly Project nor the names of its
20  *    contributors may be used to endorse or promote products derived
21  *    from this software without specific, prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
27  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #include "dmsg_local.h"
38 
39 /*
40  * Setup crypto for pthreads
41  */
42 static pthread_mutex_t *crypto_locks;
43 int crypto_count;
44 
45 static int dmsg_crypto_gcm_init(dmsg_ioq_t *, char *, int, char *, int, int);
46 static int dmsg_crypto_gcm_encrypt_chunk(dmsg_ioq_t *, char *, char *, int, int *);
47 static int dmsg_crypto_gcm_decrypt_chunk(dmsg_ioq_t *, char *, char *, int, int *);
48 
49 /*
50  * NOTE: the order of this table needs to match the DMSG_CRYPTO_ALGO_*_IDX
51  *       defines in network.h.
52  */
53 static struct crypto_algo crypto_algos[] = {
54 	{
55 		.name      = "aes-256-gcm",
56 		.keylen    = DMSG_CRYPTO_GCM_KEY_SIZE,
57 		.taglen    = DMSG_CRYPTO_GCM_TAG_SIZE,
58 		.init      = dmsg_crypto_gcm_init,
59 		.enc_chunk = dmsg_crypto_gcm_encrypt_chunk,
60 		.dec_chunk = dmsg_crypto_gcm_decrypt_chunk
61 	},
62 	{ NULL, 0, 0, NULL, NULL, NULL }
63 };
64 
65 static
66 unsigned long
67 dmsg_crypto_id_callback(void)
68 {
69 	return ((unsigned long)(uintptr_t)pthread_self());
70 }
71 
72 static
73 void
74 dmsg_crypto_locking_callback(int mode, int type,
75 				const char *file __unused, int line __unused)
76 {
77 	assert(type >= 0 && type < crypto_count);
78 	if (mode & CRYPTO_LOCK) {
79 		pthread_mutex_lock(&crypto_locks[type]);
80 	} else {
81 		pthread_mutex_unlock(&crypto_locks[type]);
82 	}
83 }
84 
85 void
86 dmsg_crypto_setup(void)
87 {
88 	crypto_count = CRYPTO_num_locks();
89 	crypto_locks = calloc(crypto_count, sizeof(crypto_locks[0]));
90 	CRYPTO_set_id_callback(dmsg_crypto_id_callback);
91 	CRYPTO_set_locking_callback(dmsg_crypto_locking_callback);
92 }
93 
94 static
95 int
96 dmsg_crypto_gcm_init(dmsg_ioq_t *ioq, char *key, int klen,
97 			char *iv_fixed, int ivlen, int enc)
98 {
99 	int i, ok;
100 
101 	if (klen < DMSG_CRYPTO_GCM_KEY_SIZE ||
102 	    ivlen < DMSG_CRYPTO_GCM_IV_FIXED_SIZE) {
103 		dm_printf(1, "%s\n", "Not enough key or iv material");
104 		return -1;
105 	}
106 
107 	dm_printf(6, "%s key: ", enc ? "Encryption" : "Decryption");
108 	for (i = 0; i < DMSG_CRYPTO_GCM_KEY_SIZE; ++i)
109 		dmx_printf(6, "%02x", (unsigned char)key[i]);
110 	dmx_printf(6, "%s\n", "");
111 
112 	dm_printf(6, "%s iv:  ", enc ? "Encryption" : "Decryption");
113 	for (i = 0; i < DMSG_CRYPTO_GCM_IV_FIXED_SIZE; ++i)
114 		dmx_printf(6, "%02x", (unsigned char)iv_fixed[i]);
115 	dmx_printf(6, "%s\n", " (fixed part only)");
116 
117 	EVP_CIPHER_CTX_init(&ioq->ctx);
118 
119 	if (enc)
120 		ok = EVP_EncryptInit_ex(&ioq->ctx, EVP_aes_256_gcm(), NULL,
121 					key, NULL);
122 	else
123 		ok = EVP_DecryptInit_ex(&ioq->ctx, EVP_aes_256_gcm(), NULL,
124 					key, NULL);
125 	if (!ok)
126 		goto fail;
127 
128 	/*
129 	 * According to the original Galois/Counter Mode of Operation (GCM)
130 	 * proposal, only IVs that are exactly 96 bits get used without any
131 	 * further processing. Other IV sizes cause the GHASH() operation
132 	 * to be applied to the IV, which is more costly.
133 	 *
134 	 * The NIST SP 800-38D also recommends using a 96 bit IV for the same
135 	 * reasons. We actually follow the deterministic construction
136 	 * recommended in NIST SP 800-38D with a 64 bit invocation field as an
137 	 * integer counter and a random, session-specific fixed field.
138 	 *
139 	 * This means that we can essentially use the same session key and
140 	 * IV fixed field for up to 2^64 invocations of the authenticated
141 	 * encryption or decryption.
142 	 *
143 	 * With a chunk size of 64 bytes, this adds up to 1 zettabyte of
144 	 * traffic.
145 	 */
146 	ok = EVP_CIPHER_CTX_ctrl(&ioq->ctx, EVP_CTRL_GCM_SET_IVLEN,
147 				 DMSG_CRYPTO_GCM_IV_SIZE, NULL);
148 	if (!ok)
149 		goto fail;
150 
151 	memset(ioq->iv, 0, DMSG_CRYPTO_GCM_IV_SIZE);
152 	memcpy(ioq->iv, iv_fixed, DMSG_CRYPTO_GCM_IV_FIXED_SIZE);
153 
154 	/*
155 	 * Strictly speaking, padding is irrelevant with a counter mode
156 	 * encryption.
157 	 *
158 	 * However, setting padding to 0, even if using a counter mode such
159 	 * as GCM, will cause an error in _finish if the pt/ct size is not
160 	 * a multiple of the cipher block size.
161 	 */
162 	EVP_CIPHER_CTX_set_padding(&ioq->ctx, 0);
163 
164 	return 0;
165 
166 fail:
167 	dm_printf(1, "%s\n", "Error during _gcm_init");
168 	return -1;
169 }
170 
171 static
172 int
173 _gcm_iv_increment(char *iv)
174 {
175 	/*
176 	 * Deterministic construction according to NIST SP 800-38D, with
177 	 * 64 bit invocation field as integer counter.
178 	 *
179 	 * In other words, our 96 bit IV consists of a 32 bit fixed field
180 	 * unique to the session and a 64 bit integer counter.
181 	 */
182 
183 	uint64_t *c = (uint64_t *)(&iv[DMSG_CRYPTO_GCM_IV_FIXED_SIZE]);
184 
185 	/* Increment invocation field integer counter */
186 	*c = htobe64(be64toh(*c)+1);
187 
188 	/*
189 	 * Detect wrap-around, which means it is time to renegotiate
190 	 * the session to get a new key and/or fixed field.
191 	 */
192 	return (*c == 0) ? 0 : 1;
193 }
194 
195 static
196 int
197 dmsg_crypto_gcm_encrypt_chunk(dmsg_ioq_t *ioq, char *ct, char *pt,
198 				 int in_size, int *out_size)
199 {
200 	int ok;
201 	int u_len, f_len;
202 
203 	*out_size = 0;
204 
205 	/* Re-initialize with new IV (but without redoing the key schedule) */
206 	ok = EVP_EncryptInit_ex(&ioq->ctx, NULL, NULL, NULL, ioq->iv);
207 	if (!ok)
208 		goto fail;
209 
210 	u_len = 0;	/* safety */
211 	ok = EVP_EncryptUpdate(&ioq->ctx, ct, &u_len, pt, in_size);
212 	if (!ok)
213 		goto fail;
214 
215 	f_len = 0;	/* safety */
216 	ok = EVP_EncryptFinal(&ioq->ctx, ct + u_len, &f_len);
217 	if (!ok)
218 		goto fail;
219 
220 	/* Retrieve auth tag */
221 	ok = EVP_CIPHER_CTX_ctrl(&ioq->ctx, EVP_CTRL_GCM_GET_TAG,
222 				 DMSG_CRYPTO_GCM_TAG_SIZE,
223 				 ct + u_len + f_len);
224 	if (!ok)
225 		goto fail;
226 
227 	ok = _gcm_iv_increment(ioq->iv);
228 	if (!ok) {
229 		ioq->error = DMSG_IOQ_ERROR_IVWRAP;
230 		goto fail_out;
231 	}
232 
233 	*out_size = u_len + f_len + DMSG_CRYPTO_GCM_TAG_SIZE;
234 
235 	return 0;
236 
237 fail:
238 	ioq->error = DMSG_IOQ_ERROR_ALGO;
239 fail_out:
240 	dm_printf(1, "%s\n", "error during encrypt_chunk");
241 	return -1;
242 }
243 
244 static
245 int
246 dmsg_crypto_gcm_decrypt_chunk(dmsg_ioq_t *ioq, char *ct, char *pt,
247 				 int out_size, int *consume_size)
248 {
249 	int ok;
250 	int u_len, f_len;
251 
252 	*consume_size = 0;
253 
254 	/* Re-initialize with new IV (but without redoing the key schedule) */
255 	ok = EVP_DecryptInit_ex(&ioq->ctx, NULL, NULL, NULL, ioq->iv);
256 	if (!ok) {
257 		ioq->error = DMSG_IOQ_ERROR_ALGO;
258 		goto fail_out;
259 	}
260 
261 	ok = EVP_CIPHER_CTX_ctrl(&ioq->ctx, EVP_CTRL_GCM_SET_TAG,
262 				 DMSG_CRYPTO_GCM_TAG_SIZE,
263 				 ct + out_size);
264 	if (!ok) {
265 		ioq->error = DMSG_IOQ_ERROR_ALGO;
266 		goto fail_out;
267 	}
268 
269 	ok = EVP_DecryptUpdate(&ioq->ctx, pt, &u_len, ct, out_size);
270 	if (!ok)
271 		goto fail;
272 
273 	ok = EVP_DecryptFinal(&ioq->ctx, pt + u_len, &f_len);
274 	if (!ok)
275 		goto fail;
276 
277 	ok = _gcm_iv_increment(ioq->iv);
278 	if (!ok) {
279 		ioq->error = DMSG_IOQ_ERROR_IVWRAP;
280 		goto fail_out;
281 	}
282 
283 	*consume_size = u_len + f_len + DMSG_CRYPTO_GCM_TAG_SIZE;
284 
285 	return 0;
286 
287 fail:
288 	ioq->error = DMSG_IOQ_ERROR_MACFAIL;
289 fail_out:
290 	dm_printf(1, "%s\n",
291 		  "error during decrypt_chunk "
292 		  "(likely authentication error)");
293 	return -1;
294 }
295 
296 /*
297  * Synchronously negotiate crypto for a new session.  This must occur
298  * within 10 seconds or the connection is error'd out.
299  *
300  * We work off the IP address and/or reverse DNS.  The IP address is
301  * checked first, followed by the IP address at various levels of granularity,
302  * followed by the full domain name and domain names at various levels of
303  * granularity.
304  *
305  *	/etc/hammer2/remote/<name>.pub	- Contains a public key
306  *	/etc/hammer2/remote/<name>.none	- Indicates no encryption (empty file)
307  *					  (e.g. localhost.none).
308  *
309  * We first attempt to locate a public key file based on the peer address or
310  * peer FQDN.
311  *
312  *	<name>.none	- No further negotiation is needed.  We simply return.
313  *			  All communication proceeds without encryption.
314  *			  No public key handshake occurs in this situation.
315  *			  (both ends must match).
316  *
317  *	<name>.pub	- We have located the public key for the peer.  Both
318  *			  sides transmit a block encrypted with their private
319  *			  keys and the peer's public key.
320  *
321  *			  Both sides receive a block and decrypt it.
322  *
323  *			  Both sides formulate a reply using the decrypted
324  *			  block and transmit it.
325  *
326  *			  communication proceeds with the negotiated session
327  *			  key (typically AES-256-CBC).
328  *
329  * If we fail to locate the appropriate file and no floating.db exists the
330  * connection is terminated without further action.
331  *
332  * If floating.db exists the connection proceeds with a floating negotiation.
333  */
334 typedef union {
335 	struct sockaddr sa;
336 	struct sockaddr_in sa_in;
337 	struct sockaddr_in6 sa_in6;
338 } sockaddr_any_t;
339 
340 void
341 dmsg_crypto_negotiate(dmsg_iocom_t *iocom)
342 {
343 	sockaddr_any_t sa;
344 	socklen_t salen = sizeof(sa);
345 	char peername[128];
346 	char realname[128];
347 	dmsg_handshake_t handtx;
348 	dmsg_handshake_t handrx;
349 	char buf1[sizeof(handtx)];
350 	char buf2[sizeof(handtx)];
351 	char *ptr;
352 	char *path;
353 	struct stat st;
354 	FILE *fp;
355 	RSA *keys[3] = { NULL, NULL, NULL };
356 	size_t i;
357 	size_t blksize;
358 	size_t blkmask;
359 	ssize_t n;
360 	int fd;
361 	int error;
362 
363 	/*
364 	 * Get the peer IP address for the connection as a string.
365 	 */
366 	if (getpeername(iocom->sock_fd, &sa.sa, &salen) < 0) {
367 		iocom->ioq_rx.error = DMSG_IOQ_ERROR_NOPEER;
368 		atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
369 		dm_printf(1, "%s\n", "accept: getpeername() failed");
370 		goto done;
371 	}
372 	if (getnameinfo(&sa.sa, salen, peername, sizeof(peername),
373 			NULL, 0, NI_NUMERICHOST) < 0) {
374 		iocom->ioq_rx.error = DMSG_IOQ_ERROR_NOPEER;
375 		atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
376 		dm_printf(1, "%s\n", "accept: cannot decode sockaddr");
377 		goto done;
378 	}
379 	if (DMsgDebugOpt) {
380 		if (realhostname_sa(realname, sizeof(realname),
381 				    &sa.sa, salen) == HOSTNAME_FOUND) {
382 			dm_printf(1, "accept from %s (%s)\n",
383 				  peername, realname);
384 		} else {
385 			dm_printf(1, "accept from %s\n", peername);
386 		}
387 	}
388 
389 	/*
390 	 * Find the remote host's public key
391 	 *
392 	 * If the link is not to be encrypted (<ip>.none located) we shortcut
393 	 * the handshake entirely.  No buffers are exchanged.
394 	 */
395 	asprintf(&path, "%s/%s.pub", DMSG_PATH_REMOTE, peername);
396 	if ((fp = fopen(path, "r")) == NULL) {
397 		free(path);
398 		asprintf(&path, "%s/%s.none",
399 			 DMSG_PATH_REMOTE, peername);
400 		if (stat(path, &st) < 0) {
401 			iocom->ioq_rx.error = DMSG_IOQ_ERROR_NORKEY;
402 			atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
403 			dm_printf(1, "%s\n", "auth failure: unknown host");
404 			goto done;
405 		}
406 		dm_printf(1, "%s\n", "auth succeeded, unencrypted link");
407 		goto done;
408 	}
409 	if (fp) {
410 		keys[0] = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
411 		fclose(fp);
412 		if (keys[0] == NULL) {
413 			iocom->ioq_rx.error = DMSG_IOQ_ERROR_KEYFMT;
414 			atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
415 			dm_printf(1, "%s\n", "auth failure: bad key format");
416 			goto done;
417 		}
418 	}
419 
420 	/*
421 	 * Get our public and private keys
422 	 */
423 	free(path);
424 	asprintf(&path, DMSG_DEFAULT_DIR "/rsa.pub");
425 	if ((fp = fopen(path, "r")) == NULL) {
426 		iocom->ioq_rx.error = DMSG_IOQ_ERROR_NOLKEY;
427 		atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
428 		goto done;
429 	}
430 	keys[1] = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
431 	fclose(fp);
432 	if (keys[1] == NULL) {
433 		iocom->ioq_rx.error = DMSG_IOQ_ERROR_KEYFMT;
434 		atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
435 		dm_printf(1, "%s\n", "auth failure: bad host key format");
436 		goto done;
437 	}
438 
439 	free(path);
440 	asprintf(&path, DMSG_DEFAULT_DIR "/rsa.prv");
441 	if ((fp = fopen(path, "r")) == NULL) {
442 		iocom->ioq_rx.error = DMSG_IOQ_ERROR_NOLKEY;
443 		atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
444 		dm_printf(1, "%s\n", "auth failure: bad host key format");
445 		goto done;
446 	}
447 	keys[2] = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
448 	fclose(fp);
449 	if (keys[2] == NULL) {
450 		iocom->ioq_rx.error = DMSG_IOQ_ERROR_KEYFMT;
451 		atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
452 		dm_printf(1, "%s\n", "auth failure: bad host key format");
453 		goto done;
454 	}
455 	free(path);
456 	path = NULL;
457 
458 	/*
459 	 * public key encrypt/decrypt block size.
460 	 */
461 	if (keys[0]) {
462 		blksize = (size_t)RSA_size(keys[0]);
463 		if (blksize != (size_t)RSA_size(keys[1]) ||
464 		    blksize != (size_t)RSA_size(keys[2]) ||
465 		    sizeof(handtx) % blksize != 0) {
466 			iocom->ioq_rx.error = DMSG_IOQ_ERROR_KEYFMT;
467 			atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
468 			dm_printf(1, "%s\n",
469 				  "auth failure: key size mismatch");
470 			goto done;
471 		}
472 	} else {
473 		blksize = sizeof(handtx);
474 	}
475 	blkmask = blksize - 1;
476 
477 	bzero(&handrx, sizeof(handrx));
478 	bzero(&handtx, sizeof(handtx));
479 
480 	/*
481 	 * Fill all unused fields (particular all junk fields) with random
482 	 * data, and also set the session key.
483 	 */
484 	fd = open("/dev/urandom", O_RDONLY);
485 	if (fd < 0 ||
486 	    fstat(fd, &st) < 0 ||	/* something wrong */
487 	    S_ISREG(st.st_mode) ||	/* supposed to be a RNG dev! */
488 	    read(fd, &handtx, sizeof(handtx)) != sizeof(handtx)) {
489 urandfail:
490 		if (fd >= 0)
491 			close(fd);
492 		iocom->ioq_rx.error = DMSG_IOQ_ERROR_BADURANDOM;
493 		atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
494 		dm_printf(1, "%s\n", "auth failure: bad rng");
495 		goto done;
496 	}
497 	if (bcmp(&handrx, &handtx, sizeof(handtx)) == 0)
498 		goto urandfail;			/* read all zeros */
499 	close(fd);
500 	/* ERR_load_crypto_strings(); openssl debugging */
501 
502 	/*
503 	 * Handshake with the remote.
504 	 *
505 	 *	Encrypt with my private and remote's public
506 	 *	Decrypt with my private and remote's public
507 	 *
508 	 * When encrypting we have to make sure our buffer fits within the
509 	 * modulus, which typically requires bit 7 o the first byte to be
510 	 * zero.  To be safe make sure that bit 7 and bit 6 is zero.
511 	 */
512 	snprintf(handtx.quickmsg, sizeof(handtx.quickmsg), "Testing 1 2 3");
513 	handtx.magic = DMSG_HDR_MAGIC;
514 	handtx.version = 1;
515 	handtx.flags = 0;
516 	assert(sizeof(handtx.verf) * 4 == sizeof(handtx.sess));
517 	bzero(handtx.verf, sizeof(handtx.verf));
518 
519 	handtx.pad1[0] &= 0x3f;	/* message must fit within modulus */
520 	handtx.pad2[0] &= 0x3f;	/* message must fit within modulus */
521 
522 	for (i = 0; i < sizeof(handtx.sess); ++i)
523 		handtx.verf[i / 4] ^= handtx.sess[i];
524 
525 	/*
526 	 * Write handshake buffer to remote
527 	 */
528 	for (i = 0; i < sizeof(handtx); i += blksize) {
529 		ptr = (char *)&handtx + i;
530 		if (keys[0]) {
531 			/*
532 			 * Since we are double-encrypting we have to make
533 			 * sure that the result of the first stage does
534 			 * not blow out the modulus for the second stage.
535 			 *
536 			 * The pointer is pointing to the pad*[] area so
537 			 * we can mess with that until the first stage
538 			 * is legal.
539 			 */
540 			do {
541 				++*(int *)(ptr + 4);
542 				if (RSA_private_encrypt(blksize, ptr, buf1,
543 					    keys[2], RSA_NO_PADDING) < 0) {
544 					iocom->ioq_rx.error =
545 						DMSG_IOQ_ERROR_KEYXCHGFAIL;
546 				}
547 			} while (buf1[0] & 0xC0);
548 
549 			if (RSA_public_encrypt(blksize, buf1, buf2,
550 					    keys[0], RSA_NO_PADDING) < 0) {
551 				iocom->ioq_rx.error =
552 					DMSG_IOQ_ERROR_KEYXCHGFAIL;
553 			}
554 		}
555 		if (write(iocom->sock_fd, buf2, blksize) != (ssize_t)blksize) {
556 			dmio_printf(iocom, 1, "%s\n", "WRITE ERROR");
557 		}
558 	}
559 	if (iocom->ioq_rx.error) {
560 		atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
561 		dmio_printf(iocom, 1, "%s\n",
562 			    "auth failure: key exchange failure "
563 			    "during encryption");
564 		goto done;
565 	}
566 
567 	/*
568 	 * Read handshake buffer from remote
569 	 */
570 	i = 0;
571 	while (i < sizeof(handrx)) {
572 		ptr = (char *)&handrx + i;
573 		n = read(iocom->sock_fd, ptr, blksize - (i & blkmask));
574 		if (n <= 0)
575 			break;
576 		ptr -= (i & blkmask);
577 		i += n;
578 		if (keys[0] && (i & blkmask) == 0) {
579 			if (RSA_private_decrypt(blksize, ptr, buf1,
580 					   keys[2], RSA_NO_PADDING) < 0)
581 				iocom->ioq_rx.error =
582 						DMSG_IOQ_ERROR_KEYXCHGFAIL;
583 			if (RSA_public_decrypt(blksize, buf1, ptr,
584 					   keys[0], RSA_NO_PADDING) < 0)
585 				iocom->ioq_rx.error =
586 						DMSG_IOQ_ERROR_KEYXCHGFAIL;
587 		}
588 	}
589 	if (iocom->ioq_rx.error) {
590 		atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
591 		dmio_printf(iocom, 1, "%s\n",
592 			    "auth failure: key exchange failure "
593 			    "during decryption");
594 		goto done;
595 	}
596 
597 	/*
598 	 * Validate the received data.  Try to make this a constant-time
599 	 * algorithm.
600 	 */
601 	if (i != sizeof(handrx)) {
602 keyxchgfail:
603 		iocom->ioq_rx.error = DMSG_IOQ_ERROR_KEYXCHGFAIL;
604 		atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
605 		dmio_printf(iocom, 1, "%s\n",
606 			    "auth failure: key exchange failure");
607 		goto done;
608 	}
609 
610 	if (handrx.magic == DMSG_HDR_MAGIC_REV) {
611 		handrx.version = bswap16(handrx.version);
612 		handrx.flags = bswap32(handrx.flags);
613 	}
614 	for (i = 0; i < sizeof(handrx.sess); ++i)
615 		handrx.verf[i / 4] ^= handrx.sess[i];
616 	n = 0;
617 	for (i = 0; i < sizeof(handrx.verf); ++i)
618 		n += handrx.verf[i];
619 	if (handrx.version != 1)
620 		++n;
621 	if (n != 0)
622 		goto keyxchgfail;
623 
624 	/*
625 	 * Use separate session keys and session fixed IVs for receive and
626 	 * transmit.
627 	 */
628 	error = crypto_algos[DMSG_CRYPTO_ALGO].init(&iocom->ioq_rx, handrx.sess,
629 	    crypto_algos[DMSG_CRYPTO_ALGO].keylen,
630 	    handrx.sess + crypto_algos[DMSG_CRYPTO_ALGO].keylen,
631 	    sizeof(handrx.sess) - crypto_algos[DMSG_CRYPTO_ALGO].keylen,
632 	    0 /* decryption */);
633 	if (error)
634 		goto keyxchgfail;
635 
636 	error = crypto_algos[DMSG_CRYPTO_ALGO].init(&iocom->ioq_tx, handtx.sess,
637 	    crypto_algos[DMSG_CRYPTO_ALGO].keylen,
638 	    handtx.sess + crypto_algos[DMSG_CRYPTO_ALGO].keylen,
639 	    sizeof(handtx.sess) - crypto_algos[DMSG_CRYPTO_ALGO].keylen,
640 	    1 /* encryption */);
641 	if (error)
642 		goto keyxchgfail;
643 
644 	atomic_set_int(&iocom->flags, DMSG_IOCOMF_CRYPTED);
645 
646 	dmio_printf(iocom, 1, "auth success: %s\n", handrx.quickmsg);
647 done:
648 	if (path)
649 		free(path);
650 	if (keys[0])
651 		RSA_free(keys[0]);
652 	if (keys[1])
653 		RSA_free(keys[1]);
654 	if (keys[1])
655 		RSA_free(keys[2]);
656 }
657 
658 /*
659  * Decrypt pending data in the ioq's fifo.  The data is decrypted in-place.
660  */
661 void
662 dmsg_crypto_decrypt(dmsg_iocom_t *iocom __unused, dmsg_ioq_t *ioq)
663 {
664 	int p_len;
665 	int used;
666 	__unused int error;	/* XXX */
667 	char buf[512];
668 
669 	/*
670 	 * fifo_beg to fifo_cdx is data already decrypted.
671 	 * fifo_cdn to fifo_end is data not yet decrypted.
672 	 */
673 	p_len = ioq->fifo_end - ioq->fifo_cdn; /* data not yet decrypted */
674 
675 	if (p_len == 0)
676 		return;
677 
678 	while (p_len >= crypto_algos[DMSG_CRYPTO_ALGO].taglen +
679 	    DMSG_CRYPTO_CHUNK_SIZE) {
680 		bcopy(ioq->buf + ioq->fifo_cdn, buf,
681 		      crypto_algos[DMSG_CRYPTO_ALGO].taglen +
682 		      DMSG_CRYPTO_CHUNK_SIZE);
683 		error = crypto_algos[DMSG_CRYPTO_ALGO].dec_chunk(
684 		    ioq, buf,
685 		    ioq->buf + ioq->fifo_cdx,
686 		    DMSG_CRYPTO_CHUNK_SIZE,
687 		    &used);
688 #ifdef CRYPTO_DEBUG
689 		dmio_printf(iocom, 5,
690 			    "dec: p_len: %d, used: %d, "
691 			    "fifo_cdn: %ju, fifo_cdx: %ju\n",
692 			     p_len, used,
693 			     ioq->fifo_cdn, ioq->fifo_cdx);
694 #endif
695 		p_len -= used;
696 		ioq->fifo_cdn += used;
697 		ioq->fifo_cdx += DMSG_CRYPTO_CHUNK_SIZE;
698 #ifdef CRYPTO_DEBUG
699 		dmio_printf(iocom, 5,
700 			    "dec: p_len: %d, used: %d, "
701 			    "fifo_cdn: %ju, fifo_cdx: %ju\n",
702 			    p_len, used, ioq->fifo_cdn, ioq->fifo_cdx);
703 #endif
704 	}
705 }
706 
707 /*
708  * *nactp is set to the number of ORIGINAL bytes consumed by the encrypter.
709  * The FIFO may contain more data.
710  */
711 int
712 dmsg_crypto_encrypt(dmsg_iocom_t *iocom __unused, dmsg_ioq_t *ioq,
713 		    struct iovec *iov, int n, size_t *nactp)
714 {
715 	int p_len, used, ct_used;
716 	int i;
717 	__unused int error;	/* XXX */
718 	size_t nmax;
719 
720 	nmax = sizeof(ioq->buf) - ioq->fifo_end;	/* max new bytes */
721 
722 	*nactp = 0;
723 	for (i = 0; i < n && nmax; ++i) {
724 		used = 0;
725 		p_len = iov[i].iov_len;
726 		assert((p_len & DMSG_ALIGNMASK) == 0);
727 
728 		while (p_len >= DMSG_CRYPTO_CHUNK_SIZE &&
729 		    nmax >= DMSG_CRYPTO_CHUNK_SIZE +
730 		    (size_t)crypto_algos[DMSG_CRYPTO_ALGO].taglen) {
731 			error = crypto_algos[DMSG_CRYPTO_ALGO].enc_chunk(
732 			    ioq,
733 			    ioq->buf + ioq->fifo_cdx,
734 			    (char *)iov[i].iov_base + used,
735 			    DMSG_CRYPTO_CHUNK_SIZE, &ct_used);
736 #ifdef CRYPTO_DEBUG
737 			dmio_printf(iocom, 5,
738 				    "nactp: %ju, p_len: %d, "
739 				    "ct_used: %d, used: %d, nmax: %ju\n",
740 				    *nactp, p_len, ct_used, used, nmax);
741 #endif
742 
743 			*nactp += (size_t)DMSG_CRYPTO_CHUNK_SIZE;	/* plaintext count */
744 			used += DMSG_CRYPTO_CHUNK_SIZE;
745 			p_len -= DMSG_CRYPTO_CHUNK_SIZE;
746 
747 			/*
748 			 * NOTE: crypted count will eventually differ from
749 			 *	 nmax, but for now we have not yet introduced
750 			 *	 random armor.
751 			 */
752 			ioq->fifo_cdx += (size_t)ct_used;
753 			ioq->fifo_cdn += (size_t)ct_used;
754 			ioq->fifo_end += (size_t)ct_used;
755 			nmax -= (size_t)ct_used;
756 #ifdef CRYPTO_DEBUG
757 			dmio_printf(iocom, 5,
758 				    "nactp: %ju, p_len: %d, "
759 				    "ct_used: %d, used: %d, nmax: %ju\n",
760 				    *nactp, p_len, ct_used, used, nmax);
761 #endif
762 		}
763 	}
764 	iov[0].iov_base = ioq->buf + ioq->fifo_beg;
765 	iov[0].iov_len = ioq->fifo_cdx - ioq->fifo_beg;
766 
767 	return (1);
768 }
769