Add parsing of signed certificate timestamps out of X.509 certs.

This is a tiny bit evil because it uses parts of the SSL protocol
analyzer in the X.509 certificate parser. Which is the fault of the
protocol, which replicates the functionality.
This commit is contained in:
Johanna Amann 2017-02-07 13:31:21 -08:00
parent a51ee9e155
commit 5dd19f84a7
12 changed files with 140 additions and 5 deletions

View file

@ -12,6 +12,7 @@ bro_plugin_pac(tls-handshake.pac tls-handshake-protocol.pac tls-handshake-analyz
proc-client-hello.pac proc-client-hello.pac
proc-server-hello.pac proc-server-hello.pac
proc-certificate.pac proc-certificate.pac
tls-handshake-signed_certificate_timestamp.pac
) )
bro_plugin_pac(ssl.pac ssl-dtls-analyzer.pac ssl-analyzer.pac ssl-dtls-protocol.pac ssl-protocol.pac ssl-defs.pac bro_plugin_pac(ssl.pac ssl-dtls-analyzer.pac ssl-analyzer.pac ssl-dtls-protocol.pac ssl-protocol.pac ssl-defs.pac
proc-client-hello.pac proc-client-hello.pac

View file

@ -554,11 +554,6 @@ type KeyShare(rec: HandshakeRecord) = case rec.msg_type of {
default -> other : bytestring &restofdata &transient; default -> other : bytestring &restofdata &transient;
}; };
type SignatureAndHashAlgorithm() = record {
HashAlgorithm: uint8;
SignatureAlgorithm: uint8;
}
type SignatureAlgorithm(rec: HandshakeRecord) = record { type SignatureAlgorithm(rec: HandshakeRecord) = record {
length: uint16; length: uint16;
supported_signature_algorithms: SignatureAndHashAlgorithm[] &until($input.length() == 0); supported_signature_algorithms: SignatureAndHashAlgorithm[] &until($input.length() == 0);

View file

@ -2,6 +2,11 @@
# If included there, it uses the exact same syntax and we just symlink it from the X.509 # If included there, it uses the exact same syntax and we just symlink it from the X.509
# file analyzer tree. # file analyzer tree.
type SignatureAndHashAlgorithm() = record {
HashAlgorithm: uint8;
SignatureAlgorithm: uint8;
}
type SignedCertificateTimestampList(rec: HandshakeRecord) = record { type SignedCertificateTimestampList(rec: HandshakeRecord) = record {
length: uint16; length: uint16;
SCTs: SignedCertificateTimestamp(rec)[] &until($input.length() == 0); SCTs: SignedCertificateTimestamp(rec)[] &until($input.length() == 0);

View file

@ -7,4 +7,5 @@ include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}
bro_plugin_begin(Bro X509) bro_plugin_begin(Bro X509)
bro_plugin_cc(X509.cc Plugin.cc) bro_plugin_cc(X509.cc Plugin.cc)
bro_plugin_bif(events.bif types.bif functions.bif) bro_plugin_bif(events.bif types.bif functions.bif)
bro_plugin_pac(x509-extension.pac x509-signed_certificate_timestamp.pac)
bro_plugin_end() bro_plugin_end()

View file

@ -4,6 +4,7 @@
#include "X509.h" #include "X509.h"
#include "Event.h" #include "Event.h"
#include "x509-extension_pac.h"
#include "events.bif.h" #include "events.bif.h"
#include "types.bif.h" #include "types.bif.h"
@ -298,6 +299,52 @@ void file_analysis::X509::ParseExtension(X509_EXTENSION* ex)
else if ( OBJ_obj2nid(ext_asn) == NID_subject_alt_name ) else if ( OBJ_obj2nid(ext_asn) == NID_subject_alt_name )
ParseSAN(ex); ParseSAN(ex);
else if ( OBJ_obj2nid(ext_asn) == NID_ct_cert_scts || OBJ_obj2nid(ext_asn) == NID_ct_precert_scts )
ParseSignedCertificateTimestamps(ex);
}
void file_analysis::X509::ParseSignedCertificateTimestamps(X509_EXTENSION* ext)
{
// Ok, signed certificate timestamps are a bit of an odd case out; we don't
// want to use the (basically nonexistant) OpenSSL functionality to parse them.
// Instead we have our own, self-written binpac parser to parse just them,
// which we will initialize here and tear down immediately again.
ASN1_OCTET_STRING* ext_val = X509_EXTENSION_get_data(ext);
// the octet string of the extension contains the octet string which in turn
// contains the SCT. Obviously.
unsigned char* ext_val_copy = (unsigned char*) OPENSSL_malloc(ext_val->length);
unsigned char* ext_val_second_pointer = ext_val_copy;
memcpy(ext_val_copy, ext_val->data, ext_val->length);
ASN1_OCTET_STRING* inner = d2i_ASN1_OCTET_STRING(NULL, (const unsigned char**) &ext_val_copy, ext_val->length);
if ( !inner )
{
reporter->Error("X509::ParseSignedCertificateTimestamps could not parse inner octet string");
return;
}
binpac::X509Extension::MockConnection* conn = new binpac::X509Extension::MockConnection(this);
binpac::X509Extension::SignedCertTimestampExt* interp = new binpac::X509Extension::SignedCertTimestampExt(conn);
try
{
interp->NewData(inner->data, inner->data + inner->length);
}
catch( const binpac::Exception& e )
{
// throw a warning or sth
reporter->Error("X509::ParseSignedCertificateTimestamps could not parse SCT");
}
OPENSSL_free(ext_val_second_pointer);
interp->FlowEOF();
delete interp;
delete conn;
} }
void file_analysis::X509::ParseBasicConstraints(X509_EXTENSION* ex) void file_analysis::X509::ParseBasicConstraints(X509_EXTENSION* ex)

View file

@ -58,6 +58,7 @@ private:
void ParseExtension(X509_EXTENSION* ex); void ParseExtension(X509_EXTENSION* ex);
void ParseBasicConstraints(X509_EXTENSION* ex); void ParseBasicConstraints(X509_EXTENSION* ex);
void ParseSAN(X509_EXTENSION* ex); void ParseSAN(X509_EXTENSION* ex);
void ParseSignedCertificateTimestamps(X509_EXTENSION* ext);
std::string cert_data; std::string cert_data;

View file

@ -55,3 +55,22 @@ event x509_ext_basic_constraints%(f: fa_file, ext: X509::BasicConstraints%);
## x509_parse x509_verify ## x509_parse x509_verify
## x509_get_certificate_string ## x509_get_certificate_string
event x509_ext_subject_alternative_name%(f: fa_file, ext: X509::SubjectAlternativeName%); event x509_ext_subject_alternative_name%(f: fa_file, ext: X509::SubjectAlternativeName%);
## Generated for the signed_certificate_timestamp X509 extension as defined in
## :rfc:`6962`. The extension is used to transmit signed proofs that are
## used for Certificate Transparency.
##
## f: The file.
##
## version: the version of the protocol to which the SCT conforms. Always
## should be 0 (representing version 1)
##
## logid: 32 bit key id
##
## timestamp: the timestamp of the sct
##
## signature_and_hashalgorithm: signature and hash algorithm used for the
## digitally_signed struct
##
## signature: signature part of the digitally_signed struct
event x509_ext_signed_certificate_timestamp%(f: fa_file, version: count, logid: string, timestamp: time, hash_algorithm: count, signature_algorithm: count, signature: string%);

View file

@ -0,0 +1,54 @@
# Binpac analyzer for X.509 extensions
# we just use it for the SignedCertificateTimestamp at the moment
%include binpac.pac
%include bro.pac
%extern{
#include "types.bif.h"
#include "file_analysis/File.h"
#include "events.bif.h"
%}
analyzer X509Extension withcontext {
connection: MockConnection;
flow: SignedCertTimestampExt;
};
connection MockConnection(bro_analyzer: BroFileAnalyzer) {
upflow = SignedCertTimestampExt;
downflow = SignedCertTimestampExt;
};
%include x509-signed_certificate_timestamp.pac
# The base record
type HandshakeRecord() = record {
signed_certificate_timestamp_list: SignedCertificateTimestampList(this)[] &transient;
} &byteorder = bigendian;
flow SignedCertTimestampExt {
flowunit = HandshakeRecord withcontext(connection, this);
};
refine connection MockConnection += {
function proc_signedcertificatetimestamp(rec: HandshakeRecord, version: uint8, logid: const_bytestring, timestamp: uint64, digitally_signed_algorithms: SignatureAndHashAlgorithm, digitally_signed_signature: const_bytestring) : bool
%{
BifEvent::generate_x509_ext_signed_certificate_timestamp((analyzer::Analyzer *) bro_analyzer(),
bro_analyzer()->GetFile()->GetVal()->Ref(),
version,
new StringVal(logid.length(), reinterpret_cast<const char*>(logid.begin())),
((double)timestamp)/1000,
digitally_signed_algorithms->HashAlgorithm(),
digitally_signed_algorithms->SignatureAlgorithm(),
new StringVal(digitally_signed_signature.length(), reinterpret_cast<const char*>(digitally_signed_signature.begin()))
);
return true;
%}
};
refine typeattr SignedCertificateTimestamp += &let {
proc : bool = $context.connection.proc_signedcertificatetimestamp(rec, version, logid, timestamp, digitally_signed_algorithms, digitally_signed_signature);
};

View file

@ -0,0 +1 @@
../../../analyzer/protocol/ssl/tls-handshake-signed_certificate_timestamp.pac

View file

@ -0,0 +1,4 @@
0, 1474927230.876, 4, 3
0, 1474927232.863, 4, 3
0, 1474927232.112, 4, 3
0, 1474927232.304, 4, 3

Binary file not shown.

View file

@ -0,0 +1,7 @@
# @TEST-EXEC: bro -r $TRACES/tls/certificate-with-sct.pcap %INPUT
# @TEST-EXEC: btest-diff .stdout
event x509_ext_signed_certificate_timestamp(f: fa_file, version: count, logid: string, timestamp: time, hash_algorithm: count, signature_algorithm: count, signature: string)
{
print version, timestamp, hash_algorithm, signature_algorithm;
}