diff options
author | Alberto Gonzalez Iniesta <agi@inittab.org> | 2016-11-21 09:37:33 +0100 |
---|---|---|
committer | Alberto Gonzalez Iniesta <agi@inittab.org> | 2016-11-21 09:37:33 +0100 |
commit | 20c8675ba46bda97330a4117c459a59a9f1c465e (patch) | |
tree | d888c714fb61947dd79dc44b64a4aaae2f70bfb7 /src/openvpn/crypto.c | |
parent | ffca24bed7a03d95585ad02278667abe75d8b272 (diff) |
New upstream version 2.4~beta1upstream/2.4_beta1
Diffstat (limited to 'src/openvpn/crypto.c')
-rw-r--r-- | src/openvpn/crypto.c | 821 |
1 files changed, 518 insertions, 303 deletions
diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c index 400bf11..05622ce 100644 --- a/src/openvpn/crypto.c +++ b/src/openvpn/crypto.c @@ -6,7 +6,7 @@ * packet compression. * * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> - * Copyright (C) 2010 Fox Crypto B.V. <openvpn@fox-it.com> + * Copyright (C) 2010-2016 Fox Crypto B.V. <openvpn@fox-it.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -35,7 +35,8 @@ #include "crypto.h" #include "error.h" -#include "misc.h" +#include "integer.h" +#include "platform.h" #include "memdbg.h" @@ -62,38 +63,114 @@ * happen unless the frame parameters are wrong. */ -#define CRYPT_ERROR(format) \ - do { msg (D_CRYPT_ERRORS, "%s: " format, error_prefix); goto error_exit; } while (false) +static void +openvpn_encrypt_aead (struct buffer *buf, struct buffer work, + struct crypto_options *opt) { +#ifdef HAVE_AEAD_CIPHER_MODES + struct gc_arena gc; + int outlen = 0; + const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt; + uint8_t *mac_out = NULL; + const cipher_kt_t *cipher_kt = cipher_ctx_get_cipher_kt (ctx->cipher); + const int mac_len = cipher_kt_tag_size (cipher_kt); + + /* IV, packet-ID and implicit IV required for this mode. */ + ASSERT (ctx->cipher); + ASSERT (cipher_kt_mode_aead (cipher_kt)); + ASSERT (opt->flags & CO_USE_IV); + ASSERT (packet_id_initialized(&opt->packet_id)); -/** - * As memcmp(), but constant-time. - * Returns 0 when data is equal, non-zero otherwise. - */ -static int -memcmp_constant_time (const void *a, const void *b, size_t size) { - const uint8_t * a1 = a; - const uint8_t * b1 = b; - int ret = 0; - size_t i; - - for (i = 0; i < size; i++) { - ret |= *a1++ ^ *b1++; + gc_init (&gc); + + /* Prepare IV */ + { + struct buffer iv_buffer; + struct packet_id_net pin; + uint8_t iv[OPENVPN_MAX_IV_LENGTH]; + const int iv_len = cipher_ctx_iv_length (ctx->cipher); + + ASSERT (iv_len >= OPENVPN_AEAD_MIN_IV_LEN && iv_len <= OPENVPN_MAX_IV_LENGTH); + + memset(iv, 0, sizeof(iv)); + buf_set_write (&iv_buffer, iv, iv_len); + + /* IV starts with packet id to make the IV unique for packet */ + packet_id_alloc_outgoing (&opt->packet_id.send, &pin, false); + ASSERT (packet_id_write (&pin, &iv_buffer, false, false)); + + /* Remainder of IV consists of implicit part (unique per session) */ + ASSERT (buf_write (&iv_buffer, ctx->implicit_iv, ctx->implicit_iv_len)); + ASSERT (iv_buffer.len == iv_len); + + /* Write explicit part of IV to work buffer */ + ASSERT (buf_write(&work, iv, iv_len - ctx->implicit_iv_len)); + dmsg (D_PACKET_CONTENT, "ENCRYPT IV: %s", format_hex (iv, iv_len, 0, &gc)); + + /* Init cipher_ctx with IV. key & keylen are already initialized */ + ASSERT (cipher_ctx_reset(ctx->cipher, iv)); } - return ret; + /* Reserve space for authentication tag */ + mac_out = buf_write_alloc (&work, mac_len); + ASSERT (mac_out); + + dmsg (D_PACKET_CONTENT, "ENCRYPT FROM: %s", format_hex (BPTR (buf), BLEN (buf), 80, &gc)); + + /* Buffer overflow check */ + if (!buf_safe (&work, buf->len + cipher_ctx_block_size(ctx->cipher))) + { + msg (D_CRYPT_ERRORS, + "ENCRYPT: buffer size error, bc=%d bo=%d bl=%d wc=%d wo=%d wl=%d", + buf->capacity, buf->offset, buf->len, work.capacity, work.offset, + work.len); + goto err; + } + + /* For AEAD ciphers, authenticate Additional Data, including opcode */ + ASSERT (cipher_ctx_update_ad (ctx->cipher, BPTR (&work), BLEN (&work) - mac_len)); + dmsg (D_PACKET_CONTENT, "ENCRYPT AD: %s", + format_hex (BPTR (&work), BLEN (&work) - mac_len, 0, &gc)); + + /* Encrypt packet ID, payload */ + ASSERT (cipher_ctx_update (ctx->cipher, BEND (&work), &outlen, BPTR (buf), BLEN (buf))); + ASSERT (buf_inc_len (&work, outlen)); + + /* Flush the encryption buffer */ + ASSERT (cipher_ctx_final (ctx->cipher, BEND (&work), &outlen)); + ASSERT (buf_inc_len (&work, outlen)); + + /* Write authentication tag */ + ASSERT (cipher_ctx_get_tag (ctx->cipher, mac_out, mac_len)); + + *buf = work; + + dmsg (D_PACKET_CONTENT, "ENCRYPT TO: %s", format_hex (BPTR (buf), BLEN (buf), 80, &gc)); + + gc_free (&gc); + return; + +err: + crypto_clear_error(); + buf->len = 0; + gc_free (&gc); + return; +#else /* HAVE_AEAD_CIPHER_MODES */ + ASSERT (0); +#endif } -void -openvpn_encrypt (struct buffer *buf, struct buffer work, - const struct crypto_options *opt, - const struct frame* frame) +static void +openvpn_encrypt_v1 (struct buffer *buf, struct buffer work, + struct crypto_options *opt) { struct gc_arena gc; gc_init (&gc); - if (buf->len > 0 && opt->key_ctx_bi) + if (buf->len > 0 && opt) { - struct key_ctx *ctx = &opt->key_ctx_bi->encrypt; + const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt; + uint8_t *mac_out = NULL; + const uint8_t *hmac_start = NULL; /* Do Encrypt from buf -> work */ if (ctx->cipher) @@ -103,6 +180,14 @@ openvpn_encrypt (struct buffer *buf, struct buffer work, const cipher_kt_t *cipher_kt = cipher_ctx_get_cipher_kt (ctx->cipher); int outlen; + /* Reserve space for HMAC */ + if (ctx->hmac) + { + mac_out = buf_write_alloc (&work, hmac_ctx_size(ctx->hmac)); + ASSERT (mac_out); + hmac_start = BEND(&work); + } + if (cipher_kt_mode_cbc(cipher_kt)) { CLEAR (iv_buf); @@ -111,11 +196,11 @@ openvpn_encrypt (struct buffer *buf, struct buffer work, if (opt->flags & CO_USE_IV) prng_bytes (iv_buf, iv_size); - /* Put packet ID in plaintext buffer or IV, depending on cipher mode */ - if (opt->packet_id) + /* Put packet ID in plaintext buffer */ + if (packet_id_initialized(&opt->packet_id)) { struct packet_id_net pin; - packet_id_alloc_outgoing (&opt->packet_id->send, &pin, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM)); + packet_id_alloc_outgoing (&opt->packet_id.send, &pin, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM)); ASSERT (packet_id_write (&pin, buf, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM), true)); } } @@ -124,10 +209,11 @@ openvpn_encrypt (struct buffer *buf, struct buffer work, struct packet_id_net pin; struct buffer b; - ASSERT (opt->flags & CO_USE_IV); /* IV and packet-ID required */ - ASSERT (opt->packet_id); /* for this mode. */ + /* IV and packet-ID required for this mode. */ + ASSERT (opt->flags & CO_USE_IV); + ASSERT (packet_id_initialized(&opt->packet_id)); - packet_id_alloc_outgoing (&opt->packet_id->send, &pin, true); + packet_id_alloc_outgoing (&opt->packet_id.send, &pin, true); memset (iv_buf, 0, iv_size); buf_set_write (&b, iv_buf, iv_size); ASSERT (packet_id_write (&pin, &b, true, false)); @@ -137,12 +223,12 @@ openvpn_encrypt (struct buffer *buf, struct buffer work, ASSERT (0); } - /* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */ - ASSERT (buf_init (&work, FRAME_HEADROOM (frame))); - /* set the IV pseudo-randomly */ if (opt->flags & CO_USE_IV) - dmsg (D_PACKET_CONTENT, "ENCRYPT IV: %s", format_hex (iv_buf, iv_size, 0, &gc)); + { + ASSERT (buf_write(&work, iv_buf, iv_size)); + dmsg (D_PACKET_CONTENT, "ENCRYPT IV: %s", format_hex (iv_buf, iv_size, 0, &gc)); + } dmsg (D_PACKET_CONTENT, "ENCRYPT FROM: %s", format_hex (BPTR (buf), BLEN (buf), 80, &gc)); @@ -165,52 +251,50 @@ openvpn_encrypt (struct buffer *buf, struct buffer work, } /* Encrypt packet ID, payload */ - ASSERT (cipher_ctx_update (ctx->cipher, BPTR (&work), &outlen, BPTR (buf), BLEN (buf))); + ASSERT (cipher_ctx_update (ctx->cipher, BEND (&work), &outlen, BPTR (buf), BLEN (buf))); ASSERT (buf_inc_len(&work, outlen)); /* Flush the encryption buffer */ - ASSERT (cipher_ctx_final(ctx->cipher, BPTR (&work) + outlen, &outlen)); + ASSERT (cipher_ctx_final(ctx->cipher, BEND (&work), &outlen)); ASSERT (buf_inc_len(&work, outlen)); /* For all CBC mode ciphers, check the last block is complete */ ASSERT (cipher_kt_mode (cipher_kt) != OPENVPN_MODE_CBC || outlen == iv_size); - - /* prepend the IV to the ciphertext */ - if (opt->flags & CO_USE_IV) - { - uint8_t *output = buf_prepend (&work, iv_size); - ASSERT (output); - memcpy (output, iv_buf, iv_size); - } - - dmsg (D_PACKET_CONTENT, "ENCRYPT TO: %s", - format_hex (BPTR (&work), BLEN (&work), 80, &gc)); } else /* No Encryption */ { - if (opt->packet_id) + if (packet_id_initialized(&opt->packet_id)) { struct packet_id_net pin; - packet_id_alloc_outgoing (&opt->packet_id->send, &pin, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM)); + packet_id_alloc_outgoing (&opt->packet_id.send, &pin, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM)); ASSERT (packet_id_write (&pin, buf, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM), true)); } + if (ctx->hmac) + { + hmac_start = BPTR(buf); + ASSERT (mac_out = buf_prepend (buf, hmac_ctx_size(ctx->hmac))); + } + if (BLEN(&work)) { + buf_write_prepend(buf, BPTR(&work), BLEN(&work)); + } work = *buf; } /* HMAC the ciphertext (or plaintext if !cipher) */ if (ctx->hmac) { - uint8_t *output = NULL; - hmac_ctx_reset (ctx->hmac); - hmac_ctx_update (ctx->hmac, BPTR(&work), BLEN(&work)); - output = buf_prepend (&work, hmac_ctx_size(ctx->hmac)); - ASSERT (output); - hmac_ctx_final (ctx->hmac, output); + hmac_ctx_update (ctx->hmac, hmac_start, BEND(&work) - hmac_start); + hmac_ctx_final (ctx->hmac, mac_out); + dmsg (D_PACKET_CONTENT, "ENCRYPT HMAC: %s", + format_hex (mac_out, hmac_ctx_size(ctx->hmac), 80, &gc)); } *buf = work; + + dmsg (D_PACKET_CONTENT, "ENCRYPT TO: %s", + format_hex (BPTR (&work), BLEN (&work), 80, &gc)); } gc_free (&gc); @@ -223,6 +307,47 @@ err: return; } +void +openvpn_encrypt (struct buffer *buf, struct buffer work, + struct crypto_options *opt) +{ + if (buf->len > 0 && opt) + { + const cipher_kt_t *cipher_kt = + cipher_ctx_get_cipher_kt(opt->key_ctx_bi.encrypt.cipher); + + if (cipher_kt_mode_aead (cipher_kt)) + openvpn_encrypt_aead(buf, work, opt); + else + openvpn_encrypt_v1(buf, work, opt); + } +} + +bool crypto_check_replay(struct crypto_options *opt, + const struct packet_id_net *pin, const char *error_prefix, + struct gc_arena *gc) { + bool ret = false; + packet_id_reap_test (&opt->packet_id.rec); + if (packet_id_test (&opt->packet_id.rec, pin)) + { + packet_id_add (&opt->packet_id.rec, pin); + if (opt->pid_persist && (opt->flags & CO_PACKET_ID_LONG_FORM)) + packet_id_persist_save_obj (opt->pid_persist, &opt->packet_id); + ret = true; + } + else + { + if (!(opt->flags & CO_MUTE_REPLAY_WARNINGS)) + { + msg (D_REPLAY_ERRORS, "%s: bad packet ID (may be a replay): %s -- " + "see the man page entry for --no-replay and --replay-window for " + "more info or silence this warning with --mute-replay-warnings", + error_prefix, packet_id_net_print (pin, true, gc)); + } + } + return ret; +} + /* * If (opt->flags & CO_USE_IV) is not NULL, we will read an IV from the packet. * @@ -231,21 +356,169 @@ err: * On success, buf is set to point to plaintext, true * is returned. */ -bool -openvpn_decrypt (struct buffer *buf, struct buffer work, - const struct crypto_options *opt, - const struct frame* frame) +static bool +openvpn_decrypt_aead (struct buffer *buf, struct buffer work, + struct crypto_options *opt, const struct frame* frame, + const uint8_t *ad_start) +{ +#ifdef HAVE_AEAD_CIPHER_MODES + static const char error_prefix[] = "AEAD Decrypt error"; + struct packet_id_net pin = { 0 }; + const struct key_ctx *ctx = &opt->key_ctx_bi.decrypt; + const cipher_kt_t *cipher_kt = cipher_ctx_get_cipher_kt (ctx->cipher); + uint8_t *tag_ptr = NULL; + int tag_size = 0; + int outlen; + struct gc_arena gc; + + gc_init (&gc); + + ASSERT (opt); + ASSERT (frame); + ASSERT (buf->len > 0); + ASSERT (ctx->cipher); + ASSERT (cipher_kt_mode_aead (cipher_kt)); + + dmsg (D_PACKET_CONTENT, "DECRYPT FROM: %s", + format_hex (BPTR (buf), BLEN (buf), 80, &gc)); + + ASSERT (ad_start >= buf->data && ad_start <= BPTR (buf)); + + ASSERT (buf_init (&work, FRAME_HEADROOM_ADJ (frame, FRAME_HEADROOM_MARKER_DECRYPT))); + + /* IV and Packet ID required for this mode */ + ASSERT (packet_id_initialized (&opt->packet_id)); + ASSERT (opt->flags & CO_USE_IV); + + /* Combine IV from explicit part from packet and implicit part from context */ + { + uint8_t iv[OPENVPN_MAX_IV_LENGTH] = { 0 }; + const int iv_len = cipher_ctx_iv_length (ctx->cipher); + const size_t packet_iv_len = iv_len - ctx->implicit_iv_len; + + ASSERT (ctx->implicit_iv_len <= iv_len); + if (buf->len + ctx->implicit_iv_len < iv_len) + CRYPT_ERROR ("missing IV info"); + + memcpy (iv, BPTR(buf), packet_iv_len); + memcpy (iv + packet_iv_len, ctx->implicit_iv, ctx->implicit_iv_len); + + dmsg (D_PACKET_CONTENT, "DECRYPT IV: %s", format_hex (iv, iv_len, 0, &gc)); + + /* Load IV, ctx->cipher was already initialized with key & keylen */ + if (!cipher_ctx_reset (ctx->cipher, iv)) + { + CRYPT_ERROR ("cipher init failed"); + } + } + + /* Read packet ID from packet */ + if (!packet_id_read (&pin, buf, false)) + { + CRYPT_ERROR ("error reading packet-id"); + } + + /* keep the tag value to feed in later */ + tag_size = cipher_kt_tag_size(cipher_kt); + if (buf->len < tag_size) + { + CRYPT_ERROR ("missing tag"); + } + tag_ptr = BPTR(buf); + ASSERT (buf_advance (buf, tag_size)); + dmsg (D_PACKET_CONTENT, "DECRYPT MAC: %s", format_hex (tag_ptr, tag_size, 0, &gc)); +#if defined(ENABLE_CRYPTO_OPENSSL) && OPENSSL_VERSION_NUMBER < 0x10001040L + /* OpenSSL <= 1.0.1c bug requires set tag before processing ciphertext */ + if (!EVP_CIPHER_CTX_ctrl (ctx->cipher, EVP_CTRL_GCM_SET_TAG, tag_size, tag_ptr)) + { + CRYPT_ERROR ("setting tag failed"); + } +#endif + + if (buf->len < 1) + { + CRYPT_ERROR ("missing payload"); + } + + dmsg (D_PACKET_CONTENT, "DECRYPT FROM: %s", format_hex (BPTR(buf), BLEN(buf), 0, &gc)); + + /* Buffer overflow check (should never fail) */ + if (!buf_safe (&work, buf->len + cipher_ctx_block_size(ctx->cipher))) + { + CRYPT_ERROR ("potential buffer overflow"); + } + + { + /* feed in tag and the authenticated data */ + const int ad_size = BPTR (buf) - ad_start - tag_size; + ASSERT (cipher_ctx_update_ad (ctx->cipher, ad_start, ad_size)); + dmsg (D_PACKET_CONTENT, "DECRYPT AD: %s", + format_hex (BPTR (buf) - ad_size - tag_size, ad_size, 0, &gc)); + } + + /* Decrypt and authenticate packet */ + if (!cipher_ctx_update (ctx->cipher, BPTR (&work), &outlen, BPTR (buf), + BLEN (buf))) + { + CRYPT_ERROR ("cipher update failed"); + } + ASSERT (buf_inc_len (&work, outlen)); + if (!cipher_ctx_final_check_tag (ctx->cipher, BPTR (&work) + outlen, + &outlen, tag_ptr, tag_size)) + { + CRYPT_ERROR ("cipher final failed"); + } + ASSERT (buf_inc_len (&work, outlen)); + + dmsg (D_PACKET_CONTENT, "DECRYPT TO: %s", + format_hex (BPTR (&work), BLEN (&work), 80, &gc)); + + if (!crypto_check_replay (opt, &pin, error_prefix, &gc)) + { + goto error_exit; + } + + *buf = work; + + gc_free (&gc); + return true; + + error_exit: + crypto_clear_error(); + buf->len = 0; + gc_free (&gc); + return false; +#else /* HAVE_AEAD_CIPHER_MODES */ + ASSERT (0); + return false; +#endif +} + +/* + * If (opt->flags & CO_USE_IV) is not NULL, we will read an IV from the packet. + * + * Set buf->len to 0 and return false on decrypt error. + * + * On success, buf is set to point to plaintext, true + * is returned. + */ +static bool +openvpn_decrypt_v1 (struct buffer *buf, struct buffer work, + struct crypto_options *opt, const struct frame* frame) { static const char error_prefix[] = "Authenticate/Decrypt packet error"; struct gc_arena gc; gc_init (&gc); - if (buf->len > 0 && opt->key_ctx_bi) + if (buf->len > 0 && opt) { - struct key_ctx *ctx = &opt->key_ctx_bi->decrypt; + const struct key_ctx *ctx = &opt->key_ctx_bi.decrypt; struct packet_id_net pin; bool have_pin = false; + dmsg (D_PACKET_CONTENT, "DECRYPT FROM: %s", + format_hex (BPTR (buf), BLEN (buf), 80, &gc)); + /* Verify the HMAC */ if (ctx->hmac) { @@ -325,7 +598,7 @@ openvpn_decrypt (struct buffer *buf, struct buffer work, { if (cipher_kt_mode_cbc(cipher_kt)) { - if (opt->packet_id) + if (packet_id_initialized(&opt->packet_id)) { if (!packet_id_read (&pin, &work, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM))) CRYPT_ERROR ("error reading CBC packet-id"); @@ -336,8 +609,9 @@ openvpn_decrypt (struct buffer *buf, struct buffer work, { struct buffer b; - ASSERT (opt->flags & CO_USE_IV); /* IV and packet-ID required */ - ASSERT (opt->packet_id); /* for this mode. */ + /* IV and packet-ID required for this mode. */ + ASSERT (opt->flags & CO_USE_IV); + ASSERT (packet_id_initialized(&opt->packet_id)); buf_set_read (&b, iv_buf, iv_size); if (!packet_id_read (&pin, &b, true)) @@ -353,7 +627,7 @@ openvpn_decrypt (struct buffer *buf, struct buffer work, else { work = *buf; - if (opt->packet_id) + if (packet_id_initialized(&opt->packet_id)) { if (!packet_id_read (&pin, &work, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM))) CRYPT_ERROR ("error reading packet-id"); @@ -361,22 +635,9 @@ openvpn_decrypt (struct buffer *buf, struct buffer work, } } - if (have_pin) + if (have_pin && !crypto_check_replay(opt, &pin, error_prefix, &gc)) { - packet_id_reap_test (&opt->packet_id->rec); - if (packet_id_test (&opt->packet_id->rec, &pin)) - { - packet_id_add (&opt->packet_id->rec, &pin); - if (opt->pid_persist && (opt->flags & CO_PACKET_ID_LONG_FORM)) - packet_id_persist_save_obj (opt->pid_persist, opt->packet_id); - } - else - { - if (!(opt->flags & CO_MUTE_REPLAY_WARNINGS)) - msg (D_REPLAY_ERRORS, "%s: bad packet ID (may be a replay): %s -- see the man page entry for --no-replay and --replay-window for more info or silence this warning with --mute-replay-warnings", - error_prefix, packet_id_net_print (&pin, true, &gc)); - goto error_exit; - } + goto error_exit; } *buf = work; } @@ -391,14 +652,36 @@ openvpn_decrypt (struct buffer *buf, struct buffer work, return false; } -/* - * How many bytes will we add to frame buffer for a given - * set of crypto options? - */ + +bool +openvpn_decrypt (struct buffer *buf, struct buffer work, + struct crypto_options *opt, const struct frame* frame, + const uint8_t *ad_start) +{ + bool ret = false; + + if (buf->len > 0 && opt) + { + const struct key_ctx *ctx = &opt->key_ctx_bi.decrypt; + if (cipher_kt_mode_aead (cipher_ctx_get_cipher_kt (ctx->cipher))) + { + ret = openvpn_decrypt_aead (buf, work, opt, frame, ad_start); + } + else + { + ret = openvpn_decrypt_v1 (buf, work, opt, frame); + } + } + else + { + ret = true; + } + return ret; +} + void crypto_adjust_frame_parameters(struct frame *frame, const struct key_type* kt, - bool cipher_defined, bool use_iv, bool packet_id, bool packet_id_long_form) @@ -408,11 +691,14 @@ crypto_adjust_frame_parameters(struct frame *frame, if (packet_id) crypto_overhead += packet_id_size (packet_id_long_form); - if (cipher_defined) + if (kt->cipher) { if (use_iv) crypto_overhead += cipher_kt_iv_size (kt->cipher); + if (cipher_kt_mode_aead (kt->cipher)) + crypto_overhead += cipher_kt_tag_size (kt->cipher); + /* extra block required by cipher_ctx_update() */ crypto_overhead += cipher_kt_block_size (kt->cipher); } @@ -421,8 +707,16 @@ crypto_adjust_frame_parameters(struct frame *frame, frame_add_to_extra_frame (frame, crypto_overhead); - msg(D_MTU_DEBUG, "%s: Adjusting frame parameters for crypto by %zu bytes", - __func__, crypto_overhead); + msg(D_MTU_DEBUG, "%s: Adjusting frame parameters for crypto by %u bytes", + __func__, (unsigned int) crypto_overhead); +} + +size_t +crypto_max_overhead(void) +{ + return packet_id_size(true) + OPENVPN_MAX_IV_LENGTH + + OPENVPN_MAX_CIPHER_BLOCK_SIZE + + max_int (OPENVPN_MAX_HMAC_SIZE, OPENVPN_AEAD_TAG_LENGTH); } /* @@ -430,39 +724,55 @@ crypto_adjust_frame_parameters(struct frame *frame, */ void init_key_type (struct key_type *kt, const char *ciphername, - bool ciphername_defined, const char *authname, - bool authname_defined, int keysize, - bool cfb_ofb_allowed, bool warn) + const char *authname, int keysize, bool tls_mode, bool warn) { + bool aead_cipher = false; + + ASSERT(ciphername); + ASSERT(authname); + CLEAR (*kt); - if (ciphername && ciphername_defined) + if (strcmp (ciphername, "none") != 0) { kt->cipher = cipher_kt_get (translate_cipher_name_from_openvpn(ciphername)); + if (!kt->cipher) + { + msg (M_FATAL, "Cipher %s not supported", ciphername); + } + kt->cipher_length = cipher_kt_key_size (kt->cipher); if (keysize > 0 && keysize <= MAX_CIPHER_KEY_LENGTH) kt->cipher_length = keysize; /* check legal cipher mode */ - { - if (!(cipher_kt_mode_cbc(kt->cipher) + aead_cipher = cipher_kt_mode_aead(kt->cipher); + if (!(cipher_kt_mode_cbc(kt->cipher) + || (tls_mode && aead_cipher) #ifdef ENABLE_OFB_CFB_MODE - || (cfb_ofb_allowed && cipher_kt_mode_ofb_cfb(kt->cipher)) + || (tls_mode && cipher_kt_mode_ofb_cfb(kt->cipher)) #endif - )) - msg (M_FATAL, "Cipher '%s' mode not supported", ciphername); - } + )) + msg (M_FATAL, "Cipher '%s' mode not supported", ciphername); + + if (OPENVPN_MAX_CIPHER_BLOCK_SIZE < cipher_kt_block_size(kt->cipher)) + msg (M_FATAL, "Cipher '%s' not allowed: block size too big.", ciphername); } else { if (warn) msg (M_WARN, "******* WARNING *******: null cipher specified, no encryption will be used"); } - if (authname && authname_defined) + if (strcmp (authname, "none") != 0) { - kt->digest = md_kt_get (authname); - kt->hmac_length = md_kt_size (kt->digest); + if (!aead_cipher) { /* Ignore auth for AEAD ciphers */ + kt->digest = md_kt_get (authname); + kt->hmac_length = md_kt_size (kt->digest); + + if (OPENVPN_MAX_HMAC_SIZE < kt->hmac_length) + msg (M_FATAL, "HMAC '%s' not allowed: digest size too big.", authname); + } } - else + else if (!aead_cipher) { if (warn) msg (M_WARN, "******* WARNING *******: null MAC specified, no authentication will be used"); @@ -486,15 +796,21 @@ init_key_ctx (struct key_ctx *ctx, struct key *key, msg (D_HANDSHAKE, "%s: Cipher '%s' initialized with %d bit key", prefix, - cipher_kt_name(kt->cipher), + translate_cipher_name_to_openvpn(cipher_kt_name(kt->cipher)), kt->cipher_length *8); dmsg (D_SHOW_KEYS, "%s: CIPHER KEY: %s", prefix, format_hex (key->cipher, kt->cipher_length, 0, &gc)); dmsg (D_CRYPTO_DEBUG, "%s: CIPHER block_size=%d iv_size=%d", - prefix, - cipher_kt_block_size(kt->cipher), - cipher_kt_iv_size(kt->cipher)); + prefix, cipher_kt_block_size(kt->cipher), + cipher_kt_iv_size(kt->cipher)); + if (cipher_kt_block_size(kt->cipher) < 128/8) + { + msg (M_WARN, "WARNING: INSECURE cipher with block size less than 128" + " bit (%d bit). This allows attacks like SWEET32. Mitigate by " + "using a --cipher with a larger block size (e.g. AES-256-CBC).", + cipher_kt_block_size(kt->cipher)*8); + } } if (kt->digest && kt->hmac_length > 0) { @@ -532,6 +848,7 @@ free_key_ctx (struct key_ctx *ctx) free(ctx->hmac); ctx->hmac = NULL; } + ctx->implicit_iv_len = 0; } void @@ -541,7 +858,6 @@ free_key_ctx_bi (struct key_ctx_bi *ctx) free_key_ctx(&ctx->decrypt); } - static bool key_is_zero (struct key *key, const struct key_type *kt) { @@ -621,8 +937,10 @@ check_replay_iv_consistency (const struct key_type *kt, bool packet_id, bool use { ASSERT(kt); - if (cipher_kt_mode_ofb_cfb(kt->cipher) && !(packet_id && use_iv)) - msg (M_FATAL, "--no-replay or --no-iv cannot be used with a CFB or OFB mode cipher"); + if (!(packet_id && use_iv) && (cipher_kt_mode_ofb_cfb(kt->cipher) || + cipher_kt_mode_aead(kt->cipher))) + msg (M_FATAL, "--no-replay or --no-iv cannot be used with a CFB, OFB or " + "AEAD mode cipher"); } /* @@ -688,7 +1006,7 @@ key2_print (const struct key2* k, } void -test_crypto (const struct crypto_options *co, struct frame* frame) +test_crypto (struct crypto_options *co, struct frame* frame) { int i, j; struct gc_arena gc = gc_new (); @@ -697,10 +1015,35 @@ test_crypto (const struct crypto_options *co, struct frame* frame) struct buffer encrypt_workspace = alloc_buf_gc (BUF_SIZE (frame), &gc); struct buffer decrypt_workspace = alloc_buf_gc (BUF_SIZE (frame), &gc); struct buffer buf = clear_buf(); + void *buf_p; /* init work */ ASSERT (buf_init (&work, FRAME_HEADROOM (frame))); +#ifdef HAVE_AEAD_CIPHER_MODES + /* init implicit IV */ + { + const cipher_kt_t *cipher = + cipher_ctx_get_cipher_kt(co->key_ctx_bi.encrypt.cipher); + + if (cipher_kt_mode_aead(cipher)) + { + size_t impl_iv_len = cipher_kt_iv_size(cipher) - sizeof(packet_id_type); + ASSERT (cipher_kt_iv_size(cipher) <= OPENVPN_MAX_IV_LENGTH); + ASSERT (cipher_kt_iv_size(cipher) >= OPENVPN_AEAD_MIN_IV_LEN); + + /* Generate dummy implicit IV */ + ASSERT (rand_bytes(co->key_ctx_bi.encrypt.implicit_iv, + OPENVPN_MAX_IV_LENGTH)); + co->key_ctx_bi.encrypt.implicit_iv_len = impl_iv_len; + + memcpy(co->key_ctx_bi.decrypt.implicit_iv, + co->key_ctx_bi.encrypt.implicit_iv, OPENVPN_MAX_IV_LENGTH); + co->key_ctx_bi.decrypt.implicit_iv_len = impl_iv_len; + } + } +#endif + msg (M_INFO, "Entering " PACKAGE_NAME " crypto self-test mode."); for (i = 1; i <= TUN_MTU_SIZE (frame); ++i) { @@ -718,13 +1061,18 @@ test_crypto (const struct crypto_options *co, struct frame* frame) /* copy source to input buf */ buf = work; - memcpy (buf_write_alloc (&buf, BLEN (&src)), BPTR (&src), BLEN (&src)); + buf_p = buf_write_alloc (&buf, BLEN (&src)); + ASSERT(buf_p); + memcpy (buf_p, BPTR (&src), BLEN (&src)); + + /* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */ + ASSERT (buf_init (&encrypt_workspace, FRAME_HEADROOM (frame))); /* encrypt */ - openvpn_encrypt (&buf, encrypt_workspace, co, frame); + openvpn_encrypt (&buf, encrypt_workspace, co); /* decrypt */ - openvpn_decrypt (&buf, decrypt_workspace, co, frame); + openvpn_decrypt (&buf, decrypt_workspace, co, frame, BPTR (&buf)); /* compare */ if (buf.len != src.len) @@ -741,90 +1089,47 @@ test_crypto (const struct crypto_options *co, struct frame* frame) gc_free (&gc); } -#ifdef ENABLE_SSL - void -get_tls_handshake_key (const struct key_type *key_type, - struct key_ctx_bi *ctx, - const char *passphrase_file, - const int key_direction, - const unsigned int flags) +crypto_read_openvpn_key (const struct key_type *key_type, + struct key_ctx_bi *ctx, const char *key_file, const char *key_inline, + const int key_direction, const char *key_name, const char *opt_name) { - if (passphrase_file && key_type->hmac_length) - { - struct key2 key2; - struct key_type kt = *key_type; - struct key_direction_state kds; - - /* for control channel we are only authenticating, not encrypting */ - kt.cipher_length = 0; - kt.cipher = NULL; - - if (flags & GHK_INLINE) - { - /* key was specified inline, key text is in passphrase_file */ - read_key_file (&key2, passphrase_file, RKF_INLINE|RKF_MUST_SUCCEED); - - /* succeeded? */ - if (key2.n == 2) - msg (M_INFO, "Control Channel Authentication: tls-auth using INLINE static key file"); - else - msg (M_FATAL, "INLINE tls-auth file lacks the requisite 2 keys"); - } - else - { - /* first try to parse as an OpenVPN static key file */ - read_key_file (&key2, passphrase_file, 0); - - /* succeeded? */ - if (key2.n == 2) - { - msg (M_INFO, - "Control Channel Authentication: using '%s' as a " PACKAGE_NAME " static key file", - passphrase_file); - } - else - { - int hash_size; - - CLEAR (key2); - - /* failed, now try to get hash from a freeform file */ - hash_size = read_passphrase_hash (passphrase_file, - kt.digest, - key2.keys[0].hmac, - MAX_HMAC_KEY_LENGTH); - ASSERT (hash_size == kt.hmac_length); + struct key2 key2; + struct key_direction_state kds; + char log_prefix[128] = { 0 }; - /* suceeded */ - key2.n = 1; + if (key_inline) + { + read_key_file (&key2, key_inline, RKF_MUST_SUCCEED|RKF_INLINE); + } + else + { + read_key_file (&key2, key_file, RKF_MUST_SUCCEED); + } - msg (M_INFO, - "Control Channel Authentication: using '%s' as a free-form passphrase file", - passphrase_file); - msg (M_WARN, "DEPRECATED OPTION: Using freeform files for tls-auth is deprecated and is not supported in OpenVPN 2.4 or newer versions"); - } - } - /* handle key direction */ + if (key2.n != 2) + { + msg (M_ERR, "File '%s' does not have OpenVPN Static Key format. Using " + "free-form passphrase file is not supported anymore.", key_file); + } - key_direction_state_init (&kds, key_direction); - must_have_n_keys (passphrase_file, "tls-auth", &key2, kds.need_keys); + /* check for and fix highly unlikely key problems */ + verify_fix_key2 (&key2, key_type, key_file); - /* initialize hmac key in both directions */ + /* handle key direction */ + key_direction_state_init (&kds, key_direction); + must_have_n_keys (key_file, opt_name, &key2, kds.need_keys); - init_key_ctx (&ctx->encrypt, &key2.keys[kds.out_key], &kt, OPENVPN_OP_ENCRYPT, - "Outgoing Control Channel Authentication"); - init_key_ctx (&ctx->decrypt, &key2.keys[kds.in_key], &kt, OPENVPN_OP_DECRYPT, - "Incoming Control Channel Authentication"); + /* initialize key in both directions */ + openvpn_snprintf (log_prefix, sizeof (log_prefix), "Outgoing %s", key_name); + init_key_ctx (&ctx->encrypt, &key2.keys[kds.out_key], key_type, + OPENVPN_OP_ENCRYPT, log_prefix); + openvpn_snprintf (log_prefix, sizeof (log_prefix), "Incoming %s", key_name); + init_key_ctx (&ctx->decrypt, &key2.keys[kds.in_key], key_type, + OPENVPN_OP_DECRYPT, log_prefix); - CLEAR (key2); - } - else - { - CLEAR (*ctx); - } + CLEAR (key2); } -#endif /* header and footer for static key file */ static const char static_key_head[] = "-----BEGIN OpenVPN Static key V1-----"; @@ -1002,9 +1307,6 @@ read_key_file (struct key2 *key2, const char *file, const unsigned int flags) if (!(flags & RKF_INLINE)) buf_clear (&in); - if (key2->n) - warn_if_group_others_accessible (error_filename); - #if 0 /* DEBUGGING */ { @@ -1028,55 +1330,6 @@ read_key_file (struct key2 *key2, const char *file, const unsigned int flags) gc_free (&gc); } -int -read_passphrase_hash (const char *passphrase_file, - const md_kt_t *digest, - uint8_t *output, - int len) -{ - unsigned int outlen = 0; - md_ctx_t md; - - ASSERT (len >= md_kt_size(digest)); - memset (output, 0, len); - - md_ctx_init(&md, digest); - - /* read passphrase file */ - { - const int min_passphrase_size = 8; - uint8_t buf[64]; - int total_size = 0; - int fd = platform_open (passphrase_file, O_RDONLY, 0); - - if (fd == -1) - msg (M_ERR, "Cannot open passphrase file: '%s'", passphrase_file); - - for (;;) - { - int size = read (fd, buf, sizeof (buf)); - if (size == 0) - break; - if (size == -1) - msg (M_ERR, "Read error on passphrase file: '%s'", - passphrase_file); - md_ctx_update(&md, buf, size); - total_size += size; - } - close (fd); - - warn_if_group_others_accessible (passphrase_file); - - if (total_size < min_passphrase_size) - msg (M_FATAL, - "Passphrase file '%s' is too small (must have at least %d characters)", - passphrase_file, min_passphrase_size); - } - md_ctx_final(&md, output); - md_ctx_cleanup(&md); - return md_kt_size(digest); -} - /* * Write key to file, return number of random bits * written. @@ -1367,7 +1620,6 @@ prng_bytes (uint8_t *output, int len) const int md_size = md_kt_size (nonce_md); while (len > 0) { - unsigned int outlen = 0; const int blen = min_int (len, md_size); md_full(nonce_md, nonce_data, md_size + nonce_secret_len, nonce_data); memcpy (output, nonce_data, blen); @@ -1397,79 +1649,42 @@ get_random() return l; } -#ifndef ENABLE_SSL +static const cipher_name_pair * +get_cipher_name_pair(const char *cipher_name) { + const cipher_name_pair *pair; + size_t i = 0; -void -init_ssl_lib (void) -{ - crypto_init_lib (); -} + /* Search for a cipher name translation */ + for (; i < cipher_name_translation_table_count; i++) + { + pair = &cipher_name_translation_table[i]; + if (0 == strcmp (cipher_name, pair->openvpn_name) || + 0 == strcmp (cipher_name, pair->lib_name)) + return pair; + } -void -free_ssl_lib (void) -{ - crypto_uninit_lib (); - prng_uninit(); + /* Nothing found, return null */ + return NULL; } -#endif /* ENABLE_SSL */ - -/* - * md5 functions - */ - const char * -md5sum (uint8_t *buf, int len, int n_print_chars, struct gc_arena *gc) -{ - uint8_t digest[MD5_DIGEST_LENGTH]; - const md_kt_t *md5_kt = md_kt_get("MD5"); - - md_full(md5_kt, buf, len, digest); - - return format_hex (digest, MD5_DIGEST_LENGTH, n_print_chars, gc); -} - -void -md5_state_init (struct md5_state *s) -{ - const md_kt_t *md5_kt = md_kt_get("MD5"); - - md_ctx_init(&s->ctx, md5_kt); -} +translate_cipher_name_from_openvpn (const char *cipher_name) { + const cipher_name_pair *pair = get_cipher_name_pair(cipher_name); -void -md5_state_update (struct md5_state *s, void *data, size_t len) -{ - md_ctx_update(&s->ctx, data, len); -} + if (NULL == pair) + return cipher_name; -void -md5_state_final (struct md5_state *s, struct md5_digest *out) -{ - md_ctx_final(&s->ctx, out->digest); - md_ctx_cleanup(&s->ctx); + return pair->lib_name; } -void -md5_digest_clear (struct md5_digest *digest) -{ - CLEAR (*digest); -} +const char * +translate_cipher_name_to_openvpn (const char *cipher_name) { + const cipher_name_pair *pair = get_cipher_name_pair(cipher_name); -bool -md5_digest_defined (const struct md5_digest *digest) -{ - int i; - for (i = 0; i < MD5_DIGEST_LENGTH; ++i) - if (digest->digest[i]) - return true; - return false; -} + if (NULL == pair) + return cipher_name; -bool -md5_digest_equal (const struct md5_digest *d1, const struct md5_digest *d2) -{ - return memcmp(d1->digest, d2->digest, MD5_DIGEST_LENGTH) == 0; + return pair->openvpn_name; } #endif /* ENABLE_CRYPTO */ |