diff options
author | Nathan Skrzypczak <nathan.skrzypczak@gmail.com> | 2019-11-07 13:52:09 +0100 |
---|---|---|
committer | Florin Coras <florin.coras@gmail.com> | 2019-12-17 17:03:50 +0000 |
commit | d1b9e706841150d0da042a05aab51f19fa804192 (patch) | |
tree | 340bcbdd128ff86323eb963631f9f3f018328a67 /src | |
parent | 70ae4efaa9b719a0bc84345143a1a4d3944a70fe (diff) |
quic: Implement crypto contexts
Type: feature
Make quic use the crypto contexts. This introduces a crypto context
pool backed by a hashtable giving ctx indexes by connect params
(ckpair, engine, rx & tx fifo sizes).
Applications keep the initialization vector common.
Change-Id: I22ed6711196cd70a2f2f74240f12113c7af8dfcd
Signed-off-by: Nathan Skrzypczak <nathan.skrzypczak@gmail.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/quic/quic.c | 297 | ||||
-rw-r--r-- | src/plugins/quic/quic.h | 20 | ||||
-rw-r--r-- | src/vnet/session/application.h | 1 | ||||
-rw-r--r-- | src/vnet/session/application_interface.h | 1 |
4 files changed, 242 insertions, 77 deletions
diff --git a/src/plugins/quic/quic.c b/src/plugins/quic/quic.c index f4e0aabdba2..738290177cc 100644 --- a/src/plugins/quic/quic.c +++ b/src/plugins/quic/quic.c @@ -48,38 +48,146 @@ static quicly_stream_open_t on_stream_open; static quicly_closed_by_peer_t on_closed_by_peer; static quicly_now_t quicly_vpp_now_cb; +/* Crypto contexts */ + +static inline void +quic_crypto_context_make_key_from_ctx (clib_bihash_kv_24_8_t * kv, + quic_ctx_t * ctx) +{ + application_t *app = application_get (ctx->parent_app_id); + kv->key[0] = ((u64) ctx->ckpair_index) << 32 | (u64) ctx->crypto_engine; + kv->key[1] = app->sm_properties.rx_fifo_size - 1; + kv->key[2] = app->sm_properties.tx_fifo_size - 1; +} + +static inline void +quic_crypto_context_make_key_from_crctx (clib_bihash_kv_24_8_t * kv, + crypto_context_t * crctx) +{ + quic_crypto_context_data_t *data = + (quic_crypto_context_data_t *) crctx->data; + kv->key[0] = ((u64) crctx->ckpair_index) << 32 | (u64) crctx->crypto_engine; + kv->key[1] = data->quicly_ctx.transport_params.max_stream_data.bidi_local; + kv->key[2] = data->quicly_ctx.transport_params.max_stream_data.bidi_remote; +} + +static void +quic_crypto_context_free_if_needed (crypto_context_t * crctx, u8 thread_index) +{ + quic_main_t *qm = &quic_main; + clib_bihash_kv_24_8_t kv; + if (crctx->n_subscribers) + return; + quic_crypto_context_make_key_from_crctx (&kv, crctx); + clib_bihash_add_del_24_8 (&qm->wrk_ctx[thread_index].crypto_context_hash, + &kv, 0 /* is_add */ ); + clib_mem_free (crctx->data); + pool_put (qm->wrk_ctx[thread_index].crypto_ctx_pool, crctx); +} + static int -quic_store_quicly_ctx (application_t * app, u32 ckpair_index, - u8 crypto_engine) +quic_app_cert_key_pair_delete_callback (app_cert_key_pair_t * ckpair) { quic_main_t *qm = &quic_main; - quicly_context_t *quicly_ctx; - ptls_iovec_t key_vec; - app_cert_key_pair_t *ckpair; - u64 max_enq; - if (app->quicly_ctx) - return 0; + crypto_context_t *crctx; + clib_bihash_kv_24_8_t kv; + vlib_thread_main_t *vtm = vlib_get_thread_main (); + int num_threads = 1 /* main thread */ + vtm->n_threads; + int i; - if (crypto_engine == CRYPTO_ENGINE_NONE) + for (i = 0; i < num_threads; i++) { - QUIC_DBG (2, "No crypto engine specified, using %d", crypto_engine); - crypto_engine = qm->default_crypto_engine; + /* *INDENT-OFF* */ + pool_foreach (crctx, qm->wrk_ctx[i].crypto_ctx_pool, ({ + if (crctx->ckpair_index == ckpair->cert_key_index) + { + quic_crypto_context_make_key_from_crctx (&kv, crctx); + clib_bihash_add_del_24_8 (&qm->wrk_ctx[i].crypto_context_hash, &kv, 0 /* is_add */ ); + } + })); + /* *INDENT-ON* */ } - if (!clib_bitmap_get (qm->available_crypto_engines, crypto_engine)) + return 0; +} + +static crypto_context_t * +quic_crypto_context_alloc (u8 thread_index) +{ + quic_main_t *qm = &quic_main; + crypto_context_t *crctx; + u32 idx; + + pool_get (qm->wrk_ctx[thread_index].crypto_ctx_pool, crctx); + clib_memset (crctx, 0, sizeof (*crctx)); + idx = (crctx - qm->wrk_ctx[thread_index].crypto_ctx_pool); + crctx->ctx_index = ((u32) thread_index) << 24 | idx; + + return crctx; +} + +static crypto_context_t * +quic_crypto_context_get (u32 cr_index, u32 thread_index) +{ + quic_main_t *qm = &quic_main; + ASSERT (cr_index >> 24 == thread_index); + return pool_elt_at_index (qm->wrk_ctx[thread_index].crypto_ctx_pool, + cr_index & 0x00ffffff); +} + +static clib_error_t * +quic_list_crypto_context_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + quic_main_t *qm = &quic_main; + crypto_context_t *crctx; + vlib_thread_main_t *vtm = vlib_get_thread_main (); + int i, num_threads = 1 /* main thread */ + vtm->n_threads; + for (i = 0; i < num_threads; i++) { - QUIC_DBG (1, "Quic does not support crypto engine %d", crypto_engine); - return VNET_API_ERROR_MISSING_CERT_KEY; + /* *INDENT-OFF* */ + pool_foreach (crctx, qm->wrk_ctx[i].crypto_ctx_pool, ({ + vlib_cli_output (vm, "[%d][Q]%U", i, format_crypto_context, crctx); + })); + /* *INDENT-ON* */ } + return 0; +} + +static void +quic_release_crypto_context (u32 crypto_context_index, u8 thread_index) +{ + crypto_context_t *crctx; + crctx = quic_crypto_context_get (crypto_context_index, thread_index); + crctx->n_subscribers--; + quic_crypto_context_free_if_needed (crctx, thread_index); +} + +static int +quic_init_crypto_context (crypto_context_t * crctx, quic_ctx_t * ctx) +{ + quic_main_t *qm = &quic_main; + quicly_context_t *quicly_ctx; + ptls_iovec_t key_vec; + app_cert_key_pair_t *ckpair; + application_t *app; + quic_crypto_context_data_t *data; + ptls_context_t *ptls_ctx; + + QUIC_DBG (2, "Init quic crctx %d thread %d", crctx->ctx_index, + ctx->c_thread_index); + + data = clib_mem_alloc (sizeof (*data)); + /* picotls depends on data being zeroed */ + clib_memset (data, 0, sizeof (*data)); + crctx->data = (void *) data; + quicly_ctx = &data->quicly_ctx; + ptls_ctx = &data->ptls_ctx; - quicly_ctx_data_t *quicly_ctx_data = - clib_mem_alloc (sizeof (quicly_ctx_data_t)); - clib_memset (quicly_ctx_data, 0, sizeof (*quicly_ctx_data)); /* picotls depends on this */ - quicly_ctx = &quicly_ctx_data->quicly_ctx; - ptls_context_t *ptls_ctx = &quicly_ctx_data->ptls_ctx; ptls_ctx->random_bytes = ptls_openssl_random_bytes; ptls_ctx->get_time = &ptls_get_time; ptls_ctx->key_exchanges = ptls_openssl_key_exchanges; - ptls_ctx->cipher_suites = qm->quic_ciphers[crypto_engine]; + ptls_ctx->cipher_suites = qm->quic_ciphers[ctx->crypto_engine]; ptls_ctx->certificates.list = NULL; ptls_ctx->certificates.count = 0; ptls_ctx->esni = NULL; @@ -92,8 +200,6 @@ quic_store_quicly_ctx (application_t * app, u32 ckpair_index, ptls_ctx->hkdf_label_prefix__obsolete = NULL; ptls_ctx->require_dhe_on_psk = 1; ptls_ctx->encrypt_ticket = &qm->session_cache.super; - - app->quicly_ctx = (u64 *) quicly_ctx; clib_memcpy (quicly_ctx, &quicly_spec_context, sizeof (quicly_context_t)); quicly_ctx->max_packet_size = QUIC_MAX_PACKET_SIZE; @@ -108,42 +214,96 @@ quic_store_quicly_ctx (application_t * app, u32 ckpair_index, quicly_ctx->transport_params.max_streams_bidi = (uint64_t) 1 << 60; quicly_ctx->transport_params.idle_timeout = qm->connection_timeout; - /* max_enq is FIFO_SIZE - 1 */ - max_enq = app->sm_properties.rx_fifo_size - 1; - quicly_ctx->transport_params.max_stream_data.bidi_local = max_enq; - max_enq = app->sm_properties.tx_fifo_size - 1; - quicly_ctx->transport_params.max_stream_data.bidi_remote = max_enq; + app = application_get (ctx->parent_app_id); + quicly_ctx->transport_params.max_stream_data.bidi_local = + app->sm_properties.rx_fifo_size - 1; + quicly_ctx->transport_params.max_stream_data.bidi_remote = + app->sm_properties.tx_fifo_size - 1; quicly_ctx->transport_params.max_stream_data.uni = QUIC_INT_MAX; - quicly_ctx->tls->random_bytes (quicly_ctx_data->cid_key, 16); - quicly_ctx_data->cid_key[16] = 0; - key_vec = ptls_iovec_init (quicly_ctx_data->cid_key, - strlen (quicly_ctx_data->cid_key)); + if (!app->quic_iv_set) + { + ptls_openssl_random_bytes (app->quic_iv, QUIC_IV_LEN - 1); + app->quic_iv[QUIC_IV_LEN - 1] = 0; + app->quic_iv_set = 1; + } + + clib_memcpy (data->cid_key, app->quic_iv, QUIC_IV_LEN); + key_vec = ptls_iovec_init (data->cid_key, QUIC_IV_LEN); quicly_ctx->cid_encryptor = quicly_new_default_cid_encryptor (&ptls_openssl_bfecb, &ptls_openssl_aes128ecb, &ptls_openssl_sha256, key_vec); - ckpair = app_cert_key_pair_get_if_valid (ckpair_index); + ckpair = app_cert_key_pair_get_if_valid (crctx->ckpair_index); if (!ckpair || !ckpair->key || !ckpair->cert) { - QUIC_DBG (1, "Wrong ckpair id %d\n", ckpair_index); - goto error; + QUIC_DBG (1, "Wrong ckpair id %d\n", crctx->ckpair_index); + return -1; } if (load_bio_private_key (quicly_ctx->tls, (char *) ckpair->key)) { QUIC_DBG (1, "failed to read private key from app configuration\n"); - goto error; + return -1; } if (load_bio_certificate_chain (quicly_ctx->tls, (char *) ckpair->cert)) { QUIC_DBG (1, "failed to load certificate\n"); - goto error; + return -1; } return 0; +} + +static int +quic_acquire_crypto_context (quic_ctx_t * ctx) +{ + quic_main_t *qm = &quic_main; + crypto_context_t *crctx; + clib_bihash_kv_24_8_t kv; + + if (ctx->crypto_engine == CRYPTO_ENGINE_NONE) + { + QUIC_DBG (2, "No crypto engine specified, using %d", + qm->default_crypto_engine); + ctx->crypto_engine = qm->default_crypto_engine; + } + if (!clib_bitmap_get (qm->available_crypto_engines, ctx->crypto_engine)) + { + QUIC_DBG (1, "Quic does not support crypto engine %d", + ctx->crypto_engine); + return VNET_API_ERROR_MISSING_CERT_KEY; + } + + /* Check for exisiting crypto ctx */ + quic_crypto_context_make_key_from_ctx (&kv, ctx); + if (clib_bihash_search_24_8 + (&qm->wrk_ctx[ctx->c_thread_index].crypto_context_hash, &kv, &kv) == 0) + { + crctx = quic_crypto_context_get (kv.value, ctx->c_thread_index); + QUIC_DBG (2, "Found exisiting crypto context %d", kv.value); + ctx->crypto_context_index = kv.value; + crctx->n_subscribers++; + return 0; + } + + crctx = quic_crypto_context_alloc (ctx->c_thread_index); + ctx->crypto_context_index = crctx->ctx_index; + kv.value = crctx->ctx_index; + crctx->crypto_engine = ctx->crypto_engine; + crctx->ckpair_index = ctx->ckpair_index; + if (quic_init_crypto_context (crctx, ctx)) + goto error; + if (vnet_app_add_cert_key_interest (ctx->ckpair_index, qm->app_index)) + goto error; + crctx->n_subscribers++; + clib_bihash_add_del_24_8 (&qm-> + wrk_ctx[ctx->c_thread_index].crypto_context_hash, + &kv, 1 /* is_add */ ); + return 0; + error: - clib_mem_free (quicly_ctx_data); + quic_crypto_context_free_if_needed (crctx, ctx->c_thread_index); return VNET_API_ERROR_MISSING_CERT_KEY; } @@ -254,7 +414,11 @@ quic_sendable_packet_count (session_t * udp_session) static quicly_context_t * quic_get_quicly_ctx_from_ctx (quic_ctx_t * ctx) { - return ctx->quicly_ctx; + crypto_context_t *crctx = + quic_crypto_context_get (ctx->crypto_context_index, ctx->c_thread_index); + quic_crypto_context_data_t *data = + (quic_crypto_context_data_t *) crctx->data; + return &data->quicly_ctx; } static quicly_context_t * @@ -263,7 +427,7 @@ quic_get_quicly_ctx_from_udp (u64 udp_session_handle) session_t *udp_session = session_get_from_handle (udp_session_handle); quic_ctx_t *ctx = quic_ctx_get (udp_session->opaque, udp_session->thread_index); - return ctx->quicly_ctx; + return quic_get_quicly_ctx_from_ctx (ctx); } static inline void @@ -474,7 +638,6 @@ quic_send_packets (quic_ctx_t * ctx) quicly_conn_t *conn; size_t num_packets, i, max_packets; quicly_packet_allocator_t *pa; - quicly_context_t *quicly_context; int err = 0; /* We have sctx, get qctx */ @@ -496,15 +659,7 @@ quic_send_packets (quic_ctx_t * ctx) if (quic_sendable_packet_count (udp_session) < 2) goto stop_sending; - quicly_context = quic_get_quicly_ctx_from_ctx (ctx); - if (!quicly_context) - { - clib_warning ("Tried to send packets on non existing app worker %u", - ctx->parent_app_wrk_id); - quic_connection_delete (ctx); - return 1; - } - pa = quicly_context->packet_allocator; + pa = quic_get_quicly_ctx_from_ctx (ctx)->packet_allocator; do { max_packets = quic_sendable_packet_count (udp_session); @@ -1118,12 +1273,10 @@ quic_connect_connection (session_endpoint_cfg_t * sep) ctx->parent_app_id = app_wrk->app_index; cargs->sep_ext.ns_index = app->ns_index; - if ((error = - quic_store_quicly_ctx (app, sep->ckpair_index, sep->crypto_engine))) + ctx->crypto_engine = sep->crypto_engine; + ctx->ckpair_index = sep->ckpair_index; + if ((error = quic_acquire_crypto_context (ctx))) return error; - /* Also store it in ctx for convenience - * Waiting for crypto_ctx logic */ - ctx->quicly_ctx = (quicly_context_t *) app->quicly_ctx; if ((error = vnet_connect (cargs))) return error; @@ -1222,9 +1375,6 @@ quic_start_listen (u32 quic_listen_session_index, transport_endpoint_t * tep) app = application_get (app_wrk->app_index); QUIC_DBG (2, "Called quic_start_listen for app %d", app_wrk->app_index); - if (quic_store_quicly_ctx (app, sep->ckpair_index, sep->crypto_engine)) - return -1; - sep->transport_proto = TRANSPORT_PROTO_UDPC; clib_memset (args, 0, sizeof (*args)); args->app_index = qm->app_index; @@ -1241,9 +1391,6 @@ quic_start_listen (u32 quic_listen_session_index, transport_endpoint_t * tep) lctx = quic_ctx_get (lctx_index, 0); lctx->flags |= QUIC_F_IS_LISTENER; - /* Also store it in ctx for convenience - * Waiting for crypto_ctx logic */ - lctx->quicly_ctx = (quicly_context_t *) app->quicly_ctx; clib_memcpy (&lctx->c_rmt_ip, &args->sep.peer.ip, sizeof (ip46_address_t)); clib_memcpy (&lctx->c_lcl_ip, &args->sep.ip, sizeof (ip46_address_t)); @@ -1256,6 +1403,10 @@ quic_start_listen (u32 quic_listen_session_index, transport_endpoint_t * tep) lctx->parent_app_id = app_wrk->app_index; lctx->udp_session_handle = udp_handle; lctx->c_s_index = quic_listen_session_index; + lctx->crypto_engine = sep->crypto_engine; + lctx->ckpair_index = sep->ckpair_index; + if (quic_acquire_crypto_context (lctx)) + return -1; QUIC_DBG (2, "Listening UDP session 0x%lx", session_handle (udp_listen_session)); @@ -1278,8 +1429,8 @@ quic_stop_listen (u32 lctx_index) if (vnet_unlisten (&a)) clib_warning ("unlisten errored"); - /* TODO: crypto state cleanup */ - + quic_release_crypto_context (lctx->crypto_context_index, + 0 /* thread_index */ ); quic_ctx_free (lctx); return 0; } @@ -1477,6 +1628,7 @@ quic_receive_connection (void *arg) new_ctx->c_thread_index = thread_index; new_ctx->c_c_index = new_ctx_id; + quic_acquire_crypto_context (new_ctx); conn = new_ctx->conn; quic_store_conn_ctx (conn, new_ctx); @@ -1510,6 +1662,7 @@ quic_transfer_connection (u32 ctx_index, u32 dest_thread) clib_memcpy (temp_ctx, ctx, sizeof (quic_ctx_t)); quic_stop_ctx_timer (ctx); + quic_release_crypto_context (ctx->crypto_context_index, thread_index); quic_ctx_free (ctx); /* Send connection to destination thread */ @@ -1608,6 +1761,8 @@ quic_udp_session_cleanup_callback (session_t * udp_session, ctx = quic_ctx_get (udp_session->opaque, udp_session->thread_index); quic_stop_ctx_timer (ctx); + quic_release_crypto_context (ctx->crypto_context_index, + ctx->c_thread_index); quic_ctx_free (ctx); } @@ -1664,10 +1819,9 @@ quic_udp_session_accepted_callback (session_t * udp_session) ctx->conn_state = QUIC_CONN_STATE_OPENED; ctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP; - /* Also store it in ctx for convenience - * Waiting for crypto_ctx logic */ - ctx->quicly_ctx = lctx->quicly_ctx; - + ctx->crypto_engine = lctx->crypto_engine; + ctx->ckpair_index = lctx->ckpair_index; + quic_acquire_crypto_context (ctx); udp_session->opaque = ctx_index; /* TODO timeout to delete these if they never connect */ @@ -2143,6 +2297,7 @@ static session_cb_vft_t quic_app_cb_vft = { .del_segment_callback = quic_del_segment_callback, .builtin_app_rx_callback = quic_udp_session_rx_callback, .session_cleanup_callback = quic_udp_session_cleanup_callback, + .app_cert_key_pair_delete_callback = quic_app_cert_key_pair_delete_callback, }; static const transport_proto_vft_t quic_proto = { @@ -2236,7 +2391,6 @@ quic_init (vlib_main_t * vm) vec_validate (qm->ctx_pool, num_threads - 1); vec_validate (qm->wrk_ctx, num_threads - 1); - /* Timer wheels, one per thread. */ for (i = 0; i < num_threads; i++) { qm->wrk_ctx[i].next_cid.thread_id = i; @@ -2244,12 +2398,13 @@ quic_init (vlib_main_t * vm) tw_timer_wheel_init_1t_3w_1024sl_ov (tw, quic_expired_timers_dispatch, 1e-3 /* timer period 1ms */ , ~0); tw->last_run_time = vlib_time_now (vlib_get_main ()); + clib_bihash_init_24_8 (&qm->wrk_ctx[i].crypto_context_hash, + "quic crypto contexts", 64, 128 << 10); } clib_bihash_init_16_8 (&qm->connection_hash, "quic connections", 1024, 4 << 20); - qm->app_index = a->app_index; qm->tstamp_ticks_per_clock = vm->clib_time.seconds_per_clock / QUIC_TSTAMP_RESOLUTION; @@ -2578,6 +2733,12 @@ VLIB_CLI_COMMAND(quic_show_ctx_command, static)= .short_help = "show quic", .function = quic_show_connections_command_fn, }; +VLIB_CLI_COMMAND (quic_list_crypto_context_command, static) = +{ + .path = "show quic crypto context", + .short_help = "list quic crypto contextes", + .function = quic_list_crypto_context_command_fn, +}; VLIB_PLUGIN_REGISTER () = { .version = VPP_BUILD_VER, diff --git a/src/plugins/quic/quic.h b/src/plugins/quic/quic.h index dcdb6cf8e24..5921f3aad8c 100644 --- a/src/plugins/quic/quic.h +++ b/src/plugins/quic/quic.h @@ -40,6 +40,7 @@ #define QUIC_INT_MAX 0x3FFFFFFFFFFFFFFF #define QUIC_DEFAULT_FIFO_SIZE (64 << 10) #define QUIC_SEND_PACKET_VEC_SIZE 16 +#define QUIC_IV_LEN 17 #define QUIC_SEND_MAX_BATCH_PACKETS 16 #define QUIC_RCV_MAX_BATCH_PACKETS 16 @@ -152,7 +153,8 @@ typedef struct quic_ctx_ u32 parent_app_wrk_id; u32 parent_app_id; u32 ckpair_index; - quicly_context_t *quicly_ctx; + u32 crypto_engine; + u32 crypto_context_index; u8 flags; } quic_ctx_t; @@ -182,12 +184,21 @@ typedef struct quic_stream_data_ u32 app_tx_data_len; /**< bytes sent */ } quic_stream_data_t; +typedef struct quic_crypto_context_data_ +{ + quicly_context_t quicly_ctx; + char cid_key[QUIC_IV_LEN]; + ptls_context_t ptls_ctx; +} quic_crypto_context_data_t; + typedef struct quic_worker_ctx_ { CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); int64_t time_now; /**< worker time */ tw_timer_wheel_1t_3w_1024sl_ov_t timer_wheel; /**< worker timer wheel */ quicly_cid_plaintext_t next_cid; + crypto_context_t *crypto_ctx_pool; /**< per thread pool of crypto contexes */ + clib_bihash_24_8_t crypto_context_hash; /**< per thread [params:crypto_ctx_index] hash */ } quic_worker_ctx_t; typedef struct quic_rx_packet_ctx_ @@ -206,13 +217,6 @@ typedef struct quic_rx_packet_ctx_ session_dgram_hdr_t ph; } quic_rx_packet_ctx_t; -typedef struct quicly_ctx_data_ -{ - quicly_context_t quicly_ctx; - char cid_key[17]; - ptls_context_t ptls_ctx; -} quicly_ctx_data_t; - typedef struct quic_main_ { u32 app_index; diff --git a/src/vnet/session/application.h b/src/vnet/session/application.h index e53c8ed8598..9943576377d 100644 --- a/src/vnet/session/application.h +++ b/src/vnet/session/application.h @@ -114,7 +114,6 @@ typedef struct application_ /** Preferred tls engine */ u8 tls_engine; - u64 *quicly_ctx; /** quic initialization vector */ char quic_iv[17]; u8 quic_iv_set; diff --git a/src/vnet/session/application_interface.h b/src/vnet/session/application_interface.h index ec29d18b59b..a40aa9d9a7f 100644 --- a/src/vnet/session/application_interface.h +++ b/src/vnet/session/application_interface.h @@ -182,6 +182,7 @@ typedef struct crypto_ctx_ u32 n_subscribers; /**< refcount of sessions using said context */ u32 ckpair_index; /**< certificate & key */ u8 crypto_engine; + void *data; /**< protocol specific data */ } crypto_context_t; /* Application attach options */ |