Skip to content
Snippets Groups Projects
Commit 79fc5217 authored by Nick Mathewson's avatar Nick Mathewson :game_die:
Browse files

For hidden services: handle INTRODUCE2, send ESTABLISH_INTRO, RENDEZVOUS1.

Also:
- Add a pending final cpath element to build_state
- Rename S_RENDEZVOUSING to S_CONNECT_REND
- Add [CS]_REND_JOINED
- Split out logic to initialize cpath crypto objects.
- Have circuits/cpaths remember the KH element from their handshake, so they
  can use it for other authentication later. (As in ESTABLISH_INTRO)


svn:r1438
parent 6b958494
No related branches found
No related tags found
No related merge requests found
......@@ -129,8 +129,8 @@ void circuit_free(circuit_t *circ) {
crypto_free_digest_env(circ->p_digest);
if(circ->build_state) {
tor_free(circ->build_state->chosen_exit);
if (circ->build_state->rend_handshake_state)
crypto_dh_free(circ->build_state->rend_handshake_state);
if (circ->build_state->pending_final_cpath)
circuit_free_cpath_node(circ->build_state->pending_final_cpath);
}
tor_free(circ->build_state);
circuit_free_cpath(circ->cpath);
......@@ -997,7 +997,7 @@ static void circuit_is_ready(circuit_t *circ) {
break;
case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
/* at Bob, waiting for introductions */
// do nothing?
rend_service_intro_is_ready(circ);
break;
case CIRCUIT_PURPOSE_C_INTRODUCING:
/* at Alice, connecting to intro point */
......@@ -1007,9 +1007,9 @@ static void circuit_is_ready(circuit_t *circ) {
/* at Alice, waiting for Bob */
// alice launches a circuit to bob's intro point
break;
case CIRCUIT_PURPOSE_S_RENDEZVOUSING:
case CIRCUIT_PURPOSE_S_CONNECT_REND:
/* at Bob, connecting to rend point */
// bob sends rend2 cell
rend_service_rendezvous_is_ready(circ);
break;
}
}
......@@ -1042,7 +1042,7 @@ static void circuit_failed(circuit_t *circ) {
/* at Alice, waiting for Bob */
// alice needs to pick a new rend point
break;
case CIRCUIT_PURPOSE_S_RENDEZVOUSING:
case CIRCUIT_PURPOSE_S_CONNECT_REND:
/* at Bob, connecting to rend point */
//
break;
......@@ -1308,13 +1308,50 @@ int circuit_extend(cell_t *cell, circuit_t *circ) {
return 0;
}
int circuit_finish_handshake(circuit_t *circ, char *reply) {
/* 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:
* 20 to initialize f_digest
* 20 to initialize b_digest
* 16 to key f_crypto
* 16 to key b_crypto
*/
int circuit_init_cpath_crypto(crypt_path_t *cpath, char *key_data)
{
unsigned char iv[16];
unsigned char keys[40+32];
crypt_path_t *hop;
assert(cpath && key_data);
assert(!(cpath->f_crypto || cpath->b_crypto ||
cpath->f_digest || cpath->b_digest));
memset(iv, 0, 16);
log_fn(LOG_DEBUG,"hop init digest forward 0x%.8x, backward 0x%.8x.",
(unsigned int)*(uint32_t*)key_data, (unsigned int)*(uint32_t*)(key_data+20));
cpath->f_digest = crypto_new_digest_env(CRYPTO_SHA1_DIGEST);
crypto_digest_add_bytes(cpath->f_digest, key_data, 20);
cpath->b_digest = crypto_new_digest_env(CRYPTO_SHA1_DIGEST);
crypto_digest_add_bytes(cpath->b_digest, key_data+20, 20);
log_fn(LOG_DEBUG,"hop init cipher forward 0x%.8x, backward 0x%.8x.",
(unsigned int)*(uint32_t*)(key_data+40), (unsigned int)*(uint32_t*)(key_data+40+16));
if (!(cpath->f_crypto =
crypto_create_init_cipher(CIRCUIT_CIPHER,key_data+40,iv,1))) {
log(LOG_WARN,"forward cipher initialization failed.");
return -1;
}
if (!(cpath->b_crypto =
crypto_create_init_cipher(CIRCUIT_CIPHER,key_data+40+16,iv,0))) {
log(LOG_WARN,"backward cipher initialization failed.");
return -1;
}
return 0;
}
int circuit_finish_handshake(circuit_t *circ, char *reply) {
unsigned char keys[CPATH_KEY_MATERIAL_LEN];
crypt_path_t *hop;
assert(circ->cpath);
if(circ->cpath->state == CPATH_STATE_AWAITING_KEYS)
hop = circ->cpath;
......@@ -1336,24 +1373,10 @@ int circuit_finish_handshake(circuit_t *circ, char *reply) {
crypto_dh_free(hop->handshake_state); /* don't need it anymore */
hop->handshake_state = NULL;
/* Remember hash of g^xy */
memcpy(hop->handshake_digest, reply+DH_KEY_LEN, CRYPTO_SHA1_DIGEST_LEN);
log_fn(LOG_DEBUG,"hop init digest forward 0x%.8x, backward 0x%.8x.",
(unsigned int)*(uint32_t*)keys, (unsigned int)*(uint32_t*)(keys+20));
hop->f_digest = crypto_new_digest_env(CRYPTO_SHA1_DIGEST);
crypto_digest_add_bytes(hop->f_digest, keys, 20);
hop->b_digest = crypto_new_digest_env(CRYPTO_SHA1_DIGEST);
crypto_digest_add_bytes(hop->b_digest, keys+20, 20);
log_fn(LOG_DEBUG,"hop init cipher forward 0x%.8x, backward 0x%.8x.",
(unsigned int)*(uint32_t*)(keys+40), (unsigned int)*(uint32_t*)(keys+40+16));
if (!(hop->f_crypto =
crypto_create_init_cipher(CIRCUIT_CIPHER,keys+40,iv,1))) {
log(LOG_WARN,"forward cipher initialization failed.");
return -1;
}
if (!(hop->b_crypto =
crypto_create_init_cipher(CIRCUIT_CIPHER,keys+40+16,iv,0))) {
log(LOG_WARN,"backward cipher initialization failed.");
if (circuit_init_cpath_crypto(hop, keys)<0) {
return -1;
}
......
......@@ -156,6 +156,8 @@ int onionskin_answer(circuit_t *circ, unsigned char *payload, unsigned char *key
return -1;
}
memcpy(circ->handshake_digest, cell.payload+DH_KEY_LEN, CRYPTO_SHA1_DIGEST_LEN);
connection_or_write_cell_to_buf(&cell, circ->p_conn);
log_fn(LOG_DEBUG,"Finished sending 'created' cell.");
......@@ -465,6 +467,19 @@ static void remove_twins_from_smartlist(smartlist_t *sl, routerinfo_t *twin) {
}
}
void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop)
{
if (*head_ptr) {
new_hop->next = (*head_ptr);
new_hop->prev = (*head_ptr)->prev;
(*head_ptr)->prev->next = new_hop;
(*head_ptr)->prev = new_hop;
} else {
*head_ptr = new_hop;
new_hop->prev = new_hop->next = new_hop;
}
}
int onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t *state, routerinfo_t **router_out)
{
int cur_len;
......@@ -554,15 +569,7 @@ int onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t *state, rout
hop = (crypt_path_t *)tor_malloc_zero(sizeof(crypt_path_t));
/* link hop into the cpath, at the end. */
if (*head_ptr) {
hop->next = (*head_ptr);
hop->prev = (*head_ptr)->prev;
(*head_ptr)->prev->next = hop;
(*head_ptr)->prev = hop;
} else {
*head_ptr = hop;
hop->prev = hop->next = hop;
}
onion_append_to_cpath(head_ptr, hop);
hop->state = CPATH_STATE_CLOSED;
......
......@@ -207,8 +207,10 @@
#define CIRCUIT_PURPOSE_S_ESTABLISH_INTRO 6 /* at Bob, waiting for introductions */
#define CIRCUIT_PURPOSE_C_INTRODUCING 7 /* at Alice, connecting to intro point */
#define CIRCUIT_PURPOSE_C_ESTABLISH_REND 8 /* at Alice, waiting for Bob */
#define CIRCUIT_PURPOSE_S_RENDEZVOUSING 9 /* at Bob, connecting to rend point */
#define _CIRCUIT_PURPOSE_MAX 9
#define CIRCUIT_PURPOSE_S_CONNECT_REND 9 /* at Bob, connecting to rend point */
#define CIRCUIT_PURPOSE_C_REND_JOINED 10 /* at Alice, rendezvous established.*/
#define CIRCUIT_PURPOSE_S_REND_JOINED 11 /* at Bob, rendezvous established.*/
#define _CIRCUIT_PURPOSE_MAX 11
#define RELAY_COMMAND_BEGIN 1
#define RELAY_COMMAND_DATA 2
......@@ -477,6 +479,7 @@ struct crypt_path_t {
crypto_digest_env_t *b_digest;
crypto_dh_env_t *handshake_state;
char handshake_digest[CRYPTO_SHA1_DIGEST];/* KH in tor-spec.txt */
uint32_t addr;
uint16_t port;
......@@ -501,9 +504,10 @@ typedef struct crypt_path_t crypt_path_t;
typedef struct {
int desired_path_len;
char *chosen_exit; /* nickname of planned exit node */
crypto_dh_env_t *rend_handshake_state; /*XXXXDOCDOC*/
unsigned char rend_key_material[52]; /*XXXXDOCDOC*/
/* nickname of planned exit node */
char *chosen_exit;
/* cpath to append after rendezvous. */
struct crypt_path_t *pending_final_cpath;
} cpath_build_state_t;
/* struct for a path (circuit) through the network */
......@@ -538,6 +542,8 @@ struct circuit_t {
crypt_path_t *cpath;
char onionskin[ONIONSKIN_CHALLENGE_LEN]; /* for storage while onionskin pending */
char handshake_digest[CRYPTO_SHA1_DIGEST_LEN]; /* Stores KH for intermediate hops */
time_t timestamp_created;
time_t timestamp_dirty; /* when the circuit was first used, or 0 if clean */
......@@ -714,6 +720,8 @@ void circuit_reset_failure_count(void);
void circuit_n_conn_open(connection_t *or_conn);
int circuit_send_next_onion_skin(circuit_t *circ);
int circuit_extend(cell_t *cell, circuit_t *circ);
#define CPATH_KEY_MATERIAL_LEN (20*2+16*2)
int circuit_init_cpath_crypto(crypt_path_t *cpath, char *key_data);
int circuit_finish_handshake(circuit_t *circ, char *reply);
int circuit_truncated(circuit_t *circ, crypt_path_t *layer);
......@@ -917,6 +925,8 @@ void onion_pending_remove(circuit_t *circ);
int onionskin_answer(circuit_t *circ, unsigned char *payload, unsigned char *keys);
void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop);
int onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t *state,
routerinfo_t **router_out);
......@@ -1038,6 +1048,9 @@ int rend_config_services(or_options_t *options);
int rend_service_init_keys(void);
int rend_services_init(void);
void rend_service_intro_is_ready(circuit_t *circuit);
void rend_service_rendezvous_is_ready(circuit_t *circuit);
#endif
/*
......
......@@ -264,16 +264,20 @@ typedef struct rend_introduction_t {
char shared_secret[128];
} rend_introduction_t;
/* Respond to an INTRODUCE2 cell by launching a circuit to the chosen
* rendezvous points.
*/
int
rend_service_introduce(circuit_t *circuit, char *request, int request_len)
{
char *ptr, *rp_nickname, *r_cookie;
char buf[RELAY_PAYLOAD_SIZE];
char secret[20+2*16]; /* Holds KH, Kf, Kb */
char keys[20+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */
rend_service_t *service;
int len, keylen;
crypto_dh_env_t *dh = NULL;
circuit_t *launched = NULL;
crypt_path_t *cpath = NULL;
if (circuit->purpose != CIRCUIT_PURPOSE_S_ESTABLISH_INTRO) {
log_fn(LOG_WARN, "Got an INTRODUCE2 over a non-introduction circuit.");
......@@ -334,14 +338,15 @@ rend_service_introduce(circuit_t *circuit, char *request, int request_len)
log_fn(LOG_WARN, "Couldn't build DH state or generate public key");
goto err;
}
if (crypto_dh_compute_secret(dh, ptr+20, 128, secret, 20+16*2)<0) {
if (crypto_dh_compute_secret(dh, ptr+20, DH_KEY_LEN, keys,
20+CPATH_KEY_MATERIAL_LEN)<0) {
log_fn(LOG_WARN, "Couldn't complete DH handshake");
goto err;
}
/* Launch a circuit to alice's chosen rendezvous point.
*/
launched = circuit_launch_new(CIRCUIT_PURPOSE_S_RENDEZVOUSING, rp_nickname);
launched = circuit_launch_new(CIRCUIT_PURPOSE_S_CONNECT_REND, rp_nickname);
if (!launched) {
log_fn(LOG_WARN, "Can't launch circuit to rendezvous point '%s'",
rp_nickname);
......@@ -351,9 +356,14 @@ rend_service_introduce(circuit_t *circuit, char *request, int request_len)
/* Fill in the circuit's state. */
memcpy(launched->rend_service, circuit->rend_service,CRYPTO_SHA1_DIGEST_LEN);
memcpy(launched->rend_cookie, r_cookie, REND_COOKIE_LEN);
memcpy(launched->build_state->rend_key_material, secret, 20+16*2);
launched->build_state->rend_handshake_state = dh;
launched->build_state->pending_final_cpath = cpath =
tor_malloc_zero(sizeof(crypt_path_t));
cpath->handshake_state = dh;
dh = NULL;
if (circuit_init_cpath_crypto(cpath,keys+20)<0)
goto err;
memcpy(cpath->handshake_digest, keys, 20);
return 0;
err:
......@@ -362,24 +372,144 @@ rend_service_introduce(circuit_t *circuit, char *request, int request_len)
return -1;
}
/* Launch a circuit to serve as an introduction point.
*/
static int
rend_service_launch_establish_intro(rend_service_t *service, char *nickname)
{
circuit_t *launched;
assert(service && nickname);
launched = circuit_launch_new(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, nickname);
if (!launched) {
log_fn(LOG_WARN, "Can't launch circuit to establish introduction at '%s'",
nickname);
return -1;
}
memcpy(launched->rend_service, service->pk_digest, CRYPTO_SHA1_DIGEST_LEN);
return 0;
}
/* Called when we're done building a circuit to an introduction point:
* sends a RELAY_ESTABLISH_INTRO cell.
*/
void
rend_service_intro_is_ready(circuit_t *circuit)
{
rend_service_t *service;
int len, r;
char buf[RELAY_PAYLOAD_SIZE];
char auth[CRYPTO_SHA1_DIGEST_LEN + 10];
assert(circuit->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO);
assert(circuit->cpath);
service = rend_service_get_by_pk_digest(circuit->rend_service);
if (!service) {
log_fn(LOG_WARN, "Internal error: unrecognized service ID on introduction circuit");
goto err;
}
/* Build the payload for a RELAY_ESTABLISH_INTRO cell. */
len = crypto_pk_asn1_encode(service->private_key, buf+2,
RELAY_PAYLOAD_SIZE-2);
set_uint16(buf, len);
len += 2;
memcpy(auth, circuit->cpath->prev->handshake_digest, CRYPTO_SHA1_DIGEST_LEN);
memcpy(auth+CRYPTO_SHA1_DIGEST_LEN, "INTRODUCE", 9);
if (crypto_SHA_digest(auth, CRYPTO_SHA1_DIGEST_LEN+9, buf+len))
goto err;
len += 20;
r = crypto_pk_private_sign_digest(service->private_key, buf, len, buf+len);
if (r<0) {
log_fn(LOG_WARN, "Couldn't sign introduction request");
goto err;
}
len += r;
if (connection_edge_send_command(NULL, circuit,RELAY_COMMAND_ESTABLISH_INTRO,
buf, len, circuit->cpath->prev)<0) {
log_fn(LOG_WARN, "Couldn't send introduction request");
goto err;
}
return;
err:
circuit_mark_for_close(circuit);
}
/* Called once a circuit to a rendezvous point is ready: sends a
* RELAY_COMMAND_RENDEZVOUS1 cell.
*/
void
rend_service_rendezvous_is_ready(circuit_t *circuit)
{
rend_service_t *service;
char buf[RELAY_PAYLOAD_SIZE];
crypt_path_t *hop;
assert(circuit->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
assert(circuit->cpath);
assert(circuit->build_state);
hop = circuit->build_state->pending_final_cpath;
assert(hop);
service = rend_service_get_by_pk_digest(circuit->rend_service);
if (!service) {
log_fn(LOG_WARN, "Internal error: unrecognized service ID on introduction circuit");
goto err;
}
/* All we need to do is send a RELAY_RENDEZVOUS1 cell... */
memcpy(buf, circuit->rend_cookie, REND_COOKIE_LEN);
if (crypto_dh_get_public(hop->handshake_state,
buf+REND_COOKIE_LEN, DH_KEY_LEN)<0) {
log_fn(LOG_WARN,"Couldn't get DH public key");
goto err;
}
memcpy(buf+REND_COOKIE_LEN+DH_KEY_LEN, hop->handshake_digest,
CRYPTO_SHA1_DIGEST_LEN);
/* Send the cell */
if (connection_edge_send_command(NULL, circuit, RELAY_COMMAND_RENDEZVOUS1,
buf, REND_COOKIE_LEN+DH_KEY_LEN+1,
circuit->cpath->prev)<0) {
log_fn(LOG_WARN, "Couldn't send RENDEZVOUS1 cell");
goto err;
}
/* Append the cpath entry. */
onion_append_to_cpath(&circuit->cpath, hop);
circuit->build_state->pending_final_cpath = NULL; /* prevent double-free */
/* Change the circuit purpose. */
circuit->purpose = CIRCUIT_PURPOSE_S_REND_JOINED;
return;
err:
circuit_mark_for_close(circuit);
}
/******
* Manage introduction points
******/
#define NUM_INTRO_POINTS 3
int rend_services_init(void) {
int i;
int i,j,r;
routerinfo_t *router;
routerlist_t *rl;
circuit_t *circ;
rend_service_t *service;
router_get_routerlist(&rl);
//for each of bob's services,
for (i=0;i<rend_service_list->num_used;++i) {
service = rend_service_list->list[i];
/* The directory is now here. Pick three ORs as intro points. */
for (i=0;i<rl->n_routers;i++) {
router = rl->routers[i];
for (j=0;j<rl->n_routers;j++) {
router = rl->routers[j];
//...
// maybe built a smartlist of all of them, then pick at random
// until you have three? or something smarter.
......@@ -393,11 +523,12 @@ int rend_services_init(void) {
// for each intro point,
{
//circ = circuit_launch_new(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, intro->nickname);
// tell circ which hidden service this is about
//r = rend_service_launch_establish_intro(service, intro->nickname);
//if (r<0) freak out
}
// anything else?
}
}
/*
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment