/* * OpenVPN -- An application to securely tunnel IP networks * over a single TCP/UDP port, with support for SSL/TLS-based * session authentication and key exchange, * packet encryption, packet authentication, and * packet compression. * * Copyright (C) 2016-2018 Fox Crypto B.V. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #elif defined(_MSC_VER) #include "config-msvc.h" #endif #include "syshead.h" #include "crypto.h" #include "session_id.h" #include "tls_crypt.h" static struct key_type tls_crypt_kt(void) { struct key_type kt; kt.cipher = cipher_kt_get("AES-256-CTR"); kt.digest = md_kt_get("SHA256"); if (!kt.cipher) { msg(M_WARN, "ERROR: --tls-crypt requires AES-256-CTR support."); return (struct key_type) { 0 }; } if (!kt.digest) { msg(M_WARN, "ERROR: --tls-crypt requires HMAC-SHA-256 support."); return (struct key_type) { 0 }; } kt.cipher_length = cipher_kt_key_size(kt.cipher); kt.hmac_length = md_kt_size(kt.digest); return kt; } int tls_crypt_buf_overhead(void) { return packet_id_size(true) + TLS_CRYPT_TAG_SIZE + TLS_CRYPT_BLOCK_SIZE; } void tls_crypt_init_key(struct key_ctx_bi *key, const char *key_file, const char *key_inline, bool tls_server) { const int key_direction = tls_server ? KEY_DIRECTION_NORMAL : KEY_DIRECTION_INVERSE; struct key_type kt = tls_crypt_kt(); if (!kt.cipher || !kt.digest) { msg (M_FATAL, "ERROR: --tls-crypt not supported"); } crypto_read_openvpn_key(&kt, key, key_file, key_inline, key_direction, "Control Channel Encryption", "tls-crypt"); } void tls_crypt_adjust_frame_parameters(struct frame *frame) { frame_add_to_extra_frame(frame, tls_crypt_buf_overhead()); msg(D_MTU_DEBUG, "%s: Adjusting frame parameters for tls-crypt by %i bytes", __func__, tls_crypt_buf_overhead()); } bool tls_crypt_wrap(const struct buffer *src, struct buffer *dst, struct crypto_options *opt) { const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt; struct gc_arena gc; /* IV, packet-ID and implicit IV required for this mode. */ ASSERT(ctx->cipher); ASSERT(ctx->hmac); ASSERT(packet_id_initialized(&opt->packet_id)); ASSERT(hmac_ctx_size(ctx->hmac) == 256/8); gc_init(&gc); dmsg(D_PACKET_CONTENT, "TLS-CRYPT WRAP FROM: %s", format_hex(BPTR(src), BLEN(src), 80, &gc)); /* Get packet ID */ if (!packet_id_write(&opt->packet_id.send, dst, true, false)) { msg(D_CRYPT_ERRORS, "TLS-CRYPT ERROR: packet ID roll over."); goto err; } dmsg(D_PACKET_CONTENT, "TLS-CRYPT WRAP AD: %s", format_hex(BPTR(dst), BLEN(dst), 0, &gc)); /* Buffer overflow check */ if (!buf_safe(dst, BLEN(src) + TLS_CRYPT_BLOCK_SIZE + TLS_CRYPT_TAG_SIZE)) { msg(D_CRYPT_ERRORS, "TLS-CRYPT WRAP: buffer size error, " "sc=%d so=%d sl=%d dc=%d do=%d dl=%d", src->capacity, src->offset, src->len, dst->capacity, dst->offset, dst->len); goto err; } /* Calculate auth tag and synthetic IV */ { uint8_t *tag = NULL; hmac_ctx_reset(ctx->hmac); hmac_ctx_update(ctx->hmac, BPTR(dst), BLEN(dst)); hmac_ctx_update(ctx->hmac, BPTR(src), BLEN(src)); ASSERT(tag = buf_write_alloc(dst, TLS_CRYPT_TAG_SIZE)); hmac_ctx_final(ctx->hmac, tag); dmsg(D_PACKET_CONTENT, "TLS-CRYPT WRAP TAG: %s", format_hex(tag, TLS_CRYPT_TAG_SIZE, 0, &gc)); /* Use the 128 most significant bits of the tag as IV */ ASSERT(cipher_ctx_reset(ctx->cipher, tag)); } /* Encrypt src */ { int outlen = 0; ASSERT(cipher_ctx_update(ctx->cipher, BEND(dst), &outlen, BPTR(src), BLEN(src))); ASSERT(buf_inc_len(dst, outlen)); ASSERT(cipher_ctx_final(ctx->cipher, BPTR(dst), &outlen)); ASSERT(buf_inc_len(dst, outlen)); } dmsg(D_PACKET_CONTENT, "TLS-CRYPT WRAP TO: %s", format_hex(BPTR(dst), BLEN(dst), 80, &gc)); gc_free(&gc); return true; err: crypto_clear_error(); dst->len = 0; gc_free(&gc); return false; } bool tls_crypt_unwrap(const struct buffer *src, struct buffer *dst, struct crypto_options *opt) { static const char error_prefix[] = "tls-crypt unwrap error"; const struct key_ctx *ctx = &opt->key_ctx_bi.decrypt; struct gc_arena gc; gc_init(&gc); ASSERT(opt); ASSERT(src->len > 0); ASSERT(ctx->cipher); ASSERT(packet_id_initialized(&opt->packet_id) || (opt->flags & CO_IGNORE_PACKET_ID)); dmsg(D_PACKET_CONTENT, "TLS-CRYPT UNWRAP FROM: %s", format_hex(BPTR(src), BLEN(src), 80, &gc)); if (buf_len(src) < TLS_CRYPT_OFF_CT) { CRYPT_ERROR("packet too short"); } /* Decrypt cipher text */ { int outlen = 0; /* Buffer overflow check (should never fail) */ if (!buf_safe(dst, BLEN(src) - TLS_CRYPT_OFF_CT + TLS_CRYPT_BLOCK_SIZE)) { CRYPT_ERROR("potential buffer overflow"); } if (!cipher_ctx_reset(ctx->cipher, BPTR(src) + TLS_CRYPT_OFF_TAG)) { CRYPT_ERROR("cipher reset failed"); } if (!cipher_ctx_update(ctx->cipher, BPTR(dst), &outlen, BPTR(src) + TLS_CRYPT_OFF_CT, BLEN(src) - TLS_CRYPT_OFF_CT)) { CRYPT_ERROR("cipher update failed"); } ASSERT(buf_inc_len(dst, outlen)); if (!cipher_ctx_final(ctx->cipher, BPTR(dst), &outlen)) { CRYPT_ERROR("cipher final failed"); } ASSERT(buf_inc_len(dst, outlen)); } /* Check authentication */ { const uint8_t *tag = BPTR(src) + TLS_CRYPT_OFF_TAG; uint8_t tag_check[TLS_CRYPT_TAG_SIZE] = { 0 }; dmsg(D_PACKET_CONTENT, "TLS-CRYPT UNWRAP AD: %s", format_hex(BPTR(src), TLS_CRYPT_OFF_TAG, 0, &gc)); dmsg(D_PACKET_CONTENT, "TLS-CRYPT UNWRAP TO: %s", format_hex(BPTR(dst), BLEN(dst), 80, &gc)); hmac_ctx_reset(ctx->hmac); hmac_ctx_update(ctx->hmac, BPTR(src), TLS_CRYPT_OFF_TAG); hmac_ctx_update(ctx->hmac, BPTR(dst), BLEN(dst)); hmac_ctx_final(ctx->hmac, tag_check); if (memcmp_constant_time(tag, tag_check, sizeof(tag_check))) { dmsg(D_CRYPTO_DEBUG, "tag : %s", format_hex(tag, sizeof(tag_check), 0, &gc)); dmsg(D_CRYPTO_DEBUG, "tag_check: %s", format_hex(tag_check, sizeof(tag_check), 0, &gc)); CRYPT_ERROR("packet authentication failed"); } } /* Check replay */ if (!(opt->flags & CO_IGNORE_PACKET_ID)) { struct packet_id_net pin; struct buffer tmp = *src; ASSERT(buf_advance(&tmp, TLS_CRYPT_OFF_PID)); ASSERT(packet_id_read(&pin, &tmp, true)); if (!crypto_check_replay(opt, &pin, error_prefix, &gc)) { CRYPT_ERROR("packet replay"); } } gc_free(&gc); return true; error_exit: crypto_clear_error(); dst->len = 0; gc_free(&gc); return false; }