aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/openvpn/crypto.c347
-rw-r--r--src/openvpn/crypto.h68
-rw-r--r--src/openvpn/crypto_backend.h59
-rw-r--r--src/openvpn/crypto_openssl.c74
-rw-r--r--src/openvpn/crypto_openssl.h7
-rw-r--r--src/openvpn/crypto_polarssl.c98
-rw-r--r--src/openvpn/crypto_polarssl.h3
-rw-r--r--src/openvpn/forward.c49
-rw-r--r--src/openvpn/ssl.c101
-rw-r--r--src/openvpn/ssl.h42
10 files changed, 749 insertions, 99 deletions
diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c
index 9c0c353..3e94470 100644
--- a/src/openvpn/crypto.c
+++ b/src/openvpn/crypto.c
@@ -83,9 +83,101 @@ memcmp_constant_time (const void *a, const void *b, size_t size) {
return ret;
}
-void
-openvpn_encrypt (struct buffer *buf, struct buffer work,
- struct crypto_options *opt, const struct frame* frame)
+static void
+openvpn_encrypt_aead (struct buffer *buf, struct buffer work,
+ struct crypto_options *opt) {
+ 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));
+
+ 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));
+ }
+
+ /* 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));
+
+ dmsg (D_PACKET_CONTENT, "ENCRYPT TO: %s", format_hex (BPTR (buf), BLEN (buf), 80, &gc));
+
+ *buf = work;
+
+cleanup:
+ gc_free (&gc);
+ return;
+
+err:
+ crypto_clear_error();
+ buf->len = 0;
+ goto cleanup;
+}
+
+static void
+openvpn_encrypt_v1 (struct buffer *buf, struct buffer work,
+ struct crypto_options *opt)
{
struct gc_arena gc;
gc_init (&gc);
@@ -104,9 +196,6 @@ openvpn_encrypt (struct buffer *buf, struct buffer work,
const cipher_kt_t *cipher_kt = cipher_ctx_get_cipher_kt (ctx->cipher);
int outlen;
- /* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */
- ASSERT (buf_init (&work, FRAME_HEADROOM (frame)));
-
/* Reserve space for HMAC */
if (ctx->hmac)
{
@@ -232,6 +321,22 @@ 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);
+ }
+}
+
/**
* Check packet ID for replay, and perform replay administration.
*
@@ -275,8 +380,141 @@ static bool crypto_check_replay(struct crypto_options *opt,
* On success, buf is set to point to plaintext, true
* is returned.
*/
-bool
-openvpn_decrypt (struct buffer *buf, struct buffer work,
+static bool
+openvpn_decrypt_aead (struct buffer *buf, struct buffer work,
+ struct crypto_options *opt, const struct frame* frame,
+ const uint8_t *ad_start)
+{
+ 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 (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 (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;
+}
+
+/*
+ * 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";
@@ -425,6 +663,33 @@ openvpn_decrypt (struct buffer *buf, struct buffer work,
return false;
}
+
+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;
+}
+
/*
* How many bytes will we add to frame buffer for a given
* set of crypto options?
@@ -447,6 +712,9 @@ crypto_adjust_frame_parameters(struct frame *frame,
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);
}
@@ -466,8 +734,10 @@ 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)
+ bool tls_mode, bool warn)
{
+ bool aead_cipher = false;
+
CLEAR (*kt);
if (ciphername && ciphername_defined)
{
@@ -477,14 +747,14 @@ init_key_type (struct key_type *kt, const char *ciphername,
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);
}
else
{
@@ -493,10 +763,12 @@ init_key_type (struct key_type *kt, const char *ciphername,
}
if (authname && authname_defined)
{
- 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);
+ }
}
- else
+ else if (!aead_cipher)
{
if (warn)
msg (M_WARN, "******* WARNING *******: null MAC specified, no authentication will be used");
@@ -566,6 +838,7 @@ free_key_ctx (struct key_ctx *ctx)
free(ctx->hmac);
ctx->hmac = NULL;
}
+ ctx->implicit_iv_len = 0;
}
void
@@ -575,7 +848,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)
{
@@ -655,8 +927,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");
}
/*
@@ -735,6 +1009,30 @@ test_crypto (struct crypto_options *co, struct frame* frame)
/* 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)
{
@@ -754,11 +1052,14 @@ test_crypto (struct crypto_options *co, struct frame* frame)
buf = work;
memcpy (buf_write_alloc (&buf, BLEN (&src)), 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)
diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h
index aac50c4..14b6ab7 100644
--- a/src/openvpn/crypto.h
+++ b/src/openvpn/crypto.h
@@ -86,6 +86,30 @@
* <tt> [ HMAC ] [ - IV - ] [ * packet payload * ] </tt>
*
* @par
+ * <b>GCM data channel crypto format</b> \n
+ * GCM modes are only supported in TLS mode. In these modes, the IV consists of
+ * the 32-bit packet counter followed by data from the HMAC key. The HMAC key
+ * can be used as IV, since in GCM and CCM modes the HMAC key is not used for
+ * the HMAC. The packet counter may not roll over within a single TLS sessions.
+ * This results in a unique IV for each packet, as required by GCM.
+ *
+ * @par
+ * The HMAC key data is pre-shared during the connection setup, and thus can be
+ * omitted in on-the-wire packets, saving 8 bytes per packet (for GCM and CCM).
+ *
+ * @par
+ * In GCM mode, P_DATA_V2 headers (the opcode and peer-id) are also
+ * authenticated as Additional Data.
+ *
+ * @par
+ * <i>GCM IV format:</i> \n
+ * <tt> [ - packet ID - ] [ - HMAC key data - ] </tt>\n
+ * <i>P_DATA_V1 GCM data channel crypto format:</i> \n
+ * <tt> [ opcode ] [ - packet ID - ] [ TAG ] [ * packet payload * ] </tt>
+ * <i>P_DATA_V2 GCM data channel crypto format:</i> \n
+ * <tt> [ - opcode/peer-id - ] [ - packet ID - ] [ TAG ] [ * packet payload * ] </tt>
+ *
+ * @par
* <b>No-crypto data channel format</b> \n
* In no-crypto mode (\c \-\-cipher \c none is specified), both TLS-mode and
* static key mode are supported. No encryption will be performed on the packet,
@@ -138,13 +162,16 @@ struct key
/**
- * Container for one set of OpenSSL cipher and/or HMAC contexts.
+ * Container for one set of cipher and/or HMAC contexts.
* @ingroup control_processor
*/
struct key_ctx
{
cipher_ctx_t *cipher; /**< Generic cipher %context. */
- hmac_ctx_t *hmac; /**< Generic HMAC %context. */
+ hmac_ctx_t *hmac; /**< Generic HMAC %context. */
+ uint8_t implicit_iv[OPENVPN_MAX_IV_LENGTH];
+ /**< The implicit part of the IV */
+ size_t implicit_iv_len; /**< The length of implicit_iv */
};
#define KEY_DIRECTION_BIDIRECTIONAL 0 /* same keys for both directions */
@@ -195,10 +222,10 @@ struct key_direction_state
*/
struct key_ctx_bi
{
- struct key_ctx encrypt; /**< OpenSSL cipher and/or HMAC contexts
- * for sending direction. */
- struct key_ctx decrypt; /**< OpenSSL cipher and/or HMAC contexts
- * for receiving direction. */
+ struct key_ctx encrypt; /**< Cipher and/or HMAC contexts for sending
+ * direction. */
+ struct key_ctx decrypt; /**< cipher and/or HMAC contexts for
+ * receiving direction. */
};
/**
@@ -238,6 +265,12 @@ struct crypto_options
* security operation functions. */
};
+/**
+ * Minimal IV length for AEAD mode ciphers (in bytes):
+ * 4-byte packet id + 8 bytes implicit IV.
+ */
+#define OPENVPN_AEAD_MIN_IV_LEN (sizeof (packet_id_type) + 8)
+
#define RKF_MUST_SUCCEED (1<<0)
#define RKF_INLINE (1<<1)
void read_key_file (struct key2 *key2, const char *file, const unsigned int flags);
@@ -278,6 +311,17 @@ void free_key_ctx (struct key_ctx *ctx);
void free_key_ctx_bi (struct key_ctx_bi *ctx);
+/**
+ * Set an implicit IV for a key context.
+ *
+ * @param ctx The key context to update
+ * @param iv The implicit IV to load into ctx
+ * @param len The length (in bytes) of iv
+ */
+bool key_ctx_set_implicit_iv (struct key_ctx *ctx, const uint8_t *iv,
+ size_t len);
+
+
/**************************************************************************/
/** @name Functions for performing security operations on data channel packets
@@ -301,17 +345,16 @@ void free_key_ctx_bi (struct key_ctx_bi *ctx);
*
* @param buf - The %buffer containing the packet on which to
* perform security operations.
- * @param work - A working %buffer.
+ * @param work - An initialized working %buffer.
* @param opt - The security parameter state for this VPN tunnel.
- * @param frame - The packet geometry parameters for this VPN
- * tunnel.
+ *
* @return This function returns void.\n On return, the \a buf argument
* will point to the resulting %buffer. This %buffer will either
* contain the processed packet ready for sending, or be empty if an
* error occurred.
*/
void openvpn_encrypt (struct buffer *buf, struct buffer work,
- struct crypto_options *opt, const struct frame* frame);
+ struct crypto_options *opt);
/**
@@ -337,6 +380,8 @@ void openvpn_encrypt (struct buffer *buf, struct buffer work,
* @param opt - The security parameter state for this VPN tunnel.
* @param frame - The packet geometry parameters for this VPN
* tunnel.
+ * @param ad_start - A pointer into buf, indicating from where to start
+ * authenticating additional data (AEAD mode only).
*
* @return
* @li True, if the packet was authenticated and decrypted successfully.
@@ -346,7 +391,8 @@ void openvpn_encrypt (struct buffer *buf, struct buffer work,
* an error occurred.
*/
bool openvpn_decrypt (struct buffer *buf, struct buffer work,
- struct crypto_options *opt, const struct frame* frame);
+ struct crypto_options *opt, const struct frame* frame,
+ const uint8_t *ad_start);
/** @} name Functions for performing security operations on data channel packets */
diff --git a/src/openvpn/crypto_backend.h b/src/openvpn/crypto_backend.h
index 1c23436..c5ff366 100644
--- a/src/openvpn/crypto_backend.h
+++ b/src/openvpn/crypto_backend.h
@@ -38,6 +38,8 @@
#endif
#include "basic.h"
+/* TLS uses a tag of 128 bytes, let's do the same for OpenVPN */
+#define OPENVPN_AEAD_TAG_LENGTH 16
/*
* This routine should have additional OpenSSL crypto library initialisations
@@ -221,6 +223,16 @@ int cipher_kt_iv_size (const cipher_kt_t *cipher_kt);
int cipher_kt_block_size (const cipher_kt_t *cipher_kt);
/**
+ * Returns the MAC tag size of the cipher, in bytes.
+ *
+ * @param ctx Static cipher parameters.
+ *
+ * @return Tag size in bytes, or 0 if the tag size could not be
+ * determined.
+ */
+int cipher_kt_tag_size (const cipher_kt_t *cipher_kt);
+
+/**
* Returns the mode that the cipher runs in.
*
* @param cipher_kt Static cipher parameters. May not be NULL.
@@ -248,6 +260,15 @@ bool cipher_kt_mode_cbc(const cipher_kt_t *cipher);
*/
bool cipher_kt_mode_ofb_cfb(const cipher_kt_t *cipher);
+/**
+ * Check if the supplied cipher is a supported AEAD mode cipher.
+ *
+ * @param cipher Static cipher parameters.
+ *
+ * @return true iff the cipher is a AEAD mode cipher.
+ */
+bool cipher_kt_mode_aead(const cipher_kt_t *cipher);
+
/**
*
@@ -287,6 +308,15 @@ void cipher_ctx_cleanup (cipher_ctx_t *ctx);
int cipher_ctx_iv_length (const cipher_ctx_t *ctx);
/**
+ * Gets the computed message authenticated code (MAC) tag for this cipher.
+ *
+ * @param ctx The cipher's context
+ * @param tag The buffer to write computed tag in.
+ * @param tag_size The tag buffer size, in bytes.
+ */
+int cipher_ctx_get_tag (cipher_ctx_t *ctx, uint8_t* tag, int tag_len);
+
+/**
* Returns the block size of the cipher, in bytes.
*
* @param ctx The cipher's context
@@ -327,6 +357,18 @@ const cipher_kt_t *cipher_ctx_get_cipher_kt (const cipher_ctx_t *ctx);
int cipher_ctx_reset (cipher_ctx_t *ctx, uint8_t *iv_buf);
/**
+ * Updates the given cipher context, providing additional data (AD) for
+ * authenticated encryption with additional data (AEAD) cipher modes.
+ *
+ * @param ctx Cipher's context. May not be NULL.
+ * @param src Source buffer
+ * @param src_len Length of the source buffer, in bytes
+ *
+ * @return \c 0 on failure, \c 1 on success.
+ */
+int cipher_ctx_update_ad (cipher_ctx_t *ctx, const uint8_t *src, int src_len);
+
+/**
* Updates the given cipher context, encrypting data in the source buffer, and
* placing any complete blocks in the destination buffer.
*
@@ -358,6 +400,23 @@ int cipher_ctx_update (cipher_ctx_t *ctx, uint8_t *dst, int *dst_len,
*/
int cipher_ctx_final (cipher_ctx_t *ctx, uint8_t *dst, int *dst_len);
+/**
+ * Like \c cipher_ctx_final, but check the computed authentication tag against
+ * the supplied (expected) tag. This function reports failure when the tags
+ * don't match.
+ *
+ * @param ctx Cipher's context. May not be NULL.
+ * @param dst Destination buffer.
+ * @param dst_len Length of the destination buffer, in bytes.
+ * @param tag The expected authentication tag.
+ * @param tag_len The length of tag, in bytes.
+ *
+ * @return \c 0 on failure, \c 1 on success.
+ */
+int cipher_ctx_final_check_tag (cipher_ctx_t *ctx, uint8_t *dst, int *dst_len,
+ uint8_t *tag, size_t tag_len);
+
+
/*
*
* Generic message digest information functions
diff --git a/src/openvpn/crypto_openssl.c b/src/openvpn/crypto_openssl.c
index 7dabe5d..56b6625 100644
--- a/src/openvpn/crypto_openssl.c
+++ b/src/openvpn/crypto_openssl.c
@@ -258,12 +258,12 @@ show_available_ciphers ()
int nid;
#ifndef ENABLE_SMALL
- printf ("The following ciphers and cipher modes are available\n"
- "for use with " PACKAGE_NAME ". Each cipher shown below may be\n"
- "used as a parameter to the --cipher option. The default\n"
- "key size is shown as well as whether or not it can be\n"
- "changed with the --keysize directive. Using a CBC mode\n"
- "is recommended. In static key mode only CBC mode is allowed.\n\n");
+ printf ("The following ciphers and cipher modes are available for use\n"
+ "with " PACKAGE_NAME ". Each cipher shown below may be use as a\n"
+ "parameter to the --cipher option. The default key size is\n"
+ "shown as well as whether or not it can be changed with the\n"
+ "--keysize directive. Using a CBC or GCM mode is recommended.\n"
+ "In static key mode only CBC mode is allowed.\n\n");
#endif
for (nid = 0; nid < 10000; ++nid) /* is there a better way to get the size of the nid list? */
@@ -275,13 +275,16 @@ show_available_ciphers ()
#ifdef ENABLE_OFB_CFB_MODE
|| cipher_kt_mode_ofb_cfb(cipher)
#endif
+#ifdef HAVE_AEAD_CIPHER_MODES
+ || cipher_kt_mode_aead(cipher)
+#endif
)
{
const char *var_key_size =
(EVP_CIPHER_flags (cipher) & EVP_CIPH_VARIABLE_LENGTH) ?
"variable" : "fixed";
- const char *ssl_only = cipher_kt_mode_ofb_cfb(cipher) ?
- " (TLS client/server mode)" : "";
+ const char *ssl_only = cipher_kt_mode_cbc(cipher) ?
+ "" : " (TLS client/server mode)";
printf ("%s %d bit default key (%s)%s\n", OBJ_nid2sn (nid),
EVP_CIPHER_key_length (cipher) * 8, var_key_size,
@@ -500,6 +503,15 @@ cipher_kt_block_size (const EVP_CIPHER *cipher_kt)
}
int
+cipher_kt_tag_size (const EVP_CIPHER *cipher_kt)
+{
+ if (cipher_kt_mode_aead(cipher_kt))
+ return OPENVPN_AEAD_TAG_LENGTH;
+ else
+ return 0;
+}
+
+int
cipher_kt_mode (const EVP_CIPHER *cipher_kt)
{
ASSERT(NULL != cipher_kt);
@@ -529,6 +541,16 @@ cipher_kt_mode_ofb_cfb(const cipher_kt_t *cipher)
;
}
+bool
+cipher_kt_mode_aead(const cipher_kt_t *cipher)
+{
+#ifdef HAVE_AEAD_CIPHER_MODES
+ return cipher && (cipher_kt_mode(cipher) == OPENVPN_MODE_GCM);
+#else
+ return false;
+#endif
+}
+
/*
*
* Generic cipher context functions
@@ -570,6 +592,15 @@ cipher_ctx_iv_length (const EVP_CIPHER_CTX *ctx)
return EVP_CIPHER_CTX_iv_length (ctx);
}
+int cipher_ctx_get_tag (EVP_CIPHER_CTX *ctx, uint8_t *tag_buf, int tag_size)
+{
+#ifdef HAVE_AEAD_CIPHER_MODES
+ return EVP_CIPHER_CTX_ctrl (ctx, EVP_CTRL_GCM_GET_TAG, tag_size, tag_buf);
+#else
+ ASSERT (0);
+#endif
+}
+
int
cipher_ctx_block_size(const EVP_CIPHER_CTX *ctx)
{
@@ -596,6 +627,19 @@ cipher_ctx_reset (EVP_CIPHER_CTX *ctx, uint8_t *iv_buf)
}
int
+cipher_ctx_update_ad (EVP_CIPHER_CTX *ctx, const uint8_t *src, int src_len)
+{
+#ifdef HAVE_AEAD_CIPHER_MODES
+ int len;
+ if (!EVP_CipherUpdate (ctx, NULL, &len, src, src_len))
+ crypto_msg(M_FATAL, "%s: EVP_CipherUpdate() failed", __func__);
+ return 1;
+#else
+ ASSERT (0);
+#endif
+}
+
+int
cipher_ctx_update (EVP_CIPHER_CTX *ctx, uint8_t *dst, int *dst_len,
uint8_t *src, int src_len)
{
@@ -610,6 +654,20 @@ cipher_ctx_final (EVP_CIPHER_CTX *ctx, uint8_t *dst, int *dst_len)
return EVP_CipherFinal (ctx, dst, dst_len);
}
+int
+cipher_ctx_final_check_tag (EVP_CIPHER_CTX *ctx, uint8_t *dst, int *dst_len,
+ uint8_t *tag, size_t tag_len)
+{
+#ifdef HAVE_AEAD_CIPHER_MODES
+ ASSERT (tag_len < SIZE_MAX);
+ if (!EVP_CIPHER_CTX_ctrl (ctx, EVP_CTRL_GCM_SET_TAG, tag_len, tag))
+ return 0;
+
+ return cipher_ctx_final (ctx, dst, dst_len);
+#else
+ ASSERT (0);
+#endif
+}
void
cipher_des_encrypt_ecb (const unsigned char key[DES_KEY_LENGTH],
diff --git a/src/openvpn/crypto_openssl.h b/src/openvpn/crypto_openssl.h
index 42c7e9a..f157041 100644
--- a/src/openvpn/crypto_openssl.h
+++ b/src/openvpn/crypto_openssl.h
@@ -61,6 +61,13 @@ typedef HMAC_CTX hmac_ctx_t;
/** Cipher is in CFB mode */
#define OPENVPN_MODE_CFB EVP_CIPH_CFB_MODE
+#ifdef HAVE_AEAD_CIPHER_MODES
+
+/** Cipher is in GCM mode */
+#define OPENVPN_MODE_GCM EVP_CIPH_GCM_MODE
+
+#endif /* HAVE_AEAD_CIPHER_MODES */
+
/** Cipher should encrypt */
#define OPENVPN_OP_ENCRYPT 1
diff --git a/src/openvpn/crypto_polarssl.c b/src/openvpn/crypto_polarssl.c
index 0e4c088..09cb129 100644
--- a/src/openvpn/crypto_polarssl.c
+++ b/src/openvpn/crypto_polarssl.c
@@ -174,21 +174,28 @@ show_available_ciphers ()
const int *ciphers = cipher_list();
#ifndef ENABLE_SMALL
- printf ("The following ciphers and cipher modes are available\n"
- "for use with " PACKAGE_NAME ". Each cipher shown below may be\n"
- "used as a parameter to the --cipher option. The default\n"
- "key size is shown as well as whether or not it can be\n"
- "changed with the --keysize directive. Using a CBC mode\n"
- "is recommended.\n\n");
+ printf ("The following ciphers and cipher modes are available for use\n"
+ "with " PACKAGE_NAME ". Each cipher shown below may be used as a\n"
+ "parameter to the --cipher option. Using a CBC or GCM mode is\n"
+ "recommended. In static key mode only CBC mode is allowed.\n\n");
#endif
while (*ciphers != 0)
{
- const cipher_info_t *info = cipher_info_from_type(*ciphers);
+ const cipher_kt_t *info = cipher_info_from_type(*ciphers);
- if (info && info->mode == POLARSSL_MODE_CBC)
- printf ("%s %d bit default key\n",
- cipher_kt_name(info), cipher_kt_key_size(info) * 8);
+ if (info && (cipher_kt_mode_cbc(info)
+#ifdef HAVE_AEAD_CIPHER_MODES
+ || cipher_kt_mode_aead(info)
+#endif
+ ))
+ {
+ const char *ssl_only = cipher_kt_mode_cbc(info) ?
+ "" : " (TLS client/server mode)";
+
+ printf ("%s %d bit default key%s\n",
+ cipher_kt_name(info), cipher_kt_key_size(info) * 8, ssl_only);
+ }
ciphers++;
}
@@ -436,6 +443,16 @@ cipher_kt_block_size (const cipher_info_t *cipher_kt)
}
int
+cipher_kt_tag_size (const cipher_info_t *cipher_kt)
+{
+#ifdef HAVE_AEAD_CIPHER_MODES
+ if (cipher_kt && cipher_kt_mode_aead(cipher_kt))
+ return OPENVPN_AEAD_TAG_LENGTH;
+#endif
+ return 0;
+}
+
+int
cipher_kt_mode (const cipher_info_t *cipher_kt)
{
ASSERT(NULL != cipher_kt);
@@ -455,6 +472,12 @@ cipher_kt_mode_ofb_cfb(const cipher_kt_t *cipher)
cipher_kt_mode(cipher) == OPENVPN_MODE_CFB);
}
+bool
+cipher_kt_mode_aead(const cipher_kt_t *cipher)
+{
+ return cipher && cipher_kt_mode(cipher) == OPENVPN_MODE_GCM;
+}
+
/*
*
@@ -491,6 +514,21 @@ int cipher_ctx_iv_length (const cipher_context_t *ctx)
return cipher_get_iv_size(ctx);
}
+int cipher_ctx_get_tag (cipher_ctx_t *ctx, uint8_t* tag, int tag_len)
+{
+#ifdef HAVE_AEAD_CIPHER_MODES
+ if (tag_len > SIZE_MAX)
+ return 0;
+
+ if (!polar_ok (cipher_write_tag (ctx, (unsigned char *) tag, tag_len)))
+ return 0;
+
+ return 1;
+#else
+ ASSERT(0);
+#endif /* HAVE_AEAD_CIPHER_MODES */
+}
+
int cipher_ctx_block_size(const cipher_context_t *ctx)
{
return cipher_get_block_size(ctx);
@@ -520,6 +558,21 @@ int cipher_ctx_reset (cipher_context_t *ctx, uint8_t *iv_buf)
return 1;
}
+int cipher_ctx_update_ad (cipher_ctx_t *ctx, const uint8_t *src, int src_len)
+{
+#ifdef HAVE_AEAD_CIPHER_MODES
+ if (src_len > SIZE_MAX)
+ return 0;
+
+ if (!polar_ok (cipher_update_ad (ctx, src, src_len)))
+ return 0;
+
+ return 1;
+#else
+ ASSERT(0);
+#endif /* HAVE_AEAD_CIPHER_MODES */
+}
+
int cipher_ctx_update (cipher_context_t *ctx, uint8_t *dst, int *dst_len,
uint8_t *src, int src_len)
{
@@ -545,6 +598,31 @@ int cipher_ctx_final (cipher_context_t *ctx, uint8_t *dst, int *dst_len)
return 1;
}
+int cipher_ctx_final_check_tag (cipher_context_t *ctx, uint8_t *dst,
+ int *dst_len, uint8_t *tag, size_t tag_len)
+{
+#ifdef HAVE_AEAD_CIPHER_MODES
+ if (POLARSSL_DECRYPT != ctx->operation)
+ return 0;
+
+ if (tag_len > SIZE_MAX)
+ return 0;
+
+ if (!cipher_ctx_final (ctx, dst, dst_len))
+ {
+ msg (D_CRYPT_ERRORS, "%s: cipher_ctx_final() failed", __func__);
+ return 0;
+ }
+
+ if (!polar_ok (cipher_check_tag (ctx, (const unsigned char *) tag, tag_len)))
+ return 0;
+
+ return 1;
+#else
+ ASSERT(0);
+#endif /* HAVE_AEAD_CIPHER_MODES */
+}
+
void
cipher_des_encrypt_ecb (const unsigned char key[DES_KEY_LENGTH],
unsigned char *src,
diff --git a/src/openvpn/crypto_polarssl.h b/src/openvpn/crypto_polarssl.h
index 94306ed..7be0862 100644
--- a/src/openvpn/crypto_polarssl.h
+++ b/src/openvpn/crypto_polarssl.h
@@ -61,6 +61,9 @@ typedef md_context_t hmac_ctx_t;
/** Cipher is in CFB mode */
#define OPENVPN_MODE_CFB POLARSSL_MODE_CFB
+/** Cipher is in GCM mode */
+#define OPENVPN_MODE_GCM POLARSSL_MODE_GCM
+
/** Cipher should encrypt */
#define OPENVPN_OP_ENCRYPT POLARSSL_ENCRYPT
diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c
index 75cd21d..4a91f92 100644
--- a/src/openvpn/forward.c
+++ b/src/openvpn/forward.c
@@ -457,43 +457,41 @@ encrypt_sign (struct context *c, bool comp_frag)
}
#ifdef ENABLE_CRYPTO
- /*
- * If TLS mode, get the key we will use to encrypt
- * the packet.
- */
+ /* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */
+ ASSERT (buf_init (&b->encrypt_buf, FRAME_HEADROOM (&c->c2.frame)));
+
if (c->c2.tls_multi)
{
+ /* Get the key we will use to encrypt the packet. */
tls_pre_encrypt (c->c2.tls_multi, &c->c2.buf, &co);
+ /* If using P_DATA_V2, prepend the 1-byte opcode and 3-byte peer-id to the
+ * packet before openvpn_encrypt(), so we can authenticate the opcode too.
+ */
+ if (c->c2.buf.len > 0 && !c->c2.tls_multi->opt.server && c->c2.tls_multi->use_peer_id)
+ tls_prepend_opcode_v2 (c->c2.tls_multi, &b->encrypt_buf);
}
else
{
co = &c->c2.crypto_options;
}
- /*
- * Encrypt the packet and write an optional
- * HMAC signature.
- */
- openvpn_encrypt (&c->c2.buf, b->encrypt_buf, co, &c->c2.frame);
+ /* Encrypt and authenticate the packet */
+ openvpn_encrypt (&c->c2.buf, b->encrypt_buf, co);
+
+ /* Do packet administration */
+ if (c->c2.tls_multi)
+ {
+ if (c->c2.buf.len > 0 && (c->c2.tls_multi->opt.server || !c->c2.tls_multi->use_peer_id))
+ tls_prepend_opcode_v1(c->c2.tls_multi, &c->c2.buf);
+ tls_post_encrypt (c->c2.tls_multi, &c->c2.buf);
+ }
#endif
+
/*
* Get the address we will be sending the packet to.
*/
link_socket_get_outgoing_addr (&c->c2.buf, get_link_socket_info (c),
&c->c2.to_link_addr);
-#ifdef ENABLE_CRYPTO
- /*
- * In TLS mode, prepend the appropriate one-byte opcode
- * to the packet which identifies it as a data channel
- * packet and gives the low-permutation version of
- * the key-id to the recipient so it knows which
- * decrypt key to use.
- */
- if (c->c2.tls_multi)
- {
- tls_post_encrypt (c->c2.tls_multi, &c->c2.buf);
- }
-#endif
/* if null encryption, copy result to read_tun_buf */
buffer_turnover (orig_buf, &c->c2.to_link, &c->c2.buf, &b->read_tun_buf);
@@ -780,6 +778,7 @@ process_incoming_link_part1 (struct context *c, struct link_socket_info *lsi, bo
if (c->c2.buf.len > 0)
{
struct crypto_options *co = NULL;
+ const uint8_t *ad_start = NULL;
if (!link_socket_verify_incoming_addr (&c->c2.buf, lsi, &c->c2.from))
link_socket_bad_incoming_addr (&c->c2.buf, lsi, &c->c2.from);
@@ -796,7 +795,8 @@ process_incoming_link_part1 (struct context *c, struct link_socket_info *lsi, bo
* will load crypto_options with the correct encryption key
* and return false.
*/
- if (tls_pre_decrypt (c->c2.tls_multi, &c->c2.from, &c->c2.buf, &co, floated))
+ if (tls_pre_decrypt (c->c2.tls_multi, &c->c2.from, &c->c2.buf, &co,
+ floated, &ad_start))
{
interval_action (&c->c2.tmp_int);
@@ -819,7 +819,8 @@ process_incoming_link_part1 (struct context *c, struct link_socket_info *lsi, bo
#endif
/* authenticate and decrypt the incoming packet */
- decrypt_status = openvpn_decrypt (&c->c2.buf, c->c2.buffers->decrypt_buf, co, &c->c2.frame);
+ decrypt_status = openvpn_decrypt (&c->c2.buf, c->c2.buffers->decrypt_buf,
+ co, &c->c2.frame, ad_start);
if (!decrypt_status && link_socket_connection_oriented (c->c2.link_socket))
{
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 7f99ee9..d1a6fa8 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -250,6 +250,20 @@ static const tls_cipher_name_pair tls_cipher_name_translation_table[] = {
{NULL, NULL}
};
+/**
+ * Update the implicit IV for a key_ctx_bi based on TLS session ids and cipher
+ * used.
+ *
+ * Note that the implicit IV is based on the HMAC key, but only in AEAD modes
+ * where the HMAC key is not used for an actual HMAC.
+ *
+ * @param ctx Encrypt/decrypt key context
+ * @param key HMAC key, used to calculate implicit IV
+ * @param key_len HMAC key length
+ */
+static void
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len);
+
const tls_cipher_name_pair *
tls_get_cipher_name_pair (const char * cipher_name, size_t len) {
const tls_cipher_name_pair * pair = tls_cipher_name_translation_table;
@@ -1252,7 +1266,7 @@ write_control_auth (struct tls_session *session,
if (session->tls_auth.key_ctx_bi.encrypt.hmac)
{
/* no encryption, only write hmac */
- openvpn_encrypt (buf, null, &session->tls_auth, NULL);
+ openvpn_encrypt (buf, null, &session->tls_auth);
ASSERT (swap_hmac (buf, &session->tls_auth, false));
}
*to_link_addr = &ks->remote_addr;
@@ -1284,7 +1298,7 @@ read_control_auth (struct buffer *buf,
/* authenticate only (no decrypt) and remove the hmac record
from the head of the buffer */
- openvpn_decrypt (buf, null, co, NULL);
+ openvpn_decrypt (buf, null, co, NULL, BPTR (buf));
if (!buf->len)
{
msg (D_TLS_ERRORS,
@@ -1438,7 +1452,7 @@ tls1_P_hash(const md_kt_t *md_kt,
* (2) The pre-master secret is generated by the client.
*/
static void
-tls1_PRF(uint8_t *label,
+tls1_PRF(const uint8_t *label,
int label_len,
const uint8_t *sec,
int slen,
@@ -1590,6 +1604,12 @@ generate_key_expansion (struct key_ctx_bi *key,
OPENVPN_OP_DECRYPT,
"Data Channel Decrypt");
+ /* Initialize implicit IVs */
+ key_ctx_update_implicit_iv (&key->encrypt, key2.keys[(int)server].hmac,
+ MAX_HMAC_KEY_LENGTH);
+ key_ctx_update_implicit_iv (&key->decrypt, key2.keys[1-(int)server].hmac,
+ MAX_HMAC_KEY_LENGTH);
+
ret = true;
exit:
@@ -1599,6 +1619,23 @@ generate_key_expansion (struct key_ctx_bi *key,
return ret;
}
+static void
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len) {
+ const cipher_kt_t *cipher_kt = cipher_ctx_get_cipher_kt (ctx->cipher);
+
+ /* Only use implicit IV in AEAD cipher mode, where HMAC key is not used */
+ if (cipher_kt_mode_aead (cipher_kt))
+ {
+ size_t impl_iv_len = 0;
+ ASSERT (cipher_kt_iv_size (cipher_kt) >= OPENVPN_AEAD_MIN_IV_LEN);
+ impl_iv_len = cipher_kt_iv_size (cipher_kt) - sizeof (packet_id_type);
+ ASSERT (impl_iv_len <= OPENVPN_MAX_IV_LENGTH);
+ ASSERT (impl_iv_len <= key_len);
+ memcpy (ctx->implicit_iv, key, impl_iv_len);
+ ctx->implicit_iv_len = impl_iv_len;
+ }
+}
+
static bool
random_bytes_to_buf (struct buffer *buf,
uint8_t *out,
@@ -2802,7 +2839,8 @@ tls_pre_decrypt (struct tls_multi *multi,
const struct link_socket_actual *from,
struct buffer *buf,
struct crypto_options **opt,
- bool floated)
+ bool floated,
+ const uint8_t **ad_start)
{
struct gc_arena gc = gc_new ();
bool ret = false;
@@ -2850,9 +2888,17 @@ tls_pre_decrypt (struct tls_multi *multi,
{
/* return appropriate data channel decrypt key in opt */
*opt = &ks->crypto_options;
- ASSERT (buf_advance (buf, 1));
if (op == P_DATA_V2)
{
+ *ad_start = BPTR(buf);
+ }
+ ASSERT (buf_advance (buf, 1));
+ if (op == P_DATA_V1)
+ {
+ *ad_start = BPTR(buf);
+ }
+ else if (op == P_DATA_V2)
+ {
if (buf->len < 4)
{
msg (D_TLS_ERRORS, "Protocol error: received P_DATA_V2 from %s but length is < 4",
@@ -3412,30 +3458,45 @@ tls_pre_encrypt (struct tls_multi *multi,
*opt = NULL;
}
-/* Prepend the appropriate opcode to encrypted buffer prior to TCP/UDP send */
void
-tls_post_encrypt (struct tls_multi *multi, struct buffer *buf)
+tls_prepend_opcode_v1 (const struct tls_multi *multi, struct buffer *buf)
{
- struct key_state *ks;
- uint8_t *op;
+ struct key_state *ks = multi->save_ks;
+ uint8_t op;
+
+ msg (D_TLS_DEBUG, __func__);
+
+ ASSERT (ks);
+
+ op = (P_DATA_V1 << P_OPCODE_SHIFT) | ks->key_id;
+ ASSERT (buf_write_prepend (buf, &op, 1));
+}
+
+void
+tls_prepend_opcode_v2 (const struct tls_multi *multi, struct buffer *buf)
+{
+ struct key_state *ks = multi->save_ks;
uint32_t peer;
- ks = multi->save_ks;
+ msg (D_TLS_DEBUG, __func__);
+
+ ASSERT (ks);
+
+ peer = htonl(((P_DATA_V2 << P_OPCODE_SHIFT) | ks->key_id) << 24
+ | (multi->peer_id & 0xFFFFFF));
+ ASSERT (buf_write_prepend (buf, &peer, 4));
+}
+
+void
+tls_post_encrypt (struct tls_multi *multi, struct buffer *buf)
+{
+ struct key_state *ks = multi->save_ks;
multi->save_ks = NULL;
+
if (buf->len > 0)
{
ASSERT (ks);
- if (!multi->opt.server && multi->use_peer_id)
- {
- peer = htonl(((P_DATA_V2 << P_OPCODE_SHIFT) | ks->key_id) << 24 | (multi->peer_id & 0xFFFFFF));
- ASSERT (buf_write_prepend (buf, &peer, 4));
- }
- else
- {
- ASSERT (op = buf_prepend (buf, 1));
- *op = (P_DATA_V1 << P_OPCODE_SHIFT) | ks->key_id;
- }
++ks->n_packets;
ks->n_bytes += buf->len;
}
diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h
index 20991cc..d9ff8d0 100644
--- a/src/openvpn/ssl.h
+++ b/src/openvpn/ssl.h
@@ -294,6 +294,8 @@ int tls_multi_process (struct tls_multi *multi,
* @param buf - A buffer structure containing the incoming packet.
* @param opt - Returns a crypto options structure with the appropriate security
* parameters to handle the packet if it is a data channel packet.
+ * @param ad_start - Returns a pointer to the start of the authenticated data of
+ * of this packet
*
* @return
* @li True if the packet is a control channel packet that has been
@@ -305,7 +307,8 @@ bool tls_pre_decrypt (struct tls_multi *multi,
const struct link_socket_actual *from,
struct buffer *buf,
struct crypto_options **opt,
- bool floated);
+ bool floated,
+ const uint8_t **ad_start);
/**************************************************************************/
@@ -366,8 +369,41 @@ void tls_pre_encrypt (struct tls_multi *multi,
/**
- * Prepend the one-byte OpenVPN header to the packet, and perform some
- * accounting for the key state used.
+ * Prepend a one-byte OpenVPN data channel P_DATA_V1 opcode to the packet.
+ *
+ * The opcode identifies the packet as a V1 data channel packet and gives the
+ * low-permutation version of the key-id to the recipient, so it knows which
+ * decrypt key to use.
+ *
+ * @param multi - The TLS state for this packet's destination VPN tunnel.
+ * @param buf - The buffer to write the header to.
+ *
+ * @ingroup data_crypto
+ */
+void
+tls_prepend_opcode_v1 (const struct tls_multi *multi, struct buffer *buf);
+
+/**
+ * Prepend an OpenVPN data channel P_DATA_V2 header to the packet. The
+ * P_DATA_V2 header consists of a 1-byte opcode, followed by a 3-byte peer-id.
+ *
+ * The opcode identifies the packet as a V2 data channel packet and gives the
+ * low-permutation version of the key-id to the recipient, so it knows which
+ * decrypt key to use.
+ *
+ * The peer-id is sent by clients to servers to help the server determine to
+ * select the decrypt key when the client is roaming between addresses/ports.
+ *
+ * @param multi - The TLS state for this packet's destination VPN tunnel.
+ * @param buf - The buffer to write the header to.
+ *
+ * @ingroup data_crypto
+ */
+void
+tls_prepend_opcode_v2 (const struct tls_multi *multi, struct buffer *buf);
+
+/**
+ * Perform some accounting for the key state used.
* @ingroup data_crypto
*
* @param multi - The TLS state for this packet's destination VPN tunnel.