root/lib/libsecureboot/vets.c
/*-
 * Copyright (c) 2017-2018, Juniper Networks, Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include <sys/cdefs.h>
/**
 * @file vets.c - trust store
 * @brief verify signatures
 *
 * We leverage code from BearSSL www.bearssl.org
 */

#include <sys/time.h>
#include <stdarg.h>
#define NEED_BRSSL_H
#include "libsecureboot-priv.h"
#include <brssl.h>
#include <ta.h>

#ifndef TRUST_ANCHOR_STR
# define TRUST_ANCHOR_STR ta_PEM
#endif

#define EPOCH_YEAR              1970
#define AVG_SECONDS_PER_YEAR    31556952L
#define SECONDS_PER_DAY         86400
#define SECONDS_PER_YEAR        365 * SECONDS_PER_DAY
#ifndef VE_UTC_MAX_JUMP
# define VE_UTC_MAX_JUMP        20 * SECONDS_PER_YEAR
#endif
#define X509_DAYS_TO_UTC0       719528

int DebugVe = 0;

#ifndef VE_VERIFY_FLAGS
# define VE_VERIFY_FLAGS VEF_VERBOSE
#endif
int VerifyFlags = VE_VERIFY_FLAGS;

typedef VECTOR(br_x509_certificate) cert_list;
typedef VECTOR(hash_data) digest_list;

static anchor_list trust_anchors = VEC_INIT;
static anchor_list forbidden_anchors = VEC_INIT;
static digest_list forbidden_digests = VEC_INIT;

static int anchor_verbose = 0;

void
ve_anchor_verbose_set(int n)
{
        anchor_verbose = n;
}

int
ve_anchor_verbose_get(void)
{
        return (anchor_verbose);
}

void
ve_debug_set(int n)
{
        DebugVe = n;
}

/*
 * For embedded systems (and boot loaders)
 * we do not want to enforce certificate validity post install.
 * It is generally unacceptible for infrastructure to stop working
 * just because it has not been updated recently.
 */
static int enforce_validity = 0;

void
ve_enforce_validity_set(int i)
{
    enforce_validity = i;
}

static char ebuf[512];

char *
ve_error_get(void)
{
        return (ebuf);
}

int
ve_error_set(const char *fmt, ...)
{
        int rc;
        va_list ap;

        va_start(ap, fmt);
        ebuf[0] = '\0';
        rc = 0;
        if (fmt) {
#ifdef STAND_H
                vsprintf(ebuf, fmt, ap); /* no vsnprintf in libstand */
                ebuf[sizeof(ebuf) - 1] = '\0';
                rc = strlen(ebuf);
#else
                rc = vsnprintf(ebuf, sizeof(ebuf), fmt, ap);
#endif
        }
        va_end(ap);
        return (rc);
}

#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))

/*
 * The *approximate* date.
 *
 * When certificate verification fails for being
 * expired or not yet valid, it helps to indicate
 * our current date.
 * Since libsa lacks strftime and gmtime,
 * this simple implementation suffices.
 */
static const char *
gdate(char *buf, size_t bufsz, time_t clock)
{
        int days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
        int year, y, m, d;

        y = clock / AVG_SECONDS_PER_YEAR;
        year = EPOCH_YEAR + y;
        for (y = EPOCH_YEAR; y < year; y++) {
                clock -= SECONDS_PER_YEAR;
                if (isleap(y))
                        clock -= SECONDS_PER_DAY;
        }
        d = clock / SECONDS_PER_DAY;
        for (m = 0; d > 1 && m < 12; m++) {
                if (d > days[m]) {
                        d -= days[m];
                        if (m == 1 && d > 0 && isleap(year))
                                d--;
                } else
                        break;
        }
        d++;
        if (d > days[m]) {
            d = 1;
            m++;
            if (m >= 12) {
                year++;
                m = 0;
            }
        }
        (void)snprintf(buf, bufsz, "%04d-%02d-%02d", year, m+1, d);
        return(buf);
}

/* this is the time we use for verifying certs */
#ifdef UNIT_TEST
extern time_t ve_utc;
time_t ve_utc = 0;
#else
static time_t ve_utc = 0;
#endif

/**
 * @brief
 * set ve_utc used for certificate verification
 *
 * @param[in] utc
 *      time - ignored unless greater than current value
 *      and not a leap of 20 years or more.
 */
void
ve_utc_set(time_t utc)
{
        if (utc > ve_utc &&
            (ve_utc == 0 || (utc - ve_utc) < VE_UTC_MAX_JUMP)) {
                DEBUG_PRINTF(2, ("Set ve_utc=%jd\n", (intmax_t)utc));
                ve_utc = utc;
        }
}

#ifdef VERIFY_CERTS_STR
static void
free_cert_contents(br_x509_certificate *xc)
{
        xfree(xc->data);
}
#endif

/*
 * a bit of a dance to get commonName from a certificate
 */
static char *
x509_cn_get(br_x509_certificate *xc, char *buf, size_t len)
{
        br_x509_minimal_context mc;
        br_name_element cn;
        unsigned char cn_oid[4];
        int err;

        if (buf == NULL)
                return (buf);
        /*
         * We want the commonName field
         * the OID we want is 2,5,4,3 - but DER encoded
         */
        cn_oid[0] = 3;
        cn_oid[1] = 0x55;
        cn_oid[2] = 4;
        cn_oid[3] = 3;
        cn.oid = cn_oid;
        cn.buf = buf;
        cn.len = len;
        cn.buf[0] = '\0';

        br_x509_minimal_init(&mc, &br_sha256_vtable, NULL, 0);
        br_x509_minimal_set_name_elements(&mc, &cn, 1);
        /* the below actually does the work - updates cn.status */
        mc.vtable->start_chain(&mc.vtable, NULL);
        mc.vtable->start_cert(&mc.vtable, xc->data_len);
        mc.vtable->append(&mc.vtable, xc->data, xc->data_len);
        mc.vtable->end_cert(&mc.vtable);
        /* we don't actually care about cert status - just its name */
        err = mc.vtable->end_chain(&mc.vtable);
        (void)err;                      /* keep compiler quiet */

        if (cn.status <= 0)
                buf = NULL;
        return (buf);
}

/* ASN parsing related defines */
#define ASN1_PRIMITIVE_TAG 0x1F
#define ASN1_INF_LENGTH    0x80
#define ASN1_LENGTH_MASK   0x7F

/*
 * Get TBS part of certificate.
 * Since BearSSL doesn't provide any API to do this,
 * it has to be implemented here.
 */
static void*
X509_to_tbs(unsigned char* cert, size_t* output_size)
{
        unsigned char *result;
        size_t tbs_size;
        int size, i;

        if (cert == NULL)
                return (NULL);

        /* Strip two sequences to get to the TBS section */
        for (i = 0; i < 2; i++) {
                /*
                 * XXX: We don't need to support extended tags since
                 * they should not be present in certificates.
                 */
                if ((*cert & ASN1_PRIMITIVE_TAG) == ASN1_PRIMITIVE_TAG)
                        return (NULL);

                cert++;

                if (*cert == ASN1_INF_LENGTH)
                        return (NULL);

                size = *cert & ASN1_LENGTH_MASK;
                tbs_size = 0;

                /* Size can either be stored on a single or multiple bytes */
                if (*cert & (ASN1_LENGTH_MASK + 1)) {
                        cert++;
                        while (*cert == 0 && size > 0) {
                                cert++;
                                size--;
                        }
                        while (size-- > 0) {
                                tbs_size <<= 8;
                                tbs_size |= *(cert++);
                        }
                }
                if (i == 0)
                        result = cert;
        }
        tbs_size += (cert - result);

        if (output_size != NULL)
                *output_size = tbs_size;

        return (result);
}

void
ve_forbidden_digest_add(hash_data *digest, size_t num)
{
        while (num--)
                VEC_ADD(forbidden_digests, digest[num]);
}

static size_t
ve_anchors_add(br_x509_certificate *xcs, size_t num, anchor_list *anchors,
    const char *anchors_name)
{
        br_x509_trust_anchor ta;
        size_t u;

        for (u = 0; u < num; u++) {
                if (certificate_to_trust_anchor_inner(&ta, &xcs[u]) < 0) {
                        break;
                }
                VEC_ADD(*anchors, ta);
                if (anchor_verbose && anchors_name) {
                        char buf[64];
                        char *cp;

                        cp = x509_cn_get(&xcs[u], buf, sizeof(buf));
                        if (cp) {
                                printf("x509_anchor(%s) %s\n", cp, anchors_name);
                        }
                }
        }
        return (u);
}

/**
 * @brief
 * add certs to our trust store
 */
size_t
ve_trust_anchors_add(br_x509_certificate *xcs, size_t num)
{
        return (ve_anchors_add(xcs, num, &trust_anchors, "trusted"));
}

size_t
ve_forbidden_anchors_add(br_x509_certificate *xcs, size_t num)
{
        return (ve_anchors_add(xcs, num, &forbidden_anchors, "forbidden"));
}


/**
 * @brief add trust anchors in buf
 *
 * Assume buf contains x509 certificates, but if not and
 * we support OpenPGP try adding as that.
 *
 * @return number of anchors added
 */
size_t
ve_trust_anchors_add_buf(unsigned char *buf, size_t len)
{
        br_x509_certificate *xcs;
        size_t num;

        num = 0;
        if (len > 0) {
                xcs = parse_certificates(buf, len, &num);
                if (xcs != NULL) {
                        num = ve_trust_anchors_add(xcs, num);
#ifdef VE_OPENPGP_SUPPORT
                } else {
                        num = openpgp_trust_add_buf(buf, len);
#endif
                }
        }
        return (num);
}

/**
 * @brief revoke trust anchors in buf
 *
 * Assume buf contains x509 certificates, but if not and
 * we support OpenPGP try revoking keyId
 *
 * @return number of anchors revoked
 */
size_t
ve_trust_anchors_revoke(unsigned char *buf, size_t len)
{
        br_x509_certificate *xcs;
        size_t num;

        num = 0;
        if (len > 0) {
                xcs = parse_certificates(buf, len, &num);
                if (xcs != NULL) {
                        num = ve_forbidden_anchors_add(xcs, num);
#ifdef VE_OPENPGP_SUPPORT
                } else {
                        if (buf[len - 1] == '\n')
                                buf[len - 1] = '\0';
                        num = openpgp_trust_revoke((char *)buf);
#endif
                }
        }
        return (num);
}

/**
 * @brief
 * initialize our trust_anchors from ta_PEM
 */
int
ve_trust_init(void)
{
        static int once = -1;

        if (once >= 0)
                return (once);
        once = 0;                       /* to be sure */
#ifdef BUILD_UTC
        ve_utc_set(BUILD_UTC);          /* ensure sanity */
#endif
        ve_utc_set(time(NULL));
        ve_error_set(NULL);             /* make sure it is empty */
#ifdef VE_PCR_SUPPORT
        ve_pcr_init();
#endif

#ifdef TRUST_ANCHOR_STR
        if (TRUST_ANCHOR_STR != NULL && strlen(TRUST_ANCHOR_STR) != 0ul)
                ve_trust_anchors_add_buf(__DECONST(unsigned char *,
                    TRUST_ANCHOR_STR), sizeof(TRUST_ANCHOR_STR));
#endif
        once = (int) VEC_LEN(trust_anchors);
#ifdef VE_OPENPGP_SUPPORT
        once += openpgp_trust_init();
#endif
        return (once);
}

#ifdef HAVE_BR_X509_TIME_CHECK
static int
verify_time_cb(void *tctx __unused,
    uint32_t not_before_days, uint32_t not_before_seconds,
    uint32_t not_after_days, uint32_t not_after_seconds)
{
        time_t not_before;
        time_t not_after;
        int rc;
#ifdef UNIT_TEST
        char date[12], nb_date[12], na_date[12];
#endif

        if (enforce_validity) {
                not_before = ((not_before_days - X509_DAYS_TO_UTC0) * SECONDS_PER_DAY) + not_before_seconds;
                not_after =  ((not_after_days - X509_DAYS_TO_UTC0) * SECONDS_PER_DAY) + not_after_seconds;
                if (ve_utc < not_before)
                        rc = -1;
                else if (ve_utc > not_after)
                        rc = 1;
                else
                        rc = 0;
#ifdef UNIT_TEST
                printf("notBefore %s notAfter %s date %s rc %d\n",
                    gdate(nb_date, sizeof(nb_date), not_before),
                    gdate(na_date, sizeof(na_date), not_after),
                    gdate(date, sizeof(date), ve_utc), rc);
#endif
        } else
                rc = 0;                 /* don't fail */
        return rc;
}
#endif

/**
 * if we can verify the certificate chain in "certs",
 * return the public key and if "xcp" is !NULL the associated
 * certificate
 */
static br_x509_pkey *
verify_signer_xcs(br_x509_certificate *xcs,
    size_t num,
    br_name_element *elts, size_t num_elts,
    anchor_list *anchors)
{
        br_x509_minimal_context mc;
        br_x509_certificate *xc;
        size_t u;
        cert_list chain = VEC_INIT;
        const br_x509_pkey *tpk;
        br_x509_pkey *pk;
        unsigned int usages;
        int err;

        DEBUG_PRINTF(5, ("verify_signer: %zu certs in chain\n", num));
        VEC_ADDMANY(chain, xcs, num);
        if (VEC_LEN(chain) == 0) {
                ve_error_set("ERROR: no/invalid certificate chain\n");
                return (NULL);
        }

        DEBUG_PRINTF(5, ("verify_signer: %zu trust anchors\n",
                VEC_LEN(*anchors)));

        br_x509_minimal_init(&mc, &br_sha256_vtable,
            &VEC_ELT(*anchors, 0),
            VEC_LEN(*anchors));
#ifdef VE_ECDSA_SUPPORT
        br_x509_minimal_set_ecdsa(&mc,
            &br_ec_prime_i31, &br_ecdsa_i31_vrfy_asn1);
#endif
#ifdef VE_RSA_SUPPORT
        br_x509_minimal_set_rsa(&mc, &br_rsa_i31_pkcs1_vrfy);
#endif
#if defined(UNIT_TEST) && defined(VE_DEPRECATED_RSA_SHA1_SUPPORT)
        /* This is deprecated! do not enable unless you absolutely have to */
        br_x509_minimal_set_hash(&mc, br_sha1_ID, &br_sha1_vtable);
#endif
        br_x509_minimal_set_hash(&mc, br_sha256_ID, &br_sha256_vtable);
#ifdef VE_SHA384_SUPPORT
        br_x509_minimal_set_hash(&mc, br_sha384_ID, &br_sha384_vtable);
#endif
#ifdef VE_SHA512_SUPPORT
        br_x509_minimal_set_hash(&mc, br_sha512_ID, &br_sha512_vtable);
#endif
        br_x509_minimal_set_name_elements(&mc, elts, num_elts);

#ifdef HAVE_BR_X509_TIME_CHECK
        br_x509_minimal_set_time_callback(&mc, NULL, verify_time_cb);
#else
#if defined(_STANDALONE) || defined(UNIT_TEST)
        /*
         * Clock is probably bogus so we use ve_utc.
         */
        mc.days = (ve_utc / SECONDS_PER_DAY) + X509_DAYS_TO_UTC0;
        mc.seconds = (ve_utc % SECONDS_PER_DAY);
#endif
#endif
        mc.vtable->start_chain(&mc.vtable, NULL);
        for (u = 0; u < VEC_LEN(chain); u ++) {
                xc = &VEC_ELT(chain, u);
                mc.vtable->start_cert(&mc.vtable, xc->data_len);
                mc.vtable->append(&mc.vtable, xc->data, xc->data_len);
                mc.vtable->end_cert(&mc.vtable);
                switch (mc.err) {
                case 0:
                case BR_ERR_X509_OK:
                case BR_ERR_X509_EXPIRED:
                        break;
                default:
                        printf("u=%zu mc.err=%d\n", u, mc.err);
                        break;
                }
        }
        err = mc.vtable->end_chain(&mc.vtable);
        pk = NULL;
        if (err) {
                char date[12];

                switch (err) {
                case 54:
                        ve_error_set("Validation failed, certificate not valid as of %s",
                            gdate(date, sizeof(date), ve_utc));
                        break;
                default: {
                        const char *err_desc = NULL;
                        const char *err_name = find_error_name(err, &err_desc);

                        if (err_name == NULL)
                                ve_error_set("Validation failed, err = %d",
                                    err);
                        else
                                ve_error_set("Validation failed, %s (%s)",
                                    err_desc, err_name);
                        break; }
                }
        } else {
                tpk = mc.vtable->get_pkey(&mc.vtable, &usages);
                if (tpk != NULL) {
                        pk = xpkeydup(tpk);
                }
        }
        VEC_CLEAR(chain);
        return (pk);
}

/*
 * Check if digest of one of the certificates from verified chain
 * is present in the forbidden database.
 * Since UEFI allows to store three types of digests
 * all of them have to be checked separately.
 */
static int
check_forbidden_digests(br_x509_certificate *xcs, size_t num)
{
        unsigned char sha256_digest[br_sha256_SIZE];
        unsigned char sha384_digest[br_sha384_SIZE];
        unsigned char sha512_digest[br_sha512_SIZE];
        void *tbs;
        hash_data *digest;
        br_hash_compat_context ctx;
        const br_hash_class *md;
        size_t tbs_len, i;
        int have_sha256, have_sha384, have_sha512;

        if (VEC_LEN(forbidden_digests) == 0)
                return (0);

        /*
         * Iterate through certificates, extract their To-Be-Signed section,
         * and compare its digest against the ones in the forbidden database.
         */
        while (num--) {
                tbs = X509_to_tbs(xcs[num].data, &tbs_len);
                if (tbs == NULL) {
                        printf("Failed to obtain TBS part of certificate\n");
                        return (1);
                }
                have_sha256 = have_sha384 = have_sha512 = 0;

                for (i = 0; i < VEC_LEN(forbidden_digests); i++) {
                        digest = &VEC_ELT(forbidden_digests, i);
                        switch (digest->hash_size) {
                        case br_sha256_SIZE:
                                if (!have_sha256) {
                                        have_sha256 = 1;
                                        md = &br_sha256_vtable;
                                        md->init(&ctx.vtable);
                                        md->update(&ctx.vtable, tbs, tbs_len);
                                        md->out(&ctx.vtable, sha256_digest);
                                }
                                if (!memcmp(sha256_digest,
                                        digest->data,
                                        br_sha256_SIZE))
                                        return (1);

                                break;
                        case br_sha384_SIZE:
                                if (!have_sha384) {
                                        have_sha384 = 1;
                                        md = &br_sha384_vtable;
                                        md->init(&ctx.vtable);
                                        md->update(&ctx.vtable, tbs, tbs_len);
                                        md->out(&ctx.vtable, sha384_digest);
                                }
                                if (!memcmp(sha384_digest,
                                        digest->data,
                                        br_sha384_SIZE))
                                        return (1);

                                break;
                        case br_sha512_SIZE:
                                if (!have_sha512) {
                                        have_sha512 = 1;
                                        md = &br_sha512_vtable;
                                        md->init(&ctx.vtable);
                                        md->update(&ctx.vtable, tbs, tbs_len);
                                        md->out(&ctx.vtable, sha512_digest);
                                }
                                if (!memcmp(sha512_digest,
                                        digest->data,
                                        br_sha512_SIZE))
                                        return (1);

                                break;
                        }
                }
        }

        return (0);
}

static br_x509_pkey *
verify_signer(const char *certs,
    br_name_element *elts, size_t num_elts)
{
        br_x509_certificate *xcs;
        br_x509_pkey *pk;
        size_t num;

        pk = NULL;

        ve_trust_init();
        xcs = read_certificates(certs, &num);
        if (xcs == NULL) {
                ve_error_set("cannot read certificates\n");
                return (NULL);
        }

        /*
         * Check if either
         * 1. There is a direct match between cert from forbidden_anchors
         * and a cert from chain.
         * 2. CA that signed the chain is found in forbidden_anchors.
         */
        if (VEC_LEN(forbidden_anchors) > 0)
                pk = verify_signer_xcs(xcs, num, elts, num_elts, &forbidden_anchors);
        if (pk != NULL) {
                ve_error_set("Certificate is on forbidden list\n");
                xfreepkey(pk);
                pk = NULL;
                goto out;
        }

        pk = verify_signer_xcs(xcs, num, elts, num_elts, &trust_anchors);
        if (pk == NULL)
                goto out;

        /*
         * Check if hash of tbs part of any certificate in chain
         * is on the forbidden list.
         */
        if (check_forbidden_digests(xcs, num)) {
                ve_error_set("Certificate hash is on forbidden list\n");
                xfreepkey(pk);
                pk = NULL;
        }
out:
        free_certificates(xcs, num);
        return (pk);
}

/**
 * we need a hex digest including trailing newline below
 */
char *
hexdigest(char *buf, size_t bufsz, unsigned char *foo, size_t foo_len)
{
        char const hex2ascii[] = "0123456789abcdef";
        size_t i;

        /* every binary byte is 2 chars in hex + newline + null  */
        if (bufsz < (2 * foo_len) + 2)
                return (NULL);

        for (i = 0; i < foo_len; i++) {
                buf[i * 2] = hex2ascii[foo[i] >> 4];
                buf[i * 2 + 1] = hex2ascii[foo[i] & 0x0f];
        }

        buf[i * 2] = 0x0A; /* we also want a newline */
        buf[i * 2 + 1] = '\0';

        return (buf);
}

/**
 * @brief
 * verify file against sigfile using pk
 *
 * When we generated the signature in sigfile,
 * we hashed (sha256) file, and sent that to signing server
 * which hashed (sha256) that hash.
 *
 * To verify we need to replicate that result.
 *
 * @param[in] pk
 *      br_x509_pkey
 *
 * @paramp[in] file
 *      file to be verified
 *
 * @param[in] sigfile
 *      signature (PEM encoded)
 *
 * @return NULL on error, otherwise content of file.
 */
#ifdef VE_ECDSA_SUPPORT
static unsigned char *
verify_ec(br_x509_pkey *pk, const char *file, const char *sigfile)
{
#ifdef VE_ECDSA_HASH_AGAIN
        char *hex, hexbuf[br_sha512_SIZE * 2 + 2];
#endif
        unsigned char rhbuf[br_sha512_SIZE];
        br_sha256_context ctx;
        unsigned char *fcp, *scp;
        size_t flen, slen, plen;
        pem_object *po;
        const br_ec_impl *ec;
        br_ecdsa_vrfy vrfy;

        if ((fcp = read_file(file, &flen)) == NULL)
                return (NULL);
        if ((scp = read_file(sigfile, &slen)) == NULL) {
                free(fcp);
                return (NULL);
        }
        if ((po = decode_pem(scp, slen, &plen)) == NULL) {
                free(fcp);
                free(scp);
                return (NULL);
        }
        br_sha256_init(&ctx);
        br_sha256_update(&ctx, fcp, flen);
        br_sha256_out(&ctx, rhbuf);
#ifdef VE_ECDSA_HASH_AGAIN
        hex = hexdigest(hexbuf, sizeof(hexbuf), rhbuf, br_sha256_SIZE);
        /* now hash that */
        if (hex) {
                br_sha256_init(&ctx);
                br_sha256_update(&ctx, hex, strlen(hex));
                br_sha256_out(&ctx, rhbuf);
        }
#endif
        ec = br_ec_get_default();
        vrfy = br_ecdsa_vrfy_asn1_get_default();
        if (!vrfy(ec, rhbuf, br_sha256_SIZE, &pk->key.ec, po->data,
                po->data_len)) {
                free(fcp);
                fcp = NULL;
        }
        free(scp);
        return (fcp);
}
#endif

#if defined(VE_RSA_SUPPORT) || defined(VE_OPENPGP_SUPPORT)
/**
 * @brief verify an rsa digest
 *
 * @return 0 on failure
 */
int
verify_rsa_digest (br_rsa_public_key *pkey,
    const unsigned char *hash_oid,
    unsigned char *mdata, size_t mlen,
    unsigned char *sdata, size_t slen)
{
        br_rsa_pkcs1_vrfy vrfy;
        unsigned char vhbuf[br_sha512_SIZE];

        vrfy = br_rsa_pkcs1_vrfy_get_default();

        if (!vrfy(sdata, slen, hash_oid, mlen, pkey, vhbuf) ||
            memcmp(vhbuf, mdata, mlen) != 0) {
                return (0);             /* fail */
        }
        return (1);                     /* ok */
}
#endif

/**
 * @brief
 * verify file against sigfile using pk
 *
 * When we generated the signature in sigfile,
 * we hashed (sha256) file, and sent that to signing server
 * which hashed (sha256) that hash.
 *
 * Or (deprecated) we simply used sha1 hash directly.
 *
 * To verify we need to replicate that result.
 *
 * @param[in] pk
 *      br_x509_pkey
 *
 * @paramp[in] file
 *      file to be verified
 *
 * @param[in] sigfile
 *      signature (PEM encoded)
 *
 * @return NULL on error, otherwise content of file.
 */
#ifdef VE_RSA_SUPPORT
static unsigned char *
verify_rsa(br_x509_pkey *pk,  const char *file, const char *sigfile)
{
        unsigned char rhbuf[br_sha512_SIZE];
        const unsigned char *hash_oid;
        const br_hash_class *md;
        br_hash_compat_context mctx;
        unsigned char *fcp, *scp;
        size_t flen, slen, plen, hlen;
        pem_object *po;

        if ((fcp = read_file(file, &flen)) == NULL)
                return (NULL);
        if ((scp = read_file(sigfile, &slen)) == NULL) {
                free(fcp);
                return (NULL);
        }
        if ((po = decode_pem(scp, slen, &plen)) == NULL) {
                free(fcp);
                free(scp);
                return (NULL);
        }

        switch (po->data_len) {
#if defined(UNIT_TEST) && defined(VE_DEPRECATED_RSA_SHA1_SUPPORT)
        case 256:
                // this is our old deprecated sig method
                md = &br_sha1_vtable;
                hlen = br_sha1_SIZE;
                hash_oid = BR_HASH_OID_SHA1;
                break;
#endif
        default:
                md = &br_sha256_vtable;
                hlen = br_sha256_SIZE;
                hash_oid = BR_HASH_OID_SHA256;
                break;
        }
        md->init(&mctx.vtable);
        md->update(&mctx.vtable, fcp, flen);
        md->out(&mctx.vtable, rhbuf);
        if (!verify_rsa_digest(&pk->key.rsa, hash_oid,
                rhbuf, hlen, po->data, po->data_len)) {
                free(fcp);
                fcp = NULL;
        }
        free(scp);
        return (fcp);
}
#endif

/**
 * @brief
 * verify a signature and return content of signed file
 *
 * @param[in] sigfile
 *      file containing signature
 *      we derrive path of signed file and certificate change from
 *      this.
 *
 * @param[in] flags
 *      only bit 1 significant so far
 *
 * @return NULL on error otherwise content of signed file
 */
unsigned char *
verify_sig(const char *sigfile, int flags)
{
        br_x509_pkey *pk;
        br_name_element cn;
        char cn_buf[80];
        unsigned char cn_oid[4];
        char pbuf[MAXPATHLEN];
        char *cp;
        unsigned char *ucp;
        size_t n;

        DEBUG_PRINTF(5, ("verify_sig: %s\n", sigfile));
        n = strlcpy(pbuf, sigfile, sizeof(pbuf));
        if (n > (sizeof(pbuf) - 5) || strcmp(&sigfile[n - 3], "sig") != 0)
                return (NULL);
        cp = strcpy(&pbuf[n - 3], "certs");
        /*
         * We want the commonName field
         * the OID we want is 2,5,4,3 - but DER encoded
         */
        cn_oid[0] = 3;
        cn_oid[1] = 0x55;
        cn_oid[2] = 4;
        cn_oid[3] = 3;
        cn.oid = cn_oid;
        cn.buf = cn_buf;
        cn.len = sizeof(cn_buf);

        pk = verify_signer(pbuf, &cn, 1);
        if (!pk) {
                printf("cannot verify: %s: %s\n", pbuf, ve_error_get());
                return (NULL);
        }
        for (; cp > pbuf; cp--) {
                if (*cp == '.') {
                        *cp = '\0';
                        break;
                }
        }
        switch (pk->key_type) {
#ifdef VE_ECDSA_SUPPORT
        case BR_KEYTYPE_EC:
                ucp = verify_ec(pk, pbuf, sigfile);
                break;
#endif
#ifdef VE_RSA_SUPPORT
        case BR_KEYTYPE_RSA:
                ucp = verify_rsa(pk, pbuf, sigfile);
                break;
#endif
        default:
                ucp = NULL;             /* not supported */
        }
        xfreepkey(pk);
        if (!ucp) {
                printf("Unverified %s (%s)\n", pbuf,
                    cn.status ? cn_buf : "unknown");
        } else if ((flags & VEF_VERBOSE) != 0) {
                printf("Verified %s signed by %s\n", pbuf,
                    cn.status ? cn_buf : "someone we trust");
        }
        return (ucp);
}


/**
 * @brief verify hash matches
 *
 * We have finished hashing a file,
 * see if we got the desired result.
 *
 * @param[in] ctx
 *      pointer to hash context
 *
 * @param[in] md
 *      pointer to hash class
 *
 * @param[in] path
 *      name of the file we are checking
 *
 * @param[in] want
 *      the expected result
 *
 * @param[in] hlen
 *      size of hash output
 *
 * @return 0 on success
 */
int
ve_check_hash(br_hash_compat_context *ctx, const br_hash_class *md,
    const char *path, const char *want, size_t hlen)
{
        char hexbuf[br_sha512_SIZE * 2 + 2];
        unsigned char hbuf[br_sha512_SIZE];
        char *hex;
        int rc;
        int n;

        md->out(&ctx->vtable, hbuf);
#ifdef VE_PCR_SUPPORT
        ve_pcr_update(path, hbuf, hlen);
#endif
        hex = hexdigest(hexbuf, sizeof(hexbuf), hbuf, hlen);
        if (!hex)
                return (VE_FINGERPRINT_WRONG);
        n = 2*hlen;
        if ((rc = strncmp(hex, want, n))) {
                ve_error_set("%s: %.*s != %.*s", path, n, hex, n, want);
                rc = VE_FINGERPRINT_WRONG;
        }
        return (rc ? rc : VE_FINGERPRINT_OK);
}

#ifdef VE_HASH_KAT_STR
static int
test_hash(const br_hash_class *md, size_t hlen,
    const char *hname, const char *s, size_t slen, const char *want)
{
        br_hash_compat_context mctx;

        md->init(&mctx.vtable);
        md->update(&mctx.vtable, s, slen);
        return (ve_check_hash(&mctx, md, hname, want, hlen) != VE_FINGERPRINT_OK);
}

#endif

#define ve_test_hash(n, N) \
        printf("Testing hash: " #n "\t\t\t\t%s\n", \
            test_hash(&br_ ## n ## _vtable, br_ ## n ## _SIZE, #n, \
            VE_HASH_KAT_STR, VE_HASH_KAT_STRLEN(VE_HASH_KAT_STR), \
            vh_ ## N) ? "Failed" : "Passed")

/**
 * @brief
 * run self tests on hash and signature verification
 *
 * Test that the hash methods (SHA1 and SHA256) work.
 * Test that we can verify a certificate for each supported
 * Root CA.
 *
 * @return cached result.
 */
int
ve_self_tests(void)
{
        static int once = -1;
#ifdef VERIFY_CERTS_STR
        br_x509_certificate *xcs;
        br_x509_pkey *pk;
        br_name_element cn;
        char cn_buf[80];
        unsigned char cn_oid[4];
        size_t num;
        size_t u;
#endif

        if (once >= 0)
                return (once);
        once = 0;

        DEBUG_PRINTF(5, ("Self tests...\n"));
#ifdef VE_HASH_KAT_STR
#ifdef VE_SHA1_SUPPORT
        ve_test_hash(sha1, SHA1);
#endif
#ifdef VE_SHA256_SUPPORT
        ve_test_hash(sha256, SHA256);
#endif
#ifdef VE_SHA384_SUPPORT
        ve_test_hash(sha384, SHA384);
#endif
#ifdef VE_SHA512_SUPPORT
        ve_test_hash(sha512, SHA512);
#endif
#endif
#ifdef VERIFY_CERTS_STR
        xcs = parse_certificates(__DECONST(unsigned char *, VERIFY_CERTS_STR),
            sizeof(VERIFY_CERTS_STR), &num);
        if (xcs != NULL) {
                /*
                 * We want the commonName field
                 * the OID we want is 2,5,4,3 - but DER encoded
                 */
                cn_oid[0] = 3;
                cn_oid[1] = 0x55;
                cn_oid[2] = 4;
                cn_oid[3] = 3;
                cn.oid = cn_oid;
                cn.buf = cn_buf;

                for (u = 0; u < num; u ++) {
                        cn.len = sizeof(cn_buf);
                        if ((pk = verify_signer_xcs(&xcs[u], 1, &cn, 1, &trust_anchors)) != NULL) {
                                free_cert_contents(&xcs[u]);
                                once++;
                                printf("Testing verify certificate: %s\tPassed\n",
                                    cn.status ? cn_buf : "");
                                xfreepkey(pk);
                        }
                }
                if (!once)
                        printf("Testing verify certificate:\t\t\tFailed\n");
                xfree(xcs);
        }
#endif  /* VERIFY_CERTS_STR */
#ifdef VE_OPENPGP_SUPPORT
        if (!openpgp_self_tests())
                once++;
#endif
        return (once);
}