Index: stable/12/lib/libsecureboot/Makefile.inc =================================================================== --- stable/12/lib/libsecureboot/Makefile.inc (revision 347758) +++ stable/12/lib/libsecureboot/Makefile.inc (revision 347759) @@ -1,155 +1,168 @@ # $FreeBSD$ .if empty(BEARSSL) .include "../libbearssl/Makefile.inc" .endif .if !target(_${__this}_) _${__this}_: libsecureboot_src:= ${.PARSEDIR} CFLAGS+= -I${libsecureboot_src}/h .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 vc_PEM using each supported hash method +# 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: ${.ALLTARGETS:M[tv]*pem:O:u} +ta.h: @( echo '/* Autogenerated - DO NOT EDIT!!! */'; echo; \ - cat ${.ALLSRC:N*crl*:Mt*.pem} /dev/null | \ + cat ${TA_PEM_LIST:O:u} /dev/null | \ file2c -sx 'static const char ta_PEM[] = {' '};'; \ - echo "${.newline}${VE_HASH_LIST:@H@static char vh_$H[] = \"`cat ${.ALLSRC:N*crl*:Mv*.pem} | ${$H:U${H:tl}}`\";${.newline}@}"; ) > ${.TARGET} + 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 ${.ALLSRC:N*crl*:Mv*.pem} /dev/null | \ + ( 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:@e@"$e",${.newline}@}'; \ + 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 Index: stable/12/lib/libsecureboot/libsecureboot-priv.h =================================================================== --- stable/12/lib/libsecureboot/libsecureboot-priv.h (revision 347758) +++ stable/12/lib/libsecureboot/libsecureboot-priv.h (revision 347759) @@ -1,65 +1,66 @@ /*- * Copyright (c) 2017, 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. */ /* * $FreeBSD$ */ #ifndef _LIBSECUREBOOT_PRIV_H_ #define _LIBSECUREBOOT_PRIV_H_ /* public api */ #include "libsecureboot.h" struct stat; typedef struct { unsigned char *data; size_t hash_size; } hash_data; size_t ve_trust_anchors_add(br_x509_certificate *, size_t); size_t ve_forbidden_anchors_add(br_x509_certificate *, size_t); void ve_forbidden_digest_add(hash_data *digest, size_t); char *fingerprint_info_lookup(int, const char *); br_x509_certificate * parse_certificates(unsigned char *, size_t, size_t *); int certificate_to_trust_anchor_inner(br_x509_trust_anchor *, br_x509_certificate *); 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); int is_verified(struct stat *stp); void add_verify_status(struct stat *stp, int status); +int openpgp_trust_init(void); int openpgp_self_tests(void); int efi_secure_boot_enabled(void); br_x509_certificate* efi_get_trusted_certs(size_t *count); br_x509_certificate* efi_get_forbidden_certs(size_t *count); hash_data* efi_get_forbidden_digests(size_t *count); #endif /* _LIBSECUREBOOT_PRIV_H_ */ Index: stable/12/lib/libsecureboot/local.trust.mk =================================================================== --- stable/12/lib/libsecureboot/local.trust.mk (revision 347758) +++ stable/12/lib/libsecureboot/local.trust.mk (revision 347759) @@ -1,113 +1,116 @@ # $FreeBSD$ # Consider this file an example. # # For Junos this is how we obtain trust anchor .pems # the signing server (http://www.crufty.net/sjg/blog/signing-server.htm) # for each key will provide the appropriate certificate chain on request # force these for Junos #MANIFEST_SKIP_ALWAYS= boot VE_HASH_LIST= \ SHA1 \ SHA256 \ SHA384 \ SHA512 VE_SIGNATURE_LIST= \ ECDSA \ RSA VE_SIGNATURE_EXT_LIST= \ esig \ rsig VE_SELF_TESTS= yes .if ${MACHINE} == "host" && ${.CURDIR:T} == "tests" VE_SIGNATURE_LIST+= \ DEPRECATED_RSA_SHA1 VE_SIGNATURE_EXT_LIST+= \ sig .endif SIGNER ?= ${SB_TOOLS_PATH:U/volume/buildtools/bin}/sign.py .if exists(${SIGNER}) SIGN_HOST ?= ${SB_SITE:Usvl}-junos-signer.juniper.net ECDSA_PORT:= ${133%y:L:gmtime} SIGN_ECDSA= ${PYTHON} ${SIGNER} -u ${SIGN_HOST}:${ECDSA_PORT} -h sha256 RSA2_PORT:= ${163%y:L:gmtime} SIGN_RSA2= ${PYTHON} ${SIGNER} -u ${SIGN_HOST}:${RSA2_PORT} -h sha256 .if !empty(OPENPGP_SIGN_URL) VE_SIGNATURE_LIST+= OPENPGP VE_SIGNATURE_EXT_LIST+= asc SIGN_OPENPGP= ${PYTHON} ${SIGNER:H}/openpgp-sign.py -a -u ${OPENPGP_SIGN_URL} ta_openpgp.asc: ${SIGN_OPENPGP} -C ${.TARGET} -ta.h: ta_openpgp.asc +ta_asc.h: ta_openpgp.asc .if ${VE_SELF_TESTS} != "no" # for self test vc_openpgp.asc: ta_openpgp.asc ${SIGN_OPENPGP} ${.ALLSRC:M*.asc} mv ta_openpgp.asc.asc ${.TARGET} -ta.h: vc_openpgp.asc +ta_asc.h: vc_openpgp.asc .endif .endif rcerts.pem: ${SIGN_RSA2} -C ${.TARGET} ecerts.pem: ${SIGN_ECDSA} -C ${.TARGET} .if ${VE_SIGNATURE_LIST:tu:MECDSA} != "" # the last cert in the chain is the one we want ta_ec.pem: ecerts.pem _LAST_PEM_USE - +ta.h: ta_ec.pem .if ${VE_SELF_TESTS} != "no" # these are for verification self test vc_ec.pem: ecerts.pem _2ndLAST_PEM_USE +ta.h: vc_ec.pem .endif .endif .if ${VE_SIGNATURE_LIST:tu:MRSA} != "" ta_rsa.pem: rcerts.pem _LAST_PEM_USE +ta.h: ta_rsa.pem .if ${VE_SELF_TESTS} != "no" vc_rsa.pem: rcerts.pem _2ndLAST_PEM_USE +ta.h: vc_rsa.pem .endif .endif # we take the mtime of this as our baseline time #BUILD_UTC_FILE= ecerts.pem #VE_DEBUG_LEVEL=3 #VE_VERBOSE_DEFAULT=1 .else # you need to provide t*.pem or t*.asc files for each trust anchor .if empty(TRUST_ANCHORS) TRUST_ANCHORS!= cd ${.CURDIR} && 'ls' -1 *.pem t*.asc 2> /dev/null .endif .if empty(TRUST_ANCHORS) && ${MK_LOADER_EFI_SECUREBOOT} != "yes" .error Need TRUST_ANCHORS see ${.CURDIR}/README.rst .endif .if ${TRUST_ANCHORS:T:Mt*.pem} != "" ta.h: ${TRUST_ANCHORS:M*.pem} .endif .if ${TRUST_ANCHORS:T:Mt*.asc} != "" VE_SIGNATURE_LIST+= OPENPGP VE_SIGNATURE_EXT_LIST+= asc ta_asc.h: ${TRUST_ANCHORS:M*.asc} .endif # we take the mtime of this as our baseline time BUILD_UTC_FILE?= ${TRUST_ANCHORS:[1]} .endif Index: stable/12/lib/libsecureboot/openpgp/Makefile.inc =================================================================== --- stable/12/lib/libsecureboot/openpgp/Makefile.inc (revision 347758) +++ stable/12/lib/libsecureboot/openpgp/Makefile.inc (revision 347759) @@ -1,48 +1,51 @@ # $FreeBSD$ # decode OpenPGP signatures per rfc4880 .PATH: ${.PARSEDIR} CFLAGS+= -DUSE_BEARSSL BRSSL_SRCS+= dearmor.c SRCS+= \ decode.c \ opgp_key.c \ opgp_sig.c opgp_key.o opgp_key.po opgp_key.pico: ta_asc.h # Generate ta_asc.h containing one or more OpenPGP trust anchors. # # Since each trust anchor must be processed individually, # we create ta_ASC as a list of pointers to them. # # If we are doing self-tests, we define another arrary vc_ASC # containing pointers to a signature of each trust anchor. # It is assumed that these v*.asc files are named similarly to # the appropriate t*.asc so that the relative order of vc_ASC # entries matches ta_ASC. -# -ta_asc.h: ${.ALLTARGETS:M[tv]*.asc:O:u} +# +TA_ASC_LIST ?= ${.ALLSRC:Mt*.asc} +VC_ASC_LIST ?= ${.ALLSRC:Mv*.asc} + +ta_asc.h: .if ${VE_SIGNATURE_LIST:MOPENPGP} != "" @( echo '/* Autogenerated - DO NOT EDIT!!! */'; echo; \ echo "#define HAVE_TA_ASC 1"; \ - set -- ${.ALLSRC:Mt*.asc:@f@$f ${f:T:R}@}; \ + set -- ${TA_ASC_LIST:@f@$f ${f:T:R}@}; \ while test $$# -ge 2; do \ file2c -sx "static const char $$2[] = {" ', 0x00 };' < $$1; \ shift 2; \ done; \ - echo 'static const char *ta_ASC[] = { ${.ALLSRC:Mt*.asc:T:R:ts,}, NULL };'; \ + echo 'static const char *ta_ASC[] = { ${TA_ASC_LIST:T:R:ts,}, NULL };'; \ echo; ) > ${.TARGET} .if ${VE_SELF_TESTS} != "no" @( echo "#define HAVE_VC_ASC 1"; \ - set -- ${.ALLSRC:Mv*.asc:@f@$f ${f:T:R}@}; \ + set -- ${VC_ASC_LIST:@f@$f ${f:T:R}@}; \ while test $$# -ge 2; do \ file2c -sx "static const char $$2[] = {" ', 0x00 };' < $$1; \ shift 2; \ done; \ - echo 'static const char *vc_ASC[] = { ${.ALLSRC:Mv*.asc:T:R:ts,}, NULL };'; \ + echo 'static const char *vc_ASC[] = { ${VC_ASC_LIST:T:R:ts,}, NULL };'; \ echo; ) >> ${.TARGET} .endif .endif Index: stable/12/lib/libsecureboot/openpgp/opgp_key.c =================================================================== --- stable/12/lib/libsecureboot/openpgp/opgp_key.c (revision 347758) +++ stable/12/lib/libsecureboot/openpgp/opgp_key.c (revision 347759) @@ -1,352 +1,369 @@ /*- * Copyright (c) 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 __FBSDID("$FreeBSD$"); #include "../libsecureboot-priv.h" #include "decode.h" #include "packet.h" /** * @brief decode user-id packet * * This is trivial * * @sa rfc4880:5.11 */ ssize_t decode_user(int tag, unsigned char **pptr, size_t len, OpenPGP_user *user) { char *cp; if (tag == 13) { user->id = malloc(len + 1); strncpy(user->id, (char *)*pptr, len); user->id[len] = '\0'; user->name = user->id; cp = strchr(user->id, '<'); if (cp > user->id) { user->id = strdup(user->id); cp[-1] = '\0'; } } *pptr += len; return ((ssize_t)len); } /** * @brief decode a key packet * * We only really support v4 and RSA * * @sa rfc4880:5.5.1.1 */ ssize_t decode_key(int tag, unsigned char **pptr, size_t len, OpenPGP_key *key) { unsigned char *ptr; int version; #ifdef USE_BEARSSL br_sha1_context mctx; unsigned char mdata[br_sha512_SIZE]; size_t mlen; #else RSA *rsa = NULL; const EVP_MD *md = NULL; EVP_MD_CTX mctx; unsigned char mdata[EVP_MAX_MD_SIZE]; unsigned int mlen; #endif if (tag != 6) return (-1); key->key = NULL; ptr = *pptr; version = *ptr; if (version == 4) { /* all we support really */ /* comput key fingerprint and id @sa rfc4880:12.2 */ mdata[0] = 0x99; /* rfc4880: 12.2.a.1 */ mdata[1] = (len >> 8) & 0xff; mdata[2] = len & 0xff; #ifdef USE_BEARSSL br_sha1_init(&mctx); br_sha1_update(&mctx, mdata, 3); br_sha1_update(&mctx, ptr, len); br_sha1_out(&mctx, mdata); mlen = br_sha1_SIZE; #else md = EVP_get_digestbyname("sha1"); EVP_DigestInit(&mctx, md); EVP_DigestUpdate(&mctx, mdata, 3); EVP_DigestUpdate(&mctx, ptr, len); mlen = (unsigned int)sizeof(mdata); EVP_DigestFinal(&mctx, mdata, &mlen); #endif key->id = octets2hex(&mdata[mlen - 8], 8); } ptr += 1; /* done with version */ ptr += 4; /* skip ctime */ if (version == 3) ptr += 2; /* valid days */ key->sig_alg = *ptr++; if (key->sig_alg == 1) { /* RSA */ #ifdef USE_BEARSSL key->key = NEW(br_rsa_public_key); if (!key->key) goto oops; key->key->n = mpi2bn(&ptr, &key->key->nlen); key->key->e = mpi2bn(&ptr, &key->key->elen); #else rsa = RSA_new(); if (!rsa) goto oops; rsa->n = mpi2bn(&ptr); rsa->e = mpi2bn(&ptr); key->key = EVP_PKEY_new(); if (!key->key || !rsa->n || !rsa->e) { goto oops; } if (!EVP_PKEY_set1_RSA(key->key, rsa)) goto oops; #endif } /* we are done */ return ((ssize_t)len); oops: #ifdef USE_BEARSSL free(key->key); key->key = NULL; #else if (rsa) RSA_free(rsa); if (key->key) { EVP_PKEY_free(key->key); key->key = NULL; } #endif return (-1); } static OpenPGP_key * load_key_buf(unsigned char *buf, size_t nbytes) { unsigned char *data = NULL; unsigned char *ptr; ssize_t rc; int tag; OpenPGP_key *key; if (!buf) return (NULL); initialize(); if (!(buf[0] & OPENPGP_TAG_ISTAG)) { data = dearmor((char *)buf, nbytes, &nbytes); ptr = data; } else ptr = buf; key = NEW(OpenPGP_key); if (key) { rc = decode_packet(0, &ptr, nbytes, (decoder_t)decode_key, key); if (rc < 0) { free(key); key = NULL; } else if (rc > 8) { int isnew, ltype; tag = decode_tag(ptr, &isnew, <ype); if (tag == 13) { key->user = NEW(OpenPGP_user); rc = decode_packet(0, &ptr, (size_t)rc, (decoder_t)decode_user, key->user); } } } free(data); return (key); } static LIST_HEAD(, OpenPGP_key_) trust_list; /** * @brief add a key to our list */ void openpgp_trust_add(OpenPGP_key *key) { static int once = 0; if (!once) { once = 1; LIST_INIT(&trust_list); } if (key) LIST_INSERT_HEAD(&trust_list, key, entries); } /** * @brief if keyID is in our list return the key * * @return key or NULL */ OpenPGP_key * openpgp_trust_get(const char *keyID) { OpenPGP_key *key; openpgp_trust_add(NULL); /* initialize if needed */ LIST_FOREACH(key, &trust_list, entries) { if (strcmp(key->id, keyID) == 0) return (key); } return (NULL); } /** * @brief load a key from file */ OpenPGP_key * load_key_file(const char *kfile) { unsigned char *data = NULL; size_t n; OpenPGP_key *key; data = read_file(kfile, &n); key = load_key_buf(data, n); free(data); openpgp_trust_add(key); return (key); } #include #ifndef _STANDALONE /* we can lookup keyID in filesystem */ static const char *trust_store[] = { "/var/db/trust", "/etc/db/trust", NULL, }; /** * @brief lookup key id in trust store * */ static OpenPGP_key * load_trusted_key_id(const char *keyID) { char kfile[MAXPATHLEN]; const char **tp; size_t n; for (tp = trust_store; *tp; tp++) { n = (size_t)snprintf(kfile, sizeof(kfile), "%s/%s", *tp, keyID); if (n >= sizeof(kfile)) return (NULL); if (access(kfile, R_OK) == 0) { return (load_key_file(kfile)); } } return (NULL); } #endif /** * @brief return key if trusted */ OpenPGP_key * load_key_id(const char *keyID) { - static int once = 0; OpenPGP_key *key; - if (!once) { + key = openpgp_trust_get(keyID); +#ifndef _STANDALONE + if (!key) + key = load_trusted_key_id(keyID); +#endif + return (key); +} + +/** + * @brief initialize our internal trust store if any + */ +int +openpgp_trust_init(void) +{ + static int once = -1; #ifdef HAVE_TA_ASC - const char **tp; - char *cp; - size_t n; + OpenPGP_key *key; + const char **tp; + char *cp; + size_t n; +#endif + if (once < 0) { + once = 0; +#ifdef HAVE_TA_ASC for (tp = ta_ASC; *tp; tp++) { if ((cp = strdup(*tp))) { n = strlen(cp); key = load_key_buf((unsigned char *)cp, n); free(cp); - openpgp_trust_add(key); + if (key) { + openpgp_trust_add(key); + once++; + } } } -#endif - once = 1; } - key = openpgp_trust_get(keyID); -#ifndef _STANDALONE - if (!key) - key = load_trusted_key_id(keyID); #endif - return (key); + return (once); } /** * @brief test that we can verify a signature * * Unlike X.509 certificates, we only support RSA keys * so we stop after first successful signature verification * (which should also be the first attempt ;-) */ int openpgp_self_tests(void) { static int rc = -1; /* remember result */ #ifdef HAVE_VC_ASC const char **vp, **tp; char *fdata, *sdata = NULL; size_t fbytes, sbytes; - for (tp = ta_ASC, vp = vc_ASC; *tp && *vp && rc; tp++, vp++) { - if ((fdata = strdup(*tp)) && - (sdata = strdup(*vp))) { - fbytes = strlen(fdata); - sbytes = strlen(sdata); - rc = openpgp_verify("ta_ASC", - (unsigned char *)fdata, fbytes, - (unsigned char *)sdata, sbytes, 0); - printf("Testing verify OpenPGP signature:\t\t%s\n", - rc ? "Failed" : "Passed"); + if (openpgp_trust_init() > 0) { + for (tp = ta_ASC, vp = vc_ASC; *tp && *vp && rc; tp++, vp++) { + if ((fdata = strdup(*tp)) && + (sdata = strdup(*vp))) { + fbytes = strlen(fdata); + sbytes = strlen(sdata); + rc = openpgp_verify("ta_ASC", + (unsigned char *)fdata, fbytes, + (unsigned char *)sdata, sbytes, 0); + printf("Testing verify OpenPGP signature:\t\t%s\n", + rc ? "Failed" : "Passed"); + } + free(fdata); + free(sdata); } - free(fdata); - free(sdata); } #endif return (rc); } Index: stable/12/lib/libsecureboot/tests/Makefile =================================================================== --- stable/12/lib/libsecureboot/tests/Makefile (revision 347758) +++ stable/12/lib/libsecureboot/tests/Makefile (revision 347759) @@ -1,20 +1,21 @@ # $FreeBSD$ .include PROG= tvo SRCS+= tvo.c CFLAGS+= -DUNIT_TEST -g -O0 LIBADD+= bearssl MAN= NO_SHARED= # we want to test verify_file api too # which requires a kludge or two +MK_LOADER_EFI_SECUREBOOT= no .include "../Makefile.libsa.inc" BRSSL_CFLAGS := ${BRSSL_CFLAGS:N-DNO_STDIO} XCFLAGS.verify_file += -DSOPEN_MAX=64 .include Index: stable/12/lib/libsecureboot/vets.c =================================================================== --- stable/12/lib/libsecureboot/vets.c (revision 347758) +++ stable/12/lib/libsecureboot/vets.c (revision 347759) @@ -1,900 +1,900 @@ /*- * 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 __FBSDID("$FreeBSD$"); /** * @file vets.c - trust store * @brief verify signatures * * We leverage code from BearSSL www.bearssl.org */ #include #include #define NEED_BRSSL_H #include "libsecureboot-priv.h" #include #include #ifndef TRUST_ANCHOR_STR # define TRUST_ANCHOR_STR ta_PEM #endif #define SECONDS_PER_DAY 86400 #define X509_DAYS_TO_UTC0 719528 int DebugVe = 0; 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; 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); } /* this is the time we use for verifying certs */ static time_t ve_utc = 0; /** * @brief * set ve_utc used for certificate verification * * @param[in] utc * time - ignored unless greater than current value. */ void ve_utc_set(time_t utc) { if (utc > ve_utc) { 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); } /* 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) { 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); } 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)); } size_t ve_forbidden_anchors_add(br_x509_certificate *xcs, size_t num) { return (ve_anchors_add(xcs, num, &forbidden_anchors)); } /** * @brief * initialize our trust_anchors from ta_PEM */ int ve_trust_init(void) { #ifdef TRUST_ANCHOR_STR br_x509_certificate *xcs; #endif static int once = -1; size_t num; if (once >= 0) return (once); ve_utc_set(time(NULL)); #ifdef BUILD_UTC ve_utc_set(BUILD_UTC); /* just in case */ #endif ve_error_set(NULL); /* make sure it is empty */ #ifdef VE_PCR_SUPPORT ve_pcr_init(); #endif #ifdef TRUST_ANCHOR_STR xcs = parse_certificates(__DECONST(unsigned char *, TRUST_ANCHOR_STR), sizeof(TRUST_ANCHOR_STR), &num); if (xcs != NULL) num = ve_trust_anchors_add(xcs, num); #endif once = (int) VEC_LEN(trust_anchors); - +#ifdef VE_OPENPGP_SUPPORT + once += openpgp_trust_init(); +#endif return (once); } /** * 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 _STANDALONE /* * 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 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) { ve_error_set("Validation failed, err = %d", err); } 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) { char hexbuf[br_sha512_SIZE * 2 + 2]; unsigned char rhbuf[br_sha512_SIZE]; char *hex; 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); 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); } 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 & 1) != 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(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, sizeof(VE_HASH_KAT_STR), \ + 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) - return (0); - /* - * 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; + 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); + 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); } - if (!once) - printf("Testing verify certificate:\t\t\tFailed\n"); - xfree(xcs); -#else - printf("No X.509 self tests\n"); #endif /* VERIFY_CERTS_STR */ #ifdef VE_OPENPGP_SUPPORT if (!openpgp_self_tests()) once++; #endif return (once); }