aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/libsecureboot/Makefile11
-rw-r--r--lib/libsecureboot/Makefile.depend17
-rw-r--r--lib/libsecureboot/Makefile.depend.host12
-rw-r--r--lib/libsecureboot/Makefile.inc133
-rw-r--r--lib/libsecureboot/Makefile.libsa.inc40
-rw-r--r--lib/libsecureboot/README.rst134
-rw-r--r--lib/libsecureboot/brf.c403
-rw-r--r--lib/libsecureboot/h/libsecureboot.h94
-rw-r--r--lib/libsecureboot/h/verify_file.h47
-rw-r--r--lib/libsecureboot/libsecureboot-priv.h48
-rw-r--r--lib/libsecureboot/local.trust.mk114
-rw-r--r--lib/libsecureboot/openpgp/Makefile.inc48
-rw-r--r--lib/libsecureboot/openpgp/dearmor.c144
-rw-r--r--lib/libsecureboot/openpgp/decode.c304
-rw-r--r--lib/libsecureboot/openpgp/decode.h57
-rw-r--r--lib/libsecureboot/openpgp/opgp_key.c352
-rw-r--r--lib/libsecureboot/openpgp/opgp_sig.c484
-rw-r--r--lib/libsecureboot/openpgp/packet.h82
-rw-r--r--lib/libsecureboot/readfile.c61
-rw-r--r--lib/libsecureboot/tests/Makefile18
-rw-r--r--lib/libsecureboot/tests/Makefile.depend.host12
-rw-r--r--lib/libsecureboot/tests/tvo.c181
-rw-r--r--lib/libsecureboot/vectx.c291
-rw-r--r--lib/libsecureboot/veopen.c458
-rw-r--r--lib/libsecureboot/vepcr.c84
-rw-r--r--lib/libsecureboot/verify_file.c421
-rw-r--r--lib/libsecureboot/vesigned.c61
-rw-r--r--lib/libsecureboot/veta.c111
-rw-r--r--lib/libsecureboot/vets.c700
29 files changed, 4922 insertions, 0 deletions
diff --git a/lib/libsecureboot/Makefile b/lib/libsecureboot/Makefile
new file mode 100644
index 000000000000..24f3d2bc5855
--- /dev/null
+++ b/lib/libsecureboot/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+.include <src.opts.mk>
+
+LIB= secureboot
+
+.include "Makefile.inc"
+
+INCS= h/libsecureboot.h
+
+.include <bsd.lib.mk>
diff --git a/lib/libsecureboot/Makefile.depend b/lib/libsecureboot/Makefile.depend
new file mode 100644
index 000000000000..6cfaab1c3644
--- /dev/null
+++ b/lib/libsecureboot/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
diff --git a/lib/libsecureboot/Makefile.depend.host b/lib/libsecureboot/Makefile.depend.host
new file mode 100644
index 000000000000..c6441c263f4a
--- /dev/null
+++ b/lib/libsecureboot/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
diff --git a/lib/libsecureboot/Makefile.inc b/lib/libsecureboot/Makefile.inc
new file mode 100644
index 000000000000..1042fe898ace
--- /dev/null
+++ b/lib/libsecureboot/Makefile.inc
@@ -0,0 +1,133 @@
+# $FreeBSD$
+
+.if empty(BEARSSL)
+.include "../libbearssl/Makefile.inc"
+.endif
+
+.if !target(_${__this}_)
+_${__this}_:
+
+libsecureboot_src:= ${.PARSEDIR}
+
+CFLAGS+= -I${libsecureboot_src}/h
+
+.PATH: ${.PARSEDIR}
+
+SRCS+= \
+ readfile.c \
+ brf.c \
+ vesigned.c \
+ vets.c
+
+.if ${.CURDIR:M*libsecureboot*} != ""
+SRCS+= veta.c
+.endif
+
+CFLAGS+= ${XCFLAGS.${.TARGET:T:R}:U}
+
+# we use a couple of files from ${BEARSSL}/tools
+BRSSL_CFLAGS+= -I${BEARSSL}/tools
+BRSSL_SRCS+= \
+ ${BEARSSL}/tools/xmem.c \
+ ${BEARSSL}/tools/vector.c
+
+# we do not need/want nested objdirs
+OBJS_SRCS_FILTER = T R
+
+SRCS+= ${BRSSL_SRCS}
+
+
+# extract the last cert from a chain (should be rootCA)
+_LAST_PEM_USE: .USE
+ sed "1,`grep -n .-END ${.ALLSRC:M*.pem} | tail -2 | head -1 | sed 's,:.*,,'`d" ${.ALLSRC:M*.pem} > ${.TARGET}
+
+# extract 2nd last cert from chain - we use this for self-test
+_2ndLAST_PEM_USE: .USE
+ sed -n "`grep -n .-BEGIN ${.ALLSRC:M*.pem} | tail -2 | \
+ sed 's,:.*,,' | xargs | (read a b; echo $$a,$$(($$b - 1)))`p" ${.ALLSRC:M*.pem} > ${.TARGET}
+
+# list of hashes we support
+VE_HASH_LIST?= SHA256
+
+# list of signatures we support
+# some people don't trust ECDSA
+VE_SIGNATURE_LIST?= RSA
+
+# this list controls our search for signatures so will not be sorted
+# note: for X509 signatures we assume we can replace the trailing
+# "sig" with "certs" to find the certificate chain
+# eg. for manifest.esig we use manifest.ecerts
+VE_SIGNATURE_EXT_LIST?= sig
+
+# needs to be yes for FIPS 140-2 compliance
+VE_SELF_TESTS?= no
+
+# rules to populate the [tv]*.pem files we use to generate ta.h
+# and can add/alter VE_*_LIST as desired.
+.-include "local.trust.mk"
+
+# this is what we use as our trust anchor
+CFLAGS+= -I. -DTRUST_ANCHOR_STR=ta_PEM
+
+.if ${VE_SELF_TESTS} != "no"
+XCFLAGS.vets+= -DVERIFY_CERTS_STR=vc_PEM
+.endif
+
+# clean these up
+VE_HASH_LIST:= ${VE_HASH_LIST:tu:O:u}
+VE_SIGNATURE_LIST:= ${VE_SIGNATURE_LIST:tu:O:u}
+
+# define what we are supporting
+CFLAGS+= ${VE_HASH_LIST:@H@-DVE_$H_SUPPORT@} \
+ ${VE_SIGNATURE_LIST:@S@-DVE_$S_SUPPORT@}
+
+.if ${VE_SIGNATURE_LIST:MOPENPGP} != ""
+.include "openpgp/Makefile.inc"
+.endif
+
+.if ${VE_SELF_TESTS} != "no"
+# The input used for hash KATs
+VE_HASH_KAT_STR?= vc_PEM
+
+XCFLAGS.vets+= -DVE_HASH_KAT_STR=${VE_HASH_KAT_STR}
+.endif
+
+# Generate ta.h containing one or more PEM encoded trust anchors in ta_PEM.
+#
+# If we are doing self-tests, we define another arrary vc_PEM
+# containing certificates that we can verify for each trust anchor.
+# This is typically a subordinate CA cert.
+# Finally we generate a hash of vc_PEM using each supported hash method
+# to use as a Known Answer Test (needed for FIPS 140-2)
+#
+vets.o vets.po vets.pico: ta.h
+ta.h: ${.ALLTARGETS:M[tv]*pem:O:u}
+ @( echo '/* Autogenerated - DO NOT EDIT!!! */'; echo; \
+ cat ${.ALLSRC:N*crl*:Mt*.pem} /dev/null | \
+ file2c -sx 'static const char ta_PEM[] = {' '};'; \
+ echo "${.newline}${VE_HASH_LIST:@H@static char vh_$H[] = \"`cat ${.ALLSRC:N*crl*:Mv*.pem} | ${$H:U${H:tl}}`\";${.newline}@}"; ) > ${.TARGET}
+.if ${VE_SELF_TESTS} != "no"
+ ( cat ${.ALLSRC:N*crl*:Mv*.pem} /dev/null | \
+ file2c -sx 'static const char vc_PEM[] = {' '};'; 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 vesigned.po vesigned.pico: vse.h
+vse.h:
+ @( echo '/* Autogenerated - DO NOT EDIT!!! */'; echo; \
+ echo "static const char *signature_exts[] = {"; \
+ echo '${VE_SIGNATURE_EXT_LIST:@e@"$e",${.newline}@}'; \
+ echo 'NULL };' ) > ${.TARGET}
+
+
+.for s in ${BRSSL_SRCS} brf.c vets.c veta.c
+.ifdef BRSSL_SED
+$s: brssl.h
+.endif
+XCFLAGS.${s:R}+= ${BRSSL_CFLAGS}
+.endfor
+
+.endif
diff --git a/lib/libsecureboot/Makefile.libsa.inc b/lib/libsecureboot/Makefile.libsa.inc
new file mode 100644
index 000000000000..2456484a89c7
--- /dev/null
+++ b/lib/libsecureboot/Makefile.libsa.inc
@@ -0,0 +1,40 @@
+# $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_file.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_file.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_file+= \
+ -DVE_DEBUG_LEVEL=${VE_DEBUG_LEVEL:U0} \
+ -DVE_VERBOSE_DEFAULT=${VE_VERBOSE_DEFAULT:U0} \
+
+.if !empty(MANIFEST_SKIP_ALWAYS)
+XCFLAGS.verify_file+= -DMANIFEST_SKIP_ALWAYS=\"${MANIFEST_SKIP_ALWAYS}\"
+.elif !empty(MANIFEST_SKIP)
+XCFLAGS.verify_file+= -DMANIFEST_SKIP=\"${MANIFEST_SKIP}\"
+.endif
diff --git a/lib/libsecureboot/README.rst b/lib/libsecureboot/README.rst
new file mode 100644
index 000000000000..8e6f7580faaf
--- /dev/null
+++ b/lib/libsecureboot/README.rst
@@ -0,0 +1,134 @@
+libsecureboot
+*************
+
+This library depends one way or another on verifying digital signatures.
+To do that, the necessary trust anchors need to be available.
+
+The simplest (and most attractive for an embedded system) is to
+capture them in this library.
+
+The makefile ``local.trust.mk`` is responsible for doing that.
+The file provided is just an example and depends on the environment
+here at Juniper.
+
+Within Juniper we use signing servers, which apart from signing things
+provide access to the necessary trust anchors.
+That signing server is freely available - see
+http://www.crufty.net/sjg/docs/signing-server.htm
+
+X.509 certificates chains offer a lot of flexibility over time and are
+a great solution for an embedded vendor like Juniper or even
+FreeBSD.org, but are probably overkill for personal or small site use.
+
+Setting up a CA for this is rather involved so I'll just provide a
+link below to suitable tutorial below.
+
+Using OpenPGP is much simpler.
+
+
+OpenPGP
+========
+
+This is very simple to setup and use.
+
+An RSA key pair can be generated with::
+
+ GNUPGHOME=$PWD/.gnupg gpg --openpgp \
+ --quick-generate-key --batch --passphrase '' "keyname" RSA
+
+The use of ``GNUPGHOME=$PWD/.gnupg`` just avoids messing with personal
+keyrings.
+We can list the resulting key::
+
+ GNUPGHOME=$PWD/.gnupg gpg --openpgp --list-keys
+
+ gpg: WARNING: unsafe permissions on homedir
+ '/h/sjg/openpgp/.gnupg'
+ gpg: Warning: using insecure memory!
+ /h/sjg/openpgp/.gnupg/pubring.kbx
+ ---------------------------------
+ pub rsa2048 2018-03-26 [SC] [expires: 2020-03-25]
+ AB39B111E40DD019E0E7C171ACA72B4719FD2523
+ uid [ultimate] OpenPGPtest
+
+The ``keyID`` we want later will be the last 8 octets
+(``ACA72B4719FD2523``)
+This is what we will use for looking up the key.
+
+We can then export the private and public keys::
+
+ GNUPGHOME=$PWD/.gnupg gpg --openpgp \
+ --export --armor > ACA72B4719FD2523.pub.asc
+ GNUPGHOME=$PWD/.gnupg gpg --openpgp \
+ --export-secret-keys --armor > ACA72B4719FD2523.sec.asc
+
+The public key ``ACA72B4719FD2523.pub.asc`` is what we want to
+embed in this library.
+If you look at the ``ta_asc.h`` target in ``openpgp/Makefile.inc``
+we want the trust anchor in a file named ``t*.asc``
+eg. ``ta_openpgp.asc``.
+
+The ``ta_asc.h`` target will capture all such ``t*.asc`` into that
+header.
+
+Signatures
+----------
+
+We expect ascii armored (``.asc``) detached signatures.
+Eg. signature for ``manifest`` would be in ``manifest.asc``
+
+We only support version 4 signatures using RSA (the default for ``gpg``).
+
+
+OpenSSL
+========
+
+The basic idea here is to setup a private CA.
+
+There are lots of good tutorials on available on this topic;
+just google *setup openssl ca*.
+A good example is https://jamielinux.com/docs/openssl-certificate-authority/
+
+All we need for this library is a copy of the PEM encoded root CA
+certificate (trust anchor). This is expected to be in a file named
+``t*.pem`` eg. ``ta_rsa.pem``.
+
+The ``ta.h`` target in ``Makefile.inc`` will combine all such
+``t*.pem`` files into that header.
+
+Signatures
+----------
+
+For Junos we currently use EC DSA signatures with file extension
+``.esig`` so the signature for ``manifest`` would be ``manifest.esig``
+
+This was the first signature method we used with the remote signing
+servers and it ends up being a signature of a hash.
+Ie. client sends a hash which during signing gets hashed again.
+So for Junos we define VE_ECDSA_HASH_AGAIN which causes ``verify_ec``
+to hash again.
+
+Otherwise our EC DSA and RSA signatures are the default used by
+OpenSSL - an original design goal was that a customer could verify our
+signatures using nothing but an ``openssl`` binary.
+
+
+Self tests
+==========
+
+If you want the ``loader`` to perform self-test of a given signature
+verification method on startup (a must for FIPS 140-2 certification)
+you need to provide a suitable file signed by each supported trust
+anchor.
+
+These should be stored in files with names that start with ``v`` and
+have the same extension as the corresponding trust anchor.
+Eg. for ``ta_openpgp.asc`` we use ``vc_openpgp.asc``
+and for ``ta_rsa.pem`` we use ``vc_rsa.pem``.
+
+Note for the X.509 case we simply extract the 2nd last certificate
+from the relevant chain - which is sure to be a valid certificate
+signed by the corresponding trust anchor.
+
+--------------------
+$FreeBSD$
diff --git a/lib/libsecureboot/brf.c b/lib/libsecureboot/brf.c
new file mode 100644
index 000000000000..9e3af4a7c089
--- /dev/null
+++ b/lib/libsecureboot/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 "libsecureboot-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;
+ }
+}
diff --git a/lib/libsecureboot/h/libsecureboot.h b/lib/libsecureboot/h/libsecureboot.h
new file mode 100644
index 000000000000..d0ec327b866a
--- /dev/null
+++ b/lib/libsecureboot/h/libsecureboot.h
@@ -0,0 +1,94 @@
+/*-
+ * Copyright (c) 2017-2018, Juniper Networks, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * $FreeBSD$
+ */
+#ifndef _LIBSECUREBOOT_H_
+#define _LIBSECUREBOOT_H_
+
+#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 */
+
+#endif /* _LIBSECUREBOOT_H_ */
diff --git a/lib/libsecureboot/h/verify_file.h b/lib/libsecureboot/h/verify_file.h
new file mode 100644
index 000000000000..3f8e6138cbc8
--- /dev/null
+++ b/lib/libsecureboot/h/verify_file.h
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 2017-2018, Juniper Networks, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _VERIFY_FILE_H_
+#define _VERIFY_FILE_H_
+
+#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 */
+
+struct stat;
+
+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);
+
+#endif /* _VERIFY_FILE_H_ */
diff --git a/lib/libsecureboot/libsecureboot-priv.h b/lib/libsecureboot/libsecureboot-priv.h
new file mode 100644
index 000000000000..0618f05f15b1
--- /dev/null
+++ b/lib/libsecureboot/libsecureboot-priv.h
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 2017, Juniper Networks, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * $FreeBSD$
+ */
+#ifndef _LIBSECUREBOOT_PRIV_H_
+#define _LIBSECUREBOOT_PRIV_H_
+
+/* public api */
+#include "libsecureboot.h"
+
+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);
+
+#endif /* _LIBSECUREBOOT_PRIV_H_ */
diff --git a/lib/libsecureboot/local.trust.mk b/lib/libsecureboot/local.trust.mk
new file mode 100644
index 000000000000..f26a4ec0a9be
--- /dev/null
+++ b/lib/libsecureboot/local.trust.mk
@@ -0,0 +1,114 @@
+# $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
+
+.if exists(${SIGNER})
+SIGN_HOST ?= ${SB_SITE:Usvl}-junos-signer.juniper.net
+ECDSA_PORT:= ${133%y:L:gmtime}
+SIGN_ECDSA= ${PYTHON} ${SIGNER} -u ${SIGN_HOST}:${ECDSA_PORT} -h sha256
+RSA2_PORT:= ${163%y:L:gmtime}
+SIGN_RSA2= ${PYTHON} ${SIGNER} -u ${SIGN_HOST}:${RSA2_PORT} -h sha256
+
+.if !empty(OPENPGP_SIGN_URL)
+VE_SIGNATURE_LIST+= OPENPGP
+VE_SIGNATURE_EXT_LIST+= asc
+
+SIGN_OPENPGP= ${PYTHON} ${SIGNER:H}/openpgp-sign.py -a -u ${OPENPGP_SIGN_URL}
+
+ta_openpgp.asc:
+ ${SIGN_OPENPGP} -C ${.TARGET}
+
+ta.h: ta_openpgp.asc
+
+.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
+#VE_DEBUG_LEVEL=3
+#VE_VERBOSE_DEFAULT=1
+
+.else
+# you need to provide t*.pem or t*.asc files for each trust anchor
+.if empty(TRUST_ANCHORS)
+TRUST_ANCHORS!= cd ${.CURDIR} && 'ls' -1 *.pem t*.asc 2> /dev/null
+.endif
+.if empty(TRUST_ANCHORS)
+.error Need TRUST_ANCHORS see ${.CURDIR}/README.rst
+.endif
+.if ${TRUST_ANCHORS:T:Mt*.pem} != ""
+ta.h: ${TRUST_ANCHORS:M*.pem}
+.endif
+.if ${TRUST_ANCHORS:T:Mt*.asc} != ""
+VE_SIGNATURE_LIST+= OPENPGP
+VE_SIGNATURE_EXT_LIST+= asc
+ta_asc.h: ${TRUST_ANCHORS:M*.asc}
+.endif
+# we take the mtime of this as our baseline time
+BUILD_UTC_FILE?= ${TRUST_ANCHORS:[1]}
+.endif
+
diff --git a/lib/libsecureboot/openpgp/Makefile.inc b/lib/libsecureboot/openpgp/Makefile.inc
new file mode 100644
index 000000000000..a1b234271852
--- /dev/null
+++ b/lib/libsecureboot/openpgp/Makefile.inc
@@ -0,0 +1,48 @@
+# $FreeBSD$
+
+# decode OpenPGP signatures per rfc4880
+.PATH: ${.PARSEDIR}
+
+CFLAGS+= -DUSE_BEARSSL
+
+BRSSL_SRCS+= dearmor.c
+SRCS+= \
+ decode.c \
+ opgp_key.c \
+ opgp_sig.c
+
+opgp_key.o opgp_key.po opgp_key.pico: ta_asc.h
+
+# Generate ta_asc.h containing one or more OpenPGP trust anchors.
+#
+# Since each trust anchor must be processed individually,
+# we create ta_ASC as a list of pointers to them.
+#
+# If we are doing self-tests, we define another arrary vc_ASC
+# containing pointers to a signature of each trust anchor.
+# It is assumed that these v*.asc files are named similarly to
+# the appropriate t*.asc so that the relative order of vc_ASC
+# entries matches ta_ASC.
+#
+ta_asc.h: ${.ALLTARGETS:M[tv]*.asc:O:u}
+.if ${VE_SIGNATURE_LIST:MOPENPGP} != ""
+ @( echo '/* Autogenerated - DO NOT EDIT!!! */'; echo; \
+ echo "#define HAVE_TA_ASC 1"; \
+ set -- ${.ALLSRC:Mt*.asc:@f@$f ${f:T:R}@}; \
+ while test $$# -ge 2; do \
+ file2c -sx "static const char $$2[] = {" ', 0x00 };' < $$1; \
+ shift 2; \
+ done; \
+ echo 'static const char *ta_ASC[] = { ${.ALLSRC:Mt*.asc:T:R:ts,}, NULL };'; \
+ echo; ) > ${.TARGET}
+.if ${VE_SELF_TESTS} != "no"
+ @( echo "#define HAVE_VC_ASC 1"; \
+ set -- ${.ALLSRC:Mv*.asc:@f@$f ${f:T:R}@}; \
+ while test $$# -ge 2; do \
+ file2c -sx "static const char $$2[] = {" ', 0x00 };' < $$1; \
+ shift 2; \
+ done; \
+ echo 'static const char *vc_ASC[] = { ${.ALLSRC:Mv*.asc:T:R:ts,}, NULL };'; \
+ echo; ) >> ${.TARGET}
+.endif
+.endif
diff --git a/lib/libsecureboot/openpgp/dearmor.c b/lib/libsecureboot/openpgp/dearmor.c
new file mode 100644
index 000000000000..3d53b54474dc
--- /dev/null
+++ b/lib/libsecureboot/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 <libsecureboot.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] & OPENPGP_TAG_ISTAG))
+ 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
diff --git a/lib/libsecureboot/openpgp/decode.c b/lib/libsecureboot/openpgp/decode.c
new file mode 100644
index 000000000000..fd6a19ad1bea
--- /dev/null
+++ b/lib/libsecureboot/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 <libsecureboot.h>
+
+#include "decode.h"
+
+char *
+octets2hex(unsigned char *ptr, size_t n)
+{
+ char *hex;
+ char *cp;
+ size_t i;
+
+ hex = malloc(2 * n + 1);
+ if (hex != NULL) {
+ for (i = 0, cp = hex; i < n; i++) {
+ snprintf(&cp[i*2], 3, "%02X", ptr[i]);
+ }
+ }
+ return (hex);
+}
+
+unsigned char *
+i2octets(int n, size_t 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, size_t n)
+{
+ size_t 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 & OPENPGP_TAG_ISTAG))
+ return (-1); /* we are lost! */
+ *isnew = tag & OPENPGP_TAG_ISNEW;
+ if (*isnew) {
+ *ltype = -1; /* irrelevant */
+ tag &= OPENPGP_TAG_NEW_MASK;
+ } else {
+ *ltype = tag & OPENPGP_TAG_OLD_TYPE;
+ tag = (tag & OPENPGP_TAG_OLD_MASK) >> 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, &ltype);
+
+ 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);
+}
diff --git a/lib/libsecureboot/openpgp/decode.h b/lib/libsecureboot/openpgp/decode.h
new file mode 100644
index 000000000000..8da39c9cf14b
--- /dev/null
+++ b/lib/libsecureboot/openpgp/decode.h
@@ -0,0 +1,57 @@
+/*-
+ * 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))
+
+#define OPENPGP_TAG_ISTAG 0200
+#define OPENPGP_TAG_ISNEW 0100
+#define OPENPGP_TAG_NEW_MASK 0077
+#define OPENPGP_TAG_OLD_MASK 0074
+#define OPENPGP_TAG_OLD_TYPE 0003
+
+typedef int (*decoder_t)(int, unsigned char **, int, void *);
+
+unsigned char * i2octets(int n, size_t i);
+int octets2i(unsigned char *ptr, size_t n);
+char * octets2hex(unsigned char *ptr, size_t 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);
diff --git a/lib/libsecureboot/openpgp/opgp_key.c b/lib/libsecureboot/openpgp/opgp_key.c
new file mode 100644
index 000000000000..1a8e06b1d677
--- /dev/null
+++ b/lib/libsecureboot/openpgp/opgp_key.c
@@ -0,0 +1,352 @@
+/*-
+ * 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 "../libsecureboot-priv.h"
+
+#include "decode.h"
+#include "packet.h"
+
+/**
+ * @brief decode user-id packet
+ *
+ * This is trivial
+ *
+ * @sa rfc4880:5.11
+ */
+ssize_t
+decode_user(int tag, unsigned char **pptr, size_t len, OpenPGP_user *user)
+{
+ char *cp;
+
+ if (tag == 13) {
+ user->id = malloc(len + 1);
+ strncpy(user->id, (char *)*pptr, len);
+ user->id[len] = '\0';
+ user->name = user->id;
+ cp = strchr(user->id, '<');
+ if (cp > user->id) {
+ user->id = strdup(user->id);
+ cp[-1] = '\0';
+ }
+ }
+ *pptr += len;
+ return ((ssize_t)len);
+}
+
+/**
+ * @brief decode a key packet
+ *
+ * We only really support v4 and RSA
+ *
+ * @sa rfc4880:5.5.1.1
+ */
+ssize_t
+decode_key(int tag, unsigned char **pptr, size_t len, OpenPGP_key *key)
+{
+ unsigned char *ptr;
+ int version;
+#ifdef USE_BEARSSL
+ br_sha1_context mctx;
+ unsigned char mdata[br_sha512_SIZE];
+ size_t mlen;
+#else
+ RSA *rsa = NULL;
+ const EVP_MD *md = NULL;
+ EVP_MD_CTX mctx;
+ unsigned char mdata[EVP_MAX_MD_SIZE];
+ unsigned int mlen;
+#endif
+
+ if (tag != 6)
+ return (-1);
+
+ key->key = NULL;
+ ptr = *pptr;
+ version = *ptr;
+ if (version == 4) { /* all we support really */
+ /* comput key fingerprint and id @sa rfc4880:12.2 */
+ mdata[0] = 0x99; /* rfc4880: 12.2.a.1 */
+ mdata[1] = (len >> 8) & 0xff;
+ mdata[2] = len & 0xff;
+
+#ifdef USE_BEARSSL
+ br_sha1_init(&mctx);
+ br_sha1_update(&mctx, mdata, 3);
+ br_sha1_update(&mctx, ptr, len);
+ br_sha1_out(&mctx, mdata);
+ mlen = br_sha1_SIZE;
+#else
+ md = EVP_get_digestbyname("sha1");
+ EVP_DigestInit(&mctx, md);
+ EVP_DigestUpdate(&mctx, mdata, 3);
+ EVP_DigestUpdate(&mctx, ptr, len);
+ mlen = (unsigned int)sizeof(mdata);
+ EVP_DigestFinal(&mctx, mdata, &mlen);
+#endif
+ key->id = octets2hex(&mdata[mlen - 8], 8);
+ }
+ ptr += 1; /* done with version */
+ ptr += 4; /* skip ctime */
+ if (version == 3)
+ ptr += 2; /* valid days */
+ key->sig_alg = *ptr++;
+ if (key->sig_alg == 1) { /* RSA */
+#ifdef USE_BEARSSL
+ key->key = NEW(br_rsa_public_key);
+ if (!key->key)
+ goto oops;
+ key->key->n = mpi2bn(&ptr, &key->key->nlen);
+ key->key->e = mpi2bn(&ptr, &key->key->elen);
+#else
+ rsa = RSA_new();
+ if (!rsa)
+ goto oops;
+ rsa->n = mpi2bn(&ptr);
+ rsa->e = mpi2bn(&ptr);
+ key->key = EVP_PKEY_new();
+ if (!key->key || !rsa->n || !rsa->e) {
+ goto oops;
+ }
+ if (!EVP_PKEY_set1_RSA(key->key, rsa))
+ goto oops;
+#endif
+ }
+ /* we are done */
+ return ((ssize_t)len);
+oops:
+#ifdef USE_BEARSSL
+ free(key->key);
+ key->key = NULL;
+#else
+ if (rsa)
+ RSA_free(rsa);
+ if (key->key) {
+ EVP_PKEY_free(key->key);
+ key->key = NULL;
+ }
+#endif
+ return (-1);
+}
+
+static OpenPGP_key *
+load_key_buf(unsigned char *buf, size_t nbytes)
+{
+ unsigned char *data = NULL;
+ unsigned char *ptr;
+ ssize_t rc;
+ int tag;
+ OpenPGP_key *key;
+
+ if (!buf)
+ return (NULL);
+
+ initialize();
+
+ if (!(buf[0] & OPENPGP_TAG_ISTAG)) {
+ data = dearmor((char *)buf, nbytes, &nbytes);
+ ptr = data;
+ } else
+ ptr = buf;
+ key = NEW(OpenPGP_key);
+ if (key) {
+ rc = decode_packet(0, &ptr, nbytes, (decoder_t)decode_key,
+ key);
+ if (rc < 0) {
+ free(key);
+ key = NULL;
+ } else if (rc > 8) {
+ int isnew, ltype;
+
+ tag = decode_tag(ptr, &isnew, &ltype);
+ 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
+ const char **tp;
+ char *cp;
+ size_t n;
+
+ for (tp = ta_ASC; *tp; tp++) {
+ if ((cp = strdup(*tp))) {
+ n = strlen(cp);
+ key = load_key_buf((unsigned char *)cp, n);
+ free(cp);
+ openpgp_trust_add(key);
+ }
+ }
+#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
+ *
+ * Unlike X.509 certificates, we only support RSA keys
+ * so we stop after first successful signature verification
+ * (which should also be the first attempt ;-)
+ */
+int
+openpgp_self_tests(void)
+{
+ static int rc = -1; /* remember result */
+#ifdef HAVE_VC_ASC
+ const char **vp, **tp;
+ char *fdata, *sdata = NULL;
+ size_t fbytes, sbytes;
+
+ for (tp = ta_ASC, vp = vc_ASC; *tp && *vp && rc; tp++, vp++) {
+ if ((fdata = strdup(*tp)) &&
+ (sdata = strdup(*vp))) {
+ fbytes = strlen(fdata);
+ sbytes = strlen(sdata);
+ rc = openpgp_verify("ta_ASC",
+ (unsigned char *)fdata, fbytes,
+ (unsigned char *)sdata, sbytes, 0);
+ printf("Testing verify OpenPGP signature:\t\t%s\n",
+ rc ? "Failed" : "Passed");
+ }
+ free(fdata);
+ free(sdata);
+ }
+#endif
+ return (rc);
+}
diff --git a/lib/libsecureboot/openpgp/opgp_sig.c b/lib/libsecureboot/openpgp/opgp_sig.c
new file mode 100644
index 000000000000..804e2c83ce45
--- /dev/null
+++ b/lib/libsecureboot/openpgp/opgp_sig.c
@@ -0,0 +1,484 @@
+/*-
+ * 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 "../libsecureboot-priv.h"
+#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
+
+#define get_error_string ve_error_get
+
+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] & OPENPGP_TAG_ISTAG))
+ 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);
+}
diff --git a/lib/libsecureboot/openpgp/packet.h b/lib/libsecureboot/openpgp/packet.h
new file mode 100644
index 000000000000..28ac3c9b40fe
--- /dev/null
+++ b/lib/libsecureboot/openpgp/packet.h
@@ -0,0 +1,82 @@
+/*-
+ * 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);
+
+/* 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);
diff --git a/lib/libsecureboot/readfile.c b/lib/libsecureboot/readfile.c
new file mode 100644
index 000000000000..f632922143ce
--- /dev/null
+++ b/lib/libsecureboot/readfile.c
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (c) 2017-2018, Juniper Networks, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <libsecureboot.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);
+}
diff --git a/lib/libsecureboot/tests/Makefile b/lib/libsecureboot/tests/Makefile
new file mode 100644
index 000000000000..0cc32562d36b
--- /dev/null
+++ b/lib/libsecureboot/tests/Makefile
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+PROG= tvo
+
+SRCS+= tvo.c
+CFLAGS+= -DUNIT_TEST -g -O0
+
+LIBADD+= bearssl
+MAN=
+NO_SHARED=
+
+# we want to test verify_file api too
+# which requires a kludge or two
+.include "../Makefile.libsa.inc"
+BRSSL_CFLAGS := ${BRSSL_CFLAGS:N-DNO_STDIO}
+XCFLAGS.verify_file += -DSOPEN_MAX=64
+
+.include <bsd.prog.mk>
diff --git a/lib/libsecureboot/tests/Makefile.depend.host b/lib/libsecureboot/tests/Makefile.depend.host
new file mode 100644
index 000000000000..772c1c8d1c6b
--- /dev/null
+++ b/lib/libsecureboot/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
diff --git a/lib/libsecureboot/tests/tvo.c b/lib/libsecureboot/tests/tvo.c
new file mode 100644
index 000000000000..9ad327801016
--- /dev/null
+++ b/lib/libsecureboot/tests/tvo.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2017-2018, Juniper Networks, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "../libsecureboot-priv.h"
+
+#include <unistd.h>
+#include <err.h>
+#include <verify_file.h>
+
+char *Skip;
+
+int
+main(int argc, char *argv[])
+{
+ int n;
+ int fd;
+ int c;
+ int Vflag;
+ char *cp;
+ char *prefix;
+
+ prefix = NULL;
+ Skip = NULL;
+
+ n = ve_trust_init();
+ printf("Trust %d\n", n);
+ Vflag = 0;
+
+ while ((c = getopt(argc, argv, "dp:s:T:V")) != -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;
+ case 'V':
+ Vflag = 1;
+ break;
+ default:
+ errx(1, "unknown option: -%c", c);
+ break;
+ }
+ }
+
+ ve_self_tests();
+
+ for ( ; optind < argc; optind++) {
+ if (Vflag) {
+ /*
+ * Simulate what loader does.
+ * verify_file should "just work"
+ */
+ fd = open(argv[optind], O_RDONLY);
+ if (fd > 0) {
+ /*
+ * See if verify_file is happy
+ */
+ int x;
+
+ x = verify_file(fd, argv[optind], 0, VE_GUESS);
+ printf("verify_file(%s) = %d\n", argv[optind], x);
+ close(fd);
+ }
+ continue;
+ }
+#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);
+}
+
diff --git a/lib/libsecureboot/vectx.c b/lib/libsecureboot/vectx.c
new file mode 100644
index 000000000000..879cecac188d
--- /dev/null
+++ b/lib/libsecureboot/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 "libsecureboot-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 supported 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);
+}
diff --git a/lib/libsecureboot/veopen.c b/lib/libsecureboot/veopen.c
new file mode 100644
index 000000000000..b9da7e4479d8
--- /dev/null
+++ b/lib/libsecureboot/veopen.c
@@ -0,0 +1,458 @@
+/*-
+ * Copyright (c) 2017-2018, Juniper Networks, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/queue.h>
+
+#include "libsecureboot-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;
+ int n;
+
+ 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;
+ }
+ }
+ /* collapse any trailing ..[/] */
+ n = 0;
+ while ((cp = strrchr(nfip->fi_prefix, '/')) != NULL) {
+ if (cp[1] == '\0') { /* trailing "/" */
+ *cp = '\0';
+ continue;
+ }
+ if (strcmp(&cp[1], "..") == 0) {
+ n++;
+ *cp = '\0';
+ continue;
+ }
+ if (n > 0) {
+ n--;
+ *cp = '\0';
+ }
+ if (n == 0)
+ break;
+ }
+#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_len = strlen(skip);
+ if (nfip->fi_skip_len)
+ nfip->fi_skip = strdup(skip);
+ else
+ nfip->fi_skip = NULL;
+ } 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));
+}
+
+#ifdef MANIFEST_SKIP_MAYBE
+/*
+ * Deal with old incompatible boot/manifest
+ * if fp[-1] is '/' and start of entry matches
+ * MANIFEST_SKIP_MAYBE, we want it.
+ */
+static char *
+maybe_skip(char *fp, struct fingerprint_info *fip, size_t *nplenp)
+{
+ char *tp;
+
+ tp = fp - sizeof(MANIFEST_SKIP_MAYBE);
+
+ if (tp >= fip->fi_data) {
+ DEBUG_PRINTF(3, ("maybe: %.48s\n", tp));
+ if ((tp == fip->fi_data || tp[-1] == '\n') &&
+ strncmp(tp, MANIFEST_SKIP_MAYBE,
+ sizeof(MANIFEST_SKIP_MAYBE) - 1) == 0) {
+ fp = tp;
+ *nplenp += sizeof(MANIFEST_SKIP_MAYBE);
+ }
+ }
+ return (fp);
+}
+#endif
+
+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];
+ do {
+ if (ep) {
+ *ep = '/';
+ cp -= 2;
+ if (cp < pbuf)
+ break;
+ }
+ nlen = plen = 0; /* keep gcc quiet */
+ if (cp > pbuf) {
+ for ( ; cp >= pbuf && *cp != '/'; cp--)
+ ; /* nothing */
+ if (cp > pbuf) {
+ ep = cp++;
+ *ep = '\0';
+ } else {
+ cp = pbuf;
+ }
+ if (ep) {
+ plen = ep - pbuf;
+ nlen = n - plen - 1;
+ }
+ }
+ if (cp == pbuf) {
+ prefix = "/";
+ plen = 1;
+ if (*cp == '/') {
+ nlen = n - 1;
+ cp++;
+ } else
+ nlen = n;
+ 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;
+#ifdef MANIFEST_SKIP_MAYBE
+ if (fip->fi_skip_len == 0 &&
+ fp > fip->fi_data && fp[-1] == '/') {
+ fp = maybe_skip(fp, fip, &nplen);
+ }
+#endif
+ /*
+ * 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) {
+#ifdef MANIFEST_SKIP_MAYBE
+ if (fip->fi_skip_len == 0 &&
+ fp > fip->fi_data &&
+ fp[-1] == '/') {
+ fp = maybe_skip(fp, fip, &nplen);
+ }
+#endif
+ 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));
+ }
+ }
+ } while (cp > &pbuf[1]);
+
+ 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);
+}
diff --git a/lib/libsecureboot/vepcr.c b/lib/libsecureboot/vepcr.c
new file mode 100644
index 000000000000..0a3dc1fa12eb
--- /dev/null
+++ b/lib/libsecureboot/vepcr.c
@@ -0,0 +1,84 @@
+/*-
+ * 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 "libsecureboot-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.
+ *
+ * NOTE: in the current standard the TPM pcr register size is for SHA1,
+ * the fact that we provide a SHA256 hash should not matter
+ * as long as we are consistent - it can be truncated or hashed
+ * before feeding to TPM.
+ */
+
+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);
+}
+
diff --git a/lib/libsecureboot/verify_file.c b/lib/libsecureboot/verify_file.c
new file mode 100644
index 000000000000..8bde42090755
--- /dev/null
+++ b/lib/libsecureboot/verify_file.c
@@ -0,0 +1,421 @@
+/*-
+ * Copyright (c) 2017-2018, Juniper Networks, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Routines to verify files loaded.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include "libsecureboot.h"
+#include <verify_file.h>
+#include <manifests.h>
+
+#define VE_NOT_CHECKED -42
+
+#ifdef UNIT_TEST
+# include <err.h>
+# define panic warn
+/*
+ * define MANIFEST_SKIP to Skip - in tests/tvo.c so that
+ * tvo can control the value we use in find_manifest()
+ */
+extern char *Skip;
+# undef MANIFEST_SKIP
+# define MANIFEST_SKIP Skip
+# undef VE_DEBUG_LEVEL
+#endif
+
+/*
+ * 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;
+ 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 /* very unlikely */
+ skip = MANIFEST_SKIP_ALWAYS;
+#else
+#ifdef MANIFEST_SKIP /* rare */
+ if (*tp[0] == '.') {
+ skip = MANIFEST_SKIP;
+ } else
+#endif
+ skip = NULL;
+#endif
+ rc = load_manifest(buf, skip ? prefix : NULL,
+ 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, (long long)st.st_dev,
+ (long long)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
+}
diff --git a/lib/libsecureboot/vesigned.c b/lib/libsecureboot/vesigned.c
new file mode 100644
index 000000000000..47195f82494a
--- /dev/null
+++ b/lib/libsecureboot/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 <libsecureboot.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);
+}
diff --git a/lib/libsecureboot/veta.c b/lib/libsecureboot/veta.c
new file mode 100644
index 000000000000..f052f41792bf
--- /dev/null
+++ b/lib/libsecureboot/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 "libsecureboot-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));
+}
diff --git a/lib/libsecureboot/vets.c b/lib/libsecureboot/vets.c
new file mode 100644
index 000000000000..db96fe965a5c
--- /dev/null
+++ b/lib/libsecureboot/vets.c
@@ -0,0 +1,700 @@
+/*-
+ * Copyright (c) 2017-2018, Juniper Networks, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/**
+ * @file vets.c - trust store
+ * @brief verify signatures
+ *
+ * We leverage code from BearSSL www.bearssl.org
+ */
+
+#include <sys/time.h>
+#include <stdarg.h>
+#define NEED_BRSSL_H
+#include "libsecureboot-priv.h"
+#include <brssl.h>
+#include <ta.h>
+
+#ifndef TRUST_ANCHOR_STR
+# define TRUST_ANCHOR_STR ta_PEM
+#endif
+
+#define 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(__DECONST(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];
+ const unsigned char *hash_oid;
+ const br_hash_class *md;
+ br_hash_compat_context mctx;
+ unsigned char *fcp, *scp;
+ size_t flen, slen, plen, hlen;
+ pem_object *po;
+
+ if ((fcp = read_file(file, &flen)) == NULL)
+ return (NULL);
+ if ((scp = read_file(sigfile, &slen)) == NULL) {
+ free(fcp);
+ return (NULL);
+ }
+ if ((po = decode_pem(scp, slen, &plen)) == NULL) {
+ free(fcp);
+ free(scp);
+ return (NULL);
+ }
+
+ switch (po->data_len) {
+#if defined(UNIT_TEST) && defined(VE_DEPRECATED_RSA_SHA1_SUPPORT)
+ case 256:
+ // this is our old deprecated sig method
+ md = &br_sha1_vtable;
+ hlen = br_sha1_SIZE;
+ hash_oid = BR_HASH_OID_SHA1;
+ break;
+#endif
+ default:
+ md = &br_sha256_vtable;
+ hlen = br_sha256_SIZE;
+ hash_oid = BR_HASH_OID_SHA256;
+ break;
+ }
+ md->init(&mctx.vtable);
+ md->update(&mctx.vtable, fcp, flen);
+ md->out(&mctx.vtable, rhbuf);
+ if (!verify_rsa_digest(&pk->key.rsa, hash_oid,
+ rhbuf, hlen, po->data, po->data_len)) {
+ free(fcp);
+ fcp = NULL;
+ }
+ free(scp);
+ return (fcp);
+}
+#endif
+
+/**
+ * @brief
+ * verify a signature and return content of signed file
+ *
+ * @param[in] sigfile
+ * file containing signature
+ * we derrive path of signed file and certificate change from
+ * this.
+ *
+ * @param[in] flags
+ * only bit 1 significant so far
+ *
+ * @return NULL on error otherwise content of signed file
+ */
+unsigned char *
+verify_sig(const char *sigfile, int flags)
+{
+ br_x509_pkey *pk;
+ br_name_element cn;
+ char cn_buf[80];
+ unsigned char cn_oid[4];
+ char pbuf[MAXPATHLEN];
+ char *cp;
+ unsigned char *ucp;
+ size_t n;
+
+ DEBUG_PRINTF(5, ("verify_sig: %s\n", sigfile));
+ n = strlcpy(pbuf, sigfile, sizeof(pbuf));
+ if (n > (sizeof(pbuf) - 5) || strcmp(&sigfile[n - 3], "sig") != 0)
+ return (NULL);
+ cp = strcpy(&pbuf[n - 3], "certs");
+ /*
+ * We want the commonName field
+ * the OID we want is 2,5,4,3 - but DER encoded
+ */
+ cn_oid[0] = 3;
+ cn_oid[1] = 0x55;
+ cn_oid[2] = 4;
+ cn_oid[3] = 3;
+ cn.oid = cn_oid;
+ cn.buf = cn_buf;
+ cn.len = sizeof(cn_buf);
+
+ pk = verify_signer(pbuf, &cn, 1);
+ if (!pk) {
+ printf("cannot verify: %s: %s\n", pbuf, ve_error_get());
+ return (NULL);
+ }
+ for (; cp > pbuf; cp--) {
+ if (*cp == '.') {
+ *cp = '\0';
+ break;
+ }
+ }
+ switch (pk->key_type) {
+#ifdef VE_ECDSA_SUPPORT
+ case BR_KEYTYPE_EC:
+ ucp = verify_ec(pk, pbuf, sigfile);
+ break;
+#endif
+#ifdef VE_RSA_SUPPORT
+ case BR_KEYTYPE_RSA:
+ ucp = verify_rsa(pk, pbuf, sigfile);
+ break;
+#endif
+ default:
+ ucp = NULL; /* not supported */
+ }
+ xfreepkey(pk);
+ if (!ucp) {
+ printf("Unverified %s (%s)\n", pbuf,
+ cn.status ? cn_buf : "unknown");
+ } else if ((flags & 1) != 0) {
+ printf("Verified %s signed by %s\n", pbuf,
+ cn.status ? cn_buf : "someone we trust");
+ }
+ return (ucp);
+}
+
+
+/**
+ * @brief verify hash matches
+ *
+ * We have finished hashing a file,
+ * see if we got the desired result.
+ *
+ * @param[in] ctx
+ * pointer to hash context
+ *
+ * @param[in] md
+ * pointer to hash class
+ *
+ * @param[in] path
+ * name of the file we are checking
+ *
+ * @param[in] want
+ * the expected result
+ *
+ * @param[in] hlen
+ * size of hash output
+ *
+ * @return 0 on success
+ */
+int
+ve_check_hash(br_hash_compat_context *ctx, const br_hash_class *md,
+ const char *path, const char *want, size_t hlen)
+{
+ char hexbuf[br_sha512_SIZE * 2 + 2];
+ unsigned char hbuf[br_sha512_SIZE];
+ char *hex;
+ int rc;
+ int n;
+
+ md->out(&ctx->vtable, hbuf);
+#ifdef VE_PCR_SUPPORT
+ ve_pcr_update(hbuf, hlen);
+#endif
+ hex = hexdigest(hexbuf, sizeof(hexbuf), hbuf, hlen);
+ if (!hex)
+ return (VE_FINGERPRINT_WRONG);
+ n = 2*hlen;
+ if ((rc = strncmp(hex, want, n))) {
+ ve_error_set("%s: %.*s != %.*s", path, n, hex, n, want);
+ rc = VE_FINGERPRINT_WRONG;
+ }
+ return (rc ? rc : VE_FINGERPRINT_OK);
+}
+
+#ifdef VE_HASH_KAT_STR
+static int
+test_hash(const br_hash_class *md, size_t hlen,
+ const char *hname, const char *s, size_t slen, const char *want)
+{
+ br_hash_compat_context mctx;
+
+ md->init(&mctx.vtable);
+ md->update(&mctx.vtable, s, slen);
+ return (ve_check_hash(&mctx, md, hname, want, hlen) != VE_FINGERPRINT_OK);
+}
+
+#endif
+
+#define ve_test_hash(n, N) \
+ printf("Testing hash: " #n "\t\t\t\t%s\n", \
+ test_hash(&br_ ## n ## _vtable, br_ ## n ## _SIZE, #n, \
+ VE_HASH_KAT_STR, sizeof(VE_HASH_KAT_STR), \
+ vh_ ## N) ? "Failed" : "Passed")
+
+/**
+ * @brief
+ * run self tests on hash and signature verification
+ *
+ * Test that the hash methods (SHA1 and SHA256) work.
+ * Test that we can verify a certificate for each supported
+ * Root CA.
+ *
+ * @return cached result.
+ */
+int
+ve_self_tests(void)
+{
+ static int once = -1;
+#ifdef VERIFY_CERTS_STR
+ br_x509_certificate *xcs;
+ br_x509_pkey *pk;
+ br_name_element cn;
+ char cn_buf[80];
+ unsigned char cn_oid[4];
+ size_t num;
+ size_t u;
+#endif
+
+ if (once >= 0)
+ return (once);
+ once = 0;
+
+ DEBUG_PRINTF(5, ("Self tests...\n"));
+#ifdef VE_HASH_KAT_STR
+#ifdef VE_SHA1_SUPPORT
+ ve_test_hash(sha1, SHA1);
+#endif
+#ifdef VE_SHA256_SUPPORT
+ ve_test_hash(sha256, SHA256);
+#endif
+#ifdef VE_SHA384_SUPPORT
+ ve_test_hash(sha384, SHA384);
+#endif
+#ifdef VE_SHA512_SUPPORT
+ ve_test_hash(sha512, SHA512);
+#endif
+#endif
+#ifdef VERIFY_CERTS_STR
+ xcs = parse_certificates(__DECONST(unsigned char *, VERIFY_CERTS_STR),
+ sizeof(VERIFY_CERTS_STR), &num);
+ if (xcs == NULL)
+ return (0);
+ /*
+ * We want the commonName field
+ * the OID we want is 2,5,4,3 - but DER encoded
+ */
+ cn_oid[0] = 3;
+ cn_oid[1] = 0x55;
+ cn_oid[2] = 4;
+ cn_oid[3] = 3;
+ cn.oid = cn_oid;
+ cn.buf = cn_buf;
+
+ 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);
+}