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