xref: /dragonfly/lib/libdmsg/crypto.c (revision a988b43e)
10c3a8cd0SMatthew Dillon /*
20c3a8cd0SMatthew Dillon  * Copyright (c) 2011-2012 The DragonFly Project.  All rights reserved.
30c3a8cd0SMatthew Dillon  *
40c3a8cd0SMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
50c3a8cd0SMatthew Dillon  * by Matthew Dillon <dillon@dragonflybsd.org>
60c3a8cd0SMatthew Dillon  * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org>
70c3a8cd0SMatthew Dillon  * by Alex Hornung <alexh@dragonflybsd.org>
80c3a8cd0SMatthew Dillon  *
90c3a8cd0SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
100c3a8cd0SMatthew Dillon  * modification, are permitted provided that the following conditions
110c3a8cd0SMatthew Dillon  * are met:
120c3a8cd0SMatthew Dillon  *
130c3a8cd0SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
140c3a8cd0SMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
150c3a8cd0SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
160c3a8cd0SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
170c3a8cd0SMatthew Dillon  *    the documentation and/or other materials provided with the
180c3a8cd0SMatthew Dillon  *    distribution.
190c3a8cd0SMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
200c3a8cd0SMatthew Dillon  *    contributors may be used to endorse or promote products derived
210c3a8cd0SMatthew Dillon  *    from this software without specific, prior written permission.
220c3a8cd0SMatthew Dillon  *
230c3a8cd0SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
240c3a8cd0SMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
250c3a8cd0SMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
260c3a8cd0SMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
270c3a8cd0SMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
280c3a8cd0SMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
290c3a8cd0SMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
300c3a8cd0SMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
310c3a8cd0SMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
320c3a8cd0SMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
330c3a8cd0SMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
340c3a8cd0SMatthew Dillon  * SUCH DAMAGE.
350c3a8cd0SMatthew Dillon  */
360c3a8cd0SMatthew Dillon 
370c3a8cd0SMatthew Dillon #include "dmsg_local.h"
380c3a8cd0SMatthew Dillon 
390c3a8cd0SMatthew Dillon /*
400c3a8cd0SMatthew Dillon  * Setup crypto for pthreads
410c3a8cd0SMatthew Dillon  */
420c3a8cd0SMatthew Dillon static pthread_mutex_t *crypto_locks;
430c3a8cd0SMatthew Dillon int crypto_count;
440c3a8cd0SMatthew Dillon 
450c3a8cd0SMatthew Dillon static int dmsg_crypto_gcm_init(dmsg_ioq_t *, char *, int, char *, int, int);
46*a988b43eSMatthew Dillon static void dmsg_crypto_gcm_uninit(dmsg_ioq_t *);
470c3a8cd0SMatthew Dillon static int dmsg_crypto_gcm_encrypt_chunk(dmsg_ioq_t *, char *, char *, int, int *);
480c3a8cd0SMatthew Dillon static int dmsg_crypto_gcm_decrypt_chunk(dmsg_ioq_t *, char *, char *, int, int *);
490c3a8cd0SMatthew Dillon 
500c3a8cd0SMatthew Dillon /*
510c3a8cd0SMatthew Dillon  * NOTE: the order of this table needs to match the DMSG_CRYPTO_ALGO_*_IDX
520c3a8cd0SMatthew Dillon  *       defines in network.h.
530c3a8cd0SMatthew Dillon  */
540c3a8cd0SMatthew Dillon static struct crypto_algo crypto_algos[] = {
550c3a8cd0SMatthew Dillon 	{
560c3a8cd0SMatthew Dillon 		.name      = "aes-256-gcm",
570c3a8cd0SMatthew Dillon 		.keylen    = DMSG_CRYPTO_GCM_KEY_SIZE,
58*a988b43eSMatthew Dillon 		.unused01  = 0,
590c3a8cd0SMatthew Dillon 		.init      = dmsg_crypto_gcm_init,
60*a988b43eSMatthew Dillon 		.uninit    = dmsg_crypto_gcm_uninit,
610c3a8cd0SMatthew Dillon 		.enc_chunk = dmsg_crypto_gcm_encrypt_chunk,
620c3a8cd0SMatthew Dillon 		.dec_chunk = dmsg_crypto_gcm_decrypt_chunk
630c3a8cd0SMatthew Dillon 	},
64*a988b43eSMatthew Dillon 	{ NULL, 0, 0, NULL, NULL, NULL, NULL }
650c3a8cd0SMatthew Dillon };
660c3a8cd0SMatthew Dillon 
670c3a8cd0SMatthew Dillon static
680c3a8cd0SMatthew Dillon unsigned long
dmsg_crypto_id_callback(void)690c3a8cd0SMatthew Dillon dmsg_crypto_id_callback(void)
700c3a8cd0SMatthew Dillon {
710c3a8cd0SMatthew Dillon 	return ((unsigned long)(uintptr_t)pthread_self());
720c3a8cd0SMatthew Dillon }
730c3a8cd0SMatthew Dillon 
740c3a8cd0SMatthew Dillon static
750c3a8cd0SMatthew Dillon void
dmsg_crypto_locking_callback(int mode,int type,const char * file __unused,int line __unused)760c3a8cd0SMatthew Dillon dmsg_crypto_locking_callback(int mode, int type,
770c3a8cd0SMatthew Dillon 				const char *file __unused, int line __unused)
780c3a8cd0SMatthew Dillon {
790c3a8cd0SMatthew Dillon 	assert(type >= 0 && type < crypto_count);
800c3a8cd0SMatthew Dillon 	if (mode & CRYPTO_LOCK) {
810c3a8cd0SMatthew Dillon 		pthread_mutex_lock(&crypto_locks[type]);
820c3a8cd0SMatthew Dillon 	} else {
830c3a8cd0SMatthew Dillon 		pthread_mutex_unlock(&crypto_locks[type]);
840c3a8cd0SMatthew Dillon 	}
850c3a8cd0SMatthew Dillon }
860c3a8cd0SMatthew Dillon 
870c3a8cd0SMatthew Dillon void
dmsg_crypto_setup(void)880c3a8cd0SMatthew Dillon dmsg_crypto_setup(void)
890c3a8cd0SMatthew Dillon {
900c3a8cd0SMatthew Dillon 	crypto_count = CRYPTO_num_locks();
910c3a8cd0SMatthew Dillon 	crypto_locks = calloc(crypto_count, sizeof(crypto_locks[0]));
920c3a8cd0SMatthew Dillon 	CRYPTO_set_id_callback(dmsg_crypto_id_callback);
930c3a8cd0SMatthew Dillon 	CRYPTO_set_locking_callback(dmsg_crypto_locking_callback);
940c3a8cd0SMatthew Dillon }
950c3a8cd0SMatthew Dillon 
960c3a8cd0SMatthew Dillon static
970c3a8cd0SMatthew Dillon int
dmsg_crypto_gcm_init(dmsg_ioq_t * ioq,char * key,int klen,char * iv_fixed,int ivlen,int enc)980c3a8cd0SMatthew Dillon dmsg_crypto_gcm_init(dmsg_ioq_t *ioq, char *key, int klen,
990c3a8cd0SMatthew Dillon 			char *iv_fixed, int ivlen, int enc)
1000c3a8cd0SMatthew Dillon {
1010c3a8cd0SMatthew Dillon 	int i, ok;
1020c3a8cd0SMatthew Dillon 
1030c3a8cd0SMatthew Dillon 	if (klen < DMSG_CRYPTO_GCM_KEY_SIZE ||
1040c3a8cd0SMatthew Dillon 	    ivlen < DMSG_CRYPTO_GCM_IV_FIXED_SIZE) {
1055ab1caedSMatthew Dillon 		dm_printf(1, "%s\n", "Not enough key or iv material");
1060c3a8cd0SMatthew Dillon 		return -1;
1070c3a8cd0SMatthew Dillon 	}
1080c3a8cd0SMatthew Dillon 
1095ab1caedSMatthew Dillon 	dm_printf(6, "%s key: ", enc ? "Encryption" : "Decryption");
1100c3a8cd0SMatthew Dillon 	for (i = 0; i < DMSG_CRYPTO_GCM_KEY_SIZE; ++i)
1115ab1caedSMatthew Dillon 		dmx_printf(6, "%02x", (unsigned char)key[i]);
1125ab1caedSMatthew Dillon 	dmx_printf(6, "%s\n", "");
1130c3a8cd0SMatthew Dillon 
1145ab1caedSMatthew Dillon 	dm_printf(6, "%s iv:  ", enc ? "Encryption" : "Decryption");
1150c3a8cd0SMatthew Dillon 	for (i = 0; i < DMSG_CRYPTO_GCM_IV_FIXED_SIZE; ++i)
1165ab1caedSMatthew Dillon 		dmx_printf(6, "%02x", (unsigned char)iv_fixed[i]);
1175ab1caedSMatthew Dillon 	dmx_printf(6, "%s\n", " (fixed part only)");
1180c3a8cd0SMatthew Dillon 
119*a988b43eSMatthew Dillon 	memset(ioq->iv, 0, DMSG_CRYPTO_GCM_IV_SIZE);
120*a988b43eSMatthew Dillon 	memcpy(ioq->iv, iv_fixed, DMSG_CRYPTO_GCM_IV_FIXED_SIZE);
121*a988b43eSMatthew Dillon 
122*a988b43eSMatthew Dillon 	ioq->ctx = EVP_CIPHER_CTX_new();
1230c3a8cd0SMatthew Dillon 
1240c3a8cd0SMatthew Dillon 	if (enc)
125*a988b43eSMatthew Dillon 		ok = EVP_EncryptInit_ex(ioq->ctx, EVP_aes_256_gcm(), NULL,
126*a988b43eSMatthew Dillon 					(unsigned char*)key, ioq->iv);
1270c3a8cd0SMatthew Dillon 	else
128*a988b43eSMatthew Dillon 		ok = EVP_DecryptInit_ex(ioq->ctx, EVP_aes_256_gcm(), NULL,
129*a988b43eSMatthew Dillon 					(unsigned char*)key, ioq->iv);
1300c3a8cd0SMatthew Dillon 	if (!ok)
1310c3a8cd0SMatthew Dillon 		goto fail;
1320c3a8cd0SMatthew Dillon 
1330c3a8cd0SMatthew Dillon 	/*
1340c3a8cd0SMatthew Dillon 	 * According to the original Galois/Counter Mode of Operation (GCM)
1350c3a8cd0SMatthew Dillon 	 * proposal, only IVs that are exactly 96 bits get used without any
1360c3a8cd0SMatthew Dillon 	 * further processing. Other IV sizes cause the GHASH() operation
1370c3a8cd0SMatthew Dillon 	 * to be applied to the IV, which is more costly.
1380c3a8cd0SMatthew Dillon 	 *
1390c3a8cd0SMatthew Dillon 	 * The NIST SP 800-38D also recommends using a 96 bit IV for the same
1400c3a8cd0SMatthew Dillon 	 * reasons. We actually follow the deterministic construction
1410c3a8cd0SMatthew Dillon 	 * recommended in NIST SP 800-38D with a 64 bit invocation field as an
1420c3a8cd0SMatthew Dillon 	 * integer counter and a random, session-specific fixed field.
1430c3a8cd0SMatthew Dillon 	 *
1440c3a8cd0SMatthew Dillon 	 * This means that we can essentially use the same session key and
1450c3a8cd0SMatthew Dillon 	 * IV fixed field for up to 2^64 invocations of the authenticated
1460c3a8cd0SMatthew Dillon 	 * encryption or decryption.
1470c3a8cd0SMatthew Dillon 	 *
1480c3a8cd0SMatthew Dillon 	 * With a chunk size of 64 bytes, this adds up to 1 zettabyte of
1490c3a8cd0SMatthew Dillon 	 * traffic.
1500c3a8cd0SMatthew Dillon 	 */
151*a988b43eSMatthew Dillon 	ok = EVP_CIPHER_CTX_ctrl(ioq->ctx, EVP_CTRL_GCM_SET_IVLEN,
1520c3a8cd0SMatthew Dillon 				 DMSG_CRYPTO_GCM_IV_SIZE, NULL);
1530c3a8cd0SMatthew Dillon 	if (!ok)
1540c3a8cd0SMatthew Dillon 		goto fail;
1550c3a8cd0SMatthew Dillon 
1560c3a8cd0SMatthew Dillon 	/*
1570c3a8cd0SMatthew Dillon 	 * Strictly speaking, padding is irrelevant with a counter mode
1580c3a8cd0SMatthew Dillon 	 * encryption.
1590c3a8cd0SMatthew Dillon 	 *
1600c3a8cd0SMatthew Dillon 	 * However, setting padding to 0, even if using a counter mode such
1610c3a8cd0SMatthew Dillon 	 * as GCM, will cause an error in _finish if the pt/ct size is not
1620c3a8cd0SMatthew Dillon 	 * a multiple of the cipher block size.
1630c3a8cd0SMatthew Dillon 	 */
164*a988b43eSMatthew Dillon 	EVP_CIPHER_CTX_set_padding(ioq->ctx, 0);
1650c3a8cd0SMatthew Dillon 
1660c3a8cd0SMatthew Dillon 	return 0;
1670c3a8cd0SMatthew Dillon 
1680c3a8cd0SMatthew Dillon fail:
1695ab1caedSMatthew Dillon 	dm_printf(1, "%s\n", "Error during _gcm_init");
1700c3a8cd0SMatthew Dillon 	return -1;
1710c3a8cd0SMatthew Dillon }
1720c3a8cd0SMatthew Dillon 
1730c3a8cd0SMatthew Dillon static
174*a988b43eSMatthew Dillon void
dmsg_crypto_gcm_uninit(dmsg_ioq_t * ioq)175*a988b43eSMatthew Dillon dmsg_crypto_gcm_uninit(dmsg_ioq_t *ioq)
176*a988b43eSMatthew Dillon {
177*a988b43eSMatthew Dillon 	EVP_CIPHER_CTX_free(ioq->ctx);
178*a988b43eSMatthew Dillon 	ioq->ctx = NULL;
179*a988b43eSMatthew Dillon }
180*a988b43eSMatthew Dillon 
181*a988b43eSMatthew Dillon static
1820c3a8cd0SMatthew Dillon int
_gcm_iv_increment(char * iv)1830c3a8cd0SMatthew Dillon _gcm_iv_increment(char *iv)
1840c3a8cd0SMatthew Dillon {
1850c3a8cd0SMatthew Dillon 	/*
1860c3a8cd0SMatthew Dillon 	 * Deterministic construction according to NIST SP 800-38D, with
1870c3a8cd0SMatthew Dillon 	 * 64 bit invocation field as integer counter.
1880c3a8cd0SMatthew Dillon 	 *
1890c3a8cd0SMatthew Dillon 	 * In other words, our 96 bit IV consists of a 32 bit fixed field
1900c3a8cd0SMatthew Dillon 	 * unique to the session and a 64 bit integer counter.
1910c3a8cd0SMatthew Dillon 	 */
1920c3a8cd0SMatthew Dillon 
1930c3a8cd0SMatthew Dillon 	uint64_t *c = (uint64_t *)(&iv[DMSG_CRYPTO_GCM_IV_FIXED_SIZE]);
1940c3a8cd0SMatthew Dillon 
1950c3a8cd0SMatthew Dillon 	/* Increment invocation field integer counter */
1960c3a8cd0SMatthew Dillon 	*c = htobe64(be64toh(*c)+1);
1970c3a8cd0SMatthew Dillon 
1980c3a8cd0SMatthew Dillon 	/*
1990c3a8cd0SMatthew Dillon 	 * Detect wrap-around, which means it is time to renegotiate
2000c3a8cd0SMatthew Dillon 	 * the session to get a new key and/or fixed field.
2010c3a8cd0SMatthew Dillon 	 */
20298f23959SSascha Wildner 	return (*c == 0) ? 0 : 1;
2030c3a8cd0SMatthew Dillon }
2040c3a8cd0SMatthew Dillon 
2050c3a8cd0SMatthew Dillon static
2060c3a8cd0SMatthew Dillon int
dmsg_crypto_gcm_encrypt_chunk(dmsg_ioq_t * ioq,char * ct,char * pt,int in_size,int * out_size)2070c3a8cd0SMatthew Dillon dmsg_crypto_gcm_encrypt_chunk(dmsg_ioq_t *ioq, char *ct, char *pt,
2080c3a8cd0SMatthew Dillon 				 int in_size, int *out_size)
2090c3a8cd0SMatthew Dillon {
2100c3a8cd0SMatthew Dillon 	int ok;
211*a988b43eSMatthew Dillon 	int u_len;
2120c3a8cd0SMatthew Dillon 
2130c3a8cd0SMatthew Dillon 	*out_size = 0;
2140c3a8cd0SMatthew Dillon 
215*a988b43eSMatthew Dillon 	/*
216*a988b43eSMatthew Dillon 	 * Change running IV for each block
217*a988b43eSMatthew Dillon 	 */
218*a988b43eSMatthew Dillon 	ok = EVP_CIPHER_CTX_set_iv(ioq->ctx, (unsigned char *)ioq->iv,
219*a988b43eSMatthew Dillon 				   DMSG_CRYPTO_GCM_IV_SIZE);
220*a988b43eSMatthew Dillon 
2210c3a8cd0SMatthew Dillon 	if (!ok)
2220c3a8cd0SMatthew Dillon 		goto fail;
2230c3a8cd0SMatthew Dillon 
2241e271b66SMatthew Dillon 	u_len = 0;	/* safety */
225*a988b43eSMatthew Dillon 	ok = EVP_EncryptUpdate(ioq->ctx, (unsigned char*)ct, &u_len,
226c17dbda3STomohiro Kusumi 		(unsigned char*)pt, in_size);
2270c3a8cd0SMatthew Dillon 	if (!ok)
2280c3a8cd0SMatthew Dillon 		goto fail;
2290c3a8cd0SMatthew Dillon 
2300c3a8cd0SMatthew Dillon 	ok = _gcm_iv_increment(ioq->iv);
2310c3a8cd0SMatthew Dillon 	if (!ok) {
2320c3a8cd0SMatthew Dillon 		ioq->error = DMSG_IOQ_ERROR_IVWRAP;
2330c3a8cd0SMatthew Dillon 		goto fail_out;
2340c3a8cd0SMatthew Dillon 	}
2350c3a8cd0SMatthew Dillon 
236*a988b43eSMatthew Dillon 	*out_size = u_len;
2370c3a8cd0SMatthew Dillon 
2380c3a8cd0SMatthew Dillon 	return 0;
2390c3a8cd0SMatthew Dillon 
2400c3a8cd0SMatthew Dillon fail:
2410c3a8cd0SMatthew Dillon 	ioq->error = DMSG_IOQ_ERROR_ALGO;
2420c3a8cd0SMatthew Dillon fail_out:
2435ab1caedSMatthew Dillon 	dm_printf(1, "%s\n", "error during encrypt_chunk");
2440c3a8cd0SMatthew Dillon 	return -1;
2450c3a8cd0SMatthew Dillon }
2460c3a8cd0SMatthew Dillon 
2470c3a8cd0SMatthew Dillon static
2480c3a8cd0SMatthew Dillon int
dmsg_crypto_gcm_decrypt_chunk(dmsg_ioq_t * ioq,char * ct,char * pt,int out_size,int * consume_size)2490c3a8cd0SMatthew Dillon dmsg_crypto_gcm_decrypt_chunk(dmsg_ioq_t *ioq, char *ct, char *pt,
2500c3a8cd0SMatthew Dillon 				 int out_size, int *consume_size)
2510c3a8cd0SMatthew Dillon {
2520c3a8cd0SMatthew Dillon 	int ok;
253*a988b43eSMatthew Dillon 	int u_len;
2540c3a8cd0SMatthew Dillon 
2550c3a8cd0SMatthew Dillon 	*consume_size = 0;
2560c3a8cd0SMatthew Dillon 
257*a988b43eSMatthew Dillon 	/*
258*a988b43eSMatthew Dillon 	 * Change running IV for each block
259*a988b43eSMatthew Dillon 	 */
260*a988b43eSMatthew Dillon 	ok = EVP_CIPHER_CTX_set_iv(ioq->ctx, (unsigned char *)ioq->iv,
261*a988b43eSMatthew Dillon 				   DMSG_CRYPTO_GCM_IV_SIZE);
2620c3a8cd0SMatthew Dillon 	if (!ok) {
2630c3a8cd0SMatthew Dillon 		ioq->error = DMSG_IOQ_ERROR_ALGO;
2640c3a8cd0SMatthew Dillon 		goto fail_out;
2650c3a8cd0SMatthew Dillon 	}
2660c3a8cd0SMatthew Dillon 
267*a988b43eSMatthew Dillon 	ok = EVP_DecryptUpdate(ioq->ctx, (unsigned char*)pt, &u_len,
268c17dbda3STomohiro Kusumi 		(unsigned char*)ct, out_size);
2690c3a8cd0SMatthew Dillon 	if (!ok)
2700c3a8cd0SMatthew Dillon 		goto fail;
2710c3a8cd0SMatthew Dillon 
2720c3a8cd0SMatthew Dillon 	ok = _gcm_iv_increment(ioq->iv);
2730c3a8cd0SMatthew Dillon 	if (!ok) {
2740c3a8cd0SMatthew Dillon 		ioq->error = DMSG_IOQ_ERROR_IVWRAP;
2750c3a8cd0SMatthew Dillon 		goto fail_out;
2760c3a8cd0SMatthew Dillon 	}
2770c3a8cd0SMatthew Dillon 
278*a988b43eSMatthew Dillon 	*consume_size = u_len;
2790c3a8cd0SMatthew Dillon 
2800c3a8cd0SMatthew Dillon 	return 0;
2810c3a8cd0SMatthew Dillon 
2820c3a8cd0SMatthew Dillon fail:
2830c3a8cd0SMatthew Dillon 	ioq->error = DMSG_IOQ_ERROR_MACFAIL;
2840c3a8cd0SMatthew Dillon fail_out:
2855ab1caedSMatthew Dillon 	dm_printf(1, "%s\n",
2865ab1caedSMatthew Dillon 		  "error during decrypt_chunk "
2875ab1caedSMatthew Dillon 		  "(likely authentication error)");
2880c3a8cd0SMatthew Dillon 	return -1;
2890c3a8cd0SMatthew Dillon }
2900c3a8cd0SMatthew Dillon 
2910c3a8cd0SMatthew Dillon /*
2920c3a8cd0SMatthew Dillon  * Synchronously negotiate crypto for a new session.  This must occur
2930c3a8cd0SMatthew Dillon  * within 10 seconds or the connection is error'd out.
2940c3a8cd0SMatthew Dillon  *
2950c3a8cd0SMatthew Dillon  * We work off the IP address and/or reverse DNS.  The IP address is
2960c3a8cd0SMatthew Dillon  * checked first, followed by the IP address at various levels of granularity,
2970c3a8cd0SMatthew Dillon  * followed by the full domain name and domain names at various levels of
2980c3a8cd0SMatthew Dillon  * granularity.
2990c3a8cd0SMatthew Dillon  *
3000c3a8cd0SMatthew Dillon  *	/etc/hammer2/remote/<name>.pub	- Contains a public key
3010c3a8cd0SMatthew Dillon  *	/etc/hammer2/remote/<name>.none	- Indicates no encryption (empty file)
3020c3a8cd0SMatthew Dillon  *					  (e.g. localhost.none).
3030c3a8cd0SMatthew Dillon  *
3040c3a8cd0SMatthew Dillon  * We first attempt to locate a public key file based on the peer address or
3050c3a8cd0SMatthew Dillon  * peer FQDN.
3060c3a8cd0SMatthew Dillon  *
3070c3a8cd0SMatthew Dillon  *	<name>.none	- No further negotiation is needed.  We simply return.
3080c3a8cd0SMatthew Dillon  *			  All communication proceeds without encryption.
3090c3a8cd0SMatthew Dillon  *			  No public key handshake occurs in this situation.
3100c3a8cd0SMatthew Dillon  *			  (both ends must match).
3110c3a8cd0SMatthew Dillon  *
3120c3a8cd0SMatthew Dillon  *	<name>.pub	- We have located the public key for the peer.  Both
3130c3a8cd0SMatthew Dillon  *			  sides transmit a block encrypted with their private
3140c3a8cd0SMatthew Dillon  *			  keys and the peer's public key.
3150c3a8cd0SMatthew Dillon  *
3160c3a8cd0SMatthew Dillon  *			  Both sides receive a block and decrypt it.
3170c3a8cd0SMatthew Dillon  *
3180c3a8cd0SMatthew Dillon  *			  Both sides formulate a reply using the decrypted
3190c3a8cd0SMatthew Dillon  *			  block and transmit it.
3200c3a8cd0SMatthew Dillon  *
3210c3a8cd0SMatthew Dillon  *			  communication proceeds with the negotiated session
3220c3a8cd0SMatthew Dillon  *			  key (typically AES-256-CBC).
3230c3a8cd0SMatthew Dillon  *
3240c3a8cd0SMatthew Dillon  * If we fail to locate the appropriate file and no floating.db exists the
3250c3a8cd0SMatthew Dillon  * connection is terminated without further action.
3260c3a8cd0SMatthew Dillon  *
3270c3a8cd0SMatthew Dillon  * If floating.db exists the connection proceeds with a floating negotiation.
3280c3a8cd0SMatthew Dillon  */
3290c3a8cd0SMatthew Dillon typedef union {
3300c3a8cd0SMatthew Dillon 	struct sockaddr sa;
3310c3a8cd0SMatthew Dillon 	struct sockaddr_in sa_in;
3320c3a8cd0SMatthew Dillon 	struct sockaddr_in6 sa_in6;
3330c3a8cd0SMatthew Dillon } sockaddr_any_t;
3340c3a8cd0SMatthew Dillon 
3350c3a8cd0SMatthew Dillon void
dmsg_crypto_negotiate(dmsg_iocom_t * iocom)3360c3a8cd0SMatthew Dillon dmsg_crypto_negotiate(dmsg_iocom_t *iocom)
3370c3a8cd0SMatthew Dillon {
3380c3a8cd0SMatthew Dillon 	sockaddr_any_t sa;
3390c3a8cd0SMatthew Dillon 	socklen_t salen = sizeof(sa);
3400c3a8cd0SMatthew Dillon 	char peername[128];
3410c3a8cd0SMatthew Dillon 	char realname[128];
3420c3a8cd0SMatthew Dillon 	dmsg_handshake_t handtx;
3430c3a8cd0SMatthew Dillon 	dmsg_handshake_t handrx;
3440c3a8cd0SMatthew Dillon 	char buf1[sizeof(handtx)];
3450c3a8cd0SMatthew Dillon 	char buf2[sizeof(handtx)];
3460c3a8cd0SMatthew Dillon 	char *ptr;
3470e172034SSascha Wildner 	char *path = NULL;
3480c3a8cd0SMatthew Dillon 	struct stat st;
3490c3a8cd0SMatthew Dillon 	FILE *fp;
3500c3a8cd0SMatthew Dillon 	RSA *keys[3] = { NULL, NULL, NULL };
3510c3a8cd0SMatthew Dillon 	size_t i;
3520c3a8cd0SMatthew Dillon 	size_t blksize;
3530c3a8cd0SMatthew Dillon 	size_t blkmask;
3540c3a8cd0SMatthew Dillon 	ssize_t n;
3550c3a8cd0SMatthew Dillon 	int fd;
3560c3a8cd0SMatthew Dillon 	int error;
3570c3a8cd0SMatthew Dillon 
3580c3a8cd0SMatthew Dillon 	/*
3590c3a8cd0SMatthew Dillon 	 * Get the peer IP address for the connection as a string.
3600c3a8cd0SMatthew Dillon 	 */
3610c3a8cd0SMatthew Dillon 	if (getpeername(iocom->sock_fd, &sa.sa, &salen) < 0) {
3620c3a8cd0SMatthew Dillon 		iocom->ioq_rx.error = DMSG_IOQ_ERROR_NOPEER;
363a2179323SMatthew Dillon 		atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
3645ab1caedSMatthew Dillon 		dm_printf(1, "%s\n", "accept: getpeername() failed");
3650c3a8cd0SMatthew Dillon 		goto done;
3660c3a8cd0SMatthew Dillon 	}
3670c3a8cd0SMatthew Dillon 	if (getnameinfo(&sa.sa, salen, peername, sizeof(peername),
3680c3a8cd0SMatthew Dillon 			NULL, 0, NI_NUMERICHOST) < 0) {
3690c3a8cd0SMatthew Dillon 		iocom->ioq_rx.error = DMSG_IOQ_ERROR_NOPEER;
370a2179323SMatthew Dillon 		atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
3715ab1caedSMatthew Dillon 		dm_printf(1, "%s\n", "accept: cannot decode sockaddr");
3720c3a8cd0SMatthew Dillon 		goto done;
3730c3a8cd0SMatthew Dillon 	}
3740c3a8cd0SMatthew Dillon 	if (DMsgDebugOpt) {
3750c3a8cd0SMatthew Dillon 		if (realhostname_sa(realname, sizeof(realname),
3760c3a8cd0SMatthew Dillon 				    &sa.sa, salen) == HOSTNAME_FOUND) {
3775ab1caedSMatthew Dillon 			dm_printf(1, "accept from %s (%s)\n",
3780c3a8cd0SMatthew Dillon 				  peername, realname);
3790c3a8cd0SMatthew Dillon 		} else {
3805ab1caedSMatthew Dillon 			dm_printf(1, "accept from %s\n", peername);
3810c3a8cd0SMatthew Dillon 		}
3820c3a8cd0SMatthew Dillon 	}
3830c3a8cd0SMatthew Dillon 
3840c3a8cd0SMatthew Dillon 	/*
3850c3a8cd0SMatthew Dillon 	 * Find the remote host's public key
3860c3a8cd0SMatthew Dillon 	 *
3870c3a8cd0SMatthew Dillon 	 * If the link is not to be encrypted (<ip>.none located) we shortcut
3880c3a8cd0SMatthew Dillon 	 * the handshake entirely.  No buffers are exchanged.
3890c3a8cd0SMatthew Dillon 	 */
3900c3a8cd0SMatthew Dillon 	asprintf(&path, "%s/%s.pub", DMSG_PATH_REMOTE, peername);
3910c3a8cd0SMatthew Dillon 	if ((fp = fopen(path, "r")) == NULL) {
3920c3a8cd0SMatthew Dillon 		free(path);
3930c3a8cd0SMatthew Dillon 		asprintf(&path, "%s/%s.none",
3940c3a8cd0SMatthew Dillon 			 DMSG_PATH_REMOTE, peername);
3950c3a8cd0SMatthew Dillon 		if (stat(path, &st) < 0) {
3960c3a8cd0SMatthew Dillon 			iocom->ioq_rx.error = DMSG_IOQ_ERROR_NORKEY;
397a2179323SMatthew Dillon 			atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
3985ab1caedSMatthew Dillon 			dm_printf(1, "%s\n", "auth failure: unknown host");
3990c3a8cd0SMatthew Dillon 			goto done;
4000c3a8cd0SMatthew Dillon 		}
4015ab1caedSMatthew Dillon 		dm_printf(1, "%s\n", "auth succeeded, unencrypted link");
4020c3a8cd0SMatthew Dillon 		goto done;
4030c3a8cd0SMatthew Dillon 	}
4040c3a8cd0SMatthew Dillon 	if (fp) {
4050c3a8cd0SMatthew Dillon 		keys[0] = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
4060c3a8cd0SMatthew Dillon 		fclose(fp);
4070c3a8cd0SMatthew Dillon 		if (keys[0] == NULL) {
4080c3a8cd0SMatthew Dillon 			iocom->ioq_rx.error = DMSG_IOQ_ERROR_KEYFMT;
409a2179323SMatthew Dillon 			atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
4105ab1caedSMatthew Dillon 			dm_printf(1, "%s\n", "auth failure: bad key format");
4110c3a8cd0SMatthew Dillon 			goto done;
4120c3a8cd0SMatthew Dillon 		}
4130c3a8cd0SMatthew Dillon 	}
4140c3a8cd0SMatthew Dillon 
4150c3a8cd0SMatthew Dillon 	/*
4160c3a8cd0SMatthew Dillon 	 * Get our public and private keys
4170c3a8cd0SMatthew Dillon 	 */
4180c3a8cd0SMatthew Dillon 	free(path);
4190c3a8cd0SMatthew Dillon 	asprintf(&path, DMSG_DEFAULT_DIR "/rsa.pub");
4200c3a8cd0SMatthew Dillon 	if ((fp = fopen(path, "r")) == NULL) {
4210c3a8cd0SMatthew Dillon 		iocom->ioq_rx.error = DMSG_IOQ_ERROR_NOLKEY;
422a2179323SMatthew Dillon 		atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
4230c3a8cd0SMatthew Dillon 		goto done;
4240c3a8cd0SMatthew Dillon 	}
4250c3a8cd0SMatthew Dillon 	keys[1] = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
4260c3a8cd0SMatthew Dillon 	fclose(fp);
4270c3a8cd0SMatthew Dillon 	if (keys[1] == NULL) {
4280c3a8cd0SMatthew Dillon 		iocom->ioq_rx.error = DMSG_IOQ_ERROR_KEYFMT;
429a2179323SMatthew Dillon 		atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
4305ab1caedSMatthew Dillon 		dm_printf(1, "%s\n", "auth failure: bad host key format");
4310c3a8cd0SMatthew Dillon 		goto done;
4320c3a8cd0SMatthew Dillon 	}
4330c3a8cd0SMatthew Dillon 
4340c3a8cd0SMatthew Dillon 	free(path);
4350c3a8cd0SMatthew Dillon 	asprintf(&path, DMSG_DEFAULT_DIR "/rsa.prv");
4360c3a8cd0SMatthew Dillon 	if ((fp = fopen(path, "r")) == NULL) {
4370c3a8cd0SMatthew Dillon 		iocom->ioq_rx.error = DMSG_IOQ_ERROR_NOLKEY;
438a2179323SMatthew Dillon 		atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
4395ab1caedSMatthew Dillon 		dm_printf(1, "%s\n", "auth failure: bad host key format");
4400c3a8cd0SMatthew Dillon 		goto done;
4410c3a8cd0SMatthew Dillon 	}
4420c3a8cd0SMatthew Dillon 	keys[2] = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
4430c3a8cd0SMatthew Dillon 	fclose(fp);
4440c3a8cd0SMatthew Dillon 	if (keys[2] == NULL) {
4450c3a8cd0SMatthew Dillon 		iocom->ioq_rx.error = DMSG_IOQ_ERROR_KEYFMT;
446a2179323SMatthew Dillon 		atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
4475ab1caedSMatthew Dillon 		dm_printf(1, "%s\n", "auth failure: bad host key format");
4480c3a8cd0SMatthew Dillon 		goto done;
4490c3a8cd0SMatthew Dillon 	}
4500c3a8cd0SMatthew Dillon 	free(path);
4510c3a8cd0SMatthew Dillon 	path = NULL;
4520c3a8cd0SMatthew Dillon 
4530c3a8cd0SMatthew Dillon 	/*
4540c3a8cd0SMatthew Dillon 	 * public key encrypt/decrypt block size.
4550c3a8cd0SMatthew Dillon 	 */
4560c3a8cd0SMatthew Dillon 	if (keys[0]) {
4570c3a8cd0SMatthew Dillon 		blksize = (size_t)RSA_size(keys[0]);
4580c3a8cd0SMatthew Dillon 		if (blksize != (size_t)RSA_size(keys[1]) ||
4590c3a8cd0SMatthew Dillon 		    blksize != (size_t)RSA_size(keys[2]) ||
4600c3a8cd0SMatthew Dillon 		    sizeof(handtx) % blksize != 0) {
4610c3a8cd0SMatthew Dillon 			iocom->ioq_rx.error = DMSG_IOQ_ERROR_KEYFMT;
462a2179323SMatthew Dillon 			atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
4635ab1caedSMatthew Dillon 			dm_printf(1, "%s\n",
4645ab1caedSMatthew Dillon 				  "auth failure: key size mismatch");
4650c3a8cd0SMatthew Dillon 			goto done;
4660c3a8cd0SMatthew Dillon 		}
4670c3a8cd0SMatthew Dillon 	} else {
4680c3a8cd0SMatthew Dillon 		blksize = sizeof(handtx);
4690c3a8cd0SMatthew Dillon 	}
4700c3a8cd0SMatthew Dillon 	blkmask = blksize - 1;
4710c3a8cd0SMatthew Dillon 
4720c3a8cd0SMatthew Dillon 	bzero(&handrx, sizeof(handrx));
4730c3a8cd0SMatthew Dillon 	bzero(&handtx, sizeof(handtx));
4740c3a8cd0SMatthew Dillon 
4750c3a8cd0SMatthew Dillon 	/*
4760c3a8cd0SMatthew Dillon 	 * Fill all unused fields (particular all junk fields) with random
4770c3a8cd0SMatthew Dillon 	 * data, and also set the session key.
4780c3a8cd0SMatthew Dillon 	 */
4790c3a8cd0SMatthew Dillon 	fd = open("/dev/urandom", O_RDONLY);
4800c3a8cd0SMatthew Dillon 	if (fd < 0 ||
4810c3a8cd0SMatthew Dillon 	    fstat(fd, &st) < 0 ||	/* something wrong */
4820c3a8cd0SMatthew Dillon 	    S_ISREG(st.st_mode) ||	/* supposed to be a RNG dev! */
4830c3a8cd0SMatthew Dillon 	    read(fd, &handtx, sizeof(handtx)) != sizeof(handtx)) {
4840c3a8cd0SMatthew Dillon urandfail:
4850c3a8cd0SMatthew Dillon 		if (fd >= 0)
4860c3a8cd0SMatthew Dillon 			close(fd);
4870c3a8cd0SMatthew Dillon 		iocom->ioq_rx.error = DMSG_IOQ_ERROR_BADURANDOM;
488a2179323SMatthew Dillon 		atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
4895ab1caedSMatthew Dillon 		dm_printf(1, "%s\n", "auth failure: bad rng");
4900c3a8cd0SMatthew Dillon 		goto done;
4910c3a8cd0SMatthew Dillon 	}
4920c3a8cd0SMatthew Dillon 	if (bcmp(&handrx, &handtx, sizeof(handtx)) == 0)
4930c3a8cd0SMatthew Dillon 		goto urandfail;			/* read all zeros */
4940c3a8cd0SMatthew Dillon 	close(fd);
4950c3a8cd0SMatthew Dillon 	/* ERR_load_crypto_strings(); openssl debugging */
4960c3a8cd0SMatthew Dillon 
4970c3a8cd0SMatthew Dillon 	/*
4980c3a8cd0SMatthew Dillon 	 * Handshake with the remote.
4990c3a8cd0SMatthew Dillon 	 *
5000c3a8cd0SMatthew Dillon 	 *	Encrypt with my private and remote's public
5010c3a8cd0SMatthew Dillon 	 *	Decrypt with my private and remote's public
5020c3a8cd0SMatthew Dillon 	 *
5030c3a8cd0SMatthew Dillon 	 * When encrypting we have to make sure our buffer fits within the
5040c3a8cd0SMatthew Dillon 	 * modulus, which typically requires bit 7 o the first byte to be
5050c3a8cd0SMatthew Dillon 	 * zero.  To be safe make sure that bit 7 and bit 6 is zero.
5060c3a8cd0SMatthew Dillon 	 */
5070c3a8cd0SMatthew Dillon 	snprintf(handtx.quickmsg, sizeof(handtx.quickmsg), "Testing 1 2 3");
5080c3a8cd0SMatthew Dillon 	handtx.magic = DMSG_HDR_MAGIC;
5090c3a8cd0SMatthew Dillon 	handtx.version = 1;
5100c3a8cd0SMatthew Dillon 	handtx.flags = 0;
5110c3a8cd0SMatthew Dillon 	assert(sizeof(handtx.verf) * 4 == sizeof(handtx.sess));
5120c3a8cd0SMatthew Dillon 	bzero(handtx.verf, sizeof(handtx.verf));
5130c3a8cd0SMatthew Dillon 
5140c3a8cd0SMatthew Dillon 	handtx.pad1[0] &= 0x3f;	/* message must fit within modulus */
5150c3a8cd0SMatthew Dillon 	handtx.pad2[0] &= 0x3f;	/* message must fit within modulus */
5160c3a8cd0SMatthew Dillon 
5170c3a8cd0SMatthew Dillon 	for (i = 0; i < sizeof(handtx.sess); ++i)
5180c3a8cd0SMatthew Dillon 		handtx.verf[i / 4] ^= handtx.sess[i];
5190c3a8cd0SMatthew Dillon 
5200c3a8cd0SMatthew Dillon 	/*
5210c3a8cd0SMatthew Dillon 	 * Write handshake buffer to remote
5220c3a8cd0SMatthew Dillon 	 */
5230c3a8cd0SMatthew Dillon 	for (i = 0; i < sizeof(handtx); i += blksize) {
5240c3a8cd0SMatthew Dillon 		ptr = (char *)&handtx + i;
5250c3a8cd0SMatthew Dillon 		if (keys[0]) {
5260c3a8cd0SMatthew Dillon 			/*
5270c3a8cd0SMatthew Dillon 			 * Since we are double-encrypting we have to make
5280c3a8cd0SMatthew Dillon 			 * sure that the result of the first stage does
5290c3a8cd0SMatthew Dillon 			 * not blow out the modulus for the second stage.
5300c3a8cd0SMatthew Dillon 			 *
5310c3a8cd0SMatthew Dillon 			 * The pointer is pointing to the pad*[] area so
5320c3a8cd0SMatthew Dillon 			 * we can mess with that until the first stage
5330c3a8cd0SMatthew Dillon 			 * is legal.
5340c3a8cd0SMatthew Dillon 			 */
5350c3a8cd0SMatthew Dillon 			do {
5360c3a8cd0SMatthew Dillon 				++*(int *)(ptr + 4);
5375b601f05STomohiro Kusumi 				if (RSA_private_encrypt(blksize,
5385b601f05STomohiro Kusumi 					    (unsigned char*)ptr,
5395b601f05STomohiro Kusumi 					    (unsigned char*)buf1,
5400c3a8cd0SMatthew Dillon 					    keys[2], RSA_NO_PADDING) < 0) {
5410c3a8cd0SMatthew Dillon 					iocom->ioq_rx.error =
5420c3a8cd0SMatthew Dillon 						DMSG_IOQ_ERROR_KEYXCHGFAIL;
5430c3a8cd0SMatthew Dillon 				}
5440c3a8cd0SMatthew Dillon 			} while (buf1[0] & 0xC0);
5450c3a8cd0SMatthew Dillon 
5465b601f05STomohiro Kusumi 			if (RSA_public_encrypt(blksize,
5475b601f05STomohiro Kusumi 					    (unsigned char*)buf1,
5485b601f05STomohiro Kusumi 					    (unsigned char*)buf2,
5490c3a8cd0SMatthew Dillon 					    keys[0], RSA_NO_PADDING) < 0) {
5500c3a8cd0SMatthew Dillon 				iocom->ioq_rx.error =
5510c3a8cd0SMatthew Dillon 					DMSG_IOQ_ERROR_KEYXCHGFAIL;
5520c3a8cd0SMatthew Dillon 			}
5530c3a8cd0SMatthew Dillon 		}
5540c3a8cd0SMatthew Dillon 		if (write(iocom->sock_fd, buf2, blksize) != (ssize_t)blksize) {
5555ab1caedSMatthew Dillon 			dmio_printf(iocom, 1, "%s\n", "WRITE ERROR");
5560c3a8cd0SMatthew Dillon 		}
5570c3a8cd0SMatthew Dillon 	}
5580c3a8cd0SMatthew Dillon 	if (iocom->ioq_rx.error) {
559a2179323SMatthew Dillon 		atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
5605ab1caedSMatthew Dillon 		dmio_printf(iocom, 1, "%s\n",
5615ab1caedSMatthew Dillon 			    "auth failure: key exchange failure "
5625ab1caedSMatthew Dillon 			    "during encryption");
5630c3a8cd0SMatthew Dillon 		goto done;
5640c3a8cd0SMatthew Dillon 	}
5650c3a8cd0SMatthew Dillon 
5660c3a8cd0SMatthew Dillon 	/*
5670c3a8cd0SMatthew Dillon 	 * Read handshake buffer from remote
5680c3a8cd0SMatthew Dillon 	 */
5690c3a8cd0SMatthew Dillon 	i = 0;
5700c3a8cd0SMatthew Dillon 	while (i < sizeof(handrx)) {
5710c3a8cd0SMatthew Dillon 		ptr = (char *)&handrx + i;
5720c3a8cd0SMatthew Dillon 		n = read(iocom->sock_fd, ptr, blksize - (i & blkmask));
5730c3a8cd0SMatthew Dillon 		if (n <= 0)
5740c3a8cd0SMatthew Dillon 			break;
5750c3a8cd0SMatthew Dillon 		ptr -= (i & blkmask);
5760c3a8cd0SMatthew Dillon 		i += n;
5770c3a8cd0SMatthew Dillon 		if (keys[0] && (i & blkmask) == 0) {
5785b601f05STomohiro Kusumi 			if (RSA_private_decrypt(blksize,
5795b601f05STomohiro Kusumi 					   (unsigned char*)ptr,
5805b601f05STomohiro Kusumi 					   (unsigned char*)buf1,
5810c3a8cd0SMatthew Dillon 					   keys[2], RSA_NO_PADDING) < 0)
5820c3a8cd0SMatthew Dillon 				iocom->ioq_rx.error =
5830c3a8cd0SMatthew Dillon 						DMSG_IOQ_ERROR_KEYXCHGFAIL;
5845b601f05STomohiro Kusumi 			if (RSA_public_decrypt(blksize,
5855b601f05STomohiro Kusumi 					   (unsigned char*)buf1,
5865b601f05STomohiro Kusumi 					   (unsigned char*)ptr,
5870c3a8cd0SMatthew Dillon 					   keys[0], RSA_NO_PADDING) < 0)
5880c3a8cd0SMatthew Dillon 				iocom->ioq_rx.error =
5890c3a8cd0SMatthew Dillon 						DMSG_IOQ_ERROR_KEYXCHGFAIL;
5900c3a8cd0SMatthew Dillon 		}
5910c3a8cd0SMatthew Dillon 	}
5920c3a8cd0SMatthew Dillon 	if (iocom->ioq_rx.error) {
593a2179323SMatthew Dillon 		atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
5945ab1caedSMatthew Dillon 		dmio_printf(iocom, 1, "%s\n",
5955ab1caedSMatthew Dillon 			    "auth failure: key exchange failure "
5965ab1caedSMatthew Dillon 			    "during decryption");
5970c3a8cd0SMatthew Dillon 		goto done;
5980c3a8cd0SMatthew Dillon 	}
5990c3a8cd0SMatthew Dillon 
6000c3a8cd0SMatthew Dillon 	/*
6010c3a8cd0SMatthew Dillon 	 * Validate the received data.  Try to make this a constant-time
6020c3a8cd0SMatthew Dillon 	 * algorithm.
6030c3a8cd0SMatthew Dillon 	 */
6040c3a8cd0SMatthew Dillon 	if (i != sizeof(handrx)) {
6050c3a8cd0SMatthew Dillon keyxchgfail:
6060c3a8cd0SMatthew Dillon 		iocom->ioq_rx.error = DMSG_IOQ_ERROR_KEYXCHGFAIL;
607a2179323SMatthew Dillon 		atomic_set_int(&iocom->flags, DMSG_IOCOMF_EOF);
6085ab1caedSMatthew Dillon 		dmio_printf(iocom, 1, "%s\n",
6095ab1caedSMatthew Dillon 			    "auth failure: key exchange failure");
6100c3a8cd0SMatthew Dillon 		goto done;
6110c3a8cd0SMatthew Dillon 	}
6120c3a8cd0SMatthew Dillon 
6130c3a8cd0SMatthew Dillon 	if (handrx.magic == DMSG_HDR_MAGIC_REV) {
6140c3a8cd0SMatthew Dillon 		handrx.version = bswap16(handrx.version);
6150c3a8cd0SMatthew Dillon 		handrx.flags = bswap32(handrx.flags);
6160c3a8cd0SMatthew Dillon 	}
6170c3a8cd0SMatthew Dillon 	for (i = 0; i < sizeof(handrx.sess); ++i)
6180c3a8cd0SMatthew Dillon 		handrx.verf[i / 4] ^= handrx.sess[i];
6190c3a8cd0SMatthew Dillon 	n = 0;
6200c3a8cd0SMatthew Dillon 	for (i = 0; i < sizeof(handrx.verf); ++i)
6210c3a8cd0SMatthew Dillon 		n += handrx.verf[i];
6220c3a8cd0SMatthew Dillon 	if (handrx.version != 1)
6230c3a8cd0SMatthew Dillon 		++n;
6240c3a8cd0SMatthew Dillon 	if (n != 0)
6250c3a8cd0SMatthew Dillon 		goto keyxchgfail;
6260c3a8cd0SMatthew Dillon 
6270c3a8cd0SMatthew Dillon 	/*
6280c3a8cd0SMatthew Dillon 	 * Use separate session keys and session fixed IVs for receive and
6290c3a8cd0SMatthew Dillon 	 * transmit.
6300c3a8cd0SMatthew Dillon 	 */
631cb4d9e67STomohiro Kusumi 	error = crypto_algos[DMSG_CRYPTO_ALGO].init(&iocom->ioq_rx,
632cb4d9e67STomohiro Kusumi 	    (char*)handrx.sess,
6330c3a8cd0SMatthew Dillon 	    crypto_algos[DMSG_CRYPTO_ALGO].keylen,
634cb4d9e67STomohiro Kusumi 	    (char*)handrx.sess + crypto_algos[DMSG_CRYPTO_ALGO].keylen,
6350c3a8cd0SMatthew Dillon 	    sizeof(handrx.sess) - crypto_algos[DMSG_CRYPTO_ALGO].keylen,
6360c3a8cd0SMatthew Dillon 	    0 /* decryption */);
6370c3a8cd0SMatthew Dillon 	if (error)
6380c3a8cd0SMatthew Dillon 		goto keyxchgfail;
6390c3a8cd0SMatthew Dillon 
640cb4d9e67STomohiro Kusumi 	error = crypto_algos[DMSG_CRYPTO_ALGO].init(&iocom->ioq_tx,
641cb4d9e67STomohiro Kusumi 	    (char*)handtx.sess,
6420c3a8cd0SMatthew Dillon 	    crypto_algos[DMSG_CRYPTO_ALGO].keylen,
643cb4d9e67STomohiro Kusumi 	    (char*)handtx.sess + crypto_algos[DMSG_CRYPTO_ALGO].keylen,
6440c3a8cd0SMatthew Dillon 	    sizeof(handtx.sess) - crypto_algos[DMSG_CRYPTO_ALGO].keylen,
6450c3a8cd0SMatthew Dillon 	    1 /* encryption */);
6460c3a8cd0SMatthew Dillon 	if (error)
6470c3a8cd0SMatthew Dillon 		goto keyxchgfail;
6480c3a8cd0SMatthew Dillon 
649a2179323SMatthew Dillon 	atomic_set_int(&iocom->flags, DMSG_IOCOMF_CRYPTED);
6500c3a8cd0SMatthew Dillon 
6515ab1caedSMatthew Dillon 	dmio_printf(iocom, 1, "auth success: %s\n", handrx.quickmsg);
6520c3a8cd0SMatthew Dillon done:
6530c3a8cd0SMatthew Dillon 	if (path)
6540c3a8cd0SMatthew Dillon 		free(path);
6550c3a8cd0SMatthew Dillon 	if (keys[0])
6560c3a8cd0SMatthew Dillon 		RSA_free(keys[0]);
6570c3a8cd0SMatthew Dillon 	if (keys[1])
6580c3a8cd0SMatthew Dillon 		RSA_free(keys[1]);
659311b17b8SSascha Wildner 	if (keys[2])
6600c3a8cd0SMatthew Dillon 		RSA_free(keys[2]);
6610c3a8cd0SMatthew Dillon }
6620c3a8cd0SMatthew Dillon 
663*a988b43eSMatthew Dillon void
dmsg_crypto_terminate(dmsg_iocom_t * iocom)664*a988b43eSMatthew Dillon dmsg_crypto_terminate(dmsg_iocom_t *iocom)
665*a988b43eSMatthew Dillon {
666*a988b43eSMatthew Dillon 	crypto_algos[DMSG_CRYPTO_ALGO].uninit(&iocom->ioq_rx);
667*a988b43eSMatthew Dillon 	crypto_algos[DMSG_CRYPTO_ALGO].uninit(&iocom->ioq_tx);
668*a988b43eSMatthew Dillon }
669*a988b43eSMatthew Dillon 
6700c3a8cd0SMatthew Dillon /*
6710c3a8cd0SMatthew Dillon  * Decrypt pending data in the ioq's fifo.  The data is decrypted in-place.
6720c3a8cd0SMatthew Dillon  */
6730c3a8cd0SMatthew Dillon void
dmsg_crypto_decrypt(dmsg_iocom_t * iocom __unused,dmsg_ioq_t * ioq)6740c3a8cd0SMatthew Dillon dmsg_crypto_decrypt(dmsg_iocom_t *iocom __unused, dmsg_ioq_t *ioq)
6750c3a8cd0SMatthew Dillon {
6760c3a8cd0SMatthew Dillon 	int p_len;
6770c3a8cd0SMatthew Dillon 	int used;
678579191cbSSascha Wildner 	__unused int error;	/* XXX */
6790c3a8cd0SMatthew Dillon 	char buf[512];
6800c3a8cd0SMatthew Dillon 
6810c3a8cd0SMatthew Dillon 	/*
6820c3a8cd0SMatthew Dillon 	 * fifo_beg to fifo_cdx is data already decrypted.
6830c3a8cd0SMatthew Dillon 	 * fifo_cdn to fifo_end is data not yet decrypted.
6840c3a8cd0SMatthew Dillon 	 */
6850c3a8cd0SMatthew Dillon 	p_len = ioq->fifo_end - ioq->fifo_cdn; /* data not yet decrypted */
6860c3a8cd0SMatthew Dillon 
6870c3a8cd0SMatthew Dillon 	if (p_len == 0)
6880c3a8cd0SMatthew Dillon 		return;
6890c3a8cd0SMatthew Dillon 
690*a988b43eSMatthew Dillon 	while (p_len >= DMSG_CRYPTO_CHUNK_SIZE) {
691*a988b43eSMatthew Dillon 		bcopy(ioq->buf + ioq->fifo_cdn, buf, DMSG_CRYPTO_CHUNK_SIZE);
6920c3a8cd0SMatthew Dillon 		error = crypto_algos[DMSG_CRYPTO_ALGO].dec_chunk(
6930c3a8cd0SMatthew Dillon 		    ioq, buf,
6940c3a8cd0SMatthew Dillon 		    ioq->buf + ioq->fifo_cdx,
6950c3a8cd0SMatthew Dillon 		    DMSG_CRYPTO_CHUNK_SIZE,
6960c3a8cd0SMatthew Dillon 		    &used);
6970c3a8cd0SMatthew Dillon #ifdef CRYPTO_DEBUG
6985ab1caedSMatthew Dillon 		dmio_printf(iocom, 5,
6995ab1caedSMatthew Dillon 			    "dec: p_len: %d, used: %d, "
7005ab1caedSMatthew Dillon 			    "fifo_cdn: %ju, fifo_cdx: %ju\n",
7015ab1caedSMatthew Dillon 			     p_len, used,
7025ab1caedSMatthew Dillon 			     ioq->fifo_cdn, ioq->fifo_cdx);
7030c3a8cd0SMatthew Dillon #endif
7040c3a8cd0SMatthew Dillon 		p_len -= used;
7050c3a8cd0SMatthew Dillon 		ioq->fifo_cdn += used;
7060c3a8cd0SMatthew Dillon 		ioq->fifo_cdx += DMSG_CRYPTO_CHUNK_SIZE;
7070c3a8cd0SMatthew Dillon #ifdef CRYPTO_DEBUG
7085ab1caedSMatthew Dillon 		dmio_printf(iocom, 5,
7095ab1caedSMatthew Dillon 			    "dec: p_len: %d, used: %d, "
7105ab1caedSMatthew Dillon 			    "fifo_cdn: %ju, fifo_cdx: %ju\n",
7110c3a8cd0SMatthew Dillon 			    p_len, used, ioq->fifo_cdn, ioq->fifo_cdx);
7120c3a8cd0SMatthew Dillon #endif
7130c3a8cd0SMatthew Dillon 	}
7140c3a8cd0SMatthew Dillon }
7150c3a8cd0SMatthew Dillon 
7160c3a8cd0SMatthew Dillon /*
7170c3a8cd0SMatthew Dillon  * *nactp is set to the number of ORIGINAL bytes consumed by the encrypter.
7180c3a8cd0SMatthew Dillon  * The FIFO may contain more data.
7190c3a8cd0SMatthew Dillon  */
7200c3a8cd0SMatthew Dillon int
dmsg_crypto_encrypt(dmsg_iocom_t * iocom __unused,dmsg_ioq_t * ioq,struct iovec * iov,int n,size_t * nactp)7210c3a8cd0SMatthew Dillon dmsg_crypto_encrypt(dmsg_iocom_t *iocom __unused, dmsg_ioq_t *ioq,
7220c3a8cd0SMatthew Dillon 		    struct iovec *iov, int n, size_t *nactp)
7230c3a8cd0SMatthew Dillon {
7240c3a8cd0SMatthew Dillon 	int p_len, used, ct_used;
7250c3a8cd0SMatthew Dillon 	int i;
726579191cbSSascha Wildner 	__unused int error;	/* XXX */
7270c3a8cd0SMatthew Dillon 	size_t nmax;
7280c3a8cd0SMatthew Dillon 
7290c3a8cd0SMatthew Dillon 	nmax = sizeof(ioq->buf) - ioq->fifo_end;	/* max new bytes */
7300c3a8cd0SMatthew Dillon 
7310c3a8cd0SMatthew Dillon 	*nactp = 0;
7320c3a8cd0SMatthew Dillon 	for (i = 0; i < n && nmax; ++i) {
7330c3a8cd0SMatthew Dillon 		used = 0;
7340c3a8cd0SMatthew Dillon 		p_len = iov[i].iov_len;
7350c3a8cd0SMatthew Dillon 		assert((p_len & DMSG_ALIGNMASK) == 0);
7360c3a8cd0SMatthew Dillon 
7370c3a8cd0SMatthew Dillon 		while (p_len >= DMSG_CRYPTO_CHUNK_SIZE &&
738*a988b43eSMatthew Dillon 		    nmax >= DMSG_CRYPTO_CHUNK_SIZE)
739*a988b43eSMatthew Dillon 		{
7400c3a8cd0SMatthew Dillon 			error = crypto_algos[DMSG_CRYPTO_ALGO].enc_chunk(
7410c3a8cd0SMatthew Dillon 			    ioq,
7420c3a8cd0SMatthew Dillon 			    ioq->buf + ioq->fifo_cdx,
7430c3a8cd0SMatthew Dillon 			    (char *)iov[i].iov_base + used,
7440c3a8cd0SMatthew Dillon 			    DMSG_CRYPTO_CHUNK_SIZE, &ct_used);
7450c3a8cd0SMatthew Dillon #ifdef CRYPTO_DEBUG
7465ab1caedSMatthew Dillon 			dmio_printf(iocom, 5,
7475ab1caedSMatthew Dillon 				    "nactp: %ju, p_len: %d, "
7485ab1caedSMatthew Dillon 				    "ct_used: %d, used: %d, nmax: %ju\n",
7490c3a8cd0SMatthew Dillon 				    *nactp, p_len, ct_used, used, nmax);
7500c3a8cd0SMatthew Dillon #endif
7510c3a8cd0SMatthew Dillon 
7520c3a8cd0SMatthew Dillon 			*nactp += (size_t)DMSG_CRYPTO_CHUNK_SIZE;	/* plaintext count */
7530c3a8cd0SMatthew Dillon 			used += DMSG_CRYPTO_CHUNK_SIZE;
7540c3a8cd0SMatthew Dillon 			p_len -= DMSG_CRYPTO_CHUNK_SIZE;
7550c3a8cd0SMatthew Dillon 
756f2239a4eSMatthew Dillon 			/*
757f2239a4eSMatthew Dillon 			 * NOTE: crypted count will eventually differ from
758f2239a4eSMatthew Dillon 			 *	 nmax, but for now we have not yet introduced
759f2239a4eSMatthew Dillon 			 *	 random armor.
760f2239a4eSMatthew Dillon 			 */
761f2239a4eSMatthew Dillon 			ioq->fifo_cdx += (size_t)ct_used;
762f2239a4eSMatthew Dillon 			ioq->fifo_cdn += (size_t)ct_used;
7630c3a8cd0SMatthew Dillon 			ioq->fifo_end += (size_t)ct_used;
7640c3a8cd0SMatthew Dillon 			nmax -= (size_t)ct_used;
7650c3a8cd0SMatthew Dillon #ifdef CRYPTO_DEBUG
7665ab1caedSMatthew Dillon 			dmio_printf(iocom, 5,
7675ab1caedSMatthew Dillon 				    "nactp: %ju, p_len: %d, "
7685ab1caedSMatthew Dillon 				    "ct_used: %d, used: %d, nmax: %ju\n",
7690c3a8cd0SMatthew Dillon 				    *nactp, p_len, ct_used, used, nmax);
7700c3a8cd0SMatthew Dillon #endif
7710c3a8cd0SMatthew Dillon 		}
7720c3a8cd0SMatthew Dillon 	}
7730c3a8cd0SMatthew Dillon 	iov[0].iov_base = ioq->buf + ioq->fifo_beg;
7740c3a8cd0SMatthew Dillon 	iov[0].iov_len = ioq->fifo_cdx - ioq->fifo_beg;
7750c3a8cd0SMatthew Dillon 
7760c3a8cd0SMatthew Dillon 	return (1);
7770c3a8cd0SMatthew Dillon }
778