aboutsummaryrefslogtreecommitdiff
path: root/lib/libsecureboot
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libsecureboot')
-rw-r--r--lib/libsecureboot/Makefile9
-rw-r--r--lib/libsecureboot/Makefile.depend15
-rw-r--r--lib/libsecureboot/Makefile.depend.amd6415
-rw-r--r--lib/libsecureboot/Makefile.depend.host10
-rw-r--r--lib/libsecureboot/Makefile.inc176
-rw-r--r--lib/libsecureboot/Makefile.libsa.inc59
-rw-r--r--lib/libsecureboot/README.rst142
-rw-r--r--lib/libsecureboot/brf.c401
-rw-r--r--lib/libsecureboot/efi/efi_init.c72
-rw-r--r--lib/libsecureboot/efi/efi_variables.c274
-rw-r--r--lib/libsecureboot/efi/include/Guid/GlobalVariable.h192
-rw-r--r--lib/libsecureboot/efi/include/Guid/ImageAuthentication.h350
-rw-r--r--lib/libsecureboot/efi/include/Protocol/Hash.h169
-rw-r--r--lib/libsecureboot/h/libsecureboot.h98
-rw-r--r--lib/libsecureboot/h/verify_file.h67
-rw-r--r--lib/libsecureboot/libsecureboot-priv.h70
-rw-r--r--lib/libsecureboot/local.trust.mk108
-rw-r--r--lib/libsecureboot/openpgp/Makefile.inc49
-rw-r--r--lib/libsecureboot/openpgp/dearmor.c142
-rw-r--r--lib/libsecureboot/openpgp/decode.c302
-rw-r--r--lib/libsecureboot/openpgp/decode.h56
-rw-r--r--lib/libsecureboot/openpgp/opgp_key.c413
-rw-r--r--lib/libsecureboot/openpgp/opgp_sig.c492
-rw-r--r--lib/libsecureboot/openpgp/packet.h81
-rw-r--r--lib/libsecureboot/pass_manifest.c148
-rw-r--r--lib/libsecureboot/readfile.c80
-rw-r--r--lib/libsecureboot/tests/Makefile19
-rw-r--r--lib/libsecureboot/tests/Makefile.depend.host11
-rw-r--r--lib/libsecureboot/tests/tvo.c213
-rw-r--r--lib/libsecureboot/vectx.c416
-rw-r--r--lib/libsecureboot/veopen.c469
-rw-r--r--lib/libsecureboot/vepcr.c167
-rw-r--r--lib/libsecureboot/verify_file.c690
-rw-r--r--lib/libsecureboot/vesigned.c59
-rw-r--r--lib/libsecureboot/veta.c109
-rw-r--r--lib/libsecureboot/vets.c1156
36 files changed, 7299 insertions, 0 deletions
diff --git a/lib/libsecureboot/Makefile b/lib/libsecureboot/Makefile
new file mode 100644
index 000000000000..85167e57abf2
--- /dev/null
+++ b/lib/libsecureboot/Makefile
@@ -0,0 +1,9 @@
+.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..6ef78fac5cbf
--- /dev/null
+++ b/lib/libsecureboot/Makefile.depend
@@ -0,0 +1,15 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ 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.amd64 b/lib/libsecureboot/Makefile.depend.amd64
new file mode 100644
index 000000000000..6ef78fac5cbf
--- /dev/null
+++ b/lib/libsecureboot/Makefile.depend.amd64
@@ -0,0 +1,15 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ 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..11aba52f82cf
--- /dev/null
+++ b/lib/libsecureboot/Makefile.depend.host
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.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..21ad019a0cb5
--- /dev/null
+++ b/lib/libsecureboot/Makefile.inc
@@ -0,0 +1,176 @@
+.if empty(BEARSSL)
+.include "../libbearssl/Makefile.inc"
+.endif
+
+.if !target(_${__this}_)
+_${__this}_:
+
+libsecureboot_src:= ${.PARSEDIR}
+
+CFLAGS+= -I${libsecureboot_src}/h
+
+CFLAGS+= -DHAVE_BR_X509_TIME_CHECK
+
+.PATH: ${.PARSEDIR}
+
+SRCS+= \
+ readfile.c \
+ brf.c \
+ vesigned.c \
+ vets.c
+
+.if ${.CURDIR:M*libsecureboot*} != ""
+SRCS+= veta.c
+.endif
+
+CFLAGS+= ${XCFLAGS.${.TARGET:T:R}:U}
+
+# we use a couple of files from ${BEARSSL}/tools
+BRSSL_CFLAGS+= -I${BEARSSL}/tools
+BRSSL_SRCS+= \
+ ${BEARSSL}/tools/xmem.c \
+ ${BEARSSL}/tools/vector.c
+
+BRSSL_DEPS= \
+ brf.c \
+ vets.c \
+ veta.c
+
+.if ${MK_LOADER_EFI_SECUREBOOT} != "no"
+BRSSL_DEPS+= \
+ efi_init.c \
+ efi_variables.c
+.endif
+
+# we do not need/want nested objdirs
+OBJS_SRCS_FILTER = T R
+
+SRCS+= ${BRSSL_SRCS}
+
+
+# extract the last cert from a chain (should be rootCA)
+_LAST_PEM_USE: .USE
+ sed "1,`grep -n .-END ${.ALLSRC:M*.pem} | tail -2 | head -1 | sed 's,:.*,,'`d" ${.ALLSRC:M*.pem} > ${.TARGET}
+
+# extract 2nd last cert from chain - we use this for self-test
+_2ndLAST_PEM_USE: .USE
+ sed -n "`grep -n .-BEGIN ${.ALLSRC:M*.pem} | tail -2 | \
+ sed 's,:.*,,' | xargs | (read a b; echo $$a,$$(($$b - 1)))`p" ${.ALLSRC:M*.pem} > ${.TARGET}
+
+# 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"
+
+# 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
+
+CFLAGS+= -I.
+
+.if ${VE_SIGNATURE_EXT_LIST:M*sig} != ""
+# this is what we use as our trust anchor
+CFLAGS+= -DTRUST_ANCHOR_STR=ta_PEM
+
+.if ${VE_SELF_TESTS} != "no"
+XCFLAGS.vets+= -DVERIFY_CERTS_STR=vc_PEM
+.endif
+.endif
+
+# clean these up
+VE_HASH_LIST:= ${VE_HASH_LIST:tu:O:u}
+VE_SIGNATURE_LIST:= ${VE_SIGNATURE_LIST:tu:O:u}
+
+# define what we are supporting
+CFLAGS+= ${VE_HASH_LIST:@H@-DVE_$H_SUPPORT@} \
+ ${VE_SIGNATURE_LIST:@S@-DVE_$S_SUPPORT@}
+
+.if ${VE_SIGNATURE_LIST:MOPENPGP} != ""
+.include "openpgp/Makefile.inc"
+.endif
+
+.if ${VE_SELF_TESTS} != "no"
+# The input used for hash KATs
+# we use a string by default so it is independent of any other test
+VE_HASH_KAT_STRLEN?= strlen
+.if ${VE_HASH_KAT_STRLEN} == "strlen"
+VE_HASH_KAT_STR?= self-tests-are-good
+VE_HASH_KAT_STR_INPUT= echo -n
+XCFLAGS.vets+= -DVE_HASH_KAT_STR=\"${VE_HASH_KAT_STR}\"
+.else
+VE_HASH_KAT_STR?= vc_PEM
+VE_HASH_KAT_STR_INPUT= cat
+VE_HASH_KAT_STRLEN= sizeof
+XCFLAGS.vets+= -DVE_HASH_KAT_STR=${VE_HASH_KAT_STR}
+.endif
+XCFLAGS.vets+= -DVE_HASH_KAT_STRLEN=${VE_HASH_KAT_STRLEN}
+.endif
+
+# this should be updated occassionally this is 2019-01-01Z
+SOURCE_DATE_EPOCH?= 1546329600
+.if ${MK_REPRODUCIBLE_BUILD} == "yes"
+BUILD_UTC?= ${SOURCE_DATE_EPOCH}
+.endif
+# BUILD_UTC provides a basis for the loader's notion of time
+# By default we use the mtime of BUILD_UTC_FILE
+.if empty(BUILD_UTC_FILE)
+BUILD_UTC_FILE:= ${.PARSEDIR:tA}/${.PARSEFILE}
+.endif
+# you can of course set BUILD_UTC to any value you like
+.if ${MAKE_VERSION} > 20230509
+BUILD_UTC?= ${BUILD_UTC_FILE:mtime}
+.else
+BUILD_UTC?= ${${STAT:Ustat} -L -f %m ${BUILD_UTC_FILE}:L:sh}
+.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 VE_HASH_KAT_STR
+# using each supported hash method
+# to use as a Known Answer Test (needed for FIPS 140-2)
+#
+TA_PEM_LIST ?= ${.ALLSRC:N*crl*:Mt*.pem}
+VC_PEM_LIST ?= ${.ALLSRC:N*crl*:Mv*.pem}
+vets.o vets.po vets.pico: ta.h
+ta.h:
+ @( echo '/* Autogenerated - DO NOT EDIT!!! */'; echo; \
+ cat ${TA_PEM_LIST:O:u} /dev/null | \
+ file2c -sx 'static const char ta_PEM[] = {' '};'; \
+ echo "${.newline}${VE_HASH_LIST:O:u:@H@static char vh_$H[] = \"`${VE_HASH_KAT_STR_INPUT} ${VE_HASH_KAT_STR} | ${$H:U${H:tl}}`\";${.newline}@}"; ) > ${.TARGET}
+.if ${VE_SELF_TESTS} != "no"
+ ( cat ${VC_PEM_LIST:O:u} /dev/null | \
+ file2c -sx 'static const char vc_PEM[] = {' '};'; echo ) >> ${.TARGET}
+.endif
+ echo '#define BUILD_UTC ${BUILD_UTC}' >> ${.TARGET} ${.OODATE:MNOMETA_CMP}
+
+# This header records our preference for signature extensions.
+vesigned.o vesigned.po vesigned.pico: vse.h
+vse.h:
+ @( echo '/* Autogenerated - DO NOT EDIT!!! */'; echo; \
+ echo "static const char *signature_exts[] = {"; \
+ echo '${VE_SIGNATURE_EXT_LIST:O:u:@e@"$e",${.newline}@}'; \
+ echo 'NULL };' ) > ${.TARGET}
+
+
+.for s in ${BRSSL_SRCS} ${BRSSL_DEPS}
+.ifdef BRSSL_SED
+$s: brssl.h
+.endif
+XCFLAGS.${s:R}+= ${BRSSL_CFLAGS}
+.endfor
+
+.endif
diff --git a/lib/libsecureboot/Makefile.libsa.inc b/lib/libsecureboot/Makefile.libsa.inc
new file mode 100644
index 000000000000..7397d0fc1144
--- /dev/null
+++ b/lib/libsecureboot/Makefile.libsa.inc
@@ -0,0 +1,59 @@
+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 \
+
+# Build library with support for the UEFI based authentication
+.if ${MK_LOADER_EFI_SECUREBOOT} == "yes"
+SRCS+= \
+ efi/efi_variables.c \
+ efi/efi_init.c
+
+# Add includes required by efi part
+CFLAGS+= \
+ -I${SRCTOP}/stand/efi/include \
+ -I${SRCTOP}/lib/libsecureboot/efi/include \
+ -I${SRCTOP}/stand/efi/include/${MACHINE}
+.endif
+
+.if ${MK_LOADER_VERIEXEC_PASS_MANIFEST} == "yes"
+SRCS+= \
+ pass_manifest.c
+.endif
+
+# 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}
+
+# only add these if set
+XCFLAGS.verify_file+= \
+ ${VE_DEBUG_LEVEL \
+ VE_VERBOSE_DEFAULT \
+ VE_VERIFY_FLAGS \
+ :L:@v@${$v:S,^,-D$v=,}@}
+
+.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..f1d3c5679d35
--- /dev/null
+++ b/lib/libsecureboot/README.rst
@@ -0,0 +1,142 @@
+libsecureboot
+*************
+
+This library depends one way or another on verifying detached 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 certificate 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.::
+
+ gpg -a --detach-sign manifest
+
+should produce the expected signature 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.
+
+Later I added a FakeHash class to the signing server so we could
+generate signatures compatible with our previous RSA scheme and
+others.
+
+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.
+
+--------------------
diff --git a/lib/libsecureboot/brf.c b/lib/libsecureboot/brf.c
new file mode 100644
index 000000000000..fbd24f322bee
--- /dev/null
+++ b/lib/libsecureboot/brf.c
@@ -0,0 +1,401 @@
+// 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>
+#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/efi/efi_init.c b/lib/libsecureboot/efi/efi_init.c
new file mode 100644
index 000000000000..1078d9bc24ca
--- /dev/null
+++ b/lib/libsecureboot/efi/efi_init.c
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 2019 Stormshield.
+ * Copyright (c) 2019 Semihalf.
+ *
+ * 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 AUTHOR ``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 AUTHOR 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>
+#define NEED_BRSSL_H
+#include "../libsecureboot-priv.h"
+#include <brssl.h>
+
+void
+ve_efi_init(void)
+{
+ br_x509_certificate *xcs;
+ hash_data *digests;
+ size_t num;
+ int result;
+ static int once = 0;
+
+ if (once > 0)
+ return;
+
+ once = 1;
+
+ result = efi_secure_boot_enabled();
+ if (result <= 0)
+ return;
+
+ xcs = efi_get_trusted_certs(&num);
+ if (num > 0 && xcs != NULL) {
+ num = ve_trust_anchors_add(xcs, num);
+ free_certificates(xcs, num);
+ }
+ xcs = efi_get_forbidden_certs(&num);
+ if (num > 0 && xcs != NULL) {
+ num = ve_forbidden_anchors_add(xcs, num);
+ free_certificates(xcs, num);
+ }
+ digests = efi_get_forbidden_digests(&num);
+ if (num > 0 && digests != NULL) {
+ ve_forbidden_digest_add(digests, num);
+ /*
+ * Don't free the buffors for digests,
+ * since they are shallow copied.
+ */
+ xfree(digests);
+ }
+
+ return;
+}
diff --git a/lib/libsecureboot/efi/efi_variables.c b/lib/libsecureboot/efi/efi_variables.c
new file mode 100644
index 000000000000..3c36bc66f788
--- /dev/null
+++ b/lib/libsecureboot/efi/efi_variables.c
@@ -0,0 +1,274 @@
+/*-
+ * Copyright (c) 2019 Stormshield.
+ * Copyright (c) 2019 Semihalf.
+ *
+ * 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 AUTHOR ``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 AUTHOR 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>
+#include <stand.h>
+#include <string.h>
+
+#include <efi.h>
+#include <efilib.h>
+#include <Guid/ImageAuthentication.h>
+
+#define NEED_BRSSL_H
+#include "../libsecureboot-priv.h"
+#include <brssl.h>
+
+static EFI_GUID ImageSecurityDatabaseGUID = EFI_IMAGE_SECURITY_DATABASE_GUID;
+
+static EFI_GUID efiCertX509GUID = EFI_CERT_X509_GUID;
+static EFI_GUID efiCertX509Sha256GUID = EFI_CERT_X509_SHA256_GUID;
+static EFI_GUID efiCertX509Sha384GUID = EFI_CERT_X509_SHA384_GUID;
+static EFI_GUID efiCertX509Sha5122UID = EFI_CERT_X509_SHA512_GUID;
+
+/*
+ * Check if Secure Boot is enabled in firmware.
+ * We evaluate two variables - Secure Boot and Setup Mode.
+ * Secure Boot is enforced only if the first one equals 1 and the other 0.
+ */
+int
+efi_secure_boot_enabled(void)
+{
+ UINT8 SecureBoot;
+ UINT8 SetupMode;
+ size_t length;
+ EFI_STATUS status;
+
+ length = sizeof(SecureBoot);
+ status = efi_global_getenv("SecureBoot", &SecureBoot, &length);
+ if (status != EFI_SUCCESS) {
+ if (status == EFI_NOT_FOUND)
+ return (0);
+
+ printf("Failed to read \"SecureBoot\" variable\n");
+ return (-efi_status_to_errno(status));
+ }
+
+ length = sizeof(SetupMode);
+ status = efi_global_getenv("SetupMode", &SetupMode, &length);
+ if (status != EFI_SUCCESS)
+ SetupMode = 0;
+
+ printf(" SecureBoot: %d, SetupMode: %d\n", SecureBoot, SetupMode);
+
+ return (SecureBoot == 1 && SetupMode == 0);
+}
+
+/*
+ * Iterate through UEFI variable and extract X509 certificates from it.
+ * The EFI_* structures and related guids are defined in UEFI standard.
+ */
+static br_x509_certificate*
+efi_get_certs(const char *name, size_t *count)
+{
+ br_x509_certificate *certs;
+ UINT8 *database;
+ EFI_SIGNATURE_LIST *list;
+ EFI_SIGNATURE_DATA *entry;
+ size_t db_size;
+ ssize_t cert_count;
+ EFI_STATUS status;
+
+ database = NULL;
+ certs = NULL;
+ db_size = 0;
+ cert_count = 0;
+
+ /*
+ * Read variable length and allocate memory for it
+ */
+ status = efi_getenv(&ImageSecurityDatabaseGUID, name, database, &db_size);
+ if (status != EFI_BUFFER_TOO_SMALL)
+ return (NULL);
+
+ database = malloc(db_size);
+ if (database == NULL)
+ return (NULL);
+
+ status = efi_getenv(&ImageSecurityDatabaseGUID, name, database, &db_size);
+ if (status != EFI_SUCCESS)
+ goto fail;
+
+ for (list = (EFI_SIGNATURE_LIST*) database;
+ db_size >= list->SignatureListSize && db_size > 0;
+ db_size -= list->SignatureListSize,
+ list = (EFI_SIGNATURE_LIST*)
+ ((UINT8*)list + list->SignatureListSize)) {
+
+ /* We are only interested in entries containing X509 certs. */
+ if (memcmp(&efiCertX509GUID,
+ &list->SignatureType,
+ sizeof(EFI_GUID)) != 0) {
+ continue;
+ }
+
+ entry = (EFI_SIGNATURE_DATA*)
+ ((UINT8*)list +
+ sizeof(EFI_SIGNATURE_LIST) +
+ list->SignatureHeaderSize);
+
+ certs = realloc(certs,
+ (cert_count + 1) * sizeof(br_x509_certificate));
+ if (certs == NULL) {
+ cert_count = 0;
+ goto fail;
+ }
+
+ certs[cert_count].data_len = list->SignatureSize - sizeof(EFI_GUID);
+ certs[cert_count].data = malloc(certs[cert_count].data_len);
+ if (certs[cert_count].data == NULL)
+ goto fail;
+
+ memcpy(certs[cert_count].data,
+ entry->SignatureData,
+ certs[cert_count].data_len);
+
+ cert_count++;
+ }
+
+ *count = cert_count;
+
+ xfree(database);
+ return (certs);
+
+fail:
+ free_certificates(certs, cert_count);
+ xfree(database);
+ return (NULL);
+
+}
+
+/*
+ * Extract digests from UEFI "dbx" variable.
+ * UEFI standard specifies three types of digest - sha256, sha386, sha512.
+ */
+hash_data*
+efi_get_forbidden_digests(size_t *count)
+{
+ UINT8 *database;
+ hash_data *digests;
+ EFI_SIGNATURE_LIST *list;
+ EFI_SIGNATURE_DATA *entry;
+ size_t db_size, header_size, hash_size;
+ int digest_count, entry_count;
+ EFI_STATUS status;
+
+ db_size = 0;
+ digest_count = 0;
+ database = NULL;
+ digests = NULL;
+
+ status = efi_getenv(&ImageSecurityDatabaseGUID, "dbx", database, &db_size);
+ if (status != EFI_BUFFER_TOO_SMALL)
+ return (NULL);
+
+ database = malloc(db_size);
+ if (database == NULL)
+ return (NULL);
+
+ status = efi_getenv(&ImageSecurityDatabaseGUID, "dbx", database, &db_size);
+ if (status != EFI_SUCCESS)
+ goto fail;
+
+
+ for (list = (EFI_SIGNATURE_LIST*) database;
+ db_size >= list->SignatureListSize && db_size > 0;
+ db_size -= list->SignatureListSize,
+ list = (EFI_SIGNATURE_LIST*)
+ ((UINT8*)list + list->SignatureListSize)) {
+
+ /* We are only interested in entries that contain digests. */
+ if (memcmp(&efiCertX509Sha256GUID, &list->SignatureType,
+ sizeof(EFI_GUID)) == 0) {
+ hash_size = br_sha256_SIZE;
+ } else if (memcmp(&efiCertX509Sha384GUID, &list->SignatureType,
+ sizeof(EFI_GUID)) == 0) {
+ hash_size = br_sha384_SIZE;
+ } else if (memcmp(&efiCertX509Sha5122UID, &list->SignatureType,
+ sizeof(EFI_GUID)) == 0) {
+ hash_size = br_sha512_SIZE;
+ } else {
+ continue;
+ }
+
+ /*
+ * A single entry can have multiple digests
+ * of the same type for some reason.
+ */
+ header_size = sizeof(EFI_SIGNATURE_LIST) + list->SignatureHeaderSize;
+
+ /* Calculate the number of entries basing on structure size */
+ entry_count = list->SignatureListSize - header_size;
+ entry_count /= list->SignatureSize;
+ entry = (EFI_SIGNATURE_DATA*)((UINT8*)list + header_size);
+ while (entry_count-- > 0) {
+ digests = realloc(digests,
+ (digest_count + 1) * sizeof(hash_data));
+ if (digests == NULL) {
+ digest_count = 0;
+ goto fail;
+ }
+
+ digests[digest_count].data = malloc(hash_size);
+ if (digests[digest_count].data == NULL)
+ goto fail;
+
+ memcpy(digests[digest_count].data,
+ entry->SignatureData,
+ hash_size);
+ digests[digest_count].hash_size = hash_size;
+
+ entry = (EFI_SIGNATURE_DATA*)(entry + list->SignatureSize);
+ digest_count++;
+ }
+ }
+ xfree(database);
+ if (count != NULL)
+ *count = digest_count;
+
+ return (digests);
+
+fail:
+ while (digest_count--)
+ xfree(digests[digest_count].data);
+
+ xfree(database);
+ xfree(digests);
+ return (NULL);
+}
+
+/* Copy x509 certificates from db */
+br_x509_certificate*
+efi_get_trusted_certs(size_t *count)
+{
+ return (efi_get_certs("db", count));
+}
+
+/* Copy forbidden certificates from dbx */
+br_x509_certificate*
+efi_get_forbidden_certs(size_t *count)
+{
+ return (efi_get_certs("dbx", count));
+}
diff --git a/lib/libsecureboot/efi/include/Guid/GlobalVariable.h b/lib/libsecureboot/efi/include/Guid/GlobalVariable.h
new file mode 100644
index 000000000000..3bd0a815fcf6
--- /dev/null
+++ b/lib/libsecureboot/efi/include/Guid/GlobalVariable.h
@@ -0,0 +1,192 @@
+/** @file
+ GUID for EFI (NVRAM) Variables.
+ Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+ @par Revision Reference:
+ GUID defined in UEFI 2.1
+**/
+
+#ifndef __GLOBAL_VARIABLE_GUID_H__
+#define __GLOBAL_VARIABLE_GUID_H__
+
+#include <sys/cdefs.h>
+#ifndef EFI_GLOBAL_VARIABLE
+#define EFI_GLOBAL_VARIABLE \
+ { \
+ 0x8BE4DF61, 0x93CA, 0x11d2, {0xAA, 0x0D, 0x00, 0xE0, 0x98, 0x03, 0x2B, 0x8C } \
+ }
+#endif /* EFI_GLOBAL_VARIABLE */
+
+extern EFI_GUID gEfiGlobalVariableGuid;
+
+//
+// Follow UEFI 2.4 spec:
+// To prevent name collisions with possible future globally defined variables,
+// other internal firmware data variables that are not defined here must be
+// saved with a unique VendorGuid other than EFI_GLOBAL_VARIABLE or
+// any other GUID defined by the UEFI Specification. Implementations must
+// only permit the creation of variables with a UEFI Specification-defined
+// VendorGuid when these variables are documented in the UEFI Specification.
+//
+// Note: except the globally defined variables defined below, the spec also defines
+// L"Boot####" - A boot load option.
+// L"Driver####" - A driver load option.
+// L"SysPrep####" - A System Prep application load option.
+// L"Key####" - Describes hot key relationship with a Boot#### load option.
+// The attribute for them is NV+BS+RT, #### is a printed hex value, and no 0x or h
+// is included in the hex value. They can not be expressed as a #define like other globally
+// defined variables, it is because we can not list the Boot0000, Boot0001, etc one by one.
+//
+
+///
+/// The language codes that the firmware supports. This value is deprecated.
+/// Its attribute is BS+RT.
+///
+#define EFI_LANG_CODES_VARIABLE_NAME L"LangCodes"
+///
+/// The language code that the system is configured for. This value is deprecated.
+/// Its attribute is NV+BS+RT.
+///
+#define EFI_LANG_VARIABLE_NAME L"Lang"
+///
+/// The firmware's boot managers timeout, in seconds, before initiating the default boot selection.
+/// Its attribute is NV+BS+RT.
+///
+#define EFI_TIME_OUT_VARIABLE_NAME L"Timeout"
+///
+/// The language codes that the firmware supports.
+/// Its attribute is BS+RT.
+///
+#define EFI_PLATFORM_LANG_CODES_VARIABLE_NAME L"PlatformLangCodes"
+///
+/// The language code that the system is configured for.
+/// Its attribute is NV+BS+RT.
+///
+#define EFI_PLATFORM_LANG_VARIABLE_NAME L"PlatformLang"
+///
+/// The device path of the default input/output/error output console.
+/// Its attribute is NV+BS+RT.
+///
+#define EFI_CON_IN_VARIABLE_NAME L"ConIn"
+#define EFI_CON_OUT_VARIABLE_NAME L"ConOut"
+#define EFI_ERR_OUT_VARIABLE_NAME L"ErrOut"
+///
+/// The device path of all possible input/output/error output devices.
+/// Its attribute is BS+RT.
+///
+#define EFI_CON_IN_DEV_VARIABLE_NAME L"ConInDev"
+#define EFI_CON_OUT_DEV_VARIABLE_NAME L"ConOutDev"
+#define EFI_ERR_OUT_DEV_VARIABLE_NAME L"ErrOutDev"
+///
+/// The ordered boot option load list.
+/// Its attribute is NV+BS+RT.
+///
+#define EFI_BOOT_ORDER_VARIABLE_NAME L"BootOrder"
+///
+/// The boot option for the next boot only.
+/// Its attribute is NV+BS+RT.
+///
+#define EFI_BOOT_NEXT_VARIABLE_NAME L"BootNext"
+///
+/// The boot option that was selected for the current boot.
+/// Its attribute is BS+RT.
+///
+#define EFI_BOOT_CURRENT_VARIABLE_NAME L"BootCurrent"
+///
+/// The types of boot options supported by the boot manager. Should be treated as read-only.
+/// Its attribute is BS+RT.
+///
+#define EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME L"BootOptionSupport"
+///
+/// The ordered driver load option list.
+/// Its attribute is NV+BS+RT.
+///
+#define EFI_DRIVER_ORDER_VARIABLE_NAME L"DriverOrder"
+///
+/// The ordered System Prep Application load option list.
+/// Its attribute is NV+BS+RT.
+///
+#define EFI_SYS_PREP_ORDER_VARIABLE_NAME L"SysPrepOrder"
+///
+/// Identifies the level of hardware error record persistence
+/// support implemented by the platform. This variable is
+/// only modified by firmware and is read-only to the OS.
+/// Its attribute is NV+BS+RT.
+///
+#define EFI_HW_ERR_REC_SUPPORT_VARIABLE_NAME L"HwErrRecSupport"
+///
+/// Whether the system is operating in setup mode (1) or not (0).
+/// All other values are reserved. Should be treated as read-only.
+/// Its attribute is BS+RT.
+///
+#define EFI_SETUP_MODE_NAME L"SetupMode"
+///
+/// The Key Exchange Key Signature Database.
+/// Its attribute is NV+BS+RT+AT.
+///
+#define EFI_KEY_EXCHANGE_KEY_NAME L"KEK"
+///
+/// The public Platform Key.
+/// Its attribute is NV+BS+RT+AT.
+///
+#define EFI_PLATFORM_KEY_NAME L"PK"
+///
+/// Array of GUIDs representing the type of signatures supported
+/// by the platform firmware. Should be treated as read-only.
+/// Its attribute is BS+RT.
+///
+#define EFI_SIGNATURE_SUPPORT_NAME L"SignatureSupport"
+///
+/// Whether the platform firmware is operating in Secure boot mode (1) or not (0).
+/// All other values are reserved. Should be treated as read-only.
+/// Its attribute is BS+RT.
+///
+#define EFI_SECURE_BOOT_MODE_NAME L"SecureBoot"
+///
+/// The OEM's default Key Exchange Key Signature Database. Should be treated as read-only.
+/// Its attribute is BS+RT.
+///
+#define EFI_KEK_DEFAULT_VARIABLE_NAME L"KEKDefault"
+///
+/// The OEM's default public Platform Key. Should be treated as read-only.
+/// Its attribute is BS+RT.
+///
+#define EFI_PK_DEFAULT_VARIABLE_NAME L"PKDefault"
+///
+/// The OEM's default secure boot signature store. Should be treated as read-only.
+/// Its attribute is BS+RT.
+///
+#define EFI_DB_DEFAULT_VARIABLE_NAME L"dbDefault"
+///
+/// The OEM's default secure boot blacklist signature store. Should be treated as read-only.
+/// Its attribute is BS+RT.
+///
+#define EFI_DBX_DEFAULT_VARIABLE_NAME L"dbxDefault"
+///
+/// The OEM's default secure boot timestamp signature store. Should be treated as read-only.
+/// Its attribute is BS+RT.
+///
+#define EFI_DBT_DEFAULT_VARIABLE_NAME L"dbtDefault"
+///
+/// Allows the firmware to indicate supported features and actions to the OS.
+/// Its attribute is BS+RT.
+///
+#define EFI_OS_INDICATIONS_SUPPORT_VARIABLE_NAME L"OsIndicationsSupported"
+///
+/// Allows the OS to request the firmware to enable certain features and to take certain actions.
+/// Its attribute is NV+BS+RT.
+///
+#define EFI_OS_INDICATIONS_VARIABLE_NAME L"OsIndications"
+///
+/// Whether the system is configured to use only vendor provided
+/// keys or not. Should be treated as read-only.
+/// Its attribute is BS+RT.
+///
+#define EFI_VENDOR_KEYS_VARIABLE_NAME L"VendorKeys"
+
+#endif
diff --git a/lib/libsecureboot/efi/include/Guid/ImageAuthentication.h b/lib/libsecureboot/efi/include/Guid/ImageAuthentication.h
new file mode 100644
index 000000000000..b1fc1f54cce1
--- /dev/null
+++ b/lib/libsecureboot/efi/include/Guid/ImageAuthentication.h
@@ -0,0 +1,350 @@
+/** @file
+ Image signature database are defined for the signed image validation.
+ Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+ @par Revision Reference:
+ GUIDs defined in UEFI 2.5 spec.
+**/
+
+#ifndef __IMAGE_AUTHTICATION_H__
+#define __IMAGE_AUTHTICATION_H__
+
+#include <sys/cdefs.h>
+#include <Guid/GlobalVariable.h>
+#include <Protocol/Hash.h>
+
+#define EFI_IMAGE_SECURITY_DATABASE_GUID \
+ { \
+ 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc, 0xda, 0xd0, 0xe, 0x67, 0x65, 0x6f } \
+ }
+
+///
+/// Varialbe name with guid EFI_IMAGE_SECURITY_DATABASE_GUID
+/// for the authorized signature database.
+///
+#define EFI_IMAGE_SECURITY_DATABASE L"db"
+///
+/// Varialbe name with guid EFI_IMAGE_SECURITY_DATABASE_GUID
+/// for the forbidden signature database.
+///
+#define EFI_IMAGE_SECURITY_DATABASE1 L"dbx"
+///
+/// Variable name with guid EFI_IMAGE_SECURITY_DATABASE_GUID
+/// for the timestamp signature database.
+///
+#define EFI_IMAGE_SECURITY_DATABASE2 L"dbt"
+
+#define SECURE_BOOT_MODE_ENABLE 1
+#define SECURE_BOOT_MODE_DISABLE 0
+
+#define SETUP_MODE 1
+#define USER_MODE 0
+
+//***********************************************************************
+// Signature Database
+//***********************************************************************
+///
+/// The format of a signature database.
+///
+#pragma pack(1)
+
+typedef struct {
+ ///
+ /// An identifier which identifies the agent which added the signature to the list.
+ ///
+ EFI_GUID SignatureOwner;
+ ///
+ /// The format of the signature is defined by the SignatureType.
+ ///
+ UINT8 SignatureData[1];
+} EFI_SIGNATURE_DATA;
+
+typedef struct {
+ ///
+ /// Type of the signature. GUID signature types are defined in below.
+ ///
+ EFI_GUID SignatureType;
+ ///
+ /// Total size of the signature list, including this header.
+ ///
+ UINT32 SignatureListSize;
+ ///
+ /// Size of the signature header which precedes the array of signatures.
+ ///
+ UINT32 SignatureHeaderSize;
+ ///
+ /// Size of each signature.
+ ///
+ UINT32 SignatureSize;
+ ///
+ /// Header before the array of signatures. The format of this header is specified
+ /// by the SignatureType.
+ /// UINT8 SignatureHeader[SignatureHeaderSize];
+ ///
+ /// An array of signatures. Each signature is SignatureSize bytes in length.
+ /// EFI_SIGNATURE_DATA Signatures[][SignatureSize];
+ ///
+} EFI_SIGNATURE_LIST;
+
+typedef struct {
+ ///
+ /// The SHA256 hash of an X.509 certificate's To-Be-Signed contents.
+ ///
+ EFI_SHA256_HASH ToBeSignedHash;
+ ///
+ /// The time that the certificate shall be considered to be revoked.
+ ///
+ EFI_TIME TimeOfRevocation;
+} EFI_CERT_X509_SHA256;
+
+typedef struct {
+ ///
+ /// The SHA384 hash of an X.509 certificate's To-Be-Signed contents.
+ ///
+ EFI_SHA384_HASH ToBeSignedHash;
+ ///
+ /// The time that the certificate shall be considered to be revoked.
+ ///
+ EFI_TIME TimeOfRevocation;
+} EFI_CERT_X509_SHA384;
+
+typedef struct {
+ ///
+ /// The SHA512 hash of an X.509 certificate's To-Be-Signed contents.
+ ///
+ EFI_SHA512_HASH ToBeSignedHash;
+ ///
+ /// The time that the certificate shall be considered to be revoked.
+ ///
+ EFI_TIME TimeOfRevocation;
+} EFI_CERT_X509_SHA512;
+
+#pragma pack()
+
+///
+/// This identifies a signature containing a SHA-256 hash. The SignatureHeader size shall
+/// always be 0. The SignatureSize shall always be 16 (size of SignatureOwner component) +
+/// 32 bytes.
+///
+#define EFI_CERT_SHA256_GUID \
+ { \
+ 0xc1c41626, 0x504c, 0x4092, {0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28} \
+ }
+
+///
+/// This identifies a signature containing an RSA-2048 key. The key (only the modulus
+/// since the public key exponent is known to be 0x10001) shall be stored in big-endian
+/// order.
+/// The SignatureHeader size shall always be 0. The SignatureSize shall always be 16 (size
+/// of SignatureOwner component) + 256 bytes.
+///
+#define EFI_CERT_RSA2048_GUID \
+ { \
+ 0x3c5766e8, 0x269c, 0x4e34, {0xaa, 0x14, 0xed, 0x77, 0x6e, 0x85, 0xb3, 0xb6} \
+ }
+
+///
+/// This identifies a signature containing a RSA-2048 signature of a SHA-256 hash. The
+/// SignatureHeader size shall always be 0. The SignatureSize shall always be 16 (size of
+/// SignatureOwner component) + 256 bytes.
+///
+#define EFI_CERT_RSA2048_SHA256_GUID \
+ { \
+ 0xe2b36190, 0x879b, 0x4a3d, {0xad, 0x8d, 0xf2, 0xe7, 0xbb, 0xa3, 0x27, 0x84} \
+ }
+
+///
+/// This identifies a signature containing a SHA-1 hash. The SignatureSize shall always
+/// be 16 (size of SignatureOwner component) + 20 bytes.
+///
+#define EFI_CERT_SHA1_GUID \
+ { \
+ 0x826ca512, 0xcf10, 0x4ac9, {0xb1, 0x87, 0xbe, 0x1, 0x49, 0x66, 0x31, 0xbd} \
+ }
+
+///
+/// TThis identifies a signature containing a RSA-2048 signature of a SHA-1 hash. The
+/// SignatureHeader size shall always be 0. The SignatureSize shall always be 16 (size of
+/// SignatureOwner component) + 256 bytes.
+///
+#define EFI_CERT_RSA2048_SHA1_GUID \
+ { \
+ 0x67f8444f, 0x8743, 0x48f1, {0xa3, 0x28, 0x1e, 0xaa, 0xb8, 0x73, 0x60, 0x80} \
+ }
+
+///
+/// This identifies a signature based on an X.509 certificate. If the signature is an X.509
+/// certificate then verification of the signature of an image should validate the public
+/// key certificate in the image using certificate path verification, up to this X.509
+/// certificate as a trusted root. The SignatureHeader size shall always be 0. The
+/// SignatureSize may vary but shall always be 16 (size of the SignatureOwner component) +
+/// the size of the certificate itself.
+/// Note: This means that each certificate will normally be in a separate EFI_SIGNATURE_LIST.
+///
+#define EFI_CERT_X509_GUID \
+ { \
+ 0xa5c059a1, 0x94e4, 0x4aa7, {0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72} \
+ }
+
+///
+/// This identifies a signature containing a SHA-224 hash. The SignatureHeader size shall
+/// always be 0. The SignatureSize shall always be 16 (size of SignatureOwner component) +
+/// 28 bytes.
+///
+#define EFI_CERT_SHA224_GUID \
+ { \
+ 0xb6e5233, 0xa65c, 0x44c9, {0x94, 0x7, 0xd9, 0xab, 0x83, 0xbf, 0xc8, 0xbd} \
+ }
+
+///
+/// This identifies a signature containing a SHA-384 hash. The SignatureHeader size shall
+/// always be 0. The SignatureSize shall always be 16 (size of SignatureOwner component) +
+/// 48 bytes.
+///
+#define EFI_CERT_SHA384_GUID \
+ { \
+ 0xff3e5307, 0x9fd0, 0x48c9, {0x85, 0xf1, 0x8a, 0xd5, 0x6c, 0x70, 0x1e, 0x1} \
+ }
+
+///
+/// This identifies a signature containing a SHA-512 hash. The SignatureHeader size shall
+/// always be 0. The SignatureSize shall always be 16 (size of SignatureOwner component) +
+/// 64 bytes.
+///
+#define EFI_CERT_SHA512_GUID \
+ { \
+ 0x93e0fae, 0xa6c4, 0x4f50, {0x9f, 0x1b, 0xd4, 0x1e, 0x2b, 0x89, 0xc1, 0x9a} \
+ }
+
+///
+/// This identifies a signature containing the SHA256 hash of an X.509 certificate's
+/// To-Be-Signed contents, and a time of revocation. The SignatureHeader size shall
+/// always be 0. The SignatureSize shall always be 16 (size of the SignatureOwner component)
+/// + 48 bytes for an EFI_CERT_X509_SHA256 structure. If the TimeOfRevocation is non-zero,
+/// the certificate should be considered to be revoked from that time and onwards, and
+/// otherwise the certificate shall be considered to always be revoked.
+///
+#define EFI_CERT_X509_SHA256_GUID \
+ { \
+ 0x3bd2a492, 0x96c0, 0x4079, {0xb4, 0x20, 0xfc, 0xf9, 0x8e, 0xf1, 0x03, 0xed } \
+ }
+
+///
+/// This identifies a signature containing the SHA384 hash of an X.509 certificate's
+/// To-Be-Signed contents, and a time of revocation. The SignatureHeader size shall
+/// always be 0. The SignatureSize shall always be 16 (size of the SignatureOwner component)
+/// + 64 bytes for an EFI_CERT_X509_SHA384 structure. If the TimeOfRevocation is non-zero,
+/// the certificate should be considered to be revoked from that time and onwards, and
+/// otherwise the certificate shall be considered to always be revoked.
+///
+#define EFI_CERT_X509_SHA384_GUID \
+ { \
+ 0x7076876e, 0x80c2, 0x4ee6, {0xaa, 0xd2, 0x28, 0xb3, 0x49, 0xa6, 0x86, 0x5b } \
+ }
+
+///
+/// This identifies a signature containing the SHA512 hash of an X.509 certificate's
+/// To-Be-Signed contents, and a time of revocation. The SignatureHeader size shall
+/// always be 0. The SignatureSize shall always be 16 (size of the SignatureOwner component)
+/// + 80 bytes for an EFI_CERT_X509_SHA512 structure. If the TimeOfRevocation is non-zero,
+/// the certificate should be considered to be revoked from that time and onwards, and
+/// otherwise the certificate shall be considered to always be revoked.
+///
+#define EFI_CERT_X509_SHA512_GUID \
+ { \
+ 0x446dbf63, 0x2502, 0x4cda, {0xbc, 0xfa, 0x24, 0x65, 0xd2, 0xb0, 0xfe, 0x9d } \
+ }
+
+///
+/// This identifies a signature containing a DER-encoded PKCS #7 version 1.5 [RFC2315]
+/// SignedData value.
+///
+#define EFI_CERT_TYPE_PKCS7_GUID \
+ { \
+ 0x4aafd29d, 0x68df, 0x49ee, {0x8a, 0xa9, 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7} \
+ }
+
+//***********************************************************************
+// Image Execution Information Table Definition
+//***********************************************************************
+typedef UINT32 EFI_IMAGE_EXECUTION_ACTION;
+
+#define EFI_IMAGE_EXECUTION_AUTHENTICATION 0x00000007
+#define EFI_IMAGE_EXECUTION_AUTH_UNTESTED 0x00000000
+#define EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED 0x00000001
+#define EFI_IMAGE_EXECUTION_AUTH_SIG_PASSED 0x00000002
+#define EFI_IMAGE_EXECUTION_AUTH_SIG_NOT_FOUND 0x00000003
+#define EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND 0x00000004
+#define EFI_IMAGE_EXECUTION_POLICY_FAILED 0x00000005
+#define EFI_IMAGE_EXECUTION_INITIALIZED 0x00000008
+
+//
+// EFI_IMAGE_EXECUTION_INFO is added to EFI System Configuration Table
+// and assigned the GUID EFI_IMAGE_SECURITY_DATABASE_GUID.
+//
+typedef struct {
+ ///
+ /// Describes the action taken by the firmware regarding this image.
+ ///
+ EFI_IMAGE_EXECUTION_ACTION Action;
+ ///
+ /// Size of all of the entire structure.
+ ///
+ UINT32 InfoSize;
+ ///
+ /// If this image was a UEFI device driver (for option ROM, for example) this is the
+ /// null-terminated, user-friendly name for the device. If the image was for an application,
+ /// then this is the name of the application. If this cannot be determined, then a simple
+ /// NULL character should be put in this position.
+ /// CHAR16 Name[];
+ ///
+
+ ///
+ /// For device drivers, this is the device path of the device for which this device driver
+ /// was intended. In some cases, the driver itself may be stored as part of the system
+ /// firmware, but this field should record the device's path, not the firmware path. For
+ /// applications, this is the device path of the application. If this cannot be determined,
+ /// a simple end-of-path device node should be put in this position.
+ /// EFI_DEVICE_PATH_PROTOCOL DevicePath;
+ ///
+
+ ///
+ /// Zero or more image signatures. If the image contained no signatures,
+ /// then this field is empty.
+ /// EFI_SIGNATURE_LIST Signature;
+ ///
+} EFI_IMAGE_EXECUTION_INFO;
+
+
+typedef struct {
+ ///
+ /// Number of EFI_IMAGE_EXECUTION_INFO structures.
+ ///
+ UINTN NumberOfImages;
+ ///
+ /// Number of image instances of EFI_IMAGE_EXECUTION_INFO structures.
+ ///
+ // EFI_IMAGE_EXECUTION_INFO InformationInfo[]
+} EFI_IMAGE_EXECUTION_INFO_TABLE;
+
+extern EFI_GUID gEfiImageSecurityDatabaseGuid;
+extern EFI_GUID gEfiCertSha256Guid;
+extern EFI_GUID gEfiCertRsa2048Guid;
+extern EFI_GUID gEfiCertRsa2048Sha256Guid;
+extern EFI_GUID gEfiCertSha1Guid;
+extern EFI_GUID gEfiCertRsa2048Sha1Guid;
+extern EFI_GUID gEfiCertX509Guid;
+extern EFI_GUID gEfiCertSha224Guid;
+extern EFI_GUID gEfiCertSha384Guid;
+extern EFI_GUID gEfiCertSha512Guid;
+extern EFI_GUID gEfiCertX509Sha256Guid;
+extern EFI_GUID gEfiCertX509Sha384Guid;
+extern EFI_GUID gEfiCertX509Sha512Guid;
+extern EFI_GUID gEfiCertPkcs7Guid;
+
+#endif
diff --git a/lib/libsecureboot/efi/include/Protocol/Hash.h b/lib/libsecureboot/efi/include/Protocol/Hash.h
new file mode 100644
index 000000000000..094bf20964cc
--- /dev/null
+++ b/lib/libsecureboot/efi/include/Protocol/Hash.h
@@ -0,0 +1,169 @@
+/** @file
+ EFI_HASH_SERVICE_BINDING_PROTOCOL as defined in UEFI 2.0.
+ EFI_HASH_PROTOCOL as defined in UEFI 2.0.
+ The EFI Hash Service Binding Protocol is used to locate hashing services support
+ provided by a driver and to create and destroy instances of the EFI Hash Protocol
+ so that a multiple drivers can use the underlying hashing services.
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed and made available under
+the terms and conditions of the BSD License that accompanies this distribution.
+The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php.
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+**/
+
+#ifndef __EFI_HASH_PROTOCOL_H__
+#define __EFI_HASH_PROTOCOL_H__
+
+#include <sys/cdefs.h>
+#define CONST const
+
+#define EFI_HASH_SERVICE_BINDING_PROTOCOL_GUID \
+ { \
+ 0x42881c98, 0xa4f3, 0x44b0, {0xa3, 0x9d, 0xdf, 0xa1, 0x86, 0x67, 0xd8, 0xcd } \
+ }
+
+#define EFI_HASH_PROTOCOL_GUID \
+ { \
+ 0xc5184932, 0xdba5, 0x46db, {0xa5, 0xba, 0xcc, 0x0b, 0xda, 0x9c, 0x14, 0x35 } \
+ }
+
+#define EFI_HASH_ALGORITHM_SHA1_GUID \
+ { \
+ 0x2ae9d80f, 0x3fb2, 0x4095, {0xb7, 0xb1, 0xe9, 0x31, 0x57, 0xb9, 0x46, 0xb6 } \
+ }
+
+#define EFI_HASH_ALGORITHM_SHA224_GUID \
+ { \
+ 0x8df01a06, 0x9bd5, 0x4bf7, {0xb0, 0x21, 0xdb, 0x4f, 0xd9, 0xcc, 0xf4, 0x5b } \
+ }
+
+#define EFI_HASH_ALGORITHM_SHA256_GUID \
+ { \
+ 0x51aa59de, 0xfdf2, 0x4ea3, {0xbc, 0x63, 0x87, 0x5f, 0xb7, 0x84, 0x2e, 0xe9 } \
+ }
+
+#define EFI_HASH_ALGORITHM_SHA384_GUID \
+ { \
+ 0xefa96432, 0xde33, 0x4dd2, {0xae, 0xe6, 0x32, 0x8c, 0x33, 0xdf, 0x77, 0x7a } \
+ }
+
+#define EFI_HASH_ALGORITHM_SHA512_GUID \
+ { \
+ 0xcaa4381e, 0x750c, 0x4770, {0xb8, 0x70, 0x7a, 0x23, 0xb4, 0xe4, 0x21, 0x30 } \
+ }
+
+#define EFI_HASH_ALGORTIHM_MD5_GUID \
+ { \
+ 0xaf7c79c, 0x65b5, 0x4319, {0xb0, 0xae, 0x44, 0xec, 0x48, 0x4e, 0x4a, 0xd7 } \
+ }
+
+#define EFI_HASH_ALGORITHM_SHA1_NOPAD_GUID \
+ { \
+ 0x24c5dc2f, 0x53e2, 0x40ca, {0x9e, 0xd6, 0xa5, 0xd9, 0xa4, 0x9f, 0x46, 0x3b } \
+ }
+
+#define EFI_HASH_ALGORITHM_SHA256_NOPAD_GUID \
+ { \
+ 0x8628752a, 0x6cb7, 0x4814, {0x96, 0xfc, 0x24, 0xa8, 0x15, 0xac, 0x22, 0x26 } \
+ }
+
+//
+// Note: Use of the following algorithms with EFI_HASH_PROTOCOL is deprecated.
+// EFI_HASH_ALGORITHM_SHA1_GUID
+// EFI_HASH_ALGORITHM_SHA224_GUID
+// EFI_HASH_ALGORITHM_SHA256_GUID
+// EFI_HASH_ALGORITHM_SHA384_GUID
+// EFI_HASH_ALGORITHM_SHA512_GUID
+// EFI_HASH_ALGORTIHM_MD5_GUID
+//
+
+typedef struct _EFI_HASH_PROTOCOL EFI_HASH_PROTOCOL;
+
+typedef UINT8 EFI_MD5_HASH[16];
+typedef UINT8 EFI_SHA1_HASH[20];
+typedef UINT8 EFI_SHA224_HASH[28];
+typedef UINT8 EFI_SHA256_HASH[32];
+typedef UINT8 EFI_SHA384_HASH[48];
+typedef UINT8 EFI_SHA512_HASH[64];
+
+typedef union {
+ EFI_MD5_HASH *Md5Hash;
+ EFI_SHA1_HASH *Sha1Hash;
+ EFI_SHA224_HASH *Sha224Hash;
+ EFI_SHA256_HASH *Sha256Hash;
+ EFI_SHA384_HASH *Sha384Hash;
+ EFI_SHA512_HASH *Sha512Hash;
+} EFI_HASH_OUTPUT;
+
+/**
+ Returns the size of the hash which results from a specific algorithm.
+ @param[in] This Points to this instance of EFI_HASH_PROTOCOL.
+ @param[in] HashAlgorithm Points to the EFI_GUID which identifies the algorithm to use.
+ @param[out] HashSize Holds the returned size of the algorithm's hash.
+ @retval EFI_SUCCESS Hash size returned successfully.
+ @retval EFI_INVALID_PARAMETER HashSize is NULL or HashAlgorithm is NULL.
+ @retval EFI_UNSUPPORTED The algorithm specified by HashAlgorithm is not supported
+ by this driver.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_HASH_GET_HASH_SIZE)(
+ IN CONST EFI_HASH_PROTOCOL *This,
+ IN CONST EFI_GUID *HashAlgorithm,
+ OUT UINTN *HashSize
+ );
+
+/**
+ Creates a hash for the specified message text.
+ @param[in] This Points to this instance of EFI_HASH_PROTOCOL.
+ @param[in] HashAlgorithm Points to the EFI_GUID which identifies the algorithm to use.
+ @param[in] Extend Specifies whether to create a new hash (FALSE) or extend the specified
+ existing hash (TRUE).
+ @param[in] Message Points to the start of the message.
+ @param[in] MessageSize The size of Message, in bytes.
+ @param[in,out] Hash On input, if Extend is TRUE, then this parameter holds a pointer
+ to a pointer to an array containing the hash to extend. If Extend
+ is FALSE, then this parameter holds a pointer to a pointer to a
+ caller-allocated array that will receive the result of the hash
+ computation. On output (regardless of the value of Extend), the
+ array will contain the result of the hash computation.
+ @retval EFI_SUCCESS Hash returned successfully.
+ @retval EFI_INVALID_PARAMETER Message or Hash, HashAlgorithm is NULL or MessageSize is 0.
+ MessageSize is not an integer multiple of block size.
+ @retval EFI_UNSUPPORTED The algorithm specified by HashAlgorithm is not supported by this
+ driver. Or, Extend is TRUE, and the algorithm doesn't support extending the hash.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_HASH_HASH)(
+ IN CONST EFI_HASH_PROTOCOL *This,
+ IN CONST EFI_GUID *HashAlgorithm,
+ IN BOOLEAN Extend,
+ IN CONST UINT8 *Message,
+ IN UINT64 MessageSize,
+ IN OUT EFI_HASH_OUTPUT *Hash
+ );
+
+///
+/// This protocol allows creating a hash of an arbitrary message digest
+/// using one or more hash algorithms.
+///
+struct _EFI_HASH_PROTOCOL {
+ EFI_HASH_GET_HASH_SIZE GetHashSize;
+ EFI_HASH_HASH Hash;
+};
+
+extern EFI_GUID gEfiHashServiceBindingProtocolGuid;
+extern EFI_GUID gEfiHashProtocolGuid;
+extern EFI_GUID gEfiHashAlgorithmSha1Guid;
+extern EFI_GUID gEfiHashAlgorithmSha224Guid;
+extern EFI_GUID gEfiHashAlgorithmSha256Guid;
+extern EFI_GUID gEfiHashAlgorithmSha384Guid;
+extern EFI_GUID gEfiHashAlgorithmSha512Guid;
+extern EFI_GUID gEfiHashAlgorithmMD5Guid;
+extern EFI_GUID gEfiHashAlgorithmSha1NoPadGuid;
+extern EFI_GUID gEfiHashAlgorithmSha256NoPadGuid;
+
+#endif
diff --git a/lib/libsecureboot/h/libsecureboot.h b/lib/libsecureboot/h/libsecureboot.h
new file mode 100644
index 000000000000..d32df9594332
--- /dev/null
+++ b/lib/libsecureboot/h/libsecureboot.h
@@ -0,0 +1,98 @@
+/*-
+ * 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.
+ */
+/*
+ */
+#ifndef _LIBSECUREBOOT_H_
+#define _LIBSECUREBOOT_H_
+
+#include <sys/param.h>
+#ifdef _STANDALONE
+#define _DEBUG_LEVEL_VAR DebugVe
+#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>
+
+unsigned char * read_fd(int, size_t);
+#ifndef NEED_BRSSL_H
+unsigned char * read_file(const char *, size_t *);
+#endif
+
+extern int DebugVe;
+extern int VerifyFlags;
+
+#ifndef DEBUG_PRINTF
+#define DEBUG_PRINTF(n, x) if (DebugVe >= n) printf x
+#endif
+
+int ve_trust_init(void);
+size_t ve_trust_anchors_add_buf(unsigned char *, size_t);
+size_t ve_trust_anchors_revoke(unsigned char *, size_t);
+int ve_trust_add(const char *);
+void ve_debug_set(int);
+void ve_enforce_validity_set(int);
+void ve_anchor_verbose_set(int);
+int ve_anchor_verbose_get(void);
+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 *);
+
+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(const char *, unsigned char *, size_t);
+ssize_t ve_pcr_get(unsigned char *, size_t);
+int ve_pcr_updating_get(void);
+void ve_pcr_updating_set(int);
+char * ve_pcr_hashed_get(int);
+
+/* flags for verify_{asc,sig,signed} */
+#define VEF_VERBOSE 1
+
+#define VE_FINGERPRINT_OK 1
+#define VE_FINGERPRINT_IGNORE 2
+/* 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..f918ed6d0e38
--- /dev/null
+++ b/lib/libsecureboot/h/verify_file.h
@@ -0,0 +1,67 @@
+/*-
+ * 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.
+ */
+#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_NOT_CHECKED -42
+#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 */
+
+/* suitable buf size for hash_string */
+#ifndef SHA_DIGEST_LENGTH
+# define SHA_DIGEST_LENGTH 20
+#endif
+
+struct stat;
+
+int verify_prep(int, const char *, off_t, struct stat *, const char *);
+void ve_debug_set(int);
+char *ve_error_get(void);
+void ve_efi_init(void);
+void ve_status_set(int, int);
+int ve_status_get(int);
+int load_manifest(const char *, const char *, const char *, struct stat *);
+int pass_manifest(const char *, const char *);
+int pass_manifest_export_envs(void);
+void verify_report(const char *, int, int, struct stat *);
+int verify_file(int, const char *, off_t, int, const char *);
+void verify_pcr_export(void);
+int hash_string(char *s, size_t n, char *buf, size_t bufsz);
+int is_verified(struct stat *);
+void add_verify_status(struct stat *, int);
+
+struct vectx;
+struct vectx* vectx_open(int, const char *, off_t, struct stat *, int *, const char *);
+ssize_t vectx_read(struct vectx *, void *, size_t);
+off_t vectx_lseek(struct vectx *, off_t, int);
+int vectx_close(struct vectx *, int, const char *);
+
+#endif /* _VERIFY_FILE_H_ */
diff --git a/lib/libsecureboot/libsecureboot-priv.h b/lib/libsecureboot/libsecureboot-priv.h
new file mode 100644
index 000000000000..039b03c70ca2
--- /dev/null
+++ b/lib/libsecureboot/libsecureboot-priv.h
@@ -0,0 +1,70 @@
+/*-
+ * 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.
+ */
+/*
+ */
+#ifndef _LIBSECUREBOOT_PRIV_H_
+#define _LIBSECUREBOOT_PRIV_H_
+
+/* public api */
+#include "libsecureboot.h"
+
+struct stat;
+
+typedef struct {
+ unsigned char *data;
+ size_t hash_size;
+} hash_data;
+
+int ve_check_hash(br_hash_compat_context *, const br_hash_class *,
+ const char *, const char *, size_t);
+
+size_t ve_trust_anchors_add(br_x509_certificate *, size_t);
+size_t ve_forbidden_anchors_add(br_x509_certificate *, size_t);
+void ve_forbidden_digest_add(hash_data *digest, size_t);
+char *fingerprint_info_lookup(int, const char *);
+
+br_x509_certificate * parse_certificates(unsigned char *, size_t, size_t *);
+int certificate_to_trust_anchor_inner(br_x509_trust_anchor *,
+ br_x509_certificate *);
+
+int verify_rsa_digest(br_rsa_public_key *pkey,
+ const unsigned char *hash_oid,
+ unsigned char *mdata, size_t mlen,
+ unsigned char *sdata, size_t slen);
+
+int is_verified(struct stat *stp);
+void add_verify_status(struct stat *stp, int status);
+
+int openpgp_trust_init(void);
+int openpgp_trust_add_buf(unsigned char *, size_t);
+int openpgp_trust_revoke(const char *);
+int openpgp_self_tests(void);
+
+int efi_secure_boot_enabled(void);
+br_x509_certificate* efi_get_trusted_certs(size_t *count);
+br_x509_certificate* efi_get_forbidden_certs(size_t *count);
+hash_data* efi_get_forbidden_digests(size_t *count);
+
+#endif /* _LIBSECUREBOOT_PRIV_H_ */
diff --git a/lib/libsecureboot/local.trust.mk b/lib/libsecureboot/local.trust.mk
new file mode 100644
index 000000000000..f2da3d3ac452
--- /dev/null
+++ b/lib/libsecureboot/local.trust.mk
@@ -0,0 +1,108 @@
+
+# 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
+
+# allow site control
+.-include "site.trust.mk"
+
+#VE_DEBUG_LEVEL?=3
+#VE_VERBOSE_DEFAULT?=2
+
+VE_HASH_LIST?= \
+ SHA256 \
+ SHA384 \
+
+VE_SELF_TESTS?= yes
+
+# client for the signing server above
+SIGNER?= /opt/sigs/sign.py
+
+.if exists(${SIGNER})
+OPENPGP_SIGNER?= ${SIGNER:H}/openpgp-sign.py
+OPENPGP_SIGN_FLAGS= -a
+OPENPGP_SIGN_HOST?= localhost
+SIGN_HOST ?= localhost
+
+# A list of name/ext/url tuples.
+# name should be one of ECDSA, OPENPGP or RSA, they can be repeated
+# Order of ext list implies runtime preference so do not sort!
+VE_SIGN_URL_LIST?= \
+ ECDSA/esig/${SIGN_HOST}:${133%y:L:localtime} \
+ RSA/rsig/${SIGN_HOST}:${163%y:L:localtime} \
+ OPENPGP/asc/${OPENPGP_SIGN_HOST}:1234 \
+
+.for sig ext url in ${VE_SIGN_URL_LIST:@x@${x:H:H} ${x:H:T} ${x:T}@}
+SIGN_${sig}:= ${PYTHON} ${${sig}_SIGNER:U${SIGNER}} -u ${url} ${${sig}_SIGN_FLAGS:U-h sha256}
+
+VE_SIGNATURE_LIST+= ${sig}
+VE_SIGNATURE_EXT_LIST+= ${ext}
+
+_SIGN_${sig}_USE: .USE
+ ${SIGN_${sig}} ${.ALLSRC}
+
+_TA_${sig}_USE: .USE
+ ${SIGN_${sig}} -C ${.TARGET}
+
+.if ${sig} == "OPENPGP"
+ta_${sig:tl}.${ext}: _TA_${sig}_USE
+ta_${ext}.h: ta_${sig:tl}.${ext}
+.else
+${ext:S/sig/certs/}.pem: _TA_${sig}_USE
+# the last cert in the chain is the one we want
+ta_${ext}.pem: ${ext:S/sig/certs/}.pem _LAST_PEM_USE
+ta.h: ta_${ext}.pem
+.if ${VE_SELF_TESTS} != "no"
+# we use the 2nd last cert to test verification
+vc_${ext}.pem: ${ext:S/sig/certs/}.pem _2ndLAST_PEM_USE
+ta.h: vc_${ext}.pem
+.endif
+.endif
+.endfor
+
+# cleanup duplicates
+VE_SIGNATURE_LIST:= ${VE_SIGNATURE_LIST:O:u}
+
+.if target(ta_asc.h)
+XCFLAGS.opgp_key+= -DHAVE_TA_ASC_H
+
+.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_asc.h: vc_openpgp.asc
+.endif
+.endif
+
+.else
+VE_SIGNATURE_LIST?= RSA
+
+# you need to provide t*.pem or t*.asc files for each trust anchor
+# below assumes they are named ta_${ext}.pem eg ta_esig.pem for ECDSA
+.if empty(TRUST_ANCHORS)
+TRUST_ANCHORS!= cd ${.CURDIR} && 'ls' -1 *.pem t*.asc 2> /dev/null || echo
+.endif
+.if empty(TRUST_ANCHORS) && ${MK_LOADER_EFI_SECUREBOOT} != "yes"
+.error Need TRUST_ANCHORS see ${.PARSEDIR}/README.rst
+.endif
+
+.if ${TRUST_ANCHORS:T:Mt*.pem} != ""
+ta.h: ${TRUST_ANCHORS:M*.pem}
+VE_SIGNATURE_EXT_LIST?= ${TRUST_ANCHORS:T:Mt*.pem:R:S/ta_//}
+.if ${VE_SIGNATURE_EXT_LIST:Mesig} != ""
+VE_SIGNATURE_LIST+= ECDSA
+.endif
+.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..3a216d205fe3
--- /dev/null
+++ b/lib/libsecureboot/openpgp/Makefile.inc
@@ -0,0 +1,49 @@
+# 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_LIST ?= ${.ALLSRC:Mt*.asc}
+VC_ASC_LIST ?= ${.ALLSRC:Mv*.asc}
+
+ta_asc.h:
+.if ${VE_SIGNATURE_LIST:MOPENPGP} != ""
+ @( echo '/* Autogenerated - DO NOT EDIT!!! */'; echo; \
+ echo "#define HAVE_TA_ASC 1"; \
+ set -- ${TA_ASC_LIST:@f@$f ${f:T:R}@}; \
+ while test $$# -ge 2; do \
+ file2c -sx "static const char $$2[] = {" ', 0x00 };' < $$1; \
+ shift 2; \
+ done; \
+ echo 'static const char *ta_ASC[] = { ${TA_ASC_LIST:T:R:ts,}, NULL };'; \
+ echo; ) > ${.TARGET}
+.if ${VE_SELF_TESTS} != "no"
+ @( echo "#define HAVE_VC_ASC 1"; \
+ set -- ${VC_ASC_LIST:@f@$f ${f:T:R}@}; \
+ while test $$# -ge 2; do \
+ file2c -sx "static const char $$2[] = {" ', 0x00 };' < $$1; \
+ shift 2; \
+ done; \
+ echo 'static const char *vc_ASC[] = { ${VC_ASC_LIST: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..452722ff1a10
--- /dev/null
+++ b/lib/libsecureboot/openpgp/dearmor.c
@@ -0,0 +1,142 @@
+/*-
+ * 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>
+#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..c59b5722c29e
--- /dev/null
+++ b/lib/libsecureboot/openpgp/decode.c
@@ -0,0 +1,302 @@
+/*-
+ * 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>
+#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..1e7e2ea1f915
--- /dev/null
+++ b/lib/libsecureboot/openpgp/decode.h
@@ -0,0 +1,56 @@
+/*-
+ * 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.
+ */
+/*
+ */
+
+#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..568d334ec117
--- /dev/null
+++ b/lib/libsecureboot/openpgp/opgp_key.c
@@ -0,0 +1,413 @@
+/*-
+ * 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>
+#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)) {
+ /* Note: we do *not* free data */
+ 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);
+ }
+ }
+ }
+ 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 && openpgp_trust_get(key->id) == NULL) {
+ if (ve_anchor_verbose_get())
+ printf("openpgp_trust_add(%s)\n", key->id);
+ LIST_INSERT_HEAD(&trust_list, key, entries);
+ }
+}
+
+/**
+ * @brief add trust anchor from buf
+ */
+int
+openpgp_trust_add_buf(unsigned char *buf, size_t nbytes)
+{
+ OpenPGP_key *key;
+
+ if ((key = load_key_buf(buf, nbytes))) {
+ openpgp_trust_add(key);
+ }
+ return (key != NULL);
+}
+
+
+/**
+ * @brief if keyID is in our list clobber it
+ *
+ * @return true if keyID removed
+ */
+int
+openpgp_trust_revoke(const char *keyID)
+{
+ OpenPGP_key *key, *tkey;
+
+ openpgp_trust_add(NULL); /* initialize if needed */
+
+ LIST_FOREACH(key, &trust_list, entries) {
+ if (strcmp(key->id, keyID) == 0) {
+ tkey = key;
+ LIST_REMOVE(tkey, entries);
+ printf("openpgp_trust_revoke(%s)\n", key->id);
+ memset(key, 0, sizeof(OpenPGP_key));
+ free(key);
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/**
+ * @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);
+}
+
+#ifdef HAVE_TA_ASC_H
+#include <ta_asc.h>
+#endif
+
+#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)
+{
+ OpenPGP_key *key;
+
+ key = openpgp_trust_get(keyID);
+#ifndef _STANDALONE
+ if (!key)
+ key = load_trusted_key_id(keyID);
+#endif
+ DEBUG_PRINTF(2, ("load_key_id(%s): %s\n", keyID, key ? "found" : "nope"));
+ return (key);
+}
+
+/**
+ * @brief initialize our internal trust store if any
+ */
+int
+openpgp_trust_init(void)
+{
+ static int once = -1;
+#ifdef HAVE_TA_ASC
+ OpenPGP_key *key;
+ const char **tp;
+ char *cp;
+ size_t n;
+#endif
+
+ if (once < 0) {
+ once = 0;
+#ifdef HAVE_TA_ASC
+ for (tp = ta_ASC; *tp; tp++) {
+ if ((cp = strdup(*tp))) {
+ n = strlen(cp);
+ key = load_key_buf((unsigned char *)cp, n);
+ free(cp);
+ if (key) {
+ openpgp_trust_add(key);
+ once++;
+ }
+ }
+ }
+#endif
+ }
+ return (once);
+}
+
+/**
+ * @brief test that we can verify a signature
+ *
+ * Unlike X.509 certificates, we only support RSA keys
+ * so we stop after first successful signature verification
+ * (which should also be the first attempt ;-)
+ */
+int
+openpgp_self_tests(void)
+{
+ static int rc = -1; /* remember result */
+#ifdef HAVE_VC_ASC
+ const char **vp, **tp;
+ char *fdata, *sdata = NULL;
+ size_t fbytes, sbytes;
+
+ if (openpgp_trust_init() > 0) {
+ for (tp = ta_ASC, vp = vc_ASC; *tp && *vp && rc; tp++, vp++) {
+ if ((fdata = strdup(*tp)) &&
+ (sdata = strdup(*vp))) {
+ fbytes = strlen(fdata);
+ sbytes = strlen(sdata);
+ rc = openpgp_verify("ta_ASC",
+ (unsigned char *)fdata, fbytes,
+ (unsigned char *)sdata, sbytes, 0);
+ printf("Testing verify OpenPGP signature:\t\t%s\n",
+ rc ? "Failed" : "Passed");
+ }
+ free(fdata);
+ free(sdata);
+ }
+ }
+#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..8846296d7122
--- /dev/null
+++ b/lib/libsecureboot/openpgp/opgp_sig.c
@@ -0,0 +1,492 @@
+/*-
+ * 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
+ *
+ * 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>
+#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)
+{
+ openpgp_trust_init();
+}
+
+#else
+
+#include <openssl/err.h>
+
+/**
+ * @brief initialize 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);
+ DEBUG_PRINTF(2, ("rc=%d keyID=%s\n", rc, sig->key_id ? sig->key_id : "?"));
+ 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;
+ case 9: /* sha384 */
+ md = &br_sha384_vtable;
+ mlen = br_sha384_SIZE;
+ hash_oid = BR_HASH_OID_SHA384;
+ break;
+ case 10: /* sha512 */
+ md = &br_sha512_vtable;
+ mlen = br_sha512_SIZE;
+ hash_oid = BR_HASH_OID_SHA512;
+ break;
+ default:
+ warnx("unsupported hash algorithm: %s", hname);
+ rc = -1;
+ 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 & VEF_VERBOSE))
+ 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, VerifyFlags));
+}
+#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;
+
+ fdata = NULL;
+ if ((sdata = read_file(sigfile, &sbytes))) {
+ n = strlcpy(pbuf, sigfile, sizeof(pbuf));
+ if (n < 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;
+ }
+ }
+ }
+ }
+ free(sdata);
+ return (fdata);
+}
diff --git a/lib/libsecureboot/openpgp/packet.h b/lib/libsecureboot/openpgp/packet.h
new file mode 100644
index 000000000000..58f797cd1651
--- /dev/null
+++ b/lib/libsecureboot/openpgp/packet.h
@@ -0,0 +1,81 @@
+/*-
+ * 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/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/pass_manifest.c b/lib/libsecureboot/pass_manifest.c
new file mode 100644
index 000000000000..e3ef1fa96aae
--- /dev/null
+++ b/lib/libsecureboot/pass_manifest.c
@@ -0,0 +1,148 @@
+/*-
+ * Copyright (c) 2019 Stormshield.
+ * Copyright (c) 2019 Semihalf.
+ *
+ * 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 AUTHOR ``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 AUTHOR 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>
+#include <sys/stat.h>
+
+#include "libsecureboot-priv.h"
+#include <verify_file.h>
+
+/*
+ * Values to pass to kernel by envs.
+ */
+static char manifest_path[MAXPATHLEN];
+static char manifest_prefix[MAXPATHLEN];
+static char manifest_hash[2 * br_sha256_SIZE + 2];
+static int manifest_present = 0;
+
+/*
+ * Verify and pass manifest path and digest to kernel through envs.
+ * The paths in manifest can be either absolute,
+ * or "prefix", if exists will be added to the ones that are not.
+ */
+int
+pass_manifest(const char *path, const char *prefix)
+{
+ char *content;
+ struct stat st;
+ unsigned char digest[br_sha256_SIZE];
+ const br_hash_class *md;
+ br_hash_compat_context ctx;
+ int rc;
+
+ content = NULL;
+ md = &br_sha256_vtable;
+
+ if (strnlen(path, MAXPATHLEN) == MAXPATHLEN ||
+ strnlen(prefix, MAXPATHLEN) == MAXPATHLEN)
+ return (EINVAL);
+
+ rc = stat(path, &st);
+ if (rc != 0)
+ goto out;
+
+ if (!S_ISREG(st.st_mode)) {
+ rc = EINVAL;
+ goto out;
+ }
+
+ rc = is_verified(&st);
+
+ if (rc != VE_NOT_CHECKED && rc != VE_VERIFIED) {
+ rc = EPERM;
+ goto out;
+ }
+
+ if (rc == VE_VERIFIED)
+ content = read_file(path, NULL);
+ else
+ content = (char *)verify_signed(path, VEF_VERBOSE);
+
+ if (content == NULL) {
+ add_verify_status(&st, VE_FINGERPRINT_WRONG);
+ rc = EIO;
+ goto out;
+ }
+
+ add_verify_status(&st, VE_VERIFIED);
+
+ md->init(&ctx.vtable);
+ md->update(&ctx.vtable, content, st.st_size);
+ md->out(&ctx.vtable, digest);
+
+ if (prefix == NULL)
+ manifest_prefix[0] = '\0';
+ else
+ strcpy(manifest_prefix, prefix);
+
+ strcpy(manifest_path, path);
+
+ hexdigest(manifest_hash, 2 * br_sha256_SIZE + 2,
+ digest, br_sha256_SIZE);
+ manifest_hash[2*br_sha256_SIZE] = '\0';
+
+ manifest_present = 1;
+ rc = 0;
+
+out:
+ if (content != NULL)
+ free(content);
+
+ return (rc);
+}
+
+/*
+ * Set appropriate envs to inform kernel about manifest location and digest.
+ * This should be called right before boot so that envs can't be replaced.
+ */
+int
+pass_manifest_export_envs()
+{
+ int rc;
+
+ /* If we have nothing to pass make sure that envs are empty. */
+ if (!manifest_present) {
+ unsetenv("veriexec.manifest_path");
+ unsetenv("veriexec.manifest_hash");
+ unsetenv("veriexec.manifest_prefix");
+ return (0);
+ }
+
+ rc = setenv("veriexec.manifest_path", manifest_path, 1);
+ if (rc != 0)
+ return (rc);
+
+ rc = setenv("veriexec.manifest_hash", manifest_hash, 1);
+ if (rc != 0) {
+ unsetenv("veriexec.manifest_path");
+ return (rc);
+ }
+
+ if (manifest_prefix[0] != '\0')
+ rc = setenv("veriexec.manifest_prefix", manifest_prefix, 1);
+
+ return (rc);
+}
+
diff --git a/lib/libsecureboot/readfile.c b/lib/libsecureboot/readfile.c
new file mode 100644
index 000000000000..19784f6f4139
--- /dev/null
+++ b/lib/libsecureboot/readfile.c
@@ -0,0 +1,80 @@
+/*-
+ * 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>
+#include <libsecureboot.h>
+
+unsigned char *
+read_fd(int fd, size_t len)
+{
+ int m, n, x;
+ unsigned char *buf;
+
+ buf = malloc(len + 1);
+ if (buf != NULL) {
+ for (x = 0, m = len; m > 0; ) {
+ n = read(fd, &buf[x], m);
+ if (n < 0)
+ break;
+ if (n > 0) {
+ m -= n;
+ x += n;
+ }
+ }
+ if (m == 0) {
+ buf[len] = '\0';
+ return (buf);
+ }
+ free(buf);
+ }
+ return (NULL);
+}
+
+unsigned char *
+read_file(const char *path, size_t *len)
+{
+ struct stat st;
+ unsigned char *ucp;
+ int fd;
+
+ if (len)
+ *len = 0;
+ if ((fd = open(path, O_RDONLY)) < 0)
+ return (NULL);
+ fstat(fd, &st);
+ ucp = read_fd(fd, st.st_size);
+ close(fd);
+ if (ucp != NULL) {
+ if (len != NULL)
+ *len = st.st_size;
+ }
+#ifdef _STANDALONE
+ else
+ printf("%s: out of memory! %lu\n", __func__,
+ (unsigned long)len);
+#endif
+
+ return (ucp);
+}
+
diff --git a/lib/libsecureboot/tests/Makefile b/lib/libsecureboot/tests/Makefile
new file mode 100644
index 000000000000..b3de2b6ce5b9
--- /dev/null
+++ b/lib/libsecureboot/tests/Makefile
@@ -0,0 +1,19 @@
+.include <src.opts.mk>
+
+PROG= tvo
+
+SRCS+= tvo.c
+CFLAGS+= -DUNIT_TEST -g -O0
+
+LIBADD+= bearssl
+MAN=
+NO_SHARED=
+
+# we want to test verify_file api too
+# which requires a kludge or two
+MK_LOADER_EFI_SECUREBOOT= no
+.include "../Makefile.libsa.inc"
+BRSSL_CFLAGS := ${BRSSL_CFLAGS:N-DNO_STDIO}
+XCFLAGS.verify_file += -DSOPEN_MAX=64
+
+.include <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..d31947a35098
--- /dev/null
+++ b/lib/libsecureboot/tests/Makefile.depend.host
@@ -0,0 +1,11 @@
+# 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..7851e79c5a2a
--- /dev/null
+++ b/lib/libsecureboot/tests/tvo.c
@@ -0,0 +1,213 @@
+/*
+ * 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>
+#include "../libsecureboot-priv.h"
+
+#include <unistd.h>
+#include <err.h>
+#include <verify_file.h>
+
+/* keep clang quiet */
+extern char *Destdir;
+extern size_t DestdirLen;
+extern char *Skip;
+extern time_t ve_utc;
+
+size_t DestdirLen;
+char *Destdir;
+char *Skip;
+
+int
+main(int argc, char *argv[])
+{
+ int n;
+ int fd;
+ int c;
+ int Vflag;
+ int vflag;
+ char *cp;
+ char *prefix;
+
+ Destdir = NULL;
+ DestdirLen = 0;
+ prefix = NULL;
+ Skip = NULL;
+
+ n = ve_trust_init();
+ Vflag = 0;
+ vflag = 0;
+
+ while ((c = getopt(argc, argv, "D:dp:s:T:u:Vv")) != -1) {
+ switch (c) {
+ case 'D':
+ Destdir = optarg;
+ DestdirLen = strlen(optarg);
+ break;
+ 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;
+ case 'v':
+ vflag = 1;
+ break;
+ case 'u':
+ ve_utc = (time_t)atoi(optarg);
+ break;
+ default:
+ errx(1, "unknown option: -%c", c);
+ break;
+ }
+ }
+
+ if (!vflag) {
+ printf("Trust %d\n", n);
+#ifdef VE_PCR_SUPPORT
+ ve_pcr_updating_set(1);
+#endif
+ 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, __func__);
+ 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);
+ if (!vflag)
+ 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);
+ if (!vflag)
+ 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;
+ off_t off;
+ size_t nb;
+
+ fstat(fd, &st);
+ lseek(fd, 0, SEEK_SET);
+ off = st.st_size % 512;
+ vp = vectx_open(fd, argv[optind], off,
+ &st, &error, __func__);
+ 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);
+ /* we can seek backwards! */
+ off = vectx_lseek(vp, off/2, SEEK_SET);
+ if (off < st.st_size) {
+ nb = vectx_read(vp, buf,
+ sizeof(buf));
+ if (nb > 0)
+ off += nb;
+ }
+ off = vectx_lseek(vp, 0, SEEK_END);
+ /* repeating that should be harmless */
+ off = vectx_lseek(vp, 0, SEEK_END);
+ error = vectx_close(vp, VE_MUST, __func__);
+ 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);
+ }
+ }
+ }
+#ifdef VE_PCR_SUPPORT
+ verify_pcr_export();
+ printf("pcr=%s\n", getenv("loader.ve.pcr"));
+#endif
+ return (0);
+}
+
diff --git a/lib/libsecureboot/vectx.c b/lib/libsecureboot/vectx.c
new file mode 100644
index 000000000000..2d56830cd81d
--- /dev/null
+++ b/lib/libsecureboot/vectx.c
@@ -0,0 +1,416 @@
+/*-
+ * 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>
+#ifndef _STANDALONE
+/* Avoid unwanted userlandish components */
+#define _KERNEL
+#include <sys/errno.h>
+#undef _KERNEL
+#endif
+
+#ifdef VECTX_DEBUG
+static int vectx_debug = VECTX_DEBUG;
+# define DEBUG_PRINTF(n, x) if (vectx_debug >= n) printf x
+#endif
+
+#include "libsecureboot-priv.h"
+#include <verify_file.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 */
+ off_t vec_hashed; /* where we have hashed to */
+ off_t vec_size; /* size of path */
+ size_t vec_hashsz; /* size of hash */
+ int vec_fd; /* file descriptor */
+ int vec_status; /* verification status */
+ int vec_closing; /* we are closing */
+};
+
+
+/**
+ * @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, const char *caller)
+{
+ struct vectx *ctx;
+ struct stat st;
+ size_t hashsz;
+ char *cp;
+ int rc;
+
+ if (!stp)
+ stp = &st;
+
+ rc = verify_prep(fd, path, off, stp, __func__);
+
+ DEBUG_PRINTF(2,
+ ("vectx_open: caller=%s,fd=%d,name='%s',prep_rc=%d\n",
+ caller, fd, path, rc));
+
+ switch (rc) {
+ case VE_FINGERPRINT_NONE:
+ case VE_FINGERPRINT_UNKNOWN:
+ case VE_FINGERPRINT_WRONG:
+ *error = rc;
+ 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_hashed = 0;
+ ctx->vec_want = NULL;
+ ctx->vec_status = 0;
+ ctx->vec_hashsz = hashsz = 0;
+ ctx->vec_closing = 0;
+
+ if (rc == 0) {
+ /* we are not verifying this */
+ *error = 0;
+ return (ctx);
+ }
+ 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, "no_hash", 7) == 0) {
+ ctx->vec_status = VE_FINGERPRINT_IGNORE;
+ hashsz = 0;
+ } 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;
+ if (hashsz > 0) {
+ ctx->vec_md->init(&ctx->vec_ctx.vtable);
+
+ if (off > 0) {
+ lseek(fd, 0, SEEK_SET);
+ vectx_lseek(ctx, off, SEEK_SET);
+ }
+ }
+ DEBUG_PRINTF(2,
+ ("vectx_open: caller=%s,name='%s',hashsz=%lu,status=%d\n",
+ caller, path, (unsigned long)ctx->vec_hashsz,
+ ctx->vec_status));
+ 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.
+ * We also track what offset we have hashed to,
+ * so we won't replay data if we seek backwards.
+ *
+ * @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 d;
+ int n;
+ int delta;
+ int x;
+ size_t off;
+
+ if (ctx->vec_hashsz == 0) /* nothing to do */
+ return (read(ctx->vec_fd, buf, nbytes));
+
+ off = 0;
+ do {
+ /*
+ * Do this in reasonable chunks so
+ * we don't timeout if doing tftp
+ */
+ x = nbytes - off;
+ x = MIN(PAGE_SIZE, x);
+ d = n = read(ctx->vec_fd, &bp[off], x);
+ if (ctx->vec_closing && n < x) {
+ DEBUG_PRINTF(3,
+ ("%s: read %d off=%ld hashed=%ld size=%ld\n",
+ __func__, n, (long)ctx->vec_off,
+ (long)ctx->vec_hashed, (long)ctx->vec_size));
+ }
+ if (n < 0) {
+ return (n);
+ }
+ if (d > 0) {
+ /* we may have seeked backwards! */
+ delta = ctx->vec_hashed - ctx->vec_off;
+ if (delta > 0) {
+ x = MIN(delta, d);
+ off += x;
+ d -= x;
+ ctx->vec_off += x;
+ }
+ if (d > 0) {
+ if (ctx->vec_closing && d < PAGE_SIZE) {
+ DEBUG_PRINTF(3,
+ ("%s: update %ld + %d\n",
+ __func__,
+ (long)ctx->vec_hashed, d));
+ }
+ ctx->vec_md->update(&ctx->vec_ctx.vtable, &bp[off], d);
+ off += d;
+ ctx->vec_off += d;
+ ctx->vec_hashed += d;
+ }
+ }
+ } while (n > 0 && off < nbytes);
+ return (off);
+}
+
+/**
+ * @brief
+ * vectx equivalent of lseek
+ *
+ * When seeking forwards we actually call vectx_read
+ * to reach the desired offset.
+ *
+ * We support seeking backwards.
+ *
+ * @param[in] pctx
+ * pointer to ctx
+ *
+ * @param[in] off
+ * desired offset
+ *
+ * @param[in] whence
+ * We try to convert whence to ``SEEK_SET``.
+ * We do not support ``SEEK_DATA`` or ``SEEK_HOLE``.
+ *
+ * @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));
+
+ /*
+ * Convert whence to SEEK_SET
+ */
+ DEBUG_PRINTF(3,
+ ("%s(%s, %ld, %d)\n", __func__, ctx->vec_path, (long)off, whence));
+ if (whence == SEEK_END && off <= 0) {
+ if (ctx->vec_size < 0) {
+ if (ctx->vec_closing) {
+ /* size unknown - read until EOF */
+ do {
+ n = vectx_read(ctx, buf, PAGE_SIZE);
+ if (n < 0)
+ return (n);
+ } while (n > 0);
+ return (ctx->vec_off);
+ }
+ } else {
+ if (ctx->vec_closing && ctx->vec_hashed < ctx->vec_size) {
+ DEBUG_PRINTF(3, ("%s: SEEK_END %ld\n",
+ __func__,
+ (long)(ctx->vec_size - ctx->vec_hashed)));
+ }
+ whence = SEEK_SET;
+ off += ctx->vec_size;
+ }
+ } else if (whence == SEEK_CUR) {
+ whence = SEEK_SET;
+ off += ctx->vec_off;
+ }
+ if (whence != SEEK_SET ||
+ (off > ctx->vec_size && ctx->vec_size > 0)) {
+ printf("ERROR: %s: unsupported operation: whence=%d off=%ld -> %ld\n",
+ __func__, whence, (long)ctx->vec_off, (long)off);
+ return (-1);
+ }
+ if (off < ctx->vec_hashed) {
+#ifdef _STANDALONE
+ struct open_file *f = fd2open_file(ctx->vec_fd);
+
+ if (f != NULL &&
+ strncmp(f->f_ops->fs_name, "tftp", 4) == 0) {
+ /* we cannot rewind if we've hashed much of the file */
+ if (ctx->vec_hashed > ctx->vec_size / 5)
+ return (-1); /* refuse! */
+ }
+#endif
+ /* seeking backwards! just do it */
+ ctx->vec_off = lseek(ctx->vec_fd, off, whence);
+ return (ctx->vec_off);
+ }
+ 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.
+ *
+ * Be sure to call this before closing the file, since we may
+ * need to seek to the end to ensure hashing is complete.
+ *
+ * @param[in] pctx
+ * pointer to ctx
+ *
+ * @return 0 or an error.
+ */
+int
+vectx_close(struct vectx *ctx, int severity, const char *caller)
+{
+ int rc;
+
+ ctx->vec_closing = 1;
+ if (ctx->vec_hashsz == 0) {
+ rc = ctx->vec_status;
+ } else {
+#ifdef VE_PCR_SUPPORT
+ /*
+ * Only update pcr with things that must verify
+ * these tend to be processed in a more deterministic
+ * order, which makes our pseudo pcr more useful.
+ */
+ ve_pcr_updating_set((severity == VE_MUST));
+#endif
+ /* make sure we have hashed it all */
+ vectx_lseek(ctx, 0, SEEK_END);
+ rc = ve_check_hash(&ctx->vec_ctx, ctx->vec_md,
+ ctx->vec_path, ctx->vec_want, ctx->vec_hashsz);
+ }
+ DEBUG_PRINTF(2,
+ ("vectx_close: caller=%s,name='%s',rc=%d,severity=%d\n",
+ caller,ctx->vec_path, rc, severity));
+ verify_report(ctx->vec_path, severity, rc, NULL);
+ if (rc == VE_FINGERPRINT_WRONG) {
+#if !defined(UNIT_TEST) && !defined(DEBUG_VECTX)
+ /* we are generally called with VE_MUST */
+ if (severity > VE_WANT)
+ panic("cannot continue");
+#endif
+ }
+ 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..0a1fc59eb9b4
--- /dev/null
+++ b/lib/libsecureboot/veopen.c
@@ -0,0 +1,469 @@
+/*-
+ * 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>
+#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 (nfip == NULL) {
+#ifdef _STANDALONE
+ printf("%s: out of memory! %lu\n", __func__,
+ (unsigned long)sizeof(struct fingerprint_info));
+#endif
+ return;
+ }
+ 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 == nfip->fi_prefix) {
+ cp[1] = '\0';
+ } else if (cp) {
+ *cp = '\0';
+ } else {
+ free(nfip->fi_prefix);
+ free(nfip);
+ return;
+ }
+ }
+ /* collapse any trailing ..[/] */
+ n = 0;
+ while ((cp = strrchr(nfip->fi_prefix, '/')) > nfip->fi_prefix) {
+ 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;
+ }
+ nfip->fi_dev = stp->st_dev;
+#ifdef UNIT_TEST
+ nfip->fi_dev = 0;
+#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);
+ if (fstat(fd, &st) == 0)
+ dev = st.st_dev;
+#ifdef UNIT_TEST
+ dev = 0;
+#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, "no_hash", 7) == 0) {
+ return (VE_FINGERPRINT_IGNORE);
+ } else 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_IGNORE:
+ 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..f4f146975e5e
--- /dev/null
+++ b/lib/libsecureboot/vepcr.c
@@ -0,0 +1,167 @@
+/*-
+ * 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>
+#include <sys/queue.h>
+#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;
+static int pcr_updating = -1;
+
+struct hashed_info {
+ const char *hi_path;
+ const char *hi_basename;
+ STAILQ_ENTRY(hashed_info) entries;
+};
+
+static STAILQ_HEAD(, hashed_info) hi_list;
+
+
+/**
+ * @brief initialize pcr context
+ *
+ * Real TPM registers only hold a SHA1 hash
+ * but we use SHA256
+ */
+void
+ve_pcr_init(void)
+{
+ if (pcr_updating < 0) {
+ pcr_updating = 0;
+ pcr_hlen = br_sha256_SIZE;
+ pcr_md = &br_sha256_vtable;
+ pcr_md->init(&pcr_ctx.vtable);
+ STAILQ_INIT(&hi_list);
+ }
+}
+
+/**
+ * @brief get pcr_updating state
+ */
+int
+ve_pcr_updating_get(void)
+{
+ return (pcr_updating);
+}
+
+/**
+ * @brief set pcr_updating state
+ */
+void
+ve_pcr_updating_set(int updating)
+{
+ pcr_updating = updating;
+}
+
+/**
+ * @brief update pcr context
+ */
+void
+ve_pcr_update(const char *path, unsigned char *data, size_t dlen)
+{
+ struct hashed_info *hip;
+
+ if (pcr_updating > 0 && pcr_md != NULL) {
+ pcr_md->update(&pcr_ctx.vtable, data, dlen);
+ /* if mallocs fail, measured boot will likely fail too */
+ if ((hip = malloc(sizeof(struct hashed_info)))) {
+ hip->hi_path = strdup(path);
+ if (!hip->hi_path) {
+ free(hip);
+ return;
+ }
+ hip->hi_basename = strrchr(hip->hi_path, '/');
+ if (hip->hi_basename) {
+ hip->hi_basename++;
+ } else {
+ hip->hi_basename = hip->hi_path;
+ }
+ STAILQ_INSERT_TAIL(&hi_list, hip, entries);
+ }
+ }
+}
+
+/**
+ * @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);
+}
+
+/**
+ * @brief get list of paths in prc
+ */
+char *
+ve_pcr_hashed_get(int flags)
+{
+ const char *cp;
+ char *hinfo;
+ struct hashed_info *hip;
+ size_t nbytes;
+ size_t x;
+ int n;
+
+ n = 0;
+ nbytes = x = 0;
+ hinfo = NULL;
+ STAILQ_FOREACH(hip, &hi_list, entries) {
+ nbytes += 1 + strlen(flags ? hip->hi_basename : hip->hi_path);
+ }
+ if (nbytes > 1) {
+ hinfo = malloc(nbytes + 2);
+ if (hinfo) {
+ STAILQ_FOREACH(hip, &hi_list, entries) {
+ cp = flags ? hip->hi_basename : hip->hi_path;
+ n = snprintf(&hinfo[x], nbytes - x, "%s,", cp);
+ x += n;
+ }
+ if (x > 0) {
+ hinfo[x-1] = '\0';
+ }
+ }
+ }
+ return hinfo;
+}
diff --git a/lib/libsecureboot/verify_file.c b/lib/libsecureboot/verify_file.c
new file mode 100644
index 000000000000..753204a33b6a
--- /dev/null
+++ b/lib/libsecureboot/verify_file.c
@@ -0,0 +1,690 @@
+/*-
+ * Copyright (c) 2017-2020, 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/param.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <sys/kenv.h>
+
+#include "libsecureboot.h"
+#include <verify_file.h>
+#include <manifests.h>
+
+#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 *Destdir;
+extern size_t DestdirLen;
+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.
+ */
+#ifndef SOPEN_MAX
+#define SOPEN_MAX 64
+#endif
+static int ve_status[SOPEN_MAX+1];
+static int ve_status_state;
+struct verify_status;
+static struct verify_status *verified_files = NULL;
+static int loaded_manifests = 0; /* have we loaded anything? */
+
+enum {
+ VE_VERBOSE_SILENT, /* only report errors */
+ VE_VERBOSE_UNVERIFIED, /* all unverified files */
+ VE_VERBOSE_MUST, /* report VE_MUST */
+ VE_VERBOSE_ALL, /* report all */
+ VE_VERBOSE_DEBUG, /* extra noise */
+};
+
+#ifndef VE_VERBOSE_DEFAULT
+# define VE_VERBOSE_DEFAULT VE_VERBOSE_MUST
+#endif
+static int Verbose = VE_VERBOSE_DEFAULT;
+
+#define VE_STATUS_NONE 1
+#define VE_STATUS_VALID 2
+
+/**
+ * @brief set ve status for fd
+ */
+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;
+};
+
+int
+is_verified(struct stat *stp)
+{
+ struct verify_status *vsp;
+ int rc = VE_NOT_CHECKED;
+
+ if (stp->st_ino > 0) {
+ for (vsp = verified_files; vsp != NULL; vsp = vsp->vs_next) {
+ if (stp->st_dev == vsp->vs_dev &&
+ stp->st_ino == vsp->vs_ino) {
+ rc = vsp->vs_status;
+ break;
+ }
+ }
+ }
+ DEBUG_PRINTF(4, ("%s: dev=%lld,ino=%llu,status=%d\n",
+ __func__, (long long)stp->st_dev,
+ (unsigned long long)stp->st_ino, rc));
+ return (rc);
+}
+
+/* most recent first, since most likely to see repeated calls. */
+void
+add_verify_status(struct stat *stp, int status)
+{
+ struct verify_status *vsp;
+
+ vsp = malloc(sizeof(struct verify_status));
+ if (vsp) {
+ vsp->vs_next = verified_files;
+ vsp->vs_dev = stp->st_dev;
+ vsp->vs_ino = stp->st_ino;
+ vsp->vs_status = status;
+ verified_files = vsp;
+ }
+ DEBUG_PRINTF(4, ("%s: dev=%lld,ino=%llu,status=%d\n",
+ __func__, (long long)stp->st_dev,
+ (unsigned long long)stp->st_ino, status));
+}
+
+
+/**
+ * @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, VerifyFlags);
+ if (content) {
+#ifdef UNIT_TEST
+ if (DestdirLen > 0 &&
+ strncmp(name, Destdir, DestdirLen) == 0) {
+ name += DestdirLen;
+ if (prefix &&
+ strncmp(prefix, Destdir, DestdirLen) == 0)
+ prefix += DestdirLen;
+ }
+#endif
+ 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 = VE_VERIFIED;
+ } 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);
+ if (*tp[0] == '.') {
+ /* skip /../ */
+ if (prefix[0] == '\0' || prefix[1] == '\0')
+ continue;
+ }
+ 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
+
+static int
+severity_guess(const char *filename)
+{
+ const char *cp;
+
+ /*
+ * Some files like *.conf and *.hints may be unsigned,
+ * a *.tgz is expected to have its own signed manifest.
+ * We allow *.conf to get VE_WANT, but files we expect
+ * to always be unverified get VE_TRY and we will not
+ * report them.
+ */
+ if ((cp = strrchr(filename, '.'))) {
+ if (strcmp(cp, ".cookie") == 0 ||
+ strcmp(cp, ".hints") == 0 ||
+ strcmp(cp, ".order") == 0 ||
+ strcmp(cp, ".tgz") == 0)
+ return (VE_TRY);
+ if (strcmp(cp, ".4th") == 0 ||
+ strcmp(cp, ".lua") == 0 ||
+ strcmp(cp, ".rc") == 0)
+ return (VE_MUST);
+ }
+ return (VE_WANT);
+}
+
+static int Verifying = -1; /* 0 if not verifying */
+
+static void
+verify_tweak(int fd, off_t off, struct stat *stp,
+ char *tweak, int *accept_no_fp)
+{
+ 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 = VE_VERBOSE_ALL;
+ /* 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 = VE_VERBOSE_ALL;
+ } else if (strcmp(tweak, "quiet") == 0) {
+ Verbose = VE_VERBOSE_UNVERIFIED;
+ VerifyFlags = 0;
+ } else if (strcmp(tweak, "silent") == 0) {
+ Verbose = VE_VERBOSE_SILENT;
+ VerifyFlags = 0;
+ } else if (strncmp(tweak, "trust", 5) == 0) {
+ /* content is trust anchor to add or revoke */
+ unsigned char *ucp;
+ size_t num;
+
+ if (off > 0)
+ lseek(fd, 0, SEEK_SET);
+ ucp = read_fd(fd, stp->st_size);
+ if (ucp == NULL)
+ return;
+ if (strstr(tweak, "revoke")) {
+ num = ve_trust_anchors_revoke(ucp, stp->st_size);
+ DEBUG_PRINTF(3, ("revoked %d trust anchors\n",
+ (int) num));
+ } else {
+ num = ve_trust_anchors_add_buf(ucp, stp->st_size);
+ DEBUG_PRINTF(3, ("added %d trust anchors\n",
+ (int) num));
+ }
+ }
+}
+
+#ifndef VE_DEBUG_LEVEL
+# define VE_DEBUG_LEVEL 0
+#endif
+
+static int
+getenv_int(const char *var, int def)
+{
+ const char *cp;
+ char *ep;
+ long val;
+
+ val = def;
+ cp = getenv(var);
+ if (cp && *cp) {
+ val = strtol(cp, &ep, 0);
+ if ((ep && *ep) || val != (int)val) {
+ val = def;
+ }
+ }
+ return (int)val;
+}
+
+
+/**
+ * @brief report verification status
+ *
+ * @param[in] path
+ * path we attempted to verify
+ *
+ * @param[in] severity
+ * indicator of how to handle case of missing fingerprint
+ *
+ * @param[in] status
+ * result of verification
+ * 0 not a file to be verified, > 0 success, < 0 error
+ *
+ * @param[in] stp
+ * pointer to struct stat, used in extra info to be output
+ *
+ * The output is dictated by combinations of the above and the setting
+ * of Verbose:
+ *
+ * VE_VERBOSE_SILENT
+ * report only failure to verify if severity is VE_WANT or higher.
+ *
+ * VE_VERBOSE_UNVERIFIED
+ * report any unverified file.
+ *
+ * VE_VERBOSE_MUST
+ * report verified only if severity is VE_MUST or higher.
+ *
+ * VE_VERBOSE_ALL
+ * report all verified files.
+ *
+ * VE_VERBOSE_DEBUG
+ * if stp is not NULL report dev,inode for path
+ */
+void
+verify_report(const char *path, int severity, int status, struct stat *stp)
+{
+ if (status < 0 || status == VE_FINGERPRINT_IGNORE) {
+ if (Verbose < VE_VERBOSE_ALL && severity < VE_WANT)
+ return;
+ if (Verbose >= VE_VERBOSE_UNVERIFIED || severity > VE_TRY ||
+ status <= VE_FINGERPRINT_WRONG) {
+ if (Verbose == VE_VERBOSE_DEBUG && stp != NULL)
+ printf("Unverified %s %llu,%llu\n",
+ ve_error_get(),
+ (long long)stp->st_dev,
+ (long long)stp->st_ino);
+ else
+ printf("Unverified %s\n", ve_error_get());
+ }
+ } else if (status > 0 && Verbose >= VE_VERBOSE_MUST) {
+ if (severity >= VE_MUST || Verbose >= VE_VERBOSE_ALL) {
+ if (Verbose == VE_VERBOSE_DEBUG && stp != NULL)
+ printf("Unverified %s %llu,%llu\n",
+ path,
+ (long long)stp->st_dev,
+ (long long)stp->st_ino);
+ else
+ printf("Verified %s\n", path);
+ }
+ }
+}
+
+
+/**
+ * @brief prepare to verify an open file
+ *
+ * @param[in] fd
+ * open descriptor
+ *
+ * @param[in] filename
+ * path we opened and will use to lookup fingerprint
+ *
+ * @param[in] stp
+ * stat pointer so we can check file type
+ */
+int
+verify_prep(int fd, const char *filename, off_t off, struct stat *stp,
+ const char *caller)
+{
+ int rc;
+
+ if (Verifying < 0) {
+ Verifying = ve_trust_init();
+ /* 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();
+ ve_anchor_verbose_set(1);
+ }
+ }
+ if (!Verifying || fd < 0)
+ return (0);
+ if (stp) {
+ if (fstat(fd, stp) < 0 || !S_ISREG(stp->st_mode))
+ return (0);
+ }
+ DEBUG_PRINTF(2,
+ ("verify_prep: caller=%s,fd=%d,name='%s',off=%lld,dev=%lld,ino=%llu\n",
+ caller, fd, filename, (long long)off, (long long)stp->st_dev,
+ (unsigned long long)stp->st_ino));
+ rc = is_verified(stp);
+ if (rc == VE_NOT_CHECKED) {
+ rc = find_manifest(filename);
+ if (rc == VE_VERIFIED)
+ rc = VE_NOT_CHECKED;
+ } else {
+ ve_status_set(fd, rc);
+ }
+ return (rc);
+}
+
+/**
+ * @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,
+ const char *caller)
+{
+ static int check_verbose = 1;
+ static int accept_no_fp = ACCEPT_NO_FP_DEFAULT;
+ struct stat st;
+ char *cp;
+ int rc;
+
+ if (check_verbose) {
+ check_verbose = 0;
+ Verbose = getenv_int("VE_VERBOSE", VE_VERBOSE_DEFAULT);
+ VerifyFlags = getenv_int("VE_VERIFY_FLAGS",
+ Verbose ? VEF_VERBOSE : 0);
+#ifndef UNIT_TEST
+ ve_debug_set(getenv_int("VE_DEBUG_LEVEL", VE_DEBUG_LEVEL));
+#endif
+ }
+
+ rc = verify_prep(fd, filename, off, &st, caller);
+
+ if (!rc)
+ return (0);
+
+ if (rc != VE_FINGERPRINT_WRONG && loaded_manifests) {
+ if (rc != VE_NOT_CHECKED)
+ return (rc);
+
+ if (severity <= VE_GUESS)
+ severity = severity_guess(filename);
+#ifdef VE_PCR_SUPPORT
+ /*
+ * Only update pcr with things that must verify
+ * these tend to be processed in a more deterministic
+ * order, which makes our pseudo pcr more useful.
+ */
+ ve_pcr_updating_set((severity == VE_MUST));
+#endif
+#ifdef UNIT_TEST
+ if (DestdirLen > 0 &&
+ strncmp(filename, Destdir, DestdirLen) == 0) {
+ filename += DestdirLen;
+ }
+#endif
+ rc = verify_fd(fd, filename, off, &st);
+ verify_report(filename, severity, rc, &st);
+ if (rc >= 0) {
+ 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(fd, off, &st, cp,
+ &accept_no_fp);
+ }
+ }
+ }
+ add_verify_status(&st, rc);
+ ve_status_set(fd, rc);
+ return (rc);
+ }
+ 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);
+
+ /* recheck debug/verbose level next time we are called */
+ if (rc == VE_UNVERIFIED_OK) {
+ check_verbose = 1;
+ }
+ }
+#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 *hinfo;
+ 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);
+ DEBUG_PRINTF(1,
+ ("%s: setenv(loader.ve.pcr, %s\n", __func__,
+ hex));
+ hinfo = ve_pcr_hashed_get(1);
+ if (hinfo) {
+ setenv("loader.ve.hashed", hinfo, 1);
+ DEBUG_PRINTF(1,
+ ("%s: setenv(loader.ve.hashed, %s\n",
+ __func__, hinfo));
+ if ((hlen = strlen(hinfo)) > KENV_MVALLEN) {
+ /*
+ * bump kenv_mvallen
+ * roundup to multiple of KENV_MVALLEN
+ */
+ char mvallen[16];
+
+ hlen += KENV_MVALLEN -
+ (hlen % KENV_MVALLEN);
+ if (snprintf(mvallen, sizeof(mvallen),
+ "%d", (int) hlen) < (int)sizeof(mvallen))
+ setenv("kenv_mvallen", mvallen, 1);
+ }
+ free(hinfo);
+ }
+ }
+ }
+#endif
+}
+
+/*
+ * For tftp and http we need to hash pathname
+ * to be able to fake stat(2) data.
+ */
+int
+hash_string(char *s, size_t n, char *buf, size_t bufsz)
+{
+ br_hash_compat_context mctx;
+ const br_hash_class *md;
+
+ switch (bufsz) {
+ case br_sha1_SIZE:
+ md = &br_sha1_vtable;
+ break;
+ case br_sha256_SIZE:
+ md = &br_sha256_vtable;
+ break;
+ default:
+ if (bufsz < br_sha1_SIZE)
+ return -1;
+ md = &br_sha1_vtable;
+ bufsz = br_sha1_SIZE;
+ break;
+ }
+ if (n == 0)
+ n = strlen(s);
+ md->init(&mctx.vtable);
+ md->update(&mctx.vtable, s, n);
+ md->out(&mctx.vtable, buf);
+ return bufsz;
+}
+
+
diff --git a/lib/libsecureboot/vesigned.c b/lib/libsecureboot/vesigned.c
new file mode 100644
index 000000000000..4015923eabfd
--- /dev/null
+++ b/lib/libsecureboot/vesigned.c
@@ -0,0 +1,59 @@
+/*-
+ * 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>
+#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..c0ddbfb4921f
--- /dev/null
+++ b/lib/libsecureboot/veta.c
@@ -0,0 +1,109 @@
+/*-
+ * 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>
+/**
+ * @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..67d27d567485
--- /dev/null
+++ b/lib/libsecureboot/vets.c
@@ -0,0 +1,1156 @@
+/*-
+ * Copyright (c) 2017-2018, Juniper Networks, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+/**
+ * @file vets.c - trust store
+ * @brief verify signatures
+ *
+ * We leverage code from BearSSL www.bearssl.org
+ */
+
+#include <sys/time.h>
+#include <stdarg.h>
+#define NEED_BRSSL_H
+#include "libsecureboot-priv.h"
+#include <brssl.h>
+#include <ta.h>
+
+#ifndef TRUST_ANCHOR_STR
+# define TRUST_ANCHOR_STR ta_PEM
+#endif
+
+#define EPOCH_YEAR 1970
+#define AVG_SECONDS_PER_YEAR 31556952L
+#define SECONDS_PER_DAY 86400
+#define SECONDS_PER_YEAR 365 * SECONDS_PER_DAY
+#ifndef VE_UTC_MAX_JUMP
+# define VE_UTC_MAX_JUMP 20 * SECONDS_PER_YEAR
+#endif
+#define X509_DAYS_TO_UTC0 719528
+
+int DebugVe = 0;
+
+#ifndef VE_VERIFY_FLAGS
+# define VE_VERIFY_FLAGS VEF_VERBOSE
+#endif
+int VerifyFlags = VE_VERIFY_FLAGS;
+
+typedef VECTOR(br_x509_certificate) cert_list;
+typedef VECTOR(hash_data) digest_list;
+
+static anchor_list trust_anchors = VEC_INIT;
+static anchor_list forbidden_anchors = VEC_INIT;
+static digest_list forbidden_digests = VEC_INIT;
+
+static int anchor_verbose = 0;
+
+void
+ve_anchor_verbose_set(int n)
+{
+ anchor_verbose = n;
+}
+
+int
+ve_anchor_verbose_get(void)
+{
+ return (anchor_verbose);
+}
+
+void
+ve_debug_set(int n)
+{
+ DebugVe = n;
+}
+
+/*
+ * For embedded systems (and boot loaders)
+ * we do not want to enforce certificate validity post install.
+ * It is generally unacceptible for infrastructure to stop working
+ * just because it has not been updated recently.
+ */
+static int enforce_validity = 0;
+
+void
+ve_enforce_validity_set(int i)
+{
+ enforce_validity = i;
+}
+
+static char ebuf[512];
+
+char *
+ve_error_get(void)
+{
+ return (ebuf);
+}
+
+int
+ve_error_set(const char *fmt, ...)
+{
+ int rc;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ebuf[0] = '\0';
+ rc = 0;
+ if (fmt) {
+#ifdef STAND_H
+ vsprintf(ebuf, fmt, ap); /* no vsnprintf in libstand */
+ ebuf[sizeof(ebuf) - 1] = '\0';
+ rc = strlen(ebuf);
+#else
+ rc = vsnprintf(ebuf, sizeof(ebuf), fmt, ap);
+#endif
+ }
+ va_end(ap);
+ return (rc);
+}
+
+#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+
+/*
+ * The *approximate* date.
+ *
+ * When certificate verification fails for being
+ * expired or not yet valid, it helps to indicate
+ * our current date.
+ * Since libsa lacks strftime and gmtime,
+ * this simple implementation suffices.
+ */
+static const char *
+gdate(char *buf, size_t bufsz, time_t clock)
+{
+ int days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+ int year, y, m, d;
+
+ y = clock / AVG_SECONDS_PER_YEAR;
+ year = EPOCH_YEAR + y;
+ for (y = EPOCH_YEAR; y < year; y++) {
+ clock -= SECONDS_PER_YEAR;
+ if (isleap(y))
+ clock -= SECONDS_PER_DAY;
+ }
+ d = clock / SECONDS_PER_DAY;
+ for (m = 0; d > 1 && m < 12; m++) {
+ if (d > days[m]) {
+ d -= days[m];
+ if (m == 1 && d > 0 && isleap(year))
+ d--;
+ } else
+ break;
+ }
+ d++;
+ if (d > days[m]) {
+ d = 1;
+ m++;
+ if (m >= 12) {
+ year++;
+ m = 0;
+ }
+ }
+ (void)snprintf(buf, bufsz, "%04d-%02d-%02d", year, m+1, d);
+ return(buf);
+}
+
+/* this is the time we use for verifying certs */
+#ifdef UNIT_TEST
+extern time_t ve_utc;
+time_t ve_utc = 0;
+#else
+static time_t ve_utc = 0;
+#endif
+
+/**
+ * @brief
+ * set ve_utc used for certificate verification
+ *
+ * @param[in] utc
+ * time - ignored unless greater than current value
+ * and not a leap of 20 years or more.
+ */
+void
+ve_utc_set(time_t utc)
+{
+ if (utc > ve_utc &&
+ (ve_utc == 0 || (utc - ve_utc) < VE_UTC_MAX_JUMP)) {
+ DEBUG_PRINTF(2, ("Set ve_utc=%jd\n", (intmax_t)utc));
+ ve_utc = utc;
+ }
+}
+
+#ifdef VERIFY_CERTS_STR
+static void
+free_cert_contents(br_x509_certificate *xc)
+{
+ xfree(xc->data);
+}
+#endif
+
+/*
+ * a bit of a dance to get commonName from a certificate
+ */
+static char *
+x509_cn_get(br_x509_certificate *xc, char *buf, size_t len)
+{
+ br_x509_minimal_context mc;
+ br_name_element cn;
+ unsigned char cn_oid[4];
+ int err;
+
+ if (buf == NULL)
+ return (buf);
+ /*
+ * We want the commonName field
+ * the OID we want is 2,5,4,3 - but DER encoded
+ */
+ cn_oid[0] = 3;
+ cn_oid[1] = 0x55;
+ cn_oid[2] = 4;
+ cn_oid[3] = 3;
+ cn.oid = cn_oid;
+ cn.buf = buf;
+ cn.len = len;
+ cn.buf[0] = '\0';
+
+ br_x509_minimal_init(&mc, &br_sha256_vtable, NULL, 0);
+ br_x509_minimal_set_name_elements(&mc, &cn, 1);
+ /* the below actually does the work - updates cn.status */
+ mc.vtable->start_chain(&mc.vtable, NULL);
+ mc.vtable->start_cert(&mc.vtable, xc->data_len);
+ mc.vtable->append(&mc.vtable, xc->data, xc->data_len);
+ mc.vtable->end_cert(&mc.vtable);
+ /* we don't actually care about cert status - just its name */
+ err = mc.vtable->end_chain(&mc.vtable);
+ (void)err; /* keep compiler quiet */
+
+ if (cn.status <= 0)
+ buf = NULL;
+ return (buf);
+}
+
+/* ASN parsing related defines */
+#define ASN1_PRIMITIVE_TAG 0x1F
+#define ASN1_INF_LENGTH 0x80
+#define ASN1_LENGTH_MASK 0x7F
+
+/*
+ * Get TBS part of certificate.
+ * Since BearSSL doesn't provide any API to do this,
+ * it has to be implemented here.
+ */
+static void*
+X509_to_tbs(unsigned char* cert, size_t* output_size)
+{
+ unsigned char *result;
+ size_t tbs_size;
+ int size, i;
+
+ if (cert == NULL)
+ return (NULL);
+
+ /* Strip two sequences to get to the TBS section */
+ for (i = 0; i < 2; i++) {
+ /*
+ * XXX: We don't need to support extended tags since
+ * they should not be present in certificates.
+ */
+ if ((*cert & ASN1_PRIMITIVE_TAG) == ASN1_PRIMITIVE_TAG)
+ return (NULL);
+
+ cert++;
+
+ if (*cert == ASN1_INF_LENGTH)
+ return (NULL);
+
+ size = *cert & ASN1_LENGTH_MASK;
+ tbs_size = 0;
+
+ /* Size can either be stored on a single or multiple bytes */
+ if (*cert & (ASN1_LENGTH_MASK + 1)) {
+ cert++;
+ while (*cert == 0 && size > 0) {
+ cert++;
+ size--;
+ }
+ while (size-- > 0) {
+ tbs_size <<= 8;
+ tbs_size |= *(cert++);
+ }
+ }
+ if (i == 0)
+ result = cert;
+ }
+ tbs_size += (cert - result);
+
+ if (output_size != NULL)
+ *output_size = tbs_size;
+
+ return (result);
+}
+
+void
+ve_forbidden_digest_add(hash_data *digest, size_t num)
+{
+ while (num--)
+ VEC_ADD(forbidden_digests, digest[num]);
+}
+
+static size_t
+ve_anchors_add(br_x509_certificate *xcs, size_t num, anchor_list *anchors,
+ const char *anchors_name)
+{
+ br_x509_trust_anchor ta;
+ size_t u;
+
+ for (u = 0; u < num; u++) {
+ if (certificate_to_trust_anchor_inner(&ta, &xcs[u]) < 0) {
+ break;
+ }
+ VEC_ADD(*anchors, ta);
+ if (anchor_verbose && anchors_name) {
+ char buf[64];
+ char *cp;
+
+ cp = x509_cn_get(&xcs[u], buf, sizeof(buf));
+ if (cp) {
+ printf("x509_anchor(%s) %s\n", cp, anchors_name);
+ }
+ }
+ }
+ return (u);
+}
+
+/**
+ * @brief
+ * add certs to our trust store
+ */
+size_t
+ve_trust_anchors_add(br_x509_certificate *xcs, size_t num)
+{
+ return (ve_anchors_add(xcs, num, &trust_anchors, "trusted"));
+}
+
+size_t
+ve_forbidden_anchors_add(br_x509_certificate *xcs, size_t num)
+{
+ return (ve_anchors_add(xcs, num, &forbidden_anchors, "forbidden"));
+}
+
+
+/**
+ * @brief add trust anchors in buf
+ *
+ * Assume buf contains x509 certificates, but if not and
+ * we support OpenPGP try adding as that.
+ *
+ * @return number of anchors added
+ */
+size_t
+ve_trust_anchors_add_buf(unsigned char *buf, size_t len)
+{
+ br_x509_certificate *xcs;
+ size_t num;
+
+ num = 0;
+ if (len > 0) {
+ xcs = parse_certificates(buf, len, &num);
+ if (xcs != NULL) {
+ num = ve_trust_anchors_add(xcs, num);
+#ifdef VE_OPENPGP_SUPPORT
+ } else {
+ num = openpgp_trust_add_buf(buf, len);
+#endif
+ }
+ }
+ return (num);
+}
+
+/**
+ * @brief revoke trust anchors in buf
+ *
+ * Assume buf contains x509 certificates, but if not and
+ * we support OpenPGP try revoking keyId
+ *
+ * @return number of anchors revoked
+ */
+size_t
+ve_trust_anchors_revoke(unsigned char *buf, size_t len)
+{
+ br_x509_certificate *xcs;
+ size_t num;
+
+ num = 0;
+ if (len > 0) {
+ xcs = parse_certificates(buf, len, &num);
+ if (xcs != NULL) {
+ num = ve_forbidden_anchors_add(xcs, num);
+#ifdef VE_OPENPGP_SUPPORT
+ } else {
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+ num = openpgp_trust_revoke((char *)buf);
+#endif
+ }
+ }
+ return (num);
+}
+
+/**
+ * @brief
+ * initialize our trust_anchors from ta_PEM
+ */
+int
+ve_trust_init(void)
+{
+ static int once = -1;
+
+ if (once >= 0)
+ return (once);
+ once = 0; /* to be sure */
+#ifdef BUILD_UTC
+ ve_utc_set(BUILD_UTC); /* ensure sanity */
+#endif
+ ve_utc_set(time(NULL));
+ ve_error_set(NULL); /* make sure it is empty */
+#ifdef VE_PCR_SUPPORT
+ ve_pcr_init();
+#endif
+
+#ifdef TRUST_ANCHOR_STR
+ if (TRUST_ANCHOR_STR != NULL && strlen(TRUST_ANCHOR_STR) != 0ul)
+ ve_trust_anchors_add_buf(__DECONST(unsigned char *,
+ TRUST_ANCHOR_STR), sizeof(TRUST_ANCHOR_STR));
+#endif
+ once = (int) VEC_LEN(trust_anchors);
+#ifdef VE_OPENPGP_SUPPORT
+ once += openpgp_trust_init();
+#endif
+ return (once);
+}
+
+#ifdef HAVE_BR_X509_TIME_CHECK
+static int
+verify_time_cb(void *tctx __unused,
+ uint32_t not_before_days, uint32_t not_before_seconds,
+ uint32_t not_after_days, uint32_t not_after_seconds)
+{
+ time_t not_before;
+ time_t not_after;
+ int rc;
+#ifdef UNIT_TEST
+ char date[12], nb_date[12], na_date[12];
+#endif
+
+ if (enforce_validity) {
+ not_before = ((not_before_days - X509_DAYS_TO_UTC0) * SECONDS_PER_DAY) + not_before_seconds;
+ not_after = ((not_after_days - X509_DAYS_TO_UTC0) * SECONDS_PER_DAY) + not_after_seconds;
+ if (ve_utc < not_before)
+ rc = -1;
+ else if (ve_utc > not_after)
+ rc = 1;
+ else
+ rc = 0;
+#ifdef UNIT_TEST
+ printf("notBefore %s notAfter %s date %s rc %d\n",
+ gdate(nb_date, sizeof(nb_date), not_before),
+ gdate(na_date, sizeof(na_date), not_after),
+ gdate(date, sizeof(date), ve_utc), rc);
+#endif
+ } else
+ rc = 0; /* don't fail */
+ return rc;
+}
+#endif
+
+/**
+ * if we can verify the certificate chain in "certs",
+ * return the public key and if "xcp" is !NULL the associated
+ * certificate
+ */
+static br_x509_pkey *
+verify_signer_xcs(br_x509_certificate *xcs,
+ size_t num,
+ br_name_element *elts, size_t num_elts,
+ anchor_list *anchors)
+{
+ br_x509_minimal_context mc;
+ br_x509_certificate *xc;
+ size_t u;
+ cert_list chain = VEC_INIT;
+ const br_x509_pkey *tpk;
+ br_x509_pkey *pk;
+ unsigned int usages;
+ int err;
+
+ DEBUG_PRINTF(5, ("verify_signer: %zu certs in chain\n", num));
+ VEC_ADDMANY(chain, xcs, num);
+ if (VEC_LEN(chain) == 0) {
+ ve_error_set("ERROR: no/invalid certificate chain\n");
+ return (NULL);
+ }
+
+ DEBUG_PRINTF(5, ("verify_signer: %zu trust anchors\n",
+ VEC_LEN(*anchors)));
+
+ br_x509_minimal_init(&mc, &br_sha256_vtable,
+ &VEC_ELT(*anchors, 0),
+ VEC_LEN(*anchors));
+#ifdef VE_ECDSA_SUPPORT
+ br_x509_minimal_set_ecdsa(&mc,
+ &br_ec_prime_i31, &br_ecdsa_i31_vrfy_asn1);
+#endif
+#ifdef VE_RSA_SUPPORT
+ br_x509_minimal_set_rsa(&mc, &br_rsa_i31_pkcs1_vrfy);
+#endif
+#if defined(UNIT_TEST) && defined(VE_DEPRECATED_RSA_SHA1_SUPPORT)
+ /* This is deprecated! do not enable unless you absolutely have to */
+ br_x509_minimal_set_hash(&mc, br_sha1_ID, &br_sha1_vtable);
+#endif
+ br_x509_minimal_set_hash(&mc, br_sha256_ID, &br_sha256_vtable);
+#ifdef VE_SHA384_SUPPORT
+ br_x509_minimal_set_hash(&mc, br_sha384_ID, &br_sha384_vtable);
+#endif
+#ifdef VE_SHA512_SUPPORT
+ br_x509_minimal_set_hash(&mc, br_sha512_ID, &br_sha512_vtable);
+#endif
+ br_x509_minimal_set_name_elements(&mc, elts, num_elts);
+
+#ifdef HAVE_BR_X509_TIME_CHECK
+ br_x509_minimal_set_time_callback(&mc, NULL, verify_time_cb);
+#else
+#if defined(_STANDALONE) || defined(UNIT_TEST)
+ /*
+ * Clock is probably bogus so we use ve_utc.
+ */
+ mc.days = (ve_utc / SECONDS_PER_DAY) + X509_DAYS_TO_UTC0;
+ mc.seconds = (ve_utc % SECONDS_PER_DAY);
+#endif
+#endif
+ mc.vtable->start_chain(&mc.vtable, NULL);
+ for (u = 0; u < VEC_LEN(chain); u ++) {
+ xc = &VEC_ELT(chain, u);
+ mc.vtable->start_cert(&mc.vtable, xc->data_len);
+ mc.vtable->append(&mc.vtable, xc->data, xc->data_len);
+ mc.vtable->end_cert(&mc.vtable);
+ switch (mc.err) {
+ case 0:
+ case BR_ERR_X509_OK:
+ case BR_ERR_X509_EXPIRED:
+ break;
+ default:
+ printf("u=%zu mc.err=%d\n", u, mc.err);
+ break;
+ }
+ }
+ err = mc.vtable->end_chain(&mc.vtable);
+ pk = NULL;
+ if (err) {
+ char date[12];
+
+ switch (err) {
+ case 54:
+ ve_error_set("Validation failed, certificate not valid as of %s",
+ gdate(date, sizeof(date), ve_utc));
+ break;
+ default: {
+ const char *err_desc = NULL;
+ const char *err_name = find_error_name(err, &err_desc);
+
+ if (err_name == NULL)
+ ve_error_set("Validation failed, err = %d",
+ err);
+ else
+ ve_error_set("Validation failed, %s (%s)",
+ err_desc, err_name);
+ break; }
+ }
+ } else {
+ tpk = mc.vtable->get_pkey(&mc.vtable, &usages);
+ if (tpk != NULL) {
+ pk = xpkeydup(tpk);
+ }
+ }
+ VEC_CLEAR(chain);
+ return (pk);
+}
+
+/*
+ * Check if digest of one of the certificates from verified chain
+ * is present in the forbidden database.
+ * Since UEFI allows to store three types of digests
+ * all of them have to be checked separately.
+ */
+static int
+check_forbidden_digests(br_x509_certificate *xcs, size_t num)
+{
+ unsigned char sha256_digest[br_sha256_SIZE];
+ unsigned char sha384_digest[br_sha384_SIZE];
+ unsigned char sha512_digest[br_sha512_SIZE];
+ void *tbs;
+ hash_data *digest;
+ br_hash_compat_context ctx;
+ const br_hash_class *md;
+ size_t tbs_len, i;
+ int have_sha256, have_sha384, have_sha512;
+
+ if (VEC_LEN(forbidden_digests) == 0)
+ return (0);
+
+ /*
+ * Iterate through certificates, extract their To-Be-Signed section,
+ * and compare its digest against the ones in the forbidden database.
+ */
+ while (num--) {
+ tbs = X509_to_tbs(xcs[num].data, &tbs_len);
+ if (tbs == NULL) {
+ printf("Failed to obtain TBS part of certificate\n");
+ return (1);
+ }
+ have_sha256 = have_sha384 = have_sha512 = 0;
+
+ for (i = 0; i < VEC_LEN(forbidden_digests); i++) {
+ digest = &VEC_ELT(forbidden_digests, i);
+ switch (digest->hash_size) {
+ case br_sha256_SIZE:
+ if (!have_sha256) {
+ have_sha256 = 1;
+ md = &br_sha256_vtable;
+ md->init(&ctx.vtable);
+ md->update(&ctx.vtable, tbs, tbs_len);
+ md->out(&ctx.vtable, sha256_digest);
+ }
+ if (!memcmp(sha256_digest,
+ digest->data,
+ br_sha256_SIZE))
+ return (1);
+
+ break;
+ case br_sha384_SIZE:
+ if (!have_sha384) {
+ have_sha384 = 1;
+ md = &br_sha384_vtable;
+ md->init(&ctx.vtable);
+ md->update(&ctx.vtable, tbs, tbs_len);
+ md->out(&ctx.vtable, sha384_digest);
+ }
+ if (!memcmp(sha384_digest,
+ digest->data,
+ br_sha384_SIZE))
+ return (1);
+
+ break;
+ case br_sha512_SIZE:
+ if (!have_sha512) {
+ have_sha512 = 1;
+ md = &br_sha512_vtable;
+ md->init(&ctx.vtable);
+ md->update(&ctx.vtable, tbs, tbs_len);
+ md->out(&ctx.vtable, sha512_digest);
+ }
+ if (!memcmp(sha512_digest,
+ digest->data,
+ br_sha512_SIZE))
+ return (1);
+
+ break;
+ }
+ }
+ }
+
+ return (0);
+}
+
+static br_x509_pkey *
+verify_signer(const char *certs,
+ br_name_element *elts, size_t num_elts)
+{
+ br_x509_certificate *xcs;
+ br_x509_pkey *pk;
+ size_t num;
+
+ pk = NULL;
+
+ ve_trust_init();
+ xcs = read_certificates(certs, &num);
+ if (xcs == NULL) {
+ ve_error_set("cannot read certificates\n");
+ return (NULL);
+ }
+
+ /*
+ * Check if either
+ * 1. There is a direct match between cert from forbidden_anchors
+ * and a cert from chain.
+ * 2. CA that signed the chain is found in forbidden_anchors.
+ */
+ if (VEC_LEN(forbidden_anchors) > 0)
+ pk = verify_signer_xcs(xcs, num, elts, num_elts, &forbidden_anchors);
+ if (pk != NULL) {
+ ve_error_set("Certificate is on forbidden list\n");
+ xfreepkey(pk);
+ pk = NULL;
+ goto out;
+ }
+
+ pk = verify_signer_xcs(xcs, num, elts, num_elts, &trust_anchors);
+ if (pk == NULL)
+ goto out;
+
+ /*
+ * Check if hash of tbs part of any certificate in chain
+ * is on the forbidden list.
+ */
+ if (check_forbidden_digests(xcs, num)) {
+ ve_error_set("Certificate hash is on forbidden list\n");
+ xfreepkey(pk);
+ pk = NULL;
+ }
+out:
+ free_certificates(xcs, num);
+ return (pk);
+}
+
+/**
+ * we need a hex digest including trailing newline below
+ */
+char *
+hexdigest(char *buf, size_t bufsz, unsigned char *foo, size_t foo_len)
+{
+ char const hex2ascii[] = "0123456789abcdef";
+ size_t i;
+
+ /* every binary byte is 2 chars in hex + newline + null */
+ if (bufsz < (2 * foo_len) + 2)
+ return (NULL);
+
+ for (i = 0; i < foo_len; i++) {
+ buf[i * 2] = hex2ascii[foo[i] >> 4];
+ buf[i * 2 + 1] = hex2ascii[foo[i] & 0x0f];
+ }
+
+ buf[i * 2] = 0x0A; /* we also want a newline */
+ buf[i * 2 + 1] = '\0';
+
+ return (buf);
+}
+
+/**
+ * @brief
+ * verify file against sigfile using pk
+ *
+ * When we generated the signature in sigfile,
+ * we hashed (sha256) file, and sent that to signing server
+ * which hashed (sha256) that hash.
+ *
+ * To verify we need to replicate that result.
+ *
+ * @param[in] pk
+ * br_x509_pkey
+ *
+ * @paramp[in] file
+ * file to be verified
+ *
+ * @param[in] sigfile
+ * signature (PEM encoded)
+ *
+ * @return NULL on error, otherwise content of file.
+ */
+#ifdef VE_ECDSA_SUPPORT
+static unsigned char *
+verify_ec(br_x509_pkey *pk, const char *file, const char *sigfile)
+{
+#ifdef VE_ECDSA_HASH_AGAIN
+ char *hex, hexbuf[br_sha512_SIZE * 2 + 2];
+#endif
+ unsigned char rhbuf[br_sha512_SIZE];
+ br_sha256_context ctx;
+ unsigned char *fcp, *scp;
+ size_t flen, slen, plen;
+ pem_object *po;
+ const br_ec_impl *ec;
+ br_ecdsa_vrfy vrfy;
+
+ if ((fcp = read_file(file, &flen)) == NULL)
+ return (NULL);
+ if ((scp = read_file(sigfile, &slen)) == NULL) {
+ free(fcp);
+ return (NULL);
+ }
+ if ((po = decode_pem(scp, slen, &plen)) == NULL) {
+ free(fcp);
+ free(scp);
+ return (NULL);
+ }
+ br_sha256_init(&ctx);
+ br_sha256_update(&ctx, fcp, flen);
+ br_sha256_out(&ctx, rhbuf);
+#ifdef VE_ECDSA_HASH_AGAIN
+ hex = hexdigest(hexbuf, sizeof(hexbuf), rhbuf, br_sha256_SIZE);
+ /* now hash that */
+ if (hex) {
+ br_sha256_init(&ctx);
+ br_sha256_update(&ctx, hex, strlen(hex));
+ br_sha256_out(&ctx, rhbuf);
+ }
+#endif
+ ec = br_ec_get_default();
+ vrfy = br_ecdsa_vrfy_asn1_get_default();
+ if (!vrfy(ec, rhbuf, br_sha256_SIZE, &pk->key.ec, po->data,
+ po->data_len)) {
+ free(fcp);
+ fcp = NULL;
+ }
+ free(scp);
+ return (fcp);
+}
+#endif
+
+#if defined(VE_RSA_SUPPORT) || defined(VE_OPENPGP_SUPPORT)
+/**
+ * @brief verify an rsa digest
+ *
+ * @return 0 on failure
+ */
+int
+verify_rsa_digest (br_rsa_public_key *pkey,
+ const unsigned char *hash_oid,
+ unsigned char *mdata, size_t mlen,
+ unsigned char *sdata, size_t slen)
+{
+ br_rsa_pkcs1_vrfy vrfy;
+ unsigned char vhbuf[br_sha512_SIZE];
+
+ vrfy = br_rsa_pkcs1_vrfy_get_default();
+
+ if (!vrfy(sdata, slen, hash_oid, mlen, pkey, vhbuf) ||
+ memcmp(vhbuf, mdata, mlen) != 0) {
+ return (0); /* fail */
+ }
+ return (1); /* ok */
+}
+#endif
+
+/**
+ * @brief
+ * verify file against sigfile using pk
+ *
+ * When we generated the signature in sigfile,
+ * we hashed (sha256) file, and sent that to signing server
+ * which hashed (sha256) that hash.
+ *
+ * Or (deprecated) we simply used sha1 hash directly.
+ *
+ * To verify we need to replicate that result.
+ *
+ * @param[in] pk
+ * br_x509_pkey
+ *
+ * @paramp[in] file
+ * file to be verified
+ *
+ * @param[in] sigfile
+ * signature (PEM encoded)
+ *
+ * @return NULL on error, otherwise content of file.
+ */
+#ifdef VE_RSA_SUPPORT
+static unsigned char *
+verify_rsa(br_x509_pkey *pk, const char *file, const char *sigfile)
+{
+ unsigned char rhbuf[br_sha512_SIZE];
+ const unsigned char *hash_oid;
+ const br_hash_class *md;
+ br_hash_compat_context mctx;
+ unsigned char *fcp, *scp;
+ size_t flen, slen, plen, hlen;
+ pem_object *po;
+
+ if ((fcp = read_file(file, &flen)) == NULL)
+ return (NULL);
+ if ((scp = read_file(sigfile, &slen)) == NULL) {
+ free(fcp);
+ return (NULL);
+ }
+ if ((po = decode_pem(scp, slen, &plen)) == NULL) {
+ free(fcp);
+ free(scp);
+ return (NULL);
+ }
+
+ switch (po->data_len) {
+#if defined(UNIT_TEST) && defined(VE_DEPRECATED_RSA_SHA1_SUPPORT)
+ case 256:
+ // this is our old deprecated sig method
+ md = &br_sha1_vtable;
+ hlen = br_sha1_SIZE;
+ hash_oid = BR_HASH_OID_SHA1;
+ break;
+#endif
+ default:
+ md = &br_sha256_vtable;
+ hlen = br_sha256_SIZE;
+ hash_oid = BR_HASH_OID_SHA256;
+ break;
+ }
+ md->init(&mctx.vtable);
+ md->update(&mctx.vtable, fcp, flen);
+ md->out(&mctx.vtable, rhbuf);
+ if (!verify_rsa_digest(&pk->key.rsa, hash_oid,
+ rhbuf, hlen, po->data, po->data_len)) {
+ free(fcp);
+ fcp = NULL;
+ }
+ free(scp);
+ return (fcp);
+}
+#endif
+
+/**
+ * @brief
+ * verify a signature and return content of signed file
+ *
+ * @param[in] sigfile
+ * file containing signature
+ * we derrive path of signed file and certificate change from
+ * this.
+ *
+ * @param[in] flags
+ * only bit 1 significant so far
+ *
+ * @return NULL on error otherwise content of signed file
+ */
+unsigned char *
+verify_sig(const char *sigfile, int flags)
+{
+ br_x509_pkey *pk;
+ br_name_element cn;
+ char cn_buf[80];
+ unsigned char cn_oid[4];
+ char pbuf[MAXPATHLEN];
+ char *cp;
+ unsigned char *ucp;
+ size_t n;
+
+ DEBUG_PRINTF(5, ("verify_sig: %s\n", sigfile));
+ n = strlcpy(pbuf, sigfile, sizeof(pbuf));
+ if (n > (sizeof(pbuf) - 5) || strcmp(&sigfile[n - 3], "sig") != 0)
+ return (NULL);
+ cp = strcpy(&pbuf[n - 3], "certs");
+ /*
+ * We want the commonName field
+ * the OID we want is 2,5,4,3 - but DER encoded
+ */
+ cn_oid[0] = 3;
+ cn_oid[1] = 0x55;
+ cn_oid[2] = 4;
+ cn_oid[3] = 3;
+ cn.oid = cn_oid;
+ cn.buf = cn_buf;
+ cn.len = sizeof(cn_buf);
+
+ pk = verify_signer(pbuf, &cn, 1);
+ if (!pk) {
+ printf("cannot verify: %s: %s\n", pbuf, ve_error_get());
+ return (NULL);
+ }
+ for (; cp > pbuf; cp--) {
+ if (*cp == '.') {
+ *cp = '\0';
+ break;
+ }
+ }
+ switch (pk->key_type) {
+#ifdef VE_ECDSA_SUPPORT
+ case BR_KEYTYPE_EC:
+ ucp = verify_ec(pk, pbuf, sigfile);
+ break;
+#endif
+#ifdef VE_RSA_SUPPORT
+ case BR_KEYTYPE_RSA:
+ ucp = verify_rsa(pk, pbuf, sigfile);
+ break;
+#endif
+ default:
+ ucp = NULL; /* not supported */
+ }
+ xfreepkey(pk);
+ if (!ucp) {
+ printf("Unverified %s (%s)\n", pbuf,
+ cn.status ? cn_buf : "unknown");
+ } else if ((flags & VEF_VERBOSE) != 0) {
+ printf("Verified %s signed by %s\n", pbuf,
+ cn.status ? cn_buf : "someone we trust");
+ }
+ return (ucp);
+}
+
+
+/**
+ * @brief verify hash matches
+ *
+ * We have finished hashing a file,
+ * see if we got the desired result.
+ *
+ * @param[in] ctx
+ * pointer to hash context
+ *
+ * @param[in] md
+ * pointer to hash class
+ *
+ * @param[in] path
+ * name of the file we are checking
+ *
+ * @param[in] want
+ * the expected result
+ *
+ * @param[in] hlen
+ * size of hash output
+ *
+ * @return 0 on success
+ */
+int
+ve_check_hash(br_hash_compat_context *ctx, const br_hash_class *md,
+ const char *path, const char *want, size_t hlen)
+{
+ char hexbuf[br_sha512_SIZE * 2 + 2];
+ unsigned char hbuf[br_sha512_SIZE];
+ char *hex;
+ int rc;
+ int n;
+
+ md->out(&ctx->vtable, hbuf);
+#ifdef VE_PCR_SUPPORT
+ ve_pcr_update(path, hbuf, hlen);
+#endif
+ hex = hexdigest(hexbuf, sizeof(hexbuf), hbuf, hlen);
+ if (!hex)
+ return (VE_FINGERPRINT_WRONG);
+ n = 2*hlen;
+ if ((rc = strncmp(hex, want, n))) {
+ ve_error_set("%s: %.*s != %.*s", path, n, hex, n, want);
+ rc = VE_FINGERPRINT_WRONG;
+ }
+ return (rc ? rc : VE_FINGERPRINT_OK);
+}
+
+#ifdef VE_HASH_KAT_STR
+static int
+test_hash(const br_hash_class *md, size_t hlen,
+ const char *hname, const char *s, size_t slen, const char *want)
+{
+ br_hash_compat_context mctx;
+
+ md->init(&mctx.vtable);
+ md->update(&mctx.vtable, s, slen);
+ return (ve_check_hash(&mctx, md, hname, want, hlen) != VE_FINGERPRINT_OK);
+}
+
+#endif
+
+#define ve_test_hash(n, N) \
+ printf("Testing hash: " #n "\t\t\t\t%s\n", \
+ test_hash(&br_ ## n ## _vtable, br_ ## n ## _SIZE, #n, \
+ VE_HASH_KAT_STR, VE_HASH_KAT_STRLEN(VE_HASH_KAT_STR), \
+ vh_ ## N) ? "Failed" : "Passed")
+
+/**
+ * @brief
+ * run self tests on hash and signature verification
+ *
+ * Test that the hash methods (SHA1 and SHA256) work.
+ * Test that we can verify a certificate for each supported
+ * Root CA.
+ *
+ * @return cached result.
+ */
+int
+ve_self_tests(void)
+{
+ static int once = -1;
+#ifdef VERIFY_CERTS_STR
+ br_x509_certificate *xcs;
+ br_x509_pkey *pk;
+ br_name_element cn;
+ char cn_buf[80];
+ unsigned char cn_oid[4];
+ size_t num;
+ size_t u;
+#endif
+
+ if (once >= 0)
+ return (once);
+ once = 0;
+
+ DEBUG_PRINTF(5, ("Self tests...\n"));
+#ifdef VE_HASH_KAT_STR
+#ifdef VE_SHA1_SUPPORT
+ ve_test_hash(sha1, SHA1);
+#endif
+#ifdef VE_SHA256_SUPPORT
+ ve_test_hash(sha256, SHA256);
+#endif
+#ifdef VE_SHA384_SUPPORT
+ ve_test_hash(sha384, SHA384);
+#endif
+#ifdef VE_SHA512_SUPPORT
+ ve_test_hash(sha512, SHA512);
+#endif
+#endif
+#ifdef VERIFY_CERTS_STR
+ xcs = parse_certificates(__DECONST(unsigned char *, VERIFY_CERTS_STR),
+ sizeof(VERIFY_CERTS_STR), &num);
+ if (xcs != NULL) {
+ /*
+ * We want the commonName field
+ * the OID we want is 2,5,4,3 - but DER encoded
+ */
+ cn_oid[0] = 3;
+ cn_oid[1] = 0x55;
+ cn_oid[2] = 4;
+ cn_oid[3] = 3;
+ cn.oid = cn_oid;
+ cn.buf = cn_buf;
+
+ for (u = 0; u < num; u ++) {
+ cn.len = sizeof(cn_buf);
+ if ((pk = verify_signer_xcs(&xcs[u], 1, &cn, 1, &trust_anchors)) != NULL) {
+ free_cert_contents(&xcs[u]);
+ once++;
+ printf("Testing verify certificate: %s\tPassed\n",
+ cn.status ? cn_buf : "");
+ xfreepkey(pk);
+ }
+ }
+ if (!once)
+ printf("Testing verify certificate:\t\t\tFailed\n");
+ xfree(xcs);
+ }
+#endif /* VERIFY_CERTS_STR */
+#ifdef VE_OPENPGP_SUPPORT
+ if (!openpgp_self_tests())
+ once++;
+#endif
+ return (once);
+}