| 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 | */ | |
| 178 | return (c == 0) ? -1 : 0; | |
| 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 | ||
| 236 | _gcm_iv_increment(ioq->iv); | |
| 237 | ||
| 238 | *out_size = u_len + f_len + HAMMER2_CRYPTO_TAG_SIZE; | |
| 239 | ||
| 240 | return 0; | |
| 241 | ||
| 242 | fail: | |
| 243 | if (DebugOpt) | |
| 244 | fprintf(stderr, "error during encrypt_chunk\n"); | |
| 245 | return -1; | |
| 246 | } | |
| 247 | ||
| 248 | static | |
| 249 | int | |
| 250 | hammer2_crypto_decrypt_chunk(hammer2_ioq_t *ioq, char *ct, char *pt, | |
| 251 | int out_size, int *consume_size) | |
| 252 | { | |
| 253 | int ok; | |
| 254 | #ifdef CRYPTO_DEBUG | |
| 255 | int i; | |
| 256 | #endif | |
| 257 | int u_len, f_len; | |
| 258 | ||
| 259 | *consume_size = 0; | |
| 260 | ||
| 261 | /* Re-initialize with new IV (but without redoing the key schedule) */ | |
| 262 | ok = EVP_DecryptInit_ex(&ioq->ctx, NULL, NULL, NULL, ioq->iv); | |
| 263 | if (!ok) | |
| 264 | goto fail; | |
| 265 | ||
| 266 | #ifdef CRYPTO_DEBUG | |
| 267 | printf("dec_chunk iv: "); | |
| 268 | for (i = 0; i < HAMMER2_CRYPTO_IV_SIZE; i++) | |
| 269 | printf("%02x", (unsigned char)ioq->iv[i]); | |
| 270 | printf("\n"); | |
| 271 | ||
| 272 | printf("dec_chunk ct: "); | |
| 273 | for (i = 0; i < out_size; i++) | |
| 274 | printf("%02x", (unsigned char)ct[i]); | |
| 275 | printf("\n"); | |
| 276 | ||
| 277 | printf("dec_chunk tag: "); | |
| 278 | for (i = 0; i < HAMMER2_CRYPTO_TAG_SIZE; i++) | |
| 279 | printf("%02x", (unsigned char)ct[out_size + i]); | |
| 280 | printf("\n"); | |
| 281 | #endif | |
| 282 | ||
| 283 | ok = EVP_CIPHER_CTX_ctrl(&ioq->ctx, EVP_CTRL_GCM_SET_TAG, | |
| 284 | HAMMER2_CRYPTO_TAG_SIZE, | |
| 285 | ct + out_size); | |
| 286 | if (!ok) | |
| 287 | goto fail; | |
| 288 | ||
| 289 | ok = EVP_DecryptUpdate(&ioq->ctx, pt, &u_len, ct, out_size); | |
| 290 | if (!ok) | |
| 291 | goto fail; | |
| 292 | ||
| 293 | ok = EVP_DecryptFinal(&ioq->ctx, pt + u_len, &f_len); | |
| 294 | if (!ok) | |
| 295 | goto fail; | |
| 296 | ||
| 297 | _gcm_iv_increment(ioq->iv); | |
| 298 | ||
| 299 | *consume_size = u_len + f_len + HAMMER2_CRYPTO_TAG_SIZE; | |
| 300 | ||
| 301 | #ifdef CRYPTO_DEBUG | |
| 302 | printf("dec_chunk pt: "); | |
| 303 | for (i = 0; i < out_size; i++) | |
| 304 | printf("%02x", (unsigned char)pt[i]); | |
| 305 | printf("\n"); | |
| 306 | #endif | |
| 307 | ||
| 308 | return 0; | |
| 309 | ||
| 310 | fail: | |
| 311 | if (DebugOpt) | |
| 312 | fprintf(stderr, "error during decrypt_chunk (likely authentication error)\n"); | |
| 313 | return -1; | |
| 314 | } | |
| 315 | ||
| 02454b3e | 316 | /* |
| 62efe6ec MD |
317 | * Synchronously negotiate crypto for a new session. This must occur |
| 318 | * within 10 seconds or the connection is error'd out. | |
| 319 | * | |
| 320 | * We work off the IP address and/or reverse DNS. The IP address is | |
| 321 | * checked first, followed by the IP address at various levels of granularity, | |
| 322 | * followed by the full domain name and domain names at various levels of | |
| 323 | * granularity. | |
| 324 | * | |
| 325 | * /etc/hammer2/remote/<name>.pub - Contains a public key | |
| 326 | * /etc/hammer2/remote/<name>.none - Indicates no encryption (empty file) | |
| 327 | * (e.g. localhost.none). | |
| 328 | * | |
| 329 | * We first attempt to locate a public key file based on the peer address or | |
| 330 | * peer FQDN. | |
| 331 | * | |
| 332 | * <name>.none - No further negotiation is needed. We simply return. | |
| 333 | * All communication proceeds without encryption. | |
| 334 | * No public key handshake occurs in this situation. | |
| 335 | * (both ends must match). | |
| 336 | * | |
| 337 | * <name>.pub - We have located the public key for the peer. Both | |
| 338 | * sides transmit a block encrypted with their private | |
| 339 | * keys and the peer's public key. | |
| 340 | * | |
| 341 | * Both sides receive a block and decrypt it. | |
| 342 | * | |
| 343 | * Both sides formulate a reply using the decrypted | |
| 344 | * block and transmit it. | |
| 345 | * | |
| 346 | * communication proceeds with the negotiated session | |
| 347 | * key (typically AES-256-CBC). | |
| 348 | * | |
| 349 | * If we fail to locate the appropriate file and no floating.db exists the | |
| 350 | * connection is terminated without further action. | |
| 351 | * | |
| 352 | * If floating.db exists the connection proceeds with a floating negotiation. | |
| 353 | */ | |
| 354 | typedef union { | |
| 355 | struct sockaddr sa; | |
| 356 | struct sockaddr_in sa_in; | |
| 357 | struct sockaddr_in6 sa_in6; | |
| 358 | } sockaddr_any_t; | |
| 359 | ||
| 360 | void | |
| 361 | hammer2_crypto_negotiate(hammer2_iocom_t *iocom) | |
| 362 | { | |
| 363 | sockaddr_any_t sa; | |
| 364 | socklen_t salen = sizeof(sa); | |
| 365 | char peername[128]; | |
| 366 | char realname[128]; | |
| 367 | hammer2_handshake_t handtx; | |
| 368 | hammer2_handshake_t handrx; | |
| 5cf97ec5 MD |
369 | char buf1[sizeof(handtx)]; |
| 370 | char buf2[sizeof(handtx)]; | |
| 62efe6ec MD |
371 | char *ptr; |
| 372 | char *path; | |
| 373 | struct stat st; | |
| 374 | FILE *fp; | |
| 375 | RSA *keys[3] = { NULL, NULL, NULL }; | |
| 376 | size_t i; | |
| 377 | size_t blksize; | |
| 378 | size_t blkmask; | |
| 379 | ssize_t n; | |
| 380 | int fd; | |
| 766ad73f | 381 | int error; |
| 62efe6ec MD |
382 | |
| 383 | /* | |
| 384 | * Get the peer IP address for the connection as a string. | |
| 385 | */ | |
| 386 | if (getpeername(iocom->sock_fd, &sa.sa, &salen) < 0) { | |
| 387 | iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_NOPEER; | |
| 388 | iocom->flags |= HAMMER2_IOCOMF_EOF; | |
| 389 | if (DebugOpt) | |
| 390 | fprintf(stderr, "accept: getpeername() failed\n"); | |
| 391 | goto done; | |
| 392 | } | |
| 393 | if (getnameinfo(&sa.sa, salen, peername, sizeof(peername), | |
| 394 | NULL, 0, NI_NUMERICHOST) < 0) { | |
| 395 | iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_NOPEER; | |
| 396 | iocom->flags |= HAMMER2_IOCOMF_EOF; | |
| 397 | if (DebugOpt) | |
| 398 | fprintf(stderr, "accept: cannot decode sockaddr\n"); | |
| 399 | goto done; | |
| 400 | } | |
| 401 | if (DebugOpt) { | |
| 402 | if (realhostname_sa(realname, sizeof(realname), | |
| 403 | &sa.sa, salen) == HOSTNAME_FOUND) { | |
| 404 | fprintf(stderr, "accept from %s (%s)\n", | |
| 405 | peername, realname); | |
| 406 | } else { | |
| 407 | fprintf(stderr, "accept from %s\n", peername); | |
| 408 | } | |
| 409 | } | |
| 410 | ||
| 411 | /* | |
| 412 | * Find the remote host's public key | |
| 9b8b748f MD |
413 | * |
| 414 | * If the link is not to be encrypted (<ip>.none located) we shortcut | |
| 415 | * the handshake entirely. No buffers are exchanged. | |
| 62efe6ec MD |
416 | */ |
| 417 | asprintf(&path, "%s/%s.pub", HAMMER2_PATH_REMOTE, peername); | |
| 418 | if ((fp = fopen(path, "r")) == NULL) { | |
| 419 | free(path); | |
| 420 | asprintf(&path, "%s/%s.none", | |
| 421 | HAMMER2_PATH_REMOTE, peername); | |
| 422 | if (stat(path, &st) < 0) { | |
| 423 | iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_NORKEY; | |
| 424 | iocom->flags |= HAMMER2_IOCOMF_EOF; | |
| 425 | if (DebugOpt) | |
| 426 | fprintf(stderr, "auth failure: unknown host\n"); | |
| 427 | goto done; | |
| 428 | } | |
| 429 | if (DebugOpt) | |
| 430 | fprintf(stderr, "auth succeeded, unencrypted link\n"); | |
| 9b8b748f | 431 | goto done; |
| 62efe6ec MD |
432 | } |
| 433 | if (fp) { | |
| 434 | keys[0] = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL); | |
| 435 | fclose(fp); | |
| 436 | if (keys[0] == NULL) { | |
| 437 | iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_KEYFMT; | |
| 438 | iocom->flags |= HAMMER2_IOCOMF_EOF; | |
| 439 | if (DebugOpt) | |
| 440 | fprintf(stderr, | |
| 441 | "auth failure: bad key format\n"); | |
| 442 | goto done; | |
| 443 | } | |
| 444 | } | |
| 445 | ||
| 446 | /* | |
| 447 | * Get our public and private keys | |
| 448 | */ | |
| 449 | free(path); | |
| 450 | asprintf(&path, HAMMER2_DEFAULT_DIR "/rsa.pub"); | |
| 451 | if ((fp = fopen(path, "r")) == NULL) { | |
| 452 | iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_NOLKEY; | |
| 453 | iocom->flags |= HAMMER2_IOCOMF_EOF; | |
| 454 | goto done; | |
| 455 | } | |
| 456 | keys[1] = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL); | |
| 457 | fclose(fp); | |
| 458 | if (keys[1] == 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 | ||
| 466 | free(path); | |
| 467 | asprintf(&path, HAMMER2_DEFAULT_DIR "/rsa.prv"); | |
| 468 | if ((fp = fopen(path, "r")) == NULL) { | |
| 469 | iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_NOLKEY; | |
| 470 | iocom->flags |= HAMMER2_IOCOMF_EOF; | |
| 471 | if (DebugOpt) | |
| 472 | fprintf(stderr, "auth failure: bad host key format\n"); | |
| 473 | goto done; | |
| 474 | } | |
| 475 | keys[2] = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL); | |
| 476 | fclose(fp); | |
| 477 | if (keys[2] == NULL) { | |
| 478 | iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_KEYFMT; | |
| 479 | iocom->flags |= HAMMER2_IOCOMF_EOF; | |
| 480 | if (DebugOpt) | |
| 481 | fprintf(stderr, "auth failure: bad host key format\n"); | |
| 482 | goto done; | |
| 483 | } | |
| 484 | free(path); | |
| 485 | path = NULL; | |
| 486 | ||
| 487 | /* | |
| 488 | * public key encrypt/decrypt block size. | |
| 489 | */ | |
| 490 | if (keys[0]) { | |
| 491 | blksize = (size_t)RSA_size(keys[0]); | |
| 492 | if (blksize != (size_t)RSA_size(keys[1]) || | |
| 493 | blksize != (size_t)RSA_size(keys[2]) || | |
| 494 | sizeof(handtx) % blksize != 0) { | |
| 495 | iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_KEYFMT; | |
| 496 | iocom->flags |= HAMMER2_IOCOMF_EOF; | |
| 497 | if (DebugOpt) | |
| 498 | fprintf(stderr, "auth failure: " | |
| 499 | "key size mismatch\n"); | |
| 500 | goto done; | |
| 501 | } | |
| 502 | } else { | |
| 503 | blksize = sizeof(handtx); | |
| 504 | } | |
| 505 | blkmask = blksize - 1; | |
| 506 | ||
| 507 | bzero(&handrx, sizeof(handrx)); | |
| 508 | bzero(&handtx, sizeof(handtx)); | |
| 509 | ||
| 510 | /* | |
| 511 | * Fill all unused fields (particular all junk fields) with random | |
| 512 | * data, and also set the session key. | |
| 513 | */ | |
| 514 | fd = open("/dev/urandom", O_RDONLY); | |
| 515 | if (fd < 0 || | |
| 516 | fstat(fd, &st) < 0 || /* something wrong */ | |
| 517 | S_ISREG(st.st_mode) || /* supposed to be a RNG dev! */ | |
| 518 | read(fd, &handtx, sizeof(handtx)) != sizeof(handtx)) { | |
| 519 | urandfail: | |
| 520 | if (fd >= 0) | |
| 521 | close(fd); | |
| 522 | iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_BADURANDOM; | |
| 523 | iocom->flags |= HAMMER2_IOCOMF_EOF; | |
| 524 | if (DebugOpt) | |
| 525 | fprintf(stderr, "auth failure: bad rng\n"); | |
| 526 | goto done; | |
| 527 | } | |
| 528 | if (bcmp(&handrx, &handtx, sizeof(handtx)) == 0) | |
| 529 | goto urandfail; /* read all zeros */ | |
| 530 | close(fd); | |
| 5cf97ec5 | 531 | /* ERR_load_crypto_strings(); openssl debugging */ |
| 62efe6ec MD |
532 | |
| 533 | /* | |
| 534 | * Handshake with the remote. | |
| 535 | * | |
| 536 | * Encrypt with my private and remote's public | |
| 537 | * Decrypt with my private and remote's public | |
| 538 | * | |
| 539 | * When encrypting we have to make sure our buffer fits within the | |
| 540 | * modulus, which typically requires bit 7 o the first byte to be | |
| 541 | * zero. To be safe make sure that bit 7 and bit 6 is zero. | |
| 542 | */ | |
| 543 | snprintf(handtx.quickmsg, sizeof(handtx.quickmsg), "Testing 1 2 3"); | |
| 544 | handtx.magic = HAMMER2_MSGHDR_MAGIC; | |
| 545 | handtx.version = 1; | |
| 546 | handtx.flags = 0; | |
| 547 | assert(sizeof(handtx.verf) * 4 == sizeof(handtx.sess)); | |
| 548 | bzero(handtx.verf, sizeof(handtx.verf)); | |
| 549 | ||
| 550 | handtx.pad1[0] &= 0x3f; /* message must fit within modulus */ | |
| 551 | handtx.pad2[0] &= 0x3f; /* message must fit within modulus */ | |
| 552 | ||
| 553 | for (i = 0; i < sizeof(handtx.sess); ++i) | |
| 554 | handtx.verf[i / 4] ^= handtx.sess[i]; | |
| 555 | ||
| 556 | /* | |
| 557 | * Write handshake buffer to remote | |
| 558 | */ | |
| 559 | for (i = 0; i < sizeof(handtx); i += blksize) { | |
| 560 | ptr = (char *)&handtx + i; | |
| 561 | if (keys[0]) { | |
| 562 | /* | |
| 563 | * Since we are double-encrypting we have to make | |
| 564 | * sure that the result of the first stage does | |
| 565 | * not blow out the modulus for the second stage. | |
| 566 | * | |
| 567 | * The pointer is pointing to the pad*[] area so | |
| 568 | * we can mess with that until the first stage | |
| 569 | * is legal. | |
| 570 | */ | |
| 571 | do { | |
| 572 | ++*(int *)(ptr + 4); | |
| 5cf97ec5 | 573 | if (RSA_private_encrypt(blksize, ptr, buf1, |
| 62efe6ec MD |
574 | keys[2], RSA_NO_PADDING) < 0) { |
| 575 | iocom->ioq_rx.error = | |
| 576 | HAMMER2_IOQ_ERROR_KEYXCHGFAIL; | |
| 577 | } | |
| 5cf97ec5 | 578 | } while (buf1[0] & 0xC0); |
| 62efe6ec | 579 | |
| 5cf97ec5 | 580 | if (RSA_public_encrypt(blksize, buf1, buf2, |
| 62efe6ec MD |
581 | keys[0], RSA_NO_PADDING) < 0) { |
| 582 | iocom->ioq_rx.error = | |
| 583 | HAMMER2_IOQ_ERROR_KEYXCHGFAIL; | |
| 584 | } | |
| 585 | } | |
| 5cf97ec5 | 586 | if (write(iocom->sock_fd, buf2, blksize) != (ssize_t)blksize) { |
| 62efe6ec MD |
587 | fprintf(stderr, "WRITE ERROR\n"); |
| 588 | } | |
| 589 | } | |
| 590 | if (iocom->ioq_rx.error) { | |
| 591 | iocom->flags |= HAMMER2_IOCOMF_EOF; | |
| 592 | if (DebugOpt) | |
| 593 | fprintf(stderr, "auth failure: key exchange failure " | |
| 594 | "during encryption\n"); | |
| 595 | goto done; | |
| 596 | } | |
| 597 | ||
| 598 | /* | |
| 599 | * Read handshake buffer from remote | |
| 600 | */ | |
| 601 | i = 0; | |
| 602 | while (i < sizeof(handrx)) { | |
| 603 | ptr = (char *)&handrx + i; | |
| 604 | n = read(iocom->sock_fd, ptr, blksize - (i & blkmask)); | |
| 605 | if (n <= 0) | |
| 606 | break; | |
| 607 | ptr -= (i & blkmask); | |
| 608 | i += n; | |
| 609 | if (keys[0] && (i & blkmask) == 0) { | |
| 5cf97ec5 | 610 | if (RSA_private_decrypt(blksize, ptr, buf1, |
| 62efe6ec MD |
611 | keys[2], RSA_NO_PADDING) < 0) |
| 612 | iocom->ioq_rx.error = | |
| 613 | HAMMER2_IOQ_ERROR_KEYXCHGFAIL; | |
| 5cf97ec5 | 614 | if (RSA_public_decrypt(blksize, buf1, ptr, |
| 62efe6ec MD |
615 | keys[0], RSA_NO_PADDING) < 0) |
| 616 | iocom->ioq_rx.error = | |
| 617 | HAMMER2_IOQ_ERROR_KEYXCHGFAIL; | |
| 618 | } | |
| 619 | } | |
| 620 | if (iocom->ioq_rx.error) { | |
| 621 | iocom->flags |= HAMMER2_IOCOMF_EOF; | |
| 622 | if (DebugOpt) | |
| 623 | fprintf(stderr, "auth failure: key exchange failure " | |
| 624 | "during decryption\n"); | |
| 625 | goto done; | |
| 626 | } | |
| 627 | ||
| 628 | /* | |
| 629 | * Validate the received data. Try to make this a constant-time | |
| 630 | * algorithm. | |
| 631 | */ | |
| 632 | if (i != sizeof(handrx)) { | |
| 633 | keyxchgfail: | |
| 634 | iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_KEYXCHGFAIL; | |
| 635 | iocom->flags |= HAMMER2_IOCOMF_EOF; | |
| 636 | if (DebugOpt) | |
| 637 | fprintf(stderr, "auth failure: key exchange failure\n"); | |
| 638 | goto done; | |
| 639 | } | |
| 640 | ||
| 641 | if (handrx.magic == HAMMER2_MSGHDR_MAGIC_REV) { | |
| 642 | handrx.version = bswap16(handrx.version); | |
| 643 | handrx.flags = bswap32(handrx.flags); | |
| 644 | } | |
| 645 | for (i = 0; i < sizeof(handrx.sess); ++i) | |
| 646 | handrx.verf[i / 4] ^= handrx.sess[i]; | |
| 647 | n = 0; | |
| 648 | for (i = 0; i < sizeof(handrx.verf); ++i) | |
| 649 | n += handrx.verf[i]; | |
| 650 | if (handrx.version != 1) | |
| 651 | ++n; | |
| 652 | if (n != 0) | |
| 653 | goto keyxchgfail; | |
| 654 | ||
| 766ad73f | 655 | assert(HAMMER2_AES_KEY_SIZE * 2 == sizeof(handrx.sess)); |
| 5cf97ec5 | 656 | /* |
| 766ad73f AH |
657 | * Use separate session keys and session fixed IVs for receive and |
| 658 | * transmit. | |
| 5cf97ec5 | 659 | */ |
| 766ad73f AH |
660 | error = _gcm_init(&iocom->ioq_rx, handrx.sess, HAMMER2_AES_KEY_SIZE, |
| 661 | handrx.sess + HAMMER2_AES_KEY_SIZE, | |
| 662 | sizeof(handrx.sess) - HAMMER2_AES_KEY_SIZE, | |
| 663 | 0 /* decryption */); | |
| 664 | if (error) | |
| 665 | goto keyxchgfail; | |
| 5cf97ec5 | 666 | |
| 766ad73f AH |
667 | error = _gcm_init(&iocom->ioq_tx, handtx.sess, HAMMER2_AES_KEY_SIZE, |
| 668 | handtx.sess + HAMMER2_AES_KEY_SIZE, | |
| 669 | sizeof(handtx.sess) - HAMMER2_AES_KEY_SIZE, | |
| 670 | 1 /* encryption */); | |
| 671 | if (error) | |
| 672 | goto keyxchgfail; | |
| 5cf97ec5 MD |
673 | |
| 674 | iocom->flags |= HAMMER2_IOCOMF_CRYPTED; | |
| 675 | ||
| 676 | if (DebugOpt) | |
| 677 | fprintf(stderr, "auth success: %s\n", handrx.quickmsg); | |
| 62efe6ec MD |
678 | done: |
| 679 | if (path) | |
| 680 | free(path); | |
| 681 | if (keys[0]) | |
| 682 | RSA_free(keys[0]); | |
| 683 | if (keys[1]) | |
| 684 | RSA_free(keys[1]); | |
| 685 | if (keys[1]) | |
| 686 | RSA_free(keys[2]); | |
| 687 | } | |
| 5cf97ec5 MD |
688 | |
| 689 | /* | |
| 690 | * Decrypt pending data in the ioq's fifo. The data is decrypted in-place. | |
| 691 | */ | |
| 692 | void | |
| 3033ecc8 | 693 | hammer2_crypto_decrypt(hammer2_iocom_t *iocom __unused, hammer2_ioq_t *ioq) |
| 5cf97ec5 MD |
694 | { |
| 695 | int p_len; | |
| 766ad73f AH |
696 | int used; |
| 697 | int error; | |
| 5cf97ec5 MD |
698 | char buf[512]; |
| 699 | ||
| 766ad73f AH |
700 | /* |
| 701 | * fifo_beg to fifo_cdx is data already decrypted. | |
| 702 | * fifo_cdn to fifo_end is data not yet decrypted. | |
| 703 | */ | |
| 704 | p_len = ioq->fifo_end - ioq->fifo_cdn; /* data not yet decrypted */ | |
| 3033ecc8 | 705 | |
| 5cf97ec5 MD |
706 | if (p_len == 0) |
| 707 | return; | |
| 766ad73f AH |
708 | |
| 709 | while (p_len >= HAMMER2_CRYPTO_TAG_SIZE + HAMMER2_CRYPTO_CHUNK_SIZE) { | |
| 710 | bcopy(ioq->buf + ioq->fifo_cdn, buf, | |
| 711 | HAMMER2_CRYPTO_TAG_SIZE + HAMMER2_CRYPTO_CHUNK_SIZE); | |
| 712 | error = hammer2_crypto_decrypt_chunk(ioq, buf, | |
| 713 | ioq->buf + ioq->fifo_cdx, | |
| 714 | HAMMER2_CRYPTO_CHUNK_SIZE, | |
| 715 | &used); | |
| 716 | #ifdef CRYPTO_DEBUG | |
| 717 | printf("dec: p_len: %d, used: %d, fifo_cdn: %ju, fifo_cdx: %ju\n", | |
| 718 | p_len, used, ioq->fifo_cdn, ioq->fifo_cdx); | |
| 719 | #endif | |
| 720 | p_len -= used; | |
| 721 | ioq->fifo_cdn += used; | |
| 722 | ioq->fifo_cdx += HAMMER2_CRYPTO_CHUNK_SIZE; | |
| 723 | #ifdef CRYPTO_DEBUG | |
| 724 | printf("dec: p_len: %d, used: %d, fifo_cdn: %ju, fifo_cdx: %ju\n", | |
| 725 | p_len, used, ioq->fifo_cdn, ioq->fifo_cdx); | |
| 726 | #endif | |
| 5cf97ec5 | 727 | } |
| 5cf97ec5 MD |
728 | } |
| 729 | ||
| 730 | /* | |
| 3033ecc8 MD |
731 | * *nactp is set to the number of ORIGINAL bytes consumed by the encrypter. |
| 732 | * The FIFO may contain more data. | |
| 5cf97ec5 | 733 | */ |
| 5cf97ec5 | 734 | int |
| 3033ecc8 MD |
735 | hammer2_crypto_encrypt(hammer2_iocom_t *iocom __unused, hammer2_ioq_t *ioq, |
| 736 | struct iovec *iov, int n, size_t *nactp) | |
| 5cf97ec5 | 737 | { |
| 766ad73f | 738 | int p_len, used, ct_used; |
| 5cf97ec5 | 739 | int i; |
| 766ad73f | 740 | int error; |
| 3033ecc8 | 741 | size_t nmax; |
| 5cf97ec5 | 742 | |
| 3033ecc8 | 743 | nmax = sizeof(ioq->buf) - ioq->fifo_end; /* max new bytes */ |
| 5cf97ec5 | 744 | |
| 3033ecc8 MD |
745 | *nactp = 0; |
| 746 | for (i = 0; i < n && nmax; ++i) { | |
| 766ad73f | 747 | used = 0; |
| 5cf97ec5 | 748 | p_len = iov[i].iov_len; |
| 3033ecc8 | 749 | assert((p_len & HAMMER2_AES_KEY_MASK) == 0); |
| 766ad73f AH |
750 | |
| 751 | while (p_len >= HAMMER2_CRYPTO_CHUNK_SIZE && | |
| 752 | nmax >= HAMMER2_CRYPTO_CHUNK_SIZE + HAMMER2_CRYPTO_TAG_SIZE) { | |
| 753 | error = hammer2_crypto_encrypt_chunk(ioq, | |
| 754 | ioq->buf + ioq->fifo_cdx, | |
| 755 | (char *)iov[i].iov_base + used, | |
| 756 | HAMMER2_CRYPTO_CHUNK_SIZE, &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 | *nactp += (size_t)HAMMER2_CRYPTO_CHUNK_SIZE; /* plaintext count */ | |
| 763 | used += HAMMER2_CRYPTO_CHUNK_SIZE; | |
| 764 | p_len -= HAMMER2_CRYPTO_CHUNK_SIZE; | |
| 765 | ||
| 766 | ioq->fifo_cdx += (size_t)ct_used; /* crypted count */ | |
| 767 | ioq->fifo_cdn += (size_t)ct_used; /* crypted count */ | |
| 768 | ioq->fifo_end += (size_t)ct_used; | |
| 769 | nmax -= (size_t)ct_used; | |
| 770 | #ifdef CRYPTO_DEBUG | |
| 771 | printf("nactp: %ju, p_len: %d, ct_used: %d, used: %d, nmax: %ju\n", | |
| 772 | *nactp, p_len, ct_used, used, nmax); | |
| 773 | #endif | |
| 774 | } | |
| 5cf97ec5 MD |
775 | } |
| 776 | iov[0].iov_base = ioq->buf + ioq->fifo_beg; | |
| 777 | iov[0].iov_len = ioq->fifo_cdx - ioq->fifo_beg; | |
| 778 | ||
| 779 | return (1); | |
| 780 | } |