/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */ /* See LICENSE for licensing information */ /* $Id$ */ #include "or.h" extern or_options_t options; /* command-line and config-file options */ static int onion_process(circuit_t *circ); static int onion_deliver_to_conn(aci_t aci, unsigned char *onion, uint32_t onionlen, connection_t *conn); static int count_acceptable_routers(routerinfo_t **rarray, int rarray_len); static int find_tracked_onion(unsigned char *onion, uint32_t onionlen, int expire); int decide_aci_type(uint32_t local_addr, uint16_t local_port, uint32_t remote_addr, uint16_t remote_port) { if(local_addr > remote_addr) return ACI_TYPE_HIGHER; if(local_addr < remote_addr) return ACI_TYPE_LOWER; if(local_port > remote_port) return ACI_TYPE_HIGHER; /* else */ return ACI_TYPE_LOWER; } /* global (within this file) variables used by the next few functions */ static struct onion_queue_t *ol_list=NULL; static struct onion_queue_t *ol_tail=NULL; static int ol_length=0; int onion_pending_add(circuit_t *circ) { struct onion_queue_t *tmp; tmp = malloc(sizeof(struct onion_queue_t)); memset(tmp, 0, sizeof(struct onion_queue_t)); tmp->circ = circ; if(!ol_tail) { assert(!ol_list); assert(!ol_length); ol_list = tmp; ol_tail = tmp; ol_length++; return 0; } assert(ol_list); assert(!ol_tail->next); if(ol_length >= options.MaxOnionsPending) { log(LOG_INFO,"onion_pending_add(): Already have %d onions queued. Closing.", ol_length); free(tmp); return -1; } ol_length++; ol_tail->next = tmp; ol_tail = tmp; return 0; } int onion_pending_check(void) { if(ol_list) return 1; else return 0; } void onion_pending_process_one(void) { struct relay_queue_t *tmpd; circuit_t *circ; if(!ol_list) return; /* no onions pending, we're done */ assert(ol_list->circ && ol_list->circ->p_conn); assert(ol_length > 0); circ = ol_list->circ; if(onion_process(circ) < 0) { log(LOG_DEBUG,"onion_pending_process_one(): Failed. Closing."); onion_pending_remove(circ); circuit_close(circ); } else { log(LOG_DEBUG,"onion_pending_process_one(): Succeeded. Delivering queued relay cells."); for(tmpd = ol_list->relay_cells; tmpd; tmpd=tmpd->next) { command_process_relay_cell(tmpd->cell, circ->p_conn); } onion_pending_remove(circ); } return; } /* go through ol_list, find the onion_queue_t element which points to * circ, remove and free that element. leave circ itself alone. */ void onion_pending_remove(circuit_t *circ) { struct onion_queue_t *tmpo, *victim; struct relay_queue_t *tmpd; if(!ol_list) return; /* nothing here. */ /* first check to see if it's the first entry */ tmpo = ol_list; if(tmpo->circ == circ) { /* it's the first one. remove it from the list. */ ol_list = tmpo->next; if(!ol_list) ol_tail = NULL; ol_length--; victim = tmpo; } else { /* we need to hunt through the rest of the list */ for( ;tmpo->next && tmpo->next->circ != circ; tmpo=tmpo->next) ; if(!tmpo->next) { log(LOG_WARNING,"onion_pending_remove(): circ (p_aci %d), not in list!",circ->p_aci); return; } /* now we know tmpo->next->circ == circ */ victim = tmpo->next; tmpo->next = victim->next; if(ol_tail == victim) ol_tail = tmpo; ol_length--; } /* now victim points to the element that needs to be removed */ /* first dump the attached relay cells too, if any */ while(victim->relay_cells) { tmpd = victim->relay_cells; victim->relay_cells = tmpd->next; free(tmpd->cell); free(tmpd); } free(victim); } struct relay_queue_t *relay_queue_add(struct relay_queue_t *list, cell_t *cell) { struct relay_queue_t *tmpd, *newd; newd = malloc(sizeof(struct relay_queue_t)); memset(newd, 0, sizeof(struct relay_queue_t)); newd->cell = malloc(sizeof(cell_t)); memcpy(newd->cell, cell, sizeof(cell_t)); if(!list) { return newd; } for(tmpd = list; tmpd->next; tmpd=tmpd->next) ; /* now tmpd->next is null */ tmpd->next = newd; return list; } /* a relay cell has arrived for a circuit which is still pending. Find * the right entry in ol_list, and add it to the end of the 'relay_cells' * list. */ void onion_pending_relay_add(circuit_t *circ, cell_t *cell) { struct onion_queue_t *tmpo; for(tmpo=ol_list; tmpo; tmpo=tmpo->next) { if(tmpo->circ == circ) { tmpo->relay_cells = relay_queue_add(tmpo->relay_cells, cell); return; } } } /* helper function for onion_process */ static int onion_deliver_to_conn(aci_t aci, unsigned char *onion, uint32_t onionlen, connection_t *conn) { char *buf; int buflen, dataleft; cell_t cell; assert(aci && onion && onionlen); buflen = onionlen+4; buf = malloc(buflen); if(!buf) return -1; log(LOG_DEBUG,"onion_deliver_to_conn(): Setting onion length to %u.",onionlen); *(uint32_t*)buf = htonl(onionlen); memcpy((buf+4),onion,onionlen); dataleft = buflen; while(dataleft > 0) { memset(&cell,0,sizeof(cell_t)); cell.command = CELL_CREATE; cell.aci = aci; if(dataleft >= CELL_PAYLOAD_SIZE) cell.length = CELL_PAYLOAD_SIZE; else cell.length = dataleft; memcpy(cell.payload, buf+buflen-dataleft, cell.length); dataleft -= cell.length; log(LOG_DEBUG,"onion_deliver_to_conn(): Delivering create cell, payload %d bytes.",cell.length); if(connection_write_cell_to_buf(&cell, conn) < 0) { log(LOG_DEBUG,"onion_deliver_to_conn(): Could not buffer new create cells. Closing."); free(buf); return -1; } } free(buf); return 0; } static int onion_process(circuit_t *circ) { connection_t *n_conn; int retval; aci_t aci_type; struct sockaddr_in me; /* my router identity */ onion_layer_t layer; if(learn_my_address(&me) < 0) return -1; /* decrypt it in-place */ if(decrypt_onion(circ->onion,circ->onionlen,getprivatekey(),&layer) < 0) { log(LOG_DEBUG,"command_process_create_cell(): decrypt_onion() failed, closing circuit."); return -1; } log(LOG_DEBUG,"command_process_create_cell(): Onion decrypted."); /* check freshness */ if (layer.expire < (uint32_t)time(NULL)) /* expired onion */ /*XXXX*/ { log(LOG_NOTICE,"I have just received an expired onion. This could be a replay attack."); return -1; } aci_type = decide_aci_type(ntohl(me.sin_addr.s_addr), ntohs(me.sin_port), layer.addr, layer.port); if(circuit_init(circ, aci_type, &layer) < 0) { log(LOG_ERR,"process_onion(): init_circuit() failed."); return -1; } /* check for replay. at the same time, add it to the pile of tracked onions. */ if(find_tracked_onion(circ->onion, circ->onionlen, layer.expire)) { log(LOG_NOTICE,"process_onion(): I have just received a replayed onion. This could be a replay attack."); return -1; } /* now we must send create cells to the next router */ if(circ->n_addr && circ->n_port) { n_conn = connection_twin_get_by_addr_port(circ->n_addr,circ->n_port); if(!n_conn || n_conn->type != CONN_TYPE_OR) { /* i've disabled making connections through OPs, but it's definitely * possible here. I'm not sure if it would be a bug or a feature. -RD */ /* note also that this will close circuits where the onion has the same * router twice in a row in the path. i think that's ok. -RD */ log(LOG_DEBUG,"command_process_create_cell(): Next router not connected. Closing."); return -1; } circ->n_addr = n_conn->addr; /* these are different if we found a twin instead */ circ->n_port = n_conn->port; circ->n_conn = n_conn; log(LOG_DEBUG,"command_process_create_cell(): n_conn is %s:%u",n_conn->address,n_conn->port); /* send the CREATE cells on to the next hop */ pad_onion(circ->onion, circ->onionlen, ONION_LAYER_SIZE); log(LOG_DEBUG,"command_process_create_cell(): Padded the onion with random data."); retval = onion_deliver_to_conn(circ->n_aci, circ->onion, circ->onionlen, n_conn); free(circ->onion); circ->onion = NULL; if (retval == -1) { log(LOG_DEBUG,"command_process_create_cell(): Could not deliver the onion to next conn. Closing."); return -1; } } else { /* this is destined for an exit */ log(LOG_DEBUG,"command_process_create_cell(): create cell reached exit. Circuit established."); #if 0 log(LOG_DEBUG,"command_process_create_cell(): Creating new exit connection."); n_conn = connection_new(CONN_TYPE_EXIT); if(!n_conn) { log(LOG_DEBUG,"command_process_create_cell(): connection_new failed. Closing."); return -1; } n_conn->state = EXIT_CONN_STATE_CONNECTING_WAIT; n_conn->receiver_bucket = -1; /* edge connections don't do receiver buckets */ n_conn->bandwidth = -1; n_conn->s = -1; /* not yet valid */ if(connection_add(n_conn) < 0) { /* no space, forget it */ log(LOG_DEBUG,"command_process_create_cell(): connection_add failed. Closing."); connection_free(n_conn); return -1; } circ->n_conn = n_conn; #endif } return 0; } /* uses a weighted coin with weight cw to choose a route length */ int chooselen(double cw) { int len = 2; uint8_t coin; if ((cw < 0) || (cw >= 1)) /* invalid parameter */ return -1; while(1) { if (CRYPTO_PSEUDO_RAND_INT(coin)) return -1; if (coin > cw*255) /* don't extend */ break; else len++; } return len; } /* returns an array of pointers to routent that define a new route through the OR network * int cw is the coin weight to use when choosing the route * order of routers is from last to first */ unsigned int *new_route(double cw, routerinfo_t **rarray, int rarray_len, int *routelen) { int i; int num_acceptable_routers; unsigned int *route; unsigned int oldchoice, choice; assert((cw >= 0) && (cw < 1) && (rarray) && (routelen) ); /* valid parameters */ *routelen = chooselen(cw); if (*routelen == -1) { log(LOG_ERR,"Choosing route length failed."); return NULL; } log(LOG_DEBUG,"new_route(): Chosen route length %d.",*routelen); num_acceptable_routers = count_acceptable_routers(rarray, rarray_len); if(num_acceptable_routers < 2) { log(LOG_INFO,"new_route(): Not enough acceptable routers. Failing."); return NULL; } if(num_acceptable_routers < *routelen) { log(LOG_DEBUG,"new_route(): Cutting routelen from %d to %d.",*routelen, num_acceptable_routers); *routelen = num_acceptable_routers; } if(*routelen < 1) { log(LOG_ERR,"new_route(): Didn't find any acceptable routers. Failing."); return NULL; } /* allocate memory for the new route */ route = (unsigned int *)malloc(*routelen * sizeof(unsigned int)); if (!route) { log(LOG_ERR,"Memory allocation failed."); return NULL; } oldchoice = rarray_len; for(i=0;i<*routelen;i++) { log(LOG_DEBUG,"new_route(): Choosing hop %u.",i); if (CRYPTO_PSEUDO_RAND_INT(choice)) { free((void *)route); return NULL; } choice = choice % (rarray_len); log(LOG_DEBUG,"new_route(): Contemplating router %u.",choice); if(choice == oldchoice || (oldchoice < rarray_len && !crypto_pk_cmp_keys(rarray[choice]->pkey, rarray[oldchoice]->pkey)) || (options.ORPort && !connection_twin_get_by_addr_port(rarray[choice]->addr, rarray[choice]->or_port))) { /* Same router as last choice, or router twin, * or no routers with that key are connected to us. * Try again. */ log(LOG_DEBUG,"new_route(): Picked a router %d that won't work as next hop.",choice); i--; continue; } log(LOG_DEBUG,"new_route(): Chosen router %u for hop %u.",choice,i); oldchoice = choice; route[i] = choice; } return route; } static int count_acceptable_routers(routerinfo_t **rarray, int rarray_len) { int i, j; int num=0; connection_t *conn; for(i=0;iaddr, rarray[i]->or_port); if(!conn || conn->type != CONN_TYPE_OR || conn->state != OR_CONN_STATE_OPEN) { log(LOG_DEBUG,"Nope, %d is not connected.",i); goto next_i_loop; } } for(j=0;jpkey, rarray[j]->pkey)) { /* these guys are twins. so we've already counted him. */ log(LOG_DEBUG,"Nope, %d is a twin of %d.",i,j); goto next_i_loop; } } num++; log(LOG_DEBUG,"I like %d. num_acceptable_routers now %d.",i, num); next_i_loop: ; /* our compiler may need an explicit statement after the label */ } return num; } /* creates a new onion from route, stores it and its length into buf and len respectively */ unsigned char *create_onion(routerinfo_t **rarray, int rarray_len, unsigned int *route, int routelen, int *len, crypt_path_t **cpath) { int i; char *layerp; crypt_path_t *hop; unsigned char *buf; routerinfo_t *router; unsigned char iv[16]; struct in_addr netaddr; onion_layer_t layer; assert(rarray && route && len && routelen && cpath); *cpath = NULL; /* calculate the size of the onion */ *len = routelen * ONION_LAYER_SIZE + ONION_PADDING_SIZE; /* 28 bytes per layer + 100 bytes padding for the innermost layer */ log(LOG_DEBUG,"create_onion() : Size of the onion is %u.",*len); /* allocate memory for the onion */ buf = malloc(*len); if(!buf) { log(LOG_ERR,"Error allocating memory."); return NULL; } log(LOG_DEBUG,"create_onion() : Allocated memory for the onion."); for(i=0; iaddr); log(LOG_DEBUG,"create_onion(): %u : %s:%u, %u/%u",routelen-i, inet_ntoa(netaddr), (rarray[route[i]])->or_port, (rarray[route[i]])->pkey, crypto_pk_keysize((rarray[route[i]])->pkey)); } layerp = buf + *len - ONION_LAYER_SIZE - ONION_PADDING_SIZE; /* pointer to innermost layer */ /* create the onion layer by layer, starting with the innermost */ for (i=0;ior_port; layer.addr = rarray[route[i-1]]->addr; } else { layer.port = 0; layer.addr = 0; } /* Expiration Time */ layer.expire = (uint32_t)(time(NULL) + 86400); /* NOW + 1 day */ /* Key Seed Material */ if(crypto_rand(ONION_KEYSEED_LEN, layer.keyseed)) { /* error */ log(LOG_ERR,"Error generating random data."); goto error; } onion_pack(layerp, &layer); // log(LOG_DEBUG,"create_onion() : Onion layer %u built : %u, %u, %u, %s, %u.",i+1,layer->zero,layer->backf,layer->forwf,inet_ntoa(*((struct in_addr *)&layer->addr)),layer->port); /* build up the crypt_path */ hop = (crypt_path_t *)malloc(sizeof(crypt_path_t)); if(!hop) { log(LOG_ERR,"Error allocating memory."); goto error; } /* link hop into the cpath, at the front */ hop->next = *cpath; hop->prev = NULL; hop->state = CPATH_STATE_OPEN; /* change when we move to incremental paths */ if(*cpath) { (*cpath)->prev = hop; } *cpath = hop; log(LOG_DEBUG,"create_onion() : Building hop %u of crypt path.",i+1); /* calculate keys */ crypto_SHA_digest(layer.keyseed,16,hop->digest3); log(LOG_DEBUG,"create_onion() : First SHA pass performed."); crypto_SHA_digest(hop->digest3,20,hop->digest2); log(LOG_DEBUG,"create_onion() : Second SHA pass performed."); crypto_SHA_digest(hop->digest2,20,hop->digest3); log(LOG_DEBUG,"create_onion() : Third SHA pass performed."); log(LOG_DEBUG,"create_onion() : Keys generated."); /* set IV to zero */ memset((void *)iv,0,16); /* initialize cipher engines */ if (! (hop->f_crypto = crypto_create_init_cipher(DEFAULT_CIPHER, hop->digest3, iv, 1))) { /* cipher initialization failed */ log(LOG_ERR,"Could not create a crypto environment."); goto error; } if (! (hop->b_crypto = crypto_create_init_cipher(DEFAULT_CIPHER, hop->digest2, iv, 0))) { /* cipher initialization failed */ log(LOG_ERR,"Could not create a crypto environment."); goto error; } log(LOG_DEBUG,"create_onion() : Built corresponding crypt path hop."); /* padding if this is the innermost layer */ if (!i) { if (crypto_pseudo_rand(ONION_PADDING_SIZE, layerp + ONION_LAYER_SIZE)) { /* error */ log(LOG_ERR,"Error generating pseudo-random data."); goto error; } log(LOG_DEBUG,"create_onion() : This is the innermost layer. Adding 100 bytes of padding."); } /* encrypt */ if(encrypt_onion(layerp,ONION_PADDING_SIZE+(i+1)*ONION_LAYER_SIZE,router->pkey,layer.keyseed) < 0) { log(LOG_ERR,"Error encrypting onion layer."); goto error; } log(LOG_DEBUG,"create_onion() : Encrypted layer."); /* calculate pointer to next layer */ layerp = buf + (routelen-i-2)*ONION_LAYER_SIZE; } /* now link cpath->prev to the end of cpath */ for(hop=*cpath; hop->next; hop=hop->next) ; hop->next = *cpath; (*cpath)->prev = hop; return buf; error: if(buf) free(buf); circuit_free_cpath(*cpath); return NULL; } /* encrypts 128 bytes of the onion with the specified public key, the rest with * DES OFB with the key as defined in the outter layer */ int encrypt_onion(unsigned char *onion, uint32_t onionlen, crypto_pk_env_t *pkey, char* keyseed) { unsigned char *tmpbuf = NULL; /* temporary buffer for crypto operations */ unsigned char digest[20]; /* stores SHA1 output - 160 bits */ unsigned char iv[8]; crypto_cipher_env_t *crypt_env = NULL; /* crypto environment */ assert(onion && pkey); assert(onionlen >= 128); memset(iv,0,8); // log(LOG_DEBUG,"Onion layer : %u, %u, %u, %s, %u.",onion->zero,onion->backf,onion->forwf,inet_ntoa(*((struct in_addr *)&onion->addr)),onion->port); /* allocate space for tmpbuf */ tmpbuf = (unsigned char *)malloc(onionlen); if(!tmpbuf) { log(LOG_ERR,"Could not allocate memory."); return -1; } log(LOG_DEBUG,"encrypt_onion() : allocated %u bytes of memory for the encrypted onion (at %u).",onionlen,tmpbuf); /* get key1 = SHA1(KeySeed) */ if (crypto_SHA_digest(keyseed,16,digest)) { log(LOG_ERR,"Error computing SHA1 digest."); goto error; } log(LOG_DEBUG,"encrypt_onion() : Computed DES key."); log(LOG_DEBUG,"encrypt_onion() : Trying to RSA encrypt."); /* encrypt 128 bytes with RSA *pkey */ if (crypto_pk_public_encrypt(pkey, onion, 128, tmpbuf, RSA_NO_PADDING) == -1) { log(LOG_ERR,"Error RSA-encrypting data :%s",crypto_perror()); goto error; } log(LOG_DEBUG,"encrypt_onion() : RSA encrypted first 128 bytes of the onion."); /* now encrypt the rest with 3DES OFB */ crypt_env = crypto_create_init_cipher(CRYPTO_CIPHER_3DES, digest, iv, 1); if (!crypt_env) { log(LOG_ERR,"Error creating the crypto environment."); goto error; } if (crypto_cipher_encrypt(crypt_env,onion+128, onionlen-128, (unsigned char *)tmpbuf+128)) { /* error */ log(LOG_ERR,"Error performing DES encryption:%s",crypto_perror()); goto error; } log(LOG_DEBUG,"encrypt_onion() : 3DES OFB encrypted the rest of the onion."); /* now copy tmpbuf to onion */ memcpy(onion,tmpbuf,onionlen); log(LOG_DEBUG,"encrypt_onion() : Copied cipher to original onion buffer."); free(tmpbuf); crypto_free_cipher_env(crypt_env); return 0; error: if (tmpbuf) free(tmpbuf); if (crypt_env) crypto_free_cipher_env(crypt_env); return -1; } /* decrypts the first 128 bytes using RSA and prkey, decrypts the rest with DES OFB with key1 */ int decrypt_onion(unsigned char *onion, uint32_t onionlen, crypto_pk_env_t *prkey, onion_layer_t *layer) { void *tmpbuf = NULL; /* temporary buffer for crypto operations */ unsigned char digest[20]; /* stores SHA1 output - 160 bits */ unsigned char iv[8]; crypto_cipher_env_t *crypt_env =NULL; /* crypto environment */ assert(onion && prkey); memset(iv,0,8); /* allocate space for tmpbuf */ tmpbuf = malloc(onionlen); if (!tmpbuf) { log(LOG_ERR,"Could not allocate memory."); return -1; } log(LOG_DEBUG,"decrypt_onion() : Allocated memory for the temporary buffer."); /* decrypt 128 bytes with RSA *prkey */ if (crypto_pk_private_decrypt(prkey, onion, 128, tmpbuf, RSA_NO_PADDING) == -1) { log(LOG_ERR,"Error RSA-decrypting data :%s",crypto_perror()); goto error; } log(LOG_DEBUG,"decrypt_onion() : RSA decryption complete."); onion_unpack(layer, tmpbuf); /* get key1 = SHA1(KeySeed) */ if (crypto_SHA_digest(layer->keyseed,16,digest)) { log(LOG_ERR,"Error computing SHA1 digest."); goto error; } log(LOG_DEBUG,"decrypt_onion() : Computed DES key."); /* now decrypt the rest with 3DES OFB */ crypt_env = crypto_create_init_cipher(CRYPTO_CIPHER_3DES, digest, iv, 0); if (!crypt_env) { log(LOG_ERR,"Error creating crypto environment"); goto error; } if (crypto_cipher_decrypt(crypt_env,onion+128, onionlen-128,tmpbuf+128)) { log(LOG_ERR,"Error performing DES decryption:%s",crypto_perror()); goto error; } log(LOG_DEBUG,"decrypt_onion() : DES decryption complete."); /* now copy tmpbuf to onion */ memcpy(onion,tmpbuf,onionlen); free(tmpbuf); crypto_free_cipher_env(crypt_env); return 0; error: if (tmpbuf) free(tmpbuf); if (crypt_env) crypto_free_cipher_env(crypt_env); return -1; } /* delete first n bytes of the onion and pads the end with n bytes of random data */ void pad_onion(unsigned char *onion, uint32_t onionlen, int n) { assert(onion); memmove(onion,onion+n,onionlen-n); crypto_pseudo_rand(n, onion+onionlen-n); } /* red black tree using Niels' tree.h. I used http://www.openbsd.org/cgi-bin/cvsweb/src/regress/sys/sys/tree/rb/ as my guide */ #include "tree.h" struct tracked_onion { RB_ENTRY(tracked_onion) node; uint32_t expire; char digest[20]; /* SHA digest of the onion */ struct tracked_onion *next; }; RB_HEAD(tracked_tree, tracked_onion) tracked_root; int compare_tracked_onions(struct tracked_onion *a, struct tracked_onion *b) { return memcmp(a->digest, b->digest, 20); } RB_PROTOTYPE(tracked_tree, tracked_onion, node, compare_tracked_onions) RB_GENERATE(tracked_tree, tracked_onion, node, compare_tracked_onions) void init_tracked_tree(void) { RB_INIT(&tracked_root); } /* see if this onion has been seen before. if so, return 1, else * return 0 and add the sha1 of this onion to the tree. */ static int find_tracked_onion(unsigned char *onion, uint32_t onionlen, int expire) { static struct tracked_onion *head_tracked_onions = NULL; /* linked list of tracked onions */ static struct tracked_onion *tail_tracked_onions = NULL; uint32_t now = time(NULL); struct tracked_onion *to; /* first take this opportunity to see if there are any expired * onions in the tree. we know this is fast because the linked list * 'tracked_onions' is ordered by when they were seen. */ while(head_tracked_onions && (head_tracked_onions->expire < now)) { to = head_tracked_onions; log(LOG_DEBUG,"find_tracked_onion(): Forgetting old onion (expires %d)", to->expire); head_tracked_onions = to->next; if(!head_tracked_onions) /* if there are no more, */ tail_tracked_onions = NULL; /* then make sure the list's tail knows that too */ RB_REMOVE(tracked_tree, &tracked_root, to); free(to); } to = malloc(sizeof(struct tracked_onion)); /* compute the SHA digest of the onion */ crypto_SHA_digest(onion, onionlen, to->digest); /* try adding it to the tree. if it's already there it will return it. */ if(RB_INSERT(tracked_tree, &tracked_root, to)) { /* yes, it's already there: this is a replay. */ free(to); return 1; } /* this is a new onion. add it to the list. */ to->expire = expire; /* set the expiration date */ to->next = NULL; if (!head_tracked_onions) { head_tracked_onions = to; } else { tail_tracked_onions->next = to; } tail_tracked_onions = to; log(LOG_DEBUG,"find_tracked_onion(): Remembered new onion (expires %d)", to->expire); return 0; } void onion_pack(char *dest, onion_layer_t *src) { assert((src->version & 0x80) == 0); *(uint8_t*)(dest) = src->version; *(uint16_t*)(dest+1) = htons(src->port); *(uint32_t*)(dest+3) = htonl(src->addr); *(uint32_t*)(dest+7) = htonl(src->expire); memcpy(dest+11, src->keyseed, ONION_KEYSEED_LEN); log(LOG_DEBUG,"onion_pack(): version %d, port %d, addr %s, expire %u", src->version, src->port, inet_ntoa(*((struct in_addr *)(dest+3))), src->expire); } void onion_unpack(onion_layer_t *dest, char *src) { dest->version = *(uint8_t*)src; dest->port = ntohs(*(uint16_t*)(src+1)); dest->addr = ntohl(*(uint32_t*)(src+3)); dest->expire = ntohl(*(uint32_t*)(src+7)); memcpy(dest->keyseed, src+11, ONION_KEYSEED_LEN); log(LOG_DEBUG,"onion_unpack(): version %d, port %d, addr %s, expire %u", dest->version, dest->port, inet_ntoa(*((struct in_addr *)(src+3))), dest->expire); } /*----------------------------------------------------------------------*/ /* Given a router's public key, generates a 208-byte encrypted DH pubkey, * and stores it into onion_skin out. Stores the DH private key into * handshake_state_out for later completion of the handshake. * * The encrypted pubkey is formed as follows: * 16 bytes of symmetric key * 192 bytes of g^x for DH. * The first 128 bytes are RSA-encrypted with the server's public key, * and the last 80 are encrypted with the symmetric key. */ int onion_skin_create(crypto_pk_env_t *router_key, crypto_dh_env_t **handshake_state_out, char *onion_skin_out) /* Must be 208 bytes long */ { char iv[16]; char *pubkey = NULL; crypto_dh_env_t *dh = NULL; crypto_cipher_env_t *cipher = NULL; int dhbytes, pkbytes; *handshake_state_out = NULL; memset(onion_skin_out, 0, 208); memset(iv, 0, 16); /* XXXX This can't be safe, can it? */ if (!(dh = crypto_dh_new())) goto err; dhbytes = crypto_dh_get_bytes(dh); pkbytes = crypto_pk_keysize(router_key); assert(dhbytes+16 == 208); if (!(pubkey = malloc(dhbytes))) goto err; if (crypto_rand(16, pubkey)) goto err; if (crypto_dh_get_public(dh, pubkey+16, dhbytes)) goto err; if (crypto_pk_public_encrypt(router_key, pubkey, pkbytes, onion_skin_out, RSA_NO_PADDING)) goto err; cipher = crypto_create_init_cipher(CRYPTO_CIPHER_3DES, pubkey, iv, 1); if (crypto_cipher_encrypt(cipher, pubkey+pkbytes, dhbytes-16-pkbytes, onion_skin_out+pkbytes)) goto err; free(pubkey); crypto_free_cipher_env(cipher); *handshake_state_out = dh; return 0; err: if (pubkey) free(pubkey); if (dh) crypto_dh_free(dh); if (cipher) crypto_free_cipher_env(cipher); return -1; } /* Given an encrypted DH public key as generated by onion_skin_create, * and the private key for this onion router, generate the 192-byte DH * reply, and key_out_len bytes of key material, stored in key_out. */ int onion_skin_server_handshake(char *onion_skin, /* 208 bytes long */ crypto_pk_env_t *private_key, char *handshake_reply_out, /* 192 bytes long */ char *key_out, int key_out_len) { char buf[208]; char iv[16]; crypto_dh_env_t *dh = NULL; crypto_cipher_env_t *cipher = NULL; int pkbytes; memset(iv, 0, 16); pkbytes = crypto_pk_keysize(private_key); if (crypto_pk_private_decrypt(private_key, onion_skin, pkbytes, buf, RSA_NO_PADDING)) goto err; cipher = crypto_create_init_cipher(CRYPTO_CIPHER_3DES, buf, iv, 0); if (crypto_cipher_decrypt(cipher, onion_skin+pkbytes, 208-pkbytes, buf+pkbytes)) goto err; dh = crypto_dh_new(); if (crypto_dh_get_public(dh, handshake_reply_out, 192)) goto err; if (crypto_dh_compute_secret(dh, buf+16, 192, buf)) goto err; memcpy(key_out, buf+192-key_out_len, key_out_len); crypto_free_cipher_env(cipher); crypto_dh_free(dh); return 0; err: if (cipher) crypto_free_cipher_env(cipher); if (dh) crypto_dh_free(dh); return -1; } /* Finish the client side of the DH handshake. * Given the 192 byte DH reply as generated by onion_skin_server_handshake * and the handshake state generated by onion_skin_create, generate * key_out_len bytes of shared key material and store them in key_out. * * After the invocation, call crypto_dh_free on handshake_state. */ int onion_skin_client_handshake(crypto_dh_env_t *handshake_state, char *handshake_reply,/* Must be 192 bytes long*/ char *key_out, int key_out_len) { char key_material[192]; assert(crypto_dh_get_bytes(handshake_state) == 192); memset(key_material, 0, 192); if (crypto_dh_compute_secret(handshake_state, handshake_reply, 192, key_material)) return -1; memcpy(key_out, key_material+192-key_out_len, key_out_len); return 0; } /* Local Variables: mode:c indent-tabs-mode:nil c-basic-offset:2 End: */