diff options
Diffstat (limited to 'lib/libsecureboot/openpgp/opgp_sig.c')
-rw-r--r-- | lib/libsecureboot/openpgp/opgp_sig.c | 492 |
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); +} |