Moving crypto_options into key_state enables us to stop using the global context for each packet encrypt/decrypt operation. Decoupling the crypto from the global context removes the need to copy the relevant parts of crypto_options for each processed packet, but instead enables us to just pass along a pointer to the related crypto_options.
This paves the way for an efficient GCM cipher mode implementation, but is probably fruitful too for threading and/or cipher negotiation. Signed-off-by: Steffan Karger <stef...@karger.me> --- src/openvpn/forward.c | 18 ++++++++++++++---- src/openvpn/init.c | 21 +++++++++++++-------- src/openvpn/ssl.c | 36 +++++++++++++++--------------------- src/openvpn/ssl.h | 15 +++++++-------- src/openvpn/ssl_common.h | 2 ++ 5 files changed, 51 insertions(+), 41 deletions(-) diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index 36a99e6..75cd21d 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -432,6 +432,7 @@ encrypt_sign (struct context *c, bool comp_frag) { struct context_buffers *b = c->c2.buffers; const uint8_t *orig_buf = c->c2.buf.data; + struct crypto_options *co = NULL; #if P2MP_SERVER /* @@ -462,14 +463,18 @@ encrypt_sign (struct context *c, bool comp_frag) */ if (c->c2.tls_multi) { - tls_pre_encrypt (c->c2.tls_multi, &c->c2.buf, &c->c2.crypto_options); + tls_pre_encrypt (c->c2.tls_multi, &c->c2.buf, &co); + } + else + { + co = &c->c2.crypto_options; } /* * Encrypt the packet and write an optional * HMAC signature. */ - openvpn_encrypt (&c->c2.buf, b->encrypt_buf, &c->c2.crypto_options, &c->c2.frame); + openvpn_encrypt (&c->c2.buf, b->encrypt_buf, co, &c->c2.frame); #endif /* * Get the address we will be sending the packet to. @@ -774,6 +779,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; 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); @@ -790,7 +796,7 @@ 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, &c->c2.crypto_options, floated)) + if (tls_pre_decrypt (c->c2.tls_multi, &c->c2.from, &c->c2.buf, &co, floated)) { interval_action (&c->c2.tmp_int); @@ -799,6 +805,10 @@ process_incoming_link_part1 (struct context *c, struct link_socket_info *lsi, bo event_timeout_reset (&c->c2.ping_rec_interval); } } + else + { + co = &c->c2.crypto_options; + } #if P2MP_SERVER /* * Drop non-TLS packet if client-connect script/plugin has not @@ -809,7 +819,7 @@ 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, &c->c2.crypto_options, &c->c2.frame); + decrypt_status = openvpn_decrypt (&c->c2.buf, c->c2.buffers->decrypt_buf, co, &c->c2.frame); if (!decrypt_status && link_socket_connection_oriented (c->c2.link_socket)) { diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 7e6e448..8fc5c5d 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -2048,14 +2048,6 @@ init_crypto_pre (struct context *c, const unsigned int flags) packet_id_persist_load (&c->c1.pid_persist, c->options.packet_id_file); } - /* Initialize crypto options */ - - if (c->options.use_iv) - c->c2.crypto_options.flags |= CO_USE_IV; - - if (c->options.mute_replay_warnings) - c->c2.crypto_options.flags |= CO_MUTE_REPLAY_WARNINGS; - #ifdef ENABLE_PREDICTION_RESISTANCE if (c->options.use_prediction_resistance) rand_ctx_enable_prediction_resistance(); @@ -2074,6 +2066,13 @@ do_init_crypto_static (struct context *c, const unsigned int flags) init_crypto_pre (c, flags); + /* Initialize flags */ + if (c->options.use_iv) + c->c2.crypto_options.flags |= CO_USE_IV; + + if (c->options.mute_replay_warnings) + c->c2.crypto_options.flags |= CO_MUTE_REPLAY_WARNINGS; + /* Initialize packet ID tracking */ if (options->replay) { @@ -2277,6 +2276,12 @@ do_init_crypto_tls (struct context *c, const unsigned int flags) /* Set all command-line TLS-related options */ CLEAR (to); + if (options->use_iv) + to.crypto_flags |= CO_USE_IV; + + if (options->mute_replay_warnings) + to.crypto_flags |= CO_MUTE_REPLAY_WARNINGS; + to.crypto_flags_and = ~(CO_PACKET_ID_LONG_FORM); if (packet_id_long_form) to.crypto_flags_or = CO_PACKET_ID_LONG_FORM; diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index d39f131..6aa9284 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -786,6 +786,13 @@ key_state_init (struct tls_session *session, struct key_state *ks) session->opt->replay_time, "SSL", ks->key_id); + ks->crypto_options.key_ctx_bi = &ks->key; + ks->crypto_options.packet_id = session->opt->replay ? &ks->packet_id : NULL; + ks->crypto_options.pid_persist = NULL; + ks->crypto_options.flags = session->opt->crypto_flags; + ks->crypto_options.flags &= session->opt->crypto_flags_and; + ks->crypto_options.flags |= session->opt->crypto_flags_or; + #ifdef MANAGEMENT_DEF_AUTH ks->mda_key_id = session->opt->mda_context->mda_key_id_counter++; #endif @@ -1700,6 +1707,8 @@ key_state_soft_reset (struct tls_session *session) ks->must_die = now + session->opt->transition_window; /* remaining lifetime of old key */ key_state_free (ks_lame, false); *ks_lame = *ks; + ks_lame->crypto_options.key_ctx_bi = &ks_lame->key; + ks_lame->crypto_options.packet_id = &ks_lame->packet_id; key_state_init (session, ks); ks->session_id_remote = ks_lame->session_id_remote; @@ -2808,7 +2817,7 @@ bool tls_pre_decrypt (struct tls_multi *multi, const struct link_socket_actual *from, struct buffer *buf, - struct crypto_options *opt, + struct crypto_options **opt, bool floated) { struct gc_arena gc = gc_new (); @@ -2856,12 +2865,7 @@ tls_pre_decrypt (struct tls_multi *multi, && (floated || link_socket_actual_match (from, &ks->remote_addr))) { /* return appropriate data channel decrypt key in opt */ - opt->key_ctx_bi = &ks->key; - opt->packet_id = multi->opt.replay ? &ks->packet_id : NULL; - opt->pid_persist = NULL; - opt->flags &= multi->opt.crypto_flags_and; - opt->flags |= multi->opt.crypto_flags_or; - + *opt = &ks->crypto_options; ASSERT (buf_advance (buf, 1)); if (op == P_DATA_V2) { @@ -3251,10 +3255,7 @@ tls_pre_decrypt (struct tls_multi *multi, done: buf->len = 0; - opt->key_ctx_bi = NULL; - opt->packet_id = NULL; - opt->pid_persist = NULL; - opt->flags &= multi->opt.crypto_flags_and; + *opt = NULL; gc_free (&gc); return ret; @@ -3380,7 +3381,7 @@ tls_pre_decrypt_lite (const struct tls_auth_standalone *tas, /* Choose the key with which to encrypt a data packet */ void tls_pre_encrypt (struct tls_multi *multi, - struct buffer *buf, struct crypto_options *opt) + struct buffer *buf, struct crypto_options **opt) { multi->save_ks = NULL; if (buf->len > 0) @@ -3409,11 +3410,7 @@ tls_pre_encrypt (struct tls_multi *multi, if (ks_select) { - opt->key_ctx_bi = &ks_select->key; - opt->packet_id = multi->opt.replay ? &ks_select->packet_id : NULL; - opt->pid_persist = NULL; - opt->flags &= multi->opt.crypto_flags_and; - opt->flags |= multi->opt.crypto_flags_or; + *opt = &ks_select->crypto_options; multi->save_ks = ks_select; dmsg (D_TLS_KEYSELECT, "TLS: tls_pre_encrypt: key_id=%d", ks_select->key_id); return; @@ -3428,10 +3425,7 @@ tls_pre_encrypt (struct tls_multi *multi, } buf->len = 0; - opt->key_ctx_bi = NULL; - opt->packet_id = NULL; - opt->pid_persist = NULL; - opt->flags &= multi->opt.crypto_flags_and; + *opt = NULL; } /* Prepend the appropriate opcode to encrypted buffer prior to TCP/UDP send */ diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h index 797c3e5..e9d0f28 100644 --- a/src/openvpn/ssl.h +++ b/src/openvpn/ssl.h @@ -293,9 +293,8 @@ int tls_multi_process (struct tls_multi *multi, * of this packet. * @param from - The source address of the packet. * @param buf - A buffer structure containing the incoming packet. - * @param opt - A crypto options structure that will be loaded with the - * appropriate security parameters to handle the packet if it is a - * data channel packet. + * @param opt - Returns a crypto options structure with the appropriate security + * parameters to handle the packet if it is a data channel packet. * * @return * @li True if the packet is a control channel packet that has been @@ -306,7 +305,7 @@ int tls_multi_process (struct tls_multi *multi, bool tls_pre_decrypt (struct tls_multi *multi, const struct link_socket_actual *from, struct buffer *buf, - struct crypto_options *opt, + struct crypto_options **opt, bool floated); @@ -356,15 +355,15 @@ bool tls_pre_decrypt_lite (const struct tls_auth_standalone *tas, * @ingroup data_crypto * * If no appropriate security parameters can be found, or if some other - * error occurs, then the buffer is set to empty. + * error occurs, then the buffer is set to empty, and the parameters to a NULL + * pointer. * * @param multi - The TLS state for this packet's destination VPN tunnel. * @param buf - The buffer containing the outgoing packet. - * @param opt - The crypto options structure into which the appropriate - * security parameters should be loaded. + * @param opt - Returns a crypto options structure with the security parameters. */ void tls_pre_encrypt (struct tls_multi *multi, - struct buffer *buf, struct crypto_options *opt); + struct buffer *buf, struct crypto_options **opt); /** diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h index e2b0ebf..00d2ce8 100644 --- a/src/openvpn/ssl_common.h +++ b/src/openvpn/ssl_common.h @@ -162,6 +162,7 @@ struct key_state struct link_socket_actual remote_addr; /* peer's IP addr */ struct packet_id packet_id; /* for data channel, to prevent replay attacks */ + struct crypto_options crypto_options;/* data channel crypto options */ struct key_ctx_bi key; /* data channel keys for encrypt/decrypt/hmac */ struct key_source2 *key_src; /* source entropy for key expansion */ @@ -259,6 +260,7 @@ struct tls_options bool pass_config_info; /* struct crypto_option flags */ + unsigned int crypto_flags; unsigned int crypto_flags_and; unsigned int crypto_flags_or; -- 2.5.0