TLS: add Signed Certificate Timestamp support (RFC 6962)

Adds support for dissecting the Signed Certificate Timestamp List
in the TLS Hello, X.509v3 Certificate and OCSP Response extensions.

Tested with tls-sct.pcap (TLS extension, OCSP) and x509-sct.pcap (cert).

Bug: 13372
Change-Id: I127dbf5cfe9a8dd9ed13741322273c4841b0f582
Reviewed-on: https://code.wireshark.org/review/20110
Petri-Dish: Peter Wu <peter@lekensteyn.nl>
Reviewed-by: Peter Wu <peter@lekensteyn.nl>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
Peter Wu 2017-02-15 00:09:48 +01:00 committed by Anders Broman
parent 23d3a30216
commit c529e9110a
3 changed files with 172 additions and 2 deletions

View File

@ -6578,6 +6578,93 @@ ssl_dissect_hnd_hello_ext_ec_point_formats(ssl_common_dissect_t *hf, tvbuff_t *t
return offset;
}
static guint32
tls_dissect_sct(ssl_common_dissect_t *hf, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
guint32 offset, guint32 offset_end, guint16 version)
{
/* https://tools.ietf.org/html/rfc6962#section-3.2
* enum { v1(0), (255) } Version;
* struct {
* opaque key_id[32];
* } LogID;
* opaque CtExtensions<0..2^16-1>;
* struct {
* Version sct_version;
* LogID id;
* uint64 timestamp;
* CtExtensions extensions;
* digitally-signed struct { ... };
* } SignedCertificateTimestamp;
*/
guint64 sct_timestamp_ms;
nstime_t sct_timestamp;
guint32 exts_len;
proto_tree_add_item(tree, hf->hf.sct_sct_version, tvb, offset, 1, ENC_NA);
offset++;
proto_tree_add_item(tree, hf->hf.sct_sct_logid, tvb, offset, 32, ENC_BIG_ENDIAN);
offset += 32;
sct_timestamp_ms = tvb_get_ntoh64(tvb, offset);
sct_timestamp.secs = sct_timestamp_ms / 1000;
sct_timestamp.nsecs = (sct_timestamp_ms % 1000) * 1000000;
proto_tree_add_time(tree, hf->hf.sct_sct_timestamp, tvb, offset, 8, &sct_timestamp);
offset += 8;
/* opaque CtExtensions<0..2^16-1> */
if (!ssl_add_vector(hf, tvb, pinfo, tree, offset, offset_end, &exts_len,
hf->hf.sct_sct_extensions_length, 0, G_MAXUINT16)) {
return offset_end;
}
offset += 2;
if (exts_len > 0) {
proto_tree_add_item(tree, hf->hf.sct_sct_extensions, tvb, offset, exts_len, ENC_BIG_ENDIAN);
offset += exts_len;
}
offset = ssl_dissect_digitally_signed(hf, tvb, pinfo, tree, offset, offset_end, version,
hf->hf.sct_sct_signature_length,
hf->hf.sct_sct_signature);
return offset;
}
guint32
tls_dissect_sct_list(ssl_common_dissect_t *hf, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
guint32 offset, guint32 offset_end, guint16 version)
{
/* https://tools.ietf.org/html/rfc6962#section-3.3
* opaque SerializedSCT<1..2^16-1>;
* struct {
* SerializedSCT sct_list <1..2^16-1>;
* } SignedCertificateTimestampList;
*/
guint32 list_length, sct_length, next_offset;
proto_tree *subtree;
/* SerializedSCT sct_list <1..2^16-1> */
if (!ssl_add_vector(hf, tvb, pinfo, tree, offset, offset_end, &list_length,
hf->hf.sct_scts_length, 1, G_MAXUINT16)) {
return offset_end;
}
offset += 2;
while (offset < offset_end) {
subtree = proto_tree_add_subtree(tree, tvb, offset, 2, hf->ett.sct, NULL, "Signed Certificate Timestamp");
/* opaque SerializedSCT<1..2^16-1> */
if (!ssl_add_vector(hf, tvb, pinfo, subtree, offset, offset_end, &sct_length,
hf->hf.sct_sct_length, 1, G_MAXUINT16)) {
return offset_end;
}
offset += 2;
next_offset = offset + sct_length;
proto_item_set_len(subtree, 2 + sct_length);
offset = tls_dissect_sct(hf, tvb, pinfo, subtree, offset, next_offset, version);
if (!ssl_end_vector(hf, tvb, pinfo, subtree, offset, next_offset)) {
offset = next_offset;
}
}
return offset;
}
/** TLS Extensions (in Client Hello and Server Hello). }}} */
/* Whether the Content and Handshake Types are valid; handle Protocol Version. {{{ */
@ -7536,6 +7623,10 @@ ssl_dissect_hnd_extension(ssl_common_dissect_t *hf, tvbuff_t *tvb, proto_tree *t
offset = ssl_dissect_hnd_hello_ext_status_request_v2(hf, tvb, ext_tree, offset);
// TODO dissect CertificateStatus for SSL_HND_CERTIFICATE (TLS 1.3)
break;
case SSL_HND_HELLO_EXT_SIGNED_CERTIFICATE_TIMESTAMP:
if (hnd_type == SSL_HND_SERVER_HELLO || hnd_type == SSL_HND_ENCRYPTED_EXTENSIONS)
offset = tls_dissect_sct_list(hf, tvb, pinfo, ext_tree, offset, next_offset, session->version);
break;
case SSL_HND_HELLO_EXT_CLIENT_CERT_TYPE:
case SSL_HND_HELLO_EXT_SERVER_CERT_TYPE:
offset = ssl_dissect_hnd_hello_ext_cert_type(hf, tvb, ext_tree,

View File

@ -774,6 +774,15 @@ typedef struct ssl_common_dissect {
gint hs_certificate_request_context_length;
gint hs_certificate_request_context;
gint hs_key_update_request_update;
gint sct_scts_length;
gint sct_sct_length;
gint sct_sct_version;
gint sct_sct_logid;
gint sct_sct_timestamp;
gint sct_sct_extensions_length;
gint sct_sct_extensions;
gint sct_sct_signature;
gint sct_sct_signature_length;
/* do not forget to update SSL_COMMON_LIST_T and SSL_COMMON_HF_LIST! */
} hf;
@ -801,6 +810,7 @@ typedef struct ssl_common_dissect {
gint cipher_suites;
gint comp_methods;
gint session_ticket;
gint sct;
/* do not forget to update SSL_COMMON_LIST_T and SSL_COMMON_ETT_LIST! */
} ett;
@ -950,6 +960,10 @@ extern void
tls13_dissect_hnd_key_update(ssl_common_dissect_t *hf, tvbuff_t *tvb,
proto_tree *tree, guint32 offset);
extern guint32
tls_dissect_sct_list(ssl_common_dissect_t *hf, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
guint32 offset, guint32 offset_end, guint16 version);
/* {{{ */
#define SSL_COMMON_LIST_T(name) \
ssl_common_dissect_t name = { \
@ -961,11 +975,12 @@ ssl_common_dissect_t name = { \
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, \
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, \
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, \
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, \
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, \
-1, -1, -1, -1, \
}, \
/* ett */ { \
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, \
-1, -1, -1, -1, -1, -1, -1, \
-1, -1, -1, -1, -1, -1, -1, -1, \
}, \
/* ei */ { \
EI_INIT, EI_INIT, EI_INIT, EI_INIT, EI_INIT, EI_INIT, \
@ -1591,6 +1606,51 @@ ssl_common_dissect_t name = { \
{ "Key Update Request", prefix ".handshake.key_update.request_update", \
FT_UINT8, BASE_DEC, VALS(tls13_key_update_request), 0x00, \
"Whether the receiver should also update its keys", HFILL } \
}, \
{ & name .hf.sct_scts_length, \
{ "Serialized SCT List Length", prefix ".sct.scts_length", \
FT_UINT16, BASE_DEC, NULL, 0x00, \
NULL, HFILL } \
}, \
{ & name .hf.sct_sct_length, \
{ "Serialized SCT Length", prefix ".sct.sct_length", \
FT_UINT16, BASE_DEC, NULL, 0x00, \
NULL, HFILL } \
}, \
{ & name .hf.sct_sct_version, \
{ "SCT Version", prefix ".sct.sct_version", \
FT_UINT8, BASE_DEC, NULL, 0x00, \
"SCT Protocol version (v1 (0) is defined in RFC 6962)", HFILL } \
}, \
{ & name .hf.sct_sct_logid, \
{ "Log ID", prefix ".sct.sct_logid", \
FT_BYTES, BASE_NONE, NULL, 0x00, \
"SHA-256 hash of log's public key", HFILL } \
}, \
{ & name .hf.sct_sct_timestamp, \
{ "Timestamp", prefix ".sct.sct_timestamp", \
FT_ABSOLUTE_TIME, ABSOLUTE_TIME_UTC, NULL, 0x00, \
"Timestamp of issuance", HFILL } \
}, \
{ & name .hf.sct_sct_extensions_length, \
{ "Extensions length", prefix ".sct.sct_extensions_length", \
FT_UINT16, BASE_DEC, NULL, 0x00, \
"Length of future extensions to this protocol (currently none)", HFILL } \
}, \
{ & name .hf.sct_sct_extensions, \
{ "Extensions", prefix ".sct.sct_extensions", \
FT_NONE, BASE_NONE, NULL, 0x00, \
"Future extensions to this protocol (currently none)", HFILL } \
}, \
{ & name .hf.sct_sct_signature_length, \
{ "Signature Length", prefix ".sct.sct_signature_length", \
FT_UINT16, BASE_DEC, NULL, 0x00, \
NULL, HFILL } \
}, \
{ & name .hf.sct_sct_signature, \
{ "Signature", prefix ".sct.sct_signature", \
FT_BYTES, BASE_NONE, NULL, 0x00, \
NULL, HFILL } \
}
/* }}} */
@ -1619,6 +1679,7 @@ ssl_common_dissect_t name = { \
& name .ett.cipher_suites, \
& name .ett.comp_methods, \
& name .ett.session_ticket, \
& name .ett.sct, \
/* }}} */
/* {{{ */

View File

@ -102,6 +102,7 @@
#include "packet-ocsp.h"
#include "packet-ssl.h"
#include "packet-ssl-utils.h"
#include "packet-ber.h"
void proto_register_ssl(void);
@ -4331,6 +4332,19 @@ proto_register_ssl(void)
tcp_port_to_display, ssl_follow_tap_listener);
}
static int dissect_tls_sct_ber(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
guint32 offset = 0;
/* Skip through tag and length for OCTET STRING encoding. */
offset = dissect_ber_identifier(pinfo, tree, tvb, offset, NULL, NULL, NULL);
offset = dissect_ber_length(pinfo, tree, tvb, offset, NULL, NULL);
/*
* RFC 6962 (Certificate Transparency) refers to RFC 5246 (TLS 1.2) for the
* DigitallySigned format, so asssume that version.
*/
return tls_dissect_sct_list(&dissect_ssl3_hf, tvb, pinfo, tree, offset, tvb_captured_length(tvb), TLSV1DOT2_VERSION);
}
/* If this dissector uses sub-dissector registration add a registration
* routine. This format is required because a script is used to find
* these routines and create the code that calls these routines.
@ -4343,6 +4357,10 @@ proto_reg_handoff_ssl(void)
ssl_parse_uat();
ssl_parse_old_keys();
exported_pdu_tap = find_tap_id(EXPORT_PDU_TAP_NAME_LAYER_7);
/* Certificate Transparency extensions: 2 (Certificate), 5 (OCSP Response) */
register_ber_oid_dissector("1.3.6.1.4.1.11129.2.4.2", dissect_tls_sct_ber, proto_ssl, "SignedCertificateTimestampList");
register_ber_oid_dissector("1.3.6.1.4.1.11129.2.4.5", dissect_tls_sct_ber, proto_ssl, "SignedCertificateTimestampList");
}
void