Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F106322505
D16335.id46360.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
117 KB
Referenced Files
None
Subscribers
None
D16335.id46360.diff
View Options
Index: lib/libve/Makefile
===================================================================
--- /dev/null
+++ lib/libve/Makefile
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+.include <src.opts.mk>
+
+.if ${MK_BEARSSL} == "yes"
+
+LIB= ve
+
+.include "../libbearssl/Makefile.inc"
+.include "Makefile.inc"
+
+INCS= h/libve.h
+
+.include <bsd.lib.mk>
+
+.else
+all ${.TARGETS}:
+.endif
Index: lib/libve/Makefile.depend
===================================================================
--- /dev/null
+++ lib/libve/Makefile.depend
@@ -0,0 +1,17 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
Index: lib/libve/Makefile.depend.host
===================================================================
--- /dev/null
+++ lib/libve/Makefile.depend.host
@@ -0,0 +1,12 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ lib/libstand \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
Index: lib/libve/Makefile.inc
===================================================================
--- /dev/null
+++ lib/libve/Makefile.inc
@@ -0,0 +1,130 @@
+# $FreeBSD$
+
+libve_src:= ${.PARSEDIR}
+
+CFLAGS+= -I${libve_src}/h
+
+.PATH: ${.PARSEDIR}
+
+SRCS+= \
+ readfile.c \
+ brf.c \
+ vesigned.c \
+ vets.c
+
+.if ${.CURDIR:M*libve*} != ""
+SRCS+= veta.c
+.if ${.CURDIR:T} == "tests"
+SRCS+= vectx.c veopen.c
+.endif
+.endif
+
+CFLAGS+= ${XCFLAGS.${.TARGET:T:R}:U}
+
+# we use a couple of files from ${BEARSSL}/tools
+# but need to make sure they are safe for use in libsa
+BRSSL_SED:= ${.PARSEDIR}/brssl.sed
+
+BRSSL_SRCS+= \
+ xmem.c \
+ vector.c \
+
+.for s in ${BEARSSL}/tools/brssl.h ${BRSSL_SRCS:S,^,${BEARSSL}/tools/,}
+${s:T}: $s
+ sed -f ${BRSSL_SED} $s > ${.TARGET}
+.endfor
+
+BRSSL_CFLAGS+= -I.
+
+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"
+CFLAGS+= -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
+
+
+# Generate ta.h containing ta_PEM as an array of strings
+# each holding a PEM encoded trust anchor.
+#
+# 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
+# to use as a Known Answer Test (needed for FIPS 140-2)
+#
+vets.o: ta.h
+ta.h: ${.ALLTARGETS:M[tv]*pem:O:u}
+ @( echo '/* Autogenerated - DO NOT EDIT!!! */'; echo; \
+ echo "static char ta_PEM[] ="; \
+ for f in ${.ALLSRC:N*crl*:Mt*.pem}; do \
+ sed 's,^\(.*\),"\1\\n",' $$f; \
+ done; echo ';'; echo; ) > ${.TARGET}
+ echo "${.newline}${VE_HASH_LIST:@H@static char vc_$H[] = \"`cat ${.ALLSRC:N*crl*:Mv*.pem} | ${$H:U${H:tl}}`\";${.newline}@}" >> ${.TARGET}
+.if ${VE_SELF_TESTS} != "no"
+ ( echo "static char vc_PEM[] ="; \
+ for f in ${.ALLSRC:N*crl*:Mv*.pem}; do \
+ sed 's,^\(.*\),"\1\\n",' $$f; \
+ done; echo ';'; \
+ ) >> ${.TARGET}
+.endif
+.if !empty(BUILD_UTC_FILE)
+ echo '#define BUILD_UTC ${${STAT:Ustat} -f %m ${BUILD_UTC_FILE}:L:sh}' >> ${.TARGET} ${.OODATE:MNOMETA_CMP}
+.endif
+
+# This header records our preference for signature extensions.
+vesigned.o: 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 'NULL };' ) > ${.TARGET}
+
+
+.for s in ${BRSSL_SRCS} brf.c vets.c veta.c
+$s: brssl.h
+XCFLAGS.${s:R}+= ${BRSSL_CFLAGS}
+.endfor
Index: lib/libve/Makefile.libsa.inc
===================================================================
--- /dev/null
+++ lib/libve/Makefile.libsa.inc
@@ -0,0 +1,37 @@
+# $FreeBSD$
+
+BRSSL_CFLAGS+= -DNO_STDIO
+
+.include "Makefile.inc"
+
+# for "measured boot"
+# loader puts the equivalent of TPM's PCR register into kenv
+# this is not as good but *way* simpler than talking to TPM
+CFLAGS+= -DVE_PCR_SUPPORT
+
+# sources that only apply to libsa
+SRCS+= \
+ vectx.c \
+ veopen.c \
+ vepcr.c \
+ verify.c \
+
+# this is the list of paths (relative to a file
+# that we need to verify) used to find a signed manifest.
+# the signature extensions in VE_SIGNATURE_EXT_LIST
+# will be applied to each.
+VE_MANIFEST_LIST?= manifest ../manifest
+
+verify.o: manifests.h
+manifests.h:
+ @( echo '/* Autogenerated - DO NOT EDIT!!! */'; echo; \
+ echo "static const char *manifest_names[] = {"; \
+ echo '${VE_MANIFEST_LIST:@m@"$m",${.newline}@}'; \
+ echo 'NULL };' ) > ${.TARGET}
+
+XCFLAGS.verify+= \
+ -DVE_DEBUG_LEVEL=${VE_DEBUG_LEVEL:U0} \
+
+.if !empty(MANIFEST_SKIP_ALWAYS)
+XCFLAGS.verify+= -DMANIFEST_SKIP_ALWAYS=\"${MANIFEST_SKIP_ALWAYS}\"
+.endif
Index: lib/libve/brf.c
===================================================================
--- /dev/null
+++ lib/libve/brf.c
@@ -0,0 +1,403 @@
+// The functions here are derrived from BearSSL/tools/*.c
+// When that is refactored suitably we can use them directly.
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define NEED_BRSSL_H
+#include "libve-priv.h"
+#include <brssl.h>
+
+
+static int
+is_ign(int c)
+{
+ if (c == 0) {
+ return (0);
+ }
+ if (c <= 32 || c == '-' || c == '_' || c == '.'
+ || c == '/' || c == '+' || c == ':')
+ {
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Get next non-ignored character, normalised:
+ * ASCII letters are converted to lowercase
+ * control characters, space, '-', '_', '.', '/', '+' and ':' are ignored
+ * A terminating zero is returned as 0.
+ */
+static int
+next_char(const char **ps, const char *limit)
+{
+ for (;;) {
+ int c;
+
+ if (*ps == limit) {
+ return (0);
+ }
+ c = *(*ps) ++;
+ if (c == 0) {
+ return (0);
+ }
+ if (c >= 'A' && c <= 'Z') {
+ c += 'a' - 'A';
+ }
+ if (!is_ign(c)) {
+ return (c);
+ }
+ }
+}
+
+/*
+ * Partial string equality comparison, with normalisation.
+ */
+static int
+eqstr_chunk(const char *s1, size_t s1_len, const char *s2, size_t s2_len)
+{
+ const char *lim1, *lim2;
+
+ lim1 = s1 + s1_len;
+ lim2 = s2 + s2_len;
+ for (;;) {
+ int c1, c2;
+
+ c1 = next_char(&s1, lim1);
+ c2 = next_char(&s2, lim2);
+ if (c1 != c2) {
+ return (0);
+ }
+ if (c1 == 0) {
+ return (1);
+ }
+ }
+}
+
+/* see brssl.h */
+int
+eqstr(const char *s1, const char *s2)
+{
+ return (eqstr_chunk(s1, strlen(s1), s2, strlen(s2)));
+}
+
+int
+looks_like_DER(const unsigned char *buf, size_t len)
+{
+ int fb;
+ size_t dlen;
+
+ if (len < 2) {
+ return (0);
+ }
+ if (*buf ++ != 0x30) {
+ return (0);
+ }
+ fb = *buf ++;
+ len -= 2;
+ if (fb < 0x80) {
+ return ((size_t)fb == len);
+ } else if (fb == 0x80) {
+ return (0);
+ } else {
+ fb -= 0x80;
+ if (len < (size_t)fb + 2) {
+ return (0);
+ }
+ len -= (size_t)fb;
+ dlen = 0;
+ while (fb -- > 0) {
+ if (dlen > (len >> 8)) {
+ return (0);
+ }
+ dlen = (dlen << 8) + (size_t)*buf ++;
+ }
+ return (dlen == len);
+ }
+}
+
+static void
+vblob_append(void *cc, const void *data, size_t len)
+{
+ bvector *bv;
+
+ bv = cc;
+ VEC_ADDMANY(*bv, data, len);
+}
+
+void
+free_pem_object_contents(pem_object *po)
+{
+ if (po != NULL) {
+ xfree(po->name);
+ xfree(po->data);
+ }
+}
+
+pem_object *
+decode_pem(const void *src, size_t len, size_t *num)
+{
+ VECTOR(pem_object) pem_list = VEC_INIT;
+ br_pem_decoder_context pc;
+ pem_object po, *pos;
+ const unsigned char *buf;
+ bvector bv = VEC_INIT;
+ int inobj;
+ int extra_nl;
+
+ *num = 0;
+ br_pem_decoder_init(&pc);
+ buf = src;
+ inobj = 0;
+ po.name = NULL;
+ po.data = NULL;
+ po.data_len = 0;
+ extra_nl = 1;
+ while (len > 0) {
+ size_t tlen;
+
+ tlen = br_pem_decoder_push(&pc, buf, len);
+ buf += tlen;
+ len -= tlen;
+ switch (br_pem_decoder_event(&pc)) {
+
+ case BR_PEM_BEGIN_OBJ:
+ po.name = xstrdup(br_pem_decoder_name(&pc));
+ br_pem_decoder_setdest(&pc, vblob_append, &bv);
+ inobj = 1;
+ break;
+
+ case BR_PEM_END_OBJ:
+ if (inobj) {
+ po.data = VEC_TOARRAY(bv);
+ po.data_len = VEC_LEN(bv);
+ VEC_ADD(pem_list, po);
+ VEC_CLEAR(bv);
+ po.name = NULL;
+ po.data = NULL;
+ po.data_len = 0;
+ inobj = 0;
+ }
+ break;
+
+ case BR_PEM_ERROR:
+ xfree(po.name);
+ VEC_CLEAR(bv);
+ ve_error_set("ERROR: invalid PEM encoding");
+ VEC_CLEAREXT(pem_list, &free_pem_object_contents);
+ return (NULL);
+ }
+
+ /*
+ * We add an extra newline at the end, in order to
+ * support PEM files that lack the newline on their last
+ * line (this is somwehat invalid, but PEM format is not
+ * standardised and such files do exist in the wild, so
+ * we'd better accept them).
+ */
+ if (len == 0 && extra_nl) {
+ extra_nl = 0;
+ buf = (const unsigned char *)"\n";
+ len = 1;
+ }
+ }
+ if (inobj) {
+ ve_error_set("ERROR: unfinished PEM object");
+ xfree(po.name);
+ VEC_CLEAR(bv);
+ VEC_CLEAREXT(pem_list, &free_pem_object_contents);
+ return (NULL);
+ }
+
+ *num = VEC_LEN(pem_list);
+ VEC_ADD(pem_list, po);
+ pos = VEC_TOARRAY(pem_list);
+ VEC_CLEAR(pem_list);
+ return (pos);
+}
+
+br_x509_certificate *
+parse_certificates(unsigned char *buf, size_t len, size_t *num)
+{
+ VECTOR(br_x509_certificate) cert_list = VEC_INIT;
+ pem_object *pos;
+ size_t u, num_pos;
+ br_x509_certificate *xcs;
+ br_x509_certificate dummy;
+
+ *num = 0;
+
+ /*
+ * Check for a DER-encoded certificate.
+ */
+ if (looks_like_DER(buf, len)) {
+ xcs = xmalloc(2 * sizeof *xcs);
+ xcs[0].data = buf;
+ xcs[0].data_len = len;
+ xcs[1].data = NULL;
+ xcs[1].data_len = 0;
+ *num = 1;
+ return (xcs);
+ }
+
+ pos = decode_pem(buf, len, &num_pos);
+ if (pos == NULL) {
+ return (NULL);
+ }
+ for (u = 0; u < num_pos; u ++) {
+ if (eqstr(pos[u].name, "CERTIFICATE")
+ || eqstr(pos[u].name, "X509 CERTIFICATE"))
+ {
+ br_x509_certificate xc;
+
+ xc.data = pos[u].data;
+ xc.data_len = pos[u].data_len;
+ pos[u].data = NULL;
+ VEC_ADD(cert_list, xc);
+ }
+ }
+ for (u = 0; u < num_pos; u ++) {
+ free_pem_object_contents(&pos[u]);
+ }
+ xfree(pos);
+
+ if (VEC_LEN(cert_list) == 0) {
+ return (NULL);
+ }
+ *num = VEC_LEN(cert_list);
+ dummy.data = NULL;
+ dummy.data_len = 0;
+ VEC_ADD(cert_list, dummy);
+ xcs = VEC_TOARRAY(cert_list);
+ VEC_CLEAR(cert_list);
+ return (xcs);
+}
+
+br_x509_certificate *
+read_certificates(const char *fname, size_t *num)
+{
+ br_x509_certificate *xcs;
+ unsigned char *buf;
+ size_t len;
+
+ *num = 0;
+
+ /*
+ * TODO: reading the whole file is crude; we could parse them
+ * in a streamed fashion. But it does not matter much in practice.
+ */
+ buf = read_file(fname, &len);
+ if (buf == NULL) {
+ return (NULL);
+ }
+ xcs = parse_certificates(buf, len, num);
+ if (xcs == NULL) {
+ ve_error_set("ERROR: no certificate in file '%s'\n", fname);
+ }
+ xfree(buf);
+ return (xcs);
+}
+
+/* see brssl.h */
+void
+free_certificates(br_x509_certificate *certs, size_t num)
+{
+ size_t u;
+
+ for (u = 0; u < num; u ++) {
+ xfree(certs[u].data);
+ }
+ xfree(certs);
+}
+
+
+static void
+dn_append(void *ctx, const void *buf, size_t len)
+{
+ VEC_ADDMANY(*(bvector *)ctx, buf, len);
+}
+
+int
+certificate_to_trust_anchor_inner(br_x509_trust_anchor *ta,
+ br_x509_certificate *xc)
+{
+ br_x509_decoder_context dc;
+ bvector vdn = VEC_INIT;
+ br_x509_pkey *pk;
+
+ br_x509_decoder_init(&dc, dn_append, &vdn);
+ br_x509_decoder_push(&dc, xc->data, xc->data_len);
+ pk = br_x509_decoder_get_pkey(&dc);
+ if (pk == NULL) {
+ ve_error_set("ERROR: CA decoding failed with error %d\n",
+ br_x509_decoder_last_error(&dc));
+ VEC_CLEAR(vdn);
+ return (-1);
+ }
+ ta->dn.data = VEC_TOARRAY(vdn);
+ ta->dn.len = VEC_LEN(vdn);
+ VEC_CLEAR(vdn);
+ ta->flags = 0;
+ if (br_x509_decoder_isCA(&dc)) {
+ ta->flags |= BR_X509_TA_CA;
+ }
+ switch (pk->key_type) {
+ case BR_KEYTYPE_RSA:
+ ta->pkey.key_type = BR_KEYTYPE_RSA;
+ ta->pkey.key.rsa.n = xblobdup(pk->key.rsa.n, pk->key.rsa.nlen);
+ ta->pkey.key.rsa.nlen = pk->key.rsa.nlen;
+ ta->pkey.key.rsa.e = xblobdup(pk->key.rsa.e, pk->key.rsa.elen);
+ ta->pkey.key.rsa.elen = pk->key.rsa.elen;
+ break;
+ case BR_KEYTYPE_EC:
+ ta->pkey.key_type = BR_KEYTYPE_EC;
+ ta->pkey.key.ec.curve = pk->key.ec.curve;
+ ta->pkey.key.ec.q = xblobdup(pk->key.ec.q, pk->key.ec.qlen);
+ ta->pkey.key.ec.qlen = pk->key.ec.qlen;
+ break;
+ default:
+ ve_error_set("ERROR: unsupported public key type in CA\n");
+ xfree(ta->dn.data);
+ return (-1);
+ }
+ return (0);
+}
+
+/* see brssl.h */
+void
+free_ta_contents(br_x509_trust_anchor *ta)
+{
+ xfree(ta->dn.data);
+ switch (ta->pkey.key_type) {
+ case BR_KEYTYPE_RSA:
+ xfree(ta->pkey.key.rsa.n);
+ xfree(ta->pkey.key.rsa.e);
+ break;
+ case BR_KEYTYPE_EC:
+ xfree(ta->pkey.key.ec.q);
+ break;
+ }
+}
Index: lib/libve/brssl.sed
===================================================================
--- /dev/null
+++ lib/libve/brssl.sed
@@ -0,0 +1,17 @@
+# $FreeBSD$
+
+/fprintf/i\
+#ifndef _STANDALONE
+
+/EXIT_FAILURE/a\
+#endif
+
+/include <stdio/i\
+#ifndef _STANDALONE
+
+/include .bearssl.h/i\
+#elif !defined(STAND_H) \
+#include <stand.h> \
+#endif \
+
+
Index: lib/libve/h/libve.h
===================================================================
--- /dev/null
+++ lib/libve/h/libve.h
@@ -0,0 +1,89 @@
+/*-
+ * 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$
+ */
+#include <sys/param.h>
+#ifdef _STANDALONE
+#include <stand.h>
+#else
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#endif
+
+#include <bearssl.h>
+
+#ifndef NEED_BRSSL_H
+unsigned char * read_file(const char *, size_t *);
+#endif
+
+extern int DebugVe;
+
+#define DEBUG_PRINTF(n, x) if (DebugVe >= n) printf x
+
+int ve_trust_init(void);
+int ve_trust_add(const char *);
+void ve_debug_set(int);
+void ve_utc_set(time_t utc);
+char *ve_error_get(void);
+int ve_error_set(const char *, ...) __printflike(1,2);
+int ve_self_tests(void);
+
+void fingerprint_info_add(const char *, const char *, const char *,
+ const char *, struct stat *);
+
+int ve_check_hash(br_hash_compat_context *, const br_hash_class *,
+ const char *, const char *, size_t);
+
+struct vectx;
+struct vectx* vectx_open(int, const char *, off_t, struct stat *, int *);
+ssize_t vectx_read(struct vectx *, void *, size_t);
+off_t vectx_lseek(struct vectx *, off_t, int);
+int vectx_close(struct vectx *);
+
+char * hexdigest(char *, size_t, unsigned char *, size_t);
+int verify_fd(int, const char *, off_t, struct stat *);
+int verify_open(const char *, int);
+
+unsigned char *verify_signed(const char *, int);
+unsigned char *verify_sig(const char *, int);
+unsigned char *verify_asc(const char *, int); /* OpenPGP */
+
+void ve_pcr_init(void);
+void ve_pcr_update(unsigned char *, size_t);
+ssize_t ve_pcr_get(unsigned char *, size_t);
+
+/* flags for verify_{asc,sig,signed} */
+#define VEF_VERBOSE 1
+
+#define VE_FINGERPRINT_OK 1
+/* errors from verify_fd */
+#define VE_FINGERPRINT_NONE -2
+#define VE_FINGERPRINT_WRONG -3
+#define VE_FINGERPRINT_UNKNOWN -4 /* may not be an error */
Index: lib/libve/h/verify.h
===================================================================
--- /dev/null
+++ lib/libve/h/verify.h
@@ -0,0 +1,40 @@
+/*-
+ * 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.
+ */
+
+#define VE_GUESS -1 /* let verify_file work it out */
+#define VE_TRY 0 /* we don't mind if unverified */
+#define VE_WANT 1 /* we want this verified */
+#define VE_MUST 2 /* this must be verified */
+
+#define VE_VERIFIED 1 /* all good */
+#define VE_UNVERIFIED_OK 0 /* not verified but that's ok */
+#define VE_NOT_VERIFYING 2 /* we are not verifying */
+
+void ve_debug_set(int);
+int ve_status_get(int);
+int load_manifest(const char *, const char *, const char *, struct stat *);
+int verify_file(int, const char *, off_t, int);
+void verify_pcr_export(void);
+
Index: lib/libve/libve-priv.h
===================================================================
--- /dev/null
+++ lib/libve/libve-priv.h
@@ -0,0 +1,45 @@
+/*-
+ * 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$
+ */
+
+/* public api */
+#include "libve.h"
+
+size_t ve_trust_anchors_add(br_x509_certificate *, 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 openpgp_self_tests(void);
+
Index: lib/libve/local.trust.mk
===================================================================
--- /dev/null
+++ lib/libve/local.trust.mk
@@ -0,0 +1,89 @@
+# $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
+
+VE_SIGNATURE_LIST= \
+ ECDSA
+
+VE_SIGNATURE_EXT_LIST= \
+ esig
+
+VE_SELF_TESTS= yes
+
+.if ${MACHINE} == "host" && ${.CURDIR:T} == "tests"
+# for testing
+VE_HASH_LIST+= \
+ SHA512
+
+VE_SIGNATURE_LIST+= \
+ RSA \
+ DEPRECATED_RSA_SHA1
+
+VE_SIGNATURE_EXT_LIST+= \
+ sig
+.endif
+
+SIGNER = ${SB_TOOLS_PATH:U/volume/buildtools/bin}/sign.py
+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
+
+.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
+.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
+
+.if ${VE_SELF_TESTS} != "no"
+# these are for verification self test
+vc_ec.pem: ecerts.pem _2ndLAST_PEM_USE
+.endif
+.endif
+
+.if ${VE_SIGNATURE_LIST:tu:MRSA} != ""
+ta_rsa.pem: rcerts.pem _LAST_PEM_USE
+.if ${VE_SELF_TESTS} != "no"
+vc_rsa.pem: rcerts.pem _2ndLAST_PEM_USE
+.endif
+.endif
+
+# we take the mtime of this as our baseline time
+BUILD_UTC_FILE= ecerts.pem
Index: lib/libve/openpgp/Makefile.inc
===================================================================
--- /dev/null
+++ lib/libve/openpgp/Makefile.inc
@@ -0,0 +1,33 @@
+# $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: ta_asc.h
+
+# This header records our OpenPGP trust anchors and tests
+ta_asc.h: ${.ALLTARGETS:M[tv]*.asc:O:u}
+.if ${VE_SIGNATURE_LIST:MOPENPGP} != ""
+ @( echo '/* Autogenerated - DO NOT EDIT!!! */'; echo; \
+ echo "#define HAVE_TA_ASC 1"; \
+ echo "static char ta_ASC[] ="; \
+ for f in ${.ALLSRC:Mt*.asc}; do \
+ sed -e '/:/d' -e 's,^\(.*\),"\1\\n",' $$f; \
+ done; echo ';'; echo; ) > ${.TARGET}
+.if ${VE_SELF_TESTS} != "no"
+ @( echo "#define HAVE_VC_ASC 1"; \
+ echo "static char vc_ASC[] ="; \
+ for f in ${.ALLSRC:Mv*.asc}; do \
+ sed 's,^\(.*\),"\1\\n",' $$f; \
+ done; echo ';'; \
+ ) >> ${.TARGET}
+.endif
+.endif
Index: lib/libve/openpgp/dearmor.c
===================================================================
--- /dev/null
+++ lib/libve/openpgp/dearmor.c
@@ -0,0 +1,144 @@
+/*-
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define NEED_BRSSL_H
+#include <libve.h>
+#include <brssl.h>
+
+#include "decode.h"
+
+/**
+ * @brief decode ascii armor
+ *
+ * once we get rid of the trailing checksum
+ * we can treat as PEM.
+ *
+ * @sa rfc4880:6.2
+ */
+unsigned char *
+dearmor(char *pem, size_t nbytes, size_t *len)
+{
+#ifdef USE_BEARSSL
+ pem_object *po;
+ size_t npo;
+#else
+ BIO *bp;
+ char *name = NULL;
+ char *header = NULL;
+#endif
+ unsigned char *data = NULL;
+ char *cp;
+ char *ep;
+
+ /* we need to remove the Armor tail */
+ if ((cp = strstr((char *)pem, "\n=")) &&
+ (ep = strstr(cp, "\n---"))) {
+ memmove(cp, ep, nbytes - (size_t)(ep - pem));
+ nbytes -= (size_t)(ep - cp);
+ pem[nbytes] = '\0';
+ }
+#ifdef USE_BEARSSL
+ /* we also need to remove any headers */
+ if ((cp = strstr((char *)pem, "---\n")) &&
+ (ep = strstr(cp, "\n\n"))) {
+ cp += 4;
+ ep += 2;
+ memmove(cp, ep, nbytes - (size_t)(ep - pem));
+ nbytes -= (size_t)(ep - cp);
+ pem[nbytes] = '\0';
+ }
+ if ((po = decode_pem(pem, nbytes, &npo))) {
+ data = po->data;
+ *len = po->data_len;
+ }
+#else
+ if ((bp = BIO_new_mem_buf(pem, (int)nbytes))) {
+ long llen = (long)nbytes;
+
+ if (!PEM_read_bio(bp, &name, &header, &data, &llen))
+ data = NULL;
+ BIO_free(bp);
+ *len = (size_t)llen;
+ }
+#endif
+ return (data);
+}
+
+#ifdef MAIN_DEARMOR
+#include <unistd.h>
+#include <fcntl.h>
+#include <err.h>
+
+/*
+ * Mostly a unit test.
+ */
+int
+main(int argc, char *argv[])
+{
+ const char *infile, *outfile;
+ unsigned char *data;
+ size_t n, x;
+ int fd;
+ int o;
+
+ infile = outfile = NULL;
+ while ((o = getopt(argc, argv, "i:o:")) != -1) {
+ switch (o) {
+ case 'i':
+ infile = optarg;
+ break;
+ case 'o':
+ outfile = optarg;
+ break;
+ default:
+ errx(1, "unknown option: -%c", o);
+ }
+ }
+ if (!infile)
+ errx(1, "need -i infile");
+ if (outfile) {
+ if ((fd = open(outfile, O_WRONLY|O_CREAT|O_TRUNC)) < 0)
+ err(1, "cannot open %s", outfile);
+ } else {
+ fd = 1; /* stdout */
+ }
+ data = read_file(infile, &n);
+ if (!(data[0] & 0200))
+ data = dearmor(data, n, &n);
+ for (x = 0; x < n; ) {
+ o = write(fd, &data[x], (n - x));
+ if (o < 0)
+ err(1, "cannot write");
+ x += o;
+ }
+ if (fd != 1)
+ close(fd);
+ free(data);
+ return (0);
+}
+#endif
Index: lib/libve/openpgp/decode.h
===================================================================
--- /dev/null
+++ lib/libve/openpgp/decode.h
@@ -0,0 +1,52 @@
+/*-
+ * 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.
+ */
+/*
+ * $FreeBSD$
+ */
+
+#ifdef USE_BEARSSL
+unsigned char * mpi2bn(unsigned char **pptr, size_t *sz);
+#else
+# include <openssl/bn.h>
+# include <openssl/rsa.h>
+# include <openssl/evp.h>
+
+BIGNUM * mpi2bn(unsigned char **pptr);
+#endif
+
+#define NEW(x) calloc(1,sizeof(x))
+
+typedef int (*decoder_t)(int, unsigned char **, int, void *);
+
+unsigned char * i2octets(int n, int i);
+int octets2i(unsigned char *ptr, int n);
+char * octets2hex(unsigned char *ptr, int n);
+int decode_tag(unsigned char *ptr, int *isnew, int *ltype);
+unsigned char * decode_mpi(unsigned char **pptr, size_t *sz);
+unsigned char * dearmor(char *pem, size_t nbytes, size_t *len);
+int decode_packet(int want, unsigned char **pptr, size_t nbytes,
+ decoder_t decoder, void *decoder_arg);
+unsigned char * decode_subpacket(unsigned char **pptr, int *stag, int *sz);
+unsigned char * read_file(const char *filename, size_t *sz);
Index: lib/libve/openpgp/decode.c
===================================================================
--- /dev/null
+++ lib/libve/openpgp/decode.c
@@ -0,0 +1,304 @@
+/*-
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <libve.h>
+
+#include "decode.h"
+
+char *
+octets2hex(unsigned char *ptr, int n)
+{
+ char *hex;
+ char *cp;
+ int i;
+
+ hex = malloc((size_t)(2*n + 1));
+ if (hex) {
+ for (i = 0, cp = hex; i < n; i++) {
+ snprintf(&cp[i*2], 3, "%02X", ptr[i]);
+ }
+ }
+ return (hex);
+}
+
+unsigned char *
+i2octets(int n, int i)
+{
+ static unsigned char o[16];
+ int x, j;
+
+ if (n > 15)
+ return (NULL);
+ for (j = 0, x = n - 1; x >= 0; x--, j++) {
+ o[j] = (unsigned char)((i & (0xff << x * 8)) >> x * 8);
+ }
+ return (o);
+}
+
+int
+octets2i(unsigned char *ptr, int n)
+{
+ int i;
+ int val;
+
+ for (val = i = 0; i < n; i++) {
+ val |= (*ptr++ << ((n - i - 1) * 8));
+ }
+ return (val);
+}
+
+/**
+ * @brief decode packet tag
+ *
+ * Also indicate if new/old and in the later case
+ * the length type
+ *
+ * @sa rfc4880:4.2
+ */
+int
+decode_tag(unsigned char *ptr, int *isnew, int *ltype)
+{
+ int tag;
+
+ if (!ptr || !isnew || !ltype)
+ return (-1);
+ tag = *ptr;
+
+ if (!(tag & 0200))
+ return (-1); /* we are lost! */
+ *isnew = tag & 0100;
+ if (*isnew) {
+ *ltype = -1; /* irrelevant */
+ tag &= 077;
+ } else {
+ *ltype = tag & 03;
+ tag = (tag & 074) >> 2;
+ }
+ return (tag);
+}
+
+/**
+ * @brief return packet length
+ *
+ * @sa rfc4880:4.2.2
+ */
+static int
+decode_new_len(unsigned char **pptr)
+{
+ unsigned char *ptr;
+ int len = -1;
+
+ if (pptr == NULL)
+ return (-1);
+ ptr = *pptr;
+
+ if (!(*ptr < 224 || *ptr == 255))
+ return (-1); /* not supported */
+
+ if (*ptr < 192)
+ len = *ptr++;
+ else if (*ptr < 224) {
+ len = ((*ptr - 192) << 8) + *(ptr+1) + 192;
+ ptr++;
+ } else if (*ptr == 255) {
+ len = (*ptr++ << 24);
+ len |= (*ptr++ << 16);
+ len |= (*ptr++ < 8);
+ len |= *ptr++;
+ }
+
+ *pptr = ptr;
+ return (len);
+}
+
+/**
+ * @brief return packet length
+ *
+ * @sa rfc4880:4.2.1
+ */
+static int
+decode_len(unsigned char **pptr, int ltype)
+{
+ unsigned char *ptr;
+ int len;
+
+ if (ltype < 0)
+ return (decode_new_len(pptr));
+
+ if (pptr == NULL)
+ return (-1);
+
+ ptr = *pptr;
+
+ switch (ltype) {
+ case 0:
+ len = *ptr++;
+ break;
+ case 1:
+ len = (*ptr++ << 8);
+ len |= *ptr++;
+ break;
+ case 2:
+ len = *ptr++ << 24;
+ len |= *ptr++ << 16;
+ len |= *ptr++ << 8;
+ len |= *ptr++;
+ break;
+ case 3:
+ default:
+ /* Not supported */
+ len = -1;
+ }
+
+ *pptr = ptr;
+ return (len);
+}
+
+/**
+ * @brief return pointer and length of an mpi
+ *
+ * @sa rfc4880:3.2
+ */
+unsigned char *
+decode_mpi(unsigned char **pptr, size_t *sz)
+{
+ unsigned char *data;
+ unsigned char *ptr;
+ size_t mlen;
+
+ if (pptr == NULL || sz == NULL)
+ return (NULL);
+
+ ptr = *pptr;
+
+ mlen = (size_t)(*ptr++ << 8);
+ mlen |= (size_t)*ptr++; /* number of bits */
+ mlen = (mlen + 7) / 8; /* number of bytes */
+ *sz = mlen;
+ data = ptr;
+ ptr += mlen;
+ *pptr = ptr;
+ return (data);
+}
+
+/**
+ * @brief return an OpenSSL BIGNUM from mpi
+ *
+ * @sa rfc4880:3.2
+ */
+#ifdef USE_BEARSSL
+unsigned char *
+mpi2bn(unsigned char **pptr, size_t *sz)
+{
+ return (decode_mpi(pptr, sz));
+}
+#else
+BIGNUM *
+mpi2bn(unsigned char **pptr)
+{
+ BIGNUM *bn = NULL;
+ unsigned char *ptr;
+ int mlen;
+
+ if (pptr == NULL)
+ return (NULL);
+
+ ptr = *pptr;
+
+ mlen = (*ptr++ << 8);
+ mlen |= *ptr++; /* number of bits */
+ mlen = (mlen + 7) / 8; /* number of bytes */
+ bn = BN_bin2bn(ptr, mlen, NULL);
+ ptr += mlen;
+ *pptr = ptr;
+
+ return (bn);
+}
+#endif
+
+/**
+ * @brief decode a packet
+ *
+ * If want is set, check that the packet tag matches
+ * if all good, call the provided decoder with its arg
+ *
+ * @return count of unconsumed data
+ *
+ * @sa rfc4880:4.2
+ */
+int
+decode_packet(int want, unsigned char **pptr, size_t nbytes,
+ decoder_t decoder, void *decoder_arg)
+{
+ int tag;
+ unsigned char *ptr;
+ unsigned char *nptr;
+ int isnew, ltype;
+ int len;
+ int hlen;
+ int rc = 0;
+
+ nptr = ptr = *pptr;
+
+ tag = decode_tag(ptr, &isnew, <ype);
+
+ if (want > 0 && tag != want)
+ return (-1);
+ ptr++;
+
+ len = rc = decode_len(&ptr, ltype);
+ hlen = (int)(ptr - nptr);
+ nptr = ptr + len; /* consume it */
+
+ if (decoder)
+ rc = decoder(tag, &ptr, len, decoder_arg);
+ *pptr = nptr;
+ nbytes -= (size_t)(hlen + len);
+ if (rc < 0)
+ return (rc); /* error */
+ return ((int)nbytes); /* unconsumed data */
+}
+
+/**
+ * @brief decode a sub packet
+ *
+ * @sa rfc4880:5.2.3.1
+ */
+unsigned char *
+decode_subpacket(unsigned char **pptr, int *stag, int *sz)
+{
+ unsigned char *ptr;
+ int len;
+
+ ptr = *pptr;
+ len = decode_len(&ptr, -1);
+ *sz = (int)(len + ptr - *pptr);
+ *pptr = ptr + len;
+ *stag = *ptr++;
+ return (ptr);
+}
Index: lib/libve/openpgp/opgp_key.c
===================================================================
--- /dev/null
+++ lib/libve/openpgp/opgp_key.c
@@ -0,0 +1,342 @@
+/*-
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "../libve-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];
+#else
+ RSA *rsa = NULL;
+ const EVP_MD *md = NULL;
+ EVP_MD_CTX mctx;
+ unsigned char mdata[EVP_MAX_MD_SIZE];
+#endif
+ size_t mlen;
+
+ 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 = sizeof(mdata);
+ EVP_DigestFinal(&mctx,mdata,(unsigned int *)&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] & 0200) == 0) {
+ 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 <ta_asc.h>
+
+#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) {
+#ifdef HAVE_TA_ASC
+ char *cp;
+ size_t n;
+
+ if ((cp = strdup(ta_ASC))) {
+ n = strlen(cp);
+ key = load_key_buf((unsigned char *)cp, n);
+ free(cp);
+ openpgp_trust_add(key);
+ }
+#endif
+ once = 1;
+ }
+ key = openpgp_trust_get(keyID);
+#ifndef _STANDALONE
+ if (!key)
+ key = load_trusted_key_id(keyID);
+#endif
+ return (key);
+}
+
+/**
+ * @brief test that we can verify a signature
+ */
+int
+openpgp_self_tests(void)
+{
+ int rc = -1;
+#ifdef HAVE_VC_ASC
+ char *fdata, *sdata = NULL;
+ size_t fbytes, sbytes;
+
+ if ((fdata = strdup(ta_ASC)) &&
+ (sdata = strdup(vc_ASC))) {
+ fbytes = strlen(fdata);
+ sbytes = strlen(sdata);
+ rc = openpgp_verify("ta_ASC",
+ (unsigned char *)fdata, fbytes,
+ (unsigned char *)sdata, sbytes, 0);
+ if (!rc) {
+ printf("Testing verify OpenPGP signature:\t\tPassed\n");
+ }
+ }
+ free(fdata);
+ free(sdata);
+#endif
+ return (rc);
+}
Index: lib/libve/openpgp/opgp_sig.c
===================================================================
--- /dev/null
+++ lib/libve/openpgp/opgp_sig.c
@@ -0,0 +1,482 @@
+/*-
+ * 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.
+ */
+/*
+ * RCSid:
+ * from: signer.c,v 1.10 2018/03/23 01:14:30 sjg
+ *
+ * @(#) Copyright (c) 2012 Simon J. Gerraty
+ *
+ * This file is provided in the hope that it will
+ * be of use. There is absolutely NO WARRANTY.
+ * Permission to copy, redistribute or otherwise
+ * use this file is hereby granted provided that
+ * the above copyright notice and this notice are
+ * left intact.
+ *
+ * Please send copies of changes and bug-fixes to:
+ * sjg@crufty.net
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "../libve-priv.h"
+#define get_error_string ve_error_get
+#ifdef _STANDALONE
+#define warnx printf
+#else
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#endif
+
+#include "decode.h"
+#include "packet.h"
+
+#ifdef USE_BEARSSL
+void
+initialize (void)
+{
+#ifdef _STANDALONE
+ ve_trust_init();
+#endif
+}
+
+#else
+
+#include <openssl/err.h>
+
+/**
+ * @brief intialize OpenSSL
+ */
+void
+initialize(void)
+{
+ static int once;
+
+ if (once)
+ return);
+ once = 1;
+ //CRYPTO_malloc_init();
+ ERR_load_crypto_strings();
+ OpenSSL_add_all_algorithms();
+}
+
+/**
+ * @brief
+ * last error from OpenSSL as a string
+ */
+char *
+get_error_string(void)
+{
+ initialize();
+ return (ERR_error_string(ERR_get_error(), NULL));
+}
+#endif
+
+/**
+ * @brief decode a signature packet
+ *
+ * We only support RSA
+ *
+ * @sa rfc4880:5.2
+ */
+ssize_t
+decode_sig(int tag, unsigned char **pptr, size_t len, OpenPGP_sig *sig)
+{
+ unsigned char *ptr;
+ unsigned char *pgpbytes;
+ unsigned char *sp;
+ int version;
+ int hcount = 0;
+ int ucount = 0;
+ int stag = 0;
+ int n;
+
+ n = tag; /* avoid unused */
+
+ /*
+ * We need to keep a reference to the packet bytes
+ * as these form part of the signature data.
+ *
+ * @sa rfc4880:5.2.4
+ */
+ pgpbytes = ptr = *pptr;
+ version = *ptr++;
+ if (version == 3) {
+ ptr++;
+ sig->pgpbytes = malloc(5);
+ if (!sig->pgpbytes)
+ return (-1);
+ memcpy(sig->pgpbytes, ptr, 5);
+ sig->pgpbytes_len = 5;
+ sig->sig_type = *ptr++;
+ ptr += 4;
+ sig->key_id = octets2hex(ptr, 8);
+ ptr += 8;
+ sig->sig_alg = *ptr++;
+ sig->hash_alg = *ptr++;
+ } else if (version == 4) {
+ sig->sig_type = *ptr++;
+ sig->sig_alg = *ptr++;
+ sig->hash_alg = *ptr++;
+ hcount = octets2i(ptr, 2);
+ ptr += 2;
+ sig->pgpbytes_len = (size_t)hcount + 6;
+ sig->pgpbytes = malloc(sig->pgpbytes_len + 6);
+ if (!sig->pgpbytes)
+ return (-1);
+ memcpy(sig->pgpbytes, pgpbytes, sig->pgpbytes_len);
+ sp = &sig->pgpbytes[sig->pgpbytes_len];
+ *sp++ = 4;
+ *sp++ = 255;
+ memcpy(sp, i2octets(4, (int)sig->pgpbytes_len), 4);
+ sig->pgpbytes_len += 6;
+
+ while (hcount > 0) {
+ sp = decode_subpacket(&ptr, &stag, &n);
+ hcount -= n;
+ /* can check stag to see if we care */
+ }
+ ucount = octets2i(ptr, 2);
+ ptr += 2;
+ while (ucount > 0) {
+ sp = decode_subpacket(&ptr, &stag, &n);
+ ucount -= n;
+ /* can check stag to see if we care */
+ if (stag == 16) {
+ free(sig->key_id);
+ sig->key_id = octets2hex(sp, 8);
+ }
+ }
+ } else
+ return (-1);
+ ptr += 2; /* skip hash16 */
+ if (sig->sig_alg == 1) { /* RSA */
+ sig->sig = decode_mpi(&ptr, &sig->sig_len);
+ }
+ /* we are done */
+ return ((ssize_t)len);
+}
+
+/**
+ * @brief map OpenPGP hash algorithm id's to name
+ *
+ * @sa rfc4880:9.4
+ */
+static struct hash_alg_map {
+ int halg;
+ const char *hname;
+} hash_algs[] = {
+ {1, "md5"},
+ {2, "sha1"},
+ {8, "sha256"},
+ {9, "sha384"},
+ {10, "sha512"},
+ {11, "sha224"},
+ {0, NULL},
+};
+
+static const char *
+get_hname(int hash_alg)
+{
+ struct hash_alg_map *hmp;
+
+ for (hmp = hash_algs; hmp->halg > 0; hmp++) {
+ if (hmp->halg == hash_alg)
+ return (hmp->hname);
+ }
+ return (NULL);
+}
+
+/* lifted from signer.c */
+/**
+ * @brief verify a digest
+ *
+ * The public key, digest name, file and signature data.
+ *
+ * @return 1 on success 0 on failure, -1 on error
+ */
+#ifndef USE_BEARSSL
+static int
+verify_digest (EVP_PKEY *pkey,
+ const char *digest,
+ unsigned char *mdata, size_t mlen,
+ unsigned char *sdata, size_t slen)
+{
+ EVP_MD_CTX ctx;
+ const EVP_MD *md = NULL;
+ EVP_PKEY_CTX *pctx = NULL;
+ int rc = 0;
+ int i = -1;
+
+ initialize();
+ md = EVP_get_digestbyname(digest);
+ EVP_DigestInit(&ctx, md);
+
+ pctx = EVP_PKEY_CTX_new(pkey, NULL);
+ if (!pctx)
+ goto fail;
+ if (EVP_PKEY_verify_init(pctx) <= 0)
+ goto fail;
+ if (EVP_PKEY_CTX_set_signature_md(pctx, ctx.digest) <= 0)
+ goto fail;
+ i = EVP_PKEY_verify(pctx, sdata, slen, mdata, mlen);
+ if (i >= 0)
+ rc = i;
+fail:
+ EVP_PKEY_CTX_free(pctx);
+ return (rc);
+}
+#endif
+
+
+/**
+ * @brief verify OpenPGP signed file
+ *
+ *
+ * @param[in] filename
+ * used to determine the signature name
+ *
+ * @param[in] fdata
+ * content of filename
+ *
+ * @param[in] fbytes
+ * of fdata
+ *
+ * @param[in] sdata
+ * content of signature
+ *
+ * @param[in] sbytes
+ * of sdata
+ *
+ * @param[in] flags
+ *
+ * @return 0 on success
+ */
+int
+openpgp_verify(const char *filename,
+ unsigned char *fdata, size_t fbytes,
+ unsigned char *sdata, size_t sbytes,
+ int flags)
+{
+ OpenPGP_key *key;
+ OpenPGP_sig *sig;
+#ifdef USE_BEARSSL
+ const br_hash_class *md;
+ br_hash_compat_context mctx;
+ const unsigned char *hash_oid;
+#else
+ const EVP_MD *md = NULL;
+ EVP_MD_CTX mctx;
+#endif
+ unsigned char mdata[64];
+ unsigned char *ptr;
+ unsigned char *ddata = NULL;
+ const char *hname;
+ size_t mlen;
+ int rc = -1;
+
+ initialize();
+
+ sig = NEW(OpenPGP_sig);
+ if (!sdata || !sig) {
+ warnx("cannot verify %s", filename);
+ goto oops;
+ }
+ if (!(sdata[0] & 0200))
+ sdata = ddata = dearmor((char *)sdata, sbytes, &sbytes);
+ ptr = sdata;
+ rc = decode_packet(2, &ptr, sbytes, (decoder_t)decode_sig, sig);
+ if (rc == 0 && sig->key_id) {
+ key = load_key_id(sig->key_id);
+ if (!key) {
+ warnx("cannot find key-id: %s", sig->key_id);
+ rc = -1;
+ } else if (!(hname = get_hname(sig->hash_alg))) {
+ warnx("unsupported hash algorithm: %d", sig->hash_alg);
+ rc = -1;
+ } else {
+ /*
+ * Hash fdata according to the OpenPGP recipe
+ *
+ * @sa rfc4880:5.2.4
+ */
+#ifdef USE_BEARSSL
+ switch (sig->hash_alg) { /* see hash_algs above */
+ case 2: /* sha1 */
+ md = &br_sha1_vtable;
+ mlen = br_sha1_SIZE;
+ hash_oid = BR_HASH_OID_SHA1;
+ break;
+ case 8: /* sha256 */
+ md = &br_sha256_vtable;
+ mlen = br_sha256_SIZE;
+ hash_oid = BR_HASH_OID_SHA256;
+ break;
+ default:
+ warnx("unsupported hash algorithm: %s", hname);
+ goto oops;
+ }
+ md->init(&mctx.vtable);
+ md->update(&mctx.vtable, fdata, fbytes);
+ md->update(&mctx.vtable, sig->pgpbytes,
+ sig->pgpbytes_len);
+ md->out(&mctx.vtable, mdata);
+
+ rc = verify_rsa_digest(key->key, hash_oid,
+ mdata, mlen, sig->sig, sig->sig_len);
+#else
+ md = EVP_get_digestbyname(hname);
+ EVP_DigestInit(&mctx, md);
+ EVP_DigestUpdate(&mctx, fdata, fbytes);
+ EVP_DigestUpdate(&mctx, sig->pgpbytes,
+ sig->pgpbytes_len);
+ mlen = sizeof(mdata);
+ EVP_DigestFinal(&mctx,mdata,(unsigned int *)&mlen);
+
+ rc = verify_digest(key->key, hname, mdata, mlen,
+ sig->sig, sig->sig_len);
+#endif
+
+ if (rc > 0) {
+ if ((flags & 1))
+ printf("Verified %s signed by %s\n",
+ filename,
+ key->user ? key->user->name : "someone");
+ rc = 0; /* success */
+ } else if (rc == 0) {
+ printf("Unverified %s: %s\n",
+ filename, get_error_string());
+ rc = 1;
+ } else {
+ printf("Unverified %s\n", filename);
+ }
+ }
+ } else {
+ warnx("cannot decode signature for %s", filename);
+ rc = -1;
+ }
+oops:
+ free(ddata);
+ free(sig);
+ return (rc);
+}
+
+#ifndef _STANDALONE
+/**
+ * @brief list of extensions we handle
+ *
+ * ".asc" is preferred as it works seamlessly with openpgp
+ */
+static const char *sig_exts[] = {
+ ".asc",
+ ".pgp",
+ ".psig",
+ NULL,
+};
+
+/**
+ * @brief verify OpenPGP signed file
+ *
+ *
+ * @param[in] filename
+ * used to determine the signature name
+ *
+ * @param[in] fdata
+ * content of filename
+ *
+ * @param[in] nbytes
+ * of fdata
+ *
+ * @return
+ */
+
+int
+openpgp_verify_file(const char *filename, unsigned char *fdata, size_t nbytes)
+{
+ char pbuf[MAXPATHLEN];
+ unsigned char *sdata;
+ const char *sname = NULL;
+ const char **ep;
+ size_t sz;
+ int n;
+
+ for (ep = sig_exts; *ep; ep++) {
+ n = snprintf(pbuf, sizeof(pbuf), "%s%s", filename, *ep);
+ if (n >= (int)sizeof(pbuf)) {
+ warnx("cannot form signature name for %s", filename);
+ return (-1);
+ }
+ if (access(pbuf, R_OK) == 0) {
+ sname = pbuf;
+ break;
+ }
+ }
+ if (!sname) {
+ warnx("cannot find signature for %s", filename);
+ return (-1);
+ }
+ sdata = read_file(sname, &sz);
+ return (openpgp_verify(filename, fdata, nbytes, sdata, sz, 1));
+}
+#endif
+
+/**
+ * @brief verify OpenPGP signature
+ *
+ * @return content of signed file
+ */
+unsigned char *
+verify_asc(const char *sigfile, int flags)
+{
+ char pbuf[MAXPATHLEN];
+ char *cp;
+ size_t n;
+ unsigned char *fdata, *sdata;
+ size_t fbytes, sbytes;
+
+ if ((sdata = read_file(sigfile, &sbytes))) {
+ n = strlcpy(pbuf, sigfile, sizeof(pbuf));
+ if ((cp = strrchr(pbuf, '.')))
+ *cp = '\0';
+ if ((fdata = read_file(pbuf, &fbytes))) {
+ if (openpgp_verify(pbuf, fdata, fbytes, sdata,
+ sbytes, flags)) {
+ free(fdata);
+ fdata = NULL;
+ }
+ }
+ } else
+ fdata = NULL;
+ free(sdata);
+ return (fdata);
+}
Index: lib/libve/openpgp/packet.h
===================================================================
--- /dev/null
+++ lib/libve/openpgp/packet.h
@@ -0,0 +1,83 @@
+/*-
+ * 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.
+ */
+/*
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+
+/*
+ * Structs to represent what we need
+ */
+
+typedef struct OpenPGP_user {
+ char *id;
+ char *name;
+} OpenPGP_user;
+
+struct OpenPGP_key_ {
+ char *id;
+ int sig_alg;
+ OpenPGP_user *user;
+#ifdef USE_BEARSSL
+ br_rsa_public_key *key;
+#else
+ EVP_PKEY *key;
+#endif
+ LIST_ENTRY(OpenPGP_key_) entries;
+};
+
+typedef struct OpenPGP_key_ OpenPGP_key;
+
+typedef struct OpenPGP_sig {
+ char *key_id;
+ int sig_type;
+ int sig_alg;
+ int hash_alg;
+ unsigned char *pgpbytes;
+ size_t pgpbytes_len;
+ unsigned char *sig;
+ size_t sig_len;
+} OpenPGP_sig;
+
+void openpgp_trust_add(OpenPGP_key *key);
+OpenPGP_key * openpgp_trust_get(const char *keyID);
+OpenPGP_key * load_key_file(const char *kfile);
+OpenPGP_key * load_key_id(const char *keyID);
+void initialize(void);
+char * get_error_string(void);
+int openpgp_verify(const char *filename, unsigned char *fdata, size_t fbytes,
+ unsigned char *sdata, size_t sbytes, int flags);
+int openpgp_verify_file(const char *filename, unsigned char *fdata,
+ size_t nbytes);
+unsigned char * verify_asc (const char *sigfile, int flags);
+
+/* packet decoders */
+#define DECODER_DECL(x) \
+ ssize_t decode_##x(int, unsigned char **, size_t, OpenPGP_##x *)
+
+DECODER_DECL(user);
+DECODER_DECL(key);
+DECODER_DECL(sig);
Index: lib/libve/readfile.c
===================================================================
--- /dev/null
+++ lib/libve/readfile.c
@@ -0,0 +1,61 @@
+/*-
+ * 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.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <libve.h>
+
+unsigned char *
+read_file(const char *path, size_t *len)
+{
+ int fd, m, n, x;
+ struct stat st;
+ unsigned char *buf;
+
+ if (len)
+ *len = 0;
+ if ((fd = open(path, O_RDONLY)) < 0)
+ return (NULL);
+ fstat(fd, &st);
+ if (len)
+ *len = st.st_size;
+ buf = malloc(st.st_size + 1);
+ for (x = 0, m = st.st_size; m > 0; ) {
+ n = read(fd, &buf[x], m);
+ if (n < 0)
+ break;
+ if (n > 0) {
+ m -= n;
+ x += n;
+ }
+ }
+ close(fd);
+ if (m == 0) {
+ buf[st.st_size] = '\0';
+ return (buf);
+ }
+ free(buf);
+ return (NULL);
+}
Index: lib/libve/tests/Makefile
===================================================================
--- /dev/null
+++ lib/libve/tests/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+PROG= tvo
+
+SRCS+= tvo.c
+CFLAGS+= -DUNIT_TEST -g -O0
+
+LIBADD+= bearssl
+MAN=
+NO_SHARED=
+
+.include <bsd.prog.mk>
Index: lib/libve/tests/Makefile.depend.host
===================================================================
--- /dev/null
+++ lib/libve/tests/Makefile.depend.host
@@ -0,0 +1,12 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ lib/libbearssl \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
Index: lib/libve/tests/tvo.c
===================================================================
--- /dev/null
+++ lib/libve/tests/tvo.c
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "../libve-priv.h"
+
+#include <unistd.h>
+#include <err.h>
+
+int
+main(int argc, char *argv[])
+{
+ int n;
+ int fd;
+ int c;
+ char *cp;
+ char *skip;
+ char *prefix;
+
+ prefix = "/";
+ skip = "";
+
+ n = ve_trust_init();
+ printf("Trust %d\n", n);
+
+ while ((c = getopt(argc, argv, "dp:s:T:")) != -1) {
+ switch (c) {
+ case 'd':
+ DebugVe++;
+ break;
+ case 'p':
+ prefix = optarg;
+ break;
+ case 's':
+ skip = optarg;
+ break;
+ case 'T':
+ n = ve_trust_add(optarg);
+ printf("Local trust %s: %d\n", optarg, n);
+ break;
+ default:
+ errx(1, "unknown option: -%c", c);
+ break;
+ }
+ }
+
+ ve_self_tests();
+
+ for ( ; optind < argc; optind++) {
+#ifdef VE_OPENPGP_SUPPORT
+ if (strstr(argv[optind], "asc")) {
+ cp = (char *)verify_asc(argv[optind], 1);
+ if (cp) {
+ printf("Verified: %s: %.28s...\n",
+ argv[optind], cp);
+ fingerprint_info_add(argv[optind],
+ prefix, skip, cp, NULL);
+ } else {
+ fprintf(stderr, "%s: %s\n",
+ argv[optind], ve_error_get());
+ }
+ } else
+#endif
+ if (strstr(argv[optind], "sig")) {
+ cp = (char *)verify_sig(argv[optind], 1);
+ if (cp) {
+ printf("Verified: %s: %.28s...\n",
+ argv[optind], cp);
+ fingerprint_info_add(argv[optind],
+ prefix, skip, cp, NULL);
+ } else {
+ fprintf(stderr, "%s: %s\n",
+ argv[optind], ve_error_get());
+ }
+ } else if (strstr(argv[optind], "manifest")) {
+ cp = (char *)read_file(argv[optind], NULL);
+ if (cp) {
+ fingerprint_info_add(argv[optind],
+ prefix, skip, cp, NULL);
+ }
+ } else {
+ fd = verify_open(argv[optind], O_RDONLY);
+ printf("verify_open(%s) = %d %s\n", argv[optind], fd,
+ (fd < 0) ? ve_error_get() : "");
+ if (fd > 0) {
+ /*
+ * Check that vectx_* can also verify the file.
+ */
+ void *vp;
+ char buf[BUFSIZ];
+ struct stat st;
+ int error;
+ size_t off, n;
+
+ fstat(fd, &st);
+ lseek(fd, 0, SEEK_SET);
+ off = st.st_size % 512;
+ vp = vectx_open(fd, argv[optind], off,
+ &st, &error);
+ if (!vp) {
+ printf("vectx_open(%s) failed: %d %s\n",
+ argv[optind], error,
+ ve_error_get());
+ } else {
+ off = vectx_lseek(vp,
+ (st.st_size % 1024), SEEK_SET);
+
+ if (off < st.st_size) {
+ n = vectx_read(vp, buf,
+ sizeof(buf));
+ if (n > 0)
+ off += n;
+ }
+ off = vectx_lseek(vp, 0, SEEK_END);
+ /* repeating that should be harmless */
+ off = vectx_lseek(vp, 0, SEEK_END);
+ error = vectx_close(vp);
+ if (error) {
+ printf("vectx_close(%s) == %d %s\n",
+ argv[optind], error,
+ ve_error_get());
+ } else {
+ printf("vectx_close: Verified: %s\n",
+ argv[optind]);
+ }
+ }
+ close(fd);
+ }
+ }
+ }
+ return (0);
+}
+
Index: lib/libve/vectx.c
===================================================================
--- /dev/null
+++ lib/libve/vectx.c
@@ -0,0 +1,291 @@
+/*-
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#ifndef _STANDALONE
+/* Avoid unwanted userlandish components */
+#define _KERNEL
+#include <sys/errno.h>
+#undef _KERNEL
+#endif
+
+#include "libve-priv.h"
+
+/**
+ * @file vectx.c
+ * @brief api to verify file while reading
+ *
+ * This API allows the hash of a file to be computed as it is read.
+ * Key to this is seeking by reading.
+ *
+ * On close an indication of the verification result is returned.
+ */
+
+struct vectx {
+ br_hash_compat_context vec_ctx; /* hash ctx */
+ const br_hash_class *vec_md; /* hash method */
+ const char *vec_path; /* path we are verifying */
+ const char *vec_want; /* hash value we want */
+ off_t vec_off; /* current offset */
+ size_t vec_size; /* size of path */
+ size_t vec_hashsz; /* size of hash */
+ int vec_fd; /* file descriptor */
+ int vec_status; /* verification status */
+};
+
+/**
+ * @brief
+ * verify an open file as we read it
+ *
+ * If the file has no fingerprint to match, we will still return a
+ * verification context containing little more than the file
+ * descriptor, and an error code in @c error.
+ *
+ * @param[in] fd
+ * open descriptor
+ *
+ * @param[in] path
+ * pathname to open
+ *
+ * @param[in] off
+ * current offset
+ *
+ * @param[in] stp
+ * pointer to struct stat
+ *
+ * @param[out] error
+ * @li 0 all is good
+ * @li ENOMEM out of memory
+ * @li VE_FINGERPRINT_NONE no entry found
+ * @li VE_FINGERPRINT_UNKNOWN no fingerprint in entry
+ *
+ * @return ctx or NULL on error.
+ * NULL is only returned for non-files or out-of-memory.
+ */
+struct vectx *
+vectx_open(int fd, const char *path, off_t off, struct stat *stp, int *error)
+{
+ struct vectx *ctx;
+ struct stat st;
+ size_t hashsz;
+ char *cp;
+
+ if (!stp) {
+ if (fstat(fd, &st) == 0)
+ stp = &st;
+ }
+
+ /* we *should* only get called for files */
+ if (stp && !S_ISREG(stp->st_mode)) {
+ *error = 0;
+ return (NULL);
+ }
+
+ ctx = malloc(sizeof(struct vectx));
+ if (!ctx)
+ goto enomem;
+ ctx->vec_fd = fd;
+ ctx->vec_path = path;
+ ctx->vec_size = stp->st_size;
+ ctx->vec_off = 0;
+ ctx->vec_want = NULL;
+ ctx->vec_status = 0;
+ hashsz = 0;
+
+ cp = fingerprint_info_lookup(fd, path);
+ if (!cp) {
+ ctx->vec_status = VE_FINGERPRINT_NONE;
+ ve_error_set("%s: no entry", path);
+ } else {
+ if (strncmp(cp, "sha256=", 7) == 0) {
+ ctx->vec_md = &br_sha256_vtable;
+ hashsz = br_sha256_SIZE;
+ cp += 7;
+#ifdef VE_SHA1_SUPPORT
+ } else if (strncmp(cp, "sha1=", 5) == 0) {
+ ctx->vec_md = &br_sha1_vtable;
+ hashsz = br_sha1_SIZE;
+ cp += 5;
+#endif
+#ifdef VE_SHA384_SUPPORT
+ } else if (strncmp(cp, "sha384=", 7) == 0) {
+ ctx->vec_md = &br_sha384_vtable;
+ hashsz = br_sha384_SIZE;
+ cp += 7;
+#endif
+#ifdef VE_SHA512_SUPPORT
+ } else if (strncmp(cp, "sha512=", 7) == 0) {
+ ctx->vec_md = &br_sha512_vtable;
+ hashsz = br_sha512_SIZE;
+ cp += 7;
+#endif
+ } else {
+ ctx->vec_status = VE_FINGERPRINT_UNKNOWN;
+ ve_error_set("%s: no fingerprint", path);
+ }
+ }
+ *error = ctx->vec_status;
+ ctx->vec_hashsz = hashsz;
+ ctx->vec_want = cp;
+ ctx->vec_md->init(&ctx->vec_ctx.vtable);
+
+ if (hashsz > 0 && off > 0) {
+ lseek(fd, 0, SEEK_SET);
+ vectx_lseek(ctx, off, SEEK_SET);
+ }
+ return (ctx);
+
+enomem: /* unlikely */
+ *error = ENOMEM;
+ free(ctx);
+ return (NULL);
+}
+
+/**
+ * @brief
+ * read bytes from file and update hash
+ *
+ * It is critical that all file I/O comes through here.
+ * We keep track of current offset.
+ *
+ * @param[in] pctx
+ * pointer to ctx
+ *
+ * @param[in] buf
+ *
+ * @param[in] nbytes
+ *
+ * @return bytes read or error.
+ */
+ssize_t
+vectx_read(struct vectx *ctx, void *buf, size_t nbytes)
+{
+ unsigned char *bp = buf;
+ int n;
+ size_t off;
+
+ if (ctx->vec_hashsz == 0) /* nothing to do */
+ return (read(ctx->vec_fd, buf, nbytes));
+
+ off = 0;
+ do {
+ n = read(ctx->vec_fd, &bp[off], nbytes - off);
+ if (n < 0)
+ return (n);
+ if (n > 0) {
+ ctx->vec_md->update(&ctx->vec_ctx.vtable, &bp[off], n);
+ off += n;
+ ctx->vec_off += n;
+ }
+ } while (n > 0 && off < nbytes);
+ return (off);
+}
+
+/**
+ * @brief
+ * vectx equivalent of lseek
+ *
+ * We do not actually, seek, but call vectx_read
+ * to reach the desired offset.
+ *
+ * We do not support seeking backwards.
+ *
+ * @param[in] pctx
+ * pointer to ctx
+ *
+ * @param[in] off
+ * desired offset
+ *
+ * @param[in] whence
+ *
+ * @return offset or error.
+ */
+off_t
+vectx_lseek(struct vectx *ctx, off_t off, int whence)
+{
+ unsigned char buf[PAGE_SIZE];
+ size_t delta;
+ ssize_t n;
+
+ if (ctx->vec_hashsz == 0) /* nothing to do */
+ return (lseek(ctx->vec_fd, off, whence));
+
+ /*
+ * Try to convert whence to SEEK_SET
+ * but we cannot support seeking backwards!
+ * Nor beyond end of file.
+ */
+ if (whence == SEEK_END && off <= 0) {
+ whence = SEEK_SET;
+ off += ctx->vec_size;
+ } else if (whence == SEEK_CUR && off >= 0) {
+ whence = SEEK_SET;
+ off += ctx->vec_off;
+ }
+ if (whence != SEEK_SET || off < ctx->vec_off ||
+ (size_t)off > ctx->vec_size) {
+ printf("ERROR: %s: unsupported operation\n", __func__);
+ return (-1);
+ }
+ n = 0;
+ do {
+ delta = off - ctx->vec_off;
+ if (delta > 0) {
+ delta = MIN(PAGE_SIZE, delta);
+ n = vectx_read(ctx, buf, delta);
+ if (n < 0)
+ return (n);
+ }
+ } while (ctx->vec_off < off && n > 0);
+ return (ctx->vec_off);
+}
+
+/**
+ * @brief
+ * check that hashes match and cleanup
+ *
+ * We have finished reading file, compare the hash with what
+ * we wanted.
+ *
+ * @param[in] pctx
+ * pointer to ctx
+ *
+ * @return 0 or an error.
+ */
+int
+vectx_close(struct vectx *ctx)
+{
+ int rc;
+
+ if (ctx->vec_hashsz == 0) {
+ rc = ctx->vec_status;
+ } else {
+ rc = ve_check_hash(&ctx->vec_ctx, ctx->vec_md,
+ ctx->vec_path, ctx->vec_want, ctx->vec_hashsz);
+ }
+ free(ctx);
+ return ((rc < 0) ? rc : 0);
+}
Index: lib/libve/veopen.c
===================================================================
--- /dev/null
+++ lib/libve/veopen.c
@@ -0,0 +1,386 @@
+/*-
+ * 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.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/queue.h>
+
+#include "libve-priv.h"
+
+
+struct fingerprint_info {
+ char *fi_prefix; /**< manifest entries relative to */
+ char *fi_skip; /**< manifest entries prefixed with */
+ const char *fi_data; /**< manifest data */
+ size_t fi_prefix_len; /**< length of prefix */
+ size_t fi_skip_len; /**< length of skip */
+ dev_t fi_dev; /**< device id */
+ LIST_ENTRY(fingerprint_info) entries;
+};
+
+static LIST_HEAD(, fingerprint_info) fi_list;
+
+static void
+fingerprint_info_init(void)
+{
+ static int once;
+
+ if (once)
+ return;
+ LIST_INIT(&fi_list);
+ once = 1;
+}
+
+/**
+ * @brief
+ * add manifest data to list
+ *
+ * list is kept sorted by longest prefix.
+ *
+ * @param[in] prefix
+ * path that all manifest entries are resolved via
+ *
+ * @param[in] skip
+ * optional prefix within manifest entries which should be skipped
+ *
+ * @param[in] data
+ * manifest data
+ */
+void
+fingerprint_info_add(const char *filename, const char *prefix,
+ const char *skip, const char *data, struct stat *stp)
+{
+ struct fingerprint_info *fip, *nfip, *lfip;
+ char *cp;
+
+ fingerprint_info_init();
+ nfip = malloc(sizeof(struct fingerprint_info));
+ if (prefix) {
+ nfip->fi_prefix = strdup(prefix);
+ } else {
+ if (!filename) {
+ free(nfip);
+ return;
+ }
+ nfip->fi_prefix = strdup(filename);
+ cp = strrchr(nfip->fi_prefix, '/');
+ if (cp)
+ *cp = '\0';
+ else {
+ free(nfip->fi_prefix);
+ free(nfip);
+ return;
+ }
+ }
+#ifdef UNIT_TEST
+ nfip->fi_dev = 0;
+#else
+ nfip->fi_dev = stp->st_dev;
+#endif
+ nfip->fi_data = data;
+ nfip->fi_prefix_len = strlen(nfip->fi_prefix);
+ if (skip) {
+ nfip->fi_skip = strdup(skip);
+ nfip->fi_skip_len = strlen(skip);
+ } else {
+ nfip->fi_skip = NULL;
+ nfip->fi_skip_len = 0;
+ }
+
+ if (LIST_EMPTY(&fi_list)) {
+ LIST_INSERT_HEAD(&fi_list, nfip, entries);
+ DEBUG_PRINTF(4, ("inserted %zu %s at head\n",
+ nfip->fi_prefix_len, nfip->fi_prefix));
+ return;
+ }
+ LIST_FOREACH(fip, &fi_list, entries) {
+ if (nfip->fi_prefix_len >= fip->fi_prefix_len) {
+ LIST_INSERT_BEFORE(fip, nfip, entries);
+ DEBUG_PRINTF(4, ("inserted %zu %s before %zu %s\n",
+ nfip->fi_prefix_len, nfip->fi_prefix,
+ fip->fi_prefix_len, fip->fi_prefix));
+ return;
+ }
+ lfip = fip;
+ }
+ LIST_INSERT_AFTER(lfip, nfip, entries);
+ DEBUG_PRINTF(4, ("inserted %zu %s after %zu %s\n",
+ nfip->fi_prefix_len, nfip->fi_prefix,
+ lfip->fi_prefix_len, lfip->fi_prefix));
+}
+
+char *
+fingerprint_info_lookup(int fd, const char *path)
+{
+ char pbuf[MAXPATHLEN+1];
+ char nbuf[MAXPATHLEN+1];
+ struct stat st;
+ struct fingerprint_info *fip;
+ char *cp, *ep, *fp, *np;
+ const char *prefix;
+ size_t n, plen, nlen, nplen;
+ dev_t dev = 0;
+
+ fingerprint_info_init();
+
+ n = strlcpy(pbuf, path, sizeof(pbuf));
+ if (n >= sizeof(pbuf))
+ return (NULL);
+#ifndef UNIT_TEST
+ if (fstat(fd, &st) == 0)
+ dev = st.st_dev;
+#endif
+ /*
+ * get the first entry - it will have longest prefix
+ * so we can can work out how to initially split path
+ */
+ fip = LIST_FIRST(&fi_list);
+ if (!fip)
+ return (NULL);
+ prefix = pbuf;
+ ep = NULL;
+ cp = &pbuf[fip->fi_prefix_len];
+ while (cp > &pbuf[1]) {
+ if (ep) {
+ *ep = '/';
+ cp -= 2;
+ }
+ nlen = plen = 0; /* keep gcc quiet */
+ if (cp > pbuf) {
+ for ( ; cp >= pbuf && *cp != '/'; cp--)
+ ; /* nothing */
+ if (cp > pbuf) {
+ ep = cp++;
+ *ep = '\0';
+ }
+ plen = ep - pbuf;
+ nlen = n - plen - 1;
+ }
+ if (cp == pbuf) {
+ prefix = "/";
+ plen = 1;
+ nlen = n - 1;
+ cp++;
+ ep = NULL;
+ }
+
+ DEBUG_PRINTF(2, ("looking for %s %zu %s\n", prefix, plen, cp));
+
+ LIST_FOREACH(fip, &fi_list, entries) {
+ DEBUG_PRINTF(4, ("at %zu %s\n",
+ fip->fi_prefix_len, fip->fi_prefix));
+
+ if (fip->fi_prefix_len < plen) {
+ DEBUG_PRINTF(3, ("skipping prefix=%s %zu %zu\n",
+ fip->fi_prefix, fip->fi_prefix_len,
+ plen));
+ break;
+ }
+ if (fip->fi_prefix_len == plen) {
+ if (fip->fi_dev != 0 && fip->fi_dev != dev) {
+ DEBUG_PRINTF(3, (
+ "skipping dev=%ld != %ld\n",
+ (long)fip->fi_dev,
+ (long)dev));
+ continue;
+ }
+ if (strcmp(prefix, fip->fi_prefix)) {
+ DEBUG_PRINTF(3, (
+ "skipping prefix=%s\n",
+ fip->fi_prefix));
+ continue;
+ }
+ DEBUG_PRINTF(3, ("checking prefix=%s\n",
+ fip->fi_prefix));
+ if (fip->fi_skip_len) {
+ np = nbuf;
+ nplen = snprintf(nbuf, sizeof(nbuf),
+ "%s/%s",
+ fip->fi_skip, cp);
+ nplen = MIN(nplen, sizeof(nbuf) - 1);
+ } else {
+ np = cp;
+ nplen = nlen;
+ }
+ DEBUG_PRINTF(3, ("lookup: '%s'\n", np));
+ if (!(fp = strstr(fip->fi_data, np)))
+ continue;
+ /*
+ * when we find a match:
+ * fp[nplen] will be space and
+ * fp will be fip->fi_data or
+ * fp[-1] will be \n
+ */
+ if (!((fp == fip->fi_data || fp[-1] == '\n') &&
+ fp[nplen] == ' ')) {
+ do {
+ fp++;
+ fp = strstr(fp, np);
+ if (fp) {
+ DEBUG_PRINTF(3,
+ ("fp[-1]=%#x fp[%zu]=%#x fp=%.78s\n",
+ fp[-1], nplen,
+ fp[nplen],
+ fp));
+ }
+ } while (fp != NULL &&
+ !(fp[-1] == '\n' &&
+ fp[nplen] == ' '));
+ if (!fp)
+ continue;
+ }
+ DEBUG_PRINTF(2, ("found %.78s\n", fp));
+ /* we have a match! */
+ for (cp = &fp[nplen]; *cp == ' '; cp++)
+ ; /* nothing */
+ return (cp);
+ } else {
+ DEBUG_PRINTF(3,
+ ("Ignoring prefix=%s\n", fip->fi_prefix));
+ }
+ }
+ }
+ return (NULL);
+}
+
+static int
+verify_fingerprint(int fd, const char *path, const char *cp, off_t off)
+{
+ unsigned char buf[PAGE_SIZE];
+ const br_hash_class *md;
+ br_hash_compat_context mctx;
+ size_t hlen;
+ int n;
+
+ if (strncmp(cp, "sha256=", 7) == 0) {
+ md = &br_sha256_vtable;
+ hlen = br_sha256_SIZE;
+ cp += 7;
+#ifdef VE_SHA1_SUPPORT
+ } else if (strncmp(cp, "sha1=", 5) == 0) {
+ md = &br_sha1_vtable;
+ hlen = br_sha1_SIZE;
+ cp += 5;
+#endif
+#ifdef VE_SHA384_SUPPORT
+ } else if (strncmp(cp, "sha384=", 7) == 0) {
+ md = &br_sha384_vtable;
+ hlen = br_sha384_SIZE;
+ cp += 7;
+#endif
+#ifdef VE_SHA512_SUPPORT
+ } else if (strncmp(cp, "sha512=", 7) == 0) {
+ md = &br_sha512_vtable;
+ hlen = br_sha512_SIZE;
+ cp += 7;
+#endif
+ } else {
+ ve_error_set("%s: no supported fingerprint", path);
+ return (VE_FINGERPRINT_UNKNOWN);
+ }
+
+ md->init(&mctx.vtable);
+ if (off)
+ lseek(fd, 0, SEEK_SET);
+ do {
+ n = read(fd, buf, sizeof(buf));
+ if (n < 0)
+ return (n);
+ if (n > 0)
+ md->update(&mctx.vtable, buf, n);
+ } while (n > 0);
+ lseek(fd, off, SEEK_SET);
+ return (ve_check_hash(&mctx, md, path, cp, hlen));
+}
+
+
+/**
+ * @brief
+ * verify an open file
+ *
+ * @param[in] fd
+ * open descriptor
+ *
+ * @param[in] path
+ * pathname to open
+ *
+ * @param[in] off
+ * current offset
+ *
+ * @return 0, VE_FINGERPRINT_OK or VE_FINGERPRINT_NONE, VE_FINGERPRINT_WRONG
+ */
+int
+verify_fd(int fd, const char *path, off_t off, struct stat *stp)
+{
+ struct stat st;
+ char *cp;
+ int rc;
+
+ if (!stp) {
+ if (fstat(fd, &st) == 0)
+ stp = &st;
+ }
+ if (stp && !S_ISREG(stp->st_mode))
+ return (0); /* not relevant */
+ cp = fingerprint_info_lookup(fd, path);
+ if (!cp) {
+ ve_error_set("%s: no entry", path);
+ return (VE_FINGERPRINT_NONE);
+ }
+ rc = verify_fingerprint(fd, path, cp, off);
+ switch (rc) {
+ case VE_FINGERPRINT_OK:
+ case VE_FINGERPRINT_UNKNOWN:
+ return (rc);
+ default:
+ return (VE_FINGERPRINT_WRONG);
+ }
+}
+
+/**
+ * @brief
+ * open a file if it can be verified
+ *
+ * @param[in] path
+ * pathname to open
+ *
+ * @param[in] flags
+ * flags for open
+ *
+ * @return fd or VE_FINGERPRINT_NONE, VE_FINGERPRINT_WRONG
+ */
+int
+verify_open(const char *path, int flags)
+{
+ int fd;
+ int rc;
+
+ if ((fd = open(path, flags)) >= 0) {
+ if ((rc = verify_fd(fd, path, 0, NULL)) < 0) {
+ close(fd);
+ fd = rc;
+ }
+ }
+ return (fd);
+}
Index: lib/libve/vepcr.c
===================================================================
--- /dev/null
+++ lib/libve/vepcr.c
@@ -0,0 +1,79 @@
+/*-
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "libve-priv.h"
+
+/*
+ * To support measured boot without putting a ton
+ * of extra code in the loader, we just maintain
+ * a hash of all the hashes we (attempt to) verify.
+ * The loader can export this for kernel or rc script
+ * to feed to a TPM pcr register - hence the name ve_pcr.
+ */
+
+static const br_hash_class *pcr_md = NULL;
+static br_hash_compat_context pcr_ctx;
+static size_t pcr_hlen = 0;
+
+/**
+ * @brief initialize pcr context
+ *
+ * Real TPM registers only hold a SHA1 hash
+ * but we use SHA256
+ */
+void
+ve_pcr_init(void)
+{
+ pcr_hlen = br_sha256_SIZE;
+ pcr_md = &br_sha256_vtable;
+ pcr_md->init(&pcr_ctx.vtable);
+}
+
+/**
+ * @brief update pcr context
+ */
+void
+ve_pcr_update(unsigned char *data, size_t dlen)
+{
+ if (pcr_md)
+ pcr_md->update(&pcr_ctx.vtable, data, dlen);
+}
+
+/**
+ * @brief get pcr result
+ */
+ssize_t
+ve_pcr_get(unsigned char *buf, size_t sz)
+{
+ if (!pcr_md)
+ return (-1);
+ if (sz < pcr_hlen)
+ return (-1);
+ pcr_md->out(&pcr_ctx.vtable, buf);
+ return (pcr_hlen);
+}
+
Index: lib/libve/verify.c
===================================================================
--- /dev/null
+++ lib/libve/verify.c
@@ -0,0 +1,410 @@
+/*-
+ * 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.
+ */
+/*
+ * Routines to verify files loaded.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include "libve.h"
+#include "verify.h"
+#include <manifests.h>
+
+#define VE_NOT_CHECKED -42
+
+/*
+ * We sometimes need to know if input is verified or not.
+ * The extra slot is for tracking most recently opened.
+ */
+static int ve_status[SOPEN_MAX+1];
+static int ve_status_state;
+struct verify_status;
+struct verify_status *verified_files = NULL;
+static int loaded_manifests = 0; /* have we loaded anything? */
+
+#define VE_STATUS_NONE 1
+#define VE_STATUS_VALID 2
+
+/**
+ * @brief set ve status for fd
+ */
+static void
+ve_status_set(int fd, int ves)
+{
+ if (fd >= 0 && fd < SOPEN_MAX) {
+ ve_status[fd] = ves;
+ ve_status_state = VE_STATUS_VALID;
+ }
+ ve_status[SOPEN_MAX] = ves;
+}
+
+/**
+ * @brief get ve status of fd
+ *
+ * What we return depends on ve_status_state.
+ *
+ * @return
+ * @li ve_status[fd] if ve_status_state is valid
+ * @li ve_status[SOPEN_MAX] if ve_status_state is none
+ * @li VE_NOT_CHECKED if ve_status_state uninitialized
+ */
+int
+ve_status_get(int fd)
+{
+ if (!ve_status_state) {
+ return (VE_NOT_CHECKED);
+ }
+ if (ve_status_state == VE_STATUS_VALID &&
+ fd >= 0 && fd < SOPEN_MAX)
+ return (ve_status[fd]);
+ return (ve_status[SOPEN_MAX]); /* most recent */
+}
+
+/**
+ * @brief track verify status
+ *
+ * occasionally loader will make multiple calls
+ * for the same file, we need only check it once.
+ */
+struct verify_status {
+ dev_t vs_dev;
+ ino_t vs_ino;
+ int vs_status;
+ struct verify_status *vs_next;
+};
+
+static int
+is_verified(struct stat *stp)
+{
+ struct verify_status *vsp;
+
+ for (vsp = verified_files; vsp != NULL; vsp = vsp->vs_next) {
+ if (stp->st_dev == vsp->vs_dev &&
+ stp->st_ino == vsp->vs_ino)
+ return (vsp->vs_status);
+ }
+ return (VE_NOT_CHECKED);
+}
+
+/* most recent first, since most likely to see repeated calls. */
+static void
+add_verify_status(struct stat *stp, int status)
+{
+ struct verify_status *vsp;
+
+ vsp = malloc(sizeof(struct verify_status));
+ vsp->vs_next = verified_files;
+ vsp->vs_dev = stp->st_dev;
+ vsp->vs_ino = stp->st_ino;
+ vsp->vs_status = status;
+ verified_files = vsp;
+}
+
+
+/**
+ * @brief
+ * load specified manifest if verified
+ */
+int
+load_manifest(const char *name, const char *prefix,
+ const char *skip, struct stat *stp)
+{
+ struct stat st;
+ size_t n;
+ int rc;
+ char *content;
+
+ rc = VE_FINGERPRINT_NONE;
+ n = strlen(name);
+ if (n > 4) {
+ if (!stp) {
+ stp = &st;
+ if (stat(name, &st) < 0 || !S_ISREG(st.st_mode))
+ return (rc);
+ }
+ rc = is_verified(stp);
+ if (rc != VE_NOT_CHECKED) {
+ return (rc);
+ }
+ /* loader has no sense of time */
+ ve_utc_set(stp->st_mtime);
+ content = (char *)verify_signed(name, VEF_VERBOSE);
+ if (content) {
+ fingerprint_info_add(name, prefix, skip, content, stp);
+ add_verify_status(stp, VE_VERIFIED);
+ loaded_manifests = 1; /* we are verifying! */
+ DEBUG_PRINTF(3, ("loaded: %s %s %s\n",
+ name, prefix, skip));
+ rc = 0;
+ } else {
+ rc = VE_FINGERPRINT_WRONG;
+ add_verify_status(stp, rc); /* remember */
+ }
+ }
+ return (rc);
+}
+
+static int
+find_manifest(const char *name)
+{
+ struct stat st;
+ char buf[MAXPATHLEN];
+ char *prefix;
+ char *skip;
+ const char **tp;
+ unsigned char *content;
+ int rc;
+
+ strncpy(buf, name, MAXPATHLEN - 1);
+ if (!(prefix = strrchr(buf, '/')))
+ return (-1);
+ *prefix = '\0';
+ prefix = strdup(buf);
+ rc = VE_FINGERPRINT_NONE;
+ for (tp = manifest_names; *tp; tp++) {
+ snprintf(buf, sizeof(buf), "%s/%s", prefix, *tp);
+ DEBUG_PRINTF(5, ("looking for %s\n", buf));
+ if (stat(buf, &st) == 0 && st.st_size > 0) {
+#ifdef MANIFEST_SKIP_ALWAYS
+ skip = MANIFEST_SKIP_ALWAYS;
+#else
+ if (*tp[0] == '.') {
+#ifdef MANIFEST_SKIP
+ skip = MANIFEST_SKIP;
+#else
+ if ((skip = strrchr(prefix, '/')))
+ skip++;
+#endif
+ } else
+ skip = NULL;
+#endif
+ rc = load_manifest(buf, prefix, skip, &st);
+ break;
+ }
+ }
+ free(prefix);
+ return (rc);
+}
+
+
+#ifdef LOADER_VERIEXEC_TESTING
+# define ACCEPT_NO_FP_DEFAULT VE_MUST + 1
+#else
+# define ACCEPT_NO_FP_DEFAULT VE_MUST
+#endif
+#ifndef VE_VERBOSE_DEFAULT
+# define VE_VERBOSE_DEFAULT 0
+#endif
+
+static int
+severity_guess(const char *filename)
+{
+ const char *cp;
+
+ /* Some files like *.conf and *.hints may be unsigned */
+ if ((cp = strrchr(filename, '.'))) {
+ if (strcmp(cp, ".conf") == 0 ||
+ strcmp(cp, ".cookie") == 0 ||
+ strcmp(cp, ".hints") == 0)
+ return (VE_TRY);
+ }
+ return (VE_WANT);
+}
+
+static void
+verify_tweak(char *tweak, int *accept_no_fp, int *verbose, int *verifying)
+{
+ if (strcmp(tweak, "off") == 0) {
+ *verifying = 0;
+ } else if (strcmp(tweak, "strict") == 0) {
+ /* anything caller wants verified must be */
+ *accept_no_fp = VE_WANT;
+ *verbose = 1; /* warn of anything unverified */
+ /* treat self test failure as fatal */
+ if (!ve_self_tests()) {
+ panic("verify self tests failed");
+ }
+ } else if (strcmp(tweak, "modules") == 0) {
+ /* modules/kernel must be verified */
+ *accept_no_fp = VE_MUST;
+ } else if (strcmp(tweak, "try") == 0) {
+ /* best effort: always accept no fp */
+ *accept_no_fp = VE_MUST + 1;
+ } else if (strcmp(tweak, "verbose") == 0) {
+ *verbose = 1;
+ } else if (strcmp(tweak, "quiet") == 0) {
+ *verbose = 0;
+ }
+}
+
+/**
+ * @brief verify an open file
+ *
+ * @param[in] fd
+ * open descriptor
+ *
+ * @param[in] filename
+ * path we opened and will use to lookup fingerprint
+ *
+ * @param[in] off
+ * current offset in fd, must be restored on return
+ *
+ * @param[in] severity
+ * indicator of how to handle case of missing fingerprint
+ *
+ * We look for a signed manifest relative to the filename
+ * just opened and verify/load it if needed.
+ *
+ * We then use verify_fd() in libve to actually verify that hash for
+ * open file. If it returns < 0 we look at the severity arg to decide
+ * what to do about it.
+ *
+ * If verify_fd() returns VE_FINGERPRINT_NONE we accept it if severity
+ * is < accept_no_fp.
+ *
+ * @return >= 0 on success < 0 on failure
+ */
+int
+verify_file(int fd, const char *filename, off_t off, int severity)
+{
+ static int verifying = -1;
+ static int accept_no_fp = ACCEPT_NO_FP_DEFAULT;
+ static int verbose = VE_VERBOSE_DEFAULT;
+ struct stat st;
+ char *cp;
+ int rc;
+
+ if (verifying < 0) {
+ verifying = ve_trust_init();
+#ifdef VE_DEBUG_LEVEL
+ ve_debug_set(VE_DEBUG_LEVEL);
+#endif
+ /* initialize ve_status with default result */
+ rc = verifying ? VE_NOT_CHECKED : VE_NOT_VERIFYING;
+ ve_status_set(0, rc);
+ ve_status_state = VE_STATUS_NONE;
+ if (verifying)
+ ve_self_tests();
+ }
+ if (!verifying)
+ return (0);
+
+ if (fd < 0 || fstat(fd, &st) < 0 || !S_ISREG(st.st_mode))
+ return (0);
+
+ DEBUG_PRINTF(3, ("fd=%d,name='%s',off=%lld,dev=%lld,ino=%lld\n",
+ fd, filename, (long long)off, st.st_dev,st.st_ino));
+
+
+ rc = is_verified(&st);
+ if (rc != VE_NOT_CHECKED) {
+ ve_status_set(fd, rc);
+ return (rc);
+ }
+ rc = find_manifest(filename);
+ if (rc != VE_FINGERPRINT_WRONG && loaded_manifests) {
+ if (severity <= VE_GUESS)
+ severity = severity_guess(filename);
+ if ((rc = verify_fd(fd, filename, off, &st)) >= 0) {
+ if (verbose || severity > VE_WANT) {
+#if defined(VE_DEBUG_LEVEL) && VE_DEBUG_LEVEL > 0
+ printf("Verified %s %llu,%llu\n", filename,
+ st.st_dev, st.st_ino);
+#else
+ printf("Verified %s\n", filename);
+#endif
+ }
+ if (severity < VE_MUST) { /* not a kernel or module */
+
+ if ((cp = strrchr(filename, '/'))) {
+ cp++;
+ if (strncmp(cp, "loader.ve.", 10) == 0) {
+ cp += 10;
+ verify_tweak(cp,
+ &accept_no_fp, &verbose,
+ &verifying);
+ }
+ }
+ }
+ add_verify_status(&st, rc);
+ ve_status_set(fd, rc);
+ return (rc);
+ }
+
+ if (severity || verbose)
+ printf("Unverified: %s\n", ve_error_get());
+ if (rc == VE_FINGERPRINT_UNKNOWN && severity < VE_MUST)
+ rc = VE_UNVERIFIED_OK;
+ else if (rc == VE_FINGERPRINT_NONE && severity < accept_no_fp)
+ rc = VE_UNVERIFIED_OK;
+
+ add_verify_status(&st, rc);
+ }
+#ifdef LOADER_VERIEXEC_TESTING
+ else if (rc != VE_FINGERPRINT_WRONG) {
+ /*
+ * We have not loaded any manifest and
+ * not because of verication failure.
+ * Most likely reason is we have none.
+ * Allow boot to proceed if we are just testing.
+ */
+ return (VE_UNVERIFIED_OK);
+ }
+#endif
+ if (rc == VE_FINGERPRINT_WRONG && severity >= accept_no_fp)
+ panic("cannot continue");
+ ve_status_set(fd, rc);
+ return (rc);
+}
+
+/**
+ * @brief get hex string for pcr value and export
+ *
+ * In case we are doing measured boot, provide
+ * value of the "pcr" data we have accumulated.
+ */
+void
+verify_pcr_export(void)
+{
+#ifdef VE_PCR_SUPPORT
+ char hexbuf[br_sha256_SIZE * 2 + 2];
+ unsigned char hbuf[br_sha256_SIZE];
+ char *hex;
+ ssize_t hlen;
+
+ hlen = ve_pcr_get(hbuf, sizeof(hbuf));
+ if (hlen > 0) {
+ hex = hexdigest(hexbuf, sizeof(hexbuf), hbuf, hlen);
+ if (hex) {
+ hex[hlen*2] = '\0'; /* clobber newline */
+ setenv("loader.ve.pcr", hex, 1);
+ }
+ }
+#endif
+}
Index: lib/libve/vesigned.c
===================================================================
--- /dev/null
+++ lib/libve/vesigned.c
@@ -0,0 +1,61 @@
+/*-
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <libve.h>
+
+#include <vse.h>
+
+/**
+ * @brief
+ * verify signed file
+ *
+ * We look for a signature using the extensions
+ * recorded in signature_exts.
+ * If we find a match we pass it to a suitable verify method.
+ *
+ * @return content of verified file or NULL on error.
+ */
+unsigned char *
+verify_signed(const char *filename, int flags)
+{
+ struct stat st;
+ char buf[MAXPATHLEN];
+ const char **se;
+
+ for (se = signature_exts; *se; se++) {
+ snprintf(buf, sizeof(buf), "%s.%s", filename, *se);
+ if (stat(buf, &st) < 0 || !S_ISREG(st.st_mode))
+ continue;
+ DEBUG_PRINTF(5, ("verify_signed: %s\n", buf));
+#ifdef VE_OPENPGP_SUPPORT
+ if (strncmp(*se, "asc", 3) == 0)
+ return (verify_asc(buf, flags));
+#endif
+ return (verify_sig(buf, flags));
+ }
+ return (NULL);
+}
Index: lib/libve/veta.c
===================================================================
--- /dev/null
+++ lib/libve/veta.c
@@ -0,0 +1,111 @@
+/*-
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/**
+ * @file veta.c - add to trust anchors
+ *
+ */
+
+#define NEED_BRSSL_H
+#include "libve-priv.h"
+#include <brssl.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#ifdef VE_OPENPGP_SUPPORT
+#include "openpgp/packet.h"
+#endif
+
+/**
+ * @brief add trust anchors from a file
+ *
+ * The file might contain X.509 certs
+ * or OpenPGP public key
+ */
+static size_t
+trust_file_add(const char *trust)
+{
+ br_x509_certificate *xcs;
+ size_t num;
+
+ xcs = read_certificates(trust, &num);
+ if (xcs) {
+ num = ve_trust_anchors_add(xcs, num);
+ }
+#ifdef VE_OPENPGP_SUPPORT
+ else if (load_key_file(trust)) {
+ num = 1;
+ }
+#endif
+ return (num);
+}
+
+/**
+ * @brief add trust anchors from a directory
+ *
+ * Pass each file in directory to trust_file_add
+ */
+static size_t
+trust_dir_add(const char *trust)
+{
+ char fbuf[MAXPATHLEN];
+ DIR *dh;
+ struct dirent *de;
+ struct stat st;
+ ssize_t sz;
+ size_t num;
+
+ if (!(dh = opendir(trust)))
+ return (0);
+ for (num = 0, de = readdir(dh); de; de = readdir(dh)) {
+ if (de->d_name[0] == '.')
+ continue;
+ sz = snprintf(fbuf, sizeof(fbuf), "%s/%s", trust, de->d_name);
+ if (sz >= (ssize_t)sizeof(fbuf))
+ continue;
+ if (stat(fbuf, &st) < 0 || S_ISDIR(st.st_mode))
+ continue;
+ num += trust_file_add(fbuf);
+ }
+ closedir(dh);
+ return (num);
+}
+
+/**
+ * @brief add trust anchors
+ */
+int
+ve_trust_add(const char *trust)
+{
+ struct stat st;
+
+ if (stat(trust, &st) < 0)
+ return (-1);
+ if (S_ISDIR(st.st_mode))
+ return (trust_dir_add(trust));
+ return (trust_file_add(trust));
+}
Index: lib/libve/vets.c
===================================================================
--- /dev/null
+++ lib/libve/vets.c
@@ -0,0 +1,699 @@
+/*-
+ * 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.
+ */
+#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 "libve-priv.h"
+#include <brssl.h>
+#include <ta.h>
+
+#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;
+
+static anchor_list trust_anchors = 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);
+}
+
+/**
+ * @brief
+ * add certs to our trust store
+ */
+size_t
+ve_trust_anchors_add(br_x509_certificate *xcs, size_t num)
+{
+ 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(trust_anchors, ta);
+ }
+ return (u);
+}
+
+/**
+ * @brief
+ * initialize our trust_anchors from ta_PEM
+ */
+int
+ve_trust_init(void)
+{
+ br_x509_certificate *xcs;
+ static int once = -1;
+ size_t num;
+
+ if (once >= 0)
+ return (once);
+ once = 0;
+
+ 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((unsigned char *)TRUST_ANCHOR_STR,
+ sizeof(TRUST_ANCHOR_STR), &num);
+ if (xcs == NULL)
+ return (0);
+ num = ve_trust_anchors_add(xcs, num);
+ once = (int) num;
+#else
+ num = 0;
+#endif
+ return (num);
+}
+
+/**
+ * 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)
+{
+ 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(trust_anchors)));
+
+ br_x509_minimal_init(&mc, &br_sha256_vtable,
+ &VEC_ELT(trust_anchors, 0),
+ VEC_LEN(trust_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_CLEAREXT(chain, &free_cert_contents);
+ return (pk);
+}
+
+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;
+
+ ve_trust_init();
+ xcs = read_certificates(certs, &num);
+ if (xcs == NULL) {
+ ve_error_set("cannot read certificates\n");
+ return (NULL);
+ }
+ pk = verify_signer_xcs(xcs, num, elts, num_elts);
+ xfree(xcs);
+ 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];
+ unsigned char vhbuf[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 VERIFY_CERTS_STR
+static int
+test_hash(const br_hash_class *md, size_t hlen,
+ const char *hname,
+ const char *s, const char *want)
+{
+ br_hash_compat_context mctx;
+
+ md->init(&mctx.vtable);
+ md->update(&mctx.vtable, s, strlen(s));
+ 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, \
+ VERIFY_CERTS_STR, vc_ ## 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 VERIFY_CERTS_STR
+ xcs = parse_certificates((unsigned char *)VERIFY_CERTS_STR,
+ sizeof(VERIFY_CERTS_STR), &num);
+ if (xcs == NULL)
+ return (0);
+#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
+ /*
+ * 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)) != NULL) {
+ 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);
+#else
+ printf("No X.509 self tests\n");
+#endif /* VERIFY_CERTS_STR */
+#ifdef VE_OPENPGP_SUPPORT
+ if (!openpgp_self_tests())
+ once++;
+#endif
+ return (once);
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Dec 29, 4:48 PM (7 h, 34 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15631704
Default Alt Text
D16335.id46360.diff (117 KB)
Attached To
Mode
D16335: Build libve for loader and sbin/veriexec
Attached
Detach File
Event Timeline
Log In to Comment