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:
parent
23d3a30216
commit
c529e9110a
|
@ -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,
|
||||
|
|
|
@ -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, \
|
||||
/* }}} */
|
||||
|
||||
/* {{{ */
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue