Index: epan/dissectors/packet-ssl-utils.c =================================================================== --- epan/dissectors/packet-ssl-utils.c (revision 28451) +++ epan/dissectors/packet-ssl-utils.c (working copy) @@ -1065,6 +1065,7 @@ {47,KEX_RSA,SIG_RSA,ENC_AES,16,128,128,DIG_SHA,20,0, SSL_CIPHER_MODE_CBC}, {51,KEX_DH, SIG_RSA,ENC_AES,16,128,128,DIG_SHA,20,0, SSL_CIPHER_MODE_CBC}, {53,KEX_RSA,SIG_RSA,ENC_AES256,16,256,256,DIG_SHA,20,0, SSL_CIPHER_MODE_CBC}, + {57,KEX_DH,SIG_RSA,ENC_AES256,16,256,256,DIG_SHA,20,0, SSL_CIPHER_MODE_CBC}, /* for tor */ {96,KEX_RSA,SIG_RSA,ENC_RC4,1,128,56,DIG_MD5,16,1, SSL_CIPHER_MODE_STREAM}, {97,KEX_RSA,SIG_RSA,ENC_RC2,1,128,56,DIG_MD5,16,1, SSL_CIPHER_MODE_STREAM}, {98,KEX_RSA,SIG_RSA,ENC_DES,8,64,64,DIG_SHA,20,1, SSL_CIPHER_MODE_STREAM}, @@ -1406,11 +1407,13 @@ /* if master_key is not yet generate, create it now*/ if (!(ssl_session->state & SSL_MASTER_SECRET)) { ssl_debug_printf("ssl_generate_keyring_material:PRF(pre_master_secret)\n"); - if (PRF(ssl_session,&ssl_session->pre_master_secret,"master secret", - &ssl_session->client_random, - &ssl_session->server_random, &ssl_session->master_secret)) { - ssl_debug_printf("ssl_generate_keyring_material can't generate master_secret\n"); - return -1; + if (!ssl_session->master_key_session) { + if (PRF(ssl_session,&ssl_session->pre_master_secret,"master secret", + &ssl_session->client_random, + &ssl_session->server_random, &ssl_session->master_secret)) { + ssl_debug_printf("ssl_generate_keyring_material can't generate master_secret\n"); + return -1; + } } ssl_print_string("master secret",&ssl_session->master_secret); @@ -1922,26 +1925,27 @@ worklen, ssl->version_netorder, ct, decoder->seq); if(ssl->version_netorder==SSLV3_VERSION){ if(ssl3_check_mac(decoder,ct,out_str->data,worklen,mac) < 0) { - ssl_debug_printf("ssl_decrypt_record: mac failed\n"); + ssl_debug_printf("sslv3_decrypt_record: mac failed\n"); return -1; } + ssl_debug_printf("sslv3_decrypt_record: mac ok\n"); } else if(ssl->version_netorder==TLSV1_VERSION || ssl->version_netorder==TLSV1DOT1_VERSION){ if(tls_check_mac(decoder,ct,ssl->version_netorder,out_str->data,worklen,mac)< 0) { - ssl_debug_printf("ssl_decrypt_record: mac failed\n"); + ssl_debug_printf("tlsv1_decrypt_record: mac failed\n"); return -1; } + ssl_debug_printf("tlsv1_decrypt_record: mac ok\n"); } else if(ssl->version_netorder==DTLSV1DOT0_VERSION || ssl->version_netorder==DTLSV1DOT0_VERSION_NOT){ /* following the openssl dtls errors the right test is: if(dtls_check_mac(decoder,ct,ssl->version_netorder,out_str->data,worklen,mac)< 0) { */ if(tls_check_mac(decoder,ct,TLSV1_VERSION,out_str->data,worklen,mac)< 0) { - ssl_debug_printf("ssl_decrypt_record: mac failed\n"); + ssl_debug_printf("dtls_decrypt_record: mac failed\n"); return -1; } } - ssl_debug_printf("ssl_decrypt_record: mac ok\n"); *outl = worklen; if (decoder->compression > 0) { @@ -2064,6 +2068,28 @@ } Ssl_private_key_t * +ssl_load_master_key(gchar *master_key) +{ + GByteArray *bytes = NULL; + gboolean res; + Ssl_private_key_t *private_key = g_malloc(sizeof(Ssl_private_key_t)); + private_key->x509_cert = 0; + private_key->x509_pkey = 0; + private_key->sexp_pkey = 0; + private_key->_master_secret = 0; + private_key->_second_master_secret = 0; + bytes = g_byte_array_new(); + res = hex_str_to_bytes(master_key, bytes,FALSE); + + if (res && bytes->len == 48) { + private_key->_master_secret=g_malloc(bytes->len); + memcpy(private_key->_master_secret,bytes->data,bytes->len); + } + g_byte_array_free(bytes, TRUE); + return private_key; +} + +Ssl_private_key_t * ssl_load_key(FILE* fp) { /* gnutls make our work much harded, since we have to work internally with @@ -2411,10 +2437,14 @@ (void *)ssl_session, (gulong)sizeof(SslDecryptSession)); ssl_session->master_secret.data = ssl_session->_master_secret; + ssl_session->primary_master_secret.data = ssl_session->_primary_master_secret; + ssl_session->secondary_master_secret.data = ssl_session->_secondary_master_secret; ssl_session->session_id.data = ssl_session->_session_id; ssl_session->client_random.data = ssl_session->_client_random; ssl_session->server_random.data = ssl_session->_server_random; ssl_session->master_secret.data_len = 48; + ssl_session->primary_master_secret.data_len = 48; + ssl_session->secondary_master_secret.data_len = 48; ssl_session->server_data_for_iv.data_len = 0; ssl_session->server_data_for_iv.data = ssl_session->_server_data_for_iv; ssl_session->client_data_for_iv.data_len = 0; @@ -2715,13 +2745,14 @@ SslService* service; Ssl_private_key_t * private_key, *tmp_private_key; FILE* fp; + const char log_line_preamble[45] = "TLS master key (address,port,protocol,key): \0"; start = g_strdup(keys_list); tmp = start; ssl_debug_printf("ssl_init keys string:\n%s\n", start); do { int read_index, write_index; - gchar* addr, *port, *protocol, *filename, *cert_passwd; + gchar* addr, *rawaddr, *port, *protocol, *filename, *cert_passwd; addr = start; /* split ip/file couple with ';' separator*/ @@ -2731,6 +2762,16 @@ start = end+1; } + /* Remove all the leading material from addr provided by a Tor log line. + This is typically of the form: + Jan 26 21:24:43.663 [info] crypto_dump_crypto_key(): \ + TLS master key (address,port,protocol,key): */ + + if ((rawaddr = strstr(addr,log_line_preamble)) != NULL) { + rawaddr += sizeof(log_line_preamble); + addr = rawaddr; + } + /* skip comments (in file) */ if (addr[0] == '#') continue; @@ -2776,12 +2817,14 @@ service->addr.len = 4; service->addr.data = ip = ((guchar*)service) + sizeof(SslService); - /* remove all spaces in addr */ + + /* remove all spaces and quotation marks in addr */ read_index = 0; write_index = 0; while(addr[read_index]) { - if (addr[read_index] != ' ') { + if (addr[read_index] != ' ' && + addr[read_index] != '"') { addr[write_index] = addr[read_index]; write_index++; } @@ -2806,36 +2849,52 @@ ssl_debug_printf("ssl_init addr '%hhu.%hhu.%hhu.%hhu' port '%d' filename '%s' password(only for p12 file) '%s'\n", ip[0], ip[1], ip[2], ip[3], service->port, filename, cert_passwd); - /* try to load pen or p12 file*/ - fp = ws_fopen(filename, "rb"); - if (!fp) { - fprintf(stderr, "can't open file %s \n",filename); - continue; - } + if (strlen(filename)==96) { + ssl_debug_printf("using master secret"); + private_key = ssl_load_master_key(filename); + /* !!! */ + if (!private_key) { + fprintf(stderr,"can't load private key from %s\n", + filename); + continue; + } + }else{ + /* try to load pen or p12 file*/ + fp = ws_fopen(filename, "rb"); + if (!fp) { + fprintf(stderr, "can't open file %s \n",filename); + continue; + } - if (!cert_passwd) { - private_key = ssl_load_key(fp); + if (!cert_passwd) { + private_key = ssl_load_key(fp); + } + else + { + private_key = ssl_load_pkcs12(fp,cert_passwd); + } + /* !!! */ + if (!private_key) { + fprintf(stderr,"can't load private key from %s\n", + filename); + continue; + } + + fclose(fp); } - else - { - private_key = ssl_load_pkcs12(fp,cert_passwd); - } - /* !!! */ - if (!private_key) { - fprintf(stderr,"can't load private key from %s\n", - filename); - continue; - } - - fclose(fp); - ssl_debug_printf("ssl_init private key file %s successfully loaded\n",filename); /* if item exists, remove first */ tmp_private_key = g_hash_table_lookup(key_hash, service); - if (tmp_private_key) { - g_hash_table_remove(key_hash, service); - ssl_free_key(tmp_private_key); + if ((tmp_private_key) && (strlen(filename)==96)) { + /* if we are loading master secrets and already have this item, then + this must be a renegotiate master secret */ + tmp_private_key->_second_master_secret=g_malloc(48); + memcpy(tmp_private_key->_second_master_secret,private_key->_master_secret,48); + continue; + }else if (tmp_private_key){ + g_hash_table_remove(key_hash, service); + ssl_free_key(tmp_private_key); } g_hash_table_insert(key_hash, service, private_key); Index: epan/dissectors/packet-ssl-utils.h =================================================================== --- epan/dissectors/packet-ssl-utils.h (revision 28451) +++ epan/dissectors/packet-ssl-utils.h (working copy) @@ -280,6 +280,8 @@ typedef struct _SslDecryptSession { guchar _master_secret[48]; + guchar _primary_master_secret[48]; + guchar _secondary_master_secret[48]; guchar _session_id[256]; guchar _client_random[32]; guchar _server_random[32]; @@ -287,6 +289,8 @@ StringInfo server_random; StringInfo client_random; StringInfo master_secret; + StringInfo primary_master_secret; + StringInfo secondary_master_secret; /* the data store for this StringInfo must be allocated explicitly with a capture lifetime scope */ StringInfo pre_master_secret; guchar _server_data_for_iv[24]; @@ -307,6 +311,8 @@ guint16 version_netorder; StringInfo app_data_segment; + guint16 master_key_session; + } SslDecryptSession; typedef struct _SslAssociation { @@ -327,7 +333,9 @@ gnutls_x509_crt_t x509_cert; gnutls_x509_privkey_t x509_pkey; #endif - SSL_PRIVATE_KEY *sexp_pkey; + SSL_PRIVATE_KEY *sexp_pkey; + gchar *_master_secret; + gchar *_second_master_secret; } Ssl_private_key_t; /** Initialize decryption engine/ssl layer. To be called once per execution */ Index: epan/dissectors/Makefile.common =================================================================== --- epan/dissectors/Makefile.common (revision 28451) +++ epan/dissectors/Makefile.common (working copy) @@ -841,6 +841,7 @@ packet-tivoconnect.c \ packet-tnef.c \ packet-tns.c \ + packet-tor.c \ packet-tpkt.c \ packet-tpncp.c \ packet-tr.c \ Index: epan/dissectors/packet-ssl.c =================================================================== --- epan/dissectors/packet-ssl.c (revision 28451) +++ epan/dissectors/packet-ssl.c (working copy) @@ -267,6 +267,7 @@ static gint ssl_decrypted_data_avail = 0; static gchar* ssl_keys_list = NULL; +static gchar* ssl_master_keys_list = NULL; #if defined(SSL_DECRYPT_DEBUG) || defined(HAVE_LIBGNUTLS) static gchar* ssl_debug_file_name = NULL; @@ -303,18 +304,45 @@ ssl_debug_flush(); } +static void +ssl_parse_keys_file(gchar* ssl_tmp_keys_list) +{ + FILE *ssl_keys_file; + struct stat statb; + size_t size; + gchar *tmp_buf; + size_t nbytes; + gboolean read_failed; + + if (file_exists(ssl_tmp_keys_list)) { + if ((ssl_keys_file = ws_fopen(ssl_tmp_keys_list, "r"))) { + read_failed = FALSE; + fstat(fileno(ssl_keys_file), &statb); + size = (size_t)statb.st_size; + tmp_buf = ep_alloc0(size + 1); + nbytes = fread(tmp_buf, 1, size, ssl_keys_file); + if (ferror(ssl_keys_file)) { + report_read_failure(ssl_tmp_keys_list, errno); + read_failed = TRUE; + } + fclose(ssl_keys_file); + tmp_buf[nbytes] = '\0'; + if (!read_failed) + ssl_parse_key_list(tmp_buf,ssl_key_hash,ssl_associations,ssl_handle,TRUE); + } else { + report_open_failure(ssl_tmp_keys_list, errno, FALSE); + } + } else { + ssl_parse_key_list(ssl_tmp_keys_list,ssl_key_hash,ssl_associations,ssl_handle,TRUE); + } + +} /* parse ssl related preferences (private keys and ports association strings) */ static void ssl_parse(void) { ep_stack_t tmp_stack; SslAssociation *tmp_assoc; - FILE *ssl_keys_file; - struct stat statb; - size_t size; - gchar *tmp_buf; - size_t nbytes; - gboolean read_failed; ssl_set_debug(ssl_debug_file_name); @@ -336,28 +364,12 @@ if (ssl_keys_list && (ssl_keys_list[0] != 0)) { - if (file_exists(ssl_keys_list)) { - if ((ssl_keys_file = ws_fopen(ssl_keys_list, "r"))) { - read_failed = FALSE; - fstat(fileno(ssl_keys_file), &statb); - size = (size_t)statb.st_size; - tmp_buf = ep_alloc0(size + 1); - nbytes = fread(tmp_buf, 1, size, ssl_keys_file); - if (ferror(ssl_keys_file)) { - report_read_failure(ssl_keys_list, errno); - read_failed = TRUE; - } - fclose(ssl_keys_file); - tmp_buf[nbytes] = '\0'; - if (!read_failed) - ssl_parse_key_list(tmp_buf,ssl_key_hash,ssl_associations,ssl_handle,TRUE); - } else { - report_open_failure(ssl_keys_list, errno, FALSE); - } - } else { - ssl_parse_key_list(ssl_keys_list,ssl_key_hash,ssl_associations,ssl_handle,TRUE); - } + ssl_parse_keys_file(ssl_keys_list); } + if (ssl_master_keys_list && (ssl_master_keys_list[0] != 0)) + { + ssl_parse_keys_file(ssl_master_keys_list); + } ssl_debug_flush(); } @@ -377,7 +389,8 @@ guint *conv_version, gboolean *need_desegmentation, SslDecryptSession *conv_data, - gboolean first_record_in_frame); + gboolean first_record_in_frame, + Ssl_private_key_t * private_key); /* change cipher spec dissector */ static void dissect_ssl3_change_cipher_spec(tvbuff_t *tvb, @@ -507,7 +520,7 @@ gboolean need_desegmentation; SslDecryptSession* ssl_session; guint* conv_version; - Ssl_private_key_t * private_key; + Ssl_private_key_t * private_key=NULL; guint32 port; ti = NULL; @@ -570,7 +583,7 @@ /* try to retrieve private key for this service. Do it now 'cause pinfo * is not always available - * Note that with HAVE_LIBGNUTLS undefined private_key is allways 0 + * Note that without HAVE_LIBGNUTLS undefined private_key is allways 0 * and thus decryption never engaged*/ @@ -598,7 +611,15 @@ if (!private_key) { ssl_debug_printf("dissect_ssl can't find any private key!\n"); } else { - ssl_session->private_key = private_key->sexp_pkey; + if (private_key->_master_secret) { + ssl_debug_printf("dissect_ssl found master_secret!\n"); + ssl_data_set(&ssl_session->primary_master_secret, private_key->_master_secret, 48); + if (private_key->_second_master_secret) + ssl_data_set(&ssl_session->secondary_master_secret, private_key->_second_master_secret, 48); + ssl_session->master_key_session=1; + }else{ + ssl_session->private_key = private_key->sexp_pkey; + } } } @@ -694,7 +715,8 @@ offset, conv_version, &need_desegmentation, ssl_session, - first_record_in_frame); + first_record_in_frame, + private_key); } break; @@ -717,7 +739,8 @@ offset, conv_version, &need_desegmentation, ssl_session, - first_record_in_frame); + first_record_in_frame, + private_key); } else { @@ -1284,7 +1307,8 @@ dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset, guint *conv_version, gboolean *need_desegmentation, - SslDecryptSession* ssl, gboolean first_record_in_frame _U_) + SslDecryptSession* ssl, gboolean first_record_in_frame _U_, + Ssl_private_key_t * private_key) { /* @@ -1535,7 +1559,7 @@ case SSL_ID_HANDSHAKE: { tvbuff_t* decrypted; - + if (private_key){}; /* try to decrypt handshake record, if possible. Store decrypted * record for later usage. The offset is used as 'key' to identify * this record in the packet (we can have multiple handshake records @@ -1545,14 +1569,26 @@ ssl_add_record_info(proto_ssl, pinfo, ssl_decrypted_data.data, ssl_decrypted_data_avail, offset); + ssl_debug_printf("dissect_ssl3_record: decrypting handshake\n"); /* try to retrieve and use decrypted handshake record, if any. */ decrypted = ssl_get_record_info(proto_ssl, pinfo, offset); if (decrypted) { /* add desegmented data to the data source list */ add_new_data_source(pinfo, decrypted, "Decrypted SSL record"); + /* if we are using user-supplied master_keys we need to switch + to the renegotiated master-key now. */ + ssl_debug_printf("dissect_ssl3_record: should i change master secret?\n"); + if (ssl && &ssl->master_key_session && &ssl->secondary_master_secret) { + ssl_debug_printf("dissect_ssl3_record: using secondary master_secret!\n"); + ssl->master_secret = ssl->secondary_master_secret; + } dissect_ssl3_handshake(decrypted, pinfo, ssl_record_tree, 0, decrypted->length, conv_version, ssl, content_type); } else { + if (ssl && &ssl->master_key_session) { + ssl_debug_printf("dissect_ssl3_record: using primary master_secret!\n"); + ssl->master_secret = ssl->primary_master_secret; + } dissect_ssl3_handshake(tvb, pinfo, ssl_record_tree, offset, record_length, conv_version, ssl, content_type); } @@ -1880,6 +1916,7 @@ if (!ssl) break; + ssl_debug_printf("dissect_ssl3_handshake SSL_HND_CLIENT_KEY_EXCHG\n"); /* get encrypted data, on tls1 we have to skip two bytes * (it's the encrypted len and should be equal to record len - 2) @@ -1899,18 +1936,23 @@ encrypted_pre_master.data_len = encrlen; tvb_memcpy(tvb, encrypted_pre_master.data, offset+skip, encrlen); - if (!ssl->private_key) { + if (!ssl->private_key && !ssl->master_key_session) { ssl_debug_printf("dissect_ssl3_handshake can't find private key\n"); break; } /* go with ssl key processessing; encrypted_pre_master * will be used for master secret store*/ - ret = ssl_decrypt_pre_master_secret(ssl, &encrypted_pre_master, ssl->private_key); - if (ret < 0) { - ssl_debug_printf("dissect_ssl3_handshake can't decrypt pre master secret\n"); - break; + if (!ssl->master_key_session) { + ret = ssl_decrypt_pre_master_secret(ssl, &encrypted_pre_master, ssl->private_key); + if (ret < 0) { + ssl_debug_printf("dissect_ssl3_handshake can't decrypt pre master secret\n"); + break; + } + }else{ + ssl->state |= SSL_MASTER_SECRET; } + if (ssl_generate_keyring_material(ssl)<0) { ssl_debug_printf("dissect_ssl3_handshake can't generate keyring material\n"); break; @@ -1969,7 +2011,7 @@ session_id_length = tvb_get_guint8(tvb, offset + 32); /* check stored session id info */ - if (from_server && (session_id_length == ssl->session_id.data_len) && + if (session_id_length && from_server && (session_id_length == ssl->session_id.data_len) && (tvb_memeql(tvb, offset+33, ssl->session_id.data, session_id_length) == 0)) { /* client/server id match: try to restore a previous cached session*/ @@ -4374,6 +4416,11 @@ " is the local file name of the RSA private key used by the specified server " "(or name of the file containing such a list)", (const gchar **)&ssl_keys_list); + prefs_register_string_preference(ssl_module, "master_keys_list", "SSL master keys list", + "Semicolon-separated list of private SSL/TLS master keys used for SSL decryption; " + "each list entry must be in the form of ,,,. " + " is the master key used during the TLS/SSL session", + (const gchar **)&ssl_master_keys_list); prefs_register_string_preference(ssl_module, "debug_file", "SSL debug file", "Redirect SSL debug to file name; leave empty to disable debugging, " "or use \"" SSL_DEBUG_USE_STDERR "\" to redirect output to stderr\n", Index: epan/dissectors/packet-tor.c =================================================================== --- epan/dissectors/packet-tor.c (revision 0) +++ epan/dissectors/packet-tor.c (revision 0) @@ -0,0 +1,1817 @@ +/* packet-tor.c + * Routines for tor.packet dissection + * https://svn.torproject.org/svn/tor/trunk/doc/spec/tor-spec.txt + * https://svn.torproject.org/svn/tor/trunk/doc/spec/rend-spec.txt + * Copyright 2009, Robert Hogan + * + * $Id: packet-tor.c 26356 2008-10-05 23:08:54Z wmeier $ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * Copied from packet-pop.c and packet-ssl.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * TODO: + * Call subdissectors for relay payloads + * Store the context for a decrypted payload rather than the decrypted payload itself + * Remove memleaks + * + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "packet-ssl.h" +#include "packet-ssl-utils.h" + +#define REVERSE 0 +#define FORWARD 1 + +#define PADDING 0x00 +#define CREATE 0x01 +#define CREATED 0x02 +#define RELAY 0x03 +#define DESTROY 0x04 +#define CREATE_FAST 0x05 +#define CREATED_FAST 0x06 +#define VERSIONS 0x07 +#define NETINFO 0x08 +#define RELAY_EARLY 0x09 + +static const value_string vals_circuit_commands[] = { + { PADDING , "(Padding)"}, + { CREATE , "(Create a circuit)"}, + { CREATED , "(Acknowledge create)"}, + { RELAY , "(End-to-end data)"}, + { DESTROY , "(Stop using a circuit)"}, + { CREATE_FAST , "(Create a circuit, no PK)"}, + { CREATED_FAST, "(Circuit created, no PK)"}, + { VERSIONS , "(Negotiate proto version)"}, + { NETINFO , "(Time and address info)"}, + { RELAY_EARLY , "(End-to-end data (Relay Early).)"}, + { 0x00, NULL } +}; + +#define NONE 0x00 +#define PROTOCOL 0x01 +#define INTERNAL 0x02 +#define REQUESTED 0x03 +#define HIBERNATING 0x04 +#define RESOURCELIMIT 0x05 +#define CONNECTFAILED 0x06 +#define OR_IDENTITY 0x07 +#define OR_CONN_CLOSED 0x08 +#define FINISHED 0x09 +#define TIMEOUT 0x0A +#define DESTROYED 0x0B +#define NOSUCHSERVICE 0x0C + +static const value_string vals_destroy_reason[] = { + { NONE , "(No reason given.)" }, + { PROTOCOL , "(Tor protocol violation.)" }, + { INTERNAL , "(Internal error.)" }, + { REQUESTED , "(A client sent a TRUNCATE command.)" }, + { HIBERNATING , "(Not currently operating; trying to save bandwidth.)" }, + { RESOURCELIMIT , "(Out of memory, sockets, or circuit IDs.)" }, + { CONNECTFAILED , "(Unable to reach server.)" }, + { OR_IDENTITY , "(Connected to server, but its OR identity was not as expected.)" }, + { OR_CONN_CLOSED, "(The OR connection that was carrying this circuit died.)" }, + { FINISHED , "(The circuit has expired for being dirty or old.)" }, + { TIMEOUT , "(Circuit construction took too long)" }, + { DESTROYED , "(The circuit was destroyed w/o client TRUNCATE)" }, + { NOSUCHSERVICE , "(Request for unknown hidden service)"}, + { 0x00, NULL } +}; + +#define RELAY_BEGIN 0x01 +#define RELAY_DATA 0x02 +#define RELAY_END 0x03 +#define RELAY_CONNECTED 0x04 +#define RELAY_SENDME 0x05 +#define RELAY_EXTEND 0x06 +#define RELAY_EXTENDED 0x07 +#define RELAY_TRUNCATE 0x08 +#define RELAY_TRUNCATED 0x09 +#define RELAY_DROP 0x0A +#define RELAY_RESOLVE 0x0B +#define RELAY_RESOLVED 0x0C +#define RELAY_BEGIN_DIR 0x0D + +#define RELAY_ESTABLISH_INTRO 0x20 +#define RELAY_ESTABLISH_RENDEZVOUS 0x21 +#define RELAY_INTRODUCE1 0x22 +#define RELAY_INTRODUCE2 0x23 +#define RELAY_RENDEZVOUS1 0x24 +#define RELAY_RENDEZVOUS2 0x25 +#define RELAY_INTRO_ESTABLISHED 0x26 +#define RELAY_RENDEZVOUS_ESTABLISHED 0x27 +#define RELAY_COMMAND_INTRODUCE_ACK 0x28 + +static const value_string vals_relay_commands[] = { + { RELAY_BEGIN , "RELAY_BEGIN"}, + { RELAY_DATA , "RELAY_DATA"}, + { RELAY_END , "RELAY_END"}, + { RELAY_CONNECTED, "RELAY_CONNECTED"}, + { RELAY_SENDME , "RELAY_SENDME"}, + { RELAY_EXTEND , "RELAY_EXTEND"}, + { RELAY_EXTENDED , "RELAY_EXTENDED"}, + { RELAY_TRUNCATE , "RELAY_TRUNCATE"}, + { RELAY_TRUNCATED, "RELAY_TRUNCATED"}, + { RELAY_DROP , "RELAY_DROP"}, + { RELAY_RESOLVE , "RELAY_RESOLVE"}, + { RELAY_RESOLVED , "RELAY_RESOLVED"}, + { RELAY_BEGIN_DIR, "RELAY_BEGIN_DIR"}, + { RELAY_ESTABLISH_INTRO , "RELAY_ESTABLISH_INTRO"}, + { RELAY_ESTABLISH_RENDEZVOUS , "RELAY_ESTABLISH_RENDEZVOUS"}, + { RELAY_INTRODUCE1 , "RELAY_INTRODUCE1"}, + { RELAY_INTRODUCE2 , "RELAY_INTRODUCE2"}, + { RELAY_RENDEZVOUS1 , "RELAY_RENDEZVOUS1"}, + { RELAY_RENDEZVOUS2 , "RELAY_RENDEZVOUS2"}, + { RELAY_INTRO_ESTABLISHED , "RELAY_INTRO_ESTABLISHED"}, + { RELAY_RENDEZVOUS_ESTABLISHED, "RELAY_RENDEZVOUS_ESTABLISHED"}, + { RELAY_COMMAND_INTRODUCE_ACK , "RELAY_COMMAND_INTRODUCE_ACK"}, + { 0x00, NULL } +}; + +#define HOSTNAME 0x00 +#define IPV4_ADDRESS 0x04 +#define IPV6_ADDRESS 0x06 +#define ERROR_TRANSIENT 0xF0 +#define ERROR_NONTRANSIENT 0xF1 + +static const value_string vals_address_types[] = { + { HOSTNAME , "HOSTNAME"}, + { IPV4_ADDRESS , "IPV4_ADDRESS"}, + { IPV6_ADDRESS , "IPV6_ADDRESS"}, + { ERROR_TRANSIENT , "ERROR_TRANSIENT"}, + { ERROR_NONTRANSIENT, "ERROR_NONTRANSIENT"}, + { 0x00, NULL } +}; + +#define REASON_MISC 0x01 +#define REASON_RESOLVEFAILED 0x02 +#define REASON_CONNECTREFUSED 0x03 +#define REASON_EXITPOLICY 0x04 +#define REASON_DESTROY 0x05 +#define REASON_DONE 0x06 +#define REASON_TIMEOUT 0x07 +#define REASON_HIBERNATING 0x09 +#define REASON_INTERNAL 0x0A +#define REASON_RESOURCELIMIT 0x0B +#define REASON_CONNRESET 0x0C +#define REASON_TORPROTOCOL 0x0D +#define REASON_NOTDIRECTORY 0x0E + +static const value_string vals_relay_end_reason[] = { + { REASON_MISC , "(catch-all for unlisted reasons)"}, + { REASON_RESOLVEFAILED , "(couldn't look up hostname)"}, + { REASON_CONNECTREFUSED , "(remote host refused connection) [*]"}, + { REASON_EXITPOLICY , "(OR refuses to connect to host or port)"}, + { REASON_DESTROY , "(Circuit is being destroyed)"}, + { REASON_DONE , "(Anonymized TCP connection was closed)"}, + { REASON_TIMEOUT , "(Connection timed out, or OR timed out while connecting)"}, + { REASON_HIBERNATING , "(OR is temporarily hibernating)"}, + { REASON_INTERNAL , "(Internal error at the OR)"}, + { REASON_RESOURCELIMIT , "(OR has no resources to fulfill request)"}, + { REASON_CONNRESET , "(Connection was unexpectedly reset)"}, + { REASON_TORPROTOCOL , "(Sent when closing connection because of Tor protocol violations.)"}, + { REASON_NOTDIRECTORY , "(Client sent RELAY_BEGIN_DIR to a non-directory server.)"}, + { 0x00, NULL } +}; + +#define CELL_LEN 512 +#define PAYLOAD_LEN 509 +#define RELAY_DATA_LEN PAYLOAD_LEN - 11 +#define HASH_LEN 20 +#define DH_LEN 128 +#define MAX_BLOCKSIZE 16 + +#define TCP_PORT_SSL_TOR 9001 + +static int proto_tor = -1; + +static int hf_tor_circid = -1; +static int hf_tor_command = -1; +static int hf_tor_payload = -1; +static int hf_tor_length = -1; +static int hf_tor_timestamp = -1; +static int hf_tor_other_or_address = -1; +static int hf_tor_other_or_address_ipv6 = -1; +static int hf_tor_number_of_addresses = -1; +static int hf_tor_this_or_address = -1; +static int hf_tor_this_or_address_ipv6 = -1; +static int hf_tor_address_type = -1; +static int hf_tor_address_length = -1; +static int hf_tor_hostname = -1; +static int hf_tor_ttl = -1; +static int hf_tor_key_material_y = -1; +static int hf_tor_key_material_x = -1; +static int hf_tor_dh_data = -1; +static int hf_tor_derivative_key_data = -1; +static int hf_tor_create = -1; +static int hf_tor_destroy_reason = -1; +static int hf_tor_relay_command = -1; +static int hf_tor_relay_data = -1; +static int hf_tor_relay_data_length = -1; +static int hf_tor_relay_digest = -1; +static int hf_tor_streamid = -1; +static int hf_tor_recognized = -1; +static int hf_tor_padding = -1; +static int hf_tor_encrypted_cell = -1; +static int hf_tor_relay_connected_ip = -1; +static int hf_tor_relay_connected_ttl = -1; +static int hf_tor_relay_end_reason = -1; +static int hf_tor_relay_end_ip = -1; +static int hf_tor_relay_end_ipv6 = -1; +static int hf_tor_relay_end_ttl = -1; +static int hf_tor_resolved_status = -1; +static int hf_tor_resolve_address = -1; +static int hf_tor_resolve_port = -1; +static int hf_tor_establish_intro_hs = -1; +static int hf_tor_establish_intro_kl = -1; +static int hf_tor_establish_intro_pk = -1; +static int hf_tor_establish_intro_sig = -1; +static int hf_tor_establish_rendezvous_cookie = -1; +static int hf_tor_introduce_encrypted_payload = -1; +static int hf_tor_introduce_pk_id = -1; +static int hf_tor_rendezvous_cookie = -1; +static int hf_tor_rendezvous_dh = -1; +static int hf_tor_rendezvous_hd = -1; + +static gint ett_tor = -1; + +static dissector_handle_t data_handle, http_handle; + +static gchar* cell_keys_list = NULL; +static GHashTable *tor_key_hash = NULL; +static GHashTable *tor_relay_data_hash = NULL; + +typedef struct _tor_cipher_hd_t +{ + + unsigned char ctr[MAX_BLOCKSIZE]; /* For Counter (CTR) mode. */ + + /*This is the context's current position in ctrbuf. Where the number + of bytes encrypted are not a multiple of c->cipher_block_size this + stores the position of the next byte in the keystream to be xor'd + by do_encrypt_ctr. ctr gets incremented every time ctrpos reaches + c->cipher_block_size. */ + unsigned int ctrpos; /* For Counter (CTR) mode. */ + + /* This contains the context's current keystream. ctrpos stores the + next position to be xor'd in ctrbuf when do_ctr_encrypt is called. + The contents of ctr are encrypted every time ctrpos reaches + zero in do_ctr_encrypt, and the result is stored in ctrbuf. */ + char ctrbuf[MAX_BLOCKSIZE]; /* For Counter (CTR) mode. */ + +} tor_cipher_hd_t; + + +typedef struct _cell_key { + gchar *key; + gint no_of_hops; + tor_cipher_hd_t ctr_data; + gcry_md_hd_t digest_hd; + guint hop; + struct _cell_key *next; +} cell_key_t; + +typedef struct _TorService { + guint16 circid; + guint8 source[4]; + guint direction; +} TorService; + +void proto_reg_handoff_tor(void); + +static void +tor_add_cell_key(cell_key_t *key, cell_key_t *newkey) +{ + cell_key_t *first_key = key; + + newkey->hop++; + while (key->next) { + newkey->hop++; + key = key->next; + } + key->next = newkey; + first_key->no_of_hops = newkey->hop; + +} + +static cell_key_t * +tor_get_cell_key(cell_key_t *rec, guint hop) +{ + while (rec) { + if (rec->hop == hop) return rec; + rec = rec->next; + } + return NULL; +} + +/* store master secret into session data cache */ +void +tor_save_decrypted(StringInfo* data_key, const guint8 *decrypted) +{ + /* allocate stringinfo chunks for session id and master secret data*/ + StringInfo* decrypted_data; + decrypted_data = se_alloc0(PAYLOAD_LEN + sizeof(StringInfo)); + + decrypted_data->data = ((guchar*)decrypted_data+sizeof(StringInfo)); + + /* ssl_hash() depends on data_key->data being aligned for guint access + * so be careful in changing how it is allocated. + */ + memcpy(decrypted_data->data, decrypted, PAYLOAD_LEN); + decrypted_data->data_len = PAYLOAD_LEN; + + g_hash_table_insert(tor_relay_data_hash, data_key, decrypted_data); +} + +static gint +tor_relay_data_equal (gconstpointer v, gconstpointer v2) +{ + const StringInfo *val1; + const StringInfo *val2; + val1 = (const StringInfo *)v; + val2 = (const StringInfo *)v2; + + if (val1->data_len == val2->data_len && + !memcmp(val1->data, val2->data, val2->data_len)) { + return 1; + } + return 0; +} + +static guint +tor_relay_data_key_hash (gconstpointer v) +{ + guint l,hash; + const StringInfo* id; + const guint* cur; + hash = 0; + id = (const StringInfo*) v; + + cur = (const guint*)(void*) id->data; + + for (l=4; (l < id->data_len); l+=4, cur++) + hash = hash ^ (*cur); + + return hash; +} + +static void +tor_relay_data_free(gpointer id, gpointer key, gpointer dummy _U_) +{ + g_free(id); + g_free((StringInfo *)key); +} + +static gint +tor_key_equal (gconstpointer v, gconstpointer v2) +{ + const TorService *val1; + const TorService *val2; + val1 = (const TorService *)v; + val2 = (const TorService *)v2; + + if ((val1->circid == val2->circid) && + (val1->direction == val2->direction) && + !memcmp(val1->source, val2->source, 4)) { + return 1; + } + return 0; +} + +static guint +tor_cell_key_hash (gconstpointer v) +{ + const TorService *key; + const guint* cr; + guint hash; + key = (const TorService *)v; + hash = key->circid; + hash = hash ^ key->direction; + cr = (const guint*) key->source; + hash = hash ^ (*cr); + return hash; +} + +static void +tor_key_free(gpointer id, gpointer key, gpointer dummy _U_) +{ + g_free(id); + g_free((cell_key_t*)key); +} + +static gint +tor_cipher_init(gcry_cipher_hd_t *cipher, guchar* sk) +{ + gint err; + + err = gcry_cipher_open(cipher, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_ECB, 0); + if (err !=0) + return -1; + err = gcry_cipher_setkey(*(cipher), sk, gcry_cipher_get_algo_blklen (GCRY_CIPHER_AES128)); + if (err != 0) + return -1; + gcry_cipher_setiv(*(cipher), NULL, 0); + if (err != 0) + return -1; + return 0; +} + +/* Initialize AES-CTR wrapper for libgcrypt */ +static gint +tor_ctr_init(tor_cipher_hd_t *c) +{ + + memset (c->ctr, 0, MAX_BLOCKSIZE); + memset (c->ctrbuf, 0, MAX_BLOCKSIZE); + c->ctrpos=0; + return 0; +} + +/* AES-CTR wrapper for libgcrypt */ +static void +tor_do_ctr_encrypt( tor_cipher_hd_t *c, guchar *sk, char *inbuf, unsigned int nbytes ) +{ + unsigned int n, i; + gcry_cipher_hd_t cipher; + tor_cipher_init(&cipher, sk); + + for (n = 0; n < nbytes;) + { + if (c->ctrpos == 0) { + gcry_cipher_encrypt ( cipher, c->ctrbuf, MAX_BLOCKSIZE, + c->ctr, MAX_BLOCKSIZE); + } + for (i = c->ctrpos; i < MAX_BLOCKSIZE; i++, n++) + { + if (n == nbytes) { c->ctrpos = i; return; } + inbuf[n] ^= c->ctrbuf[i]; + } + for (i = MAX_BLOCKSIZE; i > 0; i--) + { + c->ctr[i-1]++; + if (c->ctr[i-1] != 0) + break; + } + c->ctrpos = 0; + } + gcry_cipher_close(cipher); + + return; +} + +static void +tor_decrypt_cell(guint8 *encrypted_data, cell_key_t *cell_key) +{ + + if (!cell_key->ctr_data.ctr) { + tor_ctr_init(&cell_key->ctr_data); + } + + tor_do_ctr_encrypt(&cell_key->ctr_data, cell_key->key, encrypted_data, PAYLOAD_LEN); + +} + +static cell_key_t * +tor_load_cell_key(gchar *key, gchar *digest) +{ + GByteArray *bytes = NULL; + GByteArray *dbytes = NULL; + gboolean res; + cell_key_t *cell_key = g_malloc(sizeof(cell_key_t)); + cell_key->key = 0; + cell_key->hop=1; + cell_key->no_of_hops=1; + cell_key->next = NULL; + bytes = g_byte_array_new(); + res = hex_str_to_bytes(key, bytes,FALSE); + + /* Save the circuit key */ + if (res && bytes->len == 16) { + cell_key->key=g_malloc(bytes->len); + memcpy(cell_key->key,bytes->data,bytes->len); + } + + /* Set up the AES_CTR wrapper */ + tor_ctr_init(&cell_key->ctr_data); + + /* Seed the circuit digest */ + dbytes = g_byte_array_new(); + res = hex_str_to_bytes(digest, dbytes, FALSE); + gcry_md_open (&cell_key->digest_hd, GCRY_MD_SHA1, 0); + gcry_md_write (cell_key->digest_hd, dbytes->data, dbytes->len); + + g_byte_array_free(bytes, TRUE); + g_byte_array_free(dbytes, TRUE); + return cell_key; +} + +/* parse tor related preferences (cell keys and ports association strings) */ +static void +tor_parse_key_list(const gchar * keys_list, GHashTable *key_hash) +{ + gchar* end; + gchar* start; + gchar* tmp; + TorService* service; + cell_key_t *cell_key=NULL, *tmp_cell_key=NULL; + char log_line_preamble[60] = "Circuit key (address,port," + "circid,direction,digest,crypto): \0"; + + start = g_strdup(keys_list); + tmp = start; + ssl_debug_printf("tor_init keys string:\n%s\n", start); + do { + int read_index, write_index; + gchar *address, *port = 0, *circid = 0, *direction, *key, *digest; + + address = start; + /* split ip/file couple with ';' separator*/ + end = strpbrk(start, ";\n\r"); + if (end) { + *end = 0; + start = end+1; + } + + /* Remove all the leading material from addr provided by a Tor log line. + This is typically of the form: + Jan 26 21:24:43.663 [info] crypto_dump_crypto_key(): \ + Circuit key (address,port,circid,direction,digest,crypto): */ + + if ((address = strstr(address,log_line_preamble)) == NULL) { + ssl_debug_printf("tor_init entry malformed can't find address in %s\n", address); + continue; + } + address += sizeof(log_line_preamble); + + /* skip comments (in file) */ + if (address[0] == '#') continue; + + + ssl_debug_printf("tor_init found port entry %s\n", port); + port = strchr(address, ','); + if (!port) + { + ssl_debug_printf("tor_init entry malformed can't find port in '%s'\n", address); + continue; + } + *port = 0; + port++; + + ssl_debug_printf("tor_init found circid entry %s\n", circid); + circid = strchr(port, ','); + if (!circid) + { + ssl_debug_printf("tor_init entry malformed can't find circid in '%s'\n", port); + continue; + } + *circid = 0; + circid++; + + ssl_debug_printf("tor_init found circid entry %s\n", circid); + direction = strchr(circid, ','); + if (!direction || (strncmp(",forward", direction,8) + && (strncmp(",reverse", direction,8)))) + { + ssl_debug_printf("tor_init entry malformed can't find direction in '%s'\n", circid); + continue; + } + *direction = 0; + direction++; + + digest = strchr(direction,','); + if (!digest) + { + ssl_debug_printf("tor_init entry malformed can't find digest in %s\n", direction); + continue; + } + *digest=0; + digest++; + + key = strchr(digest,','); + if (!key) + { + ssl_debug_printf("tor_init entry malformed can't find key in %s\n", digest); + continue; + } + *key=0; + key++; + + + /* remove all spaces and quotation marks in addr */ + read_index = 0; + write_index = 0; + + while(address[read_index]) { + if (address[read_index] != ' ' && + address[read_index] != '"') { + address[write_index] = address[read_index]; + write_index++; + } + read_index++; + } + address[write_index] = 0; + + /* Load the cell key information for look-up when dissecting */ + service = g_malloc(sizeof(TorService) + 8); + service->circid = atoi(circid); + if(!strcmp("forward", direction)) { + service->direction = FORWARD; + }else{ + service->direction = REVERSE; + } + + memset(service->source,0,4); + if (strlen(key)==32) { + cell_key = tor_load_cell_key(key, digest); + /* !!! */ + if (!cell_key) { + ssl_debug_printf("can't load cell key from %s\n", key); + continue; + } + } + ssl_debug_printf("tor_init cell key file %s successfully loaded\n",key); + + /* if item exists, remove first */ + tmp_cell_key = g_hash_table_lookup(key_hash, service); + if (tmp_cell_key){ + tor_add_cell_key(tmp_cell_key, cell_key); + } else { + g_hash_table_insert(key_hash, service, cell_key); + } + + } while (end != NULL); + g_free(tmp); +} + +static void +tor_parse_keys_file(gchar* tor_tmp_keys_list) +{ + FILE *tor_keys_file; + struct stat statb; + size_t size; + gchar *tmp_buf; + size_t nbytes; + gboolean read_failed; + + if (file_exists(tor_tmp_keys_list)) { + if ((tor_keys_file = ws_fopen(tor_tmp_keys_list, "r"))) { + read_failed = FALSE; + fstat(fileno(tor_keys_file), &statb); + size = statb.st_size; + tmp_buf = ep_alloc0(size + 1); + nbytes = fread(tmp_buf, 1, size, tor_keys_file); + if (ferror(tor_keys_file)) { + report_read_failure(tor_tmp_keys_list, errno); + read_failed = TRUE; + } + fclose(tor_keys_file); + tmp_buf[nbytes] = '\0'; + if (!read_failed) + tor_parse_key_list(tmp_buf,tor_key_hash); + } else { + report_open_failure(tor_tmp_keys_list, errno, FALSE); + } + } else { + tor_parse_key_list(tor_tmp_keys_list,tor_key_hash); + } + +} + +/* parse tor related preferences (cell keys and ports association strings) */ +static void +tor_parse(void) +{ + if (tor_key_hash) + { + g_hash_table_foreach(tor_key_hash, tor_key_free, NULL); + g_hash_table_destroy(tor_key_hash); + } + + /* parse cell keys string, load available keys and put them in key hash*/ + tor_key_hash = g_hash_table_new(tor_cell_key_hash,tor_key_equal); + + if (tor_relay_data_hash) + { + g_hash_table_foreach(tor_relay_data_hash, tor_relay_data_free, NULL); + g_hash_table_destroy(tor_relay_data_hash); + } + + /* parse cell relay_datas string, load available relay_datas and put them in relay_data hash*/ + tor_relay_data_hash = g_hash_table_new(tor_relay_data_key_hash,tor_relay_data_equal); + + if (cell_keys_list && (cell_keys_list[0] != 0)) + { + tor_parse_keys_file(cell_keys_list); + } +} + +static gint +add_tor_address(proto_tree *payload_tree, tvbuff_t *tvb, gint offset, gboolean other) +{ + gint address_type, address_length, start; + struct e_in6_addr address_ipv6; + + start=offset; + + address_type=tvb_get_guint8(tvb, offset); + proto_tree_add_item(payload_tree, + hf_tor_address_type, + tvb, offset, + 1, + FALSE); + offset+=1; + + address_length=tvb_get_guint8(tvb, offset); + proto_tree_add_item(payload_tree, + hf_tor_address_length, + tvb, offset, + 1, + FALSE); + offset+=1; + + switch(address_type) { + case HOSTNAME: + proto_tree_add_item(payload_tree, + hf_tor_hostname, + tvb, offset, + address_length, + FALSE); + break; + case IPV4_ADDRESS: + proto_tree_add_ipv4(payload_tree, + (other)?hf_tor_other_or_address:hf_tor_this_or_address, + tvb, offset, + address_length, + tvb_get_ipv4(tvb,offset)); + break; + case IPV6_ADDRESS: + tvb_get_ipv6(tvb, offset, &address_ipv6); + proto_tree_add_ipv6(payload_tree, + (other)?hf_tor_other_or_address_ipv6:hf_tor_this_or_address_ipv6, + tvb, offset, + 16, (guint8 *)&address_ipv6); + break; + default: + proto_tree_add_item(payload_tree, + hf_tor_hostname, + tvb, offset, + address_length, + FALSE); + } + offset+=address_length; + return (offset-start); +} + +static gint +dissect_relay_resolved_payload(proto_tree *payload_tree, tvbuff_t *tvb, gint offset) +{ + gint start=offset; + + while (tvb_offset_exists(tvb, offset) && tvb_get_guint8(tvb, offset)) { + offset+=add_tor_address(payload_tree, tvb, offset, FALSE); + proto_tree_add_item(payload_tree, + hf_tor_ttl, + tvb, offset, + 1, + FALSE); + offset+=1; + } + return (offset-start); +} + +static gint +dissect_relay_end_payload(proto_tree *payload_tree, tvbuff_t *tvb, gint offset) +{ + int end_reason, used = 0, is_ipv6 = 0; + + end_reason = tvb_get_guint8(tvb, offset); + proto_tree_add_item(payload_tree, + hf_tor_relay_end_reason, + tvb, offset, + 1, + FALSE); + used+=1; + + if (end_reason == REASON_EXITPOLICY) { + is_ipv6 = tvb_get_guint8(tvb, offset+5); + if (is_ipv6) { + proto_tree_add_item(payload_tree, + hf_tor_relay_end_ipv6, + tvb, offset + used, + 16, + FALSE); + used+=16; + } else { + proto_tree_add_item(payload_tree, + hf_tor_relay_end_ip, + tvb, offset + used, + 4, + FALSE); + used+=4; + } + proto_tree_add_item(payload_tree, + hf_tor_relay_end_ttl, + tvb, offset + used, + 4, + FALSE); + used+=4; + } + + return used; +} + +static gint +dissect_relay_connected_payload(proto_tree *payload_tree, tvbuff_t *tvb, gint offset) +{ + int start=offset; + proto_tree_add_item(payload_tree, + hf_tor_relay_connected_ip, + tvb, offset, + 4, + FALSE); + offset+=4; + proto_tree_add_item(payload_tree, + hf_tor_relay_connected_ttl, + tvb, offset, + 4, + FALSE); + offset+=4; + return (offset-start); +} + +static gint +dissect_relay_introduce_payload(proto_tree *payload_tree, tvbuff_t *tvb, + gint offset) +{ + + proto_tree_add_item(payload_tree, + hf_tor_introduce_pk_id, + tvb, offset, + 20, + FALSE); + offset+=20; + + proto_tree_add_item(payload_tree, + hf_tor_introduce_encrypted_payload, + tvb, offset, + RELAY_DATA_LEN - 20, + FALSE); + + return RELAY_DATA_LEN; +} + +static gint +dissect_relay_rendezvous_payload(proto_tree *payload_tree, tvbuff_t *tvb, + gint offset, gboolean one) +{ + int start=offset; + + if (one) { + proto_tree_add_item(payload_tree, + hf_tor_rendezvous_cookie, + tvb, offset, + 20, + FALSE); + offset+=20; + } + + proto_tree_add_item(payload_tree, + hf_tor_rendezvous_dh, + tvb, offset, + DH_LEN, + FALSE); + offset+=DH_LEN; + + proto_tree_add_item(payload_tree, + hf_tor_rendezvous_hd, + tvb, offset, + 20, + FALSE); + offset+=20; + return (offset - start); +} + +static gint +dissect_relay_establish_rendezvous_payload(proto_tree *payload_tree, tvbuff_t *tvb, + gint offset) +{ + int start=offset; + + proto_tree_add_item(payload_tree, + hf_tor_establish_rendezvous_cookie, + tvb, offset, + 20, + FALSE); + offset+=20; + + return (offset - start); +} + +static gint +dissect_relay_establish_intro_payload(proto_tree *payload_tree, tvbuff_t *tvb, + gint offset) +{ + gint used = 0; + guint key_length; + + key_length = (guint)tvb_get_ntohs(tvb, offset); + proto_tree_add_item(payload_tree, + hf_tor_establish_intro_kl, + tvb, offset, + 2, + FALSE); + used+=2; + + proto_tree_add_item(payload_tree, + hf_tor_establish_intro_pk, + tvb, offset + used, + key_length, + FALSE); + used+=key_length; + + proto_tree_add_item(payload_tree, + hf_tor_establish_intro_hs, + tvb, offset + used, + 20, + FALSE); + used+=20; + + proto_tree_add_item(payload_tree, + hf_tor_establish_intro_sig, + tvb, offset + used, + (RELAY_DATA_LEN - used), + FALSE); + + return RELAY_DATA_LEN; +} + +static gint +dissect_relay_resolve_payload(proto_tree *payload_tree, tvbuff_t *tvb, gint offset) +{ + const guint8 *resolve_address; + + resolve_address = tvb_get_ephemeral_string(tvb, offset, RELAY_DATA_LEN); + + if (!resolve_address) + return 0; + + proto_tree_add_item(payload_tree, + hf_tor_resolve_address, + tvb, offset, + strlen(resolve_address), + FALSE); + + return strlen(resolve_address); +} + +static gint +dissect_destroy_cell(proto_tree *payload_tree, tvbuff_t *tvb, gint offset) +{ + gint start=offset; + + proto_tree_add_item(payload_tree, + hf_tor_destroy_reason, + tvb, offset, + 1, + FALSE); + offset+=1; + + return (offset-start); +} + +static gint +tor_add_padding(proto_tree *tree, tvbuff_t *tvb, gint offset) +{ + /*Add any Padding*/ + gint t_offset = offset; + while (tvb_offset_exists(tvb, t_offset) && !tvb_get_guint8(tvb, t_offset)) + ++t_offset; + if (t_offset - offset > 2) { + proto_tree_add_item(tree, + hf_tor_padding, + tvb, offset, + t_offset - offset, + FALSE); + return (t_offset - offset); + } + return 0; +} + +/* Check the four byte digest in the relay cell payload */ +static gint +check_relay_digest(gcry_md_hd_t *digest_hd, char *data, char *relay_digest) +{ + gcry_md_hd_t digest_hd_pre, digest_hd_post; + char *current_digest; + char *digest_data; + int ret = 1; + + /* Set the four byte digest in the payload to zero when calculating the + digest */ + digest_data = (guint8 *) g_malloc ((PAYLOAD_LEN) * sizeof(guint8)); + memset(digest_data,0,PAYLOAD_LEN); + memcpy(digest_data, data, PAYLOAD_LEN); + memset(digest_data + 5, 0, 4); + + /* Take a copy of the SHA1 context before and after hashing */ + gcry_md_copy (&digest_hd_pre, *(digest_hd)); + gcry_md_write (*(digest_hd), digest_data, PAYLOAD_LEN); + gcry_md_copy (&digest_hd_post, *(digest_hd)); + + + /* If the first four bytes of the calculated digest match the four + in the payload, then all is well. If they don't match, copy the + context prior to hashing back in. */ + current_digest = gcry_md_read(digest_hd_post, 0); + ssl_print_data("Decrypted Payload for Digest", digest_data, PAYLOAD_LEN); + ssl_print_data("Calculated Digest: ", current_digest, 4); + ssl_print_data("Received Digest: ", relay_digest, 4); + if (memcmp(current_digest,relay_digest,4) != 0) { + gcry_md_copy (digest_hd, digest_hd_pre); + ret = 0; + } + + gcry_md_close(digest_hd_pre); + gcry_md_close(digest_hd_post); + g_free(digest_data); + return ret; +} + +/* Parse the contents of a Tor relay cell */ +static gint +dissect_relay_cell(proto_tree *payload_tree, tvbuff_t *tvb, gint offset, guint circid, packet_info *pinfo) +{ + guint8 relay_command; + TorService* service; + cell_key_t *cell_key; + guint8 *decrypted_data = NULL; + StringInfo *data_key = NULL; + StringInfo *stored_data = NULL; + tvbuff_t *tvb_decrypted, *next_tvb; + gint d_offset=0, i; + gboolean found_payload=FALSE; + guint data_length; + dissector_handle_t tmp_handle; + guint8 *first_four; + char relay_digest[4]; + gcry_md_hd_t digest_hd; + + service = g_malloc(sizeof(TorService) + 8); + service->circid = circid; + /*FIXME: get correct direction */ + memcpy(service->source, pinfo->src.data, pinfo->src.len); + + for (service->direction = REVERSE; + service->direction < 2; service->direction++) { + cell_key = g_hash_table_lookup(tor_key_hash, service); + if (cell_key) { + break; + } + } + g_free(service); + + if ((!cell_key)) { + return 0; + } + + /* Get the encrypted payload from the tvb buffer */ + decrypted_data = (guint8 *) g_malloc ((PAYLOAD_LEN) * sizeof(guint8)); + memset(decrypted_data,0,PAYLOAD_LEN); + tvb_memcpy(tvb, decrypted_data , offset, PAYLOAD_LEN); + + /*Prepare the key for looking up the encrypted data */ + data_key = se_alloc0(8 + sizeof(StringInfo)); + data_key->data = ((guchar*)data_key+sizeof(StringInfo)); + memcpy(data_key->data, decrypted_data,8); + data_key->data_len = 8; + + /* Have we already decrypted this payload ? */ + if ((stored_data = g_hash_table_lookup(tor_relay_data_hash, data_key)) == NULL) { + /* A Tor cell is encrypted by each hop in the circuit. We iterate through the + keys we have for this circuit and decrypt each hop's layer until we get + the plaintext. */ + for (i=1; i <= cell_key->no_of_hops; i++) { + cell_key_t *ckey; + if ((ckey = tor_get_cell_key(cell_key,i)) == 0) + break; + ssl_print_data("Encrypted Data", decrypted_data, PAYLOAD_LEN); + ssl_print_data("Cell Key", ckey->key, 16); + tor_decrypt_cell(decrypted_data, ckey); + ssl_print_data("Decrypted Data", decrypted_data, PAYLOAD_LEN); + /* If bytes 2 and 3 of the decrypted cell are zero then it might + be fully decrypted. To be sure, we have to check the digest + at bytes six to ten first. */ + if (!decrypted_data[1] && !decrypted_data[2]) { + /* Use the SHA1 context for this circuit hop when checking the + cell's four-byte digest.*/ + digest_hd = ckey->digest_hd; + memcpy(relay_digest, decrypted_data + 5, 4); + /* If the digest is correct, then this packet is ready for display */ + if (check_relay_digest(&digest_hd, decrypted_data, relay_digest)) { + found_payload=TRUE; + tor_save_decrypted(data_key, decrypted_data); + break; + } + } + } + } else { + memcpy(decrypted_data,stored_data->data, stored_data->data_len); + found_payload=TRUE; + } + + /* If we can't decrypt the payload or match the digest in the decrypted payload + then we have nothing to dissect */ + if (!found_payload) { + return 0; + } + + /* Display the Decrypted Cell */ + ssl_print_data("Found Decrypted Data", decrypted_data, PAYLOAD_LEN); + + tvb_decrypted = tvb_new_real_data(decrypted_data, PAYLOAD_LEN, PAYLOAD_LEN); + tvb_set_child_real_data_tvbuff(tvb, tvb_decrypted); + + add_new_data_source(pinfo, tvb_decrypted, "Decrypted Relay Cell"); + + /* Handler to free the Decrypted Data Buffer. */ + tvb_set_free_cb(tvb_decrypted,g_free); + + /* Dissect the Decrypted Relay Cell */ + relay_command = tvb_get_guint8(tvb_decrypted, d_offset); + proto_tree_add_item(payload_tree, + hf_tor_relay_command, + tvb_decrypted, d_offset, + 1, + FALSE); + d_offset+=1; + + proto_tree_add_item(payload_tree, + hf_tor_recognized, + tvb_decrypted, d_offset, + 2, + FALSE); + d_offset+=2; + + proto_tree_add_item(payload_tree, + hf_tor_streamid, + tvb_decrypted, d_offset, + 2, + FALSE); + d_offset+=2; + + proto_tree_add_item(payload_tree, + hf_tor_relay_digest, + tvb_decrypted, d_offset, + 4, + FALSE); + d_offset+=4; + + data_length=(guint)tvb_get_ntohs(tvb_decrypted, d_offset); + proto_tree_add_item(payload_tree, + hf_tor_relay_data_length, + tvb_decrypted, d_offset, + 2, + FALSE); + d_offset+=2; + + switch(relay_command) { + case RELAY_RESOLVE: + d_offset += dissect_relay_resolve_payload(payload_tree, tvb_decrypted, d_offset); + break; + case RELAY_RESOLVED: + d_offset += dissect_relay_resolved_payload(payload_tree, tvb_decrypted, d_offset); + break; + case RELAY_CONNECTED: + d_offset += dissect_relay_connected_payload(payload_tree, tvb_decrypted, d_offset); + break; + case RELAY_END: + d_offset += dissect_relay_end_payload(payload_tree, tvb_decrypted, d_offset); + /*FIXME: Clean up with gcry_md_close (gcry_md_hd_t h) */ + break; + case RELAY_BEGIN: + d_offset += dissect_relay_resolve_payload(payload_tree, tvb_decrypted, d_offset); + break; + case RELAY_TRUNCATED: + d_offset += dissect_destroy_cell(payload_tree, tvb_decrypted, d_offset); + break; + /*This contains a payload requiring subdissection */ + case RELAY_DATA: + first_four = tvb_get_string(tvb_decrypted, d_offset, 4); + if (!memcmp(first_four,"HTTP",4) || + !memcmp(first_four,"GET",3) || + !memcmp(first_four,"POST",4)) { + tmp_handle = http_handle; + } else { + tmp_handle = data_handle; + } + next_tvb = tvb_new_subset(tvb_decrypted, d_offset, data_length, data_length); + call_dissector(tmp_handle, next_tvb, pinfo, payload_tree); + g_free(first_four); + d_offset+=RELAY_DATA_LEN; + break; + /*This contains a payload requiring subdissection */ + case RELAY_ESTABLISH_INTRO: + d_offset += dissect_relay_establish_intro_payload(payload_tree, + tvb_decrypted, d_offset); + break; + case RELAY_ESTABLISH_RENDEZVOUS: + d_offset += dissect_relay_establish_rendezvous_payload(payload_tree, + tvb_decrypted, + d_offset); + break; + case RELAY_INTRODUCE1: + case RELAY_INTRODUCE2: + d_offset += dissect_relay_introduce_payload(payload_tree, + tvb_decrypted, + d_offset); + break; + case RELAY_RENDEZVOUS1: + d_offset += dissect_relay_rendezvous_payload(payload_tree, + tvb_decrypted, + d_offset, TRUE); + break; + case RELAY_RENDEZVOUS2: + d_offset += dissect_relay_rendezvous_payload(payload_tree, + tvb_decrypted, + d_offset, FALSE); + break; + /* These contain no payload */ + case RELAY_TRUNCATE : + case RELAY_DROP : + case RELAY_BEGIN_DIR: + case RELAY_EXTEND : + case RELAY_EXTENDED : + case RELAY_SENDME : + case RELAY_INTRO_ESTABLISHED: + case RELAY_RENDEZVOUS_ESTABLISHED: + case RELAY_COMMAND_INTRODUCE_ACK: + default: + proto_tree_add_item(payload_tree, + hf_tor_relay_data, + tvb_decrypted, d_offset, + RELAY_DATA_LEN, + FALSE); + d_offset+=RELAY_DATA_LEN; + } + + d_offset+=tor_add_padding(payload_tree, tvb_decrypted, d_offset); + + return d_offset; +} + +static void +update_cell_directions(guint circid, packet_info *pinfo, guint direction) +{ + TorService* service; + cell_key_t *tmp_cell_key; + service = g_malloc(sizeof(TorService) + 8); + service->circid = circid; + service->direction = direction; + memset(service->source,0,4); + tmp_cell_key = g_hash_table_lookup(tor_key_hash, service); + if (tmp_cell_key) { + g_hash_table_remove(tor_key_hash, service); + memcpy(service->source, pinfo->src.data, pinfo->src.len); + g_hash_table_insert(tor_key_hash, service, tmp_cell_key); + } +} + +static gint +dissect_create_cell(proto_tree *payload_tree, tvbuff_t *tvb, gint offset) +{ + gint start=offset; + proto_tree_add_item(payload_tree, + hf_tor_create, + tvb, offset, + 509, + FALSE); + offset+=509; + + return (offset-start); +} + +static gint +dissect_createfast_cell(proto_tree *payload_tree, tvbuff_t *tvb, gint offset) +{ + gint start=offset; + proto_tree_add_item(payload_tree, + hf_tor_key_material_x, + tvb, offset, + HASH_LEN, + FALSE); + offset+=HASH_LEN; + + return (offset-start); +} + +static gint +dissect_createdfast_cell(proto_tree *payload_tree, tvbuff_t *tvb, gint offset) +{ + gint start=offset; + proto_tree_add_item(payload_tree, + hf_tor_key_material_y, + tvb, offset, + HASH_LEN, + FALSE); + offset+=HASH_LEN; + + proto_tree_add_item(payload_tree, + hf_tor_derivative_key_data, + tvb, offset, + HASH_LEN, + FALSE); + offset+=HASH_LEN; + + return (offset-start); +} + +static gint +dissect_created_cell(proto_tree *payload_tree, tvbuff_t *tvb, gint offset) +{ + gint start=offset; + proto_tree_add_item(payload_tree, + hf_tor_dh_data, + tvb, offset, + DH_LEN, + FALSE); + offset+=DH_LEN; + + proto_tree_add_item(payload_tree, + hf_tor_derivative_key_data, + tvb, offset, + HASH_LEN, + FALSE); + offset+=HASH_LEN; + + return (offset-start); +} + +static gint +dissect_netinfo_cell(proto_tree *payload_tree, tvbuff_t *tvb, gint offset) +{ + gint i, no_of_addresses; + gint start=offset; + proto_tree_add_item(payload_tree, + hf_tor_timestamp, + tvb, offset, + 4, + FALSE); + offset+=4; + + offset+=add_tor_address(payload_tree, tvb, offset,TRUE); + + proto_tree_add_item(payload_tree, + hf_tor_number_of_addresses, + tvb, offset, + 1, + FALSE); + no_of_addresses=tvb_get_guint8(tvb, offset); + offset+=1; + + for (i=0; i < no_of_addresses; i++) { + offset+=add_tor_address(payload_tree, tvb, offset,FALSE); + } + return (offset-start); +} + +static gint +dissect_versions_cell(proto_tree *payload_tree, tvbuff_t *tvb, gint offset, + gint tor_length) +{ + gint i; + proto_tree_add_string_format(payload_tree, + hf_tor_length, + tvb, offset, + 2, + "Length:", "Length: %s", + tvb_bytes_to_str(tvb, offset, 2)); + offset+=2; + for (i=0; icinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "Tor"); + + if (tree) { + + ti = proto_tree_add_item(tree, proto_tor, tvb, offset, -1, + FALSE); + tor_tree = proto_item_add_subtree(ti, ett_tor); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + while (tvb_offset_exists(tvb, offset)) { + + /*Inspect the packet. if we don't have enough data to dissect it, + request more. */ + tor_circid=(guint)tvb_get_ntohs(tvb, offset); + tor_cmd = tvb_get_guint8(tvb, offset+2); + switch(tor_cmd) { + case NETINFO: + /* TODO: need to calculate length based on no of addresses here */ + required=CELL_LEN; + break; + case VERSIONS: + tor_length=(guint)tvb_get_ntohs(tvb, offset+3); + required=tor_length + 4; + break; + default: + required=CELL_LEN; + } + + available = tvb_reported_length_remaining(tvb, offset); + if (available < required) { + pinfo->desegment_offset = offset; + pinfo->desegment_len = (required-available); + return; + } + + /* + * Put the line into the protocol tree. + */ + + val_str = match_strval(tor_cmd, vals_circuit_commands); + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "Tor Cell: %s", val_str); + } + + if (!tor_cmd) { + ti=proto_tree_add_item(tor_tree, + hf_tor_padding, + tvb, offset, + (tvb_length_remaining(tvb,offset) > CELL_LEN)? + CELL_LEN:tvb_length_remaining(tvb,offset), + FALSE); + offset+=(tvb_length_remaining(tvb,offset) > CELL_LEN)? + CELL_LEN:tvb_length_remaining(tvb,offset); + continue; + } + + /* + * Add the CircID field + */ + ti=proto_tree_add_item(tor_tree, + hf_tor_circid, + tvb, offset, + 2, + FALSE); + offset+=2; + + /* + * Add the Command field + */ + proto_tree_add_item(tor_tree, + hf_tor_command, + tvb, offset, + 1, + FALSE); + offset+=1; + /* + * Add the Payload + */ + ti = proto_tree_add_item(tor_tree, + hf_tor_payload, + tvb, offset, + 0, + FALSE); + payload_tree = proto_item_add_subtree(ti, ett_tor); + + switch(tor_cmd) { + case VERSIONS: + offset+=dissect_versions_cell(payload_tree, tvb, offset, tor_length); + break; + case NETINFO: + offset+=dissect_netinfo_cell(payload_tree, tvb, offset); + break; + case CREATED: + update_cell_directions(tor_circid, pinfo, REVERSE); + offset+=dissect_created_cell(payload_tree, tvb, offset); + break; + case CREATED_FAST: + update_cell_directions(tor_circid, pinfo, REVERSE); + offset+=dissect_createdfast_cell(payload_tree, tvb, offset); + break; + case CREATE_FAST: + update_cell_directions(tor_circid, pinfo, FORWARD); + offset+=dissect_createfast_cell(payload_tree, tvb, offset); + break; + case CREATE: + update_cell_directions(tor_circid, pinfo, FORWARD); + offset+=dissect_create_cell(payload_tree, tvb, offset); + break; + case DESTROY: + offset+=dissect_destroy_cell(payload_tree, tvb, offset); + break; + case RELAY_EARLY: + case RELAY: + used = dissect_relay_cell(payload_tree, tvb, offset, tor_circid, pinfo); + if (!used) { + proto_tree_add_item(payload_tree, + hf_tor_encrypted_cell, + tvb, offset, + PAYLOAD_LEN, + FALSE); + } + offset+=PAYLOAD_LEN; + break; + default: + break; + } + + offset+=tor_add_padding(tor_tree, tvb, offset); + + } + + } +} + +void +proto_register_tor(void) +{ + static hf_register_info hf[] = { + { &hf_tor_circid, + { "CircID", "tor.circid", + FT_UINT16, BASE_DEC, NULL, 0x0, + "CircID", HFILL }}, + { &hf_tor_command, + { "Command", "tor.command", + FT_UINT8, BASE_HEX, VALS(vals_circuit_commands), 0x0, + "Command", HFILL }}, + { &hf_tor_length, + { "Length", "tor.length", + FT_STRING, BASE_NONE, NULL, 0x0, + "Length", HFILL }}, + { &hf_tor_payload, + { "Payload", "tor.payload", + FT_STRING, BASE_NONE, NULL, 0x0, + "Payload", HFILL }}, + { &hf_tor_timestamp, + { "Timestamp", "tor.timestamp", + FT_UINT64, BASE_DEC, NULL, 0x0, + "Timestamp", HFILL }}, + { &hf_tor_other_or_address, + { "Other OR's Address", "tor.otheroraddress", + FT_IPv4, BASE_NONE, NULL, 0x0, + "Other OR's Address", HFILL }}, + { &hf_tor_other_or_address_ipv6, + { "Other OR's Address IPv6", "tor.otheroraddress_ipv6", + FT_IPv6, BASE_NONE, NULL, 0x0, + "Other OR's Address IPv6", HFILL }}, + { &hf_tor_number_of_addresses, + { "Number of Addresses", "tor.numberofaddresses", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Number of Addresses", HFILL }}, + { &hf_tor_this_or_address, + { "This OR's Address", "tor.thisoraddress", + FT_IPv4, BASE_NONE, NULL, 0x0, + "This OR's Address", HFILL }}, + { &hf_tor_this_or_address_ipv6, + { "This OR's Address IPv6", "tor.thisoraddress_ipv6", + FT_IPv6, BASE_NONE, NULL, 0x0, + "This OR's Address IPv6", HFILL }}, + { &hf_tor_ttl, + { "TTL", "tor.ttl", + FT_UINT64, BASE_DEC, NULL, 0x0, + "TTL", HFILL }}, + { &hf_tor_address_type, + { "Address Type", "tor.addresstype", + FT_UINT8, BASE_HEX, VALS(vals_address_types), 0x0, + "Address Type", HFILL }}, + { &hf_tor_address_length, + { "Address Length", "tor.addresslength", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Address Length", HFILL }}, + { &hf_tor_hostname, + { "Hostname", "tor.hostname", + FT_STRING, BASE_NONE, NULL, 0x0, + "Hostname", HFILL }}, + { &hf_tor_key_material_y, + { "Key Material (Y)", "tor.keymaterialy", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Key Material (Y)", HFILL }}, + { &hf_tor_key_material_x, + { "Key Material (X)", "tor.keymaterialx", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Key Material (X)", HFILL }}, + { &hf_tor_dh_data, + { "Diffie Hellman data", "tor.dhdata", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Diffie Hellman data", HFILL }}, + { &hf_tor_derivative_key_data, + { "Derivative key data", "tor.derivativekeydata", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Derivative key data", HFILL }}, + { &hf_tor_create, + { "Create Payload", "tor.create", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Create Payload", HFILL }}, + { &hf_tor_destroy_reason, + { "Destroy Reason", "tor.destroyreason", + FT_UINT8, BASE_HEX, VALS(vals_destroy_reason), 0x0, + "Destroy Reason", HFILL }}, + { &hf_tor_relay_command, + { "Relay Command", "tor.relay_command", + FT_UINT8, BASE_HEX, VALS(vals_relay_commands), 0x0, + "Relay Command", HFILL }}, + { &hf_tor_recognized, + { "Recognized", "tor.recognized", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Recognized", HFILL }}, + { &hf_tor_streamid, + { "Stream ID", "tor.streamid", + FT_UINT16, BASE_DEC, NULL, 0x0, + "Stream ID", HFILL }}, + { &hf_tor_relay_digest, + { "Digest", "tor.relay_digest", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Digest", HFILL }}, + { &hf_tor_relay_data_length, + { "Relay Data Length", "tor.relay_data_length", + FT_UINT16, BASE_DEC, NULL, 0x0, + "Relay Data Length", HFILL }}, + { &hf_tor_relay_data, + { "Relay Data", "tor.relay_data", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Relay Data", HFILL }}, + { &hf_tor_encrypted_cell, + { "Encrypted Cell", "tor.encrypted_cell", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Encrypted Cell", HFILL }}, + { &hf_tor_padding, + { "Padding", "tor.padding", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Padding", HFILL }}, + { &hf_tor_relay_connected_ip, + { "Relay Connected IP", "tor.relay_connected_ip", + FT_IPv4, BASE_NONE, NULL, 0x0, + "Relay Connected IP", HFILL }}, + { &hf_tor_relay_connected_ttl, + { "Relay Connected TTL", "tor.relay_connected_ttl", + FT_UINT32, BASE_DEC, NULL, 0x0, + "Relay Connected TTL", HFILL }}, + { &hf_tor_relay_end_reason, + { "Relay End Reason", "tor.relay_end_reason", + FT_UINT8, BASE_HEX, VALS(vals_relay_end_reason), 0x0, + "Relay End Reason", HFILL }}, + { &hf_tor_relay_end_ip, + { "Relay End IP", "tor.relay_end_ip", + FT_IPv4, BASE_NONE, NULL, 0x0, + "Relay End IP", HFILL }}, + { &hf_tor_relay_end_ipv6, + { "Relay End IPv6", "tor.relay_end_ipv6", + FT_IPv6, BASE_NONE, NULL, 0x0, + "Relay End IPv6", HFILL }}, + { &hf_tor_relay_end_ttl, + { "Relay End TTL", "tor.relay_end_ttl", + FT_UINT32, BASE_DEC, NULL, 0x0, + "Relay End TTL", HFILL }}, + { &hf_tor_resolved_status, + { "Resolved Status", "tor.resolved_status", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Resolved Status", HFILL }}, + { &hf_tor_resolve_address, + { "Resolve Address", "tor.resolve_address", + FT_STRING, BASE_NONE, NULL, 0x0, + "Resolved Address", HFILL }}, + { &hf_tor_resolve_port, + { "Resolve Port", "tor.resolve_port", + FT_UINT16, BASE_DEC, NULL, 0x0, + "Resolved Port", HFILL }}, + { &hf_tor_establish_intro_kl, + { "Establish Intro Key Length", "tor.establish_intro_kl", + FT_UINT16, BASE_DEC, NULL, 0x0, + "Establish Intro Key Length", HFILL }}, + { &hf_tor_establish_intro_pk, + { "Establish Intro Public Key", "tor.establish_intro_pk", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Establish Intro Public Key", HFILL }}, + { &hf_tor_establish_intro_hs, + { "Establish Intro Session Hash", "tor.establish_intro_hs", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Establish Intro Session Hash", HFILL }}, + { &hf_tor_establish_intro_sig, + { "Establish Intro Signature", "tor.establish_intro_sig", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Establish Intro Signature", HFILL }}, + { &hf_tor_establish_rendezvous_cookie, + { "Establish Rendezvous Cookie", "tor.establish_rendezvous_cookie", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Establish Rendezvous Cookie", HFILL }}, + { &hf_tor_introduce_pk_id, + { "Introduce PK ID", "tor.introduce_pk_id", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Introduce PK ID", HFILL }}, + { &hf_tor_introduce_encrypted_payload, + { "Introduce Encrypted Payload", "tor.introduce_encrypted_payload", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Introduce Encrypted Payload", HFILL }}, + { &hf_tor_rendezvous_cookie, + { "Rendezvous Cookie", "tor.rendezvous_cookie", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Rendezvous Cookie", HFILL }}, + { &hf_tor_rendezvous_dh, + { "Rendezvous Diffie Hellman", "tor.rendezvous_dh", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Rendezvous Diffie Hellman", HFILL }}, + { &hf_tor_rendezvous_hd, + { "Rendezvous Handshake Digest", "tor.rendezvous_hd", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Rendezvous Handshake Digest", HFILL }}, + }; + + static gint *ett[] = { + &ett_tor + }; + module_t *tor_module; + + + proto_tor = proto_register_protocol("Tor Protocol", "Tor", "tor"); + register_dissector("tor", dissect_tor, proto_tor); + proto_register_field_array(proto_tor, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + + /* Preferences */ + tor_module = prefs_register_protocol(proto_tor, proto_reg_handoff_tor); + + prefs_register_string_preference(tor_module, "cell_keys_list", "Tor cell keys list", + "Semicolon-separated list of AES keys used for Tor cell decryption; " + "each list entry must be in the form of ,,. " + " is the key used to encrypt cells", + (const gchar **)&cell_keys_list); +} + +void +proto_reg_handoff_tor(void) +{ + dissector_handle_t tor_handle; + + tor_handle = find_dissector("tor"); + ssl_dissector_add(TCP_PORT_SSL_TOR, "tor", TRUE); + data_handle = find_dissector("data"); + http_handle = find_dissector("http"); + tor_parse(); + +}