| Commit | Line | Data |
|---|---|---|
| 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 | */ | |
| 43 | static pthread_mutex_t *crypto_locks; | |
| 44 | int crypto_count; | |
| 45 | ||
| 487b3235 AH |
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 | ||
| 02454b3e MD |
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 | ||
| 766ad73f AH |
95 | static |
| 96 | int | |
| 487b3235 AH |
97 | hammer2_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 | ||
| 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 | ||
| 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 | ||
| 198 | static | |
| 199 | int | |
| 487b3235 AH |
200 | hammer2_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 | ||
| 238 | fail: | |
| fd1d02a5 AH |
239 | ioq->error = HAMMER2_IOQ_ERROR_ALGO; |
| 240 | fail_out: | |
| 766ad73f AH |
241 | if (DebugOpt) |
| 242 | fprintf(stderr, "error during encrypt_chunk\n"); | |
| 243 | return -1; | |
| 244 | } | |
| 245 | ||
| 246 | static | |
| 247 | int | |
| 487b3235 AH |
248 | hammer2_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 | ||
| 289 | fail: | |
| fd1d02a5 AH |
290 | ioq->error = HAMMER2_IOQ_ERROR_MACFAIL; |
| 291 | fail_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 | */ | |
| 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; | |
| 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)) { | |
| 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); | |
| 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)) { | |
| 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 | ||
| 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 |
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 | } | |
| 5cf97ec5 MD |
670 | |
| 671 | /* | |
| 672 | * Decrypt pending data in the ioq's fifo. The data is decrypted in-place. | |
| 673 | */ | |
| 674 | void | |
| 3033ecc8 | 675 | hammer2_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 | 719 | int |
| 3033ecc8 MD |
720 | hammer2_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 | } |