tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

commit 2cd574bb133850633bf22f4b97941d44bef48a0c
parent 1cfbff0eede113241500c4eddf638953a9d2716f
Author: David Goulet <dgoulet@torproject.org>
Date:   Thu, 12 Jun 2025 12:54:04 -0400

Merge branch 'tor-gitlab/mr/905'

Diffstat:
Achanges/cgo | 8++++++++
Msrc/core/crypto/include.am | 8++++++--
Msrc/core/crypto/onion_crypto.c | 263++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Msrc/core/crypto/onion_crypto.h | 16++++++++++++++--
Msrc/core/crypto/relay_crypto.c | 449++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msrc/core/crypto/relay_crypto.h | 44+++++++++++++++++++++++++++++++-------------
Msrc/core/crypto/relay_crypto_cgo.c | 53++++++++++++++++++++++++++++-------------------------
Msrc/core/crypto/relay_crypto_cgo.h | 13+++----------
Asrc/core/crypto/relay_crypto_st.h | 40++++++++++++++++++++++++++++++++++++++++
Asrc/core/crypto/relay_crypto_tor1.c | 313+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/core/crypto/relay_crypto_tor1.h | 33+++++++++++++++++++++++++++++++++
Asrc/core/crypto/tor1_crypt_st.h | 39+++++++++++++++++++++++++++++++++++++++
Msrc/core/mainloop/cpuworker.c | 14+++++++++-----
Msrc/core/or/circuit_st.h | 12+++++++++++-
Msrc/core/or/circuitbuild.c | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/core/or/circuitbuild.h | 4+++-
Msrc/core/or/command.c | 6++++--
Msrc/core/or/congestion_control_common.c | 110++++++++++++-------------------------------------------------------------------
Msrc/core/or/congestion_control_common.h | 11+++++------
Msrc/core/or/crypt_path.c | 62++++++++++----------------------------------------------------
Msrc/core/or/crypt_path.h | 22++++++----------------
Msrc/core/or/crypt_path_st.h | 11+++++------
Msrc/core/or/extend_info_st.h | 7+++++++
Msrc/core/or/extendinfo.c | 8++++++--
Msrc/core/or/include.am | 1-
Msrc/core/or/or.h | 13++++++++++++-
Msrc/core/or/protover.c | 2+-
Msrc/core/or/protover.h | 8++++++++
Msrc/core/or/relay.c | 4----
Dsrc/core/or/relay_crypto_st.h | 38--------------------------------------
Msrc/core/or/sendme.c | 212+++++++++++++++++++++++++++++++++++--------------------------------------------
Msrc/core/or/sendme.h | 9+++------
Msrc/core/or/versions.c | 13+++++++++++++
Msrc/feature/hs/hs_circuit.c | 5+++--
Msrc/feature/nodelist/nodelist.c | 2+-
Msrc/feature/relay/circuitbuild_relay.c | 9+++------
Msrc/feature/relay/circuitbuild_relay.h | 4++++
Msrc/test/bench.c | 12+++++-------
Msrc/test/fakecircs.c | 7++++---
Msrc/test/test_circuitbuild.c | 5+++++
Msrc/test/test_circuitpadding.c | 3++-
Msrc/test/test_conflux_pool.c | 3++-
Msrc/test/test_crypto_cgo.c | 43++++++++++++++++++++++++-------------------
Msrc/test/test_hs_client.c | 10++++++----
Msrc/test/test_hs_service.c | 10++++++----
Msrc/test/test_ntor_v3.c | 7+++++--
Msrc/test/test_relaycrypt.c | 11++++++-----
Msrc/test/test_sendme.c | 64+++++++++++-----------------------------------------------------
Msrc/trunnel/include.am | 9++++++---
Msrc/trunnel/sendme_cell.c | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/trunnel/sendme_cell.h | 25+++++++++++++++++--------
Msrc/trunnel/sendme_cell.trunnel | 10+++++-----
Asrc/trunnel/subproto_request.c | 456+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/trunnel/subproto_request.h | 157+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/trunnel/subproto_request.trunnel | 9+++++++++
55 files changed, 2122 insertions(+), 808 deletions(-)

diff --git a/changes/cgo b/changes/cgo @@ -0,0 +1,8 @@ + o Major features (cryptography): + - Clients and relays can now negotiate Counter Galois Onion (CGO) + relay cryptography, as designed by Jean Paul Degabriele, Alessandro + Melloni, Jean-Pierre Münch, and Martijn Stam. + CGO provides improved resistance to several kinds + of tagging attacks, better forward secrecy, and better + forgery resistance. Closes ticket 41047. + Implements proposal 359. diff --git a/src/core/crypto/include.am b/src/core/crypto/include.am @@ -7,7 +7,8 @@ LIBTOR_APP_A_SOURCES += \ src/core/crypto/onion_ntor.c \ src/core/crypto/onion_ntor_v3.c \ src/core/crypto/relay_crypto.c \ - src/core/crypto/relay_crypto_cgo.c + src/core/crypto/relay_crypto_cgo.c \ + src/core/crypto/relay_crypto_tor1.c # ADD_C_FILE: INSERT HEADERS HERE. noinst_HEADERS += \ @@ -17,4 +18,7 @@ noinst_HEADERS += \ src/core/crypto/onion_ntor.h \ src/core/crypto/onion_ntor_v3.h \ src/core/crypto/relay_crypto.h \ - src/core/crypto/relay_crypto_cgo.h + src/core/crypto/relay_crypto_st.h \ + src/core/crypto/relay_crypto_cgo.h \ + src/core/crypto/relay_crypto_tor1.h \ + src/core/crypto/tor1_crypt_st.h diff --git a/src/core/crypto/onion_crypto.c b/src/core/crypto/onion_crypto.c @@ -41,6 +41,8 @@ #include "lib/crypt_ops/crypto_util.h" #include "feature/relay/routerkeys.h" #include "core/or/congestion_control_common.h" +#include "core/crypto/relay_crypto.h" +#include "core/or/protover.h" #include "core/or/circuitbuild.h" @@ -49,6 +51,9 @@ #include "trunnel/congestion_control.h" #include "trunnel/extension.h" +#include "trunnel/subproto_request.h" + +#define EXT_TYPE_SUBPROTO 3 static const uint8_t NTOR3_CIRC_VERIFICATION[] = "circuit extend"; static const size_t NTOR3_CIRC_VERIFICATION_LEN = 14; @@ -56,6 +61,18 @@ static const size_t NTOR3_CIRC_VERIFICATION_LEN = 14; #define NTOR3_VERIFICATION_ARGS \ NTOR3_CIRC_VERIFICATION, NTOR3_CIRC_VERIFICATION_LEN +/** Set `params` to a set of defaults. + * + * These defaults will only change later on if we're using a handshake that has + * parameter negotiation. */ +static void +circuit_params_init(circuit_params_t *params) +{ + memset(params, 0, sizeof(*params)); + params->crypto_alg = RELAY_CRYPTO_ALG_TOR1; + params->cell_fmt = RELAY_CELL_FORMAT_V0; +} + /** Return a new server_onion_keys_t object with all of the keys * and other info we might need to do onion handshakes. (We make a copy of * our keys for each cpuworker to avoid race conditions with the main thread, @@ -134,6 +151,8 @@ onion_skin_create(int type, { int r = -1; + circuit_params_init(&state_out->chosen_params); + switch (type) { case ONION_HANDSHAKE_TYPE_TAP: return -1; @@ -157,14 +176,21 @@ onion_skin_create(int type, r = NTOR_ONIONSKIN_LEN; break; case ONION_HANDSHAKE_TYPE_NTOR_V3: - if (!extend_info_supports_ntor_v3(node)) + if (!extend_info_supports_ntor_v3(node)) { + log_warn(LD_BUG, "Chose ntorv3 handshake, but no support at node"); return -1; - if (ed25519_public_key_is_zero(&node->ed_identity)) + } + if (ed25519_public_key_is_zero(&node->ed_identity)) { + log_warn(LD_BUG, "Chose ntorv3 handshake, but no ed id"); return -1; + } size_t msg_len = 0; uint8_t *msg = NULL; - if (client_circ_negotiation_message(node, &msg, &msg_len) < 0) + if (client_circ_negotiation_message(node, &msg, &msg_len, + &state_out->chosen_params) < 0) { + log_warn(LD_BUG, "Could not create circuit negotiation msg"); return -1; + } uint8_t *onion_skin = NULL; size_t onion_skin_len = 0; int status = onion_skin_ntor3_create( @@ -176,9 +202,10 @@ onion_skin_create(int type, &onion_skin, &onion_skin_len); tor_free(msg); if (status < 0) { + log_warn(LD_BUG, "onion skin create failed"); return -1; } - if (onion_skin_len > onion_skin_out_maxlen) { + IF_BUG_ONCE(onion_skin_len > onion_skin_out_maxlen) { tor_free(onion_skin); return -1; } @@ -202,6 +229,75 @@ onion_skin_create(int type, return r; } +static bool +subproto_requests_in_order(const trn_subproto_request_t *a, + const trn_subproto_request_t *b) +{ + if (a->protocol_id < b->protocol_id) { + return true; + } else if (a->protocol_id == b->protocol_id) { + return (a->proto_cap_number < b->proto_cap_number); + } else { + return false; + } +} + +/** + * Process the SUBPROTO extension, as an OR. + * + * This extension declares one or more subproto capabilities that the + * relay must implement, and tells it to enable them. + */ +static int +relay_process_subproto_ext(const trn_extension_t *ext, + circuit_params_t *params_out) +{ + const trn_extension_field_t *field; + trn_subproto_request_ext_t *req = NULL; + int res = -1; + + field = trn_extension_find(ext, EXT_TYPE_SUBPROTO); + if (!field) { + // Nothing to do. + res = 0; + goto done; + } + + const uint8_t *f = trn_extension_field_getconstarray_field(field); + size_t len = trn_extension_field_getlen_field(field); + + if (trn_subproto_request_ext_parse(&req, f, len) < 0) { + goto done; + } + + const trn_subproto_request_t *prev = NULL; + size_t n_requests = trn_subproto_request_ext_getlen_reqs(req); + for (unsigned i = 0; i < n_requests; ++i) { + const trn_subproto_request_t *cur = + trn_subproto_request_ext_getconst_reqs(req, i); + if (prev && !subproto_requests_in_order(prev, cur)) { + // The requests were not properly sorted and deduplicated. + goto done; + } + + if (cur->protocol_id == PRT_RELAY && + cur->proto_cap_number == PROTOVER_RELAY_CRYPT_CGO) { + params_out->crypto_alg = RELAY_CRYPTO_ALG_CGO_RELAY; + params_out->cell_fmt = RELAY_CELL_FORMAT_V1; + } else { + // Unless a protocol capability is explicitly supported for use + // with this extension, we _must_ reject when it appears. + goto done; + } + } + + res = 0; + + done: + trn_subproto_request_ext_free(req); + return res; +} + /** * Takes a param request message from the client, compares it to our * consensus parameters, and creates a reply message and output @@ -221,15 +317,25 @@ negotiate_v3_ntor_server_circ_params(const uint8_t *param_request_msg, uint8_t **resp_msg_out, size_t *resp_msg_len_out) { - int ret; + int ret = -1; + trn_extension_t *ext = NULL; + + ssize_t len = + trn_extension_parse(&ext, param_request_msg, param_request_len); + if (len < 0) { + goto err; + } /* Parse request. */ - ret = congestion_control_parse_ext_request(param_request_msg, - param_request_len); + ret = congestion_control_parse_ext_request(ext); if (ret < 0) { goto err; } params_out->cc_enabled = ret && our_ns_params->cc_enabled; + ret = relay_process_subproto_ext(ext, params_out); + if (ret < 0) { + goto err; + } /* Build the response. */ ret = congestion_control_build_ext_response(our_ns_params, params_out, @@ -239,17 +345,26 @@ negotiate_v3_ntor_server_circ_params(const uint8_t *param_request_msg, } params_out->sendme_inc_cells = our_ns_params->sendme_inc_cells; + if (params_out->cell_fmt != RELAY_CELL_FORMAT_V0 && + !params_out->cc_enabled) { + // The V1 cell format is incompatible with pre-CC circuits, + // since it has no way to encode stream-level SENDME messages. + goto err; + } + /* Success. */ ret = 0; err: + trn_extension_free(ext); return ret; } /* This is the maximum value for keys_out_len passed to - * onion_skin_server_handshake, plus 16. We can make it bigger if needed: + * onion_skin_server_handshake, plus 20 for the rend_nonce. + * We can make it bigger if needed: * It just defines how many bytes to stack-allocate. */ -#define MAX_KEYS_TMP_LEN 128 +#define MAX_KEYS_TMP_LEN (MAX_RELAY_KEY_MATERIAL_LEN + DIGEST_LEN) /** Perform the second (server-side) step of a circuit-creation handshake of * type <b>type</b>, responding to the client request in <b>onion_skin</b> @@ -257,6 +372,9 @@ negotiate_v3_ntor_server_circ_params(const uint8_t *param_request_msg, * <b>reply_out</b>, generate <b>keys_out_len</b> bytes worth of key material * in <b>keys_out_len</b>, a hidden service nonce to <b>rend_nonce_out</b>, * and return the length of the reply. On failure, return -1. + * + * Requires that *keys_len_out of bytes are allocated at keys_out; + * adjusts *keys_out_len to the number of bytes actually genarated. */ int onion_skin_server_handshake(int type, @@ -265,12 +383,16 @@ onion_skin_server_handshake(int type, const circuit_params_t *our_ns_params, uint8_t *reply_out, size_t reply_out_maxlen, - uint8_t *keys_out, size_t keys_out_len, + uint8_t *keys_out, size_t *keys_len_out, uint8_t *rend_nonce_out, circuit_params_t *params_out) { int r = -1; - memset(params_out, 0, sizeof(*params_out)); + + relay_crypto_alg_t relay_alg = RELAY_CRYPTO_ALG_TOR1; + size_t keys_out_needed = relay_crypto_key_material_len(relay_alg); + + circuit_params_init(params_out); switch (type) { case ONION_HANDSHAKE_TYPE_TAP: @@ -280,7 +402,11 @@ onion_skin_server_handshake(int type, return -1; if (onionskin_len != CREATE_FAST_LEN) return -1; - if (fast_server_handshake(onion_skin, reply_out, keys_out, keys_out_len)<0) + if (BUG(*keys_len_out < keys_out_needed)) { + return -1; + } + if (fast_server_handshake(onion_skin, reply_out, keys_out, + keys_out_needed)<0) return -1; r = CREATED_FAST_LEN; memcpy(rend_nonce_out, reply_out+DIGEST_LEN, DIGEST_LEN); @@ -290,8 +416,11 @@ onion_skin_server_handshake(int type, return -1; if (onionskin_len < NTOR_ONIONSKIN_LEN) return -1; + if (BUG(*keys_len_out < keys_out_needed)) { + return -1; + } { - size_t keys_tmp_len = keys_out_len + DIGEST_LEN; + size_t keys_tmp_len = keys_out_needed + DIGEST_LEN; tor_assert(keys_tmp_len <= MAX_KEYS_TMP_LEN); uint8_t keys_tmp[MAX_KEYS_TMP_LEN]; @@ -304,15 +433,13 @@ onion_skin_server_handshake(int type, return -1; } - memcpy(keys_out, keys_tmp, keys_out_len); - memcpy(rend_nonce_out, keys_tmp+keys_out_len, DIGEST_LEN); + memcpy(keys_out, keys_tmp, keys_out_needed); + memcpy(rend_nonce_out, keys_tmp+keys_out_needed, DIGEST_LEN); memwipe(keys_tmp, 0, sizeof(keys_tmp)); r = NTOR_REPLY_LEN; } break; case ONION_HANDSHAKE_TYPE_NTOR_V3: { - size_t keys_tmp_len = keys_out_len + DIGEST_LEN; - tor_assert(keys_tmp_len <= MAX_KEYS_TMP_LEN); uint8_t keys_tmp[MAX_KEYS_TMP_LEN]; uint8_t *client_msg = NULL; size_t client_msg_len = 0; @@ -343,6 +470,16 @@ onion_skin_server_handshake(int type, return -1; } tor_free(client_msg); + /* Now we know what we negotiated, + so we can use the right lengths. */ + relay_alg = params_out->crypto_alg; + keys_out_needed = relay_crypto_key_material_len(relay_alg); + + if (BUG(*keys_len_out < keys_out_needed)) { + return -1; + } + size_t keys_tmp_len = keys_out_needed + DIGEST_LEN; + tor_assert(keys_tmp_len <= MAX_KEYS_TMP_LEN); uint8_t *server_handshake = NULL; size_t server_handshake_len = 0; @@ -364,8 +501,8 @@ onion_skin_server_handshake(int type, return -1; } - memcpy(keys_out, keys_tmp, keys_out_len); - memcpy(rend_nonce_out, keys_tmp+keys_out_len, DIGEST_LEN); + memcpy(keys_out, keys_tmp, keys_out_needed); + memcpy(rend_nonce_out, keys_tmp+keys_out_needed, DIGEST_LEN); memcpy(reply_out, server_handshake, server_handshake_len); memwipe(keys_tmp, 0, keys_tmp_len); memwipe(server_handshake, 0, server_handshake_len); @@ -383,6 +520,7 @@ onion_skin_server_handshake(int type, return -1; /* LCOV_EXCL_STOP */ } + *keys_len_out = keys_out_needed; return r; } @@ -399,11 +537,19 @@ negotiate_v3_ntor_client_circ_params(const uint8_t *param_response_msg, size_t param_response_len, circuit_params_t *params_out) { - int ret = congestion_control_parse_ext_response(param_response_msg, - param_response_len, - params_out); + int ret = -1; + trn_extension_t *ext = NULL; + + ssize_t len = + trn_extension_parse(&ext, param_response_msg, param_response_len); + if (len < 0) { + goto err; + } + + ret = congestion_control_parse_ext_response(ext, + params_out); if (ret < 0) { - return -1; + goto err; } /* If congestion control came back enabled, but we didn't ask for it @@ -418,26 +564,33 @@ negotiate_v3_ntor_client_circ_params(const uint8_t *param_response_msg, * new one. */ if (ret && !congestion_control_enabled()) { - return -1; + goto err; } params_out->cc_enabled = ret; - return 0; + err: + trn_extension_free(ext); + return ret; } /** Perform the final (client-side) step of a circuit-creation handshake of * type <b>type</b>, using our state in <b>handshake_state</b> and the - * server's response in <b>reply</b>. On success, generate <b>keys_out_len</b> - * bytes worth of key material in <b>keys_out_len</b>, set + * server's response in <b>reply</b>. On success, generate an appropriate + * amount of key material in <b>keys_out</b>, + * set <b>keys_out_len</b> to the amount generated, set * <b>rend_authenticator_out</b> to the "KH" field that can be used to * establish introduction points at this hop, and return 0. On failure, * return -1, and set *msg_out to an error message if this is worth - * complaining to the user about. */ + * complaining to the user about. + * + * Requires that *keys_len_out of bytes are allocated at keys_out; + * adjusts *keys_out_len to the number of bytes actually genarated. + */ int onion_skin_client_handshake(int type, const onion_handshake_state_t *handshake_state, const uint8_t *reply, size_t reply_len, - uint8_t *keys_out, size_t keys_out_len, + uint8_t *keys_out, size_t *keys_len_out, uint8_t *rend_authenticator_out, circuit_params_t *params_out, const char **msg_out) @@ -445,7 +598,16 @@ onion_skin_client_handshake(int type, if (handshake_state->tag != type) return -1; - memset(params_out, 0, sizeof(*params_out)); + memcpy(params_out, &handshake_state->chosen_params, + sizeof(circuit_params_t)); + + // at this point, we know the crypto algorithm we want to use + relay_crypto_alg_t relay_alg = params_out->crypto_alg; + size_t keys_out_needed = relay_crypto_key_material_len(relay_alg); + if (BUG(*keys_len_out < keys_out_needed)) { + return -1; + } + *keys_len_out = keys_out_needed; switch (type) { case ONION_HANDSHAKE_TYPE_TAP: @@ -457,7 +619,7 @@ onion_skin_client_handshake(int type, return -1; } if (fast_client_handshake(handshake_state->u.fast, reply, - keys_out, keys_out_len, msg_out) < 0) + keys_out, keys_out_needed, msg_out) < 0) return -1; memcpy(rend_authenticator_out, reply+DIGEST_LEN, DIGEST_LEN); @@ -469,7 +631,7 @@ onion_skin_client_handshake(int type, return -1; } { - size_t keys_tmp_len = keys_out_len + DIGEST_LEN; + size_t keys_tmp_len = keys_out_needed + DIGEST_LEN; uint8_t *keys_tmp = tor_malloc(keys_tmp_len); if (onion_skin_ntor_client_handshake(handshake_state->u.ntor, reply, @@ -477,14 +639,14 @@ onion_skin_client_handshake(int type, tor_free(keys_tmp); return -1; } - memcpy(keys_out, keys_tmp, keys_out_len); - memcpy(rend_authenticator_out, keys_tmp + keys_out_len, DIGEST_LEN); + memcpy(keys_out, keys_tmp, keys_out_needed); + memcpy(rend_authenticator_out, keys_tmp + keys_out_needed, DIGEST_LEN); memwipe(keys_tmp, 0, keys_tmp_len); tor_free(keys_tmp); } return 0; case ONION_HANDSHAKE_TYPE_NTOR_V3: { - size_t keys_tmp_len = keys_out_len + DIGEST_LEN; + size_t keys_tmp_len = keys_out_needed + DIGEST_LEN; uint8_t *keys_tmp = tor_malloc(keys_tmp_len); uint8_t *server_msg = NULL; size_t server_msg_len = 0; @@ -509,8 +671,8 @@ onion_skin_client_handshake(int type, } tor_free(server_msg); - memcpy(keys_out, keys_tmp, keys_out_len); - memcpy(rend_authenticator_out, keys_tmp + keys_out_len, DIGEST_LEN); + memcpy(keys_out, keys_tmp, keys_out_needed); + memcpy(rend_authenticator_out, keys_tmp + keys_out_needed, DIGEST_LEN); memwipe(keys_tmp, 0, keys_tmp_len); tor_free(keys_tmp); @@ -522,3 +684,30 @@ onion_skin_client_handshake(int type, return -1; } } + +/** + * If there is an extension field of type `ext_type` in `ext`, + * return that field. Otherwise return NULL. + */ +const trn_extension_field_t * +trn_extension_find(const trn_extension_t *ext, uint8_t ext_type) +{ + IF_BUG_ONCE(!ext) { + return NULL; + } + size_t n_fields = trn_extension_get_num(ext); + if (n_fields == 0) + return NULL; + + for (unsigned i = 0; i < n_fields; ++i) { + const trn_extension_field_t *field = trn_extension_getconst_fields(ext, i); + IF_BUG_ONCE(field == NULL) { + return NULL; + } + if (trn_extension_field_get_field_type(field) == ext_type) { + return field; + } + } + + return NULL; +} diff --git a/src/core/crypto/onion_crypto.h b/src/core/crypto/onion_crypto.h @@ -13,6 +13,7 @@ #define TOR_ONION_CRYPTO_H #include "lib/crypt_ops/crypto_ed25519.h" +#include "core/crypto/relay_crypto.h" typedef struct server_onion_keys_t { uint8_t my_identity[DIGEST_LEN]; @@ -34,6 +35,11 @@ typedef struct circuit_params_t { bool cc_enabled; /** The number of cells in a sendme increment. Only used if cc_enabled=1. */ uint8_t sendme_inc_cells; + + /** Which algorithm did we negotiate? */ + relay_crypto_alg_t crypto_alg; + /** Which cell format did we negotiate? */ + relay_cell_fmt_t cell_fmt; } circuit_params_t; int onion_skin_create(int type, @@ -47,13 +53,13 @@ int onion_skin_server_handshake(int type, const circuit_params_t *ns_params, uint8_t *reply_out, size_t reply_out_maxlen, - uint8_t *keys_out, size_t key_out_len, + uint8_t *keys_out, size_t *keys_len_out, uint8_t *rend_nonce_out, circuit_params_t *negotiated_params_out); int onion_skin_client_handshake(int type, const onion_handshake_state_t *handshake_state, const uint8_t *reply, size_t reply_len, - uint8_t *keys_out, size_t key_out_len, + uint8_t *keys_out, size_t *keys_out_len, uint8_t *rend_authenticator_out, circuit_params_t *negotiated_params_out, const char **msg_out); @@ -63,4 +69,10 @@ void server_onion_keys_free_(server_onion_keys_t *keys); #define server_onion_keys_free(keys) \ FREE_AND_NULL(server_onion_keys_t, server_onion_keys_free_, (keys)) +struct trn_extension_st; +struct trn_extension_field_st; +const struct trn_extension_field_st *trn_extension_find( + const struct trn_extension_st *ext, + uint8_t ext_type); + #endif /* !defined(TOR_ONION_CRYPTO_H) */ diff --git a/src/core/crypto/relay_crypto.c b/src/core/crypto/relay_crypto.c @@ -9,131 +9,125 @@ * @brief Header for relay_crypto.c **/ +// For access to cpath pvt_crypto field. +#define CRYPT_PATH_PRIVATE + #include "core/or/or.h" #include "core/or/circuitlist.h" #include "core/or/crypt_path.h" #include "app/config/config.h" #include "lib/crypt_ops/crypto_cipher.h" #include "lib/crypt_ops/crypto_util.h" -#include "core/crypto/hs_ntor.h" // for HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN -#include "core/or/relay.h" #include "core/crypto/relay_crypto.h" +#include "core/crypto/relay_crypto_tor1.h" #include "core/or/sendme.h" -#include "lib/cc/ctassert.h" -#include "core/or/cell_st.h" #include "core/or/or_circuit_st.h" #include "core/or/origin_circuit_st.h" -/* TODO CGO: This file will be largely incorrect when we have - * CGO crypto. */ - -/* Offset of digest within relay cell body for v0 cells. */ -#define V0_DIGEST_OFFSET 5 -#define V0_DIGEST_LEN 4 -#define V0_RECOGNIZED_OFFSET 1 - -/** Update digest{ from the payload of cell. Assign integrity part to - * cell. - */ -void -relay_set_digest_v0(crypto_digest_t *digest, cell_t *cell) -{ - char integrity[V0_DIGEST_LEN]; - - crypto_digest_add_bytes(digest, (char*)cell->payload, CELL_PAYLOAD_SIZE); - crypto_digest_get_digest(digest, integrity, V0_DIGEST_LEN); -// log_fn(LOG_DEBUG,"Putting digest of %u %u %u %u into relay cell.", -// integrity[0], integrity[1], integrity[2], integrity[3]); - memcpy(cell->payload + V0_DIGEST_OFFSET, integrity, V0_DIGEST_LEN); -} +#define CGO_AES_BITS 128 -/** Does the digest for this circuit indicate that this cell is for us? +/** Return the sendme tag within the <b>crypto</b> object, + * along with its length. * - * Update digest from the payload of cell (with the integrity part set - * to 0). If the integrity part is valid, return 1, else restore digest - * and cell to their original state and return 0. + * This is the digest from the most recent cell that we originated + * or recognized, _in either direction_. + * Calls to any encryption function on `crypto` may invalidate + * this digest. */ -static int -relay_digest_matches_v0(crypto_digest_t *digest, cell_t *cell) +const uint8_t * +relay_crypto_get_sendme_tag(relay_crypto_t *crypto, + size_t *len_out) { - uint32_t received_integrity, calculated_integrity; - crypto_digest_checkpoint_t backup_digest; - - CTASSERT(sizeof(uint32_t) == V0_DIGEST_LEN); - - crypto_digest_checkpoint(&backup_digest, digest); - - memcpy(&received_integrity, cell->payload + V0_DIGEST_OFFSET, V0_DIGEST_LEN); - memset(cell->payload + V0_DIGEST_OFFSET, 0, V0_DIGEST_LEN); - -// log_fn(LOG_DEBUG,"Reading digest of %u %u %u %u from relay cell.", -// received_integrity[0], received_integrity[1], -// received_integrity[2], received_integrity[3]); - - crypto_digest_add_bytes(digest, (char*) cell->payload, CELL_PAYLOAD_SIZE); - crypto_digest_get_digest(digest, (char*) &calculated_integrity, - V0_DIGEST_LEN); - - int rv = 1; - - if (calculated_integrity != received_integrity) { -// log_fn(LOG_INFO,"Recognized=0 but bad digest. Not recognizing."); -// (%d vs %d).", received_integrity, calculated_integrity); - /* restore digest to its old form */ - crypto_digest_restore(digest, &backup_digest); - /* restore the relay header */ - memcpy(cell->payload + V0_DIGEST_OFFSET, &received_integrity, - V0_DIGEST_LEN); - rv = 0; + tor_assert(crypto); + switch (crypto->kind) { + case RCK_TOR1: + *len_out = SENDME_TAG_LEN_TOR1; + return crypto->c.tor1.sendme_digest; + case RCK_CGO: + *len_out = SENDME_TAG_LEN_CGO; + return crypto->c.cgo.last_tag; } - - memwipe(&backup_digest, 0, sizeof(backup_digest)); - return rv; + tor_assert_unreached(); } -static inline bool -relay_cell_is_recognized_v0(const cell_t *cell) +/** Return the length of SENDME tags generated by `crypto`. */ +size_t +relay_crypto_sendme_tag_len(const relay_crypto_t *crypto) { - return get_uint16(cell->payload + V0_RECOGNIZED_OFFSET) == 0; + tor_assert(crypto); + switch (crypto->kind) { + case RCK_TOR1: + return SENDME_TAG_LEN_TOR1; + case RCK_CGO: + return SENDME_TAG_LEN_CGO; + } + tor_assert_unreached(); } -/** Apply <b>cipher</b> to CELL_PAYLOAD_SIZE bytes of <b>in</b> - * (in place). - * - * Note that we use the same operation for encrypting and for decrypting. +/** + * Handle a single layer of client-side backward encryption + * with crypto of an arbitary type. */ -void -relay_crypt_one_payload(crypto_cipher_t *cipher, uint8_t *in) +static inline bool +relay_crypt_client_backward(relay_crypto_t *crypto, cell_t *cell) { - crypto_cipher_crypt_inplace(cipher, (char*) in, CELL_PAYLOAD_SIZE); + switch (crypto->kind) { + case RCK_TOR1: + return tor1_crypt_client_backward(&crypto->c.tor1, cell); + case RCK_CGO: { + const uint8_t *tag = NULL; + cgo_crypt_client_backward(crypto->c.cgo.back, cell, &tag); + if (tag != NULL) { + memcpy(crypto->c.cgo.last_tag, tag, SENDME_TAG_LEN_CGO); + return true; + } else { + return false; + } + } + } + tor_assert_unreached(); } -/** Return the sendme_digest within the <b>crypto</b> object. */ -uint8_t * -relay_crypto_get_sendme_digest(relay_crypto_t *crypto) +/** + * Handle a relay-side forward encryption + * with crypto of an arbitary type. + */ +static inline bool +relay_crypt_relay_forward(relay_crypto_t *crypto, cell_t *cell) { - tor_assert(crypto); - return crypto->sendme_digest; + switch (crypto->kind) { + case RCK_TOR1: + return tor1_crypt_relay_forward(&crypto->c.tor1, cell); + case RCK_CGO: { + const uint8_t *tag = NULL; + cgo_crypt_relay_forward(crypto->c.cgo.fwd, cell, &tag); + if (tag != NULL) { + memcpy(crypto->c.cgo.last_tag, tag, SENDME_TAG_LEN_CGO); + return true; + } else { + return false; + } + } + } + tor_assert_unreached(); } -/** Record the cell digest, indicated by is_foward_digest or not, as the - * SENDME cell digest. */ -void -relay_crypto_record_sendme_digest(relay_crypto_t *crypto, - bool is_foward_digest) +/** + * Handle relay-side backward encryption with crypto of an arbitary type. + */ +static inline void +relay_crypt_relay_backward(relay_crypto_t *crypto, cell_t *cell) { - struct crypto_digest_t *digest; - - tor_assert(crypto); - - digest = crypto->b_digest; - if (is_foward_digest) { - digest = crypto->f_digest; + switch (crypto->kind) { + case RCK_TOR1: + tor1_crypt_relay_backward(&crypto->c.tor1, cell); + break; + case RCK_CGO: { + cgo_crypt_relay_backward(crypto->c.cgo.back, cell); + break; + } } - - crypto_digest_get_digest(digest, (char *) crypto->sendme_digest, - sizeof(crypto->sendme_digest)); } /** Do the appropriate en/decryptions for <b>cell</b> arriving on @@ -177,46 +171,68 @@ relay_decrypt_cell(circuit_t *circ, cell_t *cell, do { /* Remember: cpath is in forward order, that is, first hop first. */ tor_assert(thishop); - /* decrypt one layer */ - cpath_crypt_cell(thishop, cell->payload, true); - - if (relay_cell_is_recognized_v0(cell)) { - /* it's possibly recognized. have to check digest to be sure. */ - if (relay_digest_matches_v0(cpath_get_incoming_digest(thishop), - cell)) { - *recognized = 1; - *layer_hint = thishop; - return 0; - } + bool rec = relay_crypt_client_backward(&thishop->pvt_crypto, cell); + if (rec) { + *recognized = 1; + *layer_hint = thishop; + return 0; } - thishop = thishop->next; } while (thishop != cpath && thishop->state == CPATH_STATE_OPEN); log_fn(LOG_PROTOCOL_WARN, LD_OR, "Incoming cell at client not recognized. Closing."); return -1; } else { - relay_crypto_t *crypto = &TO_OR_CIRCUIT(circ)->crypto; /* We're in the middle. Encrypt one layer. */ - relay_crypt_one_payload(crypto->b_crypto, cell->payload); + relay_crypto_t *crypto = &TO_OR_CIRCUIT(circ)->crypto; + relay_crypt_relay_backward(crypto, cell); } } else /* cell_direction == CELL_DIRECTION_OUT */ { /* We're in the middle. Decrypt one layer. */ relay_crypto_t *crypto = &TO_OR_CIRCUIT(circ)->crypto; - relay_crypt_one_payload(crypto->f_crypto, cell->payload); - - if (relay_cell_is_recognized_v0(cell)) { - /* it's possibly recognized. have to check digest to be sure. */ - if (relay_digest_matches_v0(crypto->f_digest, cell)) { - *recognized = 1; - return 0; - } + bool rec = relay_crypt_relay_forward(crypto, cell); + if (rec) { + *recognized = 1; + return 0; } } return 0; } +/** Originate a client cell with a relay_crypt_t of arbitrary type. */ +static inline void +relay_crypt_client_originate(relay_crypto_t *crypto, cell_t *cell) +{ + switch (crypto->kind) { + case RCK_TOR1: + tor1_crypt_client_originate(&crypto->c.tor1, cell); + break; + case RCK_CGO: { + const uint8_t *tag = NULL; + cgo_crypt_client_originate(crypto->c.cgo.fwd, cell, &tag); + tor_assert(tag); + memcpy(crypto->c.cgo.last_tag, tag, SENDME_TAG_LEN_CGO); + break; + } + } +} + +/** Perform forward-direction client encryption with a relay_crypt_t + * of arbitrary type. */ +static inline void +relay_crypt_client_forward(relay_crypto_t *crypto, cell_t *cell) +{ + switch (crypto->kind) { + case RCK_TOR1: + tor1_crypt_client_forward(&crypto->c.tor1, cell); + break; + case RCK_CGO: + cgo_crypt_client_forward(crypto->c.cgo.fwd, cell); + break; + } +} + /** * Encrypt a cell <b>cell</b> that we are creating, and sending outbound on * <b>circ</b> until the hop corresponding to <b>layer_hint</b>. @@ -229,21 +245,15 @@ relay_encrypt_cell_outbound(cell_t *cell, origin_circuit_t *circ, crypt_path_t *layer_hint) { - crypt_path_t *thishop; /* counter for repeated crypts */ - cpath_set_cell_forward_digest(layer_hint, cell); - - /* Record cell digest as the SENDME digest if need be. */ - sendme_record_sending_cell_digest(TO_CIRCUIT(circ), layer_hint); + crypt_path_t *thishop = layer_hint; - thishop = layer_hint; - /* moving from farthest to nearest hop */ - do { - tor_assert(thishop); - log_debug(LD_OR,"encrypting a layer of the relay cell."); - cpath_crypt_cell(thishop, cell->payload, false); + relay_crypt_client_originate(&thishop->pvt_crypto, cell); + thishop = thishop->prev; + while (thishop != circ->cpath->prev) { + relay_crypt_client_forward(&thishop->pvt_crypto, cell); thishop = thishop->prev; - } while (thishop != circ->cpath->prev); + } } /** @@ -257,13 +267,19 @@ void relay_encrypt_cell_inbound(cell_t *cell, or_circuit_t *or_circ) { - relay_set_digest_v0(or_circ->crypto.b_digest, cell); - - /* Record cell digest as the SENDME digest if need be. */ - sendme_record_sending_cell_digest(TO_CIRCUIT(or_circ), NULL); - - /* encrypt one layer */ - relay_crypt_one_payload(or_circ->crypto.b_crypto, cell->payload); + relay_crypto_t *crypto = &or_circ->crypto; + switch (crypto->kind) { + case RCK_TOR1: + tor1_crypt_relay_originate(&crypto->c.tor1, cell); + break; + case RCK_CGO: { + const uint8_t *tag = NULL; + cgo_crypt_relay_originate(crypto->c.cgo.back, cell, &tag); + tor_assert(tag); + memcpy(&crypto->c.cgo.last_tag, tag, SENDME_TAG_LEN_CGO); + break; + } + } } /** @@ -273,12 +289,43 @@ relay_encrypt_cell_inbound(cell_t *cell, void relay_crypto_clear(relay_crypto_t *crypto) { - if (BUG(!crypto)) - return; - crypto_cipher_free(crypto->f_crypto); - crypto_cipher_free(crypto->b_crypto); - crypto_digest_free(crypto->f_digest); - crypto_digest_free(crypto->b_digest); + switch (crypto->kind) { + case RCK_TOR1: + tor1_crypt_clear(&crypto->c.tor1); + break; + case RCK_CGO: + cgo_crypt_free(crypto->c.cgo.fwd); + cgo_crypt_free(crypto->c.cgo.back); + break; + } +} + +static int +cgo_pair_init(cgo_pair_t *pair, bool is_relay, + const uint8_t *key_material, size_t key_data_len) +{ + memset(pair, 0, sizeof(*pair)); + const int aes_bits = CGO_AES_BITS; + const size_t single_cgo_len = cgo_key_material_len(aes_bits); + if (BUG(key_data_len != single_cgo_len * 2)) { + return -1; + } + + cgo_mode_t fwd_mode, back_mode; + if (is_relay) { + fwd_mode = CGO_MODE_RELAY_FORWARD; + back_mode = CGO_MODE_RELAY_BACKWARD; + } else { + fwd_mode = CGO_MODE_CLIENT_FORWARD; + back_mode = CGO_MODE_CLIENT_BACKWARD; + } + + pair->fwd = cgo_crypt_new(fwd_mode, aes_bits, + key_material, single_cgo_len); + pair->back = cgo_crypt_new(back_mode, aes_bits, + key_material + single_cgo_len, single_cgo_len); + + return 0; } /** Initialize <b>crypto</b> from the key material in key_data. @@ -299,75 +346,54 @@ relay_crypto_clear(relay_crypto_t *crypto) * Return 0 if init was successful, else -1 if it failed. */ int -relay_crypto_init(relay_crypto_t *crypto, - const char *key_data, size_t key_data_len, - int reverse, int is_hs_v3) +relay_crypto_init(relay_crypto_alg_t alg, + relay_crypto_t *crypto, + const char *key_data, size_t key_data_len) { - crypto_digest_t *tmp_digest; - crypto_cipher_t *tmp_crypto; - size_t digest_len = 0; - size_t cipher_key_len = 0; - - tor_assert(crypto); - tor_assert(key_data); - tor_assert(!(crypto->f_crypto || crypto->b_crypto || - crypto->f_digest || crypto->b_digest)); - - /* Basic key size validation */ - if (is_hs_v3 && BUG(key_data_len != HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN)) { - goto err; - } else if (!is_hs_v3 && BUG(key_data_len != CPATH_KEY_MATERIAL_LEN)) { - goto err; - } - - /* If we are using this crypto for next gen onion services use SHA3-256, - otherwise use good ol' SHA1 */ - if (is_hs_v3) { - digest_len = DIGEST256_LEN; - cipher_key_len = CIPHER256_KEY_LEN; - crypto->f_digest = crypto_digest256_new(DIGEST_SHA3_256); - crypto->b_digest = crypto_digest256_new(DIGEST_SHA3_256); - } else { - digest_len = DIGEST_LEN; - cipher_key_len = CIPHER_KEY_LEN; - crypto->f_digest = crypto_digest_new(); - crypto->b_digest = crypto_digest_new(); - } - - tor_assert(digest_len != 0); - tor_assert(cipher_key_len != 0); - const int cipher_key_bits = (int) cipher_key_len * 8; - - crypto_digest_add_bytes(crypto->f_digest, key_data, digest_len); - crypto_digest_add_bytes(crypto->b_digest, key_data+digest_len, digest_len); - - crypto->f_crypto = crypto_cipher_new_with_bits(key_data+(2*digest_len), - cipher_key_bits); - if (!crypto->f_crypto) { - log_warn(LD_BUG,"Forward cipher initialization failed."); - goto err; - } - - crypto->b_crypto = crypto_cipher_new_with_bits( - key_data+(2*digest_len)+cipher_key_len, - cipher_key_bits); - if (!crypto->b_crypto) { - log_warn(LD_BUG,"Backward cipher initialization failed."); - goto err; + switch (alg) { + /* Tor1 cases: the booleans are "reverse" and "is_hs_v3". */ + case RELAY_CRYPTO_ALG_TOR1: + crypto->kind = RCK_TOR1; + return tor1_crypt_init(&crypto->c.tor1, key_data, key_data_len, + false, false); + case RELAY_CRYPTO_ALG_TOR1_HSC: + crypto->kind = RCK_TOR1; + return tor1_crypt_init(&crypto->c.tor1, key_data, key_data_len, + false, true); + case RELAY_CRYPTO_ALG_TOR1_HSS: + crypto->kind = RCK_TOR1; + return tor1_crypt_init(&crypto->c.tor1, key_data, key_data_len, + true, true); + case RELAY_CRYPTO_ALG_CGO_CLIENT: + crypto->kind = RCK_CGO; + return cgo_pair_init(&crypto->c.cgo, false, + (const uint8_t *)key_data, key_data_len); + case RELAY_CRYPTO_ALG_CGO_RELAY: + crypto->kind = RCK_CGO; + return cgo_pair_init(&crypto->c.cgo, true, + (const uint8_t *)key_data, key_data_len); } + tor_assert_unreached(); +} - if (reverse) { - tmp_digest = crypto->f_digest; - crypto->f_digest = crypto->b_digest; - crypto->b_digest = tmp_digest; - tmp_crypto = crypto->f_crypto; - crypto->f_crypto = crypto->b_crypto; - crypto->b_crypto = tmp_crypto; +/** Return the amount of key material we need to initialize + * the given relay crypto algorithm. + * + * Return -1 if the algorithm is unrecognized. + */ +ssize_t +relay_crypto_key_material_len(relay_crypto_alg_t alg) +{ + switch (alg) { + case RELAY_CRYPTO_ALG_TOR1: + return tor1_key_material_len(false); + case RELAY_CRYPTO_ALG_TOR1_HSC: + case RELAY_CRYPTO_ALG_TOR1_HSS: + return tor1_key_material_len(true); + case RELAY_CRYPTO_ALG_CGO_CLIENT: + case RELAY_CRYPTO_ALG_CGO_RELAY: + return cgo_key_material_len(CGO_AES_BITS) * 2; } - - return 0; - err: - relay_crypto_clear(crypto); return -1; } @@ -375,8 +401,11 @@ relay_crypto_init(relay_crypto_t *crypto, void relay_crypto_assert_ok(const relay_crypto_t *crypto) { - tor_assert(crypto->f_crypto); - tor_assert(crypto->b_crypto); - tor_assert(crypto->f_digest); - tor_assert(crypto->b_digest); + switch (crypto->kind) { + case RCK_TOR1: + tor1_crypt_assert_ok(&crypto->c.tor1); + break; + case RCK_CGO: + break; + } } diff --git a/src/core/crypto/relay_crypto.h b/src/core/crypto/relay_crypto.h @@ -12,9 +12,34 @@ #ifndef TOR_RELAY_CRYPTO_H #define TOR_RELAY_CRYPTO_H -int relay_crypto_init(relay_crypto_t *crypto, - const char *key_data, size_t key_data_len, - int reverse, int is_hs_v3); +/** Enumeration to identify which relay crypto algorithm is in use. */ +typedef enum relay_crypto_alg_t { + /** Tor1 relay crypto, as used for ordinary circuit hops. */ + RELAY_CRYPTO_ALG_TOR1, + /** Tor1 relay crypto, as used as an onion service client for + * the shared virtual HS hop created with an INTRODUCE/RENVEZVOUS + * handshake. */ + RELAY_CRYPTO_ALG_TOR1_HSC, + /** Tor1 relay crypto, as used as an onion service for + * the shared virtual HS hop created with an INTRODUCE/RENVEZVOUS + * handshake. */ + RELAY_CRYPTO_ALG_TOR1_HSS, + /** CGO crypto, as used at a client */ + RELAY_CRYPTO_ALG_CGO_CLIENT, + /** CGO crypto, as used at a relay */ + RELAY_CRYPTO_ALG_CGO_RELAY, +} relay_crypto_alg_t; + +/** Largest possible return value for relay_crypto_key_material_len. */ +/* This is 2x the length needed for a single cgo direction with 256-bit AES + */ +#define MAX_RELAY_KEY_MATERIAL_LEN 224 + +ssize_t relay_crypto_key_material_len(relay_crypto_alg_t alg); + +int relay_crypto_init(relay_crypto_alg_t alg, + relay_crypto_t *crypto, + const char *key_data, size_t key_data_len); int relay_decrypt_cell(circuit_t *circ, cell_t *cell, cell_direction_t cell_direction, @@ -27,15 +52,8 @@ void relay_crypto_clear(relay_crypto_t *crypto); void relay_crypto_assert_ok(const relay_crypto_t *crypto); -uint8_t *relay_crypto_get_sendme_digest(relay_crypto_t *crypto); - -void relay_crypto_record_sendme_digest(relay_crypto_t *crypto, - bool is_foward_digest); - -void -relay_crypt_one_payload(crypto_cipher_t *cipher, uint8_t *in); - -void -relay_set_digest_v0(crypto_digest_t *digest, cell_t *cell); +const uint8_t *relay_crypto_get_sendme_tag(relay_crypto_t *crypto, + size_t *len_out); +size_t relay_crypto_sendme_tag_len(const relay_crypto_t *crypto); #endif /* !defined(TOR_RELAY_CRYPTO_H) */ diff --git a/src/core/crypto/relay_crypto_cgo.c b/src/core/crypto/relay_crypto_cgo.c @@ -21,6 +21,7 @@ #include "lib/arch/bytes.h" #include "ext/polyval/polyval.h" #include "core/crypto/relay_crypto_cgo.h" +#include "core/crypto/relay_crypto.h" #include "core/or/cell_st.h" #if 0 @@ -361,7 +362,9 @@ size_t cgo_key_material_len(int aesbits) { tor_assert(aesbits == 128 || aesbits == 192 || aesbits == 256); - return (cgo_uiv_keylen(aesbits) + CGO_TAG_LEN); + size_t r = (cgo_uiv_keylen(aesbits) + SENDME_TAG_LEN_CGO); + tor_assert(r * 2 <= MAX_RELAY_KEY_MATERIAL_LEN); + return r; } /** @@ -388,8 +391,8 @@ cgo_crypt_new(cgo_mode_t mode, int aesbits, const uint8_t *keys, size_t keylen) r = cgo_uiv_init(&cgo->uiv, aesbits, encrypt, keys); tor_assert(r == 0); keys += cgo_uiv_keylen(aesbits); - memcpy(cgo->nonce, keys, CGO_TAG_LEN); - keys += CGO_TAG_LEN; + memcpy(cgo->nonce, keys, SENDME_TAG_LEN_CGO); + keys += SENDME_TAG_LEN_CGO; tor_assert(keys == end_of_keys); cgo->aes_bytes = aesbits / 8; @@ -425,7 +428,7 @@ cgo_crypt_update(cgo_crypt_t *cgo, cgo_mode_t mode) * process an outbound cell from the client. * * If the cell is for this relay, set *'recognized_tag_out' - * to point to a CGO_TAG_LEN value that should be used + * to point to a SENDME_TAG_LEN_CGO value that should be used * if we want to acknowledge this cell with an authenticated SENDME. * * The value of 'recognized_tag_out' will become invalid @@ -442,10 +445,10 @@ cgo_crypt_relay_forward(cgo_crypt_t *cgo, cell_t *cell, .h = cgo->tprime, .cmd = cell->command, }; - memcpy(cgo->last_tag_relay_fwd, cell->payload, CGO_TAG_LEN); + memcpy(cgo->last_tag_relay_fwd, cell->payload, SENDME_TAG_LEN_CGO); cgo_uiv_encrypt(&cgo->uiv, h, cell->payload); - memcpy(cgo->tprime, cell->payload, CGO_TAG_LEN); - if (tor_memeq(cell->payload, cgo->nonce, CGO_TAG_LEN)) { + memcpy(cgo->tprime, cell->payload, SENDME_TAG_LEN_CGO); + if (tor_memeq(cell->payload, cgo->nonce, SENDME_TAG_LEN_CGO)) { cgo_crypt_update(cgo, CGO_MODE_RELAY_FORWARD); *recognized_tag_out = cgo->last_tag_relay_fwd; } else { @@ -465,7 +468,7 @@ cgo_crypt_relay_backward(cgo_crypt_t *cgo, cell_t *cell) .cmd = cell->command, }; cgo_uiv_encrypt(&cgo->uiv, h, cell->payload); - memcpy(cgo->tprime, cell->payload, CGO_TAG_LEN); + memcpy(cgo->tprime, cell->payload, SENDME_TAG_LEN_CGO); } /** @@ -473,7 +476,7 @@ cgo_crypt_relay_backward(cgo_crypt_t *cgo, cell_t *cell) * encrypt an inbound message that we are originating, for the client. * * The provided cell must have its command value set, - * and should have the first CGO_TAG_LEN bytes of its payload unused. + * and should have the first SENDME_TAG_LEN_CGO bytes of its payload unused. * * Set '*tag_out' to a value that we should expect * if we want an authenticated SENDME for this cell. @@ -490,12 +493,12 @@ cgo_crypt_relay_originate(cgo_crypt_t *cgo, cell_t *cell, .h = cgo->tprime, .cmd = cell->command, }; - memcpy(cell->payload, cgo->nonce, CGO_TAG_LEN); + memcpy(cell->payload, cgo->nonce, SENDME_TAG_LEN_CGO); cgo_uiv_encrypt(&cgo->uiv, h, cell->payload); - memcpy(&cgo->tprime, cell->payload, CGO_TAG_LEN); - memcpy(&cgo->nonce, cell->payload, CGO_TAG_LEN); + memcpy(&cgo->tprime, cell->payload, SENDME_TAG_LEN_CGO); + memcpy(&cgo->nonce, cell->payload, SENDME_TAG_LEN_CGO); if (tag_out) { - // tor_assert(tor_memeq(cgo->tprime, cell->payload, CGO_TAG_LEN)); + // tor_assert(tor_memeq(cgo->tprime, cell->payload, SENDME_TAG_LEN_CGO)); *tag_out = cgo->tprime; } cgo_crypt_update(cgo, CGO_MODE_RELAY_BACKWARD); @@ -508,14 +511,14 @@ cgo_crypt_relay_originate(cgo_crypt_t *cgo, cell_t *cell, void cgo_crypt_client_forward(cgo_crypt_t *cgo, cell_t *cell) { - uint8_t tprime_new[CGO_TAG_LEN]; - memcpy(tprime_new, cell->payload, CGO_TAG_LEN); + uint8_t tprime_new[SENDME_TAG_LEN_CGO]; + memcpy(tprime_new, cell->payload, SENDME_TAG_LEN_CGO); uiv_tweak_t h = { .h = cgo->tprime, .cmd = cell->command, }; cgo_uiv_decrypt(&cgo->uiv, h, cell->payload); - memcpy(cgo->tprime, tprime_new, CGO_TAG_LEN); + memcpy(cgo->tprime, tprime_new, SENDME_TAG_LEN_CGO); } /** @@ -523,7 +526,7 @@ cgo_crypt_client_forward(cgo_crypt_t *cgo, cell_t *cell) * originate a cell for a given target hop. * * The provided cell must have its command value set, - * and should have the first CGO_TAG_LEN bytes of its payload unused. + * and should have the first SENDME_TAG_LEN_CGO bytes of its payload unused. * * Set '*tag_out' to a value that we should expect * if we want an authenticated SENDME for this cell. @@ -536,7 +539,7 @@ void cgo_crypt_client_originate(cgo_crypt_t *cgo, cell_t *cell, const uint8_t **tag_out) { - memcpy(cell->payload, cgo->nonce, CGO_TAG_LEN); + memcpy(cell->payload, cgo->nonce, SENDME_TAG_LEN_CGO); cgo_crypt_client_forward(cgo, cell); cgo_crypt_update(cgo, CGO_MODE_CLIENT_FORWARD); *tag_out = cell->payload; @@ -547,7 +550,7 @@ cgo_crypt_client_originate(cgo_crypt_t *cgo, cell_t *cell, * process an inbound cell from a relay. * * If the cell originated from this this relay, set *'recognized_tag_out' - * to point to a CGO_TAG_LEN value that should be used + * to point to a SENDME_TAG_LEN_CGO value that should be used * if we want to acknowledge this cell with an authenticated SENDME. * * The value of 'recognized_tag_out' will become invalid @@ -564,15 +567,15 @@ cgo_crypt_client_backward(cgo_crypt_t *cgo, cell_t *cell, .h = cgo->tprime, .cmd = cell->command, }; - uint8_t t_orig[CGO_TAG_LEN]; - memcpy(t_orig, cell->payload, CGO_TAG_LEN); + uint8_t t_orig[SENDME_TAG_LEN_CGO]; + memcpy(t_orig, cell->payload, SENDME_TAG_LEN_CGO); cgo_uiv_decrypt(&cgo->uiv, h, cell->payload); - memcpy(cgo->tprime, t_orig, CGO_TAG_LEN); - if (tor_memeq(cell->payload, cgo->nonce, CGO_TAG_LEN)) { - memcpy(cgo->nonce, t_orig, CGO_TAG_LEN); + memcpy(cgo->tprime, t_orig, SENDME_TAG_LEN_CGO); + if (tor_memeq(cell->payload, cgo->nonce, SENDME_TAG_LEN_CGO)) { + memcpy(cgo->nonce, t_orig, SENDME_TAG_LEN_CGO); cgo_crypt_update(cgo, CGO_MODE_CLIENT_BACKWARD); - // tor_assert(tor_memeq(cgo->tprime, t_orig, CGO_TAG_LEN)); + // tor_assert(tor_memeq(cgo->tprime, t_orig, SENDME_TAG_LEN_CGO)); *recognized_tag_out = cgo->tprime; } else { *recognized_tag_out = NULL; diff --git a/src/core/crypto/relay_crypto_cgo.h b/src/core/crypto/relay_crypto_cgo.h @@ -29,13 +29,6 @@ typedef enum { CGO_MODE_RELAY_BACKWARD, } cgo_mode_t; -/** - * Length of a CGO cell tag. - * - * This is the value used for authenticated SENDMES. - **/ -#define CGO_TAG_LEN 16 - struct cell_t; size_t cgo_key_material_len(int aesbits); @@ -198,14 +191,14 @@ STATIC void cgo_uiv_clear(cgo_uiv_t *uiv); struct cgo_crypt_t { cgo_uiv_t uiv; - uint8_t nonce[CGO_TAG_LEN]; - uint8_t tprime[CGO_TAG_LEN]; + uint8_t nonce[SENDME_TAG_LEN_CGO]; + uint8_t tprime[SENDME_TAG_LEN_CGO]; /** * Stored version of the last incoming cell tag. * Only used for cgo_crypt_relay_fwd, where this information is not * otherwise available after encryption. */ - uint8_t last_tag_relay_fwd[CGO_TAG_LEN]; + uint8_t last_tag_relay_fwd[SENDME_TAG_LEN_CGO]; uint8_t aes_bytes; }; #endif diff --git a/src/core/crypto/relay_crypto_st.h b/src/core/crypto/relay_crypto_st.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2021, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file relay_crypto_st.h + * @brief Relay-cell encryption state structure. + **/ + +#ifndef RELAY_CRYPTO_ST_H +#define RELAY_CRYPTO_ST_H + +#include "core/crypto/tor1_crypt_st.h" +#include "core/crypto/relay_crypto_cgo.h" + +typedef enum relay_crypto_kind_t { + RCK_TOR1, + RCK_CGO, +} relay_crypto_kind_t; + +typedef struct cgo_pair_t { + // NOTE: Using pointers here is a bit awkward; we may want to refactor + // eventually. + cgo_crypt_t *fwd; + cgo_crypt_t *back; + /* The last tag that we got when originating or recognizing a message */ + uint8_t last_tag[SENDME_TAG_LEN_CGO]; +} cgo_pair_t; + +struct relay_crypto_t { + relay_crypto_kind_t kind; + union { + struct tor1_crypt_t tor1; + cgo_pair_t cgo; + } c; +}; + +#endif /* !defined(RELAY_CRYPTO_ST_H) */ diff --git a/src/core/crypto/relay_crypto_tor1.c b/src/core/crypto/relay_crypto_tor1.c @@ -0,0 +1,313 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2021, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file relay_crypto_tor1.c + * @brief Implementation for legacy (tor1) relay cell encryption. + **/ + +#include "core/or/or.h" +#include "lib/crypt_ops/crypto_cipher.h" +#include "lib/crypt_ops/crypto_util.h" +#include "core/crypto/hs_ntor.h" // for HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN +#include "core/crypto/relay_crypto_tor1.h" +#include "lib/cc/ctassert.h" + +#include "core/or/cell_st.h" +#include "core/crypto/relay_crypto_st.h" + +/* Offset of digest within relay cell body for v0 cells. */ +#define V0_DIGEST_OFFSET 5 +#define V0_DIGEST_LEN 4 +#define V0_RECOGNIZED_OFFSET 1 + +/** Update digest from the payload of cell. Assign integrity part to + * cell. Record full 20-byte digest in `buf`. + */ +static void +tor1_set_digest_v0(crypto_digest_t *digest, cell_t *cell, uint8_t *buf) +{ + crypto_digest_add_bytes(digest, (char*)cell->payload, CELL_PAYLOAD_SIZE); + crypto_digest_get_digest(digest, (char*)buf, DIGEST_LEN); +// log_fn(LOG_DEBUG,"Putting digest of %u %u %u %u into relay cell.", +// integrity[0], integrity[1], integrity[2], integrity[3]); + memcpy(cell->payload + V0_DIGEST_OFFSET, buf, V0_DIGEST_LEN); +} + +/** Does the digest for this circuit indicate that this cell is for us? + * + * Update digest from the payload of cell (with the integrity part set + * to 0). If the integrity part is valid, + * return 1 and save the full digest in the 20-byte buffer `buf`, + * else restore digest + * and cell to their original state and return 0. + */ +static int +tor1_relay_digest_matches_v0(crypto_digest_t *digest, cell_t *cell, + uint8_t *buf) +{ + uint32_t received_integrity, calculated_integrity; + uint8_t calculated_digest[DIGEST_LEN]; + crypto_digest_checkpoint_t backup_digest; + + CTASSERT(sizeof(uint32_t) == V0_DIGEST_LEN); + + crypto_digest_checkpoint(&backup_digest, digest); + + memcpy(&received_integrity, cell->payload + V0_DIGEST_OFFSET, V0_DIGEST_LEN); + memset(cell->payload + V0_DIGEST_OFFSET, 0, V0_DIGEST_LEN); + +// log_fn(LOG_DEBUG,"Reading digest of %u %u %u %u from relay cell.", +// received_integrity[0], received_integrity[1], +// received_integrity[2], received_integrity[3]); + + crypto_digest_add_bytes(digest, (char*) cell->payload, CELL_PAYLOAD_SIZE); + crypto_digest_get_digest(digest, (char*) calculated_digest, DIGEST_LEN); + calculated_integrity = get_uint32(calculated_digest); + + int rv = 1; + + if (calculated_integrity != received_integrity) { +// log_fn(LOG_INFO,"Recognized=0 but bad digest. Not recognizing."); +// (%d vs %d).", received_integrity, calculated_integrity); + /* restore digest to its old form */ + crypto_digest_restore(digest, &backup_digest); + /* restore the relay header */ + memcpy(cell->payload + V0_DIGEST_OFFSET, &received_integrity, + V0_DIGEST_LEN); + rv = 0; + } else { + memcpy(buf, calculated_digest, DIGEST_LEN); + } + + memwipe(&backup_digest, 0, sizeof(backup_digest)); + return rv; +} + +static inline bool +relay_cell_is_recognized_v0(const cell_t *cell) +{ + return get_uint16(cell->payload + V0_RECOGNIZED_OFFSET) == 0; +} + +/** Apply <b>cipher</b> to CELL_PAYLOAD_SIZE bytes of <b>in</b> + * (in place). + * + * Note that we use the same operation for encrypting and for decrypting. + */ +static void +tor1_crypt_one_payload(crypto_cipher_t *cipher, uint8_t *in) +{ + crypto_cipher_crypt_inplace(cipher, (char*) in, CELL_PAYLOAD_SIZE); +} + +/** Encrypt and authenticate `cell` using the cryptographic + * material in `tor1`. + * + * This method should be used for the first encryption performed + * by the client - that is, the one corresponding to the exit node. + */ +void +tor1_crypt_client_originate(tor1_crypt_t *tor1, + cell_t *cell) +{ + tor1_set_digest_v0(tor1->f_digest, cell, tor1->sendme_digest); + tor1_crypt_one_payload(tor1->f_crypto, cell->payload); +} + +/** Encrypt and authenticate `cell`, using the cryptographic + * material in `tor1`. + * + * This method should be used by relays when originating cells toward the + * client. + */ +void +tor1_crypt_relay_originate(tor1_crypt_t *tor1, + cell_t *cell) +{ + tor1_set_digest_v0(tor1->b_digest, cell, tor1->sendme_digest); + tor1_crypt_one_payload(tor1->b_crypto, cell->payload); +} + +/** Encrypt `cell` using the cryptographic material in `tor1`. + * + * This method should be used by clients for cryptographic layers + * that are _not_ the final recipient of the cell. */ +void +tor1_crypt_client_forward(tor1_crypt_t *tor1, cell_t *cell) +{ + tor1_crypt_one_payload(tor1->f_crypto, cell->payload); +} + +/** Encrypt `cell` using the cryptographic material in `tor1`. + * + * This method should be used by relays on cells that are moving + * toward the client. */ +void +tor1_crypt_relay_backward(tor1_crypt_t *tor1, cell_t *cell) +{ + tor1_crypt_one_payload(tor1->b_crypto, cell->payload); +} + +/** Decrypt `cell` using the cryptographic material in `tor1`. + * + * Return `true` when we are the destination for this cell. + * + * This method should be used by relays on cells + * that are moving away from the client. */ +bool +tor1_crypt_relay_forward(tor1_crypt_t *tor1, cell_t *cell) +{ + tor1_crypt_one_payload(tor1->f_crypto, cell->payload); + if (relay_cell_is_recognized_v0(cell)) { + if (tor1_relay_digest_matches_v0(tor1->f_digest, cell, + tor1->sendme_digest)) { + return true; + } + } + return false; +} + +/** Decrypt `cell` using the cryptographic material in `tor1`. + * + * Return `true` when this cell is recognized and authenticated + * as coming from the relay that also holds this cryptographic material. + * + * This method should be used by clients on incoming cells. */ +bool +tor1_crypt_client_backward(tor1_crypt_t *tor1, cell_t *cell) +{ + tor1_crypt_one_payload(tor1->b_crypto, cell->payload); + + if (relay_cell_is_recognized_v0(cell)) { + if (tor1_relay_digest_matches_v0(tor1->b_digest, cell, + tor1->sendme_digest)) { + return true; + } + } + return false; +} + +/** Return the number of bytes that tor1_crypt_init expects. */ +size_t +tor1_key_material_len(bool is_hs) +{ + if (is_hs) + return HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN; + else + return CPATH_KEY_MATERIAL_LEN; +} + +/** Initialize <b>crypto</b> from the key material in key_data. + * + * If <b>is_hs_v3</b> is set, this cpath will be used for next gen hidden + * service circuits and <b>key_data</b> must be + * HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN bytes in length. + * + * If <b>is_hs_v3</b> is not set, key_data must contain CPATH_KEY_MATERIAL_LEN + * bytes, which are used as follows: + * - 20 to initialize f_digest + * - 20 to initialize b_digest + * - 16 to key f_crypto + * - 16 to key b_crypto + * + * (If 'reverse' is true, then f_XX and b_XX are swapped.) + * + * Return 0 if init was successful, else -1 if it failed. + */ +int +tor1_crypt_init(tor1_crypt_t *crypto, + const char *key_data, size_t key_data_len, + int reverse, int is_hs_v3) +{ + crypto_digest_t *tmp_digest; + crypto_cipher_t *tmp_crypto; + size_t digest_len = 0; + size_t cipher_key_len = 0; + + tor_assert(crypto); + tor_assert(key_data); + tor_assert(!(crypto->f_crypto || crypto->b_crypto || + crypto->f_digest || crypto->b_digest)); + + /* Basic key size validation */ + if (is_hs_v3 && BUG(key_data_len != HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN)) { + goto err; + } else if (!is_hs_v3 && BUG(key_data_len != CPATH_KEY_MATERIAL_LEN)) { + goto err; + } + + /* If we are using this crypto for next gen onion services use SHA3-256, + otherwise use good ol' SHA1 */ + if (is_hs_v3) { + digest_len = DIGEST256_LEN; + cipher_key_len = CIPHER256_KEY_LEN; + crypto->f_digest = crypto_digest256_new(DIGEST_SHA3_256); + crypto->b_digest = crypto_digest256_new(DIGEST_SHA3_256); + } else { + digest_len = DIGEST_LEN; + cipher_key_len = CIPHER_KEY_LEN; + crypto->f_digest = crypto_digest_new(); + crypto->b_digest = crypto_digest_new(); + } + + tor_assert(digest_len != 0); + tor_assert(cipher_key_len != 0); + const int cipher_key_bits = (int) cipher_key_len * 8; + + crypto_digest_add_bytes(crypto->f_digest, key_data, digest_len); + crypto_digest_add_bytes(crypto->b_digest, key_data+digest_len, digest_len); + + crypto->f_crypto = crypto_cipher_new_with_bits(key_data+(2*digest_len), + cipher_key_bits); + if (!crypto->f_crypto) { + log_warn(LD_BUG,"Forward cipher initialization failed."); + goto err; + } + + crypto->b_crypto = crypto_cipher_new_with_bits( + key_data+(2*digest_len)+cipher_key_len, + cipher_key_bits); + if (!crypto->b_crypto) { + log_warn(LD_BUG,"Backward cipher initialization failed."); + goto err; + } + + if (reverse) { + tmp_digest = crypto->f_digest; + crypto->f_digest = crypto->b_digest; + crypto->b_digest = tmp_digest; + tmp_crypto = crypto->f_crypto; + crypto->f_crypto = crypto->b_crypto; + crypto->b_crypto = tmp_crypto; + } + + return 0; + err: + tor1_crypt_clear(crypto); + return -1; +} + +/** Assert that <b>crypto</b> is valid and set. */ +void +tor1_crypt_assert_ok(const tor1_crypt_t *crypto) +{ + tor_assert(crypto->f_crypto); + tor_assert(crypto->b_crypto); + tor_assert(crypto->f_digest); + tor_assert(crypto->b_digest); +} + +void +tor1_crypt_clear(tor1_crypt_t *crypto) +{ + if (BUG(!crypto)) + return; + crypto_cipher_free(crypto->f_crypto); + crypto_cipher_free(crypto->b_crypto); + crypto_digest_free(crypto->f_digest); + crypto_digest_free(crypto->b_digest); +} diff --git a/src/core/crypto/relay_crypto_tor1.h b/src/core/crypto/relay_crypto_tor1.h @@ -0,0 +1,33 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2021, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file relay.h + * \brief Header file for relay_crypto_tor1.c. + **/ + +#ifndef TOR_RELAY_CRYPTO_TOR1_H +#define TOR_RELAY_CRYPTO_TOR1_H + +typedef struct tor1_crypt_t tor1_crypt_t; + +void tor1_crypt_client_originate(tor1_crypt_t *tor1, + cell_t *cell); +void tor1_crypt_relay_originate(tor1_crypt_t *tor1, + cell_t *cell); +void tor1_crypt_relay_backward(tor1_crypt_t *tor1, cell_t *cell); +bool tor1_crypt_relay_forward(tor1_crypt_t *tor1, cell_t *cell); +bool tor1_crypt_client_backward(tor1_crypt_t *tor1, cell_t *cell); +void tor1_crypt_client_forward(tor1_crypt_t *tor1, cell_t *cell); + +size_t tor1_key_material_len(bool is_hs); +int tor1_crypt_init(tor1_crypt_t *crypto, + const char *key_data, size_t key_data_len, + int reverse, int is_hs_v3); +void tor1_crypt_assert_ok(const tor1_crypt_t *tor1); +void tor1_crypt_clear(tor1_crypt_t *crypto); + +#endif /* !defined(TOR_RELAY_CRYPTO_TOR1_H) */ diff --git a/src/core/crypto/tor1_crypt_st.h b/src/core/crypto/tor1_crypt_st.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2021, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file tor1_crypt_st.h + * @brief Structures for tor1 relay cell encryption. + **/ + +#ifndef TOR1_CRYPT_ST_H +#define TOR1_CRYPT_ST_H + +struct aes_cnt_cipher_t; +struct crypto_digest_t; + +struct tor1_crypt_t { + /** Encryption key and counter for cells heading towards the OR at this + * step. */ + struct aes_cnt_cipher_t *f_crypto; + /** Encryption key and counter for cells heading back from the OR at this + * step. */ + struct aes_cnt_cipher_t *b_crypto; + + /** Digest state for cells heading towards the OR at this step. */ + struct crypto_digest_t *f_digest; /* for integrity checking */ + /** Digest state for cells heading away from the OR at this step. */ + struct crypto_digest_t *b_digest; + + /** Digest used for the next SENDME cell if any. + * + * This digest is updated every time a cell is _originated_ or _recognized_ + * in either direction. Any operation with this object may + * invalidate this digest. */ + uint8_t sendme_digest[DIGEST_LEN]; +}; + +#endif /* !defined(TOR1_CRYPT_ST_H) */ diff --git a/src/core/mainloop/cpuworker.c b/src/core/mainloop/cpuworker.c @@ -213,7 +213,9 @@ typedef struct cpuworker_reply_t { /** The created cell to send back. */ created_cell_t created_cell; /** The keys to use on this circuit. */ - uint8_t keys[CPATH_KEY_MATERIAL_LEN]; + uint8_t keys[MAX_RELAY_KEY_MATERIAL_LEN]; + /** Length of the generated key material. */ + size_t keys_len; /** Input to use for authenticating introduce1 cells. */ uint8_t rend_auth_material[DIGEST_LEN]; /** Negotiated circuit parameters. */ @@ -451,12 +453,12 @@ cpuworker_onion_handshake_replyfn(void *work_) } } - // TODO CGO: Initialize this from a real handshake. - circ->relay_cell_format = RELAY_CELL_FORMAT_V0; + circ->relay_cell_format = rpl.circ_params.cell_fmt; if (onionskin_answer(circ, &rpl.created_cell, - (const char*)rpl.keys, sizeof(rpl.keys), + rpl.circ_params.crypto_alg, + (const char*)rpl.keys, rpl.keys_len, rpl.rend_auth_material) < 0) { log_warn(LD_OR,"onionskin_answer failed. Closing."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); @@ -498,15 +500,17 @@ cpuworker_onion_handshake_threadfn(void *state_, void *work_) rpl.handshake_type = cc->handshake_type; if (req.timed) tor_gettimeofday(&tv_start); + rpl.keys_len = sizeof(rpl.keys); n = onion_skin_server_handshake(cc->handshake_type, cc->onionskin, cc->handshake_len, onion_keys, &req.circ_ns_params, cell_out->reply, sizeof(cell_out->reply), - rpl.keys, CPATH_KEY_MATERIAL_LEN, + rpl.keys, &rpl.keys_len, rpl.rend_auth_material, &rpl.circ_params); + if (n < 0) { /* failure */ log_debug(LD_OR,"onion_skin_server_handshake failed."); diff --git a/src/core/or/circuit_st.h b/src/core/or/circuit_st.h @@ -144,7 +144,17 @@ struct circuit_t { * For example, position 2 (starting at 0) means that we've received 300 * cells so the 300th cell digest is kept at index 2. * - * At maximum, this list contains 200 bytes plus the smartlist overhead. */ + * At maximum, this list contains 200 bytes plus the smartlist overhead. + * + * The elements in this list are always of length SENDME_TAG_LEN_TOR1 + * (== DIGEST_LEN, == 20). The actual digests stored in those elements + * may be smaller, however, if another relay crypto algorithm is in use. + **/ + /* Note that this is a per-circuit field, although logically it might make + * more sense for it to be a per-hop field. That doesn't matter in C tor, + * since we don't send more than a single window of cells to any given + * relay except for the exit. + */ smartlist_t *sendme_last_digests; /** Temporary field used during circuits_handle_oom. */ diff --git a/src/core/or/circuitbuild.c b/src/core/or/circuitbuild.c @@ -52,6 +52,7 @@ #include "core/or/relay.h" #include "core/or/trace_probes_circuit.h" #include "core/or/crypt_path.h" +#include "core/or/protover.h" #include "feature/client/bridges.h" #include "feature/client/circpathbias.h" #include "feature/client/entrynodes.h" @@ -84,6 +85,7 @@ #include "trunnel/extension.h" #include "trunnel/congestion_control.h" +#include "trunnel/subproto_request.h" static int circuit_send_first_onion_skin(origin_circuit_t *circ); static int circuit_build_no_more_hops(origin_circuit_t *circ); @@ -883,6 +885,8 @@ circuit_pick_create_handshake(uint8_t *cell_type_out, if (ei->exit_supports_congestion_control && congestion_control_enabled()) *handshake_type_out = ONION_HANDSHAKE_TYPE_NTOR_V3; + else if (ei->enable_cgo) + *handshake_type_out = ONION_HANDSHAKE_TYPE_NTOR_V3; else *handshake_type_out = ONION_HANDSHAKE_TYPE_NTOR; } @@ -1258,7 +1262,7 @@ int circuit_finish_handshake(origin_circuit_t *circ, const created_cell_t *reply) { - char keys[CPATH_KEY_MATERIAL_LEN]; + char keys[MAX_RELAY_KEY_MATERIAL_LEN]; crypt_path_t *hop; int rv; @@ -1279,12 +1283,14 @@ circuit_finish_handshake(origin_circuit_t *circ, tor_assert(hop->state == CPATH_STATE_AWAITING_KEYS); circuit_params_t params; + size_t keylen = sizeof(keys); { const char *msg = NULL; + if (onion_skin_client_handshake(hop->handshake_state.tag, &hop->handshake_state, reply->reply, reply->handshake_len, - (uint8_t*)keys, sizeof(keys), + (uint8_t*)keys, &keylen, (uint8_t*)hop->rend_circ_nonce, &params, &msg) < 0) { @@ -1295,10 +1301,11 @@ circuit_finish_handshake(origin_circuit_t *circ, } onion_handshake_state_release(&hop->handshake_state); - - if (cpath_init_circuit_crypto(hop, keys, sizeof(keys), 0, 0)<0) { + if (cpath_init_circuit_crypto(params.crypto_alg, + hop, keys, keylen)<0) { return -END_CIRC_REASON_TORPROTOCOL; } + hop->relay_cell_format = params.cell_fmt; if (params.cc_enabled) { int circ_len = circuit_get_cpath_len(circ); @@ -2633,6 +2640,80 @@ circuit_upgrade_circuits_from_guard_wait(void) smartlist_free(to_upgrade); } +// TODO: Find a better place to declare this; it's duplicated in +// onion_crypto.c +#define EXT_TYPE_SUBPROTO 3 + +/** Add a request for the CGO subprotocol capability to ext. + * + * NOTE: If we need to support other subprotocol extensions, + * do not add separate functions! Instead rename this function + * and adapt it as appropriate. + */ +static int +build_cgo_subproto_request(trn_extension_t *ext) +{ + trn_extension_field_t *fld = NULL; + trn_subproto_request_t *req = NULL; + trn_subproto_request_ext_t *req_ext = NULL; + int r = 0; + + fld = trn_extension_field_new(); + req_ext = trn_subproto_request_ext_new(); + + req = trn_subproto_request_new(); + req->protocol_id = PRT_RELAY; + req->proto_cap_number = PROTOVER_RELAY_CRYPT_CGO; + trn_subproto_request_ext_add_reqs(req_ext, req); + req = NULL; // prevent double-free + + // TODO: If we add other capabilities here, we need to make + // sure they are correctly sorted. + + ssize_t len = trn_subproto_request_ext_encoded_len(req_ext); + if (BUG(len<0)) + goto err; + if (BUG(len > UINT8_MAX)) + goto err; + + trn_extension_field_setlen_field(fld, len); + trn_extension_field_set_field_type(fld, EXT_TYPE_SUBPROTO); + trn_extension_field_set_field_len(fld, len); + uint8_t *out = trn_extension_field_getarray_field(fld); + ssize_t len2 = trn_subproto_request_ext_encode(out, len, req_ext); + if (BUG(len != len2)) + goto err; + + trn_extension_add_fields(ext, fld); + fld = NULL; // prevent double-free + + // We succeeded! + r = 0; + + err: + trn_subproto_request_ext_free(req_ext); + trn_subproto_request_free(req); + trn_extension_field_free(fld); + + return r; +} + +/** Helper: Comparison function to sort extensions. */ +static int +ext_cmp(const void *a, const void *b) +{ + const trn_extension_field_t *fa = *(trn_extension_field_t **)a; + const trn_extension_field_t *fb = *(trn_extension_field_t **)b; + uint8_t ta = trn_extension_field_get_field_type(fa); + uint8_t tb = trn_extension_field_get_field_type(fb); + if (ta < tb) + return -1; + else if (ta == tb) + return 0; + else + return 1; +} + /** * Try to generate a circuit-negotiation message for communication with a * given relay. Assumes we are using ntor v3, or some later version that @@ -2644,13 +2725,53 @@ circuit_upgrade_circuits_from_guard_wait(void) int client_circ_negotiation_message(const extend_info_t *ei, uint8_t **msg_out, - size_t *msg_len_out) + size_t *msg_len_out, + circuit_params_t *params_out) { - tor_assert(ei && msg_out && msg_len_out); + tor_assert(ei && msg_out && msg_len_out && params_out); + bool cc_enabled = false; - if (!ei->exit_supports_congestion_control) { - return -1; + *msg_out = NULL; + + trn_extension_t *ext = trn_extension_new(); + + if (ei->exit_supports_congestion_control && + congestion_control_enabled()) { + if (congestion_control_build_ext_request(ext) < 0) { + goto err; + } + cc_enabled = true; } - return congestion_control_build_ext_request(msg_out, msg_len_out); + if (cc_enabled && ei->enable_cgo) { + if (build_cgo_subproto_request(ext) < 0) { + goto err; + } + params_out->cell_fmt = RELAY_CELL_FORMAT_V1; + params_out->crypto_alg = RELAY_CRYPTO_ALG_CGO_CLIENT; + } + + size_t n_fields = trn_extension_getlen_fields(ext); + qsort(trn_extension_getarray_fields(ext), + n_fields, sizeof(trn_extension_field_t *), + ext_cmp); + + trn_extension_set_num(ext, n_fields); + + ssize_t total_len = trn_extension_encoded_len(ext); + if (BUG(total_len < 0)) + goto err; + + *msg_out = tor_malloc_zero(total_len); + *msg_len_out = total_len; + if (BUG(trn_extension_encode(*msg_out, total_len, ext) < 0)) { + goto err; + } + trn_extension_free(ext); + + return 0; + err: + trn_extension_free(ext); + tor_free(*msg_out); + return -1; } diff --git a/src/core/or/circuitbuild.h b/src/core/or/circuitbuild.h @@ -70,9 +70,11 @@ circuit_deliver_create_cell,(circuit_t *circ, const struct create_cell_t *create_cell, int relayed)); +struct circuit_params_t; int client_circ_negotiation_message(const extend_info_t *ei, uint8_t **msg_out, - size_t *msg_len_out); + size_t *msg_len_out, + struct circuit_params_t *params_out); #ifdef CIRCUITBUILD_PRIVATE STATIC circid_t get_unique_circ_id_by_chan(channel_t *chan); diff --git a/src/core/or/command.c b/src/core/or/command.c @@ -371,6 +371,7 @@ command_process_create_cell(cell_t *cell, channel_t *chan) circuit_params_t params; memset(&created_cell, 0, sizeof(created_cell)); + size_t keylen = sizeof(keys); len = onion_skin_server_handshake(ONION_HANDSHAKE_TYPE_FAST, create_cell->onionskin, create_cell->handshake_len, @@ -378,11 +379,11 @@ command_process_create_cell(cell_t *cell, channel_t *chan) NULL, created_cell.reply, sizeof(created_cell.reply), - keys, CPATH_KEY_MATERIAL_LEN, + keys, &keylen, rend_circ_nonce, &params); tor_free(create_cell); - if (len < 0) { + if (len < 0 || keylen != sizeof(keys)) { log_warn(LD_OR,"Failed to generate key material. Closing."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); return; @@ -391,6 +392,7 @@ command_process_create_cell(cell_t *cell, channel_t *chan) created_cell.handshake_len = len; if (onionskin_answer(circ, &created_cell, + RELAY_CRYPTO_ALG_TOR1, (const char *)keys, sizeof(keys), rend_circ_nonce)<0) { log_warn(LD_OR,"Failed to reply to CREATE_FAST cell. Closing."); diff --git a/src/core/or/congestion_control_common.c b/src/core/or/congestion_control_common.c @@ -998,26 +998,18 @@ congestion_control_dispatch_cc_alg(congestion_control_t *cc, * Build an extension field request to negotiate congestion control. * * If congestion control is enabled, field TRUNNEL_EXT_TYPE_CC_FIELD_REQUEST - * is created in msg_out. It is a single 0-length field that signifies that we - * want to use congestion control. The length of msg_out is provided via - * msg_len_out. + * added to ext. It is a single 0-length field that signifies that we + * want to use congestion control. * - * If congestion control is not enabled, a payload with 0 extensions is created - * and returned. + * If congestion control is not enabled, no extension is added. * * If there is a failure building the request, -1 is returned, else 0. - * - * *msg_out must be freed if the return value is 0. */ int -congestion_control_build_ext_request(uint8_t **msg_out, size_t *msg_len_out) +congestion_control_build_ext_request(trn_extension_t *ext) { - uint8_t *request = NULL; - trn_extension_t *ext = NULL; trn_extension_field_t *field = NULL; - ext = trn_extension_new(); - /* With congestion control enabled, add the request, else it is an empty * request in the payload. */ @@ -1032,30 +1024,9 @@ congestion_control_build_ext_request(uint8_t **msg_out, size_t *msg_len_out) /* Build final extension. */ trn_extension_add_fields(ext, field); - trn_extension_set_num(ext, 1); - } - - /* Encode extension. */ - ssize_t ret = trn_extension_encoded_len(ext); - if (BUG(ret < 0)) { - goto err; } - size_t request_len = ret; - request = tor_malloc_zero(request_len); - ret = trn_extension_encode(request, request_len, ext); - if (BUG(ret < 0)) { - tor_free(request); - goto err; - } - *msg_out = request; - *msg_len_out = request_len; - /* Free everything, we've encoded the request now. */ - ret = 0; - - err: - trn_extension_free(ext); - return (int)ret; + return 0; } /** @@ -1069,44 +1040,19 @@ congestion_control_build_ext_request(uint8_t **msg_out, size_t *msg_len_out) * WARNING: Called from CPU worker! Must not access any global state. */ int -congestion_control_parse_ext_request(const uint8_t *msg, const size_t msg_len) +congestion_control_parse_ext_request(const trn_extension_t *ext) { ssize_t ret = 0; - trn_extension_t *ext = NULL; - size_t num_fields = 0; - /* Parse extension from payload. */ - ret = trn_extension_parse(&ext, msg, msg_len); - if (ret < 0) { - goto end; - } - - /* No extension implies no support for congestion control. In this case, we - * simply return 0 to indicate CC is disabled. */ - if ((num_fields = trn_extension_get_num(ext)) == 0) { + if (trn_extension_find(ext, TRUNNEL_EXT_TYPE_CC_FIELD_REQUEST) == NULL) { + /* No extension implies no support for congestion control. In this case, we + * simply return 0 to indicate CC is disabled. */ ret = 0; - goto end; - } - - /* Go over all fields. If any field is TRUNNEL_EXT_TYPE_CC_FIELD_REQUEST, - * then congestion control is enabled. Ignore unknown fields. */ - for (size_t f = 0; f < num_fields; f++) { - const trn_extension_field_t *field = trn_extension_get_fields(ext, f); - if (field == NULL) { - ret = -1; - goto end; - } - + } else { /* For congestion control to be enabled, we only need the field type. */ - if (trn_extension_field_get_field_type(field) == - TRUNNEL_EXT_TYPE_CC_FIELD_REQUEST) { - ret = 1; - break; - } + ret = 1; } - end: - trn_extension_free(ext); return (int)ret; } @@ -1228,13 +1174,11 @@ congestion_control_validate_sendme_increment(uint8_t sendme_inc) /** Return 1 if CC is enabled which also will set the SENDME increment into our * params_out. Return 0 if CC is disabled. Else, return -1 on error. */ int -congestion_control_parse_ext_response(const uint8_t *msg, - const size_t msg_len, +congestion_control_parse_ext_response(const trn_extension_t *ext, circuit_params_t *params_out) { ssize_t ret = 0; - size_t num_fields = 0; - trn_extension_t *ext = NULL; + const trn_extension_field_t *field = NULL; trn_extension_field_cc_t *cc_field = NULL; /* We will only accept this response (and this circuit) if sendme_inc @@ -1244,30 +1188,11 @@ congestion_control_parse_ext_response(const uint8_t *msg, * violate this range should just not be used. */ #define MAX_SENDME_INC_NEGOTIATE_FACTOR 2 - /* Parse extension from payload. */ - ret = trn_extension_parse(&ext, msg, msg_len); - if (ret < 0) { - goto end; - } + field = trn_extension_find(ext, TRUNNEL_EXT_TYPE_CC_FIELD_RESPONSE); - if ((num_fields = trn_extension_get_num(ext)) == 0) { + if (field == 0) { ret = 0; - goto end; - } - - /* Go over all fields. If any field is TRUNNEL_EXT_TYPE_CC_FIELD_RESPONSE, - * then congestion control is enabled. Ignore unknown fields. */ - for (size_t f = 0; f < num_fields; f++) { - const trn_extension_field_t *field = trn_extension_get_fields(ext, f); - if (field == NULL) { - ret = -1; - goto end; - } - - /* Only examine TRUNNEL_EXT_TYPE_CC_FIELD_RESPONSE; ignore other fields */ - if (trn_extension_field_get_field_type(field) == - TRUNNEL_EXT_TYPE_CC_FIELD_RESPONSE) { - + } else { /* Parse the field into the congestion control field. */ ret = trn_extension_field_cc_parse(&cc_field, trn_extension_field_getconstarray_field(field), @@ -1286,12 +1211,9 @@ congestion_control_parse_ext_response(const uint8_t *msg, /* All good. Get value and break */ params_out->sendme_inc_cells = sendme_inc_cells; ret = 1; - break; - } } end: - trn_extension_free(ext); trn_extension_field_cc_free(cc_field); return (int)ret; diff --git a/src/core/or/congestion_control_common.h b/src/core/or/congestion_control_common.h @@ -66,16 +66,15 @@ void congestion_control_new_consensus_params(const networkstatus_t *ns); bool congestion_control_enabled(void); -int congestion_control_build_ext_request(uint8_t **msg_out, - size_t *msg_len_out); -int congestion_control_parse_ext_request(const uint8_t *msg, - const size_t msg_len); +struct trn_extension_st; +int congestion_control_build_ext_request(struct trn_extension_st *ext); + +int congestion_control_parse_ext_request(const struct trn_extension_st *ext); int congestion_control_build_ext_response(const circuit_params_t *our_params, const circuit_params_t *circ_params, uint8_t **msg_out, size_t *msg_len_out); -int congestion_control_parse_ext_response(const uint8_t *msg, - const size_t msg_len, +int congestion_control_parse_ext_response(const struct trn_extension_st *ext, circuit_params_t *params_out); bool congestion_control_validate_sendme_increment(uint8_t sendme_inc); char *congestion_control_get_control_port_fields(const origin_circuit_t *); diff --git a/src/core/or/crypt_path.c b/src/core/or/crypt_path.c @@ -71,7 +71,7 @@ cpath_append_hop(crypt_path_t **head_ptr, extend_info_t *choice) hop->package_window = circuit_initial_package_window(); hop->deliver_window = CIRCWINDOW_START; - // TODO CGO: Initialize this from a real decision. + // This can get changed later on by circuit negotiation. hop->relay_cell_format = RELAY_CELL_FORMAT_V0; return 0; @@ -116,11 +116,8 @@ cpath_assert_layer_ok(const crypt_path_t *cp) relay_crypto_assert_ok(&cp->pvt_crypto); FALLTHROUGH; case CPATH_STATE_CLOSED: - /*XXXX Assert that there's no handshake_state either. */ - tor_assert(!cp->rend_dh_handshake_state); break; case CPATH_STATE_AWAITING_KEYS: - /* tor_assert(cp->dh_handshake_state); */ break; default: log_fn(LOG_ERR, LD_BUG, "Unexpected state %d", cp->state); @@ -148,14 +145,13 @@ cpath_assert_layer_ok(const crypt_path_t *cp) * Return 0 if init was successful, else -1 if it failed. */ int -cpath_init_circuit_crypto(crypt_path_t *cpath, - const char *key_data, size_t key_data_len, - int reverse, int is_hs_v3) +cpath_init_circuit_crypto(relay_crypto_alg_t alg, + crypt_path_t *cpath, + const char *key_data, size_t key_data_len) { tor_assert(cpath); - return relay_crypto_init(&cpath->pvt_crypto, key_data, key_data_len, - reverse, is_hs_v3); + return relay_crypto_init(alg, &cpath->pvt_crypto, key_data, key_data_len); } /** Deallocate space associated with the cpath node <b>victim</b>. */ @@ -167,7 +163,6 @@ cpath_free(crypt_path_t *victim) relay_crypto_clear(&victim->pvt_crypto); onion_handshake_state_release(&victim->handshake_state); - crypto_dh_free(victim->rend_dh_handshake_state); extend_info_free(victim->extend_info); congestion_control_free(victim->ccontrol); @@ -175,51 +170,14 @@ cpath_free(crypt_path_t *victim) tor_free(victim); } -/********************** cpath crypto API *******************************/ - -/** Encrypt or decrypt <b>payload</b> using the crypto of <b>cpath</b>. Actual - * operation decided by <b>is_decrypt</b>. */ -void -cpath_crypt_cell(const crypt_path_t *cpath, uint8_t *payload, bool is_decrypt) -{ - if (is_decrypt) { - relay_crypt_one_payload(cpath->pvt_crypto.b_crypto, payload); - } else { - relay_crypt_one_payload(cpath->pvt_crypto.f_crypto, payload); - } -} - -/** Getter for the incoming digest of <b>cpath</b>. */ -struct crypto_digest_t * -cpath_get_incoming_digest(const crypt_path_t *cpath) -{ - return cpath->pvt_crypto.b_digest; -} - -/** Set the right integrity digest on the outgoing <b>cell</b> based on the - * cell payload and update the forward digest of <b>cpath</b>. */ -void -cpath_set_cell_forward_digest(crypt_path_t *cpath, cell_t *cell) -{ - relay_set_digest_v0(cpath->pvt_crypto.f_digest, cell); -} - /************ cpath sendme API ***************************/ -/** Return the sendme_digest of this <b>cpath</b>. */ -uint8_t * -cpath_get_sendme_digest(crypt_path_t *cpath) +/** Return the sendme tag of this <b>cpath</b>, + * along with its length. */ +const uint8_t * +cpath_get_sendme_tag(crypt_path_t *cpath, size_t *len_out) { - return relay_crypto_get_sendme_digest(&cpath->pvt_crypto); -} - -/** Record the cell digest, indicated by is_foward_digest or not, as the - * SENDME cell digest. */ -void -cpath_sendme_record_cell_digest(crypt_path_t *cpath, bool is_foward_digest) -{ - tor_assert(cpath); - relay_crypto_record_sendme_digest(&cpath->pvt_crypto, is_foward_digest); + return relay_crypto_get_sendme_tag(&cpath->pvt_crypto, len_out); } /************ other cpath functions ***************************/ diff --git a/src/core/or/crypt_path.h b/src/core/or/crypt_path.h @@ -6,38 +6,28 @@ #ifndef CRYPT_PATH_H #define CRYPT_PATH_H +#include "core/crypto/relay_crypto.h" + void cpath_assert_layer_ok(const crypt_path_t *cp); void cpath_assert_ok(const crypt_path_t *cp); int cpath_append_hop(crypt_path_t **head_ptr, extend_info_t *choice); -int cpath_init_circuit_crypto(crypt_path_t *cpath, - const char *key_data, size_t key_data_len, - int reverse, int is_hs_v3); +int cpath_init_circuit_crypto(relay_crypto_alg_t alg, + crypt_path_t *cpath, + const char *key_data, size_t key_data_len); void cpath_free(crypt_path_t *victim); void cpath_extend_linked_list(crypt_path_t **head_ptr, crypt_path_t *new_hop); -void -cpath_crypt_cell(const crypt_path_t *cpath, uint8_t *payload, bool is_decrypt); - -struct crypto_digest_t * -cpath_get_incoming_digest(const crypt_path_t *cpath); - -void cpath_sendme_record_cell_digest(crypt_path_t *cpath, - bool is_foward_digest); - -void -cpath_set_cell_forward_digest(crypt_path_t *cpath, cell_t *cell); - crypt_path_t *cpath_get_next_non_open_hop(crypt_path_t *cpath); void cpath_sendme_circuit_record_inbound_cell(crypt_path_t *cpath); -uint8_t *cpath_get_sendme_digest(crypt_path_t *cpath); +const uint8_t *cpath_get_sendme_tag(crypt_path_t *cpath, size_t *len_out); #if defined(TOR_UNIT_TESTS) unsigned int cpath_get_n_hops(crypt_path_t **head_ptr); diff --git a/src/core/or/crypt_path_st.h b/src/core/or/crypt_path_st.h @@ -12,18 +12,20 @@ #ifndef CRYPT_PATH_ST_H #define CRYPT_PATH_ST_H -#include "core/or/relay_crypto_st.h" -struct crypto_dh_t; +#include "core/crypto/relay_crypto_st.h" +#include "core/crypto/onion_crypto.h" #define CRYPT_PATH_MAGIC 0x70127012u struct fast_handshake_state_t; struct ntor_handshake_state_t; -struct crypto_dh_t; struct onion_handshake_state_t { /** One of `ONION_HANDSHAKE_TYPE_*`. Determines which member of the union * is accessible. */ uint16_t tag; + /** Initial circuit parameters (selected during first stage of negotiation; + * may be changed based on response from relay). */ + circuit_params_t chosen_params; union { struct fast_handshake_state_t *fast; struct ntor_handshake_state_t *ntor; @@ -54,9 +56,6 @@ struct crypt_path_t { /** Current state of the handshake as performed with the OR at this * step. */ onion_handshake_state_t handshake_state; - /** Diffie-hellman handshake state for performing an introduction - * operations */ - struct crypto_dh_t *rend_dh_handshake_state; /** Negotiated key material shared with the OR at this step. */ char rend_circ_nonce[DIGEST_LEN];/* KH in tor-spec.txt */ diff --git a/src/core/or/extend_info_st.h b/src/core/or/extend_info_st.h @@ -36,10 +36,17 @@ struct extend_info_t { tor_addr_port_t orports[EXTEND_INFO_MAX_ADDRS]; /** Ntor onion key for this hop. */ curve25519_public_key_t curve25519_onion_key; + /** True if this hop supports ntor v3. */ + bool supports_ntor_v3; /** True if this hop is to be used as an _exit_, * and it also supports supports NtorV3 _and_ negotiation * of congestion control parameters */ bool exit_supports_congestion_control; + /** + * True if this hop supports CGO relay message enryption, + * and we intend to use it. + */ + bool enable_cgo; }; #endif /* !defined(EXTEND_INFO_ST_H) */ diff --git a/src/core/or/extendinfo.c b/src/core/or/extendinfo.c @@ -61,6 +61,11 @@ extend_info_new(const char *nickname, pv->supports_congestion_control; } + if (pv) { + info->supports_ntor_v3 = pv->supports_ntor_v3; + info->enable_cgo = pv->supports_cgo; + } + return info; } @@ -206,8 +211,7 @@ int extend_info_supports_ntor_v3(const extend_info_t *ei) { tor_assert(ei); - return extend_info_supports_ntor(ei) && - ei->exit_supports_congestion_control; + return ei->supports_ntor_v3; } /* Does ei have an onion key which it would prefer to use? diff --git a/src/core/or/include.am b/src/core/or/include.am @@ -105,7 +105,6 @@ noinst_HEADERS += \ src/core/or/protover.h \ src/core/or/reasons.h \ src/core/or/relay.h \ - src/core/or/relay_crypto_st.h \ src/core/or/relay_msg.h \ src/core/or/relay_msg_st.h \ src/core/or/scheduler.h \ diff --git a/src/core/or/or.h b/src/core/or/or.h @@ -451,6 +451,11 @@ typedef enum { /** Amount to increment a stream window when we get a stream SENDME. */ #define STREAMWINDOW_INCREMENT 50 +/** Length for authenticated sendme tag with tor1 encryption. */ +#define SENDME_TAG_LEN_TOR1 20 +/** Length for authenticated sendme tag with cgo encryption. */ +#define SENDME_TAG_LEN_CGO 16 + /** Maximum number of queued cells on a circuit for which we are the * midpoint before we give up and kill it. This must be >= circwindow * to avoid killing innocent circuits, and >= circwindow*2 to give @@ -829,8 +834,14 @@ typedef struct protover_summary_flags_t { * Requires both FlowCtrl=2 *and* Relay=4 */ unsigned int supports_congestion_control : 1; - /** True iff this router supports conflux. Requires Relay=5 */ + /** True iff this router supports conflux. */ unsigned int supports_conflux : 1; + + /** True iff this router supports CGO. */ + unsigned int supports_cgo : 1; + + /** True iff this router supports ntorv3 */ + unsigned int supports_ntor_v3 : 1; } protover_summary_flags_t; typedef struct routerinfo_t routerinfo_t; diff --git a/src/core/or/protover.c b/src/core/or/protover.c @@ -399,7 +399,7 @@ protocol_list_supports_protocol_or_later(const char *list, #define PR_LINKAUTH_V "3" #define PR_MICRODESC_V "1-3" #define PR_PADDING_V "2" -#define PR_RELAY_V "2-4" +#define PR_RELAY_V "2-6" /** Return the string containing the supported version for the given protocol * type. */ diff --git a/src/core/or/protover.h b/src/core/or/protover.h @@ -58,6 +58,14 @@ struct smartlist_t; /** The protover that signals support for congestion control */ #define PROTOVER_FLOWCTRL_CC 2 +/** The protover required for negotiating protovers as part of the circuit + * extension handshake. */ +#define PROTOVER_RELAY_NEGOTIATE_SUBPROTO 5 + +/** The protover required for negotiating protovers as part of the circuit + * extension handshake. */ +#define PROTOVER_RELAY_CRYPT_CGO 6 + /** List of recognized subprotocols. */ /// C_RUST_COUPLED: src/rust/protover/ffi.rs `translate_to_rust` /// C_RUST_COUPLED: src/rust/protover/protover.rs `Proto` diff --git a/src/core/or/relay.c b/src/core/or/relay.c @@ -265,10 +265,6 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, edge_connection_t *conn = NULL; relay_cell_fmt_t format = circuit_get_relay_format(circ, layer_hint); - /* Recognized cell, the cell digest has been updated, we'll record it for - * the SENDME if need be. */ - sendme_record_received_cell_digest(circ, layer_hint); - relay_msg_t msg_buf; if (relay_msg_decode_cell_in_place(format, cell, &msg_buf) < 0) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, diff --git a/src/core/or/relay_crypto_st.h b/src/core/or/relay_crypto_st.h @@ -1,38 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2021, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * @file relay_crypto_st.h - * @brief Relay-cell encryption state structure. - **/ - -#ifndef RELAY_CRYPTO_ST_H -#define RELAY_CRYPTO_ST_H - -#define crypto_cipher_t aes_cnt_cipher_t -struct crypto_cipher_t; -struct crypto_digest_t; - -struct relay_crypto_t { - /* crypto environments */ - /** Encryption key and counter for cells heading towards the OR at this - * step. */ - struct crypto_cipher_t *f_crypto; - /** Encryption key and counter for cells heading back from the OR at this - * step. */ - struct crypto_cipher_t *b_crypto; - - /** Digest state for cells heading towards the OR at this step. */ - struct crypto_digest_t *f_digest; /* for integrity checking */ - /** Digest state for cells heading away from the OR at this step. */ - struct crypto_digest_t *b_digest; - - /** Digest used for the next SENDME cell if any. */ - uint8_t sendme_digest[DIGEST_LEN]; -}; -#undef crypto_cipher_t - -#endif /* !defined(RELAY_CRYPTO_ST_H) */ diff --git a/src/core/or/sendme.c b/src/core/or/sendme.c @@ -7,7 +7,9 @@ * creating/parsing cells and handling the content. */ +// For access to cpath pvt_crypto field. #define SENDME_PRIVATE +#define CRYPT_PATH_PRIVATE #include "core/or/or.h" @@ -27,6 +29,15 @@ #include "lib/ctime/di_ops.h" #include "trunnel/sendme_cell.h" +/** + * Return true iff tag_len is some length we recognize. + */ +static inline bool +tag_len_ok(size_t tag_len) +{ + return tag_len == SENDME_TAG_LEN_CGO || tag_len == SENDME_TAG_LEN_TOR1; +} + /* Return the minimum version given by the consensus (if any) that should be * used when emitting a SENDME cell. */ STATIC int @@ -71,17 +82,18 @@ pop_first_cell_digest(const circuit_t *circ) return circ_digest; } -/* Return true iff the given cell digest matches the first digest in the +/* Return true iff the given cell tag matches the first digest in the * circuit sendme list. */ static bool -v1_digest_matches(const uint8_t *circ_digest, const uint8_t *cell_digest) +v1_tag_matches(const uint8_t *circ_digest, + const uint8_t *cell_tag, size_t tag_len) { tor_assert(circ_digest); - tor_assert(cell_digest); + tor_assert(cell_tag); /* Compare the digest with the one in the SENDME. This cell is invalid * without a perfect match. */ - if (tor_memneq(circ_digest, cell_digest, TRUNNEL_SENDME_V1_DIGEST_LEN)) { + if (tor_memneq(circ_digest, cell_tag, tag_len)) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "SENDME v1 cell digest do not match."); return false; @@ -98,13 +110,22 @@ v1_digest_matches(const uint8_t *circ_digest, const uint8_t *cell_digest) * cell we saw which tells us that the other side has in fact seen that cell. * See proposal 289 for more details. */ static bool -cell_v1_is_valid(const sendme_cell_t *cell, const uint8_t *circ_digest) +cell_v1_is_valid(const sendme_cell_t *cell, const uint8_t *circ_digest, + size_t circ_digest_len) { tor_assert(cell); tor_assert(circ_digest); + size_t tag_len = sendme_cell_get_data_len(cell); + if (! tag_len_ok(tag_len)) + return false; + if (sendme_cell_getlen_data_v1_digest(cell) < tag_len) + return false; + if (tag_len != circ_digest_len) + return false; + const uint8_t *cell_digest = sendme_cell_getconstarray_data_v1_digest(cell); - return v1_digest_matches(circ_digest, cell_digest); + return v1_tag_matches(circ_digest, cell_digest, tag_len); } /* Return true iff the given cell version can be handled or if the minimum @@ -156,8 +177,16 @@ cell_version_can_be_handled(uint8_t cell_version) * This is the main critical function to make sure we can continue to * send/recv cells on a circuit. If the SENDME is invalid, the circuit should * be marked for close by the caller. */ +/* + * NOTE: This function uses `layer_hint` to determine + * what the sendme tag length will be, and nothing else. + * Notably, we _don't_ keep a separate queue + * of expected tags for each layer! + */ STATIC bool -sendme_is_valid(const circuit_t *circ, const uint8_t *cell_payload, +sendme_is_valid(const circuit_t *circ, + const crypt_path_t *layer_hint, + const uint8_t *cell_payload, size_t cell_payload_len) { uint8_t cell_version; @@ -186,6 +215,19 @@ sendme_is_valid(const circuit_t *circ, const uint8_t *cell_payload, goto invalid; } + /* Determine the expected tag length for this sendme. */ + size_t circ_expects_tag_len; + if (layer_hint) { + circ_expects_tag_len = + relay_crypto_sendme_tag_len(&layer_hint->pvt_crypto); + } else if (CIRCUIT_IS_ORCIRC(circ)) { + const or_circuit_t *or_circ = CONST_TO_OR_CIRCUIT(circ); + circ_expects_tag_len = relay_crypto_sendme_tag_len(&or_circ->crypto); + } else { + tor_assert_nonfatal_unreached(); + goto invalid; + } + /* Pop the first element that was added (FIFO). We do that regardless of the * version so we don't accumulate on the circuit if v0 is used by the other * end point. */ @@ -198,12 +240,10 @@ sendme_is_valid(const circuit_t *circ, const uint8_t *cell_payload, "We received a SENDME but we have no cell digests to match. " "Closing circuit."); goto invalid; - } - - /* Validate depending on the version now. */ + } /* Validate depending on the version now. */ switch (cell_version) { case 0x01: - if (!cell_v1_is_valid(cell, circ_digest)) { + if (!cell_v1_is_valid(cell, circ_digest, circ_expects_tag_len)) { goto invalid; } break; @@ -234,12 +274,14 @@ sendme_is_valid(const circuit_t *circ, const uint8_t *cell_payload, * Return the size in bytes of the encoded cell in payload. A negative value * is returned on encoding failure. */ STATIC ssize_t -build_cell_payload_v1(const uint8_t *cell_digest, uint8_t *payload) +build_cell_payload_v1(const uint8_t *cell_tag, const size_t tag_len, + uint8_t *payload) { ssize_t len = -1; sendme_cell_t *cell = NULL; - tor_assert(cell_digest); + tor_assert(cell_tag); + tor_assert(tag_len_ok(tag_len)); tor_assert(payload); cell = sendme_cell_new(); @@ -247,11 +289,11 @@ build_cell_payload_v1(const uint8_t *cell_digest, uint8_t *payload) /* Building a payload for version 1. */ sendme_cell_set_version(cell, 0x01); /* Set the data length field for v1. */ - sendme_cell_set_data_len(cell, TRUNNEL_SENDME_V1_DIGEST_LEN); + sendme_cell_set_data_len(cell, tag_len); + sendme_cell_setlen_data_v1_digest(cell, tag_len); /* Copy the digest into the data payload. */ - memcpy(sendme_cell_getarray_data_v1_digest(cell), cell_digest, - sendme_cell_get_data_len(cell)); + memcpy(sendme_cell_getarray_data_v1_digest(cell), cell_tag, tag_len); /* Finally, encode the cell into the payload. */ len = sendme_cell_encode(payload, RELAY_PAYLOAD_SIZE_MAX, cell); @@ -267,19 +309,19 @@ build_cell_payload_v1(const uint8_t *cell_digest, uint8_t *payload) * because we failed to send the cell on it. */ static int send_circuit_level_sendme(circuit_t *circ, crypt_path_t *layer_hint, - const uint8_t *cell_digest) + const uint8_t *cell_tag, size_t tag_len) { uint8_t emit_version; uint8_t payload[RELAY_PAYLOAD_SIZE_MAX]; ssize_t payload_len; tor_assert(circ); - tor_assert(cell_digest); + tor_assert(cell_tag); emit_version = get_emit_min_version(); switch (emit_version) { case 0x01: - payload_len = build_cell_payload_v1(cell_digest, payload); + payload_len = build_cell_payload_v1(cell_tag, tag_len, payload); if (BUG(payload_len < 0)) { /* Unable to encode the cell, abort. We can recover from this by closing * the circuit but in theory it should never happen. */ @@ -307,61 +349,39 @@ send_circuit_level_sendme(circuit_t *circ, crypt_path_t *layer_hint, return 0; } -/* Record the cell digest only if the next cell is expected to be a SENDME. */ +/* Record the sendme tag as expected in a future SENDME, */ static void -record_cell_digest_on_circ(circuit_t *circ, const uint8_t *sendme_digest) +record_cell_digest_on_circ(circuit_t *circ, + const uint8_t *sendme_tag, + size_t tag_len) { tor_assert(circ); - tor_assert(sendme_digest); + tor_assert(sendme_tag); /* Add the digest to the last seen list in the circuit. */ if (circ->sendme_last_digests == NULL) { circ->sendme_last_digests = smartlist_new(); } - smartlist_add(circ->sendme_last_digests, - tor_memdup(sendme_digest, DIGEST_LEN)); + // We always allocate the largest possible tag here to + // make sure we don't have heap overflow bugs. + uint8_t *tag; + if (tag_len == SENDME_TAG_LEN_CGO) { + tag = tor_malloc_zero(SENDME_TAG_LEN_TOR1); + memcpy(tag, sendme_tag, tag_len); + // (The final bytes were initialized to zero.) + } else if (tag_len == SENDME_TAG_LEN_TOR1) { + tag = tor_memdup(sendme_tag, SENDME_TAG_LEN_TOR1); + } else { + tor_assert_unreached(); + } + + smartlist_add(circ->sendme_last_digests, tag); } /* * Public API */ -/** Return true iff the next cell for the given cell window is expected to be - * a SENDME. - * - * We are able to know that because the package or inflight window value minus - * one cell (the possible SENDME cell) should be a multiple of the - * cells-per-sendme increment value (set via consensus parameter, negotiated - * for the circuit, and passed in as sendme_inc). - * - * This function is used when recording a cell digest and this is done quite - * low in the stack when decrypting or encrypting a cell. The window is only - * updated once the cell is actually put in the outbuf. - */ -STATIC bool -circuit_sendme_cell_is_next(int deliver_window, int sendme_inc) -{ - /* Are we at the limit of the increment and if not, we don't expect next - * cell is a SENDME. - * - * We test against the window minus 1 because when we are looking if the - * next cell is a SENDME, the window (either package or deliver) hasn't been - * decremented just yet so when this is called, we are currently processing - * the "window - 1" cell. - * - * Because deliver_window starts at CIRCWINDOW_START and counts down, - * to get the actual number of received cells for this check, we must - * first convert to received cells, or the modulus operator will fail. - */ - tor_assert(deliver_window <= CIRCWINDOW_START); - if (((CIRCWINDOW_START - (deliver_window - 1)) % sendme_inc) != 0) { - return false; - } - - /* Next cell is expected to be a SENDME. */ - return true; -} - /** Called when we've just received a relay data cell, when we've just * finished flushing all bytes to stream <b>conn</b>, or when we've flushed * *some* bytes to the stream <b>conn</b>. @@ -420,7 +440,8 @@ void sendme_circuit_consider_sending(circuit_t *circ, crypt_path_t *layer_hint) { bool sent_one_sendme = false; - const uint8_t *digest; + const uint8_t *tag; + size_t tag_len = 0; int sendme_inc = sendme_get_inc_count(circ, layer_hint); while ((layer_hint ? layer_hint->deliver_window : circ->deliver_window) <= @@ -428,12 +449,13 @@ sendme_circuit_consider_sending(circuit_t *circ, crypt_path_t *layer_hint) log_debug(LD_CIRC,"Queuing circuit sendme."); if (layer_hint) { layer_hint->deliver_window += sendme_inc; - digest = cpath_get_sendme_digest(layer_hint); + tag = cpath_get_sendme_tag(layer_hint, &tag_len); } else { circ->deliver_window += sendme_inc; - digest = relay_crypto_get_sendme_digest(&TO_OR_CIRCUIT(circ)->crypto); + tag = relay_crypto_get_sendme_tag(&TO_OR_CIRCUIT(circ)->crypto, + &tag_len); } - if (send_circuit_level_sendme(circ, layer_hint, digest) < 0) { + if (send_circuit_level_sendme(circ, layer_hint, tag, tag_len) < 0) { return; /* The circuit's closed, don't continue */ } /* Current implementation is not suppose to send multiple SENDME at once @@ -470,7 +492,7 @@ sendme_process_circuit_level(crypt_path_t *layer_hint, /* Validate the SENDME cell. Depending on the version, different validation * can be done. An invalid SENDME requires us to close the circuit. */ - if (!sendme_is_valid(circ, cell_payload, cell_payload_len)) { + if (!sendme_is_valid(circ, layer_hint, cell_payload, cell_payload_len)) { return -END_CIRC_REASON_TORPROTOCOL; } @@ -697,7 +719,8 @@ sendme_note_stream_data_packaged(edge_connection_t *conn, size_t len) void sendme_record_cell_digest_on_circ(circuit_t *circ, crypt_path_t *cpath) { - uint8_t *sendme_digest; + const uint8_t *sendme_tag; + size_t tag_len = 0; tor_assert(circ); @@ -711,60 +734,11 @@ sendme_record_cell_digest_on_circ(circuit_t *circ, crypt_path_t *cpath) /* Getting the digest is expensive so we only do it once we are certain to * record it on the circuit. */ if (cpath) { - sendme_digest = cpath_get_sendme_digest(cpath); - } else { - sendme_digest = - relay_crypto_get_sendme_digest(&TO_OR_CIRCUIT(circ)->crypto); - } - - record_cell_digest_on_circ(circ, sendme_digest); -} - -/* Called once we decrypted a cell and recognized it. Record the cell digest - * as the next sendme digest only if the next cell we'll send on the circuit - * is expected to be a SENDME. */ -void -sendme_record_received_cell_digest(circuit_t *circ, crypt_path_t *cpath) -{ - tor_assert(circ); - - /* Only record if the next cell is expected to be a SENDME. */ - if (!circuit_sendme_cell_is_next(cpath ? cpath->deliver_window : - circ->deliver_window, - sendme_get_inc_count(circ, cpath))) { - return; - } - - if (cpath) { - /* Record incoming digest. */ - cpath_sendme_record_cell_digest(cpath, false); + sendme_tag = cpath_get_sendme_tag(cpath, &tag_len); } else { - /* Record forward digest. */ - relay_crypto_record_sendme_digest(&TO_OR_CIRCUIT(circ)->crypto, true); + sendme_tag = + relay_crypto_get_sendme_tag(&TO_OR_CIRCUIT(circ)->crypto, &tag_len); } -} - -/* Called once we encrypted a cell. Record the cell digest as the next sendme - * digest only if the next cell we expect to receive is a SENDME so we can - * match the digests. */ -void -sendme_record_sending_cell_digest(circuit_t *circ, crypt_path_t *cpath) -{ - tor_assert(circ); - /* Only record if the next cell is expected to be a SENDME. */ - if (!circuit_sent_cell_for_sendme(circ, cpath)) { - goto end; - } - - if (cpath) { - /* Record the forward digest. */ - cpath_sendme_record_cell_digest(cpath, true); - } else { - /* Record the incoming digest. */ - relay_crypto_record_sendme_digest(&TO_OR_CIRCUIT(circ)->crypto, false); - } - - end: - return; + record_cell_digest_on_circ(circ, sendme_tag, tag_len); } diff --git a/src/core/or/sendme.h b/src/core/or/sendme.h @@ -37,9 +37,6 @@ int sendme_note_stream_data_packaged(edge_connection_t *conn, size_t len); /* Record cell digest on circuit. */ void sendme_record_cell_digest_on_circ(circuit_t *circ, crypt_path_t *cpath); -/* Record cell digest as the SENDME digest. */ -void sendme_record_received_cell_digest(circuit_t *circ, crypt_path_t *cpath); -void sendme_record_sending_cell_digest(circuit_t *circ, crypt_path_t *cpath); /* Private section starts. */ #ifdef SENDME_PRIVATE @@ -68,13 +65,13 @@ STATIC int get_accept_min_version(void); STATIC bool cell_version_can_be_handled(uint8_t cell_version); -STATIC ssize_t build_cell_payload_v1(const uint8_t *cell_digest, +STATIC ssize_t build_cell_payload_v1(const uint8_t *cell_tag, + size_t tag_len, uint8_t *payload); STATIC bool sendme_is_valid(const circuit_t *circ, + const crypt_path_t *layer_hint, const uint8_t *cell_payload, size_t cell_payload_len); -STATIC bool circuit_sendme_cell_is_next(int deliver_window, - int sendme_inc); #endif /* defined(TOR_UNIT_TESTS) */ diff --git a/src/core/or/versions.c b/src/core/or/versions.c @@ -495,6 +495,19 @@ memoize_protover_summary(protover_summary_flags_t *out, protocol_list_supports_protocol(protocols, PRT_CONFLUX, PROTOVER_CONFLUX_V1); + out->supports_ntor_v3 = + protocol_list_supports_protocol(protocols, PRT_RELAY, + PROTOVER_RELAY_NTOR_V3); + + /* CGO requires congestion control and subproto negotiation. */ + out->supports_cgo = + protocol_list_supports_protocol(protocols, PRT_FLOWCTRL, + PROTOVER_FLOWCTRL_CC) && + protocol_list_supports_protocol(protocols, PRT_RELAY, + PROTOVER_RELAY_NEGOTIATE_SUBPROTO) && + protocol_list_supports_protocol(protocols, PRT_RELAY, + PROTOVER_RELAY_CRYPT_CGO); + protover_summary_flags_t *new_cached = tor_memdup(out, sizeof(*out)); cached = strmap_set(protover_summary_map, protocols, new_cached); tor_assert(!cached); diff --git a/src/feature/hs/hs_circuit.c b/src/feature/hs/hs_circuit.c @@ -112,9 +112,10 @@ create_rend_cpath(const uint8_t *ntor_key_seed, size_t seed_len, // TODO CGO: Pick relay cell format based on capabilities. cpath->relay_cell_format = RELAY_CELL_FORMAT_V0; + relay_crypto_alg_t alg = is_service_side ? RELAY_CRYPTO_ALG_TOR1_HSS : + RELAY_CRYPTO_ALG_TOR1_HSC; - if (cpath_init_circuit_crypto(cpath, (char*)keys, sizeof(keys), - is_service_side, 1) < 0) { + if (cpath_init_circuit_crypto(alg, cpath, (char*)keys, sizeof(keys)) < 0) { tor_free(cpath); goto err; } diff --git a/src/feature/nodelist/nodelist.c b/src/feature/nodelist/nodelist.c @@ -1239,7 +1239,7 @@ node_ed25519_id_matches(const node_t *node, const ed25519_public_key_t *id) /** Dummy object that should be unreturnable. Used to ensure that * node_get_protover_summary_flags() always returns non-NULL. */ static const protover_summary_flags_t zero_protover_flags = { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; /** Return the protover_summary_flags for a given node. */ diff --git a/src/feature/relay/circuitbuild_relay.c b/src/feature/relay/circuitbuild_relay.c @@ -535,6 +535,7 @@ circuit_extend(const relay_msg_t *rmsg, struct circuit_t *circ) int onionskin_answer(struct or_circuit_t *circ, const created_cell_t *created_cell, + relay_crypto_alg_t crypto_alg, const char *keys, size_t keys_len, const uint8_t *rend_circ_nonce) { @@ -556,8 +557,6 @@ onionskin_answer(struct or_circuit_t *circ, return -1; } - tor_assert(keys_len == CPATH_KEY_MATERIAL_LEN); - if (created_cell_format(&cell, created_cell) < 0) { log_warn(LD_BUG,"couldn't format created cell (type=%d, len=%d).", (int)created_cell->cell_type, (int)created_cell->handshake_len); @@ -567,10 +566,8 @@ onionskin_answer(struct or_circuit_t *circ, circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OPEN); - log_debug(LD_CIRC,"init digest forward 0x%.8x, backward 0x%.8x.", - (unsigned int)get_uint32(keys), - (unsigned int)get_uint32(keys+20)); - if (relay_crypto_init(&circ->crypto, keys, keys_len, 0, 0)<0) { + if (relay_crypto_init(crypto_alg, + &circ->crypto, keys, keys_len)<0) { log_warn(LD_BUG,"Circuit initialization failed."); return -1; } diff --git a/src/feature/relay/circuitbuild_relay.h b/src/feature/relay/circuitbuild_relay.h @@ -15,6 +15,7 @@ #include "lib/cc/torint.h" #include "lib/log/log.h" #include "core/or/relay_msg_st.h" +#include "core/crypto/relay_crypto.h" #include "app/config/config.h" @@ -39,6 +40,7 @@ int circuit_extend(const relay_msg_t *msg, struct circuit_t *circ); int onionskin_answer(struct or_circuit_t *circ, const struct created_cell_t *created_cell, + relay_crypto_alg_t crypto_alg, const char *keys, size_t keys_len, const uint8_t *rend_circ_nonce); @@ -56,11 +58,13 @@ circuit_extend(const relay_msg_t *msg, struct circuit_t *circ) static inline int onionskin_answer(struct or_circuit_t *circ, const struct created_cell_t *created_cell, + relay_crypto_alg_t crypto_alg, const char *keys, size_t keys_len, const uint8_t *rend_circ_nonce) { (void)circ; (void)created_cell; + (void)crypto_alg; (void)keys; (void)keys_len; (void)rend_circ_nonce; diff --git a/src/test/bench.c b/src/test/bench.c @@ -545,13 +545,11 @@ bench_cell_ops_tor1(void) or_circ->base_.purpose = CIRCUIT_PURPOSE_OR; /* Initialize crypto */ - char key1[CIPHER_KEY_LEN], key2[CIPHER_KEY_LEN]; - crypto_rand(key1, sizeof(key1)); - crypto_rand(key2, sizeof(key2)); - or_circ->crypto.f_crypto = crypto_cipher_new(key1); - or_circ->crypto.b_crypto = crypto_cipher_new(key2); - or_circ->crypto.f_digest = crypto_digest_new(); - or_circ->crypto.b_digest = crypto_digest_new(); + char keys[CPATH_KEY_MATERIAL_LEN]; + crypto_rand(keys, sizeof(keys)); + size_t keylen = sizeof(keys); + relay_crypto_init(RELAY_CRYPTO_ALG_TOR1, + &or_circ->crypto, keys, keylen); reset_perftime(); diff --git a/src/test/fakecircs.c b/src/test/fakecircs.c @@ -22,7 +22,7 @@ #include "core/or/conflux.h" #include "core/or/crypt_path.h" #include "core/or/relay.h" -#include "core/or/relay_crypto_st.h" +#include "core/crypto/relay_crypto_st.h" #include "test/fakecircs.h" @@ -59,8 +59,9 @@ new_fake_orcirc(channel_t *nchan, channel_t *pchan) cell_queue_init(&(orcirc->p_chan_cells)); memset(&tmp_cpath, 0, sizeof(tmp_cpath)); - if (cpath_init_circuit_crypto(&tmp_cpath, whatevs_key, - sizeof(whatevs_key), 0, 0)<0) { + if (cpath_init_circuit_crypto(RELAY_CRYPTO_ALG_TOR1, + &tmp_cpath, whatevs_key, + sizeof(whatevs_key))<0) { log_warn(LD_BUG,"Circuit initialization failed"); return NULL; } diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c @@ -1532,6 +1532,7 @@ test_onionskin_answer(void *arg) /* Circuit must be non-NULL */ tor_capture_bugs_(1); tt_int_op(onionskin_answer(NULL, created_cell, + RELAY_CRYPTO_ALG_TOR1, keys, CPATH_KEY_MATERIAL_LEN, rend_circ_nonce), OP_EQ, -1); tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); @@ -1543,6 +1544,7 @@ test_onionskin_answer(void *arg) /* Created cell must be non-NULL */ tor_capture_bugs_(1); tt_int_op(onionskin_answer(or_circ, NULL, + RELAY_CRYPTO_ALG_TOR1, keys, CPATH_KEY_MATERIAL_LEN, rend_circ_nonce), OP_EQ, -1); tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); @@ -1554,6 +1556,7 @@ test_onionskin_answer(void *arg) /* Keys must be non-NULL */ tor_capture_bugs_(1); tt_int_op(onionskin_answer(or_circ, created_cell, + RELAY_CRYPTO_ALG_TOR1, NULL, CPATH_KEY_MATERIAL_LEN, rend_circ_nonce), OP_EQ, -1); tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); @@ -1565,6 +1568,7 @@ test_onionskin_answer(void *arg) /* The rend circuit nonce must be non-NULL */ tor_capture_bugs_(1); tt_int_op(onionskin_answer(or_circ, created_cell, + RELAY_CRYPTO_ALG_TOR1, keys, CPATH_KEY_MATERIAL_LEN, NULL), OP_EQ, -1); tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); @@ -1579,6 +1583,7 @@ test_onionskin_answer(void *arg) /* Fail when formatting the created cell */ tt_int_op(onionskin_answer(or_circ, created_cell, + RELAY_CRYPTO_ALG_TOR1, keys, CPATH_KEY_MATERIAL_LEN, rend_circ_nonce), OP_EQ, -1); expect_log_msg("couldn't format created cell (type=0, len=0).\n"); diff --git a/src/test/test_circuitpadding.c b/src/test/test_circuitpadding.c @@ -1623,7 +1623,8 @@ simulate_single_hop_extend(circuit_t *client, circuit_t *mid_relay, digest, NULL, NULL, &addr, padding, NULL, false); - cpath_init_circuit_crypto(hop, whatevs_key, sizeof(whatevs_key), 0, 0); + cpath_init_circuit_crypto(RELAY_CRYPTO_ALG_TOR1, hop, + whatevs_key, sizeof(whatevs_key)); hop->package_window = circuit_initial_package_window(); hop->deliver_window = CIRCWINDOW_START; diff --git a/src/test/test_conflux_pool.c b/src/test/test_conflux_pool.c @@ -359,7 +359,8 @@ simulate_single_hop_extend(origin_circuit_t *client, int exit) digest, NULL, NULL, &addr, exit, NULL, exit); - cpath_init_circuit_crypto(hop, whatevs_key, sizeof(whatevs_key), 0, 0); + cpath_init_circuit_crypto(RELAY_CRYPTO_ALG_TOR1, hop, + whatevs_key, sizeof(whatevs_key)); hop->package_window = circuit_initial_package_window(); hop->deliver_window = CIRCWINDOW_START; diff --git a/src/test/test_crypto_cgo.c b/src/test/test_crypto_cgo.c @@ -255,6 +255,8 @@ test_crypto_cgo_uiv_testvec(void *arg) cgo_uiv_clear(&uiv); } +#include "core/crypto/relay_crypto_st.h" + static void test_crypto_cgo_uiv_update_testvec(void *arg) { @@ -333,7 +335,7 @@ test_crypto_cgo_fwd(void *arg) for (int trial = 0; trial < 64; ++trial) { int target_hop = crypto_rand_int(3); cell_t cell, cell_orig; - uint8_t tag_client[CGO_TAG_LEN]; + uint8_t tag_client[SENDME_TAG_LEN_CGO]; const uint8_t *tagp = NULL; memset(&cell, 0, sizeof(cell)); @@ -342,14 +344,14 @@ test_crypto_cgo_fwd(void *arg) } else { cell.command = CELL_RELAY_EARLY; } - crypto_rand((char*) cell.payload+CGO_TAG_LEN, - sizeof(cell.payload)-CGO_TAG_LEN); + crypto_rand((char*) cell.payload+SENDME_TAG_LEN_CGO, + sizeof(cell.payload)-SENDME_TAG_LEN_CGO); memcpy(&cell_orig, &cell, sizeof(cell)); // First the client encrypts the cell... cgo_crypt_client_originate(client[target_hop], &cell, &tagp); tt_assert(tagp); - memcpy(tag_client, tagp, CGO_TAG_LEN); + memcpy(tag_client, tagp, SENDME_TAG_LEN_CGO); for (int i = target_hop - 1; i >= 0; --i) { cgo_crypt_client_forward(client[i], &cell); } @@ -361,16 +363,16 @@ test_crypto_cgo_fwd(void *arg) cgo_crypt_relay_forward(relays[i], &cell, &tagp); if (tagp) { tt_int_op(i, OP_EQ, target_hop); - tt_mem_op(tagp, OP_EQ, tag_client, CGO_TAG_LEN); + tt_mem_op(tagp, OP_EQ, tag_client, SENDME_TAG_LEN_CGO); cell_recognized = true; break; } } tt_assert(cell_recognized); tt_int_op(cell.command, OP_EQ, cell_orig.command); - tt_mem_op(cell.payload + CGO_TAG_LEN, OP_EQ, - cell_orig.payload + CGO_TAG_LEN, - sizeof(cell.payload) - CGO_TAG_LEN); + tt_mem_op(cell.payload + SENDME_TAG_LEN_CGO, OP_EQ, + cell_orig.payload + SENDME_TAG_LEN_CGO, + sizeof(cell.payload) - SENDME_TAG_LEN_CGO); } for (int i = 0; i < N_HOPS; ++i) { cgo_crypt_free(client[i]); @@ -414,19 +416,19 @@ test_crypto_cgo_rev(void *arg) for (int trial = 0; trial < 64; ++trial) { int origin_hop = crypto_rand_int(3); cell_t cell, cell_orig; - uint8_t tag_relay[CGO_TAG_LEN]; + uint8_t tag_relay[SENDME_TAG_LEN_CGO]; const uint8_t *tagp = NULL; memset(&cell, 0, sizeof(cell)); cell.command = CELL_RELAY; - crypto_rand((char*) cell.payload+CGO_TAG_LEN, - sizeof(cell.payload)-CGO_TAG_LEN); + crypto_rand((char*) cell.payload+SENDME_TAG_LEN_CGO, + sizeof(cell.payload)-SENDME_TAG_LEN_CGO); memcpy(&cell_orig, &cell, sizeof(cell)); // First the specified relay encrypts the cell... cgo_crypt_relay_originate(relays[origin_hop], &cell, &tagp); tt_assert(tagp); - memcpy(tag_relay, tagp, CGO_TAG_LEN); + memcpy(tag_relay, tagp, SENDME_TAG_LEN_CGO); for (int i = origin_hop - 1; i >= 0; --i) { cgo_crypt_relay_backward(relays[i], &cell); } @@ -438,16 +440,16 @@ test_crypto_cgo_rev(void *arg) cgo_crypt_client_backward(client[i], &cell, &tagp); if (tagp) { tt_int_op(i, OP_EQ, origin_hop); - tt_mem_op(tagp, OP_EQ, tag_relay, CGO_TAG_LEN); + tt_mem_op(tagp, OP_EQ, tag_relay, SENDME_TAG_LEN_CGO); cell_recognized = true; break; } } tt_assert(cell_recognized); tt_int_op(cell.command, OP_EQ, cell_orig.command); - tt_mem_op(cell.payload + CGO_TAG_LEN, OP_EQ, - cell_orig.payload + CGO_TAG_LEN, - sizeof(cell.payload) - CGO_TAG_LEN); + tt_mem_op(cell.payload + SENDME_TAG_LEN_CGO, OP_EQ, + cell_orig.payload + SENDME_TAG_LEN_CGO, + sizeof(cell.payload) - SENDME_TAG_LEN_CGO); } for (int i = 0; i < N_HOPS; ++i) { cgo_crypt_free(client[i]); @@ -472,7 +474,8 @@ test_crypto_cgo_relay_testvec(void *arg) for (int i = 0; i < (int)ARRAY_LENGTH(CGO_RELAY_TESTVECS); ++i) { const struct cgo_relay_testvec *tv = &CGO_RELAY_TESTVECS[i]; const int aesbits = 128; - uint8_t keys[80], expect_keys[80], expect_tprime[CGO_TAG_LEN], cmd[1]; + uint8_t keys[80], expect_keys[80], expect_tprime[SENDME_TAG_LEN_CGO], + cmd[1]; cell_t cell; cell_t expect_cell; tt_int_op(sizeof(keys), OP_EQ, cgo_key_material_len(aesbits)); @@ -525,7 +528,8 @@ test_crypto_cgo_relay_originate_testvec(void *arg) const struct cgo_relay_originate_testvec *tv = &CGO_RELAY_ORIGINATE_TESTVECS[i]; const int aesbits = 128; - uint8_t keys[80], expect_keys[80], expect_tprime[CGO_TAG_LEN], cmd[1]; + uint8_t keys[80], expect_keys[80], expect_tprime[SENDME_TAG_LEN_CGO], + cmd[1]; cell_t cell; cell_t expect_cell; tt_int_op(sizeof(keys), OP_EQ, cgo_key_material_len(aesbits)); @@ -573,7 +577,8 @@ test_crypto_cgo_client_originate_testvec(void *arg) const struct cgo_client_originate_testvec *tv = &CGO_CLIENT_ORIGINATE_TESTVECS[tv_i]; const int aesbits = 128; - uint8_t keys[80], expect_keys[80], expect_tprime[CGO_TAG_LEN], cmd[1]; + uint8_t keys[80], expect_keys[80], expect_tprime[SENDME_TAG_LEN_CGO], + cmd[1]; cell_t cell; cell_t expect_cell; for (int i = 0; i < 3; ++i) { diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c @@ -246,12 +246,14 @@ test_e2e_rend_circuit_setup(void *arg) tt_int_op(retval, OP_EQ, 1); /* Check that the crypt path has prop224 algorithm parameters */ - tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->pvt_crypto.f_digest), + tt_int_op(crypto_digest_get_algorithm( + or_circ->cpath->pvt_crypto.c.tor1.f_digest), OP_EQ, DIGEST_SHA3_256); - tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->pvt_crypto.b_digest), + tt_int_op(crypto_digest_get_algorithm( + or_circ->cpath->pvt_crypto.c.tor1.b_digest), OP_EQ, DIGEST_SHA3_256); - tt_assert(or_circ->cpath->pvt_crypto.f_crypto); - tt_assert(or_circ->cpath->pvt_crypto.b_crypto); + tt_assert(or_circ->cpath->pvt_crypto.c.tor1.f_crypto); + tt_assert(or_circ->cpath->pvt_crypto.c.tor1.b_crypto); /* Ensure that circ purpose was changed */ tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_C_REND_JOINED); diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c @@ -224,12 +224,14 @@ test_e2e_rend_circuit_setup(void *arg) tt_int_op(retval, OP_EQ, 1); /* Check the digest algo */ - tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->pvt_crypto.f_digest), + tt_int_op(crypto_digest_get_algorithm( + or_circ->cpath->pvt_crypto.c.tor1.f_digest), OP_EQ, DIGEST_SHA3_256); - tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->pvt_crypto.b_digest), + tt_int_op(crypto_digest_get_algorithm( + or_circ->cpath->pvt_crypto.c.tor1.b_digest), OP_EQ, DIGEST_SHA3_256); - tt_assert(or_circ->cpath->pvt_crypto.f_crypto); - tt_assert(or_circ->cpath->pvt_crypto.b_crypto); + tt_assert(or_circ->cpath->pvt_crypto.c.tor1.f_crypto); + tt_assert(or_circ->cpath->pvt_crypto.c.tor1.b_crypto); /* Ensure that circ purpose was changed */ tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_S_REND_JOINED); diff --git a/src/test/test_ntor_v3.c b/src/test/test_ntor_v3.c @@ -192,6 +192,7 @@ run_full_handshake(circuit_params_t *serv_params_in, uint8_t client_keys[CELL_PAYLOAD_SIZE]; uint8_t rend_auth[DIGEST_LEN]; + info.supports_ntor_v3 = true; info.exit_supports_congestion_control = 1; unhex(relay_onion_key.seckey.secret_key, @@ -218,18 +219,20 @@ run_full_handshake(circuit_params_t *serv_params_in, server_keys.junk_keypair = &handshake_state.u.ntor3->client_keypair; + size_t serv_keylen = sizeof(serv_keys); + size_t client_keylen = sizeof(serv_keys); reply_len = onion_skin_server_handshake(ONION_HANDSHAKE_TYPE_NTOR_V3, onionskin, onionskin_len, &server_keys, serv_params_in, serv_reply, sizeof(serv_reply), - serv_keys, sizeof(serv_keys), + serv_keys, &serv_keylen, rend_nonce, serv_params_out); tt_int_op(reply_len, OP_NE, -1); tt_int_op(onion_skin_client_handshake(ONION_HANDSHAKE_TYPE_NTOR_V3, &handshake_state, serv_reply, reply_len, - client_keys, sizeof(client_keys), + client_keys, &client_keylen, rend_auth, client_params_out, NULL), OP_EQ, 0); diff --git a/src/test/test_relaycrypt.c b/src/test/test_relaycrypt.c @@ -49,17 +49,18 @@ testing_circuitset_setup(const struct testcase_t *testcase) for (i=0; i<3; ++i) { cs->or_circ[i] = or_circuit_new(0, NULL); tt_int_op(0, OP_EQ, - relay_crypto_init(&cs->or_circ[i]->crypto, - KEY_MATERIAL[i], sizeof(KEY_MATERIAL[i]), - 0, 0)); + relay_crypto_init(RELAY_CRYPTO_ALG_TOR1, + &cs->or_circ[i]->crypto, + KEY_MATERIAL[i], sizeof(KEY_MATERIAL[i]))); } cs->origin_circ = origin_circuit_new(); cs->origin_circ->base_.purpose = CIRCUIT_PURPOSE_C_GENERAL; for (i=0; i<3; ++i) { crypt_path_t *hop = tor_malloc_zero(sizeof(*hop)); - relay_crypto_init(&hop->pvt_crypto, KEY_MATERIAL[i], - sizeof(KEY_MATERIAL[i]), 0, 0); + relay_crypto_init(RELAY_CRYPTO_ALG_TOR1, + &hop->pvt_crypto, KEY_MATERIAL[i], + sizeof(KEY_MATERIAL[i])); hop->state = CPATH_STATE_OPEN; cpath_extend_linked_list(&cs->origin_circ->cpath, hop); tt_ptr_op(hop, OP_EQ, cs->origin_circ->cpath->prev); diff --git a/src/test/test_sendme.c b/src/test/test_sendme.c @@ -153,26 +153,28 @@ test_v1_build_cell(void *arg) smartlist_add(circ->sendme_last_digests, tor_memdup(digest, sizeof(digest))); /* SENDME v1 payload is 3 bytes + 20 bytes digest. See spec. */ - ret = build_cell_payload_v1(digest, payload); + ret = build_cell_payload_v1(digest, 20, payload); tt_int_op(ret, OP_EQ, 23); /* Validation. */ /* An empty payload means SENDME version 0 thus valid. */ - tt_int_op(sendme_is_valid(circ, payload, 0), OP_EQ, true); + tt_int_op(sendme_is_valid(circ, NULL, payload, 0), OP_EQ, true); /* Current phoney digest should have been popped. */ tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 0); /* An unparseable cell means invalid. */ setup_full_capture_of_logs(LOG_INFO); - tt_int_op(sendme_is_valid(circ, (const uint8_t *) "A", 1), OP_EQ, false); + tt_int_op(sendme_is_valid(circ, NULL, (const uint8_t *) "A", 1), + OP_EQ, false); expect_log_msg_containing("Unparseable SENDME cell received. " "Closing circuit."); teardown_capture_of_logs(); /* No cell digest recorded for this. */ setup_full_capture_of_logs(LOG_INFO); - tt_int_op(sendme_is_valid(circ, payload, sizeof(payload)), OP_EQ, false); + tt_int_op(sendme_is_valid(circ, NULL, payload, sizeof(payload)), + OP_EQ, false); expect_log_msg_containing("We received a SENDME but we have no cell digests " "to match. Closing circuit."); teardown_capture_of_logs(); @@ -182,18 +184,20 @@ test_v1_build_cell(void *arg) sendme_record_cell_digest_on_circ(circ, NULL); tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 1); setup_full_capture_of_logs(LOG_INFO); - tt_int_op(sendme_is_valid(circ, payload, sizeof(payload)), OP_EQ, false); + tt_int_op(sendme_is_valid(circ, NULL, payload, sizeof(payload)), + OP_EQ, false); /* After a validation, the last digests is always popped out. */ tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 0); expect_log_msg_containing("SENDME v1 cell digest do not match."); teardown_capture_of_logs(); /* Record the cell digest into the circuit, cell should validate. */ - memcpy(or_circ->crypto.sendme_digest, digest, sizeof(digest)); + memcpy(or_circ->crypto.c.tor1.sendme_digest, digest, sizeof(digest)); circ->package_window = CIRCWINDOW_INCREMENT + 1; sendme_record_cell_digest_on_circ(circ, NULL); tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 1); - tt_int_op(sendme_is_valid(circ, payload, sizeof(payload)), OP_EQ, true); + tt_int_op(sendme_is_valid(circ, NULL, payload, sizeof(payload)), + OP_EQ, true); /* After a validation, the last digests is always popped out. */ tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 0); @@ -313,50 +317,6 @@ test_package_payload_len(void *arg) circuit_free(c); } -/* Check that circuit_sendme_is_next works with a window of 1000, - * and a sendme_inc of 100 (old school tor compat) */ -static void -test_sendme_is_next1000(void *arg) -{ - (void)arg; - tt_int_op(circuit_sendme_cell_is_next(1000, 100), OP_EQ, 0); - tt_int_op(circuit_sendme_cell_is_next(999, 100), OP_EQ, 0); - tt_int_op(circuit_sendme_cell_is_next(901, 100), OP_EQ, 1); - - tt_int_op(circuit_sendme_cell_is_next(900, 100), OP_EQ, 0); - tt_int_op(circuit_sendme_cell_is_next(899, 100), OP_EQ, 0); - tt_int_op(circuit_sendme_cell_is_next(801, 100), OP_EQ, 1); - - tt_int_op(circuit_sendme_cell_is_next(101, 100), OP_EQ, 1); - tt_int_op(circuit_sendme_cell_is_next(100, 100), OP_EQ, 0); - tt_int_op(circuit_sendme_cell_is_next(99, 100), OP_EQ, 0); - tt_int_op(circuit_sendme_cell_is_next(1, 100), OP_EQ, 1); - tt_int_op(circuit_sendme_cell_is_next(0, 100), OP_EQ, 0); - -done: - ; -} - -/* Check that circuit_sendme_is_next works with a window of 31 */ -static void -test_sendme_is_next(void *arg) -{ - (void)arg; - tt_int_op(circuit_sendme_cell_is_next(1000, 31), OP_EQ, 0); - tt_int_op(circuit_sendme_cell_is_next(970, 31), OP_EQ, 1); - tt_int_op(circuit_sendme_cell_is_next(969, 31), OP_EQ, 0); - - /* deliver_window should never get this low, but test anyway */ - tt_int_op(circuit_sendme_cell_is_next(9, 31), OP_EQ, 1); - tt_int_op(circuit_sendme_cell_is_next(8, 31), OP_EQ, 0); - tt_int_op(circuit_sendme_cell_is_next(7, 31), OP_EQ, 0); - tt_int_op(circuit_sendme_cell_is_next(1, 31), OP_EQ, 0); - tt_int_op(circuit_sendme_cell_is_next(0, 31), OP_EQ, 0); - - done: - ; -} - struct testcase_t sendme_tests[] = { { "v1_record_digest", test_v1_record_digest, TT_FORK, NULL, NULL }, @@ -367,8 +327,6 @@ struct testcase_t sendme_tests[] = { { "cell_version_validation", test_cell_version_validation, TT_FORK, NULL, NULL }, { "package_payload_len", test_package_payload_len, 0, NULL, NULL }, - { "sendme_is_next1000", test_sendme_is_next1000, 0, NULL, NULL }, - { "sendme_is_next", test_sendme_is_next, 0, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/trunnel/include.am b/src/trunnel/include.am @@ -17,7 +17,8 @@ TRUNNELINPUTS = \ src/trunnel/congestion_control.trunnel \ src/trunnel/socks5.trunnel \ src/trunnel/circpad_negotiation.trunnel \ - src/trunnel/conflux.trunnel + src/trunnel/conflux.trunnel \ + src/trunnel/subproto_request.trunnel TRUNNELSOURCES = \ src/ext/trunnel/trunnel.c \ @@ -35,7 +36,8 @@ TRUNNELSOURCES = \ src/trunnel/socks5.c \ src/trunnel/netinfo.c \ src/trunnel/circpad_negotiation.c \ - src/trunnel/conflux.c + src/trunnel/conflux.c \ + src/trunnel/subproto_request.c TRUNNELHEADERS = \ src/ext/trunnel/trunnel.h \ @@ -55,7 +57,8 @@ TRUNNELHEADERS = \ src/trunnel/socks5.h \ src/trunnel/netinfo.h \ src/trunnel/circpad_negotiation.h \ - src/trunnel/conflux.h + src/trunnel/conflux.h \ + src/trunnel/subproto_request.h src_trunnel_libor_trunnel_a_SOURCES = $(TRUNNELSOURCES) src_trunnel_libor_trunnel_a_CPPFLAGS = \ diff --git a/src/trunnel/sendme_cell.c b/src/trunnel/sendme_cell.c @@ -34,6 +34,7 @@ sendme_cell_new(void) sendme_cell_t *val = trunnel_calloc(1, sizeof(sendme_cell_t)); if (NULL == val) return NULL; + val->data_len = 16; return val; } @@ -43,6 +44,8 @@ static void sendme_cell_clear(sendme_cell_t *obj) { (void) obj; + TRUNNEL_DYNARRAY_WIPE(&obj->data_v1_digest); + TRUNNEL_DYNARRAY_CLEAR(&obj->data_v1_digest); } void @@ -78,20 +81,23 @@ sendme_cell_get_data_len(const sendme_cell_t *inp) int sendme_cell_set_data_len(sendme_cell_t *inp, uint16_t val) { + if (! ((val == 16 || val == 20))) { + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } inp->data_len = val; return 0; } size_t sendme_cell_getlen_data_v1_digest(const sendme_cell_t *inp) { - (void)inp; return TRUNNEL_SENDME_V1_DIGEST_LEN; + return TRUNNEL_DYNARRAY_LEN(&inp->data_v1_digest); } uint8_t sendme_cell_get_data_v1_digest(sendme_cell_t *inp, size_t idx) { - trunnel_assert(idx < TRUNNEL_SENDME_V1_DIGEST_LEN); - return inp->data_v1_digest[idx]; + return TRUNNEL_DYNARRAY_GET(&inp->data_v1_digest, idx); } uint8_t @@ -102,21 +108,45 @@ sendme_cell_getconst_data_v1_digest(const sendme_cell_t *inp, size_t idx) int sendme_cell_set_data_v1_digest(sendme_cell_t *inp, size_t idx, uint8_t elt) { - trunnel_assert(idx < TRUNNEL_SENDME_V1_DIGEST_LEN); - inp->data_v1_digest[idx] = elt; + TRUNNEL_DYNARRAY_SET(&inp->data_v1_digest, idx, elt); + return 0; +} +int +sendme_cell_add_data_v1_digest(sendme_cell_t *inp, uint8_t elt) +{ + TRUNNEL_DYNARRAY_ADD(uint8_t, &inp->data_v1_digest, elt, {}); return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; } uint8_t * sendme_cell_getarray_data_v1_digest(sendme_cell_t *inp) { - return inp->data_v1_digest; + return inp->data_v1_digest.elts_; } const uint8_t * sendme_cell_getconstarray_data_v1_digest(const sendme_cell_t *inp) { return (const uint8_t *)sendme_cell_getarray_data_v1_digest((sendme_cell_t*)inp); } +int +sendme_cell_setlen_data_v1_digest(sendme_cell_t *inp, size_t newlen) +{ + uint8_t *newptr; + newptr = trunnel_dynarray_setlen(&inp->data_v1_digest.allocated_, + &inp->data_v1_digest.n_, inp->data_v1_digest.elts_, newlen, + sizeof(inp->data_v1_digest.elts_[0]), (trunnel_free_fn_t) NULL, + &inp->trunnel_error_code_); + if (newlen != 0 && newptr == NULL) + goto trunnel_alloc_failed; + inp->data_v1_digest.elts_ = newptr; + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} const char * sendme_cell_check(const sendme_cell_t *obj) { @@ -126,6 +156,8 @@ sendme_cell_check(const sendme_cell_t *obj) return "A set function failed on this object"; if (! (obj->version == 0 || obj->version == 1)) return "Integer out of bounds"; + if (! (obj->data_len == 16 || obj->data_len == 20)) + return "Integer out of bounds"; switch (obj->version) { case 0: @@ -153,7 +185,7 @@ sendme_cell_encoded_len(const sendme_cell_t *obj) /* Length of u8 version IN [0, 1] */ result += 1; - /* Length of u16 data_len */ + /* Length of u16 data_len IN [16, 20] */ result += 2; switch (obj->version) { @@ -162,8 +194,8 @@ sendme_cell_encoded_len(const sendme_cell_t *obj) case 1: - /* Length of u8 data_v1_digest[TRUNNEL_SENDME_V1_DIGEST_LEN] */ - result += TRUNNEL_SENDME_V1_DIGEST_LEN; + /* Length of u8 data_v1_digest[] */ + result += TRUNNEL_DYNARRAY_LEN(&obj->data_v1_digest); break; default: @@ -206,7 +238,7 @@ sendme_cell_encode(uint8_t *output, const size_t avail, const sendme_cell_t *obj trunnel_set_uint8(ptr, (obj->version)); written += 1; ptr += 1; - /* Encode u16 data_len */ + /* Encode u16 data_len IN [16, 20] */ backptr_data_len = ptr; trunnel_assert(written <= avail); if (avail - written < 2) @@ -225,12 +257,16 @@ sendme_cell_encode(uint8_t *output, const size_t avail, const sendme_cell_t *obj case 1: - /* Encode u8 data_v1_digest[TRUNNEL_SENDME_V1_DIGEST_LEN] */ - trunnel_assert(written <= avail); - if (avail - written < TRUNNEL_SENDME_V1_DIGEST_LEN) - goto truncated; - memcpy(ptr, obj->data_v1_digest, TRUNNEL_SENDME_V1_DIGEST_LEN); - written += TRUNNEL_SENDME_V1_DIGEST_LEN; ptr += TRUNNEL_SENDME_V1_DIGEST_LEN; + /* Encode u8 data_v1_digest[] */ + { + size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->data_v1_digest); + trunnel_assert(written <= avail); + if (avail - written < elt_len) + goto truncated; + if (elt_len) + memcpy(ptr, obj->data_v1_digest.elts_, elt_len); + written += elt_len; ptr += elt_len; + } break; default: @@ -287,10 +323,12 @@ sendme_cell_parse_into(sendme_cell_t *obj, const uint8_t *input, const size_t le if (! (obj->version == 0 || obj->version == 1)) goto fail; - /* Parse u16 data_len */ + /* Parse u16 data_len IN [16, 20] */ CHECK_REMAINING(2, truncated); obj->data_len = trunnel_ntohs(trunnel_get_uint16(ptr)); remaining -= 2; ptr += 2; + if (! (obj->data_len == 16 || obj->data_len == 20)) + goto fail; { size_t remaining_after; CHECK_REMAINING(obj->data_len, truncated); @@ -307,10 +345,12 @@ sendme_cell_parse_into(sendme_cell_t *obj, const uint8_t *input, const size_t le case 1: - /* Parse u8 data_v1_digest[TRUNNEL_SENDME_V1_DIGEST_LEN] */ - CHECK_REMAINING(TRUNNEL_SENDME_V1_DIGEST_LEN, fail); - memcpy(obj->data_v1_digest, ptr, TRUNNEL_SENDME_V1_DIGEST_LEN); - remaining -= TRUNNEL_SENDME_V1_DIGEST_LEN; ptr += TRUNNEL_SENDME_V1_DIGEST_LEN; + /* Parse u8 data_v1_digest[] */ + TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->data_v1_digest, remaining, {}); + obj->data_v1_digest.n_ = remaining; + if (remaining) + memcpy(obj->data_v1_digest.elts_, ptr, remaining); + ptr += remaining; remaining -= remaining; break; default: @@ -326,6 +366,8 @@ sendme_cell_parse_into(sendme_cell_t *obj, const uint8_t *input, const size_t le truncated: return -2; + trunnel_alloc_failed: + return -1; fail: result = -1; return result; diff --git a/src/trunnel/sendme_cell.h b/src/trunnel/sendme_cell.h @@ -8,12 +8,11 @@ #include <stdint.h> #include "trunnel.h" -#define TRUNNEL_SENDME_V1_DIGEST_LEN 20 #if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_SENDME_CELL) struct sendme_cell_st { uint8_t version; uint16_t data_len; - uint8_t data_v1_digest[TRUNNEL_SENDME_V1_DIGEST_LEN]; + TRUNNEL_DYNARRAY_HEAD(, uint8_t) data_v1_digest; uint8_t trunnel_error_code_; }; #endif @@ -71,11 +70,11 @@ uint16_t sendme_cell_get_data_len(const sendme_cell_t *inp); * 'inp' on failure. */ int sendme_cell_set_data_len(sendme_cell_t *inp, uint16_t val); -/** Return the (constant) length of the array holding the - * data_v1_digest field of the sendme_cell_t in 'inp'. +/** Return the length of the dynamic array holding the data_v1_digest + * field of the sendme_cell_t in 'inp'. */ size_t sendme_cell_getlen_data_v1_digest(const sendme_cell_t *inp); -/** Return the element at position 'idx' of the fixed array field +/** Return the element at position 'idx' of the dynamic array field * data_v1_digest of the sendme_cell_t in 'inp'. */ uint8_t sendme_cell_get_data_v1_digest(sendme_cell_t *inp, size_t idx); @@ -83,19 +82,29 @@ uint8_t sendme_cell_get_data_v1_digest(sendme_cell_t *inp, size_t idx); * pointer */ uint8_t sendme_cell_getconst_data_v1_digest(const sendme_cell_t *inp, size_t idx); -/** Change the element at position 'idx' of the fixed array field +/** Change the element at position 'idx' of the dynamic array field * data_v1_digest of the sendme_cell_t in 'inp', so that it will hold * the value 'elt'. */ int sendme_cell_set_data_v1_digest(sendme_cell_t *inp, size_t idx, uint8_t elt); -/** Return a pointer to the TRUNNEL_SENDME_V1_DIGEST_LEN-element array - * field data_v1_digest of 'inp'. +/** Append a new element 'elt' to the dynamic array field + * data_v1_digest of the sendme_cell_t in 'inp'. + */ +int sendme_cell_add_data_v1_digest(sendme_cell_t *inp, uint8_t elt); +/** Return a pointer to the variable-length array field data_v1_digest + * of 'inp'. */ uint8_t * sendme_cell_getarray_data_v1_digest(sendme_cell_t *inp); /** As sendme_cell_get_data_v1_digest, but take and return a const * pointer */ const uint8_t * sendme_cell_getconstarray_data_v1_digest(const sendme_cell_t *inp); +/** Change the length of the variable-length array field + * data_v1_digest of 'inp' to 'newlen'.Fill extra elements with 0. + * Return 0 on success; return -1 and set the error code on 'inp' on + * failure. + */ +int sendme_cell_setlen_data_v1_digest(sendme_cell_t *inp, size_t newlen); #endif diff --git a/src/trunnel/sendme_cell.trunnel b/src/trunnel/sendme_cell.trunnel @@ -1,19 +1,19 @@ /* This file contains the SENDME cell definition. */ -/* v1 digest length in bytes. */ -const TRUNNEL_SENDME_V1_DIGEST_LEN = 20; - /* SENDME cell declaration. */ struct sendme_cell { /* Version field. */ u8 version IN [0x00, 0x01]; /* Length of data contained in this cell. */ - u16 data_len; + u16 data_len IN [16, 20]; /* The data content depends on the version. */ union data[version] with length data_len { 0x00: ignore; - 0x01: u8 v1_digest[TRUNNEL_SENDME_V1_DIGEST_LEN]; + /* I'd like to use data_len here, but trunnel doesn't + * allow that. Instead have v1_digest run to the end of the cell. + */ + 0x01: u8 v1_digest[]; }; } diff --git a/src/trunnel/subproto_request.c b/src/trunnel/subproto_request.c @@ -0,0 +1,456 @@ +/* subproto_request.c -- generated by Trunnel v1.5.3. + * https://gitweb.torproject.org/trunnel.git + * You probably shouldn't edit this file. + */ +#include <stdlib.h> +#include "trunnel-impl.h" + +#include "subproto_request.h" + +#define TRUNNEL_SET_ERROR_CODE(obj) \ + do { \ + (obj)->trunnel_error_code_ = 1; \ + } while (0) + +#if defined(__COVERITY__) || defined(__clang_analyzer__) +/* If we're running a static analysis tool, we don't want it to complain + * that some of our remaining-bytes checks are dead-code. */ +int subprotorequest_deadcode_dummy__ = 0; +#define OR_DEADCODE_DUMMY || subprotorequest_deadcode_dummy__ +#else +#define OR_DEADCODE_DUMMY +#endif + +#define CHECK_REMAINING(nbytes, label) \ + do { \ + if (remaining < (nbytes) OR_DEADCODE_DUMMY) { \ + goto label; \ + } \ + } while (0) + +trn_subproto_request_t * +trn_subproto_request_new(void) +{ + trn_subproto_request_t *val = trunnel_calloc(1, sizeof(trn_subproto_request_t)); + if (NULL == val) + return NULL; + return val; +} + +/** Release all storage held inside 'obj', but do not free 'obj'. + */ +static void +trn_subproto_request_clear(trn_subproto_request_t *obj) +{ + (void) obj; +} + +void +trn_subproto_request_free(trn_subproto_request_t *obj) +{ + if (obj == NULL) + return; + trn_subproto_request_clear(obj); + trunnel_memwipe(obj, sizeof(trn_subproto_request_t)); + trunnel_free_(obj); +} + +uint8_t +trn_subproto_request_get_protocol_id(const trn_subproto_request_t *inp) +{ + return inp->protocol_id; +} +int +trn_subproto_request_set_protocol_id(trn_subproto_request_t *inp, uint8_t val) +{ + inp->protocol_id = val; + return 0; +} +uint8_t +trn_subproto_request_get_proto_cap_number(const trn_subproto_request_t *inp) +{ + return inp->proto_cap_number; +} +int +trn_subproto_request_set_proto_cap_number(trn_subproto_request_t *inp, uint8_t val) +{ + inp->proto_cap_number = val; + return 0; +} +const char * +trn_subproto_request_check(const trn_subproto_request_t *obj) +{ + if (obj == NULL) + return "Object was NULL"; + if (obj->trunnel_error_code_) + return "A set function failed on this object"; + return NULL; +} + +ssize_t +trn_subproto_request_encoded_len(const trn_subproto_request_t *obj) +{ + ssize_t result = 0; + + if (NULL != trn_subproto_request_check(obj)) + return -1; + + + /* Length of u8 protocol_id */ + result += 1; + + /* Length of u8 proto_cap_number */ + result += 1; + return result; +} +int +trn_subproto_request_clear_errors(trn_subproto_request_t *obj) +{ + int r = obj->trunnel_error_code_; + obj->trunnel_error_code_ = 0; + return r; +} +ssize_t +trn_subproto_request_encode(uint8_t *output, const size_t avail, const trn_subproto_request_t *obj) +{ + ssize_t result = 0; + size_t written = 0; + uint8_t *ptr = output; + const char *msg; +#ifdef TRUNNEL_CHECK_ENCODED_LEN + const ssize_t encoded_len = trn_subproto_request_encoded_len(obj); +#endif + + if (NULL != (msg = trn_subproto_request_check(obj))) + goto check_failed; + +#ifdef TRUNNEL_CHECK_ENCODED_LEN + trunnel_assert(encoded_len >= 0); +#endif + + /* Encode u8 protocol_id */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->protocol_id)); + written += 1; ptr += 1; + + /* Encode u8 proto_cap_number */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->proto_cap_number)); + written += 1; ptr += 1; + + + trunnel_assert(ptr == output + written); +#ifdef TRUNNEL_CHECK_ENCODED_LEN + { + trunnel_assert(encoded_len >= 0); + trunnel_assert((size_t)encoded_len == written); + } + +#endif + + return written; + + truncated: + result = -2; + goto fail; + check_failed: + (void)msg; + result = -1; + goto fail; + fail: + trunnel_assert(result < 0); + return result; +} + +/** As trn_subproto_request_parse(), but do not allocate the output + * object. + */ +static ssize_t +trn_subproto_request_parse_into(trn_subproto_request_t *obj, const uint8_t *input, const size_t len_in) +{ + const uint8_t *ptr = input; + size_t remaining = len_in; + ssize_t result = 0; + (void)result; + + /* Parse u8 protocol_id */ + CHECK_REMAINING(1, truncated); + obj->protocol_id = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + + /* Parse u8 proto_cap_number */ + CHECK_REMAINING(1, truncated); + obj->proto_cap_number = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + trunnel_assert(ptr + remaining == input + len_in); + return len_in - remaining; + + truncated: + return -2; +} + +ssize_t +trn_subproto_request_parse(trn_subproto_request_t **output, const uint8_t *input, const size_t len_in) +{ + ssize_t result; + *output = trn_subproto_request_new(); + if (NULL == *output) + return -1; + result = trn_subproto_request_parse_into(*output, input, len_in); + if (result < 0) { + trn_subproto_request_free(*output); + *output = NULL; + } + return result; +} +trn_subproto_request_ext_t * +trn_subproto_request_ext_new(void) +{ + trn_subproto_request_ext_t *val = trunnel_calloc(1, sizeof(trn_subproto_request_ext_t)); + if (NULL == val) + return NULL; + return val; +} + +/** Release all storage held inside 'obj', but do not free 'obj'. + */ +static void +trn_subproto_request_ext_clear(trn_subproto_request_ext_t *obj) +{ + (void) obj; + { + + unsigned idx; + for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->reqs); ++idx) { + trn_subproto_request_free(TRUNNEL_DYNARRAY_GET(&obj->reqs, idx)); + } + } + TRUNNEL_DYNARRAY_WIPE(&obj->reqs); + TRUNNEL_DYNARRAY_CLEAR(&obj->reqs); +} + +void +trn_subproto_request_ext_free(trn_subproto_request_ext_t *obj) +{ + if (obj == NULL) + return; + trn_subproto_request_ext_clear(obj); + trunnel_memwipe(obj, sizeof(trn_subproto_request_ext_t)); + trunnel_free_(obj); +} + +size_t +trn_subproto_request_ext_getlen_reqs(const trn_subproto_request_ext_t *inp) +{ + return TRUNNEL_DYNARRAY_LEN(&inp->reqs); +} + +struct trn_subproto_request_st * +trn_subproto_request_ext_get_reqs(trn_subproto_request_ext_t *inp, size_t idx) +{ + return TRUNNEL_DYNARRAY_GET(&inp->reqs, idx); +} + + const struct trn_subproto_request_st * +trn_subproto_request_ext_getconst_reqs(const trn_subproto_request_ext_t *inp, size_t idx) +{ + return trn_subproto_request_ext_get_reqs((trn_subproto_request_ext_t*)inp, idx); +} +int +trn_subproto_request_ext_set_reqs(trn_subproto_request_ext_t *inp, size_t idx, struct trn_subproto_request_st * elt) +{ + trn_subproto_request_t *oldval = TRUNNEL_DYNARRAY_GET(&inp->reqs, idx); + if (oldval && oldval != elt) + trn_subproto_request_free(oldval); + return trn_subproto_request_ext_set0_reqs(inp, idx, elt); +} +int +trn_subproto_request_ext_set0_reqs(trn_subproto_request_ext_t *inp, size_t idx, struct trn_subproto_request_st * elt) +{ + TRUNNEL_DYNARRAY_SET(&inp->reqs, idx, elt); + return 0; +} +int +trn_subproto_request_ext_add_reqs(trn_subproto_request_ext_t *inp, struct trn_subproto_request_st * elt) +{ + TRUNNEL_DYNARRAY_ADD(struct trn_subproto_request_st *, &inp->reqs, elt, {}); + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} + +struct trn_subproto_request_st * * +trn_subproto_request_ext_getarray_reqs(trn_subproto_request_ext_t *inp) +{ + return inp->reqs.elts_; +} +const struct trn_subproto_request_st * const * +trn_subproto_request_ext_getconstarray_reqs(const trn_subproto_request_ext_t *inp) +{ + return (const struct trn_subproto_request_st * const *)trn_subproto_request_ext_getarray_reqs((trn_subproto_request_ext_t*)inp); +} +int +trn_subproto_request_ext_setlen_reqs(trn_subproto_request_ext_t *inp, size_t newlen) +{ + struct trn_subproto_request_st * *newptr; + newptr = trunnel_dynarray_setlen(&inp->reqs.allocated_, + &inp->reqs.n_, inp->reqs.elts_, newlen, + sizeof(inp->reqs.elts_[0]), (trunnel_free_fn_t) trn_subproto_request_free, + &inp->trunnel_error_code_); + if (newlen != 0 && newptr == NULL) + goto trunnel_alloc_failed; + inp->reqs.elts_ = newptr; + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} +const char * +trn_subproto_request_ext_check(const trn_subproto_request_ext_t *obj) +{ + if (obj == NULL) + return "Object was NULL"; + if (obj->trunnel_error_code_) + return "A set function failed on this object"; + { + const char *msg; + + unsigned idx; + for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->reqs); ++idx) { + if (NULL != (msg = trn_subproto_request_check(TRUNNEL_DYNARRAY_GET(&obj->reqs, idx)))) + return msg; + } + } + return NULL; +} + +ssize_t +trn_subproto_request_ext_encoded_len(const trn_subproto_request_ext_t *obj) +{ + ssize_t result = 0; + + if (NULL != trn_subproto_request_ext_check(obj)) + return -1; + + + /* Length of struct trn_subproto_request reqs[] */ + { + + unsigned idx; + for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->reqs); ++idx) { + result += trn_subproto_request_encoded_len(TRUNNEL_DYNARRAY_GET(&obj->reqs, idx)); + } + } + return result; +} +int +trn_subproto_request_ext_clear_errors(trn_subproto_request_ext_t *obj) +{ + int r = obj->trunnel_error_code_; + obj->trunnel_error_code_ = 0; + return r; +} +ssize_t +trn_subproto_request_ext_encode(uint8_t *output, const size_t avail, const trn_subproto_request_ext_t *obj) +{ + ssize_t result = 0; + size_t written = 0; + uint8_t *ptr = output; + const char *msg; +#ifdef TRUNNEL_CHECK_ENCODED_LEN + const ssize_t encoded_len = trn_subproto_request_ext_encoded_len(obj); +#endif + + if (NULL != (msg = trn_subproto_request_ext_check(obj))) + goto check_failed; + +#ifdef TRUNNEL_CHECK_ENCODED_LEN + trunnel_assert(encoded_len >= 0); +#endif + + /* Encode struct trn_subproto_request reqs[] */ + { + + unsigned idx; + for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->reqs); ++idx) { + trunnel_assert(written <= avail); + result = trn_subproto_request_encode(ptr, avail - written, TRUNNEL_DYNARRAY_GET(&obj->reqs, idx)); + if (result < 0) + goto fail; /* XXXXXXX !*/ + written += result; ptr += result; + } + } + + + trunnel_assert(ptr == output + written); +#ifdef TRUNNEL_CHECK_ENCODED_LEN + { + trunnel_assert(encoded_len >= 0); + trunnel_assert((size_t)encoded_len == written); + } + +#endif + + return written; + + check_failed: + (void)msg; + result = -1; + goto fail; + fail: + trunnel_assert(result < 0); + return result; +} + +/** As trn_subproto_request_ext_parse(), but do not allocate the + * output object. + */ +static ssize_t +trn_subproto_request_ext_parse_into(trn_subproto_request_ext_t *obj, const uint8_t *input, const size_t len_in) +{ + const uint8_t *ptr = input; + size_t remaining = len_in; + ssize_t result = 0; + (void)result; + + /* Parse struct trn_subproto_request reqs[] */ + { + trn_subproto_request_t * elt; + while (remaining > 0) { + result = trn_subproto_request_parse(&elt, ptr, remaining); + if (result < 0) + goto fail; + trunnel_assert((size_t)result <= remaining); + remaining -= result; ptr += result; + TRUNNEL_DYNARRAY_ADD(trn_subproto_request_t *, &obj->reqs, elt, {trn_subproto_request_free(elt);}); + } + } + trunnel_assert(ptr + remaining == input + len_in); + return len_in - remaining; + + trunnel_alloc_failed: + return -1; + fail: + result = -1; + return result; +} + +ssize_t +trn_subproto_request_ext_parse(trn_subproto_request_ext_t **output, const uint8_t *input, const size_t len_in) +{ + ssize_t result; + *output = trn_subproto_request_ext_new(); + if (NULL == *output) + return -1; + result = trn_subproto_request_ext_parse_into(*output, input, len_in); + if (result < 0) { + trn_subproto_request_ext_free(*output); + *output = NULL; + } + return result; +} diff --git a/src/trunnel/subproto_request.h b/src/trunnel/subproto_request.h @@ -0,0 +1,157 @@ +/* subproto_request.h -- generated by Trunnel v1.5.3. + * https://gitweb.torproject.org/trunnel.git + * You probably shouldn't edit this file. + */ +#ifndef TRUNNEL_SUBPROTO_REQUEST_H +#define TRUNNEL_SUBPROTO_REQUEST_H + +#include <stdint.h> +#include "trunnel.h" + +#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_TRN_SUBPROTO_REQUEST) +struct trn_subproto_request_st { + uint8_t protocol_id; + uint8_t proto_cap_number; + uint8_t trunnel_error_code_; +}; +#endif +typedef struct trn_subproto_request_st trn_subproto_request_t; +#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_TRN_SUBPROTO_REQUEST_EXT) +struct trn_subproto_request_ext_st { + TRUNNEL_DYNARRAY_HEAD(, struct trn_subproto_request_st *) reqs; + uint8_t trunnel_error_code_; +}; +#endif +typedef struct trn_subproto_request_ext_st trn_subproto_request_ext_t; +/** Return a newly allocated trn_subproto_request with all elements + * set to zero. + */ +trn_subproto_request_t *trn_subproto_request_new(void); +/** Release all storage held by the trn_subproto_request in 'victim'. + * (Do nothing if 'victim' is NULL.) + */ +void trn_subproto_request_free(trn_subproto_request_t *victim); +/** Try to parse a trn_subproto_request from the buffer in 'input', + * using up to 'len_in' bytes from the input buffer. On success, + * return the number of bytes consumed and set *output to the newly + * allocated trn_subproto_request_t. On failure, return -2 if the + * input appears truncated, and -1 if the input is otherwise invalid. + */ +ssize_t trn_subproto_request_parse(trn_subproto_request_t **output, const uint8_t *input, const size_t len_in); +/** Return the number of bytes we expect to need to encode the + * trn_subproto_request in 'obj'. On failure, return a negative value. + * Note that this value may be an overestimate, and can even be an + * underestimate for certain unencodeable objects. + */ +ssize_t trn_subproto_request_encoded_len(const trn_subproto_request_t *obj); +/** Try to encode the trn_subproto_request from 'input' into the + * buffer at 'output', using up to 'avail' bytes of the output buffer. + * On success, return the number of bytes used. On failure, return -2 + * if the buffer was not long enough, and -1 if the input was invalid. + */ +ssize_t trn_subproto_request_encode(uint8_t *output, size_t avail, const trn_subproto_request_t *input); +/** Check whether the internal state of the trn_subproto_request in + * 'obj' is consistent. Return NULL if it is, and a short message if + * it is not. + */ +const char *trn_subproto_request_check(const trn_subproto_request_t *obj); +/** Clear any errors that were set on the object 'obj' by its setter + * functions. Return true iff errors were cleared. + */ +int trn_subproto_request_clear_errors(trn_subproto_request_t *obj); +/** Return the value of the protocol_id field of the + * trn_subproto_request_t in 'inp' + */ +uint8_t trn_subproto_request_get_protocol_id(const trn_subproto_request_t *inp); +/** Set the value of the protocol_id field of the + * trn_subproto_request_t in 'inp' to 'val'. Return 0 on success; + * return -1 and set the error code on 'inp' on failure. + */ +int trn_subproto_request_set_protocol_id(trn_subproto_request_t *inp, uint8_t val); +/** Return the value of the proto_cap_number field of the + * trn_subproto_request_t in 'inp' + */ +uint8_t trn_subproto_request_get_proto_cap_number(const trn_subproto_request_t *inp); +/** Set the value of the proto_cap_number field of the + * trn_subproto_request_t in 'inp' to 'val'. Return 0 on success; + * return -1 and set the error code on 'inp' on failure. + */ +int trn_subproto_request_set_proto_cap_number(trn_subproto_request_t *inp, uint8_t val); +/** Return a newly allocated trn_subproto_request_ext with all + * elements set to zero. + */ +trn_subproto_request_ext_t *trn_subproto_request_ext_new(void); +/** Release all storage held by the trn_subproto_request_ext in + * 'victim'. (Do nothing if 'victim' is NULL.) + */ +void trn_subproto_request_ext_free(trn_subproto_request_ext_t *victim); +/** Try to parse a trn_subproto_request_ext from the buffer in + * 'input', using up to 'len_in' bytes from the input buffer. On + * success, return the number of bytes consumed and set *output to the + * newly allocated trn_subproto_request_ext_t. On failure, return -2 + * if the input appears truncated, and -1 if the input is otherwise + * invalid. + */ +ssize_t trn_subproto_request_ext_parse(trn_subproto_request_ext_t **output, const uint8_t *input, const size_t len_in); +/** Return the number of bytes we expect to need to encode the + * trn_subproto_request_ext in 'obj'. On failure, return a negative + * value. Note that this value may be an overestimate, and can even be + * an underestimate for certain unencodeable objects. + */ +ssize_t trn_subproto_request_ext_encoded_len(const trn_subproto_request_ext_t *obj); +/** Try to encode the trn_subproto_request_ext from 'input' into the + * buffer at 'output', using up to 'avail' bytes of the output buffer. + * On success, return the number of bytes used. On failure, return -2 + * if the buffer was not long enough, and -1 if the input was invalid. + */ +ssize_t trn_subproto_request_ext_encode(uint8_t *output, size_t avail, const trn_subproto_request_ext_t *input); +/** Check whether the internal state of the trn_subproto_request_ext + * in 'obj' is consistent. Return NULL if it is, and a short message + * if it is not. + */ +const char *trn_subproto_request_ext_check(const trn_subproto_request_ext_t *obj); +/** Clear any errors that were set on the object 'obj' by its setter + * functions. Return true iff errors were cleared. + */ +int trn_subproto_request_ext_clear_errors(trn_subproto_request_ext_t *obj); +/** Return the length of the dynamic array holding the reqs field of + * the trn_subproto_request_ext_t in 'inp'. + */ +size_t trn_subproto_request_ext_getlen_reqs(const trn_subproto_request_ext_t *inp); +/** Return the element at position 'idx' of the dynamic array field + * reqs of the trn_subproto_request_ext_t in 'inp'. + */ +struct trn_subproto_request_st * trn_subproto_request_ext_get_reqs(trn_subproto_request_ext_t *inp, size_t idx); +/** As trn_subproto_request_ext_get_reqs, but take and return a const + * pointer + */ + const struct trn_subproto_request_st * trn_subproto_request_ext_getconst_reqs(const trn_subproto_request_ext_t *inp, size_t idx); +/** Change the element at position 'idx' of the dynamic array field + * reqs of the trn_subproto_request_ext_t in 'inp', so that it will + * hold the value 'elt'. Free the previous value, if any. + */ +int trn_subproto_request_ext_set_reqs(trn_subproto_request_ext_t *inp, size_t idx, struct trn_subproto_request_st * elt); +/** As trn_subproto_request_ext_set_reqs, but does not free the + * previous value. + */ +int trn_subproto_request_ext_set0_reqs(trn_subproto_request_ext_t *inp, size_t idx, struct trn_subproto_request_st * elt); +/** Append a new element 'elt' to the dynamic array field reqs of the + * trn_subproto_request_ext_t in 'inp'. + */ +int trn_subproto_request_ext_add_reqs(trn_subproto_request_ext_t *inp, struct trn_subproto_request_st * elt); +/** Return a pointer to the variable-length array field reqs of 'inp'. + */ +struct trn_subproto_request_st * * trn_subproto_request_ext_getarray_reqs(trn_subproto_request_ext_t *inp); +/** As trn_subproto_request_ext_get_reqs, but take and return a const + * pointer + */ +const struct trn_subproto_request_st * const * trn_subproto_request_ext_getconstarray_reqs(const trn_subproto_request_ext_t *inp); +/** Change the length of the variable-length array field reqs of 'inp' + * to 'newlen'.Fill extra elements with NULL; free removed elements. + * Return 0 on success; return -1 and set the error code on 'inp' on + * failure. + */ +int trn_subproto_request_ext_setlen_reqs(trn_subproto_request_ext_t *inp, size_t newlen); + + +#endif diff --git a/src/trunnel/subproto_request.trunnel b/src/trunnel/subproto_request.trunnel @@ -0,0 +1,9 @@ + +struct trn_subproto_request { + u8 protocol_id; + u8 proto_cap_number; +} + +struct trn_subproto_request_ext { + struct trn_subproto_request reqs[]; +}