hammer2 - further crypto cleanup
[dragonfly.git] / sbin / hammer2 / crypto.c
1 /*
2  * Copyright (c) 2011-2012 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@dragonflybsd.org>
6  * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org>
7  * by Alex Hornung <alexh@dragonflybsd.org>
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  * 3. Neither the name of The DragonFly Project nor the names of its
20  *    contributors may be used to endorse or promote products derived
21  *    from this software without specific, prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
27  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36
37 #include "hammer2.h"
38 #include <sys/endian.h>
39
40 /*
41  * Setup crypto for pthreads
42  */
43 static pthread_mutex_t *crypto_locks;
44 int crypto_count;
45
46 static int hammer2_crypto_gcm_init(hammer2_ioq_t *, char *, int, char *, int, int);
47 static int hammer2_crypto_gcm_encrypt_chunk(hammer2_ioq_t *, char *, char *, int, int *);
48 static 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  */
54 static 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
66 static
67 unsigned long
68 hammer2_crypto_id_callback(void)
69 {
70         return ((unsigned long)(uintptr_t)pthread_self());
71 }
72
73 static
74 void
75 hammer2_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
86 void
87 hammer2_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
95 static
96 int
97 hammer2_crypto_gcm_init(hammer2_ioq_t *ioq, char *key, int klen,
98                         char *iv_fixed, int ivlen, int enc)
99 {
100         int i, ok;
101
102         if (klen < HAMMER2_CRYPTO_GCM_KEY_SIZE ||
103             ivlen < HAMMER2_CRYPTO_GCM_IV_FIXED_SIZE) {
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");
110         for (i = 0; i < HAMMER2_CRYPTO_GCM_KEY_SIZE; ++i)
111                 printf("%02x", (unsigned char)key[i]);
112         printf("\n");
113
114         printf("%s iv:  ", enc ? "Encryption" : "Decryption");
115         for (i = 0; i < HAMMER2_CRYPTO_GCM_IV_FIXED_SIZE; ++i)
116                 printf("%02x", (unsigned char)iv_fixed[i]);
117         printf(" (fixed part only)\n");
118
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,
149                                  HAMMER2_CRYPTO_GCM_IV_SIZE, NULL);
150         if (!ok)
151                 goto fail;
152
153         memset(ioq->iv, 0, HAMMER2_CRYPTO_GCM_IV_SIZE);
154         memcpy(ioq->iv, iv_fixed, HAMMER2_CRYPTO_GCM_IV_FIXED_SIZE);
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
168 fail:
169         if (DebugOpt)
170                 fprintf(stderr, "Error during _gcm_init\n");
171         return -1;
172 }
173
174 static
175 int
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
186         uint64_t *c = (uint64_t *)(&iv[HAMMER2_CRYPTO_GCM_IV_FIXED_SIZE]);
187
188         /* Increment invocation field integer counter */
189         *c = htobe64(be64toh(*c)+1);
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          */
195         return (c == 0) ? 0 : 1;
196 }
197
198 static
199 int
200 hammer2_crypto_gcm_encrypt_chunk(hammer2_ioq_t *ioq, char *ct, char *pt,
201                                  int in_size, int *out_size)
202 {
203         int ok;
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,
223                                  HAMMER2_CRYPTO_GCM_TAG_SIZE,
224                                  ct + u_len + f_len);
225         if (!ok)
226                 goto fail;
227
228         ok = _gcm_iv_increment(ioq->iv);
229         if (!ok) {
230                 ioq->error = HAMMER2_IOQ_ERROR_IVWRAP;
231                 goto fail_out;
232         }
233
234         *out_size = u_len + f_len + HAMMER2_CRYPTO_GCM_TAG_SIZE;
235
236         return 0;
237
238 fail:
239         ioq->error = HAMMER2_IOQ_ERROR_ALGO;
240 fail_out:
241         if (DebugOpt)
242                 fprintf(stderr, "error during encrypt_chunk\n");
243         return -1;
244 }
245
246 static
247 int
248 hammer2_crypto_gcm_decrypt_chunk(hammer2_ioq_t *ioq, char *ct, char *pt,
249                                  int out_size, int *consume_size)
250 {
251         int ok;
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);
258         if (!ok) {
259                 ioq->error = HAMMER2_IOQ_ERROR_ALGO;
260                 goto fail_out;
261         }
262
263         ok = EVP_CIPHER_CTX_ctrl(&ioq->ctx, EVP_CTRL_GCM_SET_TAG,
264                                  HAMMER2_CRYPTO_GCM_TAG_SIZE,
265                                  ct + out_size);
266         if (!ok) {
267                 ioq->error = HAMMER2_IOQ_ERROR_ALGO;
268                 goto fail_out;
269         }
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
279         ok = _gcm_iv_increment(ioq->iv);
280         if (!ok) {
281                 ioq->error = HAMMER2_IOQ_ERROR_IVWRAP;
282                 goto fail_out;
283         }
284
285         *consume_size = u_len + f_len + HAMMER2_CRYPTO_GCM_TAG_SIZE;
286
287         return 0;
288
289 fail:
290         ioq->error = HAMMER2_IOQ_ERROR_MACFAIL;
291 fail_out:
292         if (DebugOpt)
293                 fprintf(stderr, "error during decrypt_chunk (likely authentication error)\n");
294         return -1;
295 }
296
297 /*
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  */
335 typedef union {
336         struct sockaddr sa;
337         struct sockaddr_in sa_in;
338         struct sockaddr_in6 sa_in6;
339 } sockaddr_any_t;
340
341 void
342 hammer2_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;
350         char buf1[sizeof(handtx)];
351         char buf2[sizeof(handtx)];
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;
362         int error;
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
394          *
395          * If the link is not to be encrypted (<ip>.none located) we shortcut
396          * the handshake entirely.  No buffers are exchanged.
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");
412                 goto done;
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)) {
500 urandfail:
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);
512         /* ERR_load_crypto_strings(); openssl debugging */
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);
554                                 if (RSA_private_encrypt(blksize, ptr, buf1,
555                                             keys[2], RSA_NO_PADDING) < 0) {
556                                         iocom->ioq_rx.error =
557                                                 HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
558                                 }
559                         } while (buf1[0] & 0xC0);
560
561                         if (RSA_public_encrypt(blksize, buf1, buf2,
562                                             keys[0], RSA_NO_PADDING) < 0) {
563                                 iocom->ioq_rx.error =
564                                         HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
565                         }
566                 }
567                 if (write(iocom->sock_fd, buf2, blksize) != (ssize_t)blksize) {
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) {
591                         if (RSA_private_decrypt(blksize, ptr, buf1,
592                                            keys[2], RSA_NO_PADDING) < 0)
593                                 iocom->ioq_rx.error =
594                                                 HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
595                         if (RSA_public_decrypt(blksize, buf1, ptr,
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)) {
614 keyxchgfail:
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
636         /*
637          * Use separate session keys and session fixed IVs for receive and
638          * transmit.
639          */
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,
644             0 /* decryption */);
645         if (error)
646                 goto keyxchgfail;
647
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,
652             1 /* encryption */);
653         if (error)
654                 goto keyxchgfail;
655
656         iocom->flags |= HAMMER2_IOCOMF_CRYPTED;
657
658         if (DebugOpt)
659                 fprintf(stderr, "auth success: %s\n", handrx.quickmsg);
660 done:
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 }
670
671 /*
672  * Decrypt pending data in the ioq's fifo.  The data is decrypted in-place.
673  */
674 void
675 hammer2_crypto_decrypt(hammer2_iocom_t *iocom __unused, hammer2_ioq_t *ioq)
676 {
677         int p_len;
678         int used;
679         int error;
680         char buf[512];
681
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 */
687
688         if (p_len == 0)
689                 return;
690
691         while (p_len >= crypto_algos[HAMMER2_CRYPTO_ALGO].taglen +
692             HAMMER2_CRYPTO_CHUNK_SIZE) {
693                 bcopy(ioq->buf + ioq->fifo_cdn, buf,
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);
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
712         }
713 }
714
715 /*
716  * *nactp is set to the number of ORIGINAL bytes consumed by the encrypter.
717  * The FIFO may contain more data.
718  */
719 int
720 hammer2_crypto_encrypt(hammer2_iocom_t *iocom __unused, hammer2_ioq_t *ioq,
721                        struct iovec *iov, int n, size_t *nactp)
722 {
723         int p_len, used, ct_used;
724         int i;
725         int error;
726         size_t nmax;
727
728         nmax = sizeof(ioq->buf) - ioq->fifo_end;        /* max new bytes */
729
730         *nactp = 0;
731         for (i = 0; i < n && nmax; ++i) {
732                 used = 0;
733                 p_len = iov[i].iov_len;
734                 assert((p_len & HAMMER2_MSG_ALIGNMASK) == 0);
735
736                 while (p_len >= HAMMER2_CRYPTO_CHUNK_SIZE &&
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,
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                 }
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 }