From f8dc1164ba86099d4106dffa84435366bf6cdcd6 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Fri, 12 May 2017 11:39:46 -0400 Subject: [PATCH 01/19] prop224: Add connection and circuit identifier object Signed-off-by: David Goulet --- src/or/circuitlist.c | 2 + src/or/connection.c | 3 ++ src/or/hs_common.h | 6 +++ src/or/hs_ident.c | 81 ++++++++++++++++++++++++++++ src/or/hs_ident.h | 124 +++++++++++++++++++++++++++++++++++++++++++ src/or/include.am | 16 +++--- src/or/or.h | 17 ++++++ 7 files changed, 242 insertions(+), 7 deletions(-) create mode 100644 src/or/hs_ident.c create mode 100644 src/or/hs_ident.h diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index 86b0aa097a..d11e128787 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -67,6 +67,7 @@ #include "main.h" #include "hs_circuitmap.h" #include "hs_common.h" +#include "hs_ident.h" #include "networkstatus.h" #include "nodelist.h" #include "onion.h" @@ -957,6 +958,7 @@ circuit_free(circuit_t *circ) crypto_pk_free(ocirc->intro_key); rend_data_free(ocirc->rend_data); + hs_ident_circuit_free(ocirc->hs_ident); tor_free(ocirc->dest_address); if (ocirc->socks_username) { diff --git a/src/or/connection.c b/src/or/connection.c index 4e890497e9..5c65e886c0 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -84,6 +84,7 @@ #include "geoip.h" #include "main.h" #include "hs_common.h" +#include "hs_ident.h" #include "nodelist.h" #include "policies.h" #include "reasons.h" @@ -605,6 +606,7 @@ connection_free_(connection_t *conn) } if (CONN_IS_EDGE(conn)) { rend_data_free(TO_EDGE_CONN(conn)->rend_data); + hs_ident_edge_conn_free(TO_EDGE_CONN(conn)->hs_ident); } if (conn->type == CONN_TYPE_CONTROL) { control_connection_t *control_conn = TO_CONTROL_CONN(conn); @@ -636,6 +638,7 @@ connection_free_(connection_t *conn) } rend_data_free(dir_conn->rend_data); + hs_ident_dir_conn_free(dir_conn->hs_ident); if (dir_conn->guard_state) { /* Cancel before freeing, if it's still there. */ entry_guard_cancel(&dir_conn->guard_state); diff --git a/src/or/hs_common.h b/src/or/hs_common.h index a8fded652a..872fed763a 100644 --- a/src/or/hs_common.h +++ b/src/or/hs_common.h @@ -49,6 +49,12 @@ /* The time period rotation offset as seen in prop224 section [TIME-PERIODS] */ #define HS_TIME_PERIOD_ROTATION_OFFSET (12 * 60) /* minutes */ +/* Type of authentication key used by an introduction point. */ +typedef enum { + HS_AUTH_KEY_TYPE_LEGACY = 1, + HS_AUTH_KEY_TYPE_ED25519 = 2, +} hs_auth_key_type_t; + int hs_check_service_private_dir(const char *username, const char *path, unsigned int dir_group_readable, unsigned int create); diff --git a/src/or/hs_ident.c b/src/or/hs_ident.c new file mode 100644 index 0000000000..5b5dc9aaff --- /dev/null +++ b/src/or/hs_ident.c @@ -0,0 +1,81 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_ident.c + * \brief Contains circuit and connection identifier code for the whole HS + * subsytem. + **/ + +#include "hs_ident.h" + +/* Return a newly allocated circuit identifier. The given public key is copied + * identity_pk into the identifier. */ +hs_ident_circuit_t * +hs_ident_circuit_new(const ed25519_public_key_t *identity_pk, + hs_ident_circuit_type_t circuit_type) +{ + tor_assert(circuit_type == HS_IDENT_CIRCUIT_INTRO || + circuit_type == HS_IDENT_CIRCUIT_RENDEZVOUS); + hs_ident_circuit_t *ident = tor_malloc_zero(sizeof(*ident)); + ed25519_pubkey_copy(&ident->identity_pk, identity_pk); + ident->circuit_type = circuit_type; + return ident; +} + +/* Free the given circuit identifier. */ +void +hs_ident_circuit_free(hs_ident_circuit_t *ident) +{ + if (ident == NULL) { + return; + } + if (ident->auth_key_type == HS_AUTH_KEY_TYPE_LEGACY) { + crypto_pk_free(ident->auth_rsa_pk); + } + memwipe(ident, 0, sizeof(hs_ident_circuit_t)); + tor_free(ident); +} + +/* For a given directory connection identifier src, return a newly allocated + * copy of it. This can't fail. */ +hs_ident_dir_conn_t * +hs_ident_dir_conn_dup(const hs_ident_dir_conn_t *src) +{ + hs_ident_dir_conn_t *ident = tor_malloc_zero(sizeof(*ident)); + memcpy(ident, src, sizeof(*ident)); + return ident; +} + +/* Free the given directory connection identifier. */ +void +hs_ident_dir_conn_free(hs_ident_dir_conn_t *ident) +{ + if (ident == NULL) { + return; + } + memwipe(ident, 0, sizeof(hs_ident_dir_conn_t)); + tor_free(ident); +} + +/* Return a newly allocated edge connection identifier. The given public key + * identity_pk is copied into the identifier. */ +hs_ident_edge_conn_t * +hs_ident_edge_conn_new(const ed25519_public_key_t *identity_pk) +{ + hs_ident_edge_conn_t *ident = tor_malloc_zero(sizeof(*ident)); + ed25519_pubkey_copy(&ident->identity_pk, identity_pk); + return ident; +} + +/* Free the given edge connection identifier. */ +void +hs_ident_edge_conn_free(hs_ident_edge_conn_t *ident) +{ + if (ident == NULL) { + return; + } + memwipe(ident, 0, sizeof(hs_ident_edge_conn_t)); + tor_free(ident); +} + diff --git a/src/or/hs_ident.h b/src/or/hs_ident.h new file mode 100644 index 0000000000..8a7c3598cf --- /dev/null +++ b/src/or/hs_ident.h @@ -0,0 +1,124 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_ident.h + * \brief Header file containing circuit and connection identifier data for + * the whole HS subsytem. + * + * \details + * This interface is used to uniquely identify a hidden service on a circuit + * or connection using the service identity public key. Once the circuit or + * connection subsystem calls in the hidden service one, we use those + * identifiers to lookup the corresponding objects like service, intro point + * and descriptor. + * + * Furthermore, the circuit identifier holds cryptographic material needed for + * the e2e encryption on the rendezvous circuit which is set once the + * rendezvous circuit has opened and ready to be used. + **/ + +#ifndef TOR_HS_IDENT_H +#define TOR_HS_IDENT_H + +#include "crypto.h" +#include "crypto_ed25519.h" + +#include "hs_common.h" + +/* Length of the rendezvous cookie that is used to connect circuits at the + * rendezvous point. */ +#define HS_REND_COOKIE_LEN DIGEST_LEN + +/* Type of circuit an hs_ident_t object is associated with. */ +typedef enum { + HS_IDENT_CIRCUIT_INTRO = 1, + HS_IDENT_CIRCUIT_RENDEZVOUS = 2, +} hs_ident_circuit_type_t; + +/* Client and service side circuit identifier that is used for hidden service + * circuit establishment. Not all fields contain data, it depends on the + * circuit purpose. This is attached to an origin_circuit_t. All fields are + * used by both client and service. */ +typedef struct hs_ident_circuit_t { + /* (All circuit) The public key used to uniquely identify the service. It is + * the one found in the onion address. */ + ed25519_public_key_t identity_pk; + + /* (All circuit) The type of circuit this identifier is attached to. + * Accessors of the fields in this object assert non fatal on this circuit + * type. In other words, if a rendezvous field is being accessed, the + * circuit type MUST BE of type HS_IDENT_CIRCUIT_RENDEZVOUS. This value is + * set when an object is initialized in its constructor. */ + hs_ident_circuit_type_t circuit_type; + + /* (Only intro point circuit) Which type of authentication key this + * circuit identifier is using. */ + hs_auth_key_type_t auth_key_type; + + /* (Only intro point circuit) Introduction point authentication key. In + * legacy mode, we use an RSA key else an ed25519 public key. */ + crypto_pk_t *auth_rsa_pk; + ed25519_public_key_t auth_ed25519_pk; + + /* (Only rendezvous circuit) Rendezvous cookie sent from the client to the + * service with an INTRODUCE1 cell and used by the service in an + * RENDEZVOUS1 cell. */ + uint8_t rendezvous_cookie[HS_REND_COOKIE_LEN]; + + /* (Only rendezvous circuit) The HANDSHAKE_INFO needed in the RENDEZVOUS1 + * cell of the service. The construction is as follows: + * SERVER_PK [32 bytes] + * AUTH_MAC [32 bytes] + */ + uint8_t rendezvous_handshake_info[CURVE25519_PUBKEY_LEN + DIGEST256_LEN]; + + /* (Only rendezvous circuit) The NTOR_KEY_SEED needed for key derivation for + * the e2e encryption with the client on the circuit. */ + uint8_t rendezvous_ntor_key_seed[DIGEST256_LEN]; + + /* (Only rendezvous circuit) Number of streams associated with this + * rendezvous circuit. We track this because there is a check on a maximum + * value. */ + uint64_t num_rdv_streams; +} hs_ident_circuit_t; + +/* Client and service side directory connection identifier used for a + * directory connection to identify which service is being queried. This is + * attached to a dir_connection_t. */ +typedef struct hs_ident_dir_conn_t { + /* The public key used to uniquely identify the service. It is the one found + * in the onion address. */ + ed25519_public_key_t identity_pk; + + /* XXX: Client authorization. */ +} hs_ident_dir_conn_t; + +/* Client and service side edge connection identifier used for an edge + * connection to identify which service is being queried. This is attached to + * a edge_connection_t. */ +typedef struct hs_ident_edge_conn_t { + /* The public key used to uniquely identify the service. It is the one found + * in the onion address. */ + ed25519_public_key_t identity_pk; + + /* XXX: Client authorization. */ +} hs_ident_edge_conn_t; + +/* Circuit identifier API. */ +hs_ident_circuit_t *hs_ident_circuit_new( + const ed25519_public_key_t *identity_pk, + hs_ident_circuit_type_t circuit_type); +void hs_ident_circuit_free(hs_ident_circuit_t *ident); + +/* Directory connection identifier API. */ +hs_ident_dir_conn_t *hs_ident_dir_conn_dup(const hs_ident_dir_conn_t *src); +void hs_ident_dir_conn_free(hs_ident_dir_conn_t *ident); + +/* Edge connection identifier API. */ +hs_ident_edge_conn_t *hs_ident_edge_conn_new( + const ed25519_public_key_t *identity_pk); +void hs_ident_edge_conn_free(hs_ident_edge_conn_t *ident); + +#endif /* TOR_HS_IDENT_H */ + diff --git a/src/or/include.am b/src/or/include.am index 1ef5afa013..e800db5604 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -50,16 +50,17 @@ LIBTOR_A_SOURCES = \ src/or/dnsserv.c \ src/or/fp_pair.c \ src/or/geoip.c \ - src/or/hs_intropoint.c \ - src/or/hs_circuitmap.c \ - src/or/hs_ntor.c \ - src/or/hs_service.c \ + src/or/hs_cache.c \ + src/or/hs_circuitmap.c \ + src/or/hs_common.c \ + src/or/hs_descriptor.c \ + src/or/hs_ident.c \ + src/or/hs_intropoint.c \ + src/or/hs_ntor.c \ + src/or/hs_service.c \ src/or/entrynodes.c \ src/or/ext_orport.c \ src/or/hibernate.c \ - src/or/hs_cache.c \ - src/or/hs_common.c \ - src/or/hs_descriptor.c \ src/or/keypin.c \ src/or/main.c \ src/or/microdesc.c \ @@ -182,6 +183,7 @@ ORHEADERS = \ src/or/hs_cache.h \ src/or/hs_common.h \ src/or/hs_descriptor.h \ + src/or/hs_ident.h \ src/or/hs_intropoint.h \ src/or/hs_circuitmap.h \ src/or/hs_ntor.h \ diff --git a/src/or/or.h b/src/or/or.h index 1f55b55062..db77d0837f 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -846,6 +846,11 @@ rend_data_v2_t *TO_REND_DATA_V2(const rend_data_t *d) return DOWNCAST(rend_data_v2_t, d); } +/* Stub because we can't include hs_ident.h. */ +typedef struct hs_ident_edge_conn_t hs_ident_edge_conn_t; +typedef struct hs_ident_dir_conn_t hs_ident_dir_conn_t; +typedef struct hs_ident_circuit_t hs_ident_circuit_t; + /** Time interval for tracking replays of DH public keys received in * INTRODUCE2 cells. Used only to avoid launching multiple * simultaneous attempts to connect to the same rendezvous point. */ @@ -1633,6 +1638,10 @@ typedef struct edge_connection_t { * an exit)? */ rend_data_t *rend_data; + /* Hidden service connection identifier that is which service is being + * queried? */ + hs_ident_edge_conn_t *hs_ident; + uint32_t address_ttl; /**< TTL for address-to-addr mapping on exit * connection. Exit connections only. */ uint32_t begincell_flags; /** Flags sent or received in the BEGIN cell @@ -1783,6 +1792,10 @@ typedef struct dir_connection_t { /** What rendezvous service are we querying for? */ rend_data_t *rend_data; + /* Hidden service connection identifier that is which service is being + * queried? */ + hs_ident_dir_conn_t *hs_ident; + /** If this is a one-hop connection, tracks the state of the directory guard * for this connection (if any). */ struct circuit_guard_state_t *guard_state; @@ -3186,6 +3199,10 @@ typedef struct origin_circuit_t { /** Holds all rendezvous data on either client or service side. */ rend_data_t *rend_data; + /** Holds hidden service identifier on either client or service side. This + * is for both introduction and rendezvous circuit. */ + hs_ident_circuit_t *hs_ident; + /** Holds the data that the entry guard system uses to track the * status of the guard this circuit is using, and thereby to determine * whether this circuit can be used. */ From ba928e1ac8f13714aa6b619dd6f12ae89240d6ff Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Tue, 2 May 2017 16:18:10 +0300 Subject: [PATCH 02/19] Refactor the HS ntor key expansion to fit the e2e circuit API. We want to use the circuit_init_cpath_crypto() function to setup our cpath, and that function accepts a key array as input. So let's make our HS ntor key expansion function also return a key array as output, instead of a struct. Also, we actually don't need KH from the key expansion, so the key expansion output can be one DIGEST256_LEN shorter. See here for more info: https://trac.torproject.org/projects/tor/ticket/22052#comment:3 --- src/or/hs_ntor.c | 37 ++++++++----------------------------- src/or/hs_ntor.h | 23 ++++++----------------- 2 files changed, 14 insertions(+), 46 deletions(-) diff --git a/src/or/hs_ntor.c b/src/or/hs_ntor.c index 119899817e..668ef221b8 100644 --- a/src/or/hs_ntor.c +++ b/src/or/hs_ntor.c @@ -578,49 +578,28 @@ hs_ntor_client_rendezvous2_mac_is_good( /* Input length to KDF for key expansion */ #define NTOR_KEY_EXPANSION_KDF_INPUT_LEN (DIGEST256_LEN + M_HSEXPAND_LEN) -/* Output length of KDF for key expansion */ -#define NTOR_KEY_EXPANSION_KDF_OUTPUT_LEN (DIGEST256_LEN*3+CIPHER256_KEY_LEN*2) -/** Given the rendezvous key material in hs_ntor_rend_cell_keys, do the - * circuit key expansion as specified by section '4.2.1. Key expansion' and - * return a hs_ntor_rend_circuit_keys_t structure with the computed keys. */ -hs_ntor_rend_circuit_keys_t * -hs_ntor_circuit_key_expansion( - const hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys) +/** Given the rendezvous key seed in ntor_key_seed (of size + * DIGEST256_LEN), do the circuit key expansion as specified by section + * '4.2.1. Key expansion' and place the keys in keys_out (which must be + * of size HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN). */ +void +hs_ntor_circuit_key_expansion(const uint8_t *ntor_key_seed, uint8_t *keys_out) { uint8_t *ptr; uint8_t kdf_input[NTOR_KEY_EXPANSION_KDF_INPUT_LEN]; - uint8_t keys[NTOR_KEY_EXPANSION_KDF_OUTPUT_LEN]; crypto_xof_t *xof; - hs_ntor_rend_circuit_keys_t *rend_circuit_keys = NULL; /* Let's build the input to the KDF */ ptr = kdf_input; - APPEND(ptr, hs_ntor_rend_cell_keys->ntor_key_seed, DIGEST256_LEN); + APPEND(ptr, ntor_key_seed, DIGEST256_LEN); APPEND(ptr, M_HSEXPAND, strlen(M_HSEXPAND)); tor_assert(ptr == kdf_input + sizeof(kdf_input)); /* Generate the keys */ xof = crypto_xof_new(); crypto_xof_add_bytes(xof, kdf_input, sizeof(kdf_input)); - crypto_xof_squeeze_bytes(xof, keys, sizeof(keys)); + crypto_xof_squeeze_bytes(xof, keys_out, HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN); crypto_xof_free(xof); - - /* Generate keys structure and assign keys to it */ - rend_circuit_keys = tor_malloc_zero(sizeof(hs_ntor_rend_circuit_keys_t)); - ptr = keys; - memcpy(rend_circuit_keys->KH, ptr, DIGEST256_LEN); - ptr += DIGEST256_LEN;; - memcpy(rend_circuit_keys->Df, ptr, DIGEST256_LEN); - ptr += DIGEST256_LEN; - memcpy(rend_circuit_keys->Db, ptr, DIGEST256_LEN); - ptr += DIGEST256_LEN; - memcpy(rend_circuit_keys->Kf, ptr, CIPHER256_KEY_LEN); - ptr += CIPHER256_KEY_LEN; - memcpy(rend_circuit_keys->Kb, ptr, CIPHER256_KEY_LEN); - ptr += CIPHER256_KEY_LEN; - tor_assert(ptr == keys + sizeof(keys)); - - return rend_circuit_keys; } diff --git a/src/or/hs_ntor.h b/src/or/hs_ntor.h index cd75f46a4c..3a97e17acc 100644 --- a/src/or/hs_ntor.h +++ b/src/or/hs_ntor.h @@ -6,6 +6,10 @@ #include "or.h" +/* Output length of KDF for key expansion */ +#define HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN \ + (DIGEST256_LEN*2 + CIPHER256_KEY_LEN*2) + /* Key material needed to encode/decode INTRODUCE1 cells */ typedef struct { /* Key used for encryption of encrypted INTRODUCE1 blob */ @@ -23,21 +27,6 @@ typedef struct { uint8_t ntor_key_seed[DIGEST256_LEN]; } hs_ntor_rend_cell_keys_t; -/* Key material resulting from key expansion as detailed in section "4.2.1. Key - * expansion" of rend-spec-ng.txt. */ -typedef struct { - /* Per-circuit key material used in ESTABLISH_INTRO cell */ - uint8_t KH[DIGEST256_LEN]; - /* Authentication key for outgoing RELAY cells */ - uint8_t Df[DIGEST256_LEN]; - /* Authentication key for incoming RELAY cells */ - uint8_t Db[DIGEST256_LEN]; - /* Encryption key for outgoing RELAY cells */ - uint8_t Kf[CIPHER256_KEY_LEN]; - /* Decryption key for incoming RELAY cells */ - uint8_t Kb[CIPHER256_KEY_LEN]; -} hs_ntor_rend_circuit_keys_t; - int hs_ntor_client_get_introduce1_keys( const ed25519_public_key_t *intro_auth_pubkey, const curve25519_public_key_t *intro_enc_pubkey, @@ -66,8 +55,8 @@ int hs_ntor_service_get_rendezvous1_keys( const curve25519_public_key_t *client_ephemeral_enc_pubkey, hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out); -hs_ntor_rend_circuit_keys_t *hs_ntor_circuit_key_expansion( - const hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys); +void hs_ntor_circuit_key_expansion(const uint8_t *ntor_key_seed, + uint8_t *keys_out); int hs_ntor_client_rendezvous2_mac_is_good( const hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys, From 83249015c2741be55cf3d084660e6209323b5a1a Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Tue, 2 May 2017 16:19:12 +0300 Subject: [PATCH 03/19] Refactor circuit_init_cpath_crypto() to do prop224 rend circuits. circuit_init_cpath_crypto() is responsible for creating the cpath of legacy SHA1/AES128 circuits currently. We want to use it for prop224 circuits, so we refactor it to create circuits with SHA3-256 and AES256 as well. Signed-off-by: David Goulet --- src/or/circuitbuild.c | 54 ++++++++++++++++++++++++++++++++----------- src/or/circuitbuild.h | 2 +- src/or/rendclient.c | 2 +- src/or/rendservice.c | 2 +- 4 files changed, 43 insertions(+), 17 deletions(-) diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 240c64b6d1..ec7ca2c4ce 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -1325,9 +1325,14 @@ circuit_extend(cell_t *cell, circuit_t *circ) return 0; } -/** Initialize cpath-\>{f|b}_{crypto|digest} from the key material in - * key_data. key_data must contain CPATH_KEY_MATERIAL bytes, which are - * used as follows: +/** Initialize cpath-\>{f|b}_{crypto|digest} from the key material in key_data. + * + * If is_hs_v3 is set, this cpath will be used for next gen hidden + * service circuits and key_data must be at least + * HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN bytes in length. + * + * If is_hs_v3 is not set, key_data must contain CPATH_KEY_MATERIAL + * bytes, which are used as follows: * - 20 to initialize f_digest * - 20 to initialize b_digest * - 16 to key f_crypto @@ -1337,28 +1342,49 @@ circuit_extend(cell_t *cell, circuit_t *circ) */ int circuit_init_cpath_crypto(crypt_path_t *cpath, const char *key_data, - int reverse) + 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(cpath); tor_assert(key_data); tor_assert(!(cpath->f_crypto || cpath->b_crypto || cpath->f_digest || cpath->b_digest)); - cpath->f_digest = crypto_digest_new(); - crypto_digest_add_bytes(cpath->f_digest, key_data, DIGEST_LEN); - cpath->b_digest = crypto_digest_new(); - crypto_digest_add_bytes(cpath->b_digest, key_data+DIGEST_LEN, DIGEST_LEN); + /* If we are using this cpath 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; + cpath->f_digest = crypto_digest256_new(DIGEST_SHA3_256); + cpath->b_digest = crypto_digest256_new(DIGEST_SHA3_256); + } else { + digest_len = DIGEST_LEN; + cipher_key_len = CIPHER_KEY_LEN; + cpath->f_digest = crypto_digest_new(); + cpath->b_digest = crypto_digest_new(); + } - if (!(cpath->f_crypto = - crypto_cipher_new(key_data+(2*DIGEST_LEN)))) { + tor_assert(digest_len != 0); + tor_assert(cipher_key_len != 0); + + crypto_digest_add_bytes(cpath->f_digest, key_data, digest_len); + crypto_digest_add_bytes(cpath->b_digest, key_data+digest_len, digest_len); + + cpath->f_crypto = crypto_cipher_new_with_bits(key_data+(2*digest_len), + cipher_key_len*8); + if (!cpath->f_crypto) { log_warn(LD_BUG,"Forward cipher initialization failed."); return -1; } - if (!(cpath->b_crypto = - crypto_cipher_new(key_data+(2*DIGEST_LEN)+CIPHER_KEY_LEN))) { + + cpath->b_crypto = crypto_cipher_new_with_bits( + key_data+(2*digest_len)+cipher_key_len, + cipher_key_len*8); + if (!cpath->b_crypto) { log_warn(LD_BUG,"Backward cipher initialization failed."); return -1; } @@ -1424,7 +1450,7 @@ circuit_finish_handshake(origin_circuit_t *circ, onion_handshake_state_release(&hop->handshake_state); - if (circuit_init_cpath_crypto(hop, keys, 0)<0) { + if (circuit_init_cpath_crypto(hop, keys, 0, 0)<0) { return -END_CIRC_REASON_TORPROTOCOL; } @@ -1512,7 +1538,7 @@ onionskin_answer(or_circuit_t *circ, log_debug(LD_CIRC,"init digest forward 0x%.8x, backward 0x%.8x.", (unsigned int)get_uint32(keys), (unsigned int)get_uint32(keys+20)); - if (circuit_init_cpath_crypto(tmp_cpath, keys, 0)<0) { + if (circuit_init_cpath_crypto(tmp_cpath, keys, 0, 0)<0) { log_warn(LD_BUG,"Circuit initialization failed"); tor_free(tmp_cpath); return -1; diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index 45d9b2fb75..ae6864ea57 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -32,7 +32,7 @@ int circuit_send_next_onion_skin(origin_circuit_t *circ); void circuit_note_clock_jumped(int seconds_elapsed); int circuit_extend(cell_t *cell, circuit_t *circ); int circuit_init_cpath_crypto(crypt_path_t *cpath, const char *key_data, - int reverse); + int reverse, int is_hs_v3); struct created_cell_t; int circuit_finish_handshake(origin_circuit_t *circ, const struct created_cell_t *created_cell); diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 9bc2d6289d..3cf67b56e3 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -1184,7 +1184,7 @@ rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request, goto err; } /* ... and set up cpath. */ - if (circuit_init_cpath_crypto(hop, keys+DIGEST_LEN, 0)<0) + if (circuit_init_cpath_crypto(hop, keys+DIGEST_LEN, 0, 0)<0) goto err; /* Check whether the digest is right... */ diff --git a/src/or/rendservice.c b/src/or/rendservice.c index f3b78c4663..d389a87ae3 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -2195,7 +2195,7 @@ rend_service_receive_introduction(origin_circuit_t *circuit, cpath->rend_dh_handshake_state = dh; dh = NULL; - if (circuit_init_cpath_crypto(cpath,keys+DIGEST_LEN,1)<0) + if (circuit_init_cpath_crypto(cpath,keys+DIGEST_LEN,1, 0)<0) goto err; memcpy(cpath->rend_circ_nonce, keys, DIGEST_LEN); From 0b2018a4d078a6ea47678c296c634714ab7eee94 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Tue, 2 May 2017 16:20:26 +0300 Subject: [PATCH 04/19] Refactor legacy code to support hs_ident along with rend_data. The legacy HS circuit code uses rend_data to match between circuits and streams. We refactor some of that code so that it understands hs_ident as well which is used for prop224. --- src/or/circuituse.c | 40 +++++++++++++++++++++++++++++++++------- src/or/connection_edge.c | 8 +++++++- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/or/circuituse.c b/src/or/circuituse.c index 9f9d3abf7c..288b49e3c5 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -42,6 +42,7 @@ #include "control.h" #include "entrynodes.h" #include "hs_common.h" +#include "hs_ident.h" #include "nodelist.h" #include "networkstatus.h" #include "policies.h" @@ -55,6 +56,36 @@ static void circuit_expire_old_circuits_clientside(void); static void circuit_increment_failure_count(void); +/** Check whether the hidden service destination of the stream at + * edge_conn is the same as the destination of the circuit at + * origin_circ. */ +static int +circuit_matches_with_rend_stream(const edge_connection_t *edge_conn, + const origin_circuit_t *origin_circ) +{ + /* Check if this is a v2 rendezvous circ/stream */ + if ((edge_conn->rend_data && !origin_circ->rend_data) || + (!edge_conn->rend_data && origin_circ->rend_data) || + (edge_conn->rend_data && origin_circ->rend_data && + rend_cmp_service_ids(rend_data_get_address(edge_conn->rend_data), + rend_data_get_address(origin_circ->rend_data)))) { + /* this circ is not for this conn */ + return 0; + } + + /* Check if this is a v3 rendezvous circ/stream */ + if ((edge_conn->hs_ident && !origin_circ->hs_ident) || + (!edge_conn->hs_ident && origin_circ->hs_ident) || + (edge_conn->hs_ident && origin_circ->hs_ident && + !ed25519_pubkey_eq(&edge_conn->hs_ident->identity_pk, + &origin_circ->hs_ident->identity_pk))) { + /* this circ is not for this conn */ + return 0; + } + + return 1; +} + /** Return 1 if circ could be returned by circuit_get_best(). * Else return 0. */ @@ -169,14 +200,9 @@ circuit_is_acceptable(const origin_circuit_t *origin_circ, /* can't exit from this router */ return 0; } - } else { /* not general */ + } else { /* not general: this might be a rend circuit */ const edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn); - if ((edge_conn->rend_data && !origin_circ->rend_data) || - (!edge_conn->rend_data && origin_circ->rend_data) || - (edge_conn->rend_data && origin_circ->rend_data && - rend_cmp_service_ids(rend_data_get_address(edge_conn->rend_data), - rend_data_get_address(origin_circ->rend_data)))) { - /* this circ is not for this conn */ + if (!circuit_matches_with_rend_stream(edge_conn, origin_circ)) { return 0; } } diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 8480a35458..9c98c56baa 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -3566,8 +3566,14 @@ int connection_edge_is_rendezvous_stream(const edge_connection_t *conn) { tor_assert(conn); - if (conn->rend_data) + + if (BUG(conn->rend_data && conn->hs_ident)) { + log_warn(LD_BUG, "Connection has both rend_data and hs_ident..."); + } + + if (conn->rend_data || conn->hs_ident) { return 1; + } return 0; } From 0cb66fc900f5198fb063454801af3b4b22867e50 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Fri, 12 May 2017 12:00:43 -0400 Subject: [PATCH 05/19] prop224: Introduce e2e rendezvous circuit code. This commit adds most of the work of #21859. It introduces hs_circuit.c functions that can handle the setup of e2e circuits for prop224 hidden services, and also for legacy hidden service clients. Entry points are: prop224 circuits: hs_circuit_setup_e2e_rend_circ() legacy client-side circuits: hs_circuit_setup_e2e_rend_circ_legacy_client() This commit swaps the old rendclient code to use the new API. I didn't try to accomodate the legacy service-side code in this API, since that's too tangled up and it would mess up the new API considerably IMO (all this service_pending_final_cpath_ref stuff is complicated and I didn't want to change it). Signed-off-by: David Goulet --- src/or/hs_circuit.c | 215 ++++++++++++++++++++++++++++++++++++++++++++ src/or/hs_circuit.h | 23 +++++ src/or/include.am | 2 + src/or/rendclient.c | 52 +---------- 4 files changed, 244 insertions(+), 48 deletions(-) create mode 100644 src/or/hs_circuit.c create mode 100644 src/or/hs_circuit.h diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c new file mode 100644 index 0000000000..f8c95f7b6d --- /dev/null +++ b/src/or/hs_circuit.c @@ -0,0 +1,215 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_circuit.c + **/ + +#include "or.h" +#include "circuitbuild.h" +#include "circuitlist.h" +#include "circuituse.h" +#include "config.h" + +#include "hs_circuit.h" +#include "hs_ident.h" +#include "hs_ntor.h" + +/* A circuit is about to become an e2e rendezvous circuit. Check + * circ_purpose and ensure that it's properly set. Return 0 if circuit + * purpose is properly set, otherwise return -1. */ +static int +rend_circuit_validate_purpose(unsigned int circ_purpose, int is_service_side) +{ + if (is_service_side) { + if (circ_purpose != CIRCUIT_PURPOSE_S_CONNECT_REND) { + log_warn(LD_GENERAL, "HS e2e circuit setup with wrong purpose(%d)", + circ_purpose); + return -1; + } + } + + if (!is_service_side) { + if (circ_purpose != CIRCUIT_PURPOSE_C_REND_READY && + circ_purpose != CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) { + log_warn(LD_GENERAL, "Client e2e circuit setup with wrong purpose(%d)", + circ_purpose); + return -1; + } + } + + return 0; +} + +/* Create and return a crypt path for the final hop of a v3 prop224 rendezvous + * circuit. Initialize the crypt path crypto using the output material from the + * ntor key exchange at ntor_key_seed. + * + * If is_service_side is set, we are the hidden service and the final + * hop of the rendezvous circuit is the client on the other side. */ +static crypt_path_t * +get_rend_cpath(const uint8_t *ntor_key_seed, int is_service_side) +{ + uint8_t keys[HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN]; + crypt_path_t *cpath = NULL; + + /* Do the key expansion */ + hs_ntor_circuit_key_expansion(ntor_key_seed, keys); + + /* Setup the cpath */ + cpath = tor_malloc_zero(sizeof(crypt_path_t)); + cpath->magic = CRYPT_PATH_MAGIC; + + if (circuit_init_cpath_crypto(cpath, (char*)keys, + is_service_side, 1) < 0) { + tor_free(cpath); + goto err; + } + + err: + memwipe(keys, 0, sizeof(keys)); + return cpath; +} + +/* We are a v2 legacy HS client: Create and return a crypt path for the hidden + * service on the other side of the rendezvous circuit circ. Initialize + * the crypt path crypto using the body of the RENDEZVOUS1 cell at + * rend_cell_body (which must be at least DH_KEY_LEN+DIGEST_LEN bytes). + */ +static crypt_path_t * +get_rend_cpath_legacy(origin_circuit_t *circ, const uint8_t *rend_cell_body) +{ + crypt_path_t *hop = NULL; + char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; + + /* first DH_KEY_LEN bytes are g^y from the service. Finish the dh + * handshake...*/ + tor_assert(circ->build_state); + tor_assert(circ->build_state->pending_final_cpath); + hop = circ->build_state->pending_final_cpath; + + tor_assert(hop->rend_dh_handshake_state); + if (crypto_dh_compute_secret(LOG_PROTOCOL_WARN, hop->rend_dh_handshake_state, + (char*)rend_cell_body, DH_KEY_LEN, + keys, DIGEST_LEN+CPATH_KEY_MATERIAL_LEN)<0) { + log_warn(LD_GENERAL, "Couldn't complete DH handshake."); + goto err; + } + /* ... and set up cpath. */ + if (circuit_init_cpath_crypto(hop, keys+DIGEST_LEN, 0, 0)<0) + goto err; + + /* Check whether the digest is right... */ + if (tor_memneq(keys, rend_cell_body+DH_KEY_LEN, DIGEST_LEN)) { + log_warn(LD_PROTOCOL, "Incorrect digest of key material."); + goto err; + } + + /* clean up the crypto stuff we just made */ + crypto_dh_free(hop->rend_dh_handshake_state); + hop->rend_dh_handshake_state = NULL; + + goto done; + + err: + hop = NULL; + + done: + memwipe(keys, 0, sizeof(keys)); + return hop; +} + +/* Append the final hop to the cpath of the rend circ, and mark + * circ ready for use to transfer HS relay cells. */ +static void +finalize_rend_circuit(origin_circuit_t *circ, crypt_path_t *hop, + int is_service_side) +{ + tor_assert(circ); + tor_assert(hop); + + /* Notify the circuit state machine that we are splicing this circuit */ + int new_circ_purpose = is_service_side ? + CIRCUIT_PURPOSE_S_REND_JOINED : CIRCUIT_PURPOSE_C_REND_JOINED; + circuit_change_purpose(TO_CIRCUIT(circ), new_circ_purpose); + + /* All is well. Extend the circuit. */ + hop->state = CPATH_STATE_OPEN; + /* Set the windows to default. These are the windows that the client thinks + * the service has. */ + hop->package_window = circuit_initial_package_window(); + hop->deliver_window = CIRCWINDOW_START; + + /* Now that this circuit has finished connecting to its destination, + * make sure circuit_get_open_circ_or_launch is willing to return it + * so we can actually use it. */ + circ->hs_circ_has_timed_out = 0; + + /* Append the hop to the cpath of this circuit */ + onion_append_to_cpath(&circ->cpath, hop); + + /* In legacy code, 'pending_final_cpath' points to the final hop we just + * appended to the cpath. We set the original pointer to NULL so that we + * don't double free it. */ + if (circ->build_state) { + circ->build_state->pending_final_cpath = NULL; + } + + /* Finally, mark circuit as ready to be used for client streams */ + if (!is_service_side) { + circuit_try_attaching_streams(circ); + } +} + +/* Circuit circ just finished the rend ntor key exchange. Use the key + * exchange output material at ntor_key_seed and setup circ to + * serve as a rendezvous end-to-end circuit between the client and the + * service. If is_service_side is set, then we are the hidden service + * and the other side is the client. + * + * Return 0 if the operation went well; in case of error return -1. */ +int +hs_circuit_setup_e2e_rend_circ(origin_circuit_t *circ, + const uint8_t *ntor_key_seed, + int is_service_side) +{ + if (BUG(rend_circuit_validate_purpose(TO_CIRCUIT(circ)->purpose, + is_service_side)) < 0) { + return -1; + } + + crypt_path_t *hop = get_rend_cpath(ntor_key_seed, is_service_side); + if (!hop) { + log_warn(LD_REND, "Couldn't get v3 %s cpath!", + is_service_side ? "service-side" : "client-side"); + return -1; + } + + finalize_rend_circuit(circ, hop, is_service_side); + + return 0; +} + +/* We are a v2 legacy HS client and we just received a RENDEZVOUS1 cell + * rend_cell_body on circ. Finish up the DH key exchange and then + * extend the crypt path of circ so that the hidden service is on the + * other side. */ +int +hs_circuit_setup_e2e_rend_circ_legacy_client(origin_circuit_t *circ, + const uint8_t *rend_cell_body) +{ + if (BUG(rend_circuit_validate_purpose(TO_CIRCUIT(circ)->purpose, 0)) < 0) { + return -1; + } + + crypt_path_t *hop = get_rend_cpath_legacy(circ, rend_cell_body); + if (!hop) { + log_warn(LD_GENERAL, "Couldn't get v2 cpath."); + return -1; + } + + finalize_rend_circuit(circ, hop, 0); + + return 0; +} + diff --git a/src/or/hs_circuit.h b/src/or/hs_circuit.h new file mode 100644 index 0000000000..1c2924ccad --- /dev/null +++ b/src/or/hs_circuit.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_circuit.h + * \brief Header file containing circuit data for the whole HS subsytem. + **/ + +#ifndef TOR_HS_CIRCUIT_H +#define TOR_HS_CIRCUIT_H + +#include "or.h" + +/* e2e circuit API. */ + +int hs_circuit_setup_e2e_rend_circ(origin_circuit_t *circ, + const uint8_t *ntor_key_seed, + int is_service_side); +int hs_circuit_setup_e2e_rend_circ_legacy_client(origin_circuit_t *circ, + const uint8_t *rend_cell_body); + +#endif /* TOR_HS_CIRCUIT_H */ + diff --git a/src/or/include.am b/src/or/include.am index e800db5604..18868e48f7 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -53,6 +53,7 @@ LIBTOR_A_SOURCES = \ src/or/hs_cache.c \ src/or/hs_circuitmap.c \ src/or/hs_common.c \ + src/or/hs_circuit.c \ src/or/hs_descriptor.c \ src/or/hs_ident.c \ src/or/hs_intropoint.c \ @@ -182,6 +183,7 @@ ORHEADERS = \ src/or/hibernate.h \ src/or/hs_cache.h \ src/or/hs_common.h \ + src/or/hs_circuit.h \ src/or/hs_descriptor.h \ src/or/hs_ident.h \ src/or/hs_intropoint.h \ diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 3cf67b56e3..3d160bdc35 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -17,6 +17,7 @@ #include "connection_edge.h" #include "directory.h" #include "hs_common.h" +#include "hs_circuit.h" #include "main.h" #include "networkstatus.h" #include "nodelist.h" @@ -1150,9 +1151,6 @@ int rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request, size_t request_len) { - crypt_path_t *hop; - char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; - if ((circ->base_.purpose != CIRCUIT_PURPOSE_C_REND_READY && circ->base_.purpose != CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) || !circ->build_state->pending_final_cpath) { @@ -1170,55 +1168,13 @@ rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request, log_info(LD_REND,"Got RENDEZVOUS2 cell from hidden service."); - /* first DH_KEY_LEN bytes are g^y from the service. Finish the dh - * handshake...*/ - tor_assert(circ->build_state); - tor_assert(circ->build_state->pending_final_cpath); - hop = circ->build_state->pending_final_cpath; - tor_assert(hop->rend_dh_handshake_state); - if (crypto_dh_compute_secret(LOG_PROTOCOL_WARN, - hop->rend_dh_handshake_state, (char*)request, - DH_KEY_LEN, - keys, DIGEST_LEN+CPATH_KEY_MATERIAL_LEN)<0) { - log_warn(LD_GENERAL, "Couldn't complete DH handshake."); + if (hs_circuit_setup_e2e_rend_circ_legacy_client(circ, request) < 0) { + log_warn(LD_GENERAL, "Failed to setup circ"); goto err; } - /* ... and set up cpath. */ - if (circuit_init_cpath_crypto(hop, keys+DIGEST_LEN, 0, 0)<0) - goto err; - - /* Check whether the digest is right... */ - if (tor_memneq(keys, request+DH_KEY_LEN, DIGEST_LEN)) { - log_warn(LD_PROTOCOL, "Incorrect digest of key material."); - goto err; - } - - crypto_dh_free(hop->rend_dh_handshake_state); - hop->rend_dh_handshake_state = NULL; - - /* All is well. Extend the circuit. */ - circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_REND_JOINED); - hop->state = CPATH_STATE_OPEN; - /* set the windows to default. these are the windows - * that the client thinks the service has. - */ - hop->package_window = circuit_initial_package_window(); - hop->deliver_window = CIRCWINDOW_START; - - /* Now that this circuit has finished connecting to its destination, - * make sure circuit_get_open_circ_or_launch is willing to return it - * so we can actually use it. */ - circ->hs_circ_has_timed_out = 0; - - onion_append_to_cpath(&circ->cpath, hop); - circ->build_state->pending_final_cpath = NULL; /* prevent double-free */ - - circuit_try_attaching_streams(circ); - - memwipe(keys, 0, sizeof(keys)); return 0; + err: - memwipe(keys, 0, sizeof(keys)); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL); return -1; } From 9ff5613a340f220a202d3f0332f091d812068881 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Tue, 2 May 2017 16:22:00 +0300 Subject: [PATCH 06/19] test: Introduce hs_client_note_connection_attempt_succeeded(). This commit paves the way for the e2e circuit unittests. Add a stub for the prop224 equivalent of rend_client_note_connection_attempt_ended(). That function was needed for tests, since the legacy function would get called when we attach streams and our client-side tests would crash with assert failures on rend_data. This also introduces hs_client.[ch] to the codebase. --- src/or/circuituse.c | 4 ++-- src/or/hs_client.c | 47 +++++++++++++++++++++++++++++++++++++++++++++ src/or/hs_client.h | 16 +++++++++++++++ src/or/include.am | 2 ++ 4 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 src/or/hs_client.c create mode 100644 src/or/hs_client.h diff --git a/src/or/circuituse.c b/src/or/circuituse.c index 288b49e3c5..a3b7066b18 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -42,6 +42,7 @@ #include "control.h" #include "entrynodes.h" #include "hs_common.h" +#include "hs_client.h" #include "hs_ident.h" #include "nodelist.h" #include "networkstatus.h" @@ -2374,8 +2375,7 @@ link_apconn_to_circ(entry_connection_t *apconn, origin_circuit_t *circ, /* We are attaching a stream to a rendezvous circuit. That means * that an attempt to connect to a hidden service just * succeeded. Tell rendclient.c. */ - rend_client_note_connection_attempt_ended( - ENTRY_TO_EDGE_CONN(apconn)->rend_data); + hs_client_note_connection_attempt_succeeded(ENTRY_TO_EDGE_CONN(apconn)); } if (cpath) { /* we were given one; use it */ diff --git a/src/or/hs_client.c b/src/or/hs_client.c new file mode 100644 index 0000000000..12fcf3a491 --- /dev/null +++ b/src/or/hs_client.c @@ -0,0 +1,47 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_service.c + * \brief Implement next generation hidden service client functionality + **/ + +#include "or.h" +#include "hs_circuit.h" +#include "connection_edge.h" +#include "rendclient.h" + +#include "hs_client.h" + +/** A prop224 v3 HS circuit successfully connected to the hidden + * service. Update the stream state at hs_conn_ident appropriately. */ +static void +hs_client_attempt_succeeded(const hs_ident_edge_conn_t *hs_conn_ident) +{ + (void) hs_conn_ident; + + /* TODO: When implementing client side */ + return; +} + +/** A circuit just finished connecting to a hidden service that the stream + * conn has been waiting for. Let the HS subsystem know about this. */ +void +hs_client_note_connection_attempt_succeeded(const edge_connection_t *conn) +{ + tor_assert(connection_edge_is_rendezvous_stream(conn)); + + if (BUG(conn->rend_data && conn->hs_ident)) { + log_warn(LD_BUG, "Stream had both rend_data and hs_ident..." + "Prioritizing hs_ident"); + } + + if (conn->hs_ident) { /* It's v3: pass it to the prop224 handler */ + hs_client_attempt_succeeded(conn->hs_ident); + return; + } else if (conn->rend_data) { /* It's v2: pass it to the legacy handler */ + rend_client_note_connection_attempt_ended(conn->rend_data); + return; + } +} + diff --git a/src/or/hs_client.h b/src/or/hs_client.h new file mode 100644 index 0000000000..4f28937b03 --- /dev/null +++ b/src/or/hs_client.h @@ -0,0 +1,16 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_client.h + * \brief Header file containing client data for the HS subsytem. + **/ + +#ifndef TOR_HS_CLIENT_H +#define TOR_HS_CLIENT_H + +void hs_client_note_connection_attempt_succeeded( + const edge_connection_t *conn); + +#endif /* TOR_HS_CLIENT_H */ + diff --git a/src/or/include.am b/src/or/include.am index 18868e48f7..2f9f1a9c43 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -59,6 +59,7 @@ LIBTOR_A_SOURCES = \ src/or/hs_intropoint.c \ src/or/hs_ntor.c \ src/or/hs_service.c \ + src/or/hs_client.c \ src/or/entrynodes.c \ src/or/ext_orport.c \ src/or/hibernate.c \ @@ -190,6 +191,7 @@ ORHEADERS = \ src/or/hs_circuitmap.h \ src/or/hs_ntor.h \ src/or/hs_service.h \ + src/or/hs_client.h \ src/or/keypin.h \ src/or/main.h \ src/or/microdesc.h \ From 43a73f6eb648632552a20dd17f5736550df75e10 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Tue, 2 May 2017 16:33:49 +0300 Subject: [PATCH 07/19] test: Crypto groundwork for e2e circuit unittests. - Move some crypto structures so that they are visible by tests. - Introduce a func to count number of hops in cpath which will be used by the tests. - Mark a function as mockable. --- src/common/crypto.c | 15 --------------- src/common/crypto.h | 19 +++++++++++++++++++ src/or/circuitbuild.c | 24 ++++++++++++++++++++++++ src/or/circuitbuild.h | 2 ++ src/or/connection_edge.c | 4 ++-- src/or/connection_edge.h | 3 ++- 6 files changed, 49 insertions(+), 18 deletions(-) diff --git a/src/common/crypto.c b/src/common/crypto.c index 0fc8474832..8b214a63b9 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -1839,21 +1839,6 @@ crypto_digest_algorithm_get_length(digest_algorithm_t alg) } } -/** Intermediate information about the digest of a stream of data. */ -struct crypto_digest_t { - digest_algorithm_t algorithm; /**< Which algorithm is in use? */ - /** State for the digest we're using. Only one member of the - * union is usable, depending on the value of algorithm. Note also - * that space for other members might not even be allocated! - */ - union { - SHA_CTX sha1; /**< state for SHA1 */ - SHA256_CTX sha2; /**< state for SHA256 */ - SHA512_CTX sha512; /**< state for SHA512 */ - keccak_state sha3; /**< state for SHA3-[256,512] */ - } d; -}; - /** * Return the number of bytes we need to malloc in order to get a * crypto_digest_t for alg, or the number of bytes we need to wipe diff --git a/src/common/crypto.h b/src/common/crypto.h index c70d91c262..3766830f73 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -20,6 +20,9 @@ #include "testsupport.h" #include "compat.h" +#include +#include "keccak-tiny/keccak-tiny.h" + /* Macro to create an arbitrary OpenSSL version number as used by OPENSSL_VERSION_NUMBER or SSLeay(), since the actual numbers are a bit hard @@ -335,6 +338,22 @@ struct dh_st *crypto_dh_get_dh_(crypto_dh_t *dh); void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in); #ifdef CRYPTO_PRIVATE + +/** Intermediate information about the digest of a stream of data. */ +struct crypto_digest_t { + digest_algorithm_t algorithm; /**< Which algorithm is in use? */ + /** State for the digest we're using. Only one member of the + * union is usable, depending on the value of algorithm. Note also + * that space for other members might not even be allocated! + */ + union { + SHA_CTX sha1; /**< state for SHA1 */ + SHA256_CTX sha2; /**< state for SHA256 */ + SHA512_CTX sha512; /**< state for SHA512 */ + keccak_state sha3; /**< state for SHA3-[256,512] */ + } d; +}; + STATIC int crypto_force_rand_ssleay(void); STATIC int crypto_strongest_rand_raw(uint8_t *out, size_t out_len); diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index ec7ca2c4ce..fdcf3e7a75 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -2338,6 +2338,30 @@ onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop) } } +#ifdef TOR_UNIT_TESTS + +/** Unittest helper function: Count number of hops in cpath linked list. */ +unsigned int +cpath_get_n_hops(crypt_path_t **head_ptr) +{ + unsigned int n_hops = 0; + crypt_path_t *tmp; + + if (!*head_ptr) { + return 0; + } + + tmp = *head_ptr; + if (tmp) { + n_hops++; + tmp = (*head_ptr)->next; + } + + return n_hops; +} + +#endif + /** A helper function used by onion_extend_cpath(). Use purpose * and state and the cpath head (currently populated only * to length cur_len to decide a suitable middle hop for a diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index ae6864ea57..6910b3a083 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -83,6 +83,8 @@ MOCK_DECL(STATIC int, count_acceptable_nodes, (smartlist_t *nodes)); #if defined(ENABLE_TOR2WEB_MODE) || defined(TOR_UNIT_TESTS) STATIC const node_t *pick_tor2web_rendezvous_node(router_crn_flags_t flags, const or_options_t *options); +unsigned int cpath_get_n_hops(crypt_path_t **head_ptr); + #endif #endif diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 9c98c56baa..0e18550a41 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -2455,8 +2455,8 @@ connection_ap_get_begincell_flags(entry_connection_t *ap_conn) * * If ap_conn is broken, mark it for close and return -1. Else return 0. */ -int -connection_ap_handshake_send_begin(entry_connection_t *ap_conn) +MOCK_IMPL(int, +connection_ap_handshake_send_begin,(entry_connection_t *ap_conn)) { char payload[CELL_PAYLOAD_SIZE]; int payload_len; diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h index e4780b3c7d..9987f88b85 100644 --- a/src/or/connection_edge.h +++ b/src/or/connection_edge.h @@ -33,7 +33,8 @@ int connection_edge_finished_connecting(edge_connection_t *conn); void connection_ap_about_to_close(entry_connection_t *edge_conn); void connection_exit_about_to_close(edge_connection_t *edge_conn); -int connection_ap_handshake_send_begin(entry_connection_t *ap_conn); +MOCK_DECL(int, + connection_ap_handshake_send_begin,(entry_connection_t *ap_conn)); int connection_ap_handshake_send_resolve(entry_connection_t *ap_conn); entry_connection_t *connection_ap_make_link(connection_t *partner, From dc3a2037f54bcef14b7021b847edd91d36bf4514 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Tue, 2 May 2017 16:35:22 +0300 Subject: [PATCH 08/19] test: Move some test code to test helpers. Move code to create connection streams and rend_data structures to test_helpers so that we can use them from the e2e rendezvous circuit unittests. --- src/test/include.am | 1 + src/test/rend_test_helpers.c | 16 ++++++ src/test/rend_test_helpers.h | 1 + src/test/test_connection.c | 93 ++-------------------------------- src/test/test_connection.h | 13 +++++ src/test/test_helpers.c | 96 ++++++++++++++++++++++++++++++++++++ src/test/test_helpers.h | 3 ++ src/test/test_rendcache.c | 16 ------ 8 files changed, 133 insertions(+), 106 deletions(-) create mode 100644 src/test/test_connection.h diff --git a/src/test/include.am b/src/test/include.am index 29ba1ce7c9..91feae45be 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -270,6 +270,7 @@ noinst_HEADERS+= \ src/test/test.h \ src/test/test_helpers.h \ src/test/test_dir_common.h \ + src/test/test_connection.h \ src/test/test_descriptors.inc \ src/test/example_extrainfo.inc \ src/test/failing_routerdescs.inc \ diff --git a/src/test/rend_test_helpers.c b/src/test/rend_test_helpers.c index f7880046fb..095bfecf21 100644 --- a/src/test/rend_test_helpers.c +++ b/src/test/rend_test_helpers.c @@ -71,3 +71,19 @@ create_descriptor(rend_service_descriptor_t **generated, char **service_id, crypto_pk_free(pk2); } +rend_data_t * +mock_rend_data(const char *onion_address) +{ + rend_data_v2_t *v2_data = tor_malloc_zero(sizeof(*v2_data)); + rend_data_t *rend_query = &v2_data->base_; + rend_query->version = 2; + + strlcpy(v2_data->onion_address, onion_address, + sizeof(v2_data->onion_address)); + v2_data->auth_type = REND_NO_AUTH; + rend_query->hsdirs_fp = smartlist_new(); + smartlist_add(rend_query->hsdirs_fp, tor_memdup("aaaaaaaaaaaaaaaaaaaaaaaa", + DIGEST_LEN)); + return rend_query; +} + diff --git a/src/test/rend_test_helpers.h b/src/test/rend_test_helpers.h index 486adba436..6f0ef114de 100644 --- a/src/test/rend_test_helpers.h +++ b/src/test/rend_test_helpers.h @@ -10,6 +10,7 @@ void generate_desc(int time_diff, rend_encoded_v2_service_descriptor_t **desc, char **service_id, int intro_points); void create_descriptor(rend_service_descriptor_t **generated, char **service_id, int intro_points); +rend_data_t *mock_rend_data(const char *onion_address); #endif diff --git a/src/test/test_connection.c b/src/test/test_connection.c index 7e5193b203..f2529026f9 100644 --- a/src/test/test_connection.c +++ b/src/test/test_connection.c @@ -17,9 +17,8 @@ #include "rendcache.h" #include "directory.h" -static void test_conn_lookup_addr_helper(const char *address, - int family, - tor_addr_t *addr); +#include "test_connection.h" +#include "test_helpers.h" static void * test_conn_get_basic_setup(const struct testcase_t *tc); static int test_conn_get_basic_teardown(const struct testcase_t *tc, @@ -62,48 +61,7 @@ static int test_conn_get_rsrc_teardown(const struct testcase_t *tc, #define TEST_CONN_UNATTACHED_STATE (AP_CONN_STATE_CIRCUIT_WAIT) #define TEST_CONN_ATTACHED_STATE (AP_CONN_STATE_CONNECT_WAIT) -#define TEST_CONN_FD_INIT 50 -static int mock_connection_connect_sockaddr_called = 0; -static int fake_socket_number = TEST_CONN_FD_INIT; - -static int -mock_connection_connect_sockaddr(connection_t *conn, - const struct sockaddr *sa, - socklen_t sa_len, - const struct sockaddr *bindaddr, - socklen_t bindaddr_len, - int *socket_error) -{ - (void)sa_len; - (void)bindaddr; - (void)bindaddr_len; - - tor_assert(conn); - tor_assert(sa); - tor_assert(socket_error); - - mock_connection_connect_sockaddr_called++; - - conn->s = fake_socket_number++; - tt_assert(SOCKET_OK(conn->s)); - /* We really should call tor_libevent_initialize() here. Because we don't, - * we are relying on other parts of the code not checking if the_event_base - * (and therefore event->ev_base) is NULL. */ - tt_assert(connection_add_connecting(conn) == 0); - - done: - /* Fake "connected" status */ - return 1; -} - -static int -fake_close_socket(evutil_socket_t sock) -{ - (void)sock; - return 0; -} - -static void +void test_conn_lookup_addr_helper(const char *address, int family, tor_addr_t *addr) { int rv = 0; @@ -122,51 +80,6 @@ test_conn_lookup_addr_helper(const char *address, int family, tor_addr_t *addr) tor_addr_make_null(addr, TEST_CONN_FAMILY); } -static connection_t * -test_conn_get_connection(uint8_t state, uint8_t type, uint8_t purpose) -{ - connection_t *conn = NULL; - tor_addr_t addr; - int socket_err = 0; - int in_progress = 0; - - MOCK(connection_connect_sockaddr, - mock_connection_connect_sockaddr); - MOCK(tor_close_socket, fake_close_socket); - - init_connection_lists(); - - conn = connection_new(type, TEST_CONN_FAMILY); - tt_assert(conn); - - test_conn_lookup_addr_helper(TEST_CONN_ADDRESS, TEST_CONN_FAMILY, &addr); - tt_assert(!tor_addr_is_null(&addr)); - - tor_addr_copy_tight(&conn->addr, &addr); - conn->port = TEST_CONN_PORT; - mock_connection_connect_sockaddr_called = 0; - in_progress = connection_connect(conn, TEST_CONN_ADDRESS_PORT, &addr, - TEST_CONN_PORT, &socket_err); - tt_assert(mock_connection_connect_sockaddr_called == 1); - tt_assert(!socket_err); - tt_assert(in_progress == 0 || in_progress == 1); - - /* fake some of the attributes so the connection looks OK */ - conn->state = state; - conn->purpose = purpose; - assert_connection_ok(conn, time(NULL)); - - UNMOCK(connection_connect_sockaddr); - UNMOCK(tor_close_socket); - return conn; - - /* On failure */ - done: - UNMOCK(connection_connect_sockaddr); - UNMOCK(tor_close_socket); - return NULL; -} - static void * test_conn_get_basic_setup(const struct testcase_t *tc) { diff --git a/src/test/test_connection.h b/src/test/test_connection.h new file mode 100644 index 0000000000..392783b53b --- /dev/null +++ b/src/test/test_connection.h @@ -0,0 +1,13 @@ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** Some constants used by test_connection and helpers */ +#define TEST_CONN_FAMILY (AF_INET) +#define TEST_CONN_ADDRESS "127.0.0.1" +#define TEST_CONN_PORT (12345) +#define TEST_CONN_ADDRESS_PORT "127.0.0.1:12345" +#define TEST_CONN_FD_INIT 50 + +void test_conn_lookup_addr_helper(const char *address, + int family, tor_addr_t *addr); + diff --git a/src/test/test_helpers.c b/src/test/test_helpers.c index 9fada5a675..22d9de3f5b 100644 --- a/src/test/test_helpers.c +++ b/src/test/test_helpers.c @@ -7,9 +7,14 @@ */ #define ROUTERLIST_PRIVATE +#define CONNECTION_PRIVATE +#define MAIN_PRIVATE + #include "orconfig.h" #include "or.h" +#include "connection.h" +#include "main.h" #include "relay.h" #include "routerlist.h" #include "nodelist.h" @@ -17,6 +22,7 @@ #include "test.h" #include "test_helpers.h" +#include "test_connection.h" #ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS DISABLE_GCC_WARNING(overlength-strings) @@ -143,3 +149,93 @@ mock_tor_addr_lookup__fail_on_bad_addrs(const char *name, return tor_addr_lookup__real(name, family, out); } +/*********** Helper funcs for making new connections/streams *****************/ + +/* Helper for test_conn_get_connection() */ +static int +fake_close_socket(evutil_socket_t sock) +{ + (void)sock; + return 0; +} + +static int mock_connection_connect_sockaddr_called = 0; +static int fake_socket_number = TEST_CONN_FD_INIT; + +/* Helper for test_conn_get_connection() */ +static int +mock_connection_connect_sockaddr(connection_t *conn, + const struct sockaddr *sa, + socklen_t sa_len, + const struct sockaddr *bindaddr, + socklen_t bindaddr_len, + int *socket_error) +{ + (void)sa_len; + (void)bindaddr; + (void)bindaddr_len; + + tor_assert(conn); + tor_assert(sa); + tor_assert(socket_error); + + mock_connection_connect_sockaddr_called++; + + conn->s = fake_socket_number++; + tt_assert(SOCKET_OK(conn->s)); + /* We really should call tor_libevent_initialize() here. Because we don't, + * we are relying on other parts of the code not checking if the_event_base + * (and therefore event->ev_base) is NULL. */ + tt_assert(connection_add_connecting(conn) == 0); + + done: + /* Fake "connected" status */ + return 1; +} + +/** Create and return a new connection/stream */ +connection_t * +test_conn_get_connection(uint8_t state, uint8_t type, uint8_t purpose) +{ + connection_t *conn = NULL; + tor_addr_t addr; + int socket_err = 0; + int in_progress = 0; + + MOCK(connection_connect_sockaddr, + mock_connection_connect_sockaddr); + MOCK(tor_close_socket, fake_close_socket); + + init_connection_lists(); + + conn = connection_new(type, TEST_CONN_FAMILY); + tt_assert(conn); + + test_conn_lookup_addr_helper(TEST_CONN_ADDRESS, TEST_CONN_FAMILY, &addr); + tt_assert(!tor_addr_is_null(&addr)); + + tor_addr_copy_tight(&conn->addr, &addr); + conn->port = TEST_CONN_PORT; + mock_connection_connect_sockaddr_called = 0; + in_progress = connection_connect(conn, TEST_CONN_ADDRESS_PORT, &addr, + TEST_CONN_PORT, &socket_err); + tt_assert(mock_connection_connect_sockaddr_called == 1); + tt_assert(!socket_err); + tt_assert(in_progress == 0 || in_progress == 1); + + /* fake some of the attributes so the connection looks OK */ + conn->state = state; + conn->purpose = purpose; + assert_connection_ok(conn, time(NULL)); + + UNMOCK(connection_connect_sockaddr); + UNMOCK(tor_close_socket); + return conn; + + /* On failure */ + done: + UNMOCK(connection_connect_sockaddr); + UNMOCK(tor_close_socket); + return NULL; +} + diff --git a/src/test/test_helpers.h b/src/test/test_helpers.h index 4621631cc1..aa7aff111c 100644 --- a/src/test/test_helpers.h +++ b/src/test/test_helpers.h @@ -20,6 +20,9 @@ void connection_write_to_buf_mock(const char *string, size_t len, int mock_tor_addr_lookup__fail_on_bad_addrs(const char *name, uint16_t family, tor_addr_t *out); +connection_t *test_conn_get_connection(uint8_t state, + uint8_t type, uint8_t purpose); + extern const char TEST_DESCRIPTORS[]; #endif diff --git a/src/test/test_rendcache.c b/src/test/test_rendcache.c index feba8f664e..c1c178930a 100644 --- a/src/test/test_rendcache.c +++ b/src/test/test_rendcache.c @@ -21,22 +21,6 @@ static const int TIME_IN_THE_PAST = -(REND_CACHE_MAX_AGE + \ REND_CACHE_MAX_SKEW + 60); static const int TIME_IN_THE_FUTURE = REND_CACHE_MAX_SKEW + 60; -static rend_data_t * -mock_rend_data(const char *onion_address) -{ - rend_data_v2_t *v2_data = tor_malloc_zero(sizeof(*v2_data)); - rend_data_t *rend_query = &v2_data->base_; - rend_query->version = 2; - - strlcpy(v2_data->onion_address, onion_address, - sizeof(v2_data->onion_address)); - v2_data->auth_type = REND_NO_AUTH; - rend_query->hsdirs_fp = smartlist_new(); - smartlist_add(rend_query->hsdirs_fp, tor_memdup("aaaaaaaaaaaaaaaaaaaaaaaa", - DIGEST_LEN)); - return rend_query; -} - static void test_rend_cache_lookup_entry(void *data) { From 173dd486e3648ff24a7814679ac6e60e1da7a16b Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Tue, 2 May 2017 16:36:59 +0300 Subject: [PATCH 09/19] test: Add client-side unittests for e2e rend circuits. --- src/test/include.am | 1 + src/test/test.c | 1 + src/test/test.h | 1 + src/test/test_helpers.h | 6 +- src/test/test_hs_client.c | 280 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 287 insertions(+), 2 deletions(-) create mode 100644 src/test/test_hs_client.c diff --git a/src/test/include.am b/src/test/include.am index 91feae45be..e7a2e0278b 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -115,6 +115,7 @@ src_test_test_SOURCES = \ src/test/test_extorport.c \ src/test/test_hs.c \ src/test/test_hs_service.c \ + src/test/test_hs_client.c \ src/test/test_hs_intropoint.c \ src/test/test_handles.c \ src/test/test_hs_cache.c \ diff --git a/src/test/test.c b/src/test/test.c index 68f5f90fd7..31b3db3a4f 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1215,6 +1215,7 @@ struct testgroup_t testgroups[] = { { "hs_cache/", hs_cache }, { "hs_descriptor/", hs_descriptor }, { "hs_service/", hs_service_tests }, + { "hs_client/", hs_client_tests }, { "hs_intropoint/", hs_intropoint_tests }, { "introduce/", introduce_tests }, { "keypin/", keypin_tests }, diff --git a/src/test/test.h b/src/test/test.h index 6abaf39e6f..4de0da99fb 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -209,6 +209,7 @@ extern struct testcase_t hs_tests[]; extern struct testcase_t hs_cache[]; extern struct testcase_t hs_descriptor[]; extern struct testcase_t hs_service_tests[]; +extern struct testcase_t hs_client_tests[]; extern struct testcase_t hs_intropoint_tests[]; extern struct testcase_t introduce_tests[]; extern struct testcase_t keypin_tests[]; diff --git a/src/test/test_helpers.h b/src/test/test_helpers.h index aa7aff111c..96a4b594eb 100644 --- a/src/test/test_helpers.h +++ b/src/test/test_helpers.h @@ -1,9 +1,11 @@ -/* Copyright (c) 2014-2017, The Tor Project, Inc. */ +/* Copyright (c) 2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #ifndef TOR_TEST_HELPERS_H #define TOR_TEST_HELPERS_H +#include "or.h" + const char *get_yesterday_date_str(void); circuit_t * dummy_origin_circuit_new(int num_cells); @@ -25,5 +27,5 @@ connection_t *test_conn_get_connection(uint8_t state, extern const char TEST_DESCRIPTORS[]; -#endif +#endif /* TOR_TEST_HELPERS_H */ diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c new file mode 100644 index 0000000000..9e5fe04a62 --- /dev/null +++ b/src/test/test_hs_client.c @@ -0,0 +1,280 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_hs_client.c + * \brief Test prop224 HS client functionality. + */ + +#define CRYPTO_PRIVATE +#define MAIN_PRIVATE +#define TOR_CHANNEL_INTERNAL_ +#define CIRCUITBUILD_PRIVATE +#define CIRCUITLIST_PRIVATE +#define CONNECTION_PRIVATE + +#include "test.h" +#include "test_helpers.h" +#include "log_test_helpers.h" +#include "rend_test_helpers.h" + +#include "config.h" +#include "crypto.h" +#include "channeltls.h" + +#include "hs_circuit.h" +#include "hs_ident.h" +#include "circuitlist.h" +#include "circuitbuild.h" +#include "connection.h" +#include "connection_edge.h" + +static int +mock_connection_ap_handshake_send_begin(entry_connection_t *ap_conn) +{ + (void) ap_conn; + return 0; +} + +/* Test helper function: Setup a circuit and a stream with the same hidden + * service destination, and put them in circ_out and + * conn_out. Make the stream wait for circuits to be established to the + * hidden service. */ +static int +helper_get_circ_and_stream_for_test(origin_circuit_t **circ_out, + connection_t **conn_out, + int is_legacy) +{ + int retval; + channel_tls_t *n_chan=NULL; + rend_data_t *conn_rend_data = NULL; + origin_circuit_t *or_circ = NULL; + connection_t *conn = NULL; + ed25519_public_key_t service_pk; + + /* Make a dummy connection stream and make it wait for our circuit */ + conn = test_conn_get_connection(AP_CONN_STATE_CIRCUIT_WAIT, + CONN_TYPE_AP /* ??? */, + 0); + if (is_legacy) { + /* Legacy: Setup rend_data of stream */ + char service_id[REND_SERVICE_ID_LEN_BASE32+1] = {0}; + TO_EDGE_CONN(conn)->rend_data = mock_rend_data(service_id); + conn_rend_data = TO_EDGE_CONN(conn)->rend_data; + } else { + /* prop224: Setup hs conn identifier on the stream */ + ed25519_secret_key_t sk; + tt_int_op(0, OP_EQ, ed25519_secret_key_generate(&sk, 0)); + tt_int_op(0, OP_EQ, ed25519_public_key_generate(&service_pk, &sk)); + + /* Setup hs_conn_identifier of stream */ + TO_EDGE_CONN(conn)->hs_ident = hs_ident_edge_conn_new(&service_pk); + } + + /* Make it wait for circuit */ + connection_ap_mark_as_pending_circuit(TO_ENTRY_CONN(conn)); + + /* This is needed to silence a BUG warning from + connection_edge_update_circuit_isolation() */ + TO_ENTRY_CONN(conn)->original_dest_address = + tor_strdup(TO_ENTRY_CONN(conn)->socks_request->address); + + /****************************************************/ + + /* Now make dummy circuit */ + or_circ = origin_circuit_new(); + + or_circ->base_.purpose = CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED; + + or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t)); + or_circ->build_state->is_internal = 1; + + if (is_legacy) { + /* Legacy: Setup rend data and final cpath */ + or_circ->build_state->pending_final_cpath = + tor_malloc_zero(sizeof(crypt_path_t)); + or_circ->build_state->pending_final_cpath->magic = CRYPT_PATH_MAGIC; + or_circ->build_state->pending_final_cpath->rend_dh_handshake_state = + crypto_dh_new(DH_TYPE_REND); + tt_assert( + or_circ->build_state->pending_final_cpath->rend_dh_handshake_state); + retval = crypto_dh_generate_public( + or_circ->build_state->pending_final_cpath->rend_dh_handshake_state); + tt_int_op(retval, ==, 0); + or_circ->rend_data = rend_data_dup(conn_rend_data); + } else { + /* prop224: Setup hs ident on the circuit */ + or_circ->hs_ident = hs_ident_circuit_new(&service_pk, + HS_IDENT_CIRCUIT_RENDEZVOUS); + } + + TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN; + + /* fake n_chan */ + n_chan = tor_malloc_zero(sizeof(channel_tls_t)); + n_chan->base_.global_identifier = 1; + or_circ->base_.n_chan = &(n_chan->base_); + + *circ_out = or_circ; + *conn_out = conn; + + return 0; + + done: + /* something failed */ + return -1; +} + +/* Test: Ensure that setting up legacy e2e rendezvous circuits works + * correctly. */ +static void +test_e2e_rend_circuit_setup_legacy(void *arg) +{ + int retval; + origin_circuit_t *or_circ = NULL; + connection_t *conn = NULL; + + (void) arg; + + /** In this test we create a v2 legacy HS stream and a circuit with the same + * hidden service destination. We make the stream wait for circuits to be + * established to the hidden service, and then we complete the circuit using + * the hs_circuit_setup_e2e_rend_circ_legacy_client() function. We then + * check that the end-to-end cpath was setup correctly and that the stream + * was attached to the circuit as expected. */ + + MOCK(connection_ap_handshake_send_begin, + mock_connection_ap_handshake_send_begin); + + /* Setup */ + retval = helper_get_circ_and_stream_for_test( &or_circ, &conn, 1); + tt_int_op(retval, OP_EQ, 0); + tt_assert(or_circ); + tt_assert(conn); + + /* Check number of hops */ + retval = cpath_get_n_hops(&or_circ->cpath); + tt_int_op(retval, ==, 0); + + /* Check that our stream is not attached on any circuits */ + tt_assert(!TO_EDGE_CONN(conn)->on_circuit); + + /********************************************** */ + + /* Make a good RENDEZVOUS1 cell body because it needs to pass key exchange + * digest verification... */ + uint8_t rend_cell_body[DH_KEY_LEN+DIGEST_LEN] = {2}; + { + char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; + crypto_dh_t *dh_state = + or_circ->build_state->pending_final_cpath->rend_dh_handshake_state; + /* compute and overwrite digest of cell body with the right value */ + retval = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, dh_state, + (char*)rend_cell_body, DH_KEY_LEN, + keys, DIGEST_LEN+CPATH_KEY_MATERIAL_LEN); + tt_int_op(retval, OP_GT, 0); + memcpy(rend_cell_body+DH_KEY_LEN, keys, DIGEST_LEN); + } + + /* Setup the circuit */ + retval = hs_circuit_setup_e2e_rend_circ_legacy_client(or_circ, + rend_cell_body); + tt_int_op(retval, OP_EQ, 0); + + /**********************************************/ + + /* See that a hop was added to the circuit's cpath */ + retval = cpath_get_n_hops(&or_circ->cpath); + tt_int_op(retval, OP_EQ, 1); + + /* Check the digest algo */ + tt_int_op(or_circ->cpath->f_digest->algorithm, OP_EQ, DIGEST_SHA1); + tt_int_op(or_circ->cpath->b_digest->algorithm, OP_EQ, DIGEST_SHA1); + tt_assert(or_circ->cpath->f_crypto); + tt_assert(or_circ->cpath->b_crypto); + + /* Ensure that circ purpose was changed */ + tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_C_REND_JOINED); + + /* Test that stream got attached */ + tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, TO_CIRCUIT(or_circ)); + + done: + connection_free_(conn); + tor_free(TO_CIRCUIT(or_circ)->n_chan); + circuit_free(TO_CIRCUIT(or_circ)); +} + +/* Test: Ensure that setting up v3 rendezvous circuits works correctly. */ +static void +test_e2e_rend_circuit_setup(void *arg) +{ + uint8_t ntor_key_seed[DIGEST256_LEN] = {0}; + origin_circuit_t *or_circ; + int retval; + connection_t *conn = NULL; + + (void) arg; + + /** In this test we create a prop224 v3 HS stream and a circuit with the same + * hidden service destination. We make the stream wait for circuits to be + * established to the hidden service, and then we complete the circuit using + * the hs_circuit_setup_e2e_rend_circ() function. We then check that the + * end-to-end cpath was setup correctly and that the stream was attached to + * the circuit as expected. */ + + MOCK(connection_ap_handshake_send_begin, + mock_connection_ap_handshake_send_begin); + + /* Setup */ + retval = helper_get_circ_and_stream_for_test( &or_circ, &conn, 0); + tt_int_op(retval, OP_EQ, 0); + tt_assert(or_circ); + tt_assert(conn); + + /* Check number of hops: There should be no hops yet to this circ */ + retval = cpath_get_n_hops(&or_circ->cpath); + tt_int_op(retval, ==, 0); + tt_assert(!or_circ->cpath); + + /* Check that our stream is not attached on any circuits */ + tt_assert(!TO_EDGE_CONN(conn)->on_circuit); + + /**********************************************/ + + /* Setup the circuit */ + retval = hs_circuit_setup_e2e_rend_circ(or_circ, ntor_key_seed, 0); + tt_int_op(retval, OP_EQ, 0); + + /**********************************************/ + + /* See that a hop was added to the circuit's cpath */ + retval = cpath_get_n_hops(&or_circ->cpath); + tt_int_op(retval, OP_EQ, 1); + + /* Check that the crypt path has prop224 algorithm parameters */ + tt_int_op(or_circ->cpath->f_digest->algorithm, OP_EQ, DIGEST_SHA3_256); + tt_int_op(or_circ->cpath->b_digest->algorithm, OP_EQ, DIGEST_SHA3_256); + tt_assert(or_circ->cpath->f_crypto); + tt_assert(or_circ->cpath->b_crypto); + + /* Ensure that circ purpose was changed */ + tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_C_REND_JOINED); + + /* Test that stream got attached */ + tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, TO_CIRCUIT(or_circ)); + + done: + connection_free_(conn); + tor_free(TO_CIRCUIT(or_circ)->n_chan); + circuit_free(TO_CIRCUIT(or_circ)); +} + +struct testcase_t hs_client_tests[] = { + { "e2e_rend_circuit_setup_legacy", test_e2e_rend_circuit_setup_legacy, + TT_FORK, NULL, NULL }, + { "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup, + TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + From 9bccc04f8d8e5a8b7d9ae4f18c9fe2c3264bef0c Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Tue, 2 May 2017 16:37:17 +0300 Subject: [PATCH 10/19] test: Add service-side unittests for e2e rendezvous circuits. --- src/test/test_hs_service.c | 88 +++++++++++++++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 5 deletions(-) diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index fcfb3b992d..b5aaa0c75e 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -6,20 +6,34 @@ * \brief Test hidden service functionality. */ +#define CIRCUITBUILD_PRIVATE +#define CIRCUITLIST_PRIVATE +#define CONNECTION_PRIVATE +#define CRYPTO_PRIVATE #define HS_COMMON_PRIVATE -#define HS_SERVICE_PRIVATE #define HS_INTROPOINT_PRIVATE +#define MAIN_PRIVATE +#define TOR_CHANNEL_INTERNAL_ #include "test.h" #include "log_test_helpers.h" -#include "crypto.h" +#include "rend_test_helpers.h" -#include "hs/cell_establish_intro.h" +#include "or.h" +#include "channeltls.h" +#include "circuitbuild.h" +#include "circuitlist.h" +#include "circuituse.h" +#include "config.h" +#include "connection.h" +#include "hs_circuit.h" #include "hs_common.h" -#include "hs_service.h" +#include "hs_ident.h" #include "hs_intropoint.h" - #include "hs_ntor.h" +#include "hs_service.h" +#include "main.h" +#include "rendservice.h" /** We simulate the creation of an outgoing ESTABLISH_INTRO cell, and then we * parse it from the receiver side. */ @@ -235,6 +249,68 @@ test_time_period(void *arg) ; } +/* Test: Ensure that setting up rendezvous circuits works correctly. */ +static void +test_e2e_rend_circuit_setup(void *arg) +{ + ed25519_public_key_t service_pk; + origin_circuit_t *or_circ; + int retval; + + /** In this test we create a v3 prop224 service-side rendezvous circuit. + * We simulate an HS ntor key exchange with a client, and check that + * the circuit was setup correctly and is ready to accept rendezvous data */ + + (void) arg; + + /* Now make dummy circuit */ + { + or_circ = origin_circuit_new(); + + or_circ->base_.purpose = CIRCUIT_PURPOSE_S_CONNECT_REND; + + or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t)); + or_circ->build_state->is_internal = 1; + + /* prop224: Setup hs conn identifier on the stream */ + ed25519_secret_key_t sk; + tt_int_op(0, OP_EQ, ed25519_secret_key_generate(&sk, 0)); + tt_int_op(0, OP_EQ, ed25519_public_key_generate(&service_pk, &sk)); + + or_circ->hs_ident = hs_ident_circuit_new(&service_pk, + HS_IDENT_CIRCUIT_RENDEZVOUS); + + TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN; + } + + /* Check number of hops */ + retval = cpath_get_n_hops(&or_circ->cpath); + tt_int_op(retval, OP_EQ, 0); + + /* Setup the circuit: do the ntor key exchange */ + { + uint8_t ntor_key_seed[DIGEST256_LEN] = {2}; + retval = hs_circuit_setup_e2e_rend_circ(or_circ, ntor_key_seed, 1); + tt_int_op(retval, OP_EQ, 0); + } + + /* See that a hop was added to the circuit's cpath */ + retval = cpath_get_n_hops(&or_circ->cpath); + tt_int_op(retval, OP_EQ, 1); + + /* Check the digest algo */ + tt_int_op(or_circ->cpath->f_digest->algorithm, OP_EQ, DIGEST_SHA3_256); + tt_int_op(or_circ->cpath->b_digest->algorithm, OP_EQ, DIGEST_SHA3_256); + tt_assert(or_circ->cpath->f_crypto); + tt_assert(or_circ->cpath->b_crypto); + + /* Ensure that circ purpose was changed */ + tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_S_REND_JOINED); + + done: + circuit_free(TO_CIRCUIT(or_circ)); +} + struct testcase_t hs_service_tests[] = { { "gen_establish_intro_cell", test_gen_establish_intro_cell, TT_FORK, NULL, NULL }, @@ -244,6 +320,8 @@ struct testcase_t hs_service_tests[] = { NULL, NULL }, { "time_period", test_time_period, TT_FORK, NULL, NULL }, + { "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup, TT_FORK, + NULL, NULL }, END_OF_TESTCASES }; From 91da032e9cc264d88802d0bfc072b0b4117c5f33 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Thu, 6 Jul 2017 13:37:55 +0300 Subject: [PATCH 11/19] Improve docs based on Nick's review. --- src/or/hs_circuit.c | 3 +-- src/or/or.h | 10 ++++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c index f8c95f7b6d..02c9afa309 100644 --- a/src/or/hs_circuit.c +++ b/src/or/hs_circuit.c @@ -135,8 +135,7 @@ finalize_rend_circuit(origin_circuit_t *circ, crypt_path_t *hop, /* All is well. Extend the circuit. */ hop->state = CPATH_STATE_OPEN; - /* Set the windows to default. These are the windows that the client thinks - * the service has. */ + /* Set the windows to default. */ hop->package_window = circuit_initial_package_window(); hop->deliver_window = CIRCWINDOW_START; diff --git a/src/or/or.h b/src/or/or.h index db77d0837f..dcda7e251d 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1638,8 +1638,9 @@ typedef struct edge_connection_t { * an exit)? */ rend_data_t *rend_data; - /* Hidden service connection identifier that is which service is being - * queried? */ + /* Hidden service connection identifier for edge connections. Used by the HS + * client-side code to identify client SOCKS connections and by the + * service-side code to match HS circuits with their streams. */ hs_ident_edge_conn_t *hs_ident; uint32_t address_ttl; /**< TTL for address-to-addr mapping on exit @@ -1792,8 +1793,9 @@ typedef struct dir_connection_t { /** What rendezvous service are we querying for? */ rend_data_t *rend_data; - /* Hidden service connection identifier that is which service is being - * queried? */ + /* Hidden service connection identifier for dir connections: Used by HS + client-side code to fetch HS descriptors, and by the service-side code to + upload descriptors. */ hs_ident_dir_conn_t *hs_ident; /** If this is a one-hop connection, tracks the state of the directory guard From fee95dabcfe21a35e3f5a1ad4b455fe4be1f749a Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Thu, 6 Jul 2017 13:50:16 +0300 Subject: [PATCH 12/19] Turn some warnings into bugs and non-fatal asserts. --- src/or/connection_edge.c | 6 ++---- src/or/hs_circuit.c | 8 ++++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 0e18550a41..5e6041f652 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -3566,10 +3566,8 @@ int connection_edge_is_rendezvous_stream(const edge_connection_t *conn) { tor_assert(conn); - - if (BUG(conn->rend_data && conn->hs_ident)) { - log_warn(LD_BUG, "Connection has both rend_data and hs_ident..."); - } + /* It should not be possible to set both of these structs */ + tor_assert_nonfatal(!(conn->rend_data && conn->hs_ident)); if (conn->rend_data || conn->hs_ident) { return 1; diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c index 02c9afa309..2d186e9f47 100644 --- a/src/or/hs_circuit.c +++ b/src/or/hs_circuit.c @@ -23,8 +23,8 @@ rend_circuit_validate_purpose(unsigned int circ_purpose, int is_service_side) { if (is_service_side) { if (circ_purpose != CIRCUIT_PURPOSE_S_CONNECT_REND) { - log_warn(LD_GENERAL, "HS e2e circuit setup with wrong purpose(%d)", - circ_purpose); + log_fn(LOG_PROTOCOL_WARN, LD_GENERAL, + "HS e2e circuit setup with wrong purpose(%d)", circ_purpose); return -1; } } @@ -32,8 +32,8 @@ rend_circuit_validate_purpose(unsigned int circ_purpose, int is_service_side) if (!is_service_side) { if (circ_purpose != CIRCUIT_PURPOSE_C_REND_READY && circ_purpose != CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) { - log_warn(LD_GENERAL, "Client e2e circuit setup with wrong purpose(%d)", - circ_purpose); + log_fn(LOG_PROTOCOL_WARN, LD_GENERAL, + "Client e2e circuit setup with wrong purpose(%d)", circ_purpose); return -1; } } From b490ae68c7d3a14766db898b48aeb5cee8924de1 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Thu, 6 Jul 2017 13:51:06 +0300 Subject: [PATCH 13/19] Rename rend_circuit_validate_purpose() based on Nick's review. --- src/or/hs_circuit.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c index 2d186e9f47..213f5cbfab 100644 --- a/src/or/hs_circuit.c +++ b/src/or/hs_circuit.c @@ -16,16 +16,16 @@ #include "hs_ntor.h" /* A circuit is about to become an e2e rendezvous circuit. Check - * circ_purpose and ensure that it's properly set. Return 0 if circuit - * purpose is properly set, otherwise return -1. */ + * circ_purpose and ensure that it's properly set. Return true iff + * circuit purpose is properly set, otherwise return false. */ static int -rend_circuit_validate_purpose(unsigned int circ_purpose, int is_service_side) +circuit_purpose_is_correct_for_rend(unsigned int circ_purpose, int is_service_side) { if (is_service_side) { if (circ_purpose != CIRCUIT_PURPOSE_S_CONNECT_REND) { log_fn(LOG_PROTOCOL_WARN, LD_GENERAL, "HS e2e circuit setup with wrong purpose(%d)", circ_purpose); - return -1; + return 0; } } @@ -34,11 +34,11 @@ rend_circuit_validate_purpose(unsigned int circ_purpose, int is_service_side) circ_purpose != CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) { log_fn(LOG_PROTOCOL_WARN, LD_GENERAL, "Client e2e circuit setup with wrong purpose(%d)", circ_purpose); - return -1; + return 0; } } - return 0; + return 1; } /* Create and return a crypt path for the final hop of a v3 prop224 rendezvous @@ -172,8 +172,8 @@ hs_circuit_setup_e2e_rend_circ(origin_circuit_t *circ, const uint8_t *ntor_key_seed, int is_service_side) { - if (BUG(rend_circuit_validate_purpose(TO_CIRCUIT(circ)->purpose, - is_service_side)) < 0) { + if (BUG(!circuit_purpose_is_correct_for_rend(TO_CIRCUIT(circ)->purpose, + is_service_side))) { return -1; } @@ -197,7 +197,9 @@ int hs_circuit_setup_e2e_rend_circ_legacy_client(origin_circuit_t *circ, const uint8_t *rend_cell_body) { - if (BUG(rend_circuit_validate_purpose(TO_CIRCUIT(circ)->purpose, 0)) < 0) { + + if (BUG(!circuit_purpose_is_correct_for_rend( + TO_CIRCUIT(circ)->purpose, 0))) { return -1; } From 2432499705b0df5427604b5bf501bb22ccd73131 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Thu, 6 Jul 2017 13:53:08 +0300 Subject: [PATCH 14/19] Rename get_rend_cpath() to create_rend_cpath(). based on Nick's review. --- src/or/hs_circuit.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c index 213f5cbfab..1729080c20 100644 --- a/src/or/hs_circuit.c +++ b/src/or/hs_circuit.c @@ -48,7 +48,7 @@ circuit_purpose_is_correct_for_rend(unsigned int circ_purpose, int is_service_si * If is_service_side is set, we are the hidden service and the final * hop of the rendezvous circuit is the client on the other side. */ static crypt_path_t * -get_rend_cpath(const uint8_t *ntor_key_seed, int is_service_side) +create_rend_cpath(const uint8_t *ntor_key_seed, int is_service_side) { uint8_t keys[HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN]; crypt_path_t *cpath = NULL; @@ -77,7 +77,7 @@ get_rend_cpath(const uint8_t *ntor_key_seed, int is_service_side) * rend_cell_body (which must be at least DH_KEY_LEN+DIGEST_LEN bytes). */ static crypt_path_t * -get_rend_cpath_legacy(origin_circuit_t *circ, const uint8_t *rend_cell_body) +create_rend_cpath_legacy(origin_circuit_t *circ, const uint8_t *rend_cell_body) { crypt_path_t *hop = NULL; char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; @@ -177,7 +177,7 @@ hs_circuit_setup_e2e_rend_circ(origin_circuit_t *circ, return -1; } - crypt_path_t *hop = get_rend_cpath(ntor_key_seed, is_service_side); + crypt_path_t *hop = create_rend_cpath(ntor_key_seed, is_service_side); if (!hop) { log_warn(LD_REND, "Couldn't get v3 %s cpath!", is_service_side ? "service-side" : "client-side"); @@ -203,7 +203,7 @@ hs_circuit_setup_e2e_rend_circ_legacy_client(origin_circuit_t *circ, return -1; } - crypt_path_t *hop = get_rend_cpath_legacy(circ, rend_cell_body); + crypt_path_t *hop = create_rend_cpath_legacy(circ, rend_cell_body); if (!hop) { log_warn(LD_GENERAL, "Couldn't get v2 cpath."); return -1; From c4d17faf81d8cfe4cf943ba11be03413c58f4d44 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Thu, 6 Jul 2017 14:15:23 +0300 Subject: [PATCH 15/19] Explicit length checks in circuit_init_cpath_crypto(). --- src/or/circuitbuild.c | 23 ++++++++++++++++++----- src/or/circuitbuild.h | 5 +++-- src/or/command.c | 3 ++- src/or/cpuworker.c | 2 +- src/or/hs_circuit.c | 6 ++++-- src/or/rendservice.c | 4 +++- 6 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index fdcf3e7a75..0dd33fc03a 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -46,6 +46,7 @@ #include "crypto.h" #include "directory.h" #include "entrynodes.h" +#include "hs_ntor.h" #include "main.h" #include "microdesc.h" #include "networkstatus.h" @@ -1331,7 +1332,7 @@ circuit_extend(cell_t *cell, circuit_t *circ) * service circuits and key_data must be at least * HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN bytes in length. * - * If is_hs_v3 is not set, key_data must contain CPATH_KEY_MATERIAL + * If is_hs_v3 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 @@ -1339,9 +1340,12 @@ circuit_extend(cell_t *cell, circuit_t *circ) * - 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 -circuit_init_cpath_crypto(crypt_path_t *cpath, const char *key_data, +circuit_init_cpath_crypto(crypt_path_t *cpath, + const char *key_data, size_t key_data_len, int reverse, int is_hs_v3) { crypto_digest_t *tmp_digest; @@ -1354,6 +1358,13 @@ circuit_init_cpath_crypto(crypt_path_t *cpath, const char *key_data, tor_assert(!(cpath->f_crypto || cpath->b_crypto || cpath->f_digest || cpath->b_digest)); + /* Basic key size validation */ + if (is_hs_v3 && BUG(key_data_len != HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN)) { + return -1; + } else if (!is_hs_v3 && BUG(key_data_len != CPATH_KEY_MATERIAL_LEN)) { + return -1; + } + /* If we are using this cpath for next gen onion services use SHA3-256, otherwise use good ol' SHA1 */ if (is_hs_v3) { @@ -1450,7 +1461,7 @@ circuit_finish_handshake(origin_circuit_t *circ, onion_handshake_state_release(&hop->handshake_state); - if (circuit_init_cpath_crypto(hop, keys, 0, 0)<0) { + if (circuit_init_cpath_crypto(hop, keys, sizeof(keys), 0, 0)<0) { return -END_CIRC_REASON_TORPROTOCOL; } @@ -1517,12 +1528,14 @@ circuit_truncated(origin_circuit_t *circ, crypt_path_t *layer, int reason) int onionskin_answer(or_circuit_t *circ, const created_cell_t *created_cell, - const char *keys, + const char *keys, size_t keys_len, const uint8_t *rend_circ_nonce) { cell_t cell; crypt_path_t *tmp_cpath; + 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); @@ -1538,7 +1551,7 @@ onionskin_answer(or_circuit_t *circ, log_debug(LD_CIRC,"init digest forward 0x%.8x, backward 0x%.8x.", (unsigned int)get_uint32(keys), (unsigned int)get_uint32(keys+20)); - if (circuit_init_cpath_crypto(tmp_cpath, keys, 0, 0)<0) { + if (circuit_init_cpath_crypto(tmp_cpath, keys, keys_len, 0, 0)<0) { log_warn(LD_BUG,"Circuit initialization failed"); tor_free(tmp_cpath); return -1; diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index 6910b3a083..62a6367ed2 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -31,7 +31,8 @@ int circuit_timeout_want_to_count_circ(origin_circuit_t *circ); int circuit_send_next_onion_skin(origin_circuit_t *circ); void circuit_note_clock_jumped(int seconds_elapsed); int circuit_extend(cell_t *cell, circuit_t *circ); -int circuit_init_cpath_crypto(crypt_path_t *cpath, const char *key_data, +int circuit_init_cpath_crypto(crypt_path_t *cpath, + const char *key_data, size_t key_data_len, int reverse, int is_hs_v3); struct created_cell_t; int circuit_finish_handshake(origin_circuit_t *circ, @@ -40,7 +41,7 @@ int circuit_truncated(origin_circuit_t *circ, crypt_path_t *layer, int reason); int onionskin_answer(or_circuit_t *circ, const struct created_cell_t *created_cell, - const char *keys, + const char *keys, size_t keys_len, const uint8_t *rend_circ_nonce); MOCK_DECL(int, circuit_all_predicted_ports_handled, (time_t now, int *need_uptime, diff --git a/src/or/command.c b/src/or/command.c index c667cbbe52..2c82984901 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -381,7 +381,8 @@ command_process_create_cell(cell_t *cell, channel_t *chan) created_cell.handshake_len = len; if (onionskin_answer(circ, &created_cell, - (const char *)keys, rend_circ_nonce)<0) { + (const char *)keys, sizeof(keys), + rend_circ_nonce)<0) { log_warn(LD_OR,"Failed to reply to CREATE_FAST cell. Closing."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); return; diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c index 06d45f9960..f786d79505 100644 --- a/src/or/cpuworker.c +++ b/src/or/cpuworker.c @@ -372,7 +372,7 @@ cpuworker_onion_handshake_replyfn(void *work_) if (onionskin_answer(circ, &rpl.created_cell, - (const char*)rpl.keys, + (const char*)rpl.keys, sizeof(rpl.keys), rpl.rend_auth_material) < 0) { log_warn(LD_OR,"onionskin_answer failed. Closing."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c index 1729080c20..42c5dcb91a 100644 --- a/src/or/hs_circuit.c +++ b/src/or/hs_circuit.c @@ -60,7 +60,7 @@ create_rend_cpath(const uint8_t *ntor_key_seed, int is_service_side) cpath = tor_malloc_zero(sizeof(crypt_path_t)); cpath->magic = CRYPT_PATH_MAGIC; - if (circuit_init_cpath_crypto(cpath, (char*)keys, + if (circuit_init_cpath_crypto(cpath, (char*)keys, sizeof(keys), is_service_side, 1) < 0) { tor_free(cpath); goto err; @@ -96,7 +96,9 @@ create_rend_cpath_legacy(origin_circuit_t *circ, const uint8_t *rend_cell_body) goto err; } /* ... and set up cpath. */ - if (circuit_init_cpath_crypto(hop, keys+DIGEST_LEN, 0, 0)<0) + if (circuit_init_cpath_crypto(hop, + keys+DIGEST_LEN, sizeof(keys)-DIGEST_LEN, + 0, 0) < 0) goto err; /* Check whether the digest is right... */ diff --git a/src/or/rendservice.c b/src/or/rendservice.c index d389a87ae3..b8e704e54b 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -2195,7 +2195,9 @@ rend_service_receive_introduction(origin_circuit_t *circuit, cpath->rend_dh_handshake_state = dh; dh = NULL; - if (circuit_init_cpath_crypto(cpath,keys+DIGEST_LEN,1, 0)<0) + if (circuit_init_cpath_crypto(cpath, + keys+DIGEST_LEN, sizeof(keys)-DIGEST_LEN, + 1, 0)<0) goto err; memcpy(cpath->rend_circ_nonce, keys, DIGEST_LEN); From 70d08f764d9912e66a2c6c0f3e4241f563d53ebd Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Thu, 6 Jul 2017 16:23:30 +0300 Subject: [PATCH 16/19] Explicit length checks in create_rend_cpath(). Had to also edit hs_ntor_circuit_key_expansion() to make it happen. --- src/or/hs_circuit.c | 13 +++++++++---- src/or/hs_circuit.h | 1 + src/or/hs_ntor.c | 19 ++++++++++++++++--- src/or/hs_ntor.h | 4 ++-- src/test/test_hs_client.c | 4 +++- src/test/test_hs_service.c | 4 +++- 6 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c index 42c5dcb91a..f2ea8f5538 100644 --- a/src/or/hs_circuit.c +++ b/src/or/hs_circuit.c @@ -48,13 +48,17 @@ circuit_purpose_is_correct_for_rend(unsigned int circ_purpose, int is_service_si * If is_service_side is set, we are the hidden service and the final * hop of the rendezvous circuit is the client on the other side. */ static crypt_path_t * -create_rend_cpath(const uint8_t *ntor_key_seed, int is_service_side) +create_rend_cpath(const uint8_t *ntor_key_seed, size_t seed_len, + int is_service_side) { uint8_t keys[HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN]; crypt_path_t *cpath = NULL; /* Do the key expansion */ - hs_ntor_circuit_key_expansion(ntor_key_seed, keys); + if (hs_ntor_circuit_key_expansion(ntor_key_seed, seed_len, + keys, sizeof(keys)) < 0) { + goto err; + } /* Setup the cpath */ cpath = tor_malloc_zero(sizeof(crypt_path_t)); @@ -171,7 +175,7 @@ finalize_rend_circuit(origin_circuit_t *circ, crypt_path_t *hop, * Return 0 if the operation went well; in case of error return -1. */ int hs_circuit_setup_e2e_rend_circ(origin_circuit_t *circ, - const uint8_t *ntor_key_seed, + const uint8_t *ntor_key_seed, size_t seed_len, int is_service_side) { if (BUG(!circuit_purpose_is_correct_for_rend(TO_CIRCUIT(circ)->purpose, @@ -179,7 +183,8 @@ hs_circuit_setup_e2e_rend_circ(origin_circuit_t *circ, return -1; } - crypt_path_t *hop = create_rend_cpath(ntor_key_seed, is_service_side); + crypt_path_t *hop = create_rend_cpath(ntor_key_seed, seed_len, + is_service_side); if (!hop) { log_warn(LD_REND, "Couldn't get v3 %s cpath!", is_service_side ? "service-side" : "client-side"); diff --git a/src/or/hs_circuit.h b/src/or/hs_circuit.h index 1c2924ccad..71ce5c3331 100644 --- a/src/or/hs_circuit.h +++ b/src/or/hs_circuit.h @@ -15,6 +15,7 @@ int hs_circuit_setup_e2e_rend_circ(origin_circuit_t *circ, const uint8_t *ntor_key_seed, + size_t seed_len, int is_service_side); int hs_circuit_setup_e2e_rend_circ_legacy_client(origin_circuit_t *circ, const uint8_t *rend_cell_body); diff --git a/src/or/hs_ntor.c b/src/or/hs_ntor.c index 668ef221b8..a416bc46c3 100644 --- a/src/or/hs_ntor.c +++ b/src/or/hs_ntor.c @@ -582,14 +582,25 @@ hs_ntor_client_rendezvous2_mac_is_good( /** Given the rendezvous key seed in ntor_key_seed (of size * DIGEST256_LEN), do the circuit key expansion as specified by section * '4.2.1. Key expansion' and place the keys in keys_out (which must be - * of size HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN). */ -void -hs_ntor_circuit_key_expansion(const uint8_t *ntor_key_seed, uint8_t *keys_out) + * of size HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN). + * + * Return 0 if things went well, else return -1. */ +int +hs_ntor_circuit_key_expansion(const uint8_t *ntor_key_seed, size_t seed_len, + uint8_t *keys_out, size_t keys_out_len) { uint8_t *ptr; uint8_t kdf_input[NTOR_KEY_EXPANSION_KDF_INPUT_LEN]; crypto_xof_t *xof; + /* Sanity checks on lengths to make sure we are good */ + if (BUG(seed_len != DIGEST256_LEN)) { + return -1; + } + if (BUG(keys_out_len != HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN)) { + return -1; + } + /* Let's build the input to the KDF */ ptr = kdf_input; APPEND(ptr, ntor_key_seed, DIGEST256_LEN); @@ -601,5 +612,7 @@ hs_ntor_circuit_key_expansion(const uint8_t *ntor_key_seed, uint8_t *keys_out) crypto_xof_add_bytes(xof, kdf_input, sizeof(kdf_input)); crypto_xof_squeeze_bytes(xof, keys_out, HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN); crypto_xof_free(xof); + + return 0; } diff --git a/src/or/hs_ntor.h b/src/or/hs_ntor.h index 3a97e17acc..37c3261ae7 100644 --- a/src/or/hs_ntor.h +++ b/src/or/hs_ntor.h @@ -55,8 +55,8 @@ int hs_ntor_service_get_rendezvous1_keys( const curve25519_public_key_t *client_ephemeral_enc_pubkey, hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out); -void hs_ntor_circuit_key_expansion(const uint8_t *ntor_key_seed, - uint8_t *keys_out); +int hs_ntor_circuit_key_expansion(const uint8_t *ntor_key_seed, size_t seed_len, + uint8_t *keys_out, size_t keys_out_len); int hs_ntor_client_rendezvous2_mac_is_good( const hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys, diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c index 9e5fe04a62..938d3d24f0 100644 --- a/src/test/test_hs_client.c +++ b/src/test/test_hs_client.c @@ -243,7 +243,9 @@ test_e2e_rend_circuit_setup(void *arg) /**********************************************/ /* Setup the circuit */ - retval = hs_circuit_setup_e2e_rend_circ(or_circ, ntor_key_seed, 0); + retval = hs_circuit_setup_e2e_rend_circ(or_circ, + ntor_key_seed, sizeof(ntor_key_seed), + 0); tt_int_op(retval, OP_EQ, 0); /**********************************************/ diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index b5aaa0c75e..57937475c0 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -290,7 +290,9 @@ test_e2e_rend_circuit_setup(void *arg) /* Setup the circuit: do the ntor key exchange */ { uint8_t ntor_key_seed[DIGEST256_LEN] = {2}; - retval = hs_circuit_setup_e2e_rend_circ(or_circ, ntor_key_seed, 1); + retval = hs_circuit_setup_e2e_rend_circ(or_circ, + ntor_key_seed, sizeof(ntor_key_seed), + 1); tt_int_op(retval, OP_EQ, 0); } From f35f52e8697407dde391ebc4f1be6ba76e1a1bb2 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Thu, 6 Jul 2017 16:39:48 +0300 Subject: [PATCH 17/19] Hide crypto_digest_t again and use an accessor for tests. --- src/common/crypto.c | 27 +++++++++++++++++++++++++++ src/common/crypto.h | 16 +--------------- src/test/test_hs_client.c | 12 ++++++++---- src/test/test_hs_service.c | 6 ++++-- 4 files changed, 40 insertions(+), 21 deletions(-) diff --git a/src/common/crypto.c b/src/common/crypto.c index 8b214a63b9..875b4eeb56 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -1839,6 +1839,33 @@ crypto_digest_algorithm_get_length(digest_algorithm_t alg) } } +/** Intermediate information about the digest of a stream of data. */ +struct crypto_digest_t { + digest_algorithm_t algorithm; /**< Which algorithm is in use? */ + /** State for the digest we're using. Only one member of the + * union is usable, depending on the value of algorithm. Note also + * that space for other members might not even be allocated! + */ + union { + SHA_CTX sha1; /**< state for SHA1 */ + SHA256_CTX sha2; /**< state for SHA256 */ + SHA512_CTX sha512; /**< state for SHA512 */ + keccak_state sha3; /**< state for SHA3-[256,512] */ + } d; +}; + +#ifdef TOR_UNIT_TESTS + +digest_algorithm_t +crypto_digest_get_algorithm(crypto_digest_t *digest) +{ + tor_assert(digest); + + return digest->algorithm; +} + +#endif + /** * Return the number of bytes we need to malloc in order to get a * crypto_digest_t for alg, or the number of bytes we need to wipe diff --git a/src/common/crypto.h b/src/common/crypto.h index 3766830f73..5951321c05 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -339,21 +339,6 @@ void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in); #ifdef CRYPTO_PRIVATE -/** Intermediate information about the digest of a stream of data. */ -struct crypto_digest_t { - digest_algorithm_t algorithm; /**< Which algorithm is in use? */ - /** State for the digest we're using. Only one member of the - * union is usable, depending on the value of algorithm. Note also - * that space for other members might not even be allocated! - */ - union { - SHA_CTX sha1; /**< state for SHA1 */ - SHA256_CTX sha2; /**< state for SHA256 */ - SHA512_CTX sha512; /**< state for SHA512 */ - keccak_state sha3; /**< state for SHA3-[256,512] */ - } d; -}; - STATIC int crypto_force_rand_ssleay(void); STATIC int crypto_strongest_rand_raw(uint8_t *out, size_t out_len); @@ -365,6 +350,7 @@ extern int break_strongest_rng_fallback; #ifdef TOR_UNIT_TESTS void crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src); +digest_algorithm_t crypto_digest_get_algorithm(crypto_digest_t *digest); #endif #endif diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c index 938d3d24f0..77fee88eda 100644 --- a/src/test/test_hs_client.c +++ b/src/test/test_hs_client.c @@ -188,8 +188,10 @@ test_e2e_rend_circuit_setup_legacy(void *arg) tt_int_op(retval, OP_EQ, 1); /* Check the digest algo */ - tt_int_op(or_circ->cpath->f_digest->algorithm, OP_EQ, DIGEST_SHA1); - tt_int_op(or_circ->cpath->b_digest->algorithm, OP_EQ, DIGEST_SHA1); + tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->f_digest), + OP_EQ, DIGEST_SHA1); + tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->b_digest), + OP_EQ, DIGEST_SHA1); tt_assert(or_circ->cpath->f_crypto); tt_assert(or_circ->cpath->b_crypto); @@ -255,8 +257,10 @@ 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(or_circ->cpath->f_digest->algorithm, OP_EQ, DIGEST_SHA3_256); - tt_int_op(or_circ->cpath->b_digest->algorithm, OP_EQ, DIGEST_SHA3_256); + tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->f_digest), + OP_EQ, DIGEST_SHA3_256); + tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->b_digest), + OP_EQ, DIGEST_SHA3_256); tt_assert(or_circ->cpath->f_crypto); tt_assert(or_circ->cpath->b_crypto); diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index 57937475c0..aa9a12e28c 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -301,8 +301,10 @@ test_e2e_rend_circuit_setup(void *arg) tt_int_op(retval, OP_EQ, 1); /* Check the digest algo */ - tt_int_op(or_circ->cpath->f_digest->algorithm, OP_EQ, DIGEST_SHA3_256); - tt_int_op(or_circ->cpath->b_digest->algorithm, OP_EQ, DIGEST_SHA3_256); + tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->f_digest), + OP_EQ, DIGEST_SHA3_256); + tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->b_digest), + OP_EQ, DIGEST_SHA3_256); tt_assert(or_circ->cpath->f_crypto); tt_assert(or_circ->cpath->b_crypto); From ec3e0469864ebb11fd7af3150e748f8082d57d13 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 7 Jul 2017 11:14:47 -0400 Subject: [PATCH 18/19] Use LD_BUG, not LOG_PROTOCOL_WARN, for bad-purpose cases. --- src/or/hs_circuit.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c index f2ea8f5538..2f595d72e5 100644 --- a/src/or/hs_circuit.c +++ b/src/or/hs_circuit.c @@ -19,12 +19,13 @@ * circ_purpose and ensure that it's properly set. Return true iff * circuit purpose is properly set, otherwise return false. */ static int -circuit_purpose_is_correct_for_rend(unsigned int circ_purpose, int is_service_side) +circuit_purpose_is_correct_for_rend(unsigned int circ_purpose, + int is_service_side) { if (is_service_side) { if (circ_purpose != CIRCUIT_PURPOSE_S_CONNECT_REND) { - log_fn(LOG_PROTOCOL_WARN, LD_GENERAL, - "HS e2e circuit setup with wrong purpose(%d)", circ_purpose); + log_warn(LD_BUG, + "HS e2e circuit setup with wrong purpose (%d)", circ_purpose); return 0; } } @@ -32,8 +33,8 @@ circuit_purpose_is_correct_for_rend(unsigned int circ_purpose, int is_service_si if (!is_service_side) { if (circ_purpose != CIRCUIT_PURPOSE_C_REND_READY && circ_purpose != CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) { - log_fn(LOG_PROTOCOL_WARN, LD_GENERAL, - "Client e2e circuit setup with wrong purpose(%d)", circ_purpose); + log_warn(LD_BUG, + "Client e2e circuit setup with wrong purpose (%d)", circ_purpose); return 0; } } From 6a64563b1d608dbc09f38b50cc47d0ae2de7836d Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 7 Jul 2017 11:15:27 -0400 Subject: [PATCH 19/19] Fix wide lines --- src/or/hs_ntor.h | 3 ++- src/test/test_hs_service.c | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/or/hs_ntor.h b/src/or/hs_ntor.h index 37c3261ae7..d07bff8cf0 100644 --- a/src/or/hs_ntor.h +++ b/src/or/hs_ntor.h @@ -55,7 +55,8 @@ int hs_ntor_service_get_rendezvous1_keys( const curve25519_public_key_t *client_ephemeral_enc_pubkey, hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out); -int hs_ntor_circuit_key_expansion(const uint8_t *ntor_key_seed, size_t seed_len, +int hs_ntor_circuit_key_expansion(const uint8_t *ntor_key_seed, + size_t seed_len, uint8_t *keys_out, size_t keys_out_len); int hs_ntor_client_rendezvous2_mac_is_good( diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index aa9a12e28c..17772f1df0 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -291,8 +291,8 @@ test_e2e_rend_circuit_setup(void *arg) { uint8_t ntor_key_seed[DIGEST256_LEN] = {2}; retval = hs_circuit_setup_e2e_rend_circ(or_circ, - ntor_key_seed, sizeof(ntor_key_seed), - 1); + ntor_key_seed, sizeof(ntor_key_seed), + 1); tt_int_op(retval, OP_EQ, 0); }