zeek/src/file_analysis/analyzer/x509/functions.bif
Johanna Amann 115a676d08 SCT: Allow verification of SCTs in Certs.
This is much more complex than the TLS Extension/OCSP cases. We need to
first alter the certificate and remove the extension from it, before
extracting the tbscert. Furthermore, we need the key hash of the issuing
certificate to be able to validate the proof - which means that we need
a valid certificate chain.

Missing: documentation, nice integration so that we can just add a
script and use this in Bro.
2017-03-29 09:17:24 -07:00

810 lines
23 KiB
Text

%%{
#include "file_analysis/analyzer/x509/X509.h"
#include "types.bif.h"
#include "net_util.h"
#include <openssl/x509.h>
#include <openssl/asn1.h>
#include <openssl/x509_vfy.h>
#include <openssl/ocsp.h>
#include <openssl/pem.h>
#include <openssl/err.h>
// This is the indexed map of X509 certificate stores.
static map<Val*, X509_STORE*> x509_stores;
// ### NOTE: while d2i_X509 does not take a const u_char** pointer,
// here we assume d2i_X509 does not write to <data>, so it is safe to
// convert data to a non-const pointer. Could some X509 guru verify
// this?
X509* d2i_X509_(X509** px, const u_char** in, int len)
{
#ifdef OPENSSL_D2I_X509_USES_CONST_CHAR
return d2i_X509(px, in, len);
#else
return d2i_X509(px, (u_char**)in, len);
#endif
}
// construct an error record
RecordVal* x509_result_record(uint64_t num, const char* reason, Val* chainVector = 0)
{
RecordVal* rrecord = new RecordVal(BifType::Record::X509::Result);
rrecord->Assign(0, new Val(num, TYPE_INT));
rrecord->Assign(1, new StringVal(reason));
if ( chainVector )
rrecord->Assign(2, chainVector);
return rrecord;
}
X509_STORE* x509_get_root_store(TableVal* root_certs)
{
// If this certificate store was built previously, just reuse the old one.
if ( x509_stores.count(root_certs) > 0 )
return x509_stores[root_certs];
X509_STORE* ctx = X509_STORE_new();
ListVal* idxs = root_certs->ConvertToPureList();
// Build the validation store
for ( int i = 0; i < idxs->Length(); ++i )
{
Val* key = idxs->Index(i);
StringVal *sv = root_certs->Lookup(key)->AsStringVal();
assert(sv);
const uint8* data = sv->Bytes();
X509* x = d2i_X509_(NULL, &data, sv->Len());
if ( ! x )
{
builtin_error(fmt("Root CA error: %s", ERR_error_string(ERR_get_error(),NULL)));
return 0;
}
X509_STORE_add_cert(ctx, x);
X509_free(x);
}
delete idxs;
// Save the newly constructed certificate store into the cacheing map.
x509_stores[root_certs] = ctx;
return ctx;
}
// get all cretificates starting at the second one (assuming the first one is the host certificate)
STACK_OF(X509)* x509_get_untrusted_stack(VectorVal* certs_vec)
{
STACK_OF(X509)* untrusted_certs = sk_X509_new_null();
if ( ! untrusted_certs )
{
builtin_error(fmt("Untrusted certificate stack initialization error: %s", ERR_error_string(ERR_get_error(),NULL)));
return 0;
}
for ( int i = 1; i < (int) certs_vec->Size(); ++i ) // start at 1 - 0 is host cert
{
Val *sv = certs_vec->Lookup(i);
if ( ! sv )
continue;
// Fixme: check type
X509* x = ((file_analysis::X509Val*) sv)->GetCertificate();
if ( ! x )
{
sk_X509_free(untrusted_certs);
builtin_error(fmt("No certificate in opaque in stack"));
return 0;
}
sk_X509_push(untrusted_certs, x);
}
return untrusted_certs;
}
// We need this function to be able to identify the signer certificate of an
// OCSP request out of a list of possible certificates.
X509* x509_get_ocsp_signer(STACK_OF(X509) *certs, OCSP_RESPID *rid)
{
// We support two lookup types - either by response id or by key.
if ( rid->type == V_OCSP_RESPID_NAME )
return X509_find_by_subject(certs, rid->value.byName);
// There only should be name and type - but let's be sure...
if ( rid->type != V_OCSP_RESPID_KEY )
return 0;
// Just like OpenSSL, we just support SHA-1 lookups and bail out otherwhise.
if ( rid->value.byKey->length != SHA_DIGEST_LENGTH )
return 0;
unsigned char* key_hash = rid->value.byKey->data;
for ( int i = 0; i < sk_X509_num(certs); ++i )
{
unsigned char digest[SHA_DIGEST_LENGTH];
X509* cert = sk_X509_value(certs, i);
if ( ! X509_pubkey_digest(cert, EVP_sha1(), digest, NULL) )
// digest failed for this certificate, try with next
continue;
if ( memcmp(digest, key_hash, SHA_DIGEST_LENGTH) == 0 )
// keys match, return certificate
return cert;
}
return 0;
}
const EVP_MD* hash_to_evp(int hash)
{
switch ( hash )
{
case 1:
return EVP_md5();
break;
case 2:
return EVP_sha1();
break;
case 3:
return EVP_sha224();
break;
case 4:
return EVP_sha256();
break;
case 5:
return EVP_sha384();
break;
case 6:
return EVP_sha512();
break;
default:
return nullptr;
}
}
%%}
## Parses a certificate into an X509::Certificate structure.
##
## cert: The X509 certificate opaque handle.
##
## Returns: A X509::Certificate structure.
##
## .. bro:see:: x509_certificate x509_extension x509_ext_basic_constraints
## x509_ext_subject_alternative_name x509_verify
## x509_get_certificate_string
function x509_parse%(cert: opaque of x509%): X509::Certificate
%{
assert(cert);
file_analysis::X509Val* h = (file_analysis::X509Val*) cert;
return file_analysis::X509::ParseCertificate(h);
%}
## Returns the string form of a certificate.
##
## cert: The X509 certificate opaque handle.
##
## pem: A boolean that specifies if the certificate is returned
## in pem-form (true), or as the raw ASN1 encoded binary
## (false).
##
## Returns: X509 certificate as a string.
##
## .. bro:see:: x509_certificate x509_extension x509_ext_basic_constraints
## x509_ext_subject_alternative_name x509_parse x509_verify
function x509_get_certificate_string%(cert: opaque of x509, pem: bool &default=F%): string
%{
assert(cert);
file_analysis::X509Val* h = (file_analysis::X509Val*) cert;
BIO *bio = BIO_new(BIO_s_mem());
if ( pem )
PEM_write_bio_X509(bio, h->GetCertificate());
else
i2d_X509_bio(bio, h->GetCertificate());
StringVal* ext_val = file_analysis::X509::GetExtensionFromBIO(bio);
if ( ! ext_val )
ext_val = new StringVal("");
return ext_val;
%}
## Verifies an OCSP reply.
##
## certs: Specifies the certificate chain to use. Server certificate first.
##
## ocsp_reply: the ocsp reply to validate.
##
## root_certs: A list of root certificates to validate the certificate chain.
##
## verify_time: Time for the validity check of the certificates.
##
## Returns: A record of type X509::Result containing the result code of the
## verify operation.
##
## .. bro:see:: x509_certificate x509_extension x509_ext_basic_constraints
## x509_ext_subject_alternative_name x509_parse
## x509_get_certificate_string x509_verify
function x509_ocsp_verify%(certs: x509_opaque_vector, ocsp_reply: string, root_certs: table_string_of_string, verify_time: time &default=network_time()%): X509::Result
%{
RecordVal* rval = 0;
X509_STORE* ctx = x509_get_root_store(root_certs->AsTableVal());
if ( ! ctx )
return x509_result_record(-1, "Problem initializing root store");
VectorVal *certs_vec = certs->AsVectorVal();
if ( certs_vec->Size() < 1 )
{
reporter->Error("No certificates given in vector");
return x509_result_record(-1, "no certificates");
}
// host certificate
unsigned int index = 0; // to prevent overloading to 0pointer
Val *sv = certs_vec->Lookup(index);
if ( ! sv )
{
builtin_error("undefined value in certificate vector");
return x509_result_record(-1, "undefined value in certificate vector");
}
file_analysis::X509Val* cert_handle = (file_analysis::X509Val*) sv;
X509* cert = cert_handle->GetCertificate();
if ( ! cert )
{
builtin_error(fmt("No certificate in opaque"));
return x509_result_record(-1, "No certificate in opaque");
}
const unsigned char* start = ocsp_reply->Bytes();
STACK_OF(X509)* untrusted_certs = x509_get_untrusted_stack(certs_vec);
if ( ! untrusted_certs )
return x509_result_record(-1, "Problem initializing list of untrusted certificates");
// from here, always goto cleanup. Initialize all other required variables...
time_t vtime = (time_t) verify_time;
OCSP_BASICRESP *basic = 0;
OCSP_SINGLERESP *single = 0;
X509_STORE_CTX *csc = 0;
OCSP_CERTID *certid = 0;
int status = -1;
int out = -1;
int result = -1;
X509* issuer_certificate = 0;
X509* signer = 0;
OCSP_RESPONSE *resp = d2i_OCSP_RESPONSE(NULL, &start, ocsp_reply->Len());
if ( ! resp )
{
rval = x509_result_record(-1, "Could not parse OCSP response");
goto x509_ocsp_cleanup;
}
status = OCSP_response_status(resp);
if ( status != OCSP_RESPONSE_STATUS_SUCCESSFUL )
{
rval = x509_result_record(-2, OCSP_response_status_str(status));
goto x509_ocsp_cleanup;
}
basic = OCSP_response_get1_basic(resp);
if ( ! basic )
{
rval = x509_result_record(-1, "Could not parse OCSP response");
goto x509_ocsp_cleanup;
}
// the following code took me _forever_ to get right.
// The OCSP_basic_verify command takes a list of certificates. However (which is not immediately
// visible or understandable), those are only used to find the signer certificate. They are _not_
// used for chain building during the actual verification (this would be stupid). But - if we sneakily
// inject the certificates in the certificate list of the OCSP reply, they actually are used during
// the lookup.
// Yay.
if ( ! basic->certs )
{
basic->certs = sk_X509_new_null();
if ( ! basic->certs )
{
rval = x509_result_record(-1, "Could not allocate basic x509 stack");
goto x509_ocsp_cleanup;
}
}
issuer_certificate = 0;
for ( int i = 0; i < sk_X509_num(untrusted_certs); i++)
{
sk_X509_push(basic->certs, X509_dup(sk_X509_value(untrusted_certs, i)));
if ( X509_NAME_cmp(X509_get_issuer_name(cert), X509_get_subject_name(sk_X509_value(untrusted_certs, i))) == 0 )
issuer_certificate = sk_X509_value(untrusted_certs, i);
}
// Because we actually want to be able to give nice error messages that show why we were
// not able to verify the OCSP response - do our own verification logic first.
signer = x509_get_ocsp_signer(basic->certs, basic->tbsResponseData->responderId);
/*
Do this perhaps - OpenSSL also cannot do it, so I do not really feel bad about it.
Needs a different lookup because the root store is no stack of X509 certs
if ( !s igner )
// if we did not find it in the certificates that were sent, search in the root store
signer = x509_get_ocsp_signer(basic->certs, basic->tbsResponseData->responderId);
*/
if ( ! signer )
{
rval = x509_result_record(-1, "Could not find OCSP responder certificate");
goto x509_ocsp_cleanup;
}
csc = X509_STORE_CTX_new();
X509_STORE_CTX_init(csc, ctx, signer, basic->certs);
X509_STORE_CTX_set_time(csc, 0, (time_t) verify_time);
X509_STORE_CTX_set_purpose(csc, X509_PURPOSE_OCSP_HELPER);
result = X509_verify_cert(csc);
if ( result != 1 )
{
const char *reason = X509_verify_cert_error_string((*csc).error);
rval = x509_result_record(result, X509_verify_cert_error_string((*csc).error));
goto x509_ocsp_cleanup;
}
// We pass OCSP_NOVERIFY to let OCSP_basic_verify skip the chain verification.
// With that, it only verifies the signature of the basic response and we are responsible
// for the chain ourselves. We have to do that since we cannot get OCSP_basic_verify to use our timestamp.
out = OCSP_basic_verify(basic, NULL, ctx, OCSP_NOVERIFY);
if ( out < 1 )
{
rval = x509_result_record(out, ERR_error_string(ERR_get_error(),NULL));
goto x509_ocsp_cleanup;
}
// ok, now we verified the OCSP response. This means that we have a valid chain tying it
// to a root that we trust and that the signature also hopefully is valid. This does not yet
// mean that the ocsp response actually matches the certificate the server send us or that
// the OCSP response even says that the certificate is valid.
// let's start this out by checking that the response is actually for the certificate we want
// to validate and not for something completely unrelated that the server is trying to trick us
// into accepting.
if ( issuer_certificate )
certid = OCSP_cert_to_id(NULL, cert, issuer_certificate);
else
{
// issuer not in list sent by server, check store
X509_OBJECT obj;
int lookup = X509_STORE_get_by_subject(csc, X509_LU_X509, X509_get_subject_name(cert), &obj);
if ( lookup <= 0)
{
rval = x509_result_record(lookup, "Could not find issuer of host certificate");
goto x509_ocsp_cleanup;
}
certid = OCSP_cert_to_id(NULL, cert, obj.data.x509);
}
if ( ! certid )
{
rval = x509_result_record(-1, "Certificate ID construction failed");
goto x509_ocsp_cleanup;
}
// for now, assume we have one reply...
single = sk_OCSP_SINGLERESP_value(basic->tbsResponseData->responses, 0);
if ( ! single )
{
rval = x509_result_record(-1, "Could not lookup OCSP response information");
goto x509_ocsp_cleanup;
}
if ( OCSP_id_cmp(certid, single->certId) != 0 )
return x509_result_record(-1, "OCSP reply is not for host certificate");
// next - check freshness of proof...
if ( ! ASN1_GENERALIZEDTIME_check(single->thisUpdate) || ! ASN1_GENERALIZEDTIME_check(single->nextUpdate) )
{
rval = x509_result_record(-1, "OCSP reply contains invalid dates");
goto x509_ocsp_cleanup;
}
// now - nearly done. Check freshness and status code.
// There is a function to check the freshness of the ocsp reply in the ocsp code of OpenSSL. But - it only
// supports comparing it against the current time, not against arbitrary times. Hence it is kind of unusable
// for us...
// Well, we will do it manually.
if ( X509_cmp_time(single->thisUpdate, &vtime) > 0 )
rval = x509_result_record(-1, "OCSP reply specifies time in future");
else if ( X509_cmp_time(single->nextUpdate, &vtime) < 0 )
rval = x509_result_record(-1, "OCSP reply expired");
else if ( single->certStatus->type != V_OCSP_CERTSTATUS_GOOD )
rval = x509_result_record(-1, OCSP_cert_status_str(single->certStatus->type));
// if we have no error so far, we are done.
if ( !rval )
rval = x509_result_record(1, OCSP_cert_status_str(single->certStatus->type));
x509_ocsp_cleanup:
if ( untrusted_certs )
sk_X509_free(untrusted_certs);
if ( resp )
OCSP_RESPONSE_free(resp);
if ( basic )
OCSP_BASICRESP_free(basic);
if ( csc )
{
X509_STORE_CTX_cleanup(csc);
X509_STORE_CTX_free(csc);
}
if ( certid )
OCSP_CERTID_free(certid);
return rval;
%}
## Verifies a certificate.
##
## certs: Specifies a certificate chain that is being used to validate
## the given certificate against the root store given in *root_certs*.
## The host certificate has to be at index 0.
##
## root_certs: A list of root certificates to validate the certificate chain.
##
## verify_time: Time for the validity check of the certificates.
##
## Returns: A record of type X509::Result containing the result code of the
## verify operation. In case of success also returns the full
## certificate chain.
##
## .. bro:see:: x509_certificate x509_extension x509_ext_basic_constraints
## x509_ext_subject_alternative_name x509_parse
## x509_get_certificate_string x509_ocsp_verify
function x509_verify%(certs: x509_opaque_vector, root_certs: table_string_of_string, verify_time: time &default=network_time()%): X509::Result
%{
X509_STORE* ctx = x509_get_root_store(root_certs->AsTableVal());
if ( ! ctx )
return x509_result_record(-1, "Problem initializing root store");
VectorVal *certs_vec = certs->AsVectorVal();
if ( ! certs_vec || certs_vec->Size() < 1 )
{
reporter->Error("No certificates given in vector");
return x509_result_record(-1, "no certificates");
}
// host certificate
unsigned int index = 0; // to prevent overloading to 0pointer
Val *sv = certs_vec->Lookup(index);
if ( !sv )
{
builtin_error("undefined value in certificate vector");
return x509_result_record(-1, "undefined value in certificate vector");
}
file_analysis::X509Val* cert_handle = (file_analysis::X509Val*) sv;
X509* cert = cert_handle->GetCertificate();
if ( ! cert )
{
builtin_error(fmt("No certificate in opaque"));
return x509_result_record(-1, "No certificate in opaque");
}
STACK_OF(X509)* untrusted_certs = x509_get_untrusted_stack(certs_vec);
if ( ! untrusted_certs )
return x509_result_record(-1, "Problem initializing list of untrusted certificates");
X509_STORE_CTX csc;
X509_STORE_CTX_init(&csc, ctx, cert, untrusted_certs);
X509_STORE_CTX_set_time(&csc, 0, (time_t) verify_time);
X509_STORE_CTX_set_flags(&csc, X509_V_FLAG_USE_CHECK_TIME);
int result = X509_verify_cert(&csc);
VectorVal* chainVector = 0;
if ( result == 1 ) // we have a valid chain. try to get it...
{
STACK_OF(X509)* chain = X509_STORE_CTX_get1_chain(&csc); // get1 = deep copy
if ( ! chain )
{
reporter->Error("Encountered valid chain that could not be resolved");
sk_X509_pop_free(chain, X509_free);
goto x509_verify_chainerror;
}
int num_certs = sk_X509_num(chain);
chainVector = new VectorVal(internal_type("x509_opaque_vector")->AsVectorType());
for ( int i = 0; i < num_certs; i++ )
{
X509* currcert = sk_X509_value(chain, i);
if ( currcert )
// X509Val takes ownership of currcert.
chainVector->Assign(i, new file_analysis::X509Val(currcert));
else
{
reporter->InternalWarning("OpenSSL returned null certificate");
sk_X509_pop_free(chain, X509_free);
goto x509_verify_chainerror;
}
}
sk_X509_free(chain);
}
x509_verify_chainerror:
X509_STORE_CTX_cleanup(&csc);
sk_X509_free(untrusted_certs);
RecordVal* rrecord = x509_result_record(csc.error, X509_verify_cert_error_string(csc.error), chainVector);
return rrecord;
%}
function sct_verify%(cert: opaque of x509, logid: string, log_key: string, signature: string, timestamp: count, hash_algorithm: count, issuer_key_hash: string &default=""%): 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);
bool precert = issuer_key_hash->Len() > 0;
if ( precert && issuer_key_hash->Len() != 32)
{
reporter->Error("Invalid issuer_key_hash length");
return new Val(0, TYPE_BOOL);
}
std::string data;
data.push_back(0); // version
data.push_back(0); // signature_type -> certificate_timestamp
data.append(reinterpret_cast<const char*>(&timestamp_network), sizeof(timestamp_network)); // timestamp -> 64 bits
if ( precert )
data.append("\0\1", 2); // entry-type: precert_entry
else
data.append("\0\0", 2); // entry-type: x509_entry
if ( precert )
{
x = X509_dup(x);
assert(x);
#ifdef NID_ct_precert_scts
int pos = X509_get_ext_by_NID(x, NID_ct_precert_scts, -1);
if ( pos < 0 )
{
reporter->Error("NID_ct_precert_scts not found");
return new Val(0, TYPE_BOOL);
}
#else
int num_ext = X509_get_ext_count(x);
int pos = -1;
for ( int k = 0; k < num_ext; ++k )
{
char oid[256];
X509_EXTENSION* ex = X509_get_ext(x, k);
ASN1_OBJECT* ext_asn = X509_EXTENSION_get_object(ex);
OBJ_obj2txt(oid, 255, ext_asn, 1);
if ( strcmp(oid, "1.3.6.1.4.1.11129.2.4.2") == 0 )
{
pos = k;
break;
}
}
#endif
X509_EXTENSION_free(X509_delete_ext(x, pos));
assert( X509_get_ext_by_NID(x, NID_ct_precert_scts, -1) == -1 );
}
unsigned char *cert_out = nullptr;
uint32 cert_length;
if ( precert )
{
// we also could use i2d_re_X509_tbs, for OpenSSL >= 1.0.2
x->cert_info->enc.modified = 1;
cert_length = i2d_X509_CINF(x->cert_info, &cert_out);
data.append(reinterpret_cast<const char*>(issuer_key_hash->Bytes()), issuer_key_hash->Len());
}
else
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<const char*>(&cert_length_network)+1, 3); // 3 bytes certificate length
data.append(reinterpret_cast<const char*>(cert_out), cert_length); // der-encoded certificate
OPENSSL_free(cert_out);
if ( precert )
X509_free(x);
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 = hash_to_evp(hash_algorithm);
if ( ! hash )
{
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);
%}
%%{
/**
* 0 -> subject name
* 1 -> issuer name
* 2 -> pubkey
*/
StringVal* x509_entity_hash(file_analysis::X509Val *cert_handle, unsigned int hash_alg, unsigned int type)
{
assert(cert_handle);
if ( type > 2 )
{
reporter->InternalError("Unknown type in x509_entity_hash");
return nullptr;
}
X509 *cert_x509 = cert_handle->GetCertificate();
if ( cert_x509 == nullptr )
{
builtin_error("cannot get cert from opaque");
return nullptr;
}
X509_NAME *subject_name = X509_get_subject_name(cert_x509);
X509_NAME *issuer_name = X509_get_issuer_name(cert_x509);
if ( subject_name == nullptr || issuer_name == nullptr )
{
builtin_error("fail to get subject/issuer name from certificate");
return nullptr;
}
const EVP_MD *dgst = hash_to_evp(hash_alg);
if ( dgst == nullptr )
{
builtin_error("Unknown hash algorithm.");
return nullptr;
}
unsigned char md[EVP_MAX_MD_SIZE];
memset(md, 0, sizeof(md));
unsigned int len = 0;
int res = 0;
ASN1_BIT_STRING *key = X509_get0_pubkey_bitstr(cert_x509);
if ( key == 0 )
{
printf("No key in X509_get0_pubkey_bitstr\n");
}
if ( type == 0 )
res = X509_NAME_digest(subject_name, dgst, md, &len);
else if ( type == 1 )
res = X509_NAME_digest(issuer_name, dgst, md, &len);
else if ( type == 2 )
{
unsigned char *spki = nullptr;
int pklen = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert_x509), &spki);
if ( ! pklen )
{
builtin_error("Could not get SPKI");
return nullptr;
}
res = EVP_Digest(spki, pklen, md, &len, dgst, nullptr);
OPENSSL_free(spki);
}
if ( ! res )
{
builtin_error("Could not perform hash");
return nullptr;
}
assert( len <= sizeof(md) );
return new StringVal(len, reinterpret_cast<const char*>(md));
}
%%}
function x509_subject_name_hash%(cert: opaque of x509, hash_alg: count%): string
%{
file_analysis::X509Val *cert_handle = (file_analysis::X509Val *) cert;
return x509_entity_hash(cert_handle, hash_alg, 0);
%}
## Get the hash of issuer name of a certificate
##
## cert: The X509 certificate opaque handle.
##
## hash_alg: the hash algorithm to use
##
## Returns: A string of hash of issuer name.
##
## .. bro:see:: x509_certificate x509_extension x509_ext_basic_constraints
## x509_ext_subject_alternative_name x509_parse
## x509_get_certificate_string x509_verify
function x509_issuer_name_hash%(cert: opaque of x509, hash_alg: count%): string
%{
file_analysis::X509Val *cert_handle = (file_analysis::X509Val *) cert;
return x509_entity_hash(cert_handle, hash_alg, 1);
%}
function x509_spki_hash%(cert: opaque of x509, hash_alg: count%): string
%{
file_analysis::X509Val *cert_handle = (file_analysis::X509Val *) cert;
return x509_entity_hash(cert_handle, hash_alg, 2);
%}