blob: ecc654ee3a16f4646a9ef4f03d3f265edd9f49db [file] [log] [blame]
/*
* 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. <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
* 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"
#ifdef ENABLE_CRYPTO
#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;
}
#endif /* EMABLE_CRYPTO */