Merge remote-tracking branch 'origin/master' into topic/johanna/tls-more-data

This commit is contained in:
Johanna Amann 2018-08-17 11:52:00 -07:00
commit b1dbd757a6
1468 changed files with 41493 additions and 19065 deletions

View file

@ -50,33 +50,33 @@ event bro_init()
# Minimum length a heartbeat packet must have for different cipher suites.
# Note - tls 1.1f and 1.0 have different lengths :(
# This should be all cipher suites usually supported by vulnerable servers.
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_AES_256_GCM_SHA384$/, $min_length=43];
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_AES_128_GCM_SHA256$/, $min_length=43];
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_256_CBC_SHA384$/, $min_length=96];
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_256_CBC_SHA256$/, $min_length=80];
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_256_CBC_SHA$/, $min_length=64];
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_128_CBC_SHA256$/, $min_length=80];
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_128_CBC_SHA$/, $min_length=64];
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_3DES_EDE_CBC_SHA$/, $min_length=48];
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_SEED_CBC_SHA$/, $min_length=64];
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_IDEA_CBC_SHA$/, $min_length=48];
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_DES_CBC_SHA$/, $min_length=48];
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_DES40_CBC_SHA$/, $min_length=48];
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_RC4_128_SHA$/, $min_length=39];
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_RC4_128_MD5$/, $min_length=35];
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_RC4_40_MD5$/, $min_length=35];
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_RC2_CBC_40_MD5$/, $min_length=48];
min_lengths[|min_lengths|] = [$cipher=/_256_CBC_SHA$/, $min_length=48];
min_lengths[|min_lengths|] = [$cipher=/_128_CBC_SHA$/, $min_length=48];
min_lengths[|min_lengths|] = [$cipher=/_3DES_EDE_CBC_SHA$/, $min_length=40];
min_lengths[|min_lengths|] = [$cipher=/_SEED_CBC_SHA$/, $min_length=48];
min_lengths[|min_lengths|] = [$cipher=/_IDEA_CBC_SHA$/, $min_length=40];
min_lengths[|min_lengths|] = [$cipher=/_DES_CBC_SHA$/, $min_length=40];
min_lengths[|min_lengths|] = [$cipher=/_DES40_CBC_SHA$/, $min_length=40];
min_lengths[|min_lengths|] = [$cipher=/_RC4_128_SHA$/, $min_length=39];
min_lengths[|min_lengths|] = [$cipher=/_RC4_128_MD5$/, $min_length=35];
min_lengths[|min_lengths|] = [$cipher=/_RC4_40_MD5$/, $min_length=35];
min_lengths[|min_lengths|] = [$cipher=/_RC2_CBC_40_MD5$/, $min_length=40];
min_lengths_tls11 += [$cipher=/_AES_256_GCM_SHA384$/, $min_length=43];
min_lengths_tls11 += [$cipher=/_AES_128_GCM_SHA256$/, $min_length=43];
min_lengths_tls11 += [$cipher=/_256_CBC_SHA384$/, $min_length=96];
min_lengths_tls11 += [$cipher=/_256_CBC_SHA256$/, $min_length=80];
min_lengths_tls11 += [$cipher=/_256_CBC_SHA$/, $min_length=64];
min_lengths_tls11 += [$cipher=/_128_CBC_SHA256$/, $min_length=80];
min_lengths_tls11 += [$cipher=/_128_CBC_SHA$/, $min_length=64];
min_lengths_tls11 += [$cipher=/_3DES_EDE_CBC_SHA$/, $min_length=48];
min_lengths_tls11 += [$cipher=/_SEED_CBC_SHA$/, $min_length=64];
min_lengths_tls11 += [$cipher=/_IDEA_CBC_SHA$/, $min_length=48];
min_lengths_tls11 += [$cipher=/_DES_CBC_SHA$/, $min_length=48];
min_lengths_tls11 += [$cipher=/_DES40_CBC_SHA$/, $min_length=48];
min_lengths_tls11 += [$cipher=/_RC4_128_SHA$/, $min_length=39];
min_lengths_tls11 += [$cipher=/_RC4_128_MD5$/, $min_length=35];
min_lengths_tls11 += [$cipher=/_RC4_40_MD5$/, $min_length=35];
min_lengths_tls11 += [$cipher=/_RC2_CBC_40_MD5$/, $min_length=48];
min_lengths += [$cipher=/_256_CBC_SHA$/, $min_length=48];
min_lengths += [$cipher=/_128_CBC_SHA$/, $min_length=48];
min_lengths += [$cipher=/_3DES_EDE_CBC_SHA$/, $min_length=40];
min_lengths += [$cipher=/_SEED_CBC_SHA$/, $min_length=48];
min_lengths += [$cipher=/_IDEA_CBC_SHA$/, $min_length=40];
min_lengths += [$cipher=/_DES_CBC_SHA$/, $min_length=40];
min_lengths += [$cipher=/_DES40_CBC_SHA$/, $min_length=40];
min_lengths += [$cipher=/_RC4_128_SHA$/, $min_length=39];
min_lengths += [$cipher=/_RC4_128_MD5$/, $min_length=35];
min_lengths += [$cipher=/_RC4_40_MD5$/, $min_length=35];
min_lengths += [$cipher=/_RC2_CBC_40_MD5$/, $min_length=40];
}
event ssl_heartbeat(c: connection, is_orig: bool, length: count, heartbeat_type: count, payload_length: count, payload: string)

View file

@ -4,6 +4,7 @@
@load base/utils/directions-and-hosts
@load base/protocols/ssl
@load base/files/x509
@load base/frameworks/cluster
module Known;
@ -29,27 +30,144 @@ export {
## The certificates whose existence should be logged and tracked.
## Choices are: LOCAL_HOSTS, REMOTE_HOSTS, ALL_HOSTS, NO_HOSTS.
const cert_tracking = LOCAL_HOSTS &redef;
## Toggles between different implementations of this script.
## When true, use a Broker data store, else use a regular Bro set
## with keys uniformly distributed over proxy nodes in cluster
## operation.
const use_cert_store = T &redef;
type AddrCertHashPair: record {
host: addr;
hash: string;
};
## Holds the set of all known certificates. Keys in the store are of
## type :bro:type:`Known::AddrCertHashPair` and their associated value is
## always the boolean value of "true".
global cert_store: Cluster::StoreInfo;
## The Broker topic name to use for :bro:see:`Known::cert_store`.
const cert_store_name = "bro/known/certs" &redef;
## The expiry interval of new entries in :bro:see:`Known::cert_store`.
## This also changes the interval at which certs get logged.
const cert_store_expiry = 1day &redef;
## The timeout interval to use for operations against
## :bro:see:`Known::cert_store`.
const cert_store_timeout = 15sec &redef;
## The set of all known certificates to store for preventing duplicate
## logging. It can also be used from other scripts to
## inspect if a certificate has been seen in use. The string value
## in the set is for storing the DER formatted certificate' SHA1 hash.
global certs: set[addr, string] &create_expire=1day &synchronized &redef;
##
## In cluster operation, this set is uniformly distributed across
## proxy nodes.
global certs: set[addr, string] &create_expire=1day &redef;
## Event that can be handled to access the loggable record as it is sent
## on to the logging framework.
global log_known_certs: event(rec: CertsInfo);
}
event bro_init() &priority=5
event bro_init()
{
Log::create_stream(Known::CERTS_LOG, [$columns=CertsInfo, $ev=log_known_certs, $path="known_certs"]);
if ( ! Known::use_cert_store )
return;
Known::cert_store = Cluster::create_store(Known::cert_store_name);
}
event Known::cert_found(info: CertsInfo, hash: string)
{
if ( ! Known::use_cert_store )
return;
local key = AddrCertHashPair($host = info$host, $hash = hash);
when ( local r = Broker::put_unique(Known::cert_store$store, key,
T, Known::cert_store_expiry) )
{
if ( r$status == Broker::SUCCESS )
{
if ( r$result as bool )
Log::write(Known::CERTS_LOG, info);
}
else
Reporter::error(fmt("%s: data store put_unique failure",
Known::cert_store_name));
}
timeout Known::cert_store_timeout
{
# Can't really tell if master store ended up inserting a key.
Log::write(Known::CERTS_LOG, info);
}
}
event known_cert_add(info: CertsInfo, hash: string)
{
if ( Known::use_cert_store )
return;
if ( [info$host, hash] in Known::certs )
return;
add Known::certs[info$host, hash];
@if ( ! Cluster::is_enabled() ||
Cluster::local_node_type() == Cluster::PROXY )
Log::write(Known::CERTS_LOG, info);
@endif
}
event Known::cert_found(info: CertsInfo, hash: string)
{
if ( Known::use_cert_store )
return;
if ( [info$host, hash] in Known::certs )
return;
local key = cat(info$host, hash);
Cluster::publish_hrw(Cluster::proxy_pool, key, known_cert_add, info, hash);
event known_cert_add(info, hash);
}
event Cluster::node_up(name: string, id: string)
{
if ( Known::use_cert_store )
return;
if ( Cluster::local_node_type() != Cluster::WORKER )
return;
# Drop local suppression cache on workers to force HRW key repartitioning.
Known::certs = table();
}
event Cluster::node_down(name: string, id: string)
{
if ( Known::use_cert_store )
return;
if ( Cluster::local_node_type() != Cluster::WORKER )
return;
# Drop local suppression cache on workers to force HRW key repartitioning.
Known::certs = table();
}
event ssl_established(c: connection) &priority=3
{
if ( ! c$ssl?$cert_chain || |c$ssl$cert_chain| < 1 ||
! c$ssl$cert_chain[0]?$x509 )
if ( ! c$ssl?$cert_chain )
return;
if ( |c$ssl$cert_chain| < 1 )
return;
if ( ! c$ssl$cert_chain[0]?$x509 )
return;
local fuid = c$ssl$cert_chain_fuids[0];
@ -61,16 +179,21 @@ event ssl_established(c: connection) &priority=3
return;
}
local host = c$id$resp_h;
if ( ! addr_matches_host(host, cert_tracking) )
return;
local hash = c$ssl$cert_chain[0]$sha1;
local cert = c$ssl$cert_chain[0]$x509$certificate;
local host = c$id$resp_h;
if ( [host, hash] !in certs && addr_matches_host(host, cert_tracking) )
{
add certs[host, hash];
Log::write(Known::CERTS_LOG, [$ts=network_time(), $host=host,
$port_num=c$id$resp_p, $subject=cert$subject,
$issuer_subject=cert$issuer,
$serial=cert$serial]);
}
local info = CertsInfo($ts = network_time(), $host = host,
$port_num = c$id$resp_p, $subject = cert$subject,
$issuer_subject = cert$issuer,
$serial = cert$serial);
event Known::cert_found(info, hash);
}
event bro_init() &priority=5
{
Log::create_stream(Known::CERTS_LOG, [$columns=CertsInfo, $ev=log_known_certs, $path="known_certs"]);
}

View file

@ -8,7 +8,7 @@ module X509;
export {
redef record Info += {
# Logging is suppressed if field is set to F
## Logging of certificate is suppressed if set to F
logcert: bool &default=T;
};
}
@ -39,14 +39,29 @@ event bro_init() &priority=2
Log::add_filter(X509::LOG, f);
}
event file_over_new_connection(f: fa_file, c: connection, is_orig: bool) &priority=2
event file_sniff(f: fa_file, meta: fa_metadata) &priority=4
{
if ( ! c?$ssl )
if ( |f$conns| != 1 )
return;
if ( ! f?$info || ! f$info?$mime_type )
return;
if ( ! ( f$info$mime_type == "application/x-x509-ca-cert" || f$info$mime_type == "application/x-x509-user-cert"
|| f$info$mime_type == "application/pkix-cert" ) )
return;
for ( cid in f$conns )
{
if ( ! f$conns[cid]?$ssl )
return;
local c = f$conns[cid];
}
local chain: vector of string;
if ( is_orig )
if ( f$is_orig )
chain = c$ssl$client_cert_chain_fuids;
else
chain = c$ssl$cert_chain_fuids;

View file

@ -56,7 +56,7 @@ event ssl_established(c: connection) &priority=3
local waits_already = digest in waitlist;
if ( ! waits_already )
waitlist[digest] = vector();
waitlist[digest][|waitlist[digest]|] = c$ssl;
waitlist[digest] += c$ssl;
if ( waits_already )
return;

View file

@ -3,6 +3,7 @@
# Also caches all intermediate certificates encountered so far and use them
# for future validations.
@load base/frameworks/cluster
@load base/frameworks/notice
@load base/protocols/ssl
@ -19,12 +20,17 @@ export {
redef record Info += {
## Result of certificate validation for this connection.
validation_status: string &log &optional;
## Result of certificate validation for this connection, given
## as OpenSSL validation code.
validation_code: int &optional;
## Ordered chain of validated certificate, if validation succeeded.
valid_chain: vector of opaque of x509 &optional;
};
## MD5 hash values for recently validated chains along with the
## Result values for recently validated chains along with the
## validation status are kept in this table to avoid constant
## validation every time the same certificate chain is seen.
global recently_validated_certs: table[string] of string = table()
global recently_validated_certs: table[string] of X509::Result = table()
&read_expire=5mins &redef;
## Use intermediate CA certificate caching when trying to validate
@ -39,11 +45,16 @@ export {
## that you encounter. Only disable if you want to find misconfigured servers.
global ssl_cache_intermediate_ca: bool = T &redef;
## Event from a worker to the manager that it has encountered a new
## valid intermediate.
## Store the valid chain in c$ssl$valid_chain if validation succeeds.
## This has a potentially high memory impact, depending on the local environment
## and is thus disabled by default.
global ssl_store_valid_chain: bool = F &redef;
## Event from a manager to workers when encountering a new, valid
## intermediate.
global intermediate_add: event(key: string, value: vector of opaque of x509);
## Event from the manager to the workers that a new intermediate chain
## Event from workers to the manager when a new intermediate chain
## is to be added.
global new_intermediate: event(key: string, value: vector of opaque of x509);
}
@ -51,12 +62,13 @@ export {
global intermediate_cache: table[string] of vector of opaque of x509;
@if ( Cluster::is_enabled() )
@load base/frameworks/cluster
redef Cluster::manager2worker_events += /SSL::intermediate_add/;
redef Cluster::worker2manager_events += /SSL::new_intermediate/;
event bro_init()
{
Broker::auto_publish(Cluster::worker_topic, SSL::intermediate_add);
Broker::auto_publish(Cluster::manager_topic, SSL::new_intermediate);
}
@endif
function add_to_cache(key: string, value: vector of opaque of x509)
{
intermediate_cache[key] = value;
@ -83,7 +95,7 @@ event SSL::new_intermediate(key: string, value: vector of opaque of x509)
}
@endif
function cache_validate(chain: vector of opaque of x509): string
function cache_validate(chain: vector of opaque of x509): X509::Result
{
local chain_hash: vector of string = vector();
@ -97,7 +109,10 @@ function cache_validate(chain: vector of opaque of x509): string
return recently_validated_certs[chain_id];
local result = x509_verify(chain, root_certs);
recently_validated_certs[chain_id] = result$result_string;
if ( ! ssl_store_valid_chain && result?$chain_certs )
recently_validated_certs[chain_id] = X509::Result($result=result$result, $result_string=result$result_string);
else
recently_validated_certs[chain_id] = result;
# if we have a working chain where we did not store the intermediate certs
# in our cache yet - do so
@ -107,8 +122,8 @@ function cache_validate(chain: vector of opaque of x509): string
|result$chain_certs| > 2 )
{
local result_chain = result$chain_certs;
local icert = x509_parse(result_chain[1]);
if ( icert$subject !in intermediate_cache )
local isnh = x509_subject_name_hash(result_chain[1], 4); # SHA256
if ( isnh !in intermediate_cache )
{
local cachechain: vector of opaque of x509;
for ( i in result_chain )
@ -116,14 +131,14 @@ function cache_validate(chain: vector of opaque of x509): string
if ( i >=1 && i<=|result_chain|-2 )
cachechain[i-1] = result_chain[i];
}
add_to_cache(icert$subject, cachechain);
add_to_cache(isnh, cachechain);
}
}
return result$result_string;
return result;
}
event ssl_established(c: connection) &priority=3
hook ssl_finishing(c: connection) &priority=20
{
# If there aren't any certs we can't very well do certificate validation.
if ( ! c$ssl?$cert_chain || |c$ssl$cert_chain| == 0 ||
@ -131,23 +146,26 @@ event ssl_established(c: connection) &priority=3
return;
local intermediate_chain: vector of opaque of x509 = vector();
local issuer = c$ssl$cert_chain[0]$x509$certificate$issuer;
local issuer_name_hash = x509_issuer_name_hash(c$ssl$cert_chain[0]$x509$handle, 4); # SHA256
local hash = c$ssl$cert_chain[0]$sha1;
local result: string;
local result: X509::Result;
# Look if we already have a working chain for the issuer of this cert.
# If yes, try this chain first instead of using the chain supplied from
# the server.
if ( ssl_cache_intermediate_ca && issuer in intermediate_cache )
if ( ssl_cache_intermediate_ca && issuer_name_hash in intermediate_cache )
{
intermediate_chain[0] = c$ssl$cert_chain[0]$x509$handle;
for ( i in intermediate_cache[issuer] )
intermediate_chain[i+1] = intermediate_cache[issuer][i];
for ( i in intermediate_cache[issuer_name_hash] )
intermediate_chain[i+1] = intermediate_cache[issuer_name_hash][i];
result = cache_validate(intermediate_chain);
if ( result == "ok" )
if ( result$result_string == "ok" )
{
c$ssl$validation_status = result;
c$ssl$validation_status = result$result_string;
c$ssl$validation_code = result$result;
if ( result?$chain_certs )
c$ssl$valid_chain = result$chain_certs;
return;
}
}
@ -163,13 +181,16 @@ event ssl_established(c: connection) &priority=3
}
result = cache_validate(chain);
c$ssl$validation_status = result;
c$ssl$validation_status = result$result_string;
c$ssl$validation_code = result$result;
if ( result?$chain_certs )
c$ssl$valid_chain = result$chain_certs;
if ( result != "ok" )
if ( result$result_string != "ok" )
{
local message = fmt("SSL certificate validation failed with (%s)", c$ssl$validation_status);
NOTICE([$note=Invalid_Server_Cert, $msg=message,
$sub=c$ssl$subject, $conn=c,
$identifier=cat(c$id$resp_h,c$id$resp_p,hash,c$ssl$validation_status)]);
$sub=c$ssl$cert_chain[0]$x509$certificate$subject, $conn=c,
$identifier=cat(c$id$resp_h,c$id$resp_p,hash,c$ssl$validation_code)]);
}
}

View file

@ -1,4 +1,9 @@
##! Perform OCSP response validation.
##! Perform validation of stapled OCSP responses.
#!
#! Note: this _only_ performs validation of stapled OCSP responsed. It does
#! not validate OCSP responses that are retrieved via HTTP, because we do not
#! have a mapping to certificates.
@load base/frameworks/notice
@load base/protocols/ssl
@ -15,7 +20,6 @@ export {
redef record Info += {
## Result of ocsp validation for this connection.
ocsp_status: string &log &optional;
## ocsp response as string.
ocsp_response: string &optional;
};

View file

@ -0,0 +1,212 @@
##! Perform validation of Signed Certificate Timestamps, as used
##! for Certificate Transparency. See RFC6962 for more details.
@load base/protocols/ssl
@load protocols/ssl/validate-certs
# We need to know issuer certificates to be able to determine the IssuerKeyHash,
# which is required for validating certificate extensions.
redef SSL::ssl_store_valid_chain = T;
module SSL;
export {
## List of the different sources for Signed Certificate Timestamp
type SctSource: enum {
## Signed Certificate Timestamp was encountered in the extension of
## an X.509 certificate.
SCT_X509_EXT,
## Signed Certificate Timestamp was encountered in an TLS session
## extension.
SCT_TLS_EXT,
## Signed Certificate Timestamp was encountered in the extension of
## an stapled OCSP reply.
SCT_OCSP_EXT
};
## This record is used to store information about the SCTs that are
## encountered in a SSL connection.
type SctInfo: record {
## The version of the encountered SCT (should always be 0 for v1).
version: count;
## The ID of the log issuing this SCT.
logid: string;
## The timestamp at which this SCT was issued measured since the
## epoch (January 1, 1970, 00:00), ignoring leap seconds, in
## milliseconds. Not converted to a Bro timestamp because we need
## the exact value for validation.
timestamp: count;
## The signature algorithm used for this sct.
sig_alg: count;
## The hash algorithm used for this sct.
hash_alg: count;
## The signature of this SCT.
signature: string;
## Source of this SCT.
source: SctSource;
## Validation result of this SCT.
valid: bool &optional;
};
redef record Info += {
## Number of valid SCTs that were encountered in the connection.
valid_scts: count &optional;
## Number of SCTs that could not be validated that were encountered in the connection.
invalid_scts: count &optional;
## Number of different Logs for which valid SCTs were encountered in the connection.
valid_ct_logs: count &log &optional;
## Number of different Log operators of which valid SCTs were encountered in the connection.
valid_ct_operators: count &log &optional;
## List of operators for which valid SCTs were encountered in the connection.
valid_ct_operators_list: set[string] &optional;
## Information about all SCTs that were encountered in the connection.
ct_proofs: vector of SctInfo &default=vector();
};
}
# Used to cache validations for 5 minutes to lessen computational load.
global recently_validated_scts: table[string] of bool = table()
&read_expire=5mins &redef;
event bro_init()
{
Files::register_for_mime_type(Files::ANALYZER_OCSP_REPLY, "application/ocsp-response");
}
event ssl_extension_signed_certificate_timestamp(c: connection, is_orig: bool, version: count, logid: string, timestamp: count, signature_and_hashalgorithm: SSL::SignatureAndHashAlgorithm, signature: string) &priority=5
{
c$ssl$ct_proofs += SctInfo($version=version, $logid=logid, $timestamp=timestamp, $sig_alg=signature_and_hashalgorithm$SignatureAlgorithm, $hash_alg=signature_and_hashalgorithm$HashAlgorithm, $signature=signature, $source=SCT_TLS_EXT);
}
event x509_ocsp_ext_signed_certificate_timestamp(f: fa_file, version: count, logid: string, timestamp: count, hash_algorithm: count, signature_algorithm: count, signature: string) &priority=5
{
local src: SctSource;
if ( ! f?$info )
return;
if ( f$source == "SSL" && f$info$mime_type == "application/ocsp-response" )
src = SCT_OCSP_EXT;
else if ( f$source == "SSL" && f$info$mime_type == "application/x-x509-user-cert" )
src = SCT_X509_EXT;
else
return;
if ( |f$conns| != 1 )
return;
for ( cid in f$conns )
{
if ( ! f$conns[cid]?$ssl )
return;
local c = f$conns[cid];
}
c$ssl$ct_proofs += SctInfo($version=version, $logid=logid, $timestamp=timestamp, $sig_alg=signature_algorithm, $hash_alg=hash_algorithm, $signature=signature, $source=src);
}
# Priority = 19 will be handled after validation is done
hook ssl_finishing(c: connection) &priority=19
{
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;
local certhash = c$ssl$cert_chain[0]$sha1;
local issuer_name_hash = x509_issuer_name_hash(cert, 4);
local valid_proofs = 0;
local invalid_proofs = 0;
c$ssl$valid_ct_operators_list = string_set();
local valid_logs = string_set();
local issuer_key_hash = "";
for ( i in c$ssl$ct_proofs )
{
local proof = c$ssl$ct_proofs[i];
if ( proof$logid !in SSL::ct_logs )
{
# Well, if we don't know the log, there is nothing to do here...
proof$valid = F;
next;
}
local log = SSL::ct_logs[proof$logid];
local valid = F;
local found_cache = F;
local validatestring = cat(certhash,proof$logid,proof$timestamp,proof$hash_alg,proof$signature,proof$source);
if ( proof$source == SCT_X509_EXT && c$ssl?$validation_code )
validatestring = cat(validatestring, c$ssl$validation_code);
local validate_hash = sha1_hash(validatestring);
if ( validate_hash in recently_validated_scts )
{
valid = recently_validated_scts[validate_hash];
found_cache = T;
}
if ( found_cache == F && ( proof$source == SCT_TLS_EXT || proof$source == SCT_OCSP_EXT ) )
{
valid = sct_verify(cert, proof$logid, log$key, proof$signature, proof$timestamp, proof$hash_alg);
}
else if ( found_cache == F )
{
# X.509 proof. Here things get awkward because we need information about
# the issuer cert... and we need to try a few times, because we have to see if we got
# the right issuer cert.
#
# First - Let's try if a previous round already established the correct issuer key hash.
if ( issuer_key_hash != "" )
{
valid = sct_verify(cert, proof$logid, log$key, proof$signature, proof$timestamp, proof$hash_alg, issuer_key_hash);
}
# Second - let's see if we might already know the issuer cert through verification.
if ( ! valid && issuer_name_hash in intermediate_cache )
{
issuer_key_hash = x509_spki_hash(intermediate_cache[issuer_name_hash][0], 4);
valid = sct_verify(cert, proof$logid, log$key, proof$signature, proof$timestamp, proof$hash_alg, issuer_key_hash);
}
if ( ! valid && c$ssl?$valid_chain && |c$ssl$valid_chain| >= 2 )
{
issuer_key_hash = x509_spki_hash(c$ssl$valid_chain[1], 4);
valid = sct_verify(cert, proof$logid, log$key, proof$signature, proof$timestamp, proof$hash_alg, issuer_key_hash);
}
# ok, if it still did not work - let's just try with all the certs that were sent
# in the connection. Perhaps it will work with one of them.
if ( !valid )
for ( i in c$ssl$cert_chain )
{
if ( i == 0 ) # end-host-cert
next;
if ( ! c$ssl$cert_chain[i]?$x509 || ! c$ssl$cert_chain[i]$x509?$handle )
next;
issuer_key_hash = x509_spki_hash(c$ssl$cert_chain[i]$x509$handle, 4);
valid = sct_verify(cert, proof$logid, log$key, proof$signature, proof$timestamp, proof$hash_alg, issuer_key_hash);
if ( valid )
break;
}
}
if ( ! found_cache )
recently_validated_scts[validate_hash] = valid;
proof$valid = valid;
if ( valid )
{
++valid_proofs;
add c$ssl$valid_ct_operators_list[log$operator];
add valid_logs[proof$logid];
}
else
++invalid_proofs;
}
c$ssl$valid_scts = valid_proofs;
c$ssl$invalid_scts = invalid_proofs;
c$ssl$valid_ct_operators = |c$ssl$valid_ct_operators_list|;
c$ssl$valid_ct_logs = |valid_logs|;
}