diff options
Diffstat (limited to 'lib/libsecureboot/openpgp')
-rw-r--r-- | lib/libsecureboot/openpgp/Makefile.inc | 49 | ||||
-rw-r--r-- | lib/libsecureboot/openpgp/dearmor.c | 142 | ||||
-rw-r--r-- | lib/libsecureboot/openpgp/decode.c | 302 | ||||
-rw-r--r-- | lib/libsecureboot/openpgp/decode.h | 56 | ||||
-rw-r--r-- | lib/libsecureboot/openpgp/opgp_key.c | 413 | ||||
-rw-r--r-- | lib/libsecureboot/openpgp/opgp_sig.c | 492 | ||||
-rw-r--r-- | lib/libsecureboot/openpgp/packet.h | 81 |
7 files changed, 1535 insertions, 0 deletions
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, <ype); + + if (want > 0 && tag != want) + return (-1); + ptr++; + + len = rc = decode_len(&ptr, ltype); + hlen = (int)(ptr - nptr); + nptr = ptr + len; /* consume it */ + + if (decoder) + rc = decoder(tag, &ptr, len, decoder_arg); + *pptr = nptr; + nbytes -= (size_t)(hlen + len); + if (rc < 0) + return (rc); /* error */ + return ((int)nbytes); /* unconsumed data */ +} + +/** + * @brief decode a sub packet + * + * @sa rfc4880:5.2.3.1 + */ +unsigned char * +decode_subpacket(unsigned char **pptr, int *stag, int *sz) +{ + unsigned char *ptr; + int len; + + ptr = *pptr; + len = decode_len(&ptr, -1); + *sz = (int)(len + ptr - *pptr); + *pptr = ptr + len; + *stag = *ptr++; + return (ptr); +} 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, <ype); + 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); |