mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
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:
parent
d50bddfbfb
commit
c403a7f4e6
6 changed files with 171 additions and 0 deletions
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
%%{
|
%%{
|
||||||
#include "analyzer/protocol/ssl/SSL.h"
|
#include "analyzer/protocol/ssl/SSL.h"
|
||||||
|
#include <openssl/x509.h>
|
||||||
%%}
|
%%}
|
||||||
|
|
||||||
## Sets if the SSL analyzer should consider the connection established (handshake
|
## Sets if the SSL analyzer should consider the connection established (handshake
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
%%{
|
%%{
|
||||||
#include "file_analysis/analyzer/x509/X509.h"
|
#include "file_analysis/analyzer/x509/X509.h"
|
||||||
#include "types.bif.h"
|
#include "types.bif.h"
|
||||||
|
#include "net_util.h"
|
||||||
|
|
||||||
#include <openssl/x509.h>
|
#include <openssl/x509.h>
|
||||||
#include <openssl/asn1.h>
|
#include <openssl/asn1.h>
|
||||||
|
@ -543,6 +544,100 @@ x509_verify_chainerror:
|
||||||
return rrecord;
|
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*>(×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<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
|
## Get the hash of issuer name of a certificate
|
||||||
##
|
##
|
||||||
## cert: The X509 certificate opaque handle.
|
## cert: The X509 certificate opaque handle.
|
||||||
|
|
|
@ -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
|
|
@ -1,3 +1,9 @@
|
||||||
0, Google 'Pilot' log, 1406997753.366, [HashAlgorithm=4, SignatureAlgorithm=3]
|
0, Google 'Pilot' log, 1406997753.366, [HashAlgorithm=4, SignatureAlgorithm=3]
|
||||||
0, Google 'Aviator' log, 1407002457.456, [HashAlgorithm=4, SignatureAlgorithm=3]
|
0, Google 'Aviator' log, 1407002457.456, [HashAlgorithm=4, SignatureAlgorithm=3]
|
||||||
0, Google 'Rocketeer' log, 1410299366.023, [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
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -1,7 +1,39 @@
|
||||||
# @TEST-EXEC: bro -r $TRACES/tls/signed_certificate_timestamp.pcap %INPUT
|
# @TEST-EXEC: bro -r $TRACES/tls/signed_certificate_timestamp.pcap %INPUT
|
||||||
# @TEST-EXEC: btest-diff .stdout
|
# @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)
|
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;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue