SCT: add validation of proofs for extensions and OCSP.

This does not yet work for certificates, because this requires some
changing the ASN.1 structure before validation (we need to extract the
tbscert and remove the SCT extension before).

API will change in the future.
This commit is contained in:
Johanna Amann 2017-03-17 11:40:49 -07:00
parent d50bddfbfb
commit c403a7f4e6
6 changed files with 171 additions and 0 deletions

View file

@ -1,6 +1,7 @@
%%{
#include "analyzer/protocol/ssl/SSL.h"
#include <openssl/x509.h>
%%}
## Sets if the SSL analyzer should consider the connection established (handshake

View file

@ -1,6 +1,7 @@
%%{
#include "file_analysis/analyzer/x509/X509.h"
#include "types.bif.h"
#include "net_util.h"
#include <openssl/x509.h>
#include <openssl/asn1.h>
@ -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<char*>(&timestamp_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<char*>(&cert_length_network)+1, 3); // 3 bytes certificate length
data.append(reinterpret_cast<char*>(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.

View file

@ -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

View file

@ -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

View file

@ -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);
}

View file

@ -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);
}
}