Date: Mon, 18 Apr 2022 21:53:16 GMT From: "Simon J. Gerraty" <sjg@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Subject: git: cc9e6590773d - main - Merge bearssl-20220418 Message-ID: <202204182153.23ILrG8w018103@gitrepo.freebsd.org>
next in thread | raw e-mail | index | archive | help
The branch main has been updated by sjg: URL: https://cgit.FreeBSD.org/src/commit/?id=cc9e6590773dba57440750c124173ed531349a06 commit cc9e6590773dba57440750c124173ed531349a06 Merge: ecbe50447d04 f6acb9b9f81c Author: Simon J. Gerraty <sjg@FreeBSD.org> AuthorDate: 2022-04-18 21:47:09 +0000 Commit: Simon J. Gerraty <sjg@FreeBSD.org> CommitDate: 2022-04-18 21:52:30 +0000 Merge bearssl-20220418 Main change is a callback for checking validity period of certificates. Merge commit 'f6acb9b9f81c96ae7c9592bee1bb89c4357cc3e5' Add -DHAVE_BR_X509_TIME_CHECK to libsecureboot/Makefile.inc contrib/bearssl/.gitignore | 6 + contrib/bearssl/T0Comp.exe | Bin 72704 -> 73216 bytes contrib/bearssl/flist | 459 +++++++++++++++ contrib/bearssl/inc/bearssl_ec.h | 2 +- contrib/bearssl/inc/bearssl_hash.h | 4 +- contrib/bearssl/inc/bearssl_ssl.h | 8 +- contrib/bearssl/inc/bearssl_x509.h | 83 ++- contrib/bearssl/src/config.h | 22 +- contrib/bearssl/src/ec/ec_c25519_m64.c | 4 - contrib/bearssl/src/ec/ec_p256_m15.c | 22 +- contrib/bearssl/src/ec/ec_p256_m31.c | 22 +- contrib/bearssl/src/ec/ec_p256_m62.c | 2 +- contrib/bearssl/src/ec/ec_p256_m64.c | 67 ++- contrib/bearssl/src/ec/ec_prime_i15.c | 10 +- contrib/bearssl/src/ec/ec_prime_i31.c | 13 +- contrib/bearssl/src/inner.h | 26 +- contrib/bearssl/src/rand/sysrng.c | 88 ++- contrib/bearssl/src/rsa/rsa_i15_keygen.c | 6 +- contrib/bearssl/src/rsa/rsa_i15_modulus.c | 2 +- contrib/bearssl/src/rsa/rsa_i31_keygen_inner.c | 6 +- contrib/bearssl/src/rsa/rsa_i31_modulus.c | 2 +- contrib/bearssl/src/ssl/ssl_engine.c | 15 + contrib/bearssl/src/ssl/ssl_rec_cbc.c | 2 +- contrib/bearssl/src/x509/asn1.t0 | 2 +- contrib/bearssl/src/x509/skey_decoder.c | 2 +- contrib/bearssl/src/x509/skey_decoder.t0 | 2 +- contrib/bearssl/src/x509/x509_minimal.c | 751 +++++++++++++------------ contrib/bearssl/src/x509/x509_minimal.t0 | 80 ++- contrib/bearssl/test/test_crypto.c | 4 +- contrib/bearssl/test/test_x509.c | 210 ++++--- contrib/bearssl/tools/sslio.c | 2 +- lib/libsecureboot/Makefile.inc | 2 + lib/libsecureboot/vets.c | 2 +- 33 files changed, 1358 insertions(+), 570 deletions(-) diff --cc contrib/bearssl/.gitignore index 000000000000,000000000000..7da362eddc00 new file mode 100644 --- /dev/null +++ b/contrib/bearssl/.gitignore @@@ -1,0 -1,0 +1,6 @@@ ++/build/ ++/libbearssl.a ++/brssl ++/testcrypto ++/testspeed ++/testx509 diff --cc contrib/bearssl/T0Comp.exe index 67eba109800e,de2364d69e07..de2364d69e07 Binary files differ diff --cc contrib/bearssl/flist index 000000000000,9751ad231065..9751ad231065 mode 000000,100644..100644 --- a/contrib/bearssl/flist +++ b/contrib/bearssl/flist diff --cc lib/libsecureboot/Makefile.inc index e0faf287a245,000000000000..921b5e7bc1d8 mode 100644,000000..100644 --- a/lib/libsecureboot/Makefile.inc +++ b/lib/libsecureboot/Makefile.inc @@@ -1,168 -1,0 +1,170 @@@ +# $FreeBSD$ + +.if empty(BEARSSL) +.include "../libbearssl/Makefile.inc" +.endif + +.if !target(_${__this}_) +_${__this}_: + +libsecureboot_src:= ${.PARSEDIR} + +CFLAGS+= -I${libsecureboot_src}/h + ++CFLAGS+= -DHAVE_BR_X509_TIME_CHECK ++ +.PATH: ${.PARSEDIR} + +SRCS+= \ + readfile.c \ + brf.c \ + vesigned.c \ + vets.c + +.if ${.CURDIR:M*libsecureboot*} != "" +SRCS+= veta.c +.endif + +CFLAGS+= ${XCFLAGS.${.TARGET:T:R}:U} + +# we use a couple of files from ${BEARSSL}/tools +BRSSL_CFLAGS+= -I${BEARSSL}/tools +BRSSL_SRCS+= \ + ${BEARSSL}/tools/xmem.c \ + ${BEARSSL}/tools/vector.c + +BRSSL_DEPS= \ + brf.c \ + vets.c \ + veta.c + +.if ${MK_LOADER_EFI_SECUREBOOT} != "no" +BRSSL_DEPS+= \ + efi_init.c \ + efi_variables.c +.endif + +# we do not need/want nested objdirs +OBJS_SRCS_FILTER = T R + +SRCS+= ${BRSSL_SRCS} + + +# extract the last cert from a chain (should be rootCA) +_LAST_PEM_USE: .USE + sed "1,`grep -n .-END ${.ALLSRC:M*.pem} | tail -2 | head -1 | sed 's,:.*,,'`d" ${.ALLSRC:M*.pem} > ${.TARGET} + +# extract 2nd last cert from chain - we use this for self-test +_2ndLAST_PEM_USE: .USE + sed -n "`grep -n .-BEGIN ${.ALLSRC:M*.pem} | tail -2 | \ + sed 's,:.*,,' | xargs | (read a b; echo $$a,$$(($$b - 1)))`p" ${.ALLSRC:M*.pem} > ${.TARGET} + +# list of hashes we support +VE_HASH_LIST?= SHA256 + +# list of signatures we support +# some people don't trust ECDSA +VE_SIGNATURE_LIST?= RSA + +# this list controls our search for signatures so will not be sorted +# note: for X509 signatures we assume we can replace the trailing +# "sig" with "certs" to find the certificate chain +# eg. for manifest.esig we use manifest.ecerts +VE_SIGNATURE_EXT_LIST?= sig + +# needs to be yes for FIPS 140-2 compliance +VE_SELF_TESTS?= no + +# rules to populate the [tv]*.pem files we use to generate ta.h +# and can add/alter VE_*_LIST as desired. +.-include "local.trust.mk" + +# this is what we use as our trust anchor +CFLAGS+= -I. -DTRUST_ANCHOR_STR=ta_PEM + +.if ${VE_SELF_TESTS} != "no" +XCFLAGS.vets+= -DVERIFY_CERTS_STR=vc_PEM +.endif + +# clean these up +VE_HASH_LIST:= ${VE_HASH_LIST:tu:O:u} +VE_SIGNATURE_LIST:= ${VE_SIGNATURE_LIST:tu:O:u} + +# define what we are supporting +CFLAGS+= ${VE_HASH_LIST:@H@-DVE_$H_SUPPORT@} \ + ${VE_SIGNATURE_LIST:@S@-DVE_$S_SUPPORT@} + +.if ${VE_SIGNATURE_LIST:MOPENPGP} != "" +.include "openpgp/Makefile.inc" +.endif + +.if ${VE_SELF_TESTS} != "no" +# The input used for hash KATs +# we use a string by default so it is independent of any other test +VE_HASH_KAT_STRLEN?= strlen +.if ${VE_HASH_KAT_STRLEN} == "strlen" +VE_HASH_KAT_STR?= self-tests-are-good +VE_HASH_KAT_STR_INPUT= echo -n +XCFLAGS.vets+= -DVE_HASH_KAT_STR=\"${VE_HASH_KAT_STR}\" +.else +VE_HASH_KAT_STR?= vc_PEM +VE_HASH_KAT_STR_INPUT= cat +VE_HASH_KAT_STRLEN= sizeof +XCFLAGS.vets+= -DVE_HASH_KAT_STR=${VE_HASH_KAT_STR} +.endif +XCFLAGS.vets+= -DVE_HASH_KAT_STRLEN=${VE_HASH_KAT_STRLEN} +.endif + +# this should be updated occassionally this is 2019-01-01Z +SOURCE_DATE_EPOCH?= 1546329600 +.if ${MK_REPRODUCIBLE_BUILD} == "yes" +BUILD_UTC?= ${SOURCE_DATE_EPOCH} +.endif +# BUILD_UTC provides a basis for the loader's notion of time +# By default we use the mtime of BUILD_UTC_FILE +.if empty(BUILD_UTC_FILE) +BUILD_UTC_FILE:= ${.PARSEDIR:tA}/${.PARSEFILE} +.endif +# you can of course set BUILD_UTC to any value you like +BUILD_UTC?= ${${STAT:Ustat} -f %m ${BUILD_UTC_FILE}:L:sh} + +# Generate ta.h containing one or more PEM encoded trust anchors in ta_PEM. +# +# If we are doing self-tests, we define another arrary vc_PEM +# containing certificates that we can verify for each trust anchor. +# This is typically a subordinate CA cert. +# Finally we generate a hash of VE_HASH_KAT_STR +# using each supported hash method +# to use as a Known Answer Test (needed for FIPS 140-2) +# +TA_PEM_LIST ?= ${.ALLSRC:N*crl*:Mt*.pem} +VC_PEM_LIST ?= ${.ALLSRC:N*crl*:Mv*.pem} +vets.o vets.po vets.pico: ta.h +ta.h: + @( echo '/* Autogenerated - DO NOT EDIT!!! */'; echo; \ + cat ${TA_PEM_LIST:O:u} /dev/null | \ + file2c -sx 'static const char ta_PEM[] = {' '};'; \ + echo "${.newline}${VE_HASH_LIST:O:u:@H@static char vh_$H[] = \"`${VE_HASH_KAT_STR_INPUT} ${VE_HASH_KAT_STR} | ${$H:U${H:tl}}`\";${.newline}@}"; ) > ${.TARGET} +.if ${VE_SELF_TESTS} != "no" + ( cat ${VC_PEM_LIST:O:u} /dev/null | \ + file2c -sx 'static const char vc_PEM[] = {' '};'; echo ) >> ${.TARGET} +.endif + echo '#define BUILD_UTC ${BUILD_UTC}' >> ${.TARGET} ${.OODATE:MNOMETA_CMP} + +# This header records our preference for signature extensions. +vesigned.o vesigned.po vesigned.pico: vse.h +vse.h: + @( echo '/* Autogenerated - DO NOT EDIT!!! */'; echo; \ + echo "static const char *signature_exts[] = {"; \ + echo '${VE_SIGNATURE_EXT_LIST:O:u:@e@"$e",${.newline}@}'; \ + echo 'NULL };' ) > ${.TARGET} + + +.for s in ${BRSSL_SRCS} ${BRSSL_DEPS} +.ifdef BRSSL_SED +$s: brssl.h +.endif +XCFLAGS.${s:R}+= ${BRSSL_CFLAGS} +.endfor + +.endif diff --cc lib/libsecureboot/vets.c index dd347a0a8c13,000000000000..a3c1fb34a419 mode 100644,000000..100644 --- a/lib/libsecureboot/vets.c +++ b/lib/libsecureboot/vets.c @@@ -1,1128 -1,0 +1,1128 @@@ +/*- + * 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> +__FBSDID("$FreeBSD$"); + +/** + * @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; +} + +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; + } +} + +static void +free_cert_contents(br_x509_certificate *xc) +{ + xfree(xc->data); +} + +/* + * 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' actually care about cert status - just its name */ + err = mc.vtable->end_chain(&mc.vtable); + + if (!cn.status) + 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; + 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; + 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 + 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, ++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 + + 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 +#if defined(_STANDALONE) + rc = 0; /* don't fail */ +#endif + 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 absoultely 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: + ve_error_set("Validation failed, err = %d", err); + 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); +} + *** 398 LINES SKIPPED ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202204182153.23ILrG8w018103>