diff --git a/src/analyzer/protocol/ssl/functions.bif b/src/analyzer/protocol/ssl/functions.bif index f7fa76ca36..17720bcbb1 100644 --- a/src/analyzer/protocol/ssl/functions.bif +++ b/src/analyzer/protocol/ssl/functions.bif @@ -1,6 +1,7 @@ %%{ #include "analyzer/protocol/ssl/SSL.h" +#include %%} ## Sets if the SSL analyzer should consider the connection established (handshake diff --git a/src/file_analysis/analyzer/x509/functions.bif b/src/file_analysis/analyzer/x509/functions.bif index 0fe65c7d55..161a009515 100644 --- a/src/file_analysis/analyzer/x509/functions.bif +++ b/src/file_analysis/analyzer/x509/functions.bif @@ -1,6 +1,7 @@ %%{ #include "file_analysis/analyzer/x509/X509.h" #include "types.bif.h" +#include "net_util.h" #include #include @@ -543,6 +544,100 @@ x509_verify_chainerror: return rrecord; %} +function sct_verify%(cert: opaque of x509, logid: string, log_key: string, signature: string, timestamp: count, hash_algorithm: count%): bool + %{ + assert(cert); + file_analysis::X509Val* h = (file_analysis::X509Val*) cert; + X509* x = ((file_analysis::X509Val*) h)->GetCertificate(); + + assert(sizeof(timestamp) >= 8); + uint64_t timestamp_network = htonll(timestamp); + + std::string data; + data.push_back(0); // version + data.push_back(0); // signature_type -> certificate_timestamp + data.append(reinterpret_cast(×tamp_network), sizeof(timestamp_network)); // timestamp -> 64 bits + data.append("\0\0", 2); // entry-type: x509_entry + + unsigned char *cert_out = nullptr; + uint32 cert_length = i2d_X509(x, &cert_out); + assert( cert_out ); + uint32 cert_length_network = htonl(cert_length); + assert( sizeof(cert_length_network) == 4); + + data.append(reinterpret_cast(&cert_length_network)+1, 3); // 3 bytes certificate length + data.append(reinterpret_cast(cert_out), cert_length); // der-encoded certificate + OPENSSL_free(cert_out); + data.append("\0\0", 2); // no extensions + + // key is given as a DER-encoded SubjectPublicKeyInfo. + const unsigned char *key_char = log_key->Bytes(); + EVP_PKEY* key = d2i_PUBKEY(nullptr, &key_char, log_key->Len()); + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + assert(mdctx); + + string errstr; + int success = 0; + + const EVP_MD* hash; + // numbers from http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18 + switch ( hash_algorithm ) + { + case 1: + hash = EVP_md5(); + break; + case 2: + hash = EVP_sha1(); + break; + case 3: + hash = EVP_sha224(); + break; + case 4: + hash = EVP_sha256(); + break; + case 5: + hash = EVP_sha384(); + break; + case 6: + hash = EVP_sha512(); + break; + default: + errstr = "Unknown hash algorithm"; + goto sct_verify_err; + } + + if ( ! key ) + { + errstr = "Could not load log key"; + goto sct_verify_err; + } + + if ( ! EVP_DigestVerifyInit(mdctx, NULL, hash, NULL, key) ) + { + errstr = "Could not init signature verification"; + goto sct_verify_err; + } + + if ( ! EVP_DigestVerifyUpdate(mdctx, data.data(), data.size()) ) + { + errstr = "Could not update digest for verification"; + goto sct_verify_err; + } + + success = EVP_DigestVerifyFinal(mdctx, signature->Bytes(), signature->Len()); + EVP_MD_CTX_destroy(mdctx); + + return new Val(success, TYPE_BOOL); + +sct_verify_err: + if (mdctx) + EVP_MD_CTX_destroy(mdctx); + cerr << errstr << endl; + reporter->Error("%s", errstr.c_str()); + return new Val(0, TYPE_BOOL); + %} + ## Get the hash of issuer name of a certificate ## ## cert: The X509 certificate opaque handle. diff --git a/testing/btest/Baseline/scripts.base.files.x509.signed_certificate_timestamp_ocsp/.stdout b/testing/btest/Baseline/scripts.base.files.x509.signed_certificate_timestamp_ocsp/.stdout new file mode 100644 index 0000000000..5384f4e6b7 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.files.x509.signed_certificate_timestamp_ocsp/.stdout @@ -0,0 +1,6 @@ +0, Google 'Pilot' log, 1406997753.366, 4, 3 +Verify of, Google 'Pilot' log, T +Bad verify of, Google 'Pilot' log, F +0, Google 'Aviator' log, 1407002457.456, 4, 3 +Verify of, Google 'Aviator' log, T +Bad verify of, Google 'Aviator' log, F diff --git a/testing/btest/Baseline/scripts.base.protocols.ssl.signed_certificate_timestamp/.stdout b/testing/btest/Baseline/scripts.base.protocols.ssl.signed_certificate_timestamp/.stdout index bc56f161ab..342228a1cf 100644 --- a/testing/btest/Baseline/scripts.base.protocols.ssl.signed_certificate_timestamp/.stdout +++ b/testing/btest/Baseline/scripts.base.protocols.ssl.signed_certificate_timestamp/.stdout @@ -1,3 +1,9 @@ 0, Google 'Pilot' log, 1406997753.366, [HashAlgorithm=4, SignatureAlgorithm=3] 0, Google 'Aviator' log, 1407002457.456, [HashAlgorithm=4, SignatureAlgorithm=3] 0, Google 'Rocketeer' log, 1410299366.023, [HashAlgorithm=4, SignatureAlgorithm=3] +Verify of, Google 'Pilot' log, T +Bad verify of, Google 'Pilot' log, F +Verify of, Google 'Aviator' log, T +Bad verify of, Google 'Aviator' log, F +Verify of, Google 'Rocketeer' log, T +Bad verify of, Google 'Rocketeer' log, F diff --git a/testing/btest/scripts/base/files/x509/signed_certificate_timestamp_ocsp.test b/testing/btest/scripts/base/files/x509/signed_certificate_timestamp_ocsp.test new file mode 100644 index 0000000000..01ed128541 --- /dev/null +++ b/testing/btest/scripts/base/files/x509/signed_certificate_timestamp_ocsp.test @@ -0,0 +1,31 @@ +# @TEST-EXEC: bro -r $TRACES/tls/signed_certificate_timestamp.pcap %INPUT +# @TEST-EXEC: btest-diff .stdout + +event bro_init() + { + Files::register_for_mime_type(Files::ANALYZER_OCSP_REPLY, "application/ocsp-response"); + } + +event x509_ocsp_ext_signed_certificate_timestamp(f: fa_file, version: count, logid: string, timestamp: count, hash_algorithm: count, signature_algorithm: count, signature: string) + { + print version, SSL::ct_logs[logid]$description, double_to_time(timestamp/1000.0), hash_algorithm, signature_algorithm; + + if ( |f$conns| != 1 ) + return; + + for ( cid in f$conns ) + { + if ( ! f$conns[cid]?$ssl ) + return; + + local c = f$conns[cid]; + } + + if ( ! c$ssl?$cert_chain || |c$ssl$cert_chain| == 0 || ! c$ssl$cert_chain[0]?$x509 ) + return; + + local cert = c$ssl$cert_chain[0]$x509$handle; + + print "Verify of", SSL::ct_logs[logid]$description, sct_verify(cert, logid, SSL::ct_logs[logid]$key, signature, timestamp, hash_algorithm); + print "Bad verify of", SSL::ct_logs[logid]$description, sct_verify(cert, logid, SSL::ct_logs[logid]$key, signature, timestamp+1, hash_algorithm); + } diff --git a/testing/btest/scripts/base/protocols/ssl/signed_certificate_timestamp.test b/testing/btest/scripts/base/protocols/ssl/signed_certificate_timestamp.test index d0eaacd096..0b9c5fc157 100644 --- a/testing/btest/scripts/base/protocols/ssl/signed_certificate_timestamp.test +++ b/testing/btest/scripts/base/protocols/ssl/signed_certificate_timestamp.test @@ -1,7 +1,39 @@ # @TEST-EXEC: bro -r $TRACES/tls/signed_certificate_timestamp.pcap %INPUT # @TEST-EXEC: btest-diff .stdout +export { + type LogInfo: record { + version: count; + logid: string; + timestamp: count; + sig_alg: count; + hash_alg: count; + signature: string; + }; +} + +redef record SSL::Info += { + ct_proofs: vector of LogInfo &default=vector(); +}; + event ssl_extension_signed_certificate_timestamp(c: connection, is_orig: bool, version: count, logid: string, timestamp: count, signature_and_hashalgorithm: SSL::SignatureAndHashAlgorithm, signature: string) { print version, SSL::ct_logs[logid]$description, double_to_time(timestamp/1000.0), signature_and_hashalgorithm; + c$ssl$ct_proofs[|c$ssl$ct_proofs|] = LogInfo($version=version, $logid=logid, $timestamp=timestamp, $sig_alg=signature_and_hashalgorithm$SignatureAlgorithm, $hash_alg=signature_and_hashalgorithm$HashAlgorithm, $signature=signature); + } + +event ssl_established(c: connection) + { + if ( ! c$ssl?$cert_chain || |c$ssl$cert_chain| == 0 || ! c$ssl$cert_chain[0]?$x509 ) + return; + + local cert = c$ssl$cert_chain[0]$x509$handle; + + for ( i in c$ssl$ct_proofs ) + { + local log = c$ssl$ct_proofs[i]; + + print "Verify of", SSL::ct_logs[log$logid]$description, sct_verify(cert, log$logid, SSL::ct_logs[log$logid]$key, log$signature, log$timestamp, log$hash_alg); + print "Bad verify of", SSL::ct_logs[log$logid]$description, sct_verify(cert, log$logid, SSL::ct_logs[log$logid]$key, log$signature, log$timestamp+1, log$hash_alg); + } }