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