freebsd-src/lib/libsecureboot/openpgp/opgp_sig.c
Warner Losh dc36d6f9bb lib: Remove ancient SCCS tags.
Remove ancient SCCS tags from the tree, automated scripting, with two
minor fixup to keep things compiling. All the common forms in the tree
were removed with a perl script.

Sponsored by:		Netflix
2023-11-26 22:23:28 -07:00

482 lines
10 KiB
C

/*-
* 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;
default:
warnx("unsupported hash algorithm: %s", hname);
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);
}