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