From owner-svn-src-all@freebsd.org Tue Feb 26 06:09:13 2019 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 70D06150C9A7; Tue, 26 Feb 2019 06:09:13 +0000 (UTC) (envelope-from sjg@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 11E656D0D2; Tue, 26 Feb 2019 06:09:13 +0000 (UTC) (envelope-from sjg@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id F253FD11; Tue, 26 Feb 2019 06:09:12 +0000 (UTC) (envelope-from sjg@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id x1Q69CCH093733; Tue, 26 Feb 2019 06:09:12 GMT (envelope-from sjg@FreeBSD.org) Received: (from sjg@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x1Q69Arq093718; Tue, 26 Feb 2019 06:09:10 GMT (envelope-from sjg@FreeBSD.org) Message-Id: <201902260609.x1Q69Arq093718@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: sjg set sender to sjg@FreeBSD.org using -f From: "Simon J. Gerraty" Date: Tue, 26 Feb 2019 06:09:10 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r344565 - in head/lib/libsecureboot: . h openpgp tests X-SVN-Group: head X-SVN-Commit-Author: sjg X-SVN-Commit-Paths: in head/lib/libsecureboot: . h openpgp tests X-SVN-Commit-Revision: 344565 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Rspamd-Queue-Id: 11E656D0D2 X-Spamd-Bar: -- Authentication-Results: mx1.freebsd.org X-Spamd-Result: default: False [-2.97 / 15.00]; local_wl_from(0.00)[FreeBSD.org]; NEURAL_HAM_MEDIUM(-1.00)[-0.998,0]; NEURAL_HAM_SHORT(-0.97)[-0.974,0]; NEURAL_HAM_LONG(-1.00)[-1.000,0]; ASN(0.00)[asn:11403, ipnet:2610:1c1:1::/48, country:US] X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 26 Feb 2019 06:09:13 -0000 Author: sjg Date: Tue Feb 26 06:09:10 2019 New Revision: 344565 URL: https://svnweb.freebsd.org/changeset/base/344565 Log: Add libsecureboot Used by loader and veriexec Depends on libbearssl Reviewed by: emaste Sponsored by: Juniper Networks Differential Revision: D16335 Added: head/lib/libsecureboot/ head/lib/libsecureboot/Makefile (contents, props changed) head/lib/libsecureboot/Makefile.depend (contents, props changed) head/lib/libsecureboot/Makefile.depend.host (contents, props changed) head/lib/libsecureboot/Makefile.inc (contents, props changed) head/lib/libsecureboot/Makefile.libsa.inc (contents, props changed) head/lib/libsecureboot/README.rst (contents, props changed) head/lib/libsecureboot/brf.c (contents, props changed) head/lib/libsecureboot/h/ head/lib/libsecureboot/h/libsecureboot.h (contents, props changed) head/lib/libsecureboot/h/verify_file.h (contents, props changed) head/lib/libsecureboot/libsecureboot-priv.h (contents, props changed) head/lib/libsecureboot/local.trust.mk (contents, props changed) head/lib/libsecureboot/openpgp/ head/lib/libsecureboot/openpgp/Makefile.inc (contents, props changed) head/lib/libsecureboot/openpgp/dearmor.c (contents, props changed) head/lib/libsecureboot/openpgp/decode.c (contents, props changed) head/lib/libsecureboot/openpgp/decode.h (contents, props changed) head/lib/libsecureboot/openpgp/opgp_key.c (contents, props changed) head/lib/libsecureboot/openpgp/opgp_sig.c (contents, props changed) head/lib/libsecureboot/openpgp/packet.h (contents, props changed) head/lib/libsecureboot/readfile.c (contents, props changed) head/lib/libsecureboot/tests/ head/lib/libsecureboot/tests/Makefile (contents, props changed) head/lib/libsecureboot/tests/Makefile.depend.host (contents, props changed) head/lib/libsecureboot/tests/tvo.c (contents, props changed) head/lib/libsecureboot/vectx.c (contents, props changed) head/lib/libsecureboot/veopen.c (contents, props changed) head/lib/libsecureboot/vepcr.c (contents, props changed) head/lib/libsecureboot/verify_file.c (contents, props changed) head/lib/libsecureboot/vesigned.c (contents, props changed) head/lib/libsecureboot/veta.c (contents, props changed) head/lib/libsecureboot/vets.c (contents, props changed) Added: head/lib/libsecureboot/Makefile ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/lib/libsecureboot/Makefile Tue Feb 26 06:09:10 2019 (r344565) @@ -0,0 +1,11 @@ +# $FreeBSD$ + +.include + +LIB= secureboot + +.include "Makefile.inc" + +INCS= h/libsecureboot.h + +.include Added: head/lib/libsecureboot/Makefile.depend ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/lib/libsecureboot/Makefile.depend Tue Feb 26 06:09:10 2019 (r344565) @@ -0,0 +1,17 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + gnu/lib/csu \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif Added: head/lib/libsecureboot/Makefile.depend.host ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/lib/libsecureboot/Makefile.depend.host Tue Feb 26 06:09:10 2019 (r344565) @@ -0,0 +1,12 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + lib/libstand \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif Added: head/lib/libsecureboot/Makefile.inc ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/lib/libsecureboot/Makefile.inc Tue Feb 26 06:09:10 2019 (r344565) @@ -0,0 +1,133 @@ +# $FreeBSD$ + +.if empty(BEARSSL) +.include "../libbearssl/Makefile.inc" +.endif + +.if !target(_${__this}_) +_${__this}_: + +libsecureboot_src:= ${.PARSEDIR} + +CFLAGS+= -I${libsecureboot_src}/h + +.PATH: ${.PARSEDIR} + +SRCS+= \ + readfile.c \ + brf.c \ + vesigned.c \ + vets.c + +.if ${.CURDIR:M*libsecureboot*} != "" +SRCS+= veta.c +.endif + +CFLAGS+= ${XCFLAGS.${.TARGET:T:R}:U} + +# we use a couple of files from ${BEARSSL}/tools +BRSSL_CFLAGS+= -I${BEARSSL}/tools +BRSSL_SRCS+= \ + ${BEARSSL}/tools/xmem.c \ + ${BEARSSL}/tools/vector.c + +# we do not need/want nested objdirs +OBJS_SRCS_FILTER = T R + +SRCS+= ${BRSSL_SRCS} + + +# extract the last cert from a chain (should be rootCA) +_LAST_PEM_USE: .USE + sed "1,`grep -n .-END ${.ALLSRC:M*.pem} | tail -2 | head -1 | sed 's,:.*,,'`d" ${.ALLSRC:M*.pem} > ${.TARGET} + +# extract 2nd last cert from chain - we use this for self-test +_2ndLAST_PEM_USE: .USE + sed -n "`grep -n .-BEGIN ${.ALLSRC:M*.pem} | tail -2 | \ + sed 's,:.*,,' | xargs | (read a b; echo $$a,$$(($$b - 1)))`p" ${.ALLSRC:M*.pem} > ${.TARGET} + +# list of hashes we support +VE_HASH_LIST?= SHA256 + +# list of signatures we support +# some people don't trust ECDSA +VE_SIGNATURE_LIST?= RSA + +# this list controls our search for signatures so will not be sorted +# note: for X509 signatures we assume we can replace the trailing +# "sig" with "certs" to find the certificate chain +# eg. for manifest.esig we use manifest.ecerts +VE_SIGNATURE_EXT_LIST?= sig + +# needs to be yes for FIPS 140-2 compliance +VE_SELF_TESTS?= no + +# rules to populate the [tv]*.pem files we use to generate ta.h +# and can add/alter VE_*_LIST as desired. +.-include "local.trust.mk" + +# this is what we use as our trust anchor +CFLAGS+= -I. -DTRUST_ANCHOR_STR=ta_PEM + +.if ${VE_SELF_TESTS} != "no" +XCFLAGS.vets+= -DVERIFY_CERTS_STR=vc_PEM +.endif + +# clean these up +VE_HASH_LIST:= ${VE_HASH_LIST:tu:O:u} +VE_SIGNATURE_LIST:= ${VE_SIGNATURE_LIST:tu:O:u} + +# define what we are supporting +CFLAGS+= ${VE_HASH_LIST:@H@-DVE_$H_SUPPORT@} \ + ${VE_SIGNATURE_LIST:@S@-DVE_$S_SUPPORT@} + +.if ${VE_SIGNATURE_LIST:MOPENPGP} != "" +.include "openpgp/Makefile.inc" +.endif + +.if ${VE_SELF_TESTS} != "no" +# The input used for hash KATs +VE_HASH_KAT_STR?= vc_PEM + +XCFLAGS.vets+= -DVE_HASH_KAT_STR=${VE_HASH_KAT_STR} +.endif + +# Generate ta.h containing one or more PEM encoded trust anchors in ta_PEM. +# +# If we are doing self-tests, we define another arrary vc_PEM +# containing certificates that we can verify for each trust anchor. +# This is typically a subordinate CA cert. +# Finally we generate a hash of vc_PEM using each supported hash method +# to use as a Known Answer Test (needed for FIPS 140-2) +# +vets.o vets.po vets.pico: ta.h +ta.h: ${.ALLTARGETS:M[tv]*pem:O:u} + @( echo '/* Autogenerated - DO NOT EDIT!!! */'; echo; \ + cat ${.ALLSRC:N*crl*:Mt*.pem} /dev/null | \ + file2c -sx 'static const char ta_PEM[] = {' '};'; \ + echo "${.newline}${VE_HASH_LIST:@H@static char vh_$H[] = \"`cat ${.ALLSRC:N*crl*:Mv*.pem} | ${$H:U${H:tl}}`\";${.newline}@}"; ) > ${.TARGET} +.if ${VE_SELF_TESTS} != "no" + ( cat ${.ALLSRC:N*crl*:Mv*.pem} /dev/null | \ + file2c -sx 'static const char vc_PEM[] = {' '};'; echo ) >> ${.TARGET} +.endif +.if !empty(BUILD_UTC_FILE) + echo '#define BUILD_UTC ${${STAT:Ustat} -f %m ${BUILD_UTC_FILE}:L:sh}' >> ${.TARGET} ${.OODATE:MNOMETA_CMP} +.endif + +# This header records our preference for signature extensions. +vesigned.o vesigned.po vesigned.pico: vse.h +vse.h: + @( echo '/* Autogenerated - DO NOT EDIT!!! */'; echo; \ + echo "static const char *signature_exts[] = {"; \ + echo '${VE_SIGNATURE_EXT_LIST:@e@"$e",${.newline}@}'; \ + echo 'NULL };' ) > ${.TARGET} + + +.for s in ${BRSSL_SRCS} brf.c vets.c veta.c +.ifdef BRSSL_SED +$s: brssl.h +.endif +XCFLAGS.${s:R}+= ${BRSSL_CFLAGS} +.endfor + +.endif Added: head/lib/libsecureboot/Makefile.libsa.inc ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/lib/libsecureboot/Makefile.libsa.inc Tue Feb 26 06:09:10 2019 (r344565) @@ -0,0 +1,40 @@ +# $FreeBSD$ + +BRSSL_CFLAGS+= -DNO_STDIO + +.include "Makefile.inc" + +# for "measured boot" +# loader puts the equivalent of TPM's PCR register into kenv +# this is not as good but *way* simpler than talking to TPM +CFLAGS+= -DVE_PCR_SUPPORT + +# sources that only apply to libsa +SRCS+= \ + vectx.c \ + veopen.c \ + vepcr.c \ + verify_file.c \ + +# this is the list of paths (relative to a file +# that we need to verify) used to find a signed manifest. +# the signature extensions in VE_SIGNATURE_EXT_LIST +# will be applied to each. +VE_MANIFEST_LIST?= manifest ../manifest + +verify_file.o: manifests.h +manifests.h: + @( echo '/* Autogenerated - DO NOT EDIT!!! */'; echo; \ + echo "static const char *manifest_names[] = {"; \ + echo '${VE_MANIFEST_LIST:@m@"$m",${.newline}@}'; \ + echo 'NULL };' ) > ${.TARGET} + +XCFLAGS.verify_file+= \ + -DVE_DEBUG_LEVEL=${VE_DEBUG_LEVEL:U0} \ + -DVE_VERBOSE_DEFAULT=${VE_VERBOSE_DEFAULT:U0} \ + +.if !empty(MANIFEST_SKIP_ALWAYS) +XCFLAGS.verify_file+= -DMANIFEST_SKIP_ALWAYS=\"${MANIFEST_SKIP_ALWAYS}\" +.elif !empty(MANIFEST_SKIP) +XCFLAGS.verify_file+= -DMANIFEST_SKIP=\"${MANIFEST_SKIP}\" +.endif Added: head/lib/libsecureboot/README.rst ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/lib/libsecureboot/README.rst Tue Feb 26 06:09:10 2019 (r344565) @@ -0,0 +1,134 @@ +libsecureboot +************* + +This library depends one way or another on verifying digital signatures. +To do that, the necessary trust anchors need to be available. + +The simplest (and most attractive for an embedded system) is to +capture them in this library. + +The makefile ``local.trust.mk`` is responsible for doing that. +The file provided is just an example and depends on the environment +here at Juniper. + +Within Juniper we use signing servers, which apart from signing things +provide access to the necessary trust anchors. +That signing server is freely available - see +http://www.crufty.net/sjg/docs/signing-server.htm + +X.509 certificates chains offer a lot of flexibility over time and are +a great solution for an embedded vendor like Juniper or even +FreeBSD.org, but are probably overkill for personal or small site use. + +Setting up a CA for this is rather involved so I'll just provide a +link below to suitable tutorial below. + +Using OpenPGP is much simpler. + + +OpenPGP +======== + +This is very simple to setup and use. + +An RSA key pair can be generated with:: + + GNUPGHOME=$PWD/.gnupg gpg --openpgp \ + --quick-generate-key --batch --passphrase '' "keyname" RSA + +The use of ``GNUPGHOME=$PWD/.gnupg`` just avoids messing with personal +keyrings. +We can list the resulting key:: + + GNUPGHOME=$PWD/.gnupg gpg --openpgp --list-keys + + gpg: WARNING: unsafe permissions on homedir + '/h/sjg/openpgp/.gnupg' + gpg: Warning: using insecure memory! + /h/sjg/openpgp/.gnupg/pubring.kbx + --------------------------------- + pub rsa2048 2018-03-26 [SC] [expires: 2020-03-25] + AB39B111E40DD019E0E7C171ACA72B4719FD2523 + uid [ultimate] OpenPGPtest + +The ``keyID`` we want later will be the last 8 octets +(``ACA72B4719FD2523``) +This is what we will use for looking up the key. + +We can then export the private and public keys:: + + GNUPGHOME=$PWD/.gnupg gpg --openpgp \ + --export --armor > ACA72B4719FD2523.pub.asc + GNUPGHOME=$PWD/.gnupg gpg --openpgp \ + --export-secret-keys --armor > ACA72B4719FD2523.sec.asc + +The public key ``ACA72B4719FD2523.pub.asc`` is what we want to +embed in this library. +If you look at the ``ta_asc.h`` target in ``openpgp/Makefile.inc`` +we want the trust anchor in a file named ``t*.asc`` +eg. ``ta_openpgp.asc``. + +The ``ta_asc.h`` target will capture all such ``t*.asc`` into that +header. + +Signatures +---------- + +We expect ascii armored (``.asc``) detached signatures. +Eg. signature for ``manifest`` would be in ``manifest.asc`` + +We only support version 4 signatures using RSA (the default for ``gpg``). + + +OpenSSL +======== + +The basic idea here is to setup a private CA. + +There are lots of good tutorials on available on this topic; +just google *setup openssl ca*. +A good example is https://jamielinux.com/docs/openssl-certificate-authority/ + +All we need for this library is a copy of the PEM encoded root CA +certificate (trust anchor). This is expected to be in a file named +``t*.pem`` eg. ``ta_rsa.pem``. + +The ``ta.h`` target in ``Makefile.inc`` will combine all such +``t*.pem`` files into that header. + +Signatures +---------- + +For Junos we currently use EC DSA signatures with file extension +``.esig`` so the signature for ``manifest`` would be ``manifest.esig`` + +This was the first signature method we used with the remote signing +servers and it ends up being a signature of a hash. +Ie. client sends a hash which during signing gets hashed again. +So for Junos we define VE_ECDSA_HASH_AGAIN which causes ``verify_ec`` +to hash again. + +Otherwise our EC DSA and RSA signatures are the default used by +OpenSSL - an original design goal was that a customer could verify our +signatures using nothing but an ``openssl`` binary. + + +Self tests +========== + +If you want the ``loader`` to perform self-test of a given signature +verification method on startup (a must for FIPS 140-2 certification) +you need to provide a suitable file signed by each supported trust +anchor. + +These should be stored in files with names that start with ``v`` and +have the same extension as the corresponding trust anchor. +Eg. for ``ta_openpgp.asc`` we use ``vc_openpgp.asc`` +and for ``ta_rsa.pem`` we use ``vc_rsa.pem``. + +Note for the X.509 case we simply extract the 2nd last certificate +from the relevant chain - which is sure to be a valid certificate +signed by the corresponding trust anchor. + +-------------------- +$FreeBSD$ Added: head/lib/libsecureboot/brf.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/lib/libsecureboot/brf.c Tue Feb 26 06:09:10 2019 (r344565) @@ -0,0 +1,403 @@ +// The functions here are derrived from BearSSL/tools/*.c +// When that is refactored suitably we can use them directly. +/* + * Copyright (c) 2016 Thomas Pornin + * + * 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 +__FBSDID("$FreeBSD$"); + +#define NEED_BRSSL_H +#include "libsecureboot-priv.h" +#include + + +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; + } +} Added: head/lib/libsecureboot/h/libsecureboot.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/lib/libsecureboot/h/libsecureboot.h Tue Feb 26 06:09:10 2019 (r344565) @@ -0,0 +1,94 @@ +/*- + * Copyright (c) 2017-2018, Juniper Networks, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * $FreeBSD$ + */ +#ifndef _LIBSECUREBOOT_H_ +#define _LIBSECUREBOOT_H_ + +#include +#ifdef _STANDALONE +#include +#else +#include +#include +#include +#include +#include +#include +#endif + +#include + +#ifndef NEED_BRSSL_H +unsigned char * read_file(const char *, size_t *); +#endif + +extern int DebugVe; + +#define DEBUG_PRINTF(n, x) if (DebugVe >= n) printf x + +int ve_trust_init(void); +int ve_trust_add(const char *); +void ve_debug_set(int); +void ve_utc_set(time_t utc); +char *ve_error_get(void); +int ve_error_set(const char *, ...) __printflike(1,2); +int ve_self_tests(void); + +void fingerprint_info_add(const char *, const char *, const char *, + const char *, struct stat *); + +int ve_check_hash(br_hash_compat_context *, const br_hash_class *, + const char *, const char *, size_t); + +struct vectx; +struct vectx* vectx_open(int, const char *, off_t, struct stat *, int *); +ssize_t vectx_read(struct vectx *, void *, size_t); +off_t vectx_lseek(struct vectx *, off_t, int); +int vectx_close(struct vectx *); + +char * hexdigest(char *, size_t, unsigned char *, size_t); +int verify_fd(int, const char *, off_t, struct stat *); +int verify_open(const char *, int); + +unsigned char *verify_signed(const char *, int); +unsigned char *verify_sig(const char *, int); +unsigned char *verify_asc(const char *, int); /* OpenPGP */ + +void ve_pcr_init(void); +void ve_pcr_update(unsigned char *, size_t); +ssize_t ve_pcr_get(unsigned char *, size_t); + +/* flags for verify_{asc,sig,signed} */ +#define VEF_VERBOSE 1 + +#define VE_FINGERPRINT_OK 1 +/* errors from verify_fd */ +#define VE_FINGERPRINT_NONE -2 +#define VE_FINGERPRINT_WRONG -3 +#define VE_FINGERPRINT_UNKNOWN -4 /* may not be an error */ + +#endif /* _LIBSECUREBOOT_H_ */ Added: head/lib/libsecureboot/h/verify_file.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/lib/libsecureboot/h/verify_file.h Tue Feb 26 06:09:10 2019 (r344565) @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 2017-2018, Juniper Networks, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _VERIFY_FILE_H_ +#define _VERIFY_FILE_H_ + +#define VE_GUESS -1 /* let verify_file work it out */ +#define VE_TRY 0 /* we don't mind if unverified */ +#define VE_WANT 1 /* we want this verified */ +#define VE_MUST 2 /* this must be verified */ + +#define VE_VERIFIED 1 /* all good */ +#define VE_UNVERIFIED_OK 0 /* not verified but that's ok */ +#define VE_NOT_VERIFYING 2 /* we are not verifying */ + +struct stat; + +void ve_debug_set(int); +int ve_status_get(int); +int load_manifest(const char *, const char *, const char *, struct stat *); +int verify_file(int, const char *, off_t, int); +void verify_pcr_export(void); + +#endif /* _VERIFY_FILE_H_ */ Added: head/lib/libsecureboot/libsecureboot-priv.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/lib/libsecureboot/libsecureboot-priv.h Tue Feb 26 06:09:10 2019 (r344565) @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2017, Juniper Networks, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * $FreeBSD$ + */ +#ifndef _LIBSECUREBOOT_PRIV_H_ +#define _LIBSECUREBOOT_PRIV_H_ + +/* public api */ +#include "libsecureboot.h" + +size_t ve_trust_anchors_add(br_x509_certificate *, size_t); +char *fingerprint_info_lookup(int, const char *); + +br_x509_certificate * parse_certificates(unsigned char *, size_t, size_t *); +int certificate_to_trust_anchor_inner(br_x509_trust_anchor *, + br_x509_certificate *); + +int verify_rsa_digest(br_rsa_public_key *pkey, + const unsigned char *hash_oid, + unsigned char *mdata, size_t mlen, + unsigned char *sdata, size_t slen); + +int openpgp_self_tests(void); + +#endif /* _LIBSECUREBOOT_PRIV_H_ */ Added: head/lib/libsecureboot/local.trust.mk ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/lib/libsecureboot/local.trust.mk Tue Feb 26 06:09:10 2019 (r344565) @@ -0,0 +1,114 @@ +# $FreeBSD$ + +# Consider this file an example. +# +# For Junos this is how we obtain trust anchor .pems +# the signing server (http://www.crufty.net/sjg/blog/signing-server.htm) +# for each key will provide the appropriate certificate chain on request + +# force these for Junos +MANIFEST_SKIP_ALWAYS= boot +VE_HASH_LIST= \ + SHA1 \ + SHA256 \ + SHA384 + +VE_SIGNATURE_LIST= \ + ECDSA + +VE_SIGNATURE_EXT_LIST= \ + esig + +VE_SELF_TESTS= yes + +.if ${MACHINE} == "host" && ${.CURDIR:T} == "tests" +# for testing +VE_HASH_LIST+= \ + SHA512 + *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***