Appendix C - Wireshark Code Sample
Appendix C - Wireshark Code Sample
and Security
Appendix C:
Wireshark Code
Wireshark University
Note: This is only a portion of the code. See ssl-c.txt in the \misc directory on the
Student Supplement DVD.
/* packet-ssl.c
* Routines for ssl dissection
* Copyright (c) 2000-2001, Scott Renfro <scott@renfro.org>
*
* $Id$
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* 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.
*
References
• See http://www.netscape.com/eng/security/SSL_2.html for SSL 2.0 specs.
• See http://www.netscape.com/eng/ssl3/for SSL 3.0 specs.
• See RFC 2246 for SSL 3.1/TLS 1.0 specs.
• See (among other places) http://www.graphcomp.com/info/specs/ms/pct.htm for PCT
1 draft specs.
• See http://research.sun.com/projects/crypto/draft-ietf-tls-ecc-05.txt for Elliptic
Curve Cryptography cipher suites.
• See http://www.ietf.org/internet-drafts/draft-ietf-tls-camellia-04.txt for
Camellia-based cipher suites.
[Portion of code removed by course developer; see ssl-c.txt in the \misc directory of the Student
Supplement DVD.]
/*********************************************************************
*
* Main dissector
*
*********************************************************************/
/*
* Code to actually dissect the packets
*/
static void
dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
conversation_t *conversation;
void *conv_data;
proto_item *ti;
proto_tree *ssl_tree;
guint32 offset;
gboolean first_record_in_frame;
gboolean need_desegmentation;
SslDecryptSession* ssl_session;
guint* conv_version;
ti = NULL;
ssl_tree = NULL;
offset = 0;
first_record_in_frame = TRUE;
ssl_session = NULL;
if (!conversation)
{
/* create a new conversation */
conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo-
>ptype,
pinfo->srcport, pinfo->destport, 0);
}
conv_data = conversation_get_proto_data(conversation, proto_ssl);
ssl_session = se_alloc0(sizeof(SslDecryptSession));
ssl_session_init(ssl_session);
ssl_session->version = SSL_VER_UNKNOWN;
conversation_add_proto_data(conversation, proto_ssl, ssl_session);
/* try to retrive 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
* and thus decryption never engaged*/
ssl_session->private_key = g_hash_table_lookup(ssl_key_hash, &dummy);
if (!ssl_session->private_key)
ssl_debug_printf("dissect_ssl can't find private key for this server!\n");
}
conv_version= & ssl_session->version;
/*
* Assume, for now, that this doesn't need desegmentation.
*/
need_desegmentation = FALSE;
case SSL_VER_SSLv3:
case SSL_VER_TLS:
/* the version tracking code works too well ;-)
* at times, we may visit a v2 client hello after
* we already know the version of the connection;
* work around that here by detecting and calling
* the v2 dissector instead
*/
if (ssl_is_v2_client_hello(tvb, offset))
{
offset = dissect_ssl2_record(tvb, pinfo, ssl_tree,
offset, conv_version,
&need_desegmentation,
ssl_session);
}
else
{
offset = dissect_ssl3_record(tvb, pinfo, ssl_tree,
offset, conv_version,
&need_desegmentation,
ssl_session,
first_record_in_frame);
}
break;
offset = tvb_length(tvb);
if (check_col(pinfo->cinfo, COL_INFO))
col_append_str(pinfo->cinfo, COL_INFO,
"Continuation Data");
static gint
decrypt_ssl3_record(tvbuff_t *tvb, packet_info *pinfo, guint32 offset,
guint32 record_length, guint8 content_type, SslDecryptSession* ssl,
gboolean save_plaintext)
{
gint ret;
gint direction;
StringInfo* data_for_iv;
gint data_for_iv_len;
SslDecoder* decoder;
ret = 0;
/* if we can decrypt and decryption have success
* add decrypted data to this packet info*/
ssl_debug_printf("decrypt_ssl3_record: app_data len %d ssl, state 0x%02X\n",
record_length, ssl->state);
direction = ssl_packet_from_server(ssl_associations, pinfo->srcport, pinfo->ptype ==
PT_TCP);
if (!(ssl->state & SSL_HAVE_SESSION_KEY)) {
ssl_debug_printf("decrypt_ssl3_record: no session key\n");
/* save data to update IV if session key is obtained later */
data_for_iv = (direction != 0) ? &ssl->server_data_for_iv : &ssl-
>client_data_for_iv;
data_for_iv_len = (record_length < 24) ? record_length : 24;
ssl_data_set(data_for_iv, (guchar*)tvb_get_ptr(tvb, offset + record_length -
data_for_iv_len, data_for_iv_len), data_for_iv_len);
return ret;
}
record_length + 32);
ssl_decrypted_data.data_len = record_length + 32;
}
void
dissect_ssl_payload(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree,
SslAssociation* association)
{
gboolean save_fragmented;
SslDataInfo *appl_data;
proto_tree *ti;
tvbuff_t *next_tvb;
}
}
/*********************************************************************
*
/*
* struct {
* uint8 major, minor;
* } ProtocolVersion;
*
*
* enum {
* change_cipher_spec(20), alert(21), handshake(22),
* application_data(23), (255)
* } ContentType;
*
* struct {
* ContentType type;
* ProtocolVersion version;
* uint16 length;
* opaque fragment[TLSPlaintext.length];
* } TLSPlaintext;
*/
guint32 record_length;
guint16 version;
guint8 content_type;
guint8 next_byte;
proto_tree *ti;
proto_tree *ssl_record_tree;
SslAssociation* association;
guint32 available_bytes;
ti = NULL;
ssl_record_tree = NULL;
available_bytes = 0;
/* TLS 1.0/1.1 just ignores unknown records - RFC 2246 chapter 6. The TLS Record
Protocol */
if ((*conv_version==SSL_VER_TLS || *conv_version==SSL_VER_TLSv1DOT1) &&
(available_bytes >=1 ) && !ssl_is_valid_content_type(tvb_get_guint8(tvb,
offset))) {
proto_tree_add_text(tree, tvb, offset, available_bytes, "Ignored Unknown Record");
if (check_col(pinfo->cinfo, COL_INFO))
col_append_str(pinfo->cinfo, COL_INFO, "Ignored Unknown Record");
if (check_col(pinfo->cinfo, COL_PROTOCOL))
col_set_str(pinfo->cinfo, COL_PROTOCOL, ssl_version_short_names[*conv_version]);
return offset + available_bytes;
}
/*
* Can we do reassembly?
*/
if (ssl_desegment && pinfo->can_desegment) {
/*
* Yes - is the record header split across segment boundaries?
*/
if (available_bytes < 5) {
WSU04: Network Forensics and Security – Appendix C Page C-9
© 2007 Protocol Analysis Institute, Inc.
Wireshark University
/*
* Yes. Tell the TCP dissector where the data for this
* message starts in the data it handed us, and how many
* more bytes we need, and return.
*/
pinfo->desegment_offset = offset;
pinfo->desegment_len = 5 - available_bytes;
*need_desegmentation = TRUE;
return offset;
}
}
/*
* Get the record layer fields of interest
*/
content_type = tvb_get_guint8(tvb, offset);
version = tvb_get_ntohs(tvb, offset + 1);
record_length = tvb_get_ntohs(tvb, offset + 3);
if (ssl_is_valid_content_type(content_type)) {
/*
* Can we do reassembly?
*/
if (ssl_desegment && pinfo->can_desegment) {
/*
* Yes - is the record split across segment boundaries?
*/
if (available_bytes < record_length + 5) {
/*
* Yes. Tell the TCP dissector where the data for this
* message starts in the data it handed us, and how many
* more bytes we need, and return.
*/
pinfo->desegment_offset = offset;
pinfo->desegment_len = (record_length + 5) - available_bytes;
*need_desegmentation = TRUE;
return offset;
}
}
} else {
/*
* If GUI, fill in record layer part of tree
*/
if (tree)
{
/*
* if we don't already have a version set for this conversation,
* but this message's version is authoritative (i.e., it's
* not client_hello, then save the version to to conversation
* structure and print the column version
*/
next_byte = tvb_get_guint8(tvb, offset);
if (*conv_version == SSL_VER_UNKNOWN
&& ssl_is_authoritative_version_message(content_type, next_byte))
{
if (version == SSLV3_VERSION)
{
*conv_version = SSL_VER_SSLv3;
if (ssl) {
ssl->version_netorder = version;
ssl->state |= SSL_VERSION;
ssl_debug_printf("dissect_ssl3_record found version 0x%04X -> state
0x%02X\n", ssl->version_netorder, ssl->state);
}
/*ssl_set_conv_version(pinfo, ssl->version);*/
}
else if (version == TLSV1_VERSION)
{
*conv_version = SSL_VER_TLS;
if (ssl) {
ssl->version_netorder = version;
ssl->state |= SSL_VERSION;
ssl_debug_printf("dissect_ssl3_record found version 0x%04X -> state
0x%02X\n", ssl->version_netorder, ssl->state);
}
/*ssl_set_conv_version(pinfo, ssl->version);*/
}
else if (version == TLSV1DOT1_VERSION)
{
*conv_version = SSL_VER_TLSv1DOT1;
if (ssl) {
ssl->version_netorder = version;
ssl->state |= SSL_VERSION;
ssl_debug_printf("dissect_ssl3_record found version 0x%04X -> state
0x%02X\n", ssl->version_netorder, ssl->state);
}
/*ssl_set_conv_version(pinfo, ssl->version);*/
}
}
if (check_col(pinfo->cinfo, COL_PROTOCOL))
{
col_set_str(pinfo->cinfo, COL_PROTOCOL,
ssl_version_short_names[*conv_version]);
}
/*
* now dissect the next layer
*/
ssl_debug_printf("dissect_ssl3_record: content_type %d\n",content_type);
/* PAOLO try to decrypt each record (we must keep ciphers "in sync")
* store plain text only for app data */
switch (content_type) {
case SSL_ID_CHG_CIPHER_SPEC:
if (check_col(pinfo->cinfo, COL_INFO))
col_append_str(pinfo->cinfo, COL_INFO, "Change Cipher Spec");
dissect_ssl3_change_cipher_spec(tvb, ssl_record_tree,
offset, conv_version, content_type);
ssl_debug_printf("dissect_ssl3_change_cipher_spec\n");
break;
case SSL_ID_ALERT:
{
tvbuff_t* decrypted;
decrypted=0;
if (ssl&&decrypt_ssl3_record(tvb, pinfo, offset,
record_length, content_type, ssl, FALSE))
ssl_add_record_info(proto_ssl, pinfo, ssl_decrypted_data.data,
ssl_decrypted_data_avail, offset);
proto_item_set_text(ssl_record_tree,
"%s Record Layer: %s Protocol: %s",
ssl_version_short_names[*conv_version],
val_to_str(content_type, ssl_31_content_type, "unknown"),
association?association->info:"Application Data");
break;
default:
/* shouldn't get here since we check above for valid types */
if (check_col(pinfo->cinfo, COL_INFO))
col_append_str(pinfo->cinfo, COL_INFO, "Bad SSLv3 Content Type");
break;
}
offset += record_length; /* skip to end of record */
return offset;
}
* AlertLevel level;
* AlertDescription description;
* } Alert;
*/
proto_tree *ti;
proto_tree *ssl_alert_tree;
const gchar *level;
const gchar *desc;
guint8 byte;
ssl_alert_tree = NULL;
if (tree)
{
ti = proto_tree_add_item(tree, hf_ssl_alert_message, tvb,
offset, 2, 0);
ssl_alert_tree = proto_item_add_subtree(ti, ett_ssl_alert);
}
/*
* set the record layer label
*/
/* first lookup the names for the alert level and description */
byte = tvb_get_guint8(tvb, offset); /* grab the level byte */
level = match_strval(byte, ssl_31_alert_level);
if (tree)
{
if (level && desc)
{
proto_item_set_text(tree, "%s Record Layer: Alert "
"(Level: %s, Description: %s)",
ssl_version_short_names[*conv_version],
level, desc);
proto_tree_add_item(ssl_alert_tree, hf_ssl_alert_message_level,
tvb, offset++, 1, FALSE);
proto_tree_add_item(ssl_alert_tree, hf_ssl_alert_message_description,
tvb, offset++, 1, FALSE);
}
else
{
proto_item_set_text(tree,
"%s Record Layer: Encrypted Alert",
ssl_version_short_names[*conv_version]);
proto_item_set_text(ssl_alert_tree,
"Alert Message: Encrypted Alert");
}
}
}
/*
* Update our info string
*/
if (check_col(pinfo->cinfo, COL_INFO))
if (tree)
{
/* set the label text on the record layer expanding node */
if (first_iteration)
{
proto_item_set_text(tree, "%s Record Layer: %s Protocol: %s",
ssl_version_short_names[*conv_version],
val_to_str(content_type, ssl_31_content_type,
"unknown"),
(msg_type_str!=NULL) ? msg_type_str :
"Encrypted Handshake Message");
}
else
{
proto_item_set_text(tree, "%s Record Layer: %s Protocol: %s",
ssl_version_short_names[*conv_version],
val_to_str(content_type, ssl_31_content_type,
"unknown"),
"Multiple Handshake Messages");
}
if (ssl_hand_tree)
{
/* set the text label on the subtree node */
proto_item_set_text(ssl_hand_tree, "Handshake Protocol: %s",
(msg_type_str != NULL) ? msg_type_str :
"Encrypted Handshake Message");
}
}
/* PAOLO: if we are doing ssl decryption we must dissect some requests type */
if (ssl_hand_tree || ssl)
{
/* add nodes for the message type and message length */
if (ssl_hand_tree)
proto_tree_add_item(ssl_hand_tree, hf_ssl_handshake_type,
tvb, offset, 1, msg_type);
offset++;
if (ssl_hand_tree)
proto_tree_add_uint(ssl_hand_tree, hf_ssl_handshake_length,
tvb, offset, 3, length);
offset += 3;
case SSL_HND_CLIENT_HELLO:
dissect_ssl3_hnd_cli_hello(tvb, ssl_hand_tree, offset, length, ssl);
break;
case SSL_HND_SERVER_HELLO:
dissect_ssl3_hnd_srv_hello(tvb, ssl_hand_tree, offset, length, ssl);
break;
case SSL_HND_CERTIFICATE:
dissect_ssl3_hnd_cert(tvb, ssl_hand_tree, offset, pinfo);
break;
case SSL_HND_SERVER_KEY_EXCHG:
/* unimplemented */
break;
case SSL_HND_CERT_REQUEST:
dissect_ssl3_hnd_cert_req(tvb, ssl_hand_tree, offset);
break;
case SSL_HND_SVR_HELLO_DONE:
/* server_hello_done has no fields, so nothing to do! */
break;
case SSL_HND_CERT_VERIFY:
/* unimplemented */
break;
case SSL_HND_CLIENT_KEY_EXCHG:
{
/* PAOLO: here we can have all the data to build session key*/
StringInfo encrypted_pre_master;
gint ret;
guint encrlen, skip;
encrlen = length;
skip = 0;
if (!ssl)
break;
(SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION));
break;
}
if (!ssl->private_key) {
ssl_debug_printf("dissect_ssl3_handshake can't find private
key\n");
break;
}
case SSL_HND_FINISHED:
dissect_ssl3_hnd_finished(tvb, ssl_hand_tree,
offset, conv_version);
break;
}
}
else
offset += 4; /* skip the handshake header when handshake is not
processed*/
offset += length;
first_iteration = FALSE; /* set up for next pass, if any */
}
}
static gint
dissect_ssl3_hnd_hello_common(tvbuff_t *tvb, proto_tree *tree,
guint32 offset, SslDecryptSession* ssl, gint from_server)
{
/* show the client's random challenge */
nstime_t gmt_unix_time;
guint8 session_id_length;
proto_item *ti_rnd;
proto_tree *ssl_rnd_tree;
session_id_length = 0;
if (ssl)
{
/* PAOLO: get proper peer information*/
StringInfo* rnd;
if (from_server)
rnd = &ssl->server_random;
else
rnd = &ssl->client_random;
if (tree)
{
ti_rnd = proto_tree_add_text(tree, tvb, offset, 32, "Random");
ssl_rnd_tree = proto_item_add_subtree(ti_rnd, ett_ssl_random);
/* XXXX */
return session_id_length+33;
}
static gint
dissect_ssl3_hnd_hello_ext(tvbuff_t *tvb,
proto_tree *tree, guint32 offset, guint32 left)
{
guint16 extension_length;
guint16 ext_type;
guint16 ext_len;
proto_item *pi;
proto_tree *ext_tree;
if (left < 2)
return offset;
proto_tree_add_uint(ext_tree, hf_ssl_handshake_extension_type,
tvb, offset, 2, ext_type);
offset += 2;
proto_tree_add_uint(ext_tree, hf_ssl_handshake_extension_len,
tvb, offset, 2, ext_len);
offset += 2;
proto_tree_add_bytes_format(ext_tree, hf_ssl_handshake_extension_data,
tvb, offset, ext_len,
tvb_get_ptr(tvb, offset, ext_len),
"Data (%u byte%s)",
ext_len, plurality(ext_len, "", "s"));
offset += ext_len;
left -= 2 + 2 + ext_len;
}
return offset;
}
static void
dissect_ssl3_hnd_cli_hello(tvbuff_t *tvb,
proto_tree *tree, guint32 offset, guint32 length,
SslDecryptSession*ssl)
{
/* struct {
* ProtocolVersion client_version;
* Random random;
* SessionID session_id;
* CipherSuite cipher_suites<2..2^16-1>;
* CompressionMethod compression_methods<1..2^8-1>;
* Extension client_hello_extension_list<0..2^16-1>;
* } ClientHello;
*
*/
proto_tree *ti;
proto_tree *cs_tree;
guint16 cipher_suite_length;
guint8 compression_methods_length;
guint8 compression_method;
guint16 start_offset;
cipher_suite_length = 0;
compression_methods_length = 0;
start_offset = offset;
if (tree || ssl)
{
/* show the client version */
if (tree)
proto_tree_add_item(tree, hf_ssl_handshake_client_version, tvb,
offset, 2, FALSE);
offset += 2;
if (cipher_suite_length > 0)
{
tvb_ensure_bytes_exist(tvb, offset, cipher_suite_length);
ti = proto_tree_add_none_format(tree,
hf_ssl_handshake_cipher_suites,
tvb, offset, cipher_suite_length,
"Cipher Suites (%u suite%s)",
cipher_suite_length / 2,
plurality(cipher_suite_length/2, "", "s"));
if (compression_methods_length > 0)
{
tvb_ensure_bytes_exist(tvb, offset, compression_methods_length);
ti = proto_tree_add_none_format(tree,
hf_ssl_handshake_comp_methods,
tvb, offset, compression_methods_length,
"Compression Methods (%u method%s)",
compression_methods_length,
plurality(compression_methods_length,
"", "s"));
static void
dissect_ssl3_hnd_srv_hello(tvbuff_t *tvb,
proto_tree *tree, guint32 offset, guint32 length,
SslDecryptSession* ssl)
{
/* struct {
* ProtocolVersion server_version;
* Random random;
* SessionID session_id;
* CipherSuite cipher_suite;
* CompressionMethod compression_method;
* Extension server_hello_extension_list<0..2^16-1>;
* } ServerHello;
*/
guint16 start_offset;
start_offset = offset;
if (tree || ssl)
{
/* show the server version */
if (tree)
proto_tree_add_item(tree, hf_ssl_handshake_server_version, tvb,
offset, 2, FALSE);
offset += 2;
ssl->state |= SSL_CIPHER;
ssl_debug_printf("dissect_ssl3_hnd_srv_hello found CIPHER 0x%04X -> state
0x%02X\n",
ssl->cipher, ssl->state);
(SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION|SSL_MASTER_SECRET)) !=
(SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION|SSL_MASTER_SECRET)) {
ssl_debug_printf("dissect_ssl3_hnd_srv_hello not enough data to generate
key (required 0x%02X)\n",
(SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION|SSL_MASTER_SECRET));
goto no_cipher;
}
static void
dissect_ssl3_hnd_cert(tvbuff_t *tvb,
proto_tree *tree, guint32 offset, packet_info *pinfo)
{
/* opaque ASN.1Cert<2^24-1>;
*
* struct {
* ASN.1Cert certificate_list<1..2^24-1>;
* } Certificate;
*/
guint32 certificate_list_length;
proto_tree *ti;
proto_tree *subtree;
if (tree)
{
certificate_list_length = tvb_get_ntoh24(tvb, offset);
proto_tree_add_uint(tree, hf_ssl_handshake_certificates_len,
tvb, offset, 3, certificate_list_length);
offset += 3; /* 24-bit length value */
if (certificate_list_length > 0)
{
tvb_ensure_bytes_exist(tvb, offset, certificate_list_length);
ti = proto_tree_add_none_format(tree,
hf_ssl_handshake_certificates,
tvb, offset, certificate_list_length,
"Certificates (%u byte%s)",
certificate_list_length,
plurality(certificate_list_length,
"", "s"));
/* make it a subtree */
subtree = proto_item_add_subtree(ti, ett_ssl_certs);
if (!subtree)
{
subtree = tree; /* failsafe */
}
{
/* get the length of the current certificate */
guint32 cert_length;
cert_length = tvb_get_ntoh24(tvb, offset);
certificate_list_length -= 3 + cert_length;
proto_tree_add_item(subtree, hf_ssl_handshake_certificate_len,
tvb, offset, 3, FALSE);
offset += 3;
}
}
static void
dissect_ssl3_hnd_cert_req(tvbuff_t *tvb,
proto_tree *tree, guint32 offset)
{
/*
* enum {
* rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4),
* (255)
* } ClientCertificateType;
*
* opaque DistinguishedName<1..2^16-1>;
*
* struct {
* ClientCertificateType certificate_types<1..2^8-1>;
* DistinguishedName certificate_authorities<3..2^16-1>;
* } CertificateRequest;
*
*/
proto_tree *ti;
proto_tree *subtree;
guint8 cert_types_count;
gint dnames_length;
cert_types_count = 0;
dnames_length = 0;
if (tree)
{
cert_types_count = tvb_get_guint8(tvb, offset);
proto_tree_add_uint(tree, hf_ssl_handshake_cert_types_count,
tvb, offset, 1, cert_types_count);
offset++;
if (cert_types_count > 0)
{
ti = proto_tree_add_none_format(tree,
hf_ssl_handshake_cert_types,
tvb, offset, cert_types_count,
"Certificate types (%u type%s)",
cert_types_count,
plurality(cert_types_count, "", "s"));
subtree = proto_item_add_subtree(ti, ett_ssl_cert_types);
if (!subtree)
{
subtree = tree;
}
}
}
if (dnames_length > 0)
{
tvb_ensure_bytes_exist(tvb, offset, dnames_length);
ti = proto_tree_add_none_format(tree,
hf_ssl_handshake_dnames,
tvb, offset, dnames_length,
"Distinguished Names (%d byte%s)",
dnames_length,
plurality(dnames_length, "", "s"));
subtree = proto_item_add_subtree(ti, ett_ssl_dnames);
if (!subtree)
{
subtree = tree;
}
proto_tree_add_item(subtree, hf_ssl_handshake_dname_len,
tvb, offset, 2, FALSE);
offset += 2;
static void
dissect_ssl3_hnd_finished(tvbuff_t *tvb,
proto_tree *tree, guint32 offset,
guint* conv_version)
{
/* For TLS:
* struct {
* opaque verify_data[12];
* } Finished;
*
* For SSLv3:
* struct {
* opaque md5_hash[16];
* opaque sha_hash[20];
* } Finished;
*/
switch(*conv_version) {
case SSL_VER_TLS:
case SSL_VER_TLSv1DOT1:
proto_tree_add_item(tree, hf_ssl_handshake_finished,
tvb, offset, 12, FALSE);
break;
case SSL_VER_SSLv3:
proto_tree_add_item(tree, hf_ssl_handshake_md5_hash,
tvb, offset, 16, FALSE);
offset += 16;
proto_tree_add_item(tree, hf_ssl_handshake_sha_hash,
tvb, offset, 20, FALSE);
offset += 20;
break;
}
}
Note: This is only a portion of the code. The entire code can be found in the \misc
directory on the Student Supplement DVD.