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