commit e5d40b9d934f0e18fd3939d9c7ddc4dd4b29b136
parent e9eb21ed1e78f7a912e3157e4cc17911e09b9ec0
Author: Nick Mathewson <nickm@torproject.org>
Date: Wed, 28 May 2025 12:51:08 -0400
Propagate longer keylens through onion handshakes.
Diffstat:
6 files changed, 60 insertions(+), 28 deletions(-)
diff --git a/src/core/crypto/onion_crypto.c b/src/core/crypto/onion_crypto.c
@@ -259,6 +259,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,
@@ -267,12 +270,19 @@ onion_skin_server_handshake(int type,
const circuit_params_t *our_ns_params,
uint8_t *reply_out,
size_t reply_out_maxlen,
- // XXXX keys_out_len will depend on the algorithm we're negotiating.
- 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;
+
+ relay_crypto_alg_t relay_alg = RELAY_CRYPTO_ALG_TOR1;
+ 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;
+
memset(params_out, 0, sizeof(*params_out));
switch (type) {
@@ -283,7 +293,8 @@ 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 (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);
@@ -294,7 +305,7 @@ onion_skin_server_handshake(int type,
if (onionskin_len < NTOR_ONIONSKIN_LEN)
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];
@@ -307,14 +318,14 @@ 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;
+ 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];
uint8_t *client_msg = NULL;
@@ -367,8 +378,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);
@@ -430,17 +441,22 @@ negotiate_v3_ntor_client_circ_params(const uint8_t *param_response_msg,
/** 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)
@@ -448,6 +464,13 @@ onion_skin_client_handshake(int type,
if (handshake_state->tag != type)
return -1;
+ relay_crypto_alg_t relay_alg = RELAY_CRYPTO_ALG_TOR1;
+ 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;
+
memset(params_out, 0, sizeof(*params_out));
switch (type) {
@@ -460,7 +483,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);
@@ -472,7 +495,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,
@@ -480,14 +503,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;
@@ -512,8 +535,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);
diff --git a/src/core/crypto/onion_crypto.h b/src/core/crypto/onion_crypto.h
@@ -47,13 +47,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);
diff --git a/src/core/mainloop/cpuworker.c b/src/core/mainloop/cpuworker.c
@@ -498,15 +498,18 @@ cpuworker_onion_handshake_threadfn(void *state_, void *work_)
rpl.handshake_type = cc->handshake_type;
if (req.timed)
tor_gettimeofday(&tv_start);
+ size_t 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, &keys_len,
rpl.rend_auth_material,
&rpl.circ_params);
+ // XXXX Will be wrong for cgo.
+ tor_assert(keys_len == CPATH_KEY_MATERIAL_LEN);
if (n < 0) {
/* failure */
log_debug(LD_OR,"onion_skin_server_handshake failed.");
diff --git a/src/core/or/circuitbuild.c b/src/core/or/circuitbuild.c
@@ -1281,15 +1281,18 @@ circuit_finish_handshake(origin_circuit_t *circ,
circuit_params_t params;
{
const char *msg = NULL;
+ size_t keylen = sizeof(keys);
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) {
if (msg)
log_warn(LD_CIRC,"onion_skin_client_handshake failed: %s", msg);
+ // XXXX This will be wrong for CGO.
+ tor_assert(keylen == sizeof(keys));
return -END_CIRC_REASON_TORPROTOCOL;
}
}
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;
diff --git a/src/test/test_ntor_v3.c b/src/test/test_ntor_v3.c
@@ -218,18 +218,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);