mirror of
https://github.com/opnsense/src.git
synced 2026-06-08 16:22:46 -04:00
pkg: finish adding the ECC signer and signature type bits
Signature types need to be parsed out of the key/signature information
that we are presented with from the files we download. We use that to
understand whicher signer we need to dispatch to.
The ECC signer is more-or-less lifted from pkg(8), with some changes to
slim it down for pkg(7).
Reviewed by: bapt
(cherry picked from commit 3d0a0dda3a)
This commit is contained in:
parent
cb09fc9a60
commit
8aeeab4331
4 changed files with 711 additions and 20 deletions
|
|
@ -22,11 +22,14 @@ CONFSNAME_${PKGCONF}= ${PKGCONF:C/\.conf.+$/.conf/}
|
|||
CONFSDIR= /etc/pkg
|
||||
CONFSMODE= 644
|
||||
PROG= pkg
|
||||
SRCS= pkg.c rsa.c dns_utils.c config.c hash.c
|
||||
SRCS= pkg.c rsa.c dns_utils.c config.c ecc.c hash.c
|
||||
MAN= pkg.7
|
||||
|
||||
CFLAGS+=-I${SRCTOP}/contrib/libucl/include
|
||||
.PATH: ${SRCTOP}/contrib/libucl/include
|
||||
LIBADD= archive fetch ucl crypto ssl util md
|
||||
LIBADD= archive der fetch pkgecc ucl crypto ssl util md
|
||||
|
||||
CFLAGS+=-I${SRCTOP}/contrib/libder/libder
|
||||
CFLAGS+=-I${SRCTOP}/crypto/libecc/include
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
|
|
|||
606
usr.sbin/pkg/ecc.c
Normal file
606
usr.sbin/pkg/ecc.c
Normal file
|
|
@ -0,0 +1,606 @@
|
|||
/*-
|
||||
* Copyright (c) 2011-2013 Baptiste Daroussin <bapt@FreeBSD.org>
|
||||
* Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
* Copyright (c) 2021 Kyle Evans <kevans@FreeBSD.org>
|
||||
*
|
||||
* 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
|
||||
* in this position and unchanged.
|
||||
* 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 AUTHOR(S) ``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 AUTHOR(S) 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/stat.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include <libder.h>
|
||||
|
||||
#define WITH_STDLIB
|
||||
#include <libecc/libsig.h>
|
||||
#undef WITH_STDLIB
|
||||
|
||||
#include "pkg.h"
|
||||
#include "hash.h"
|
||||
|
||||
/* libpkg shim */
|
||||
#define STREQ(l, r) (strcmp(l, r) == 0)
|
||||
|
||||
struct ecc_sign_ctx {
|
||||
struct pkgsign_ctx sctx;
|
||||
ec_params params;
|
||||
ec_key_pair keypair;
|
||||
ec_alg_type sig_alg;
|
||||
hash_alg_type sig_hash;
|
||||
bool loaded;
|
||||
};
|
||||
|
||||
/* Grab the ossl context from a pkgsign_ctx. */
|
||||
#define ECC_CCTX(c) (__containerof(c, const struct ecc_sign_ctx, sctx))
|
||||
#define ECC_CTX(c) (__containerof(c, struct ecc_sign_ctx, sctx))
|
||||
|
||||
#define PUBKEY_UNCOMPRESSED 0x04
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a,b) (((a)>(b))?(a):(b))
|
||||
#endif
|
||||
|
||||
static const uint8_t oid_ecpubkey[] = \
|
||||
{ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01 };
|
||||
|
||||
static const uint8_t oid_secp[] = \
|
||||
{ 0x2b, 0x81, 0x04, 0x00 };
|
||||
static const uint8_t oid_secp256k1[] = \
|
||||
{ 0x2b, 0x81, 0x04, 0x00, 0x0a };
|
||||
static const uint8_t oid_brainpoolP[] = \
|
||||
{ 0x2b, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01 };
|
||||
|
||||
#define ENTRY(name, params) { #name, sizeof(#name) - 1, params }
|
||||
static const struct pkgkey_map_entry {
|
||||
const char *name;
|
||||
size_t namesz;
|
||||
const ec_str_params *params;
|
||||
} pkgkey_map[] = {
|
||||
ENTRY(WEI25519, &wei25519_str_params),
|
||||
ENTRY(SECP256K1, &secp256k1_str_params),
|
||||
ENTRY(SECP384R1, &secp384r1_str_params),
|
||||
ENTRY(SECP512R1, &secp521r1_str_params),
|
||||
ENTRY(BRAINPOOLP256R1, &brainpoolp256r1_str_params),
|
||||
ENTRY(BRAINPOOLP256T1, &brainpoolp256t1_str_params),
|
||||
ENTRY(BRAINPOOLP320R1, &brainpoolp320r1_str_params),
|
||||
ENTRY(BRAINPOOLP320T1, &brainpoolp320t1_str_params),
|
||||
ENTRY(BRAINPOOLP384R1, &brainpoolp384r1_str_params),
|
||||
ENTRY(BRAINPOOLP384T1, &brainpoolp384t1_str_params),
|
||||
ENTRY(BRAINPOOLP512R1, &brainpoolp512r1_str_params),
|
||||
ENTRY(BRAINPOOLP512T1, &brainpoolp512t1_str_params),
|
||||
};
|
||||
|
||||
static const char pkgkey_app[] = "pkg";
|
||||
static const char pkgkey_signer[] = "ecc";
|
||||
|
||||
static const ec_str_params *
|
||||
ecc_pkgkey_params(const uint8_t *curve, size_t curvesz)
|
||||
{
|
||||
const struct pkgkey_map_entry *entry;
|
||||
|
||||
for (size_t i = 0; i < nitems(pkgkey_map); i++) {
|
||||
entry = &pkgkey_map[i];
|
||||
if (curvesz != entry->namesz)
|
||||
continue;
|
||||
if (memcmp(curve, entry->name, curvesz) == 0)
|
||||
return (entry->params);
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
ecc_read_pkgkey(struct libder_object *root, ec_params *params, int public,
|
||||
uint8_t *rawkey, size_t *rawlen)
|
||||
{
|
||||
struct libder_object *obj;
|
||||
const uint8_t *data;
|
||||
const ec_str_params *sparams;
|
||||
size_t datasz;
|
||||
int ret;
|
||||
|
||||
if (libder_obj_type_simple(root) != BT_SEQUENCE)
|
||||
return (1);
|
||||
|
||||
/* Application */
|
||||
obj = libder_obj_child(root, 0);
|
||||
if (obj == NULL || libder_obj_type_simple(obj) != BT_UTF8STRING)
|
||||
return (1);
|
||||
data = libder_obj_data(obj, &datasz);
|
||||
if (datasz != sizeof(pkgkey_app) - 1 ||
|
||||
memcmp(data, pkgkey_app, datasz) != 0)
|
||||
return (1);
|
||||
|
||||
/* Version */
|
||||
obj = libder_obj_child(root, 1);
|
||||
if (obj == NULL || libder_obj_type_simple(obj) != BT_INTEGER)
|
||||
return (1);
|
||||
data = libder_obj_data(obj, &datasz);
|
||||
if (datasz != 1 || *data != 1 /* XXX */)
|
||||
return (1);
|
||||
|
||||
/* Signer */
|
||||
obj = libder_obj_child(root, 2);
|
||||
if (obj == NULL || libder_obj_type_simple(obj) != BT_UTF8STRING)
|
||||
return (1);
|
||||
data = libder_obj_data(obj, &datasz);
|
||||
if (datasz != sizeof(pkgkey_signer) - 1 ||
|
||||
memcmp(data, pkgkey_signer, datasz) != 0)
|
||||
return (1);
|
||||
|
||||
/* KeyType (curve) */
|
||||
obj = libder_obj_child(root, 3);
|
||||
if (obj == NULL || libder_obj_type_simple(obj) != BT_UTF8STRING)
|
||||
return (1);
|
||||
data = libder_obj_data(obj, &datasz);
|
||||
sparams = ecc_pkgkey_params(data, datasz);
|
||||
if (sparams == NULL)
|
||||
return (1);
|
||||
|
||||
ret = import_params(params, sparams);
|
||||
if (ret != 0)
|
||||
return (1);
|
||||
|
||||
/* Public? */
|
||||
obj = libder_obj_child(root, 4);
|
||||
if (obj == NULL || libder_obj_type_simple(obj) != BT_BOOLEAN)
|
||||
return (1);
|
||||
data = libder_obj_data(obj, &datasz);
|
||||
if (datasz != 1 || !data[0] != !public)
|
||||
return (1);
|
||||
|
||||
/* Key */
|
||||
obj = libder_obj_child(root, 5);
|
||||
if (obj == NULL || libder_obj_type_simple(obj) != BT_BITSTRING)
|
||||
return (1);
|
||||
data = libder_obj_data(obj, &datasz);
|
||||
if (datasz <= 2 || data[0] != 0 || data[1] != PUBKEY_UNCOMPRESSED)
|
||||
return (1);
|
||||
|
||||
data += 2;
|
||||
datasz -= 2;
|
||||
|
||||
if (datasz > *rawlen)
|
||||
return (1);
|
||||
|
||||
|
||||
memcpy(rawkey, data, datasz);
|
||||
*rawlen = datasz;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
ecc_extract_signature(const uint8_t *sig, size_t siglen, uint8_t *rawsig,
|
||||
size_t rawlen)
|
||||
{
|
||||
struct libder_ctx *ctx;
|
||||
struct libder_object *obj, *root;
|
||||
const uint8_t *sigdata;
|
||||
size_t compsz, datasz, sigoff;
|
||||
int rc;
|
||||
|
||||
ctx = libder_open();
|
||||
if (ctx == NULL)
|
||||
return (1);
|
||||
|
||||
rc = 1;
|
||||
root = libder_read(ctx, sig, &siglen);
|
||||
if (root == NULL || libder_obj_type_simple(root) != BT_SEQUENCE)
|
||||
goto out;
|
||||
|
||||
/* Descend into the sequence's payload, extract both numbers. */
|
||||
compsz = rawlen / 2;
|
||||
sigoff = 0;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
obj = libder_obj_child(root, i);
|
||||
if (libder_obj_type_simple(obj) != BT_INTEGER)
|
||||
goto out;
|
||||
|
||||
sigdata = libder_obj_data(obj, &datasz);
|
||||
if (datasz < 2 || datasz > compsz + 1)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* We may see an extra lead byte if our high bit of the first
|
||||
* byte was set, since these numbers are positive by definition.
|
||||
*/
|
||||
if (sigdata[0] == 0 && (sigdata[1] & 0x80) != 0) {
|
||||
sigdata++;
|
||||
datasz--;
|
||||
}
|
||||
|
||||
/* Sanity check: don't overflow the output. */
|
||||
if (sigoff + datasz > rawlen)
|
||||
goto out;
|
||||
|
||||
/* Padding to the significant end if we're too small. */
|
||||
if (datasz < compsz) {
|
||||
memset(&rawsig[sigoff], 0, compsz - datasz);
|
||||
sigoff += compsz - datasz;
|
||||
}
|
||||
|
||||
memcpy(&rawsig[sigoff], sigdata, datasz);
|
||||
sigoff += datasz;
|
||||
}
|
||||
|
||||
/* Sanity check: must have exactly the required # of signature bits. */
|
||||
rc = (sigoff == rawlen) ? 0 : 1;
|
||||
|
||||
out:
|
||||
libder_obj_free(root);
|
||||
libder_close(ctx);
|
||||
return (rc);
|
||||
}
|
||||
|
||||
static int
|
||||
ecc_extract_pubkey_string(const uint8_t *data, size_t datalen, uint8_t *rawkey,
|
||||
size_t *rawlen)
|
||||
{
|
||||
uint8_t prefix, usebit;
|
||||
|
||||
if (datalen <= 2)
|
||||
return (1);
|
||||
|
||||
usebit = *data++;
|
||||
datalen--;
|
||||
|
||||
if (usebit != 0)
|
||||
return (1);
|
||||
|
||||
prefix = *data++;
|
||||
datalen--;
|
||||
|
||||
if (prefix != PUBKEY_UNCOMPRESSED)
|
||||
return (1);
|
||||
|
||||
if (datalen > *rawlen)
|
||||
return (1);
|
||||
|
||||
memcpy(rawkey, data, datalen);
|
||||
*rawlen = datalen;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
ecc_extract_key_params(const uint8_t *oid, size_t oidlen,
|
||||
ec_params *rawparams)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (oidlen >= sizeof(oid_secp) &&
|
||||
memcmp(oid, oid_secp, sizeof(oid_secp)) >= 0) {
|
||||
oid += sizeof(oid_secp);
|
||||
oidlen -= sizeof(oid_secp);
|
||||
|
||||
if (oidlen != 1)
|
||||
return (1);
|
||||
|
||||
ret = -1;
|
||||
switch (*oid) {
|
||||
case 0x0a: /* secp256k1 */
|
||||
ret = import_params(rawparams, &secp256k1_str_params);
|
||||
break;
|
||||
case 0x22: /* secp384r1 */
|
||||
ret = import_params(rawparams, &secp384r1_str_params);
|
||||
break;
|
||||
case 0x23: /* secp521r1 */
|
||||
ret = import_params(rawparams, &secp521r1_str_params);
|
||||
break;
|
||||
default:
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
return (0);
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (oidlen >= sizeof(oid_brainpoolP) &&
|
||||
memcmp(oid, oid_brainpoolP, sizeof(oid_brainpoolP)) >= 0) {
|
||||
oid += sizeof(oid_brainpoolP);
|
||||
oidlen -= sizeof(oid_brainpoolP);
|
||||
|
||||
if (oidlen != 1)
|
||||
return (1);
|
||||
|
||||
ret = -1;
|
||||
switch (*oid) {
|
||||
case 0x07: /* brainpoolP256r1 */
|
||||
ret = import_params(rawparams, &brainpoolp256r1_str_params);
|
||||
break;
|
||||
case 0x08: /* brainpoolP256t1 */
|
||||
ret = import_params(rawparams, &brainpoolp256t1_str_params);
|
||||
break;
|
||||
case 0x09: /* brainpoolP320r1 */
|
||||
ret = import_params(rawparams, &brainpoolp320r1_str_params);
|
||||
break;
|
||||
case 0x0a: /* brainpoolP320t1 */
|
||||
ret = import_params(rawparams, &brainpoolp320t1_str_params);
|
||||
break;
|
||||
case 0x0b: /* brainpoolP384r1 */
|
||||
ret = import_params(rawparams, &brainpoolp384r1_str_params);
|
||||
break;
|
||||
case 0x0c: /* brainpoolP384t1 */
|
||||
ret = import_params(rawparams, &brainpoolp384t1_str_params);
|
||||
break;
|
||||
case 0x0d: /* brainpoolP512r1 */
|
||||
ret = import_params(rawparams, &brainpoolp512r1_str_params);
|
||||
break;
|
||||
case 0x0e: /* brainpoolP512t1 */
|
||||
ret = import_params(rawparams, &brainpoolp512t1_str_params);
|
||||
break;
|
||||
default:
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
return (0);
|
||||
return (1);
|
||||
}
|
||||
|
||||
#ifdef ECC_DEBUG
|
||||
for (size_t i = 0; i < oidlen; i++) {
|
||||
fprintf(stderr, "%.02x ", oid[i]);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* On entry, *rawparams should point to an ec_params that we can import the
|
||||
* key parameters to. We'll either do that, or we'll set it to NULL if we could
|
||||
* not deduce the curve.
|
||||
*/
|
||||
static int
|
||||
ecc_extract_pubkey(FILE *keyfp, const uint8_t *key, size_t keylen,
|
||||
uint8_t *rawkey, size_t *rawlen, ec_params *rawparams)
|
||||
{
|
||||
const uint8_t *oidp;
|
||||
struct libder_ctx *ctx;
|
||||
struct libder_object *keydata, *oid, *params, *root;
|
||||
size_t oidsz;
|
||||
int rc;
|
||||
|
||||
ctx = libder_open();
|
||||
if (ctx == NULL)
|
||||
return (1);
|
||||
|
||||
rc = 1;
|
||||
assert((keyfp != NULL) ^ (key != NULL));
|
||||
if (keyfp != NULL) {
|
||||
root = libder_read_file(ctx, keyfp, &keylen);
|
||||
} else {
|
||||
root = libder_read(ctx, key, &keylen);
|
||||
}
|
||||
|
||||
if (root == NULL || libder_obj_type_simple(root) != BT_SEQUENCE)
|
||||
goto out;
|
||||
|
||||
params = libder_obj_child(root, 0);
|
||||
|
||||
if (params == NULL) {
|
||||
goto out;
|
||||
} else if (libder_obj_type_simple(params) != BT_SEQUENCE) {
|
||||
rc = ecc_read_pkgkey(root, rawparams, 1, rawkey, rawlen);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Is a sequence */
|
||||
keydata = libder_obj_child(root, 1);
|
||||
if (keydata == NULL || libder_obj_type_simple(keydata) != BT_BITSTRING)
|
||||
goto out;
|
||||
|
||||
/* Key type */
|
||||
oid = libder_obj_child(params, 0);
|
||||
if (oid == NULL || libder_obj_type_simple(oid) != BT_OID)
|
||||
goto out;
|
||||
|
||||
oidp = libder_obj_data(oid, &oidsz);
|
||||
if (oidsz != sizeof(oid_ecpubkey) ||
|
||||
memcmp(oidp, oid_ecpubkey, oidsz) != 0)
|
||||
return (1);
|
||||
|
||||
/* Curve */
|
||||
oid = libder_obj_child(params, 1);
|
||||
if (oid == NULL || libder_obj_type_simple(oid) != BT_OID)
|
||||
goto out;
|
||||
|
||||
oidp = libder_obj_data(oid, &oidsz);
|
||||
if (ecc_extract_key_params(oidp, oidsz, rawparams) != 0)
|
||||
goto out;
|
||||
|
||||
/* Finally, peel off the key material */
|
||||
key = libder_obj_data(keydata, &keylen);
|
||||
if (ecc_extract_pubkey_string(key, keylen, rawkey, rawlen) != 0)
|
||||
goto out;
|
||||
|
||||
rc = 0;
|
||||
out:
|
||||
libder_obj_free(root);
|
||||
libder_close(ctx);
|
||||
return (rc);
|
||||
}
|
||||
|
||||
struct ecc_verify_cbdata {
|
||||
const struct pkgsign_ctx *sctx;
|
||||
FILE *keyfp;
|
||||
const unsigned char *key;
|
||||
size_t keylen;
|
||||
unsigned char *sig;
|
||||
size_t siglen;
|
||||
};
|
||||
|
||||
static int
|
||||
ecc_verify_internal(struct ecc_verify_cbdata *cbdata, const uint8_t *hash,
|
||||
size_t hashsz)
|
||||
{
|
||||
ec_pub_key pubkey;
|
||||
ec_params derparams;
|
||||
const struct ecc_sign_ctx *keyinfo = ECC_CCTX(cbdata->sctx);
|
||||
uint8_t keybuf[EC_PUB_KEY_MAX_SIZE];
|
||||
uint8_t rawsig[EC_MAX_SIGLEN];
|
||||
size_t keysz;
|
||||
int ret;
|
||||
uint8_t ecsiglen;
|
||||
|
||||
keysz = MIN(sizeof(keybuf), cbdata->keylen / 2);
|
||||
|
||||
keysz = sizeof(keybuf);
|
||||
if (ecc_extract_pubkey(cbdata->keyfp, cbdata->key, cbdata->keylen,
|
||||
keybuf, &keysz, &derparams) != 0) {
|
||||
warnx("failed to parse key");
|
||||
return (1);
|
||||
}
|
||||
|
||||
ret = ec_get_sig_len(&derparams, keyinfo->sig_alg, keyinfo->sig_hash,
|
||||
&ecsiglen);
|
||||
if (ret != 0)
|
||||
return (1);
|
||||
|
||||
/*
|
||||
* Signatures are DER-encoded, whether by OpenSSL or pkg.
|
||||
*/
|
||||
if (ecc_extract_signature(cbdata->sig, cbdata->siglen,
|
||||
rawsig, ecsiglen) != 0) {
|
||||
warnx("failed to decode signature");
|
||||
return (1);
|
||||
}
|
||||
|
||||
ret = ec_pub_key_import_from_aff_buf(&pubkey, &derparams,
|
||||
keybuf, keysz, keyinfo->sig_alg);
|
||||
if (ret != 0) {
|
||||
warnx("failed to import key");
|
||||
return (1);
|
||||
}
|
||||
|
||||
ret = ec_verify(rawsig, ecsiglen, &pubkey, hash, hashsz, keyinfo->sig_alg,
|
||||
keyinfo->sig_hash, NULL, 0);
|
||||
if (ret != 0) {
|
||||
warnx("failed to verify signature");
|
||||
return (1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static bool
|
||||
ecc_verify_data(const struct pkgsign_ctx *sctx,
|
||||
const char *data, size_t datasz, const char *sigfile,
|
||||
const unsigned char *key, int keylen,
|
||||
unsigned char *sig, int siglen)
|
||||
{
|
||||
int ret;
|
||||
struct ecc_verify_cbdata cbdata;
|
||||
|
||||
ret = 1;
|
||||
|
||||
if (sigfile != NULL) {
|
||||
cbdata.keyfp = fopen(sigfile, "r");
|
||||
if (cbdata.keyfp == NULL) {
|
||||
warn("fopen: %s", sigfile);
|
||||
return (false);
|
||||
}
|
||||
} else {
|
||||
cbdata.keyfp = NULL;
|
||||
cbdata.key = key;
|
||||
cbdata.keylen = keylen;
|
||||
}
|
||||
|
||||
cbdata.sctx = sctx;
|
||||
cbdata.sig = sig;
|
||||
cbdata.siglen = siglen;
|
||||
|
||||
ret = ecc_verify_internal(&cbdata, data, datasz);
|
||||
|
||||
if (cbdata.keyfp != NULL)
|
||||
fclose(cbdata.keyfp);
|
||||
|
||||
return (ret == 0);
|
||||
}
|
||||
|
||||
static bool
|
||||
ecc_verify_cert(const struct pkgsign_ctx *sctx, int fd,
|
||||
const char *sigfile, const unsigned char *key, int keylen,
|
||||
unsigned char *sig, int siglen)
|
||||
{
|
||||
bool ret;
|
||||
char *sha256;
|
||||
|
||||
ret = false;
|
||||
if (lseek(fd, 0, SEEK_SET) == -1) {
|
||||
warn("lseek");
|
||||
return (false);
|
||||
}
|
||||
|
||||
if ((sha256 = sha256_fd(fd)) != NULL) {
|
||||
ret = ecc_verify_data(sctx, sha256, strlen(sha256), sigfile, key,
|
||||
keylen, sig, siglen);
|
||||
free(sha256);
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
ecc_new(const char *name __unused, struct pkgsign_ctx *sctx)
|
||||
{
|
||||
struct ecc_sign_ctx *keyinfo = ECC_CTX(sctx);
|
||||
int ret;
|
||||
|
||||
ret = 1;
|
||||
if (STREQ(name, "ecc") || STREQ(name, "eddsa")) {
|
||||
keyinfo->sig_alg = EDDSA25519;
|
||||
keyinfo->sig_hash = SHA512;
|
||||
ret = import_params(&keyinfo->params, &wei25519_str_params);
|
||||
} else if (STREQ(name, "ecdsa")) {
|
||||
keyinfo->sig_alg = ECDSA;
|
||||
keyinfo->sig_hash = SHA256;
|
||||
ret = import_params(&keyinfo->params, &secp256k1_str_params);
|
||||
}
|
||||
|
||||
if (ret != 0)
|
||||
return (1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
const struct pkgsign_ops pkgsign_ecc = {
|
||||
.pkgsign_ctx_size = sizeof(struct ecc_sign_ctx),
|
||||
.pkgsign_new = ecc_new,
|
||||
.pkgsign_verify_cert = ecc_verify_cert,
|
||||
.pkgsign_verify_data = ecc_verify_data,
|
||||
};
|
||||
|
|
@ -56,6 +56,8 @@
|
|||
#include "config.h"
|
||||
#include "hash.h"
|
||||
|
||||
#define PKGSIGN_MARKER "$PKGSIGN:"
|
||||
|
||||
static const struct pkgsign_impl {
|
||||
const char *pi_name;
|
||||
const struct pkgsign_ops *pi_ops;
|
||||
|
|
@ -64,6 +66,18 @@ static const struct pkgsign_impl {
|
|||
.pi_name = "rsa",
|
||||
.pi_ops = &pkgsign_rsa,
|
||||
},
|
||||
{
|
||||
.pi_name = "ecc",
|
||||
.pi_ops = &pkgsign_ecc,
|
||||
},
|
||||
{
|
||||
.pi_name = "ecdsa",
|
||||
.pi_ops = &pkgsign_ecc,
|
||||
},
|
||||
{
|
||||
.pi_name = "eddsa",
|
||||
.pi_ops = &pkgsign_ecc,
|
||||
},
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
|
|
@ -489,11 +503,41 @@ pkg_read_fd(int fd, size_t *osz)
|
|||
return (obuf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a copy of the signature type stored on the heap, and advances *bufp
|
||||
* past the type.
|
||||
*/
|
||||
static char *
|
||||
parse_sigtype(char **bufp, size_t *bufszp)
|
||||
{
|
||||
char *buf = *bufp;
|
||||
char *endp;
|
||||
char *sigtype;
|
||||
size_t bufsz = *bufszp;
|
||||
|
||||
if (bufsz <= sizeof(PKGSIGN_MARKER) - 1 ||
|
||||
strncmp(buf, PKGSIGN_MARKER, sizeof(PKGSIGN_MARKER) - 1) != 0)
|
||||
goto dflt;
|
||||
|
||||
buf += sizeof(PKGSIGN_MARKER) - 1;
|
||||
endp = strchr(buf, '$');
|
||||
if (endp == NULL)
|
||||
goto dflt;
|
||||
|
||||
sigtype = strndup(buf, endp - buf);
|
||||
*bufp = endp + 1;
|
||||
*bufszp -= *bufp - buf;
|
||||
|
||||
return (sigtype);
|
||||
dflt:
|
||||
return (strdup("rsa"));
|
||||
}
|
||||
|
||||
static struct pubkey *
|
||||
read_pubkey(int fd)
|
||||
{
|
||||
struct pubkey *pk;
|
||||
char *sigb;
|
||||
char *osigb, *sigb, *sigtype;
|
||||
size_t sigsz;
|
||||
|
||||
if (lseek(fd, 0, 0) == -1) {
|
||||
|
|
@ -501,13 +545,15 @@ read_pubkey(int fd)
|
|||
return (NULL);
|
||||
}
|
||||
|
||||
sigb = pkg_read_fd(fd, &sigsz);
|
||||
osigb = sigb = pkg_read_fd(fd, &sigsz);
|
||||
sigtype = parse_sigtype(&sigb, &sigsz);
|
||||
|
||||
pk = calloc(1, sizeof(struct pubkey));
|
||||
pk->siglen = sigsz;
|
||||
pk->sig = calloc(1, pk->siglen);
|
||||
memcpy(pk->sig, sigb, pk->siglen);
|
||||
free(sigb);
|
||||
pk->sigtype = sigtype;
|
||||
free(osigb);
|
||||
|
||||
return (pk);
|
||||
}
|
||||
|
|
@ -516,17 +562,18 @@ static struct sig_cert *
|
|||
parse_cert(int fd) {
|
||||
int my_fd;
|
||||
struct sig_cert *sc;
|
||||
FILE *fp, *sigfp, *certfp, *tmpfp;
|
||||
FILE *fp, *sigfp, *certfp, *tmpfp, *typefp;
|
||||
char *line;
|
||||
char *sig, *cert;
|
||||
size_t linecap, sigsz, certsz;
|
||||
char *sig, *cert, *type;
|
||||
size_t linecap, sigsz, certsz, typesz;
|
||||
ssize_t linelen;
|
||||
bool end_seen;
|
||||
|
||||
sc = NULL;
|
||||
line = NULL;
|
||||
linecap = 0;
|
||||
sig = cert = NULL;
|
||||
sigfp = certfp = tmpfp = NULL;
|
||||
sig = cert = type = NULL;
|
||||
sigfp = certfp = tmpfp = typefp = NULL;
|
||||
|
||||
if (lseek(fd, 0, 0) == -1) {
|
||||
warn("lseek");
|
||||
|
|
@ -545,22 +592,30 @@ parse_cert(int fd) {
|
|||
return (NULL);
|
||||
}
|
||||
|
||||
sigsz = certsz = 0;
|
||||
sigsz = certsz = typesz = 0;
|
||||
sigfp = open_memstream(&sig, &sigsz);
|
||||
if (sigfp == NULL)
|
||||
err(EXIT_FAILURE, "open_memstream()");
|
||||
certfp = open_memstream(&cert, &certsz);
|
||||
if (certfp == NULL)
|
||||
err(EXIT_FAILURE, "open_memstream()");
|
||||
typefp = open_memstream(&type, &typesz);
|
||||
if (typefp == NULL)
|
||||
err(EXIT_FAILURE, "open_memstream()");
|
||||
|
||||
end_seen = false;
|
||||
while ((linelen = getline(&line, &linecap, fp)) > 0) {
|
||||
if (strcmp(line, "SIGNATURE\n") == 0) {
|
||||
tmpfp = sigfp;
|
||||
continue;
|
||||
} else if (strcmp(line, "TYPE\n") == 0) {
|
||||
tmpfp = typefp;
|
||||
continue;
|
||||
} else if (strcmp(line, "CERT\n") == 0) {
|
||||
tmpfp = certfp;
|
||||
continue;
|
||||
} else if (strcmp(line, "END\n") == 0) {
|
||||
end_seen = true;
|
||||
break;
|
||||
}
|
||||
if (tmpfp != NULL)
|
||||
|
|
@ -570,11 +625,28 @@ parse_cert(int fd) {
|
|||
fclose(fp);
|
||||
fclose(sigfp);
|
||||
fclose(certfp);
|
||||
fclose(typefp);
|
||||
|
||||
sc = calloc(1, sizeof(struct sig_cert));
|
||||
sc->siglen = sigsz -1; /* Trim out unrelated trailing newline */
|
||||
sc->sig = sig;
|
||||
|
||||
if (typesz == 0) {
|
||||
sc->type = strdup("rsa");
|
||||
free(type);
|
||||
} else {
|
||||
assert(type[typesz - 1] == '\n');
|
||||
type[typesz - 1] = '\0';
|
||||
sc->type = type;
|
||||
}
|
||||
|
||||
/*
|
||||
* cert could be DER-encoded rather than PEM, so strip off any trailing
|
||||
* END marker if we ran over it.
|
||||
*/
|
||||
if (!end_seen && certsz > 4 &&
|
||||
strcmp(&cert[certsz - 4], "END\n") == 0)
|
||||
certsz -= 4;
|
||||
sc->certlen = certsz;
|
||||
sc->cert = cert;
|
||||
|
||||
|
|
@ -611,16 +683,23 @@ verify_pubsignature(int fd_pkg, int fd_sig)
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Future types shouldn't do this. */
|
||||
if ((data = sha256_fd(fd_pkg)) == NULL) {
|
||||
warnx("Error creating SHA256 hash for package");
|
||||
goto cleanup;
|
||||
if (strcmp(pk->sigtype, "rsa") == 0) {
|
||||
/* Future types shouldn't do this. */
|
||||
if ((data = sha256_fd(fd_pkg)) == NULL) {
|
||||
warnx("Error creating SHA256 hash for package");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
datasz = strlen(data);
|
||||
} else {
|
||||
if ((data = pkg_read_fd(fd_pkg, &datasz)) == NULL) {
|
||||
warnx("Failed to read package data");
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
datasz = strlen(data);
|
||||
|
||||
if (pkgsign_new("rsa", &sctx) != 0) {
|
||||
warnx("Failed to fetch 'rsa' signer");
|
||||
if (pkgsign_new(pk->sigtype, &sctx) != 0) {
|
||||
warnx("Failed to fetch '%s' signer", pk->sigtype);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
|
@ -723,7 +802,7 @@ verify_signature(int fd_pkg, int fd_sig)
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
if (pkgsign_new("rsa", &sctx) != 0) {
|
||||
if (pkgsign_new(sc->type, &sctx) != 0) {
|
||||
fprintf(stderr, "Failed to fetch 'rsa' signer\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,10 +51,12 @@ struct pkgsign_ops {
|
|||
pkgsign_verify_data_cb *pkgsign_verify_data;
|
||||
};
|
||||
|
||||
extern const struct pkgsign_ops pkgsign_ecc;
|
||||
extern const struct pkgsign_ops pkgsign_rsa;
|
||||
|
||||
struct sig_cert {
|
||||
char *name;
|
||||
char *type;
|
||||
unsigned char *sig;
|
||||
int siglen;
|
||||
unsigned char *cert;
|
||||
|
|
@ -63,6 +65,7 @@ struct sig_cert {
|
|||
};
|
||||
|
||||
struct pubkey {
|
||||
char *sigtype;
|
||||
unsigned char *sig;
|
||||
int siglen;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue