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:
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,
¶ms,
&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,
¶ms);
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[];
+}