aboutsummaryrefslogtreecommitdiff
path: root/lib/libsecureboot/openpgp/opgp_sig.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libsecureboot/openpgp/opgp_sig.c')
-rw-r--r--lib/libsecureboot/openpgp/opgp_sig.c492
1 files changed, 492 insertions, 0 deletions
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);
+}