Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
HardenedBSD-pkg libpkg pkgsign_ecc.c
/*-
 * 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 <ctype.h>
#include <fcntl.h>
#include <strings.h>

#include <libder.h>

#define	WITH_STDLIB
#include <libecc/libsig.h>
#undef WITH_STDLIB

#include "pkg.h"
#include "private/event.h"
#include "private/pkg.h"
#include "private/pkgsign.h"

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_CTX(c)	((struct ecc_sign_ctx *)(c))

#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 < NELEM(pkgkey_map); i++) {
		entry = &pkgkey_map[i];
		if (curvesz != entry->namesz)
			continue;
		if (memcmp(curve, entry->name, curvesz) == 0)
			return (entry->params);
	}

	return (NULL);
}

/*
 * PKCS#8 Key:
 *     PublicKeyInfo ::= SEQUENCE {
 *       algorithm   AlgorithmIdentifier,
 *       PublicKey   BIT STRING
 *     }
 *
 *     AlgorithmIdentifier ::= SEQUENCE {
 *       algorithm   OBJECT IDENTIFIER,
 *       parameters  ANY DEFINED BY algorithm OPTIONAL
 *      }
 *
 */
/* XXX Should eventually support other kinds of keys. */
static int
ecc_pubkey_write_pkcs8(const uint8_t *keydata, size_t keysz,
    uint8_t **buf, size_t *buflen)
{
	uint8_t keybuf[EC_PUB_KEY_MAX_SIZE + 2], *outbuf;
	struct libder_ctx *ctx;
	struct libder_object *keybits, *oid, *params, *root;
	int rc;
	bool ok;

	if (keysz > sizeof(keybuf) - 2)
		return (EPKG_FATAL);

	ctx = libder_open();
	if (ctx == NULL)
		return (EPKG_FATAL);

	rc = EPKG_FATAL;
	root = libder_obj_alloc_simple(ctx, BT_SEQUENCE, NULL, 0);
	if (root == NULL)
		goto out;

	params = libder_obj_alloc_simple(ctx, BT_SEQUENCE, NULL, 0);
	if (params == NULL)
		goto out;

	ok = libder_obj_append(root, params);
	assert(ok);

	/* id-ecPublicKey */
	oid = libder_obj_alloc_simple(ctx, BT_OID, oid_ecpubkey,
	    sizeof(oid_ecpubkey));
	if (oid == NULL)
		goto out;
	ok = libder_obj_append(params, oid);
	assert(ok);

	/*
	 * secp256k1, we should eventually allow other curves and actually
	 * construct the OID.
	 */
	oid = libder_obj_alloc_simple(ctx, BT_OID, oid_secp256k1,
	    sizeof(oid_secp256k1));
	if (oid == NULL)
		goto out;
	ok = libder_obj_append(params, oid);
	assert(ok);

	memset(keybuf, 0, sizeof(keybuf));
	keybuf[0] = 0;	/* No unused bits */
	keybuf[1] = PUBKEY_UNCOMPRESSED;
	memcpy(&keybuf[2], keydata, keysz);

	keybits = libder_obj_alloc_simple(ctx, BT_BITSTRING, &keybuf[0],
	    keysz + 2);
	if (keybits == NULL)
		goto out;
	ok = libder_obj_append(root, keybits);
	assert(ok);

	/* Finally, write it out. */
	*buflen = 0;
	outbuf = libder_write(ctx, root, NULL, buflen);
	if (outbuf != NULL) {
		*buf = outbuf;
		rc = EPKG_OK;
	}

out:
	libder_obj_free(root);
	libder_close(ctx);
	return (rc);
}

/*
 * pkg key (only for EdDSA):
 *     PkgPublicKeyInfo ::= SEQUENCE {
 *       Application UTF8String
 *       Version     INTEGER
 *       Signer      UTF8String
 *       KeyType     UTF8String
 *       Public      BOOLEAN
 *       Key         BIT STRING
 *     }
 *
 * "Application" will literally contain the string: "pkg"
 * "Version" must be 1
 * "Signer" must contain the part after "pkgsign_"
 * "KeyType" is signer-defined; for ECC, it must be the curve_name
 * "Public" is self-explanatory
 * "Key" is the key data itself, encoded as the PKCS#8 public key bit string
 *   with a lead byte indicating uncompressed (0x04)
 */
static int
ecc_write_pkgkey(const ec_params *params, uint8_t public,
    const uint8_t *keydata, size_t keysz, uint8_t **buf, size_t *buflen)
{
	uint8_t keybuf[MAX(EC_PRIV_KEY_MAX_SIZE, EC_PUB_KEY_MAX_SIZE) + 2];
	uint8_t *outbuf;
	struct libder_ctx *ctx;
	struct libder_object *keybits, *obj, *root;
	int rc;
	uint8_t version = 1;
	bool ok;

	if (keysz > sizeof(keybuf) - 2)
		return (EPKG_FATAL);

	ctx = libder_open();
	if (ctx == NULL)
		return (EPKG_FATAL);

	rc = EPKG_FATAL;
	root = libder_obj_alloc_simple(ctx, BT_SEQUENCE, NULL, 0);
	if (root == NULL)
		goto out;

	/* Application */
	obj = libder_obj_alloc_simple(ctx, BT_UTF8STRING, pkgkey_app,
	    sizeof(pkgkey_app) - 1);
	if (obj == NULL)
		goto out;
	ok = libder_obj_append(root, obj);
	assert(ok);

	/* Version */
	obj = libder_obj_alloc_simple(ctx, BT_INTEGER, &version, sizeof(version));
	if (obj == NULL)
		goto out;
	ok = libder_obj_append(root, obj);
	assert(ok);

	/* Signer */
	obj = libder_obj_alloc_simple(ctx, BT_UTF8STRING, pkgkey_signer,
	    sizeof(pkgkey_signer) - 1);
	if (obj == NULL)
		goto out;
	ok = libder_obj_append(root, obj);
	assert(ok);

	/* KeyType */
	obj = libder_obj_alloc_simple(ctx, BT_UTF8STRING, params->curve_name,
	    strlen(params->curve_name));
	if (obj == NULL)
		goto out;
	ok = libder_obj_append(root, obj);
	assert(ok);

	/* Public */
	obj = libder_obj_alloc_simple(ctx, BT_BOOLEAN, &public, sizeof(public));
	if (obj == NULL)
		goto out;
	ok = libder_obj_append(root, obj);
	assert(ok);

	memset(keybuf, 0, sizeof(keybuf));
	keybuf[0] = 0;	/* No unused bits */
	keybuf[1] = PUBKEY_UNCOMPRESSED;
	memcpy(&keybuf[2], keydata, keysz);

	keybits = libder_obj_alloc_simple(ctx, BT_BITSTRING, &keybuf[0],
	    keysz + 2);
	if (keybits == NULL)
		goto out;
	ok = libder_obj_append(root, keybits);
	assert(ok);

	/* Finally, write it out. */
	*buflen = 0;
	outbuf = libder_write(ctx, root, NULL, buflen);
	if (outbuf != NULL) {
		*buf = outbuf;
		rc = EPKG_OK;
	}

out:
	libder_obj_free(root);
	libder_close(ctx);
	return (rc);
}

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 (EPKG_FATAL);

	/* Application */
	obj = libder_obj_child(root, 0);
	if (obj == NULL || libder_obj_type_simple(obj) != BT_UTF8STRING)
		return (EPKG_FATAL);
	data = libder_obj_data(obj, &datasz);
	if (datasz != sizeof(pkgkey_app) - 1 ||
	    memcmp(data, pkgkey_app, datasz) != 0)
		return (EPKG_FATAL);

	/* Version */
	obj = libder_obj_child(root, 1);
	if (obj == NULL || libder_obj_type_simple(obj) != BT_INTEGER)
		return (EPKG_FATAL);
	data = libder_obj_data(obj, &datasz);
	if (datasz != 1 || *data != 1 /* XXX */)
		return (EPKG_FATAL);

	/* Signer */
	obj = libder_obj_child(root, 2);
	if (obj == NULL || libder_obj_type_simple(obj) != BT_UTF8STRING)
		return (EPKG_FATAL);
	data = libder_obj_data(obj, &datasz);
	if (datasz != sizeof(pkgkey_signer) - 1 ||
	    memcmp(data, pkgkey_signer, datasz) != 0)
		return (EPKG_FATAL);

	/* KeyType (curve) */
	obj = libder_obj_child(root, 3);
	if (obj == NULL || libder_obj_type_simple(obj) != BT_UTF8STRING)
		return (EPKG_FATAL);
	data = libder_obj_data(obj, &datasz);
	sparams = ecc_pkgkey_params(data, datasz);
	if (sparams == NULL)
		return (EPKG_FATAL);

	ret = import_params(params, sparams);
	if (ret != 0)
		return (EPKG_FATAL);

	/* Public? */
	obj = libder_obj_child(root, 4);
	if (obj == NULL || libder_obj_type_simple(obj) != BT_BOOLEAN)
		return (EPKG_FATAL);
	data = libder_obj_data(obj, &datasz);
	if (datasz != 1 || !data[0] != !public)
		return (EPKG_FATAL);

	/* Key */
	obj = libder_obj_child(root, 5);
	if (obj == NULL || libder_obj_type_simple(obj) != BT_BITSTRING)
		return (EPKG_FATAL);
	data = libder_obj_data(obj, &datasz);
	if (datasz <= 2 || data[0] != 0 || data[1] != PUBKEY_UNCOMPRESSED)
		return (EPKG_FATAL);

	data += 2;
	datasz -= 2;

	if (datasz > *rawlen)
		return (EPKG_FATAL);


	memcpy(rawkey, data, datasz);
	*rawlen = datasz;

	return (EPKG_OK);
}

static int
ecc_write_signature_component(struct libder_ctx *ctx, struct libder_object *root,
    const uint8_t *sigpart, size_t partlen)
{
	uint8_t sigbounce[EC_MAX_SIGLEN];
	struct libder_object *obj;
	size_t curlen;
	bool ok;

	/*
	 * If we need a leading 0 because the sign bit is set, we may need to
	 * bounce through sigbounce.  We may also need to bounce if there's some
	 * leading zeros.
	 */
	curlen = partlen;
	while (curlen > 0 && sigpart[0] == 0) {
		curlen--;
		sigpart++;
	}

	if ((sigpart[0] & 0x80) != 0) {
		/*
		 * If the high bit is set, we need to bounce it through
		 * sigbounce and insert a leading 0.
		 */
		sigbounce[0] = 0;
		memcpy(&sigbounce[1], sigpart, curlen);

		obj = libder_obj_alloc_simple(ctx, BT_INTEGER, sigbounce,
		    curlen + 1);
	} else {
		/*
		 * Otherwise, we can just leave it be.
		 */

		obj = libder_obj_alloc_simple(ctx, BT_INTEGER, sigpart, curlen);
	}

	if (obj == NULL)
		return (EPKG_FATAL);

	ok = libder_obj_append(root, obj);
	assert(ok);
	return (EPKG_OK);
}

/*
 *       Ecdsa-Sig-Value  ::=  SEQUENCE  {
 *            r     INTEGER,
 *            s     INTEGER  }
 */
static int
ecc_write_signature(const uint8_t *sig, size_t siglen, uint8_t **sigret,
    size_t *sigretlen)
{
	struct libder_ctx *ctx;
	struct libder_object *obj, *root;
	uint8_t *outbuf;
	size_t complen;
	int rc;

	ctx = libder_open();
	if (ctx == NULL)
		return (EPKG_FATAL);

	rc = EPKG_FATAL;
	obj = NULL;
	root = libder_obj_alloc_simple(ctx, BT_SEQUENCE, NULL, 0);
	if (root == NULL)
		goto out;

	complen = siglen / 2;
	rc = ecc_write_signature_component(ctx, root, sig, complen);
	if (rc != EPKG_OK)
		goto out;

	sig += complen;
	siglen -= complen;
	rc = ecc_write_signature_component(ctx, root, sig, complen);
	if (rc != EPKG_OK)
		goto out;

	*sigretlen = 0;
	outbuf = libder_write(ctx, root, NULL, sigretlen);
	if (outbuf != NULL) {
		*sigret = outbuf;
		rc = EPKG_OK;
	}
out:
	libder_obj_free(obj);
	libder_close(ctx);
	return (rc);
}

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 (EPKG_FATAL);

	rc = EPKG_FATAL;
	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) ? EPKG_OK : EPKG_FATAL;

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 (EPKG_FATAL);

	usebit = *data++;
	datalen--;

	if (usebit != 0)
		return (EPKG_FATAL);

	prefix = *data++;
	datalen--;

	if (prefix != PUBKEY_UNCOMPRESSED)
		return (EPKG_FATAL);

	if (datalen > *rawlen)
		return (EPKG_FATAL);

	memcpy(rawkey, data, datalen);
	*rawlen = datalen;

	return (EPKG_OK);
}

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 (EPKG_FATAL);

		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 (EPKG_FATAL);
		}

		if (ret == 0)
			return (EPKG_OK);
		return (EPKG_FATAL);
	}

	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 (EPKG_FATAL);

		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 (EPKG_FATAL);
		}

		if (ret == 0)
			return (EPKG_OK);
		return (EPKG_FATAL);
	}

#ifdef ECC_DEBUG
	for (size_t i = 0; i < oidlen; i++) {
		fprintf(stderr, "%.02x ", oid[i]);
	}

	fprintf(stderr, "\n");
#endif

	return (EPKG_FATAL);
}

/*
 * 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(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 (EPKG_FATAL);

	rc = EPKG_FATAL;
	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 (EPKG_FATAL);

	/* 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) != EPKG_OK)
		goto out;

	/* Finally, peel off the key material */
	key = libder_obj_data(keydata, &keylen);
	if (ecc_extract_pubkey_string(key, keylen, rawkey, rawlen) != EPKG_OK)
		goto out;

	rc = EPKG_OK;
out:
	libder_obj_free(root);
	libder_close(ctx);
	return (rc);
}

static int
ecc_extract_privkey(const uint8_t *key, size_t keylen, uint8_t *rawkey,
    size_t *rawlen, ec_params *rawparams)
{
	const uint8_t *data;
	struct libder_ctx *ctx;
	struct libder_object *obj, *root;
	size_t datasz;
	int rc;

	ctx = libder_open();
	if (ctx == NULL)
		return (EPKG_FATAL);

	rc = EPKG_FATAL;
	root = libder_read(ctx, key, &keylen);
	if (root == NULL || libder_obj_type_simple(root) != BT_SEQUENCE)
		goto out;

	/* Sanity check the version, we're expecting version 1. */
	obj = libder_obj_child(root, 0);
	if (obj == NULL)
		goto out;
	if (libder_obj_type_simple(obj) != BT_INTEGER) {
		rc = ecc_read_pkgkey(root, rawparams, 0, rawkey, rawlen);
		goto out;
	}

	data = libder_obj_data(obj, &datasz);
	if (datasz != 1 || *data != 1)
		goto out;

	/* Grab the key data itself. */
	obj = libder_obj_child(root, 1);
	if (obj == NULL || libder_obj_type_simple(obj) != BT_OCTETSTRING)
		goto out;

	data = libder_obj_data(obj, &datasz);
	if (datasz == 0 || datasz > *rawlen)
		goto out;

	memcpy(rawkey, data, datasz);
	*rawlen = datasz;

	/* Next, extract the OID describing the key type. */
	obj = libder_obj_child(root, 2);
	if (obj == NULL || libder_obj_type_simple(obj) !=
	    ((BC_CONTEXT << 6) | BER_TYPE_CONSTRUCTED_MASK))
		goto out;

	obj = libder_obj_child(obj, 0);
	if (obj == NULL || libder_obj_type_simple(obj) != BT_OID)
		goto out;

	data = libder_obj_data(obj, &datasz);
	if (ecc_extract_key_params(data, datasz, rawparams) != EPKG_OK)
		goto out;

	rc = EPKG_OK;
out:
	libder_obj_free(root);
	libder_close(ctx);
	return (rc);
}

static int
_generate_private_key(struct ecc_sign_ctx *keyinfo)
{
	int ret;

	ret = ec_key_pair_gen(&keyinfo->keypair, &keyinfo->params,
	    keyinfo->sig_alg);

	if (ret != 0) {
		pkg_emit_error("failed to generate ecc keypair");
		return (EPKG_FATAL);
	}

	keyinfo->loaded = true;
	return (EPKG_OK);
}

static inline void
_specific_explicit_bzero(void *buf, size_t bufsz)
{

#ifdef __APPLE__
	memset_s(buf, bufsz, 0, bufsz);
#else
	explicit_bzero(buf, bufsz);
#endif
}


static int
_load_private_key(struct ecc_sign_ctx *keyinfo)
{
	struct stat st;
	uint8_t keybuf[EC_PRIV_KEY_MAX_SIZE];
	uint8_t *filedata;
	ssize_t readsz;
	size_t keysz;
	int fd, rc, ret;
	size_t offset, resid;

	filedata = NULL;
	fd = -1;
	rc = EPKG_FATAL;

	keyinfo->loaded = false;
	if ((fd = open(keyinfo->sctx.path, O_RDONLY)) == -1)
		return (EPKG_FATAL);

	if (fstat(fd, &st) == -1)
		goto out;

	filedata = xmalloc(st.st_size);
	resid = st.st_size;
	offset = 0;
	while (resid != 0) {
		readsz = read(fd, &filedata[offset], resid);
		if (readsz <= 0)
			break;
		resid -= readsz;
		offset += readsz;
	}

	if (readsz < 0) {
		pkg_emit_errno("read", keyinfo->sctx.path);
		goto out;
	} else if (resid != 0) {
		pkg_emit_error("%s: failed to read key",
		    keyinfo->sctx.path);
		goto out;
	}

	/*
	 * Try DER-decoding it.  Unlike with loading a pubkey, anything
	 * requiring the privkey requires a new context for each operation, so
	 * we can just clobber keyinfo->params at will.
	 */
	keysz = sizeof(keybuf);
	if (ecc_extract_privkey(filedata, offset, keybuf, &keysz,
	    &keyinfo->params) != EPKG_OK) {
		pkg_emit_error("failed to decode private key");
		goto out;
	}

	ret = ec_priv_key_import_from_buf(&keyinfo->keypair.priv_key,
	    &keyinfo->params, keybuf, keysz, keyinfo->sig_alg);
	if (ret == 0) {
		ret = init_pubkey_from_privkey(&keyinfo->keypair.pub_key,
			&keyinfo->keypair.priv_key);
		if (ret == 0) {
			keyinfo->loaded = true;
			rc = EPKG_OK;
		} else {
			pkg_emit_error("%s: failed to derive public key",
			    keyinfo->sctx.path);
			rc = EPKG_FATAL;
		}
	} else {
		pkg_emit_error("%s: failed to import private key",
		    keyinfo->sctx.path);
		rc = EPKG_FATAL;
	}

out:
	_specific_explicit_bzero(keybuf, sizeof(keybuf));
	free(filedata);
	if (fd != -1)
		close(fd);
	return (rc);
}

struct ecc_verify_cbdata {
	const struct pkgsign_ctx *sctx;
	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;
	struct ecc_sign_ctx *keyinfo = ECC_CTX(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->key, cbdata->keylen, keybuf,
	    &keysz, &derparams) != EPKG_OK) {
		pkg_emit_error("failed to parse key");
		return (EPKG_FATAL);
	}

	ret = ec_get_sig_len(&derparams, keyinfo->sig_alg, keyinfo->sig_hash,
	    &ecsiglen);
	if (ret != 0)
		return (EPKG_FATAL);

	/*
	 * Signatures are DER-encoded, whether by OpenSSL or pkg.
	 */
	if (ecc_extract_signature(cbdata->sig, cbdata->siglen,
	    rawsig, ecsiglen) != EPKG_OK) {
		pkg_emit_error("failed to decode signature");
		return (EPKG_FATAL);
	}

	ret = ec_pub_key_import_from_aff_buf(&pubkey, &derparams,
	    keybuf, keysz, keyinfo->sig_alg);
	if (ret != 0) {
		pkg_emit_error("failed to import key");
		return (EPKG_FATAL);
	}

	ret = ec_verify(rawsig, ecsiglen, &pubkey, hash, hashsz, keyinfo->sig_alg,
	    keyinfo->sig_hash, NULL, 0);
	if (ret != 0) {
		pkg_emit_error("failed to verify signature");
		return (EPKG_FATAL);
	}

	return (EPKG_OK);
}


static int
ecc_verify_cert_cb(int fd, void *ud)
{
	struct ecc_verify_cbdata *cbdata = ud;
	char *sha256;
	int ret;

	sha256 = pkg_checksum_fd(fd, PKG_HASH_TYPE_SHA256_HEX);
	if (sha256 == NULL)
		return (EPKG_FATAL);

	ret = ecc_verify_internal(cbdata, sha256, strlen(sha256));
	if (ret != 0) {
		pkg_emit_error("ecc signature verification failure");
		return (EPKG_FATAL);
	}

	return (EPKG_OK);
}

static int
ecc_verify_cert(const struct pkgsign_ctx *sctx, unsigned char *key,
    size_t keylen, unsigned char *sig, size_t siglen, int fd)
{
	int ret;
	struct ecc_verify_cbdata cbdata;

	(void)lseek(fd, 0, SEEK_SET);

	cbdata.sctx = sctx;
	cbdata.key = key;
	cbdata.keylen = keylen;
	cbdata.sig = sig;
	cbdata.siglen = siglen;

	ret = pkg_emit_sandbox_call(ecc_verify_cert_cb, fd, &cbdata);

	return (ret);
}

static int
ecc_verify_cb(int fd, void *ud)
{
	struct ecc_verify_cbdata *cbdata = ud;
	uint8_t *blake2;
	int ret;

	blake2 = pkg_checksum_fd(fd, PKG_HASH_TYPE_BLAKE2_RAW);
	if (blake2 == NULL)
		return (EPKG_FATAL);

	ret = ecc_verify_internal(cbdata, blake2,
	    pkg_checksum_type_size(PKG_HASH_TYPE_BLAKE2_RAW));

	free(blake2);
	if (ret != 0) {
		pkg_emit_error("ecc signature verification failure");
		return (EPKG_FATAL);
	}

	return (EPKG_OK);
}

static int
ecc_verify(const struct pkgsign_ctx *sctx, const char *keypath,
    unsigned char *sig, size_t sig_len, int fd)
{
	int ret;
	struct ecc_verify_cbdata cbdata;
	char *key_buf;
	off_t key_len;

	if (file_to_buffer(keypath, (char**)&key_buf, &key_len) != EPKG_OK) {
		pkg_emit_errno("ecc_verify", "cannot read key");
		return (EPKG_FATAL);
	}

	(void)lseek(fd, 0, SEEK_SET);

	cbdata.sctx = sctx;
	cbdata.key = key_buf;
	cbdata.keylen = key_len;
	cbdata.sig = sig;
	cbdata.siglen = sig_len;

	ret = pkg_emit_sandbox_call(ecc_verify_cb, fd, &cbdata);

	free(key_buf);

	return (ret);
}

static int
ecc_sign_data(struct pkgsign_ctx *sctx, const unsigned char *msg, size_t msgsz,
    unsigned char **sigret, size_t *siglen)
{
	uint8_t rawsig[EC_MAX_SIGLEN];
	struct ecc_sign_ctx *keyinfo = ECC_CTX(sctx);
	int ret;
	uint8_t rawlen;

	if (!keyinfo->loaded && _load_private_key(keyinfo) != EPKG_OK) {
		pkg_emit_error("%s: failed to load key", keyinfo->sctx.path);
		return (EPKG_FATAL);
	}

	ret = ec_get_sig_len(&keyinfo->params, keyinfo->sig_alg, keyinfo->sig_hash,
		&rawlen);
	if (ret != 0)
		return (EPKG_FATAL);

	assert(rawlen <= sizeof(rawsig));

	assert(priv_key_check_initialized_and_type(&keyinfo->keypair.priv_key,
	    keyinfo->sig_alg) == 0);
	assert(pub_key_check_initialized_and_type(&keyinfo->keypair.pub_key,
	    keyinfo->sig_alg) == 0);

	ret = ec_sign(rawsig, rawlen, &keyinfo->keypair, msg, msgsz,
	    keyinfo->sig_alg, keyinfo->sig_hash, NULL, 0);

	if (ret != 0) {
		pkg_emit_error("%s: ecc signing failure", keyinfo->sctx.path);
		return (EPKG_FATAL);
	}

	if (ecc_write_signature(rawsig, rawlen, sigret, siglen) != EPKG_OK) {
		pkg_emit_error("failed to encode signature");
		return (EPKG_FATAL);
	}

	return (EPKG_OK);
}

static int
ecc_sign(struct pkgsign_ctx *sctx, const char *path, unsigned char **sigret,
    size_t *siglen)
{
	uint8_t *blake2;
	struct ecc_sign_ctx *keyinfo = ECC_CTX(sctx);
	int ret;

	if (access(keyinfo->sctx.path, R_OK) == -1) {
		pkg_emit_errno("access", keyinfo->sctx.path);
		return (EPKG_FATAL);
	}

	blake2 = pkg_checksum_file(path, PKG_HASH_TYPE_BLAKE2_RAW);
	if (blake2 == NULL)
		return (EPKG_FATAL);

	ret = ecc_sign_data(sctx, blake2,
	    pkg_checksum_type_size(PKG_HASH_TYPE_BLAKE2_RAW), sigret, siglen);
	free(blake2);

	return (ret);
}

static int
ecc_generate(struct pkgsign_ctx *sctx, const struct iovec *iov __unused,
    int niov __unused)
{
	uint8_t keybuf[EC_PRIV_KEY_MAX_SIZE], *outbuf;
	struct ecc_sign_ctx *keyinfo = ECC_CTX(sctx);
	const char *path = sctx->path;
	FILE *fp;
	size_t keysz, outsz;

	if (niov != 0)
		return (EPKG_FATAL);

	if (_generate_private_key(keyinfo) != 0)
		return (EPKG_FATAL);

	assert(priv_key_check_initialized_and_type(&keyinfo->keypair.priv_key,
	    keyinfo->sig_alg) == 0);
	assert(pub_key_check_initialized_and_type(&keyinfo->keypair.pub_key,
	    keyinfo->sig_alg) == 0);

	keysz = EC_PRIV_KEY_EXPORT_SIZE(&keyinfo->keypair.priv_key);
	if (ec_priv_key_export_to_buf(&keyinfo->keypair.priv_key,
	    keybuf, keysz) != 0) {
		pkg_emit_error("failed to export ecc key");
		return (EPKG_FATAL);
	}

	outbuf = NULL;
	outsz = 0;
	if (ecc_write_pkgkey(&keyinfo->params, 0, keybuf, keysz, &outbuf,
	    &outsz) != EPKG_OK) {
		pkg_emit_error("%s: failed to write DER-encoded key",
		    sctx->path);
		return (EPKG_FATAL);
	}

	fp = fopen(path, "wb");
	if (fp == NULL) {
		pkg_emit_errno("fopen write", path);
		free(outbuf);
		return (EPKG_FATAL);
	}

	if (fchmod(fileno(fp), 0400) != 0) {
		pkg_emit_errno("fchmod", path);
		free(outbuf);
		fclose(fp);
		return (EPKG_FATAL);
	}

	fwrite(outbuf, outsz, 1, fp);
	free(outbuf);
	outbuf = NULL;
	if (ferror(fp) != 0 || fflush(fp) != 0) {
		pkg_emit_errno("fwrite", path);
		fclose(fp);
		return (EPKG_FATAL);
	}

	fclose(fp);
	return (EPKG_OK);
}

static int
ecc_pubkey(struct pkgsign_ctx *sctx, char **pubkey, size_t *pubkeylen)
{
	struct ecc_sign_ctx *keyinfo = ECC_CTX(sctx);
	uint8_t keybuf[EC_PUB_KEY_MAX_SIZE];
	size_t keylen;
	int ret;

	if (!keyinfo->loaded && _load_private_key(keyinfo) != EPKG_OK) {
		pkg_emit_error("%s: failed to load key", sctx->path);
		return (EPKG_FATAL);
	}

	assert(keyinfo->loaded);
	assert(pub_key_check_initialized_and_type(&keyinfo->keypair.pub_key,
	    keyinfo->sig_alg) == 0);

	keylen = 2 * BYTECEIL(keyinfo->params.ec_fp.p_bitlen);
	ret = ec_pub_key_export_to_aff_buf(&keyinfo->keypair.pub_key, keybuf, keylen);
	if (ret != 0) {
		pkg_emit_error("%s: failed to export key", sctx->path);
		return (EPKG_FATAL);
	}

	/*
	 * We'll write a custom format for anything but ECDSA that includes our
	 * params wholesale so that we can just import them directly.
	 *
	 * ECDSA keys get exported as PKCS#8 for interoperability with OpenSSL.
	 */
	if (keyinfo->sig_alg != ECDSA) {
		if (ecc_write_pkgkey(&keyinfo->params, 1, keybuf, keylen,
		    (uint8_t **)pubkey, pubkeylen) != EPKG_OK) {
			pkg_emit_error("%s: failed to write DER-encoded key",

			    sctx->path);
			return (EPKG_FATAL);
		}
	} else if (ecc_pubkey_write_pkcs8(keybuf, keylen,
	    (uint8_t **)pubkey, pubkeylen) != EPKG_OK) {
		pkg_emit_error("%s: failed to write DER-encoded key", sctx->path);
		return (EPKG_FATAL);
	}

	return (EPKG_OK);
}

static int
ecc_new(const char *name __unused, struct pkgsign_ctx *sctx)
{
	struct ecc_sign_ctx *keyinfo = ECC_CTX(sctx);
	int ret;

	ret = EPKG_FATAL;
	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 (EPKG_FATAL);

	return (0);
}

static void
ecc_free(struct pkgsign_ctx *sctx)
{
	struct ecc_sign_ctx *keyinfo = ECC_CTX(sctx);

	_specific_explicit_bzero(&keyinfo->keypair, sizeof(keyinfo->keypair));
}

const struct pkgsign_ops pkgsign_ecc = {
	.pkgsign_ctx_size = sizeof(struct ecc_sign_ctx),
	.pkgsign_new = ecc_new,
	.pkgsign_free = ecc_free,

	.pkgsign_sign = ecc_sign,
	.pkgsign_verify = ecc_verify,
	.pkgsign_verify_cert = ecc_verify_cert,

	.pkgsign_generate = ecc_generate,
	.pkgsign_pubkey = ecc_pubkey,
	.pkgsign_sign_data = ecc_sign_data,
};