/* $OpenBSD: ec_asn1_test.c,v 1.41 2025/12/07 11:39:00 tb Exp $ */
/*
 * Copyright (c) 2017, 2021 Joel Sing <jsing@openbsd.org>
 * Copyright (c) 2024, 2025 Theo Buehler <tb@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <err.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include <openssl/bio.h>
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/err.h>
#include <openssl/objects.h>
#include <openssl/sha.h>

#include "ec_local.h"

EC_GROUP *EC_GROUP_new(const EC_METHOD *);

/* set to 0 if/when we are going to enforce 0 <= a,b < p. */
#define NEGATIVE_CURVE_COEFFICIENTS_ALLOWED	1

static const uint8_t ec_secp256r1_pkparameters_named_curve[] = {
	0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03,
	0x01, 0x07,
};

static const uint8_t ec_secp256r1_pkparameters_parameters[] = {
	0x30, 0x81, 0xf7, 0x02, 0x01, 0x01, 0x30, 0x2c,
	0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x01,
	0x01, 0x02, 0x21, 0x00, 0xff, 0xff, 0xff, 0xff,
	0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0x30, 0x5b, 0x04, 0x20,
	0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc,
	0x04, 0x20, 0x5a, 0xc6, 0x35, 0xd8, 0xaa, 0x3a,
	0x93, 0xe7, 0xb3, 0xeb, 0xbd, 0x55, 0x76, 0x98,
	0x86, 0xbc, 0x65, 0x1d, 0x06, 0xb0, 0xcc, 0x53,
	0xb0, 0xf6, 0x3b, 0xce, 0x3c, 0x3e, 0x27, 0xd2,
	0x60, 0x4b, 0x03, 0x15, 0x00, 0xc4, 0x9d, 0x36,
	0x08, 0x86, 0xe7, 0x04, 0x93, 0x6a, 0x66, 0x78,
	0xe1, 0x13, 0x9d, 0x26, 0xb7, 0x81, 0x9f, 0x7e,
	0x90, 0x04, 0x41, 0x04, 0x6b, 0x17, 0xd1, 0xf2,
	0xe1, 0x2c, 0x42, 0x47, 0xf8, 0xbc, 0xe6, 0xe5,
	0x63, 0xa4, 0x40, 0xf2, 0x77, 0x03, 0x7d, 0x81,
	0x2d, 0xeb, 0x33, 0xa0, 0xf4, 0xa1, 0x39, 0x45,
	0xd8, 0x98, 0xc2, 0x96, 0x4f, 0xe3, 0x42, 0xe2,
	0xfe, 0x1a, 0x7f, 0x9b, 0x8e, 0xe7, 0xeb, 0x4a,
	0x7c, 0x0f, 0x9e, 0x16, 0x2b, 0xce, 0x33, 0x57,
	0x6b, 0x31, 0x5e, 0xce, 0xcb, 0xb6, 0x40, 0x68,
	0x37, 0xbf, 0x51, 0xf5, 0x02, 0x21, 0x00, 0xff,
	0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbc,
	0xe6, 0xfa, 0xad, 0xa7, 0x17, 0x9e, 0x84, 0xf3,
	0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51, 0x02,
	0x01, 0x01,
};

static const uint8_t ec_secp256k1_pkparameters_parameters[] = {
	0x30, 0x81, 0xe0, 0x02, 0x01, 0x01, 0x30, 0x2c,
	0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x01,
	0x01, 0x02, 0x21, 0x00, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
	0xff, 0xff, 0xfc, 0x2f, 0x30, 0x44, 0x04, 0x20,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x07, 0x04, 0x41, 0x04, 0x79, 0xbe, 0x66,
	0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62,
	0x95, 0xce, 0x87, 0x0b, 0x07, 0x02, 0x9b, 0xfc,
	0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81,
	0x5b, 0x16, 0xf8, 0x17, 0x98, 0x48, 0x3a, 0xda,
	0x77, 0x26, 0xa3, 0xc4, 0x65, 0x5d, 0xa4, 0xfb,
	0xfc, 0x0e, 0x11, 0x08, 0xa8, 0xfd, 0x17, 0xb4,
	0x48, 0xa6, 0x85, 0x54, 0x19, 0x9c, 0x47, 0xd0,
	0x8f, 0xfb, 0x10, 0xd4, 0xb8, 0x02, 0x21, 0x00,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
	0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b,
	0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41,
	0x02, 0x01, 0x01,
};

static void
hexdump(const unsigned char *buf, size_t len)
{
	size_t i;

	for (i = 1; i <= len; i++)
		fprintf(stderr, " 0x%02hhx,%s", buf[i - 1], i % 8 ? "" : "\n");

	fprintf(stderr, "\n");
}

static int
compare_data(const char *label, const unsigned char *d1, size_t d1_len,
    const unsigned char *d2, size_t d2_len)
{
	if (d1_len != d2_len) {
		fprintf(stderr, "FAIL: got %s with length %zu, want %zu\n",
		    label, d1_len, d2_len);
		return -1;
	}
	if (memcmp(d1, d2, d1_len) != 0) {
		fprintf(stderr, "FAIL: %s differ\n", label);
		fprintf(stderr, "got:\n");
		hexdump(d1, d1_len);
		fprintf(stderr, "want:\n");
		hexdump(d2, d2_len);
		return -1;
	}
	return 0;
}

static int
ec_group_pkparameters_test(const char *label, int nid, int asn1_flag,
    const uint8_t *test_data, size_t test_data_len)
{
	EC_GROUP *group_a = NULL, *group_b = NULL;
	unsigned char *out = NULL, *data = NULL;
	const unsigned char *p;
	BIO *bio_mem = NULL;
	int failure = 1;
	int len;

	/*
	 * Test i2d_ECPKParameters/d2i_ECPKParameters.
	 */
	if ((group_a = EC_GROUP_new_by_curve_name(nid)) == NULL)
		errx(1, "failed to create EC_GROUP");

	EC_GROUP_set_asn1_flag(group_a, asn1_flag);

	if ((len = i2d_ECPKParameters(group_a, &out)) < 0) {
		fprintf(stderr, "FAIL: i2d_ECPKParameters failed\n");
		goto done;
	}
	if (compare_data(label, out, len, test_data, test_data_len) == -1)
		goto done;

	p = out;
	if ((group_b = d2i_ECPKParameters(NULL, &p, len)) == NULL) {
		fprintf(stderr, "FAIL: d2i_ECPKParameters failed\n");
		goto done;
	}

	if (EC_GROUP_cmp(group_a, group_b, NULL) != 0) {
		fprintf(stderr, "FAIL: EC_GROUPs do not match!\n");
		goto done;
	}

	p = out;
	if ((group_a = d2i_ECPKParameters(&group_a, &p, len)) == NULL) {
		fprintf(stderr, "FAIL: d2i_ECPKParameters failed\n");
		goto done;
	}

	if (EC_GROUP_cmp(group_a, group_b, NULL) != 0) {
		fprintf(stderr, "FAIL: EC_GROUPs do not match!\n");
		goto done;
	}

	/*
	 * Test i2d_ECPKParameters_bio/d2i_ECPKParameters_bio.
	 */
	if ((bio_mem = BIO_new(BIO_s_mem())) == NULL)
                errx(1, "BIO_new failed for BIO_s_mem");

	if (i2d_ECPKParameters_bio(bio_mem, group_a) < 0) {
		fprintf(stderr, "FAIL: i2d_ECPKParameters_bio failed\n");
		goto done;
	}

	len = BIO_get_mem_data(bio_mem, &data);
	if (compare_data(label, out, len, test_data, test_data_len) == -1)
		goto done;

	EC_GROUP_free(group_b);
	if ((group_b = d2i_ECPKParameters_bio(bio_mem, NULL)) == NULL) {
		fprintf(stderr, "FAIL: d2i_ECPKParameters_bio failed\n");
		goto done;
	}

	if (EC_GROUP_cmp(group_a, group_b, NULL) != 0) {
		fprintf(stderr, "FAIL: EC_GROUPs do not match!\n");
		goto done;
	}

	failure = 0;

 done:
	BIO_free_all(bio_mem);
	EC_GROUP_free(group_a);
	EC_GROUP_free(group_b);
	free(out);

	return failure;
}

static int
ec_group_pkparameters_named_curve_test(void)
{
	return ec_group_pkparameters_test("ECPKPARAMETERS named curve",
	    NID_X9_62_prime256v1, OPENSSL_EC_NAMED_CURVE,
	    ec_secp256r1_pkparameters_named_curve,
	    sizeof(ec_secp256r1_pkparameters_named_curve));
}

static int
ec_group_pkparameters_parameters_test(void)
{
	return ec_group_pkparameters_test("ECPKPARAMETERS parameters",
	    NID_X9_62_prime256v1, OPENSSL_EC_EXPLICIT_CURVE,
	    ec_secp256r1_pkparameters_parameters,
	    sizeof(ec_secp256r1_pkparameters_parameters));
}

static int
ec_group_pkparameters_correct_padding_test(void)
{
	return ec_group_pkparameters_test("ECPKPARAMETERS parameters",
	    NID_secp256k1, OPENSSL_EC_EXPLICIT_CURVE,
	    ec_secp256k1_pkparameters_parameters,
	    sizeof(ec_secp256k1_pkparameters_parameters));
}

static EC_GROUP *
ec_group_simple_from_builtin(const EC_GROUP *group, int nid, BN_CTX *ctx)
{
	EC_GROUP *simple_group;
	BIGNUM *p, *a, *b, *x, *y, *order, *cofactor;
	const EC_POINT *generator;
	EC_POINT *simple_generator = NULL;

	BN_CTX_start(ctx);

	if ((p = BN_CTX_get(ctx)) == NULL)
		errx(1, "BN_CTX_get");
	if ((a = BN_CTX_get(ctx)) == NULL)
		errx(1, "BN_CTX_get");
	if ((b = BN_CTX_get(ctx)) == NULL)
		errx(1, "BN_CTX_get");

	if ((x = BN_CTX_get(ctx)) == NULL)
		errx(1, "BN_CTX_get");
	if ((y = BN_CTX_get(ctx)) == NULL)
		errx(1, "BN_CTX_get");

	if ((order = BN_CTX_get(ctx)) == NULL)
		errx(1, "BN_CTX_get");
	if ((cofactor = BN_CTX_get(ctx)) == NULL)
		errx(1, "BN_CTX_get");

	if (!EC_GROUP_get_curve(group, p, a, b, ctx))
		errx(1, "EC_GROUP_get_curve");
	if (!EC_GROUP_get_order(group, order, ctx))
		errx(1, "EC_GROUP_get_order");
	if (!EC_GROUP_get_cofactor(group, cofactor, ctx))
		errx(1, "EC_GROUP_get_cofactor");
	if ((generator = EC_GROUP_get0_generator(group)) == NULL)
		errx(1, "EC_GROUP_get0_generator");
	if (!EC_POINT_get_affine_coordinates(group, generator, x, y, ctx))
		errx(1, "EC_POINT_get_affine_coordinates");

	if ((simple_group = EC_GROUP_new(EC_GFp_simple_method())) == NULL)
		errx(1, "EC_GROUP_new");
	if (!EC_GROUP_set_curve(simple_group, p, a, b, ctx))
		errx(1, "EC_GROUP_set_curve");
	EC_GROUP_set_curve_name(simple_group, nid);

	if ((simple_generator = EC_POINT_new(simple_group)) == NULL)
		errx(1, "EC_POINT_new");
	if (!EC_POINT_set_compressed_coordinates(simple_group, simple_generator,
	    x, BN_is_odd(y), ctx))
		errx(1, "EC_POINT_set_affine_coordinates");
	if (!EC_GROUP_set_generator(simple_group, simple_generator, order,
	    cofactor))
		errx(1, "EC_GROUP_set_generator");

	BN_CTX_end(ctx);

	EC_POINT_free(simple_generator);

	return simple_group;
}

static int
ec_group_roundtrip_curve(const EC_GROUP *group, const char *descr, int nid)
{
	EC_GROUP *new_group = NULL;
	unsigned char *der = NULL, *new_der = NULL;
	int der_len = 0, new_der_len = 0;
	const unsigned char *p;
	int failed = 1;

	der = NULL;
	if ((der_len = i2d_ECPKParameters(group, &der)) <= 0)
		errx(1, "failed to serialize %s %d", descr, nid);

	p = der;
	if ((new_group = d2i_ECPKParameters(NULL, &p, der_len)) == NULL)
		errx(1, "failed to deserialize %s %d", descr, nid);

	new_der = NULL;
	if ((new_der_len = i2d_ECPKParameters(new_group, &new_der)) <= 0)
		errx(1, "failed to serialize new %s %d", descr, nid);

	if (compare_data(__func__, der, der_len, new_der, new_der_len) == -1) {
		fprintf(stderr, "FAIL: new and old der for %s %d\n", descr, nid);
		goto err;
	}

	if (EC_GROUP_get_asn1_flag(group) != EC_GROUP_get_asn1_flag(new_group)) {
		fprintf(stderr, "FAIL: %s %d asn1_flag %x != %x\n", descr, nid,
		    EC_GROUP_get_asn1_flag(group),
		    EC_GROUP_get_asn1_flag(new_group));
		goto err;
	}
	if (EC_GROUP_get_point_conversion_form(group) !=
	    EC_GROUP_get_point_conversion_form(new_group)) {
		fprintf(stderr, "FAIL: %s %d form %02x != %02x\n", descr, nid,
		    EC_GROUP_get_point_conversion_form(group),
		    EC_GROUP_get_point_conversion_form(new_group));
		goto err;
	}

	failed = 0;

 err:
	EC_GROUP_free(new_group);
	freezero(der, der_len);
	freezero(new_der, new_der_len);

	return failed;
}

static int
ec_group_roundtrip_group(EC_GROUP *group, int nid)
{
	int failed = 1;

	if (EC_GROUP_get_asn1_flag(group) != OPENSSL_EC_NAMED_CURVE) {
		fprintf(stderr, "FAIL: ASN.1 flag not set for %d\n", nid);
		goto err;
	}
	if (EC_GROUP_get_point_conversion_form(group) !=
	    POINT_CONVERSION_UNCOMPRESSED) {
		fprintf(stderr, "FAIL: %d has point conversion form %02x\n",
		    nid, EC_GROUP_get_point_conversion_form(group));
		goto err;
	}

	failed = 0;

	failed |= ec_group_roundtrip_curve(group, "named", nid);

	EC_GROUP_set_asn1_flag(group, 0);
	failed |= ec_group_roundtrip_curve(group, "explicit", nid);

	EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_COMPRESSED);
	failed |= ec_group_roundtrip_curve(group, "compressed", nid);

	EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_HYBRID);
	failed |= ec_group_roundtrip_curve(group, "hybrid", nid);

 err:
	return failed;
}

static int
ec_group_roundtrip_builtin_curve(const EC_builtin_curve *curve, BN_CTX *ctx)
{
	EC_GROUP *group = NULL, *simple_group = NULL;
	int failed = 0;

	if ((group = EC_GROUP_new_by_curve_name(curve->nid)) == NULL)
		errx(1, "failed to instantiate curve %d", curve->nid);

	if (!EC_GROUP_check(group, NULL)) {
		fprintf(stderr, "FAIL: EC_GROUP_check(%d) failed\n", curve->nid);
		goto err;
	}

	if ((simple_group = ec_group_simple_from_builtin(group, curve->nid,
	    ctx)) == NULL)
		errx(1, "failed to instantiate simple group %d", curve->nid);

	if (!EC_GROUP_check(group, NULL)) {
		fprintf(stderr, "FAIL: EC_GROUP_check(%d) failed\n", curve->nid);
		goto err;
	}

	failed |= ec_group_roundtrip_group(group, curve->nid);
	failed |= ec_group_roundtrip_group(simple_group, curve->nid);

 err:
	EC_GROUP_free(group);
	EC_GROUP_free(simple_group);

	return failed;
}

static int
ec_group_roundtrip_builtin_curves(void)
{
	BN_CTX *ctx = NULL;
	EC_builtin_curve *all_curves = NULL;
	size_t curve_id, ncurves;
	int failed = 0;

	if ((ctx = BN_CTX_new()) == NULL)
		errx(1, "BN_CTX_new");

	ncurves = EC_get_builtin_curves(NULL, 0);
	if ((all_curves = calloc(ncurves, sizeof(*all_curves))) == NULL)
		err(1, "calloc builtin curves");
	EC_get_builtin_curves(all_curves, ncurves);

	for (curve_id = 0; curve_id < ncurves; curve_id++)
		failed |= ec_group_roundtrip_builtin_curve(&all_curves[curve_id], ctx);

	free(all_curves);
	BN_CTX_free(ctx);

	return failed;
}

struct curve {
	const char *descr;
	const char *oid;
	const char *sn;
	const char *ln;
	const char *p;
	const char *a;
	const char *b;
	const char *order;
	const char *cofactor;
	const char *x;
	const char *y;
	int known_named_curve;
	const char *named;
	size_t named_len;
	const char *param;
	size_t param_len;
};

/*
 * From draft-ietf-lwig-curve-representation-23, Appendix E.3
 */

static const uint8_t ec_wei25519_pkparameters_named_curve[] = {
	0x06, 0x03, 0x2b, 0x65, 0x6c,
};

static const uint8_t ec_wei25519_pkparameters_parameters[] = {
	0x30, 0x81, 0xde, 0x02, 0x01, 0x01, 0x30, 0x2b,
	0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x01,
	0x01, 0x02, 0x20, 0x7f, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xed, 0x30, 0x44, 0x04, 0x20, 0x2a,
	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
	0xaa, 0xaa, 0x98, 0x49, 0x14, 0xa1, 0x44, 0x04,
	0x20, 0x7b, 0x42, 0x5e, 0xd0, 0x97, 0xb4, 0x25,
	0xed, 0x09, 0x7b, 0x42, 0x5e, 0xd0, 0x97, 0xb4,
	0x25, 0xed, 0x09, 0x7b, 0x42, 0x5e, 0xd0, 0x97,
	0xb4, 0x26, 0x0b, 0x5e, 0x9c, 0x77, 0x10, 0xc8,
	0x64, 0x04, 0x41, 0x04, 0x2a, 0xaa, 0xaa, 0xaa,
	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
	0xaa, 0xad, 0x24, 0x5a, 0x20, 0xae, 0x19, 0xa1,
	0xb8, 0xa0, 0x86, 0xb4, 0xe0, 0x1e, 0xdd, 0x2c,
	0x77, 0x48, 0xd1, 0x4c, 0x92, 0x3d, 0x4d, 0x7e,
	0x6d, 0x7c, 0x61, 0xb2, 0x29, 0xe9, 0xc5, 0xa2,
	0x7e, 0xce, 0xd3, 0xd9, 0x02, 0x20, 0x10, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xde,
	0xf9, 0xde, 0xa2, 0xf7, 0x9c, 0xd6, 0x58, 0x12,
	0x63, 0x1a, 0x5c, 0xf5, 0xd3, 0xed, 0x02, 0x01,
	0x08,
};

static const struct curve wei25519 = {
	.descr = "short Weierstrass 25519",
	.oid = "1.3.101.108",
	.sn = "Wei25519",
	.p =	 "7fffffff" "ffffffff" "ffffffff" "ffffffff"
		 "ffffffff" "ffffffff" "ffffffff" "ffffffed",
	.a =	 "2aaaaaaa" "aaaaaaaa" "aaaaaaaa" "aaaaaaaa"
		 "aaaaaaaa" "aaaaaaaa" "aaaaaa98" "4914a144",
	.b =	 "7b425ed0" "97b425ed" "097b425e" "d097b425"
		 "ed097b42" "5ed097b4" "260b5e9c" "7710c864",
	.x =	 "2aaaaaaa" "aaaaaaaa" "aaaaaaaa" "aaaaaaaa"
		 "aaaaaaaa" "aaaaaaaa" "aaaaaaaa" "aaad245a",
	.y =	 "20ae19a1" "b8a086b4" "e01edd2c" "7748d14c"
		 "923d4d7e" "6d7c61b2" "29e9c5a2" "7eced3d9",
	.order = "10000000" "00000000" "00000000" "00000000"
		 "14def9de" "a2f79cd6" "5812631a" "5cf5d3ed",
	.cofactor = "8",
	.named = ec_wei25519_pkparameters_named_curve,
	.named_len = sizeof(ec_wei25519_pkparameters_named_curve),
	.param = ec_wei25519_pkparameters_parameters,
	.param_len = sizeof(ec_wei25519_pkparameters_parameters),
};

/*
 * From draft-ietf-lwig-curve-representation-23, Appendix G.3
 */

static const uint8_t ec_wei25519_2_pkparameters_parameters[] = {
	0x30, 0x81, 0xde, 0x02, 0x01, 0x01, 0x30, 0x2b,
	0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x01,
	0x01, 0x02, 0x20, 0x7f, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xed, 0x30, 0x44, 0x04, 0x20, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04,
	0x20, 0x1a, 0xc1, 0xda, 0x05, 0xb5, 0x5b, 0xc1,
	0x46, 0x33, 0xbd, 0x39, 0xe4, 0x7f, 0x94, 0x30,
	0x2e, 0xf1, 0x98, 0x43, 0xdc, 0xf6, 0x69, 0x91,
	0x6f, 0x6a, 0x5d, 0xfd, 0x01, 0x65, 0x53, 0x8c,
	0xd1, 0x04, 0x41, 0x04, 0x17, 0xcf, 0xea, 0xc3,
	0x78, 0xae, 0xd6, 0x61, 0x31, 0x8e, 0x86, 0x34,
	0x58, 0x22, 0x75, 0xb6, 0xd9, 0xad, 0x4d, 0xef,
	0x07, 0x2e, 0xa1, 0x93, 0x5e, 0xe3, 0xc4, 0xe8,
	0x7a, 0x94, 0x0f, 0xfa, 0x0c, 0x08, 0xa9, 0x52,
	0xc5, 0x5d, 0xfa, 0xd6, 0x2c, 0x4f, 0x13, 0xf1,
	0xa8, 0xf6, 0x8d, 0xca, 0xdc, 0x5c, 0x33, 0x1d,
	0x29, 0x7a, 0x37, 0xb6, 0xf0, 0xd7, 0xfd, 0xcc,
	0x51, 0xe1, 0x6b, 0x4d, 0x02, 0x20, 0x10, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xde,
	0xf9, 0xde, 0xa2, 0xf7, 0x9c, 0xd6, 0x58, 0x12,
	0x63, 0x1a, 0x5c, 0xf5, 0xd3, 0xed, 0x02, 0x01,
	0x08,
};

static const struct curve wei25519_2 = {
	.descr = "short Weierstrass 25519.2",
	.oid = "1.3.101.108",
	.sn = "Wei25519",
	.p =	 "7fffffff" "ffffffff" "ffffffff" "ffffffff"
		 "ffffffff" "ffffffff" "ffffffff" "ffffffed",
	.a =	 "02",
	.b =	 "1ac1da05" "b55bc146" "33bd39e4" "7f94302e"
		 "f19843dc" "f669916f" "6a5dfd01" "65538cd1",
	.x =	 "17cfeac3" "78aed661" "318e8634" "582275b6"
		 "d9ad4def" "072ea193" "5ee3c4e8" "7a940ffa",
	.y =	 "0c08a952" "c55dfad6" "2c4f13f1" "a8f68dca"
		 "dc5c331d" "297a37b6" "f0d7fdcc" "51e16b4d",
	.order = "10000000" "00000000" "00000000" "00000000"
		 "14def9de" "a2f79cd6" "5812631a" "5cf5d3ed",
	.cofactor = "8",
	.named = ec_wei25519_pkparameters_named_curve,
	.named_len = sizeof(ec_wei25519_pkparameters_named_curve),
	.param = ec_wei25519_2_pkparameters_parameters,
	.param_len = sizeof(ec_wei25519_2_pkparameters_parameters),
};

static const uint8_t ec_wei25519_3_pkparameters_parameters[] = {
	0x30, 0x81, 0xde, 0x02, 0x01, 0x01, 0x30, 0x2b,
	0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x01,
	0x01, 0x02, 0x20, 0x7f, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xed, 0x30, 0x44, 0x04, 0x20, 0x7f,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xea, 0x04,
	0x20, 0x41, 0xa3, 0xb6, 0xbf, 0xc6, 0x68, 0x77,
	0x8e, 0xbe, 0x29, 0x54, 0xa4, 0xb1, 0xdf, 0x36,
	0xd1, 0x48, 0x5e, 0xce, 0xf1, 0xea, 0x61, 0x42,
	0x95, 0x79, 0x6e, 0x10, 0x22, 0x40, 0x89, 0x1f,
	0xaa, 0x04, 0x41, 0x04, 0x77, 0x06, 0xc3, 0x7b,
	0x5a, 0x84, 0x12, 0x8a, 0x38, 0x84, 0xa5, 0xd7,
	0x18, 0x11, 0xf1, 0xb5, 0x5d, 0xa3, 0x23, 0x0f,
	0xfb, 0x17, 0xa8, 0xab, 0x0b, 0x32, 0xe4, 0x8d,
	0x31, 0xa6, 0x68, 0x5c, 0x0f, 0x60, 0x48, 0x0c,
	0x7a, 0x5c, 0x0e, 0x11, 0x40, 0x34, 0x0a, 0xdc,
	0x79, 0xd6, 0xa2, 0xbf, 0x0c, 0xb5, 0x7a, 0xd0,
	0x49, 0xd0, 0x25, 0xdc, 0x38, 0xd8, 0x0c, 0x77,
	0x98, 0x5f, 0x03, 0x29, 0x02, 0x20, 0x10, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xde,
	0xf9, 0xde, 0xa2, 0xf7, 0x9c, 0xd6, 0x58, 0x12,
	0x63, 0x1a, 0x5c, 0xf5, 0xd3, 0xed, 0x02, 0x01,
	0x08,
};

static const struct curve wei25519_3 = {
	.descr = "short Weierstrass 25519.-3",
	.oid = "1.3.101.108",
	.sn = "Wei25519",
	.p =	 "7fffffff" "ffffffff" "ffffffff" "ffffffff"
		 "ffffffff" "ffffffff" "ffffffff" "ffffffed",
	.a =	 "7fffffff" "ffffffff" "ffffffff" "ffffffff"
		 "ffffffff" "ffffffff" "ffffffff" "ffffffea",
	.b =	 "41a3b6bf" "c668778e" "be2954a4" "b1df36d1"
		 "485ecef1" "ea614295" "796e1022" "40891faa",
	.x =	 "7706c37b" "5a84128a" "3884a5d7" "1811f1b5"
		 "5da3230f" "fb17a8ab" "0b32e48d" "31a6685c",
	.y =	 "0f60480c" "7a5c0e11" "40340adc" "79d6a2bf"
		 "0cb57ad0" "49d025dc" "38d80c77" "985f0329",
	.order = "10000000" "00000000" "00000000" "00000000"
		 "14def9de" "a2f79cd6" "5812631a" "5cf5d3ed",
	.cofactor = "8",
	.named = ec_wei25519_pkparameters_named_curve,
	.named_len = sizeof(ec_wei25519_pkparameters_named_curve),
	.param = ec_wei25519_3_pkparameters_parameters,
	.param_len = sizeof(ec_wei25519_3_pkparameters_parameters),
};

#if NEGATIVE_CURVE_COEFFICIENTS_ALLOWED
/* Same as wei25519_3 except for a. */
static const struct curve wei25519_3_neg = {
	.descr = "short Weierstrass 25519.-3 with negative a",
	.oid = "1.3.101.108",
	.sn = "Wei25519",
	.p =	 "7fffffff" "ffffffff" "ffffffff" "ffffffff"
		 "ffffffff" "ffffffff" "ffffffff" "ffffffed",
	.a =	 "-03",
	.b =	 "41a3b6bf" "c668778e" "be2954a4" "b1df36d1"
		 "485ecef1" "ea614295" "796e1022" "40891faa",
	.x =	 "7706c37b" "5a84128a" "3884a5d7" "1811f1b5"
		 "5da3230f" "fb17a8ab" "0b32e48d" "31a6685c",
	.y =	 "0f60480c" "7a5c0e11" "40340adc" "79d6a2bf"
		 "0cb57ad0" "49d025dc" "38d80c77" "985f0329",
	.order = "10000000" "00000000" "00000000" "00000000"
		 "14def9de" "a2f79cd6" "5812631a" "5cf5d3ed",
	.cofactor = "8",
	.named = ec_wei25519_pkparameters_named_curve,
	.named_len = sizeof(ec_wei25519_pkparameters_named_curve),
	.param = ec_wei25519_3_pkparameters_parameters,
	.param_len = sizeof(ec_wei25519_3_pkparameters_parameters),
};
#endif

/*
 * From draft-ietf-lwig-curve-representation-23, Appendix L.3
 */

static const uint8_t ec_secp256k1_m_pkparameters_named_curve[] = {
	0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x0a,
};

static const uint8_t ec_secp256k1_m_pkparameters_parameters[] = {
	0x30, 0x81, 0xe0, 0x02, 0x01, 0x01, 0x30, 0x2c,
	0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x01,
	0x01, 0x02, 0x21, 0x00, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
	0xff, 0xff, 0xfc, 0x2f, 0x30, 0x44, 0x04, 0x20,
	0xcf, 0xcd, 0x5c, 0x21, 0x75, 0xe2, 0xef, 0x7d,
	0xcc, 0xdc, 0xe7, 0x37, 0x77, 0x0b, 0x73, 0x81,
	0x5a, 0x2f, 0x13, 0xc5, 0x09, 0x03, 0x5c, 0xa2,
	0x54, 0xa1, 0x4a, 0xc9, 0xf0, 0x89, 0x74, 0xaf,
	0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x06, 0xeb, 0x04, 0x41, 0x04, 0x3a, 0xca, 0x53,
	0x00, 0x95, 0x9f, 0xa1, 0xd0, 0xba, 0xf7, 0x8d,
	0xcf, 0xf7, 0x7a, 0x61, 0x6f, 0x39, 0x5e, 0x58,
	0x6d, 0x67, 0xac, 0xed, 0x0a, 0x88, 0x79, 0x81,
	0x29, 0x0c, 0x27, 0x91, 0x45, 0x95, 0x80, 0xfc,
	0xe5, 0x3a, 0x17, 0x0f, 0x4f, 0xb7, 0x44, 0x57,
	0x9f, 0xf3, 0xd6, 0x20, 0x86, 0x12, 0xcd, 0x6a,
	0x23, 0x3e, 0x2d, 0xe2, 0x37, 0xf9, 0x76, 0xc6,
	0xa7, 0x86, 0x11, 0xc8, 0x00, 0x02, 0x21, 0x00,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
	0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b,
	0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41,
	0x02, 0x01, 0x01,
};

static const struct curve secp256k1_m = {
	.descr = "short Weierstrass secp256k1.m",
	.oid =	 "1.3.132.0.10",
	.sn =	 SN_secp256k1,
	.p =	 "ffffffff" "ffffffff" "ffffffff" "ffffffff"
		 "ffffffff" "ffffffff" "fffffffe" "fffffc2f",
	.a =	 "cfcd5c21" "75e2ef7d" "ccdce737" "770b7381"
		 "5a2f13c5" "09035ca2" "54a14ac9" "f08974af",
	.b =	 "06eb",
	.x =	 "3aca5300" "959fa1d0" "baf78dcf" "f77a616f"
		 "395e586d" "67aced0a" "88798129" "0c279145",
	.y =	 "9580fce5" "3a170f4f" "b744579f" "f3d62086"
		 "12cd6a23" "3e2de237" "f976c6a7" "8611c800",
	.order = "ffffffff" "ffffffff" "ffffffff" "fffffffe"
		 "baaedce6" "af48a03b" "bfd25e8c" "d0364141",
	.cofactor = "1",
	.known_named_curve = 1,
	.named = ec_secp256k1_m_pkparameters_named_curve,
	.named_len = sizeof(ec_secp256k1_m_pkparameters_named_curve),
	.param = ec_secp256k1_m_pkparameters_parameters,
	.param_len = sizeof(ec_secp256k1_m_pkparameters_parameters),
};

/*
 * From https://eips.ethereum.org/EIPS/eip-2539
 */

static const uint8_t ec_bls12_377_pkparameters_named_curve[] = {
	0x06, 0x04, 0x29, 0x01, 0x01, 0x01,
};

static const uint8_t ec_bls12_377_pkparameters_parameters[] = {
	0x30, 0x82, 0x01, 0x3d, 0x02, 0x01, 0x01, 0x30,
	0x3b, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d,
	0x01, 0x01, 0x02, 0x30, 0x01, 0xae, 0x3a, 0x46,
	0x17, 0xc5, 0x10, 0xea, 0xc6, 0x3b, 0x05, 0xc0,
	0x6c, 0xa1, 0x49, 0x3b, 0x1a, 0x22, 0xd9, 0xf3,
	0x00, 0xf5, 0x13, 0x8f, 0x1e, 0xf3, 0x62, 0x2f,
	0xba, 0x09, 0x48, 0x00, 0x17, 0x0b, 0x5d, 0x44,
	0x30, 0x00, 0x00, 0x00, 0x85, 0x08, 0xc0, 0x00,
	0x00, 0x00, 0x00, 0x01, 0x30, 0x64, 0x04, 0x30,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x01, 0x04, 0x61, 0x04, 0x00, 0x88, 0x48,
	0xde, 0xfe, 0x74, 0x0a, 0x67, 0xc8, 0xfc, 0x62,
	0x25, 0xbf, 0x87, 0xff, 0x54, 0x85, 0x95, 0x1e,
	0x2c, 0xaa, 0x9d, 0x41, 0xbb, 0x18, 0x82, 0x82,
	0xc8, 0xbd, 0x37, 0xcb, 0x5c, 0xd5, 0x48, 0x15,
	0x12, 0xff, 0xcd, 0x39, 0x4e, 0xea, 0xb9, 0xb1,
	0x6e, 0xb2, 0x1b, 0xe9, 0xef, 0x01, 0x91, 0x4a,
	0x69, 0xc5, 0x10, 0x2e, 0xff, 0x1f, 0x67, 0x4f,
	0x5d, 0x30, 0xaf, 0xee, 0xc4, 0xbd, 0x7f, 0xb3,
	0x48, 0xca, 0x3e, 0x52, 0xd9, 0x6d, 0x18, 0x2a,
	0xd4, 0x4f, 0xb8, 0x23, 0x05, 0xc2, 0xfe, 0x3d,
	0x36, 0x34, 0xa9, 0x59, 0x1a, 0xfd, 0x82, 0xde,
	0x55, 0x55, 0x9c, 0x8e, 0xa6, 0x02, 0x20, 0x12,
	0xab, 0x65, 0x5e, 0x9a, 0x2c, 0xa5, 0x56, 0x60,
	0xb4, 0x4d, 0x1e, 0x5c, 0x37, 0xb0, 0x01, 0x59,
	0xaa, 0x76, 0xfe, 0xd0, 0x00, 0x00, 0x01, 0x0a,
	0x11, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
	0x10, 0x17, 0x0b, 0x5d, 0x44, 0x30, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00,
};

static const struct curve bls12_377 = {
	.descr = "BLS12-377",
	.oid =	 "1.1.1.1.1", /* XXX */
	.sn =	 "BLS12-377",
	.p =	 "01ae3a46" "17c510ea" "c63b05c0" "6ca1493b"
		 "1a22d9f3" "00f5138f" "1ef3622f" "ba094800"
		 "170b5d44" "30000000" "8508c000" "00000001",
	.a =     "0",
	.b =     "1",
	.x =	 "008848de" "fe740a67" "c8fc6225" "bf87ff54"
		 "85951e2c" "aa9d41bb" "188282c8" "bd37cb5c"
		 "d5481512" "ffcd394e" "eab9b16e" "b21be9ef",
	.y =	 "01914a69" "c5102eff" "1f674f5d" "30afeec4"
		 "bd7fb348" "ca3e52d9" "6d182ad4" "4fb82305"
		 "c2fe3d36" "34a9591a" "fd82de55" "559c8ea6",
	.order = "12ab655e" "9a2ca556" "60b44d1e" "5c37b001"
		 "59aa76fe" "d0000001" "0a118000" "00000001",
	.cofactor = "170b5d44" "30000000" "00000000" "00000000",
	.named = ec_bls12_377_pkparameters_named_curve,
	.named_len = sizeof(ec_bls12_377_pkparameters_named_curve),
	.param = ec_bls12_377_pkparameters_parameters,
	.param_len = sizeof(ec_bls12_377_pkparameters_parameters),
};

static EC_GROUP *
ec_group_from_curve_method(const struct curve *curve, const EC_METHOD *method,
    BN_CTX *ctx)
{
	EC_GROUP *group;
	EC_POINT *generator = NULL;
	BIGNUM *p, *a, *b;
	BIGNUM *order, *x, *y;

	BN_CTX_start(ctx);

	if ((p = BN_CTX_get(ctx)) == NULL)
		errx(1, "BN_CTX_get");
	if ((a = BN_CTX_get(ctx)) == NULL)
		errx(1, "BN_CTX_get");
	if ((b = BN_CTX_get(ctx)) == NULL)
		errx(1, "BN_CTX_get");

	if ((order = BN_CTX_get(ctx)) == NULL)
		errx(1, "BN_CTX_get");
	if ((x = BN_CTX_get(ctx)) == NULL)
		errx(1, "BN_CTX_get");
	if ((y = BN_CTX_get(ctx)) == NULL)
		errx(1, "BN_CTX_get");

	if (BN_hex2bn(&p, curve->p) == 0)
		errx(1, "BN_hex2bn(p)");
	if (BN_hex2bn(&a, curve->a) == 0)
		errx(1, "BN_hex2bn(a)");
	if (BN_hex2bn(&b, curve->b) == 0)
		errx(1, "BN_hex2bn(b)");

	if ((group = EC_GROUP_new(method)) == NULL)
		errx(1, "EC_GROUP_new");

	if (!EC_GROUP_set_curve(group, p, a, b, ctx))
		errx(1, "EC_GROUP_set_curve");

	if (BN_hex2bn(&x, curve->x) == 0)
		errx(1, "BN_hex2bn(x)");
	if (BN_hex2bn(&x, curve->x) == 0)
		errx(1, "BN_hex2bn(x)");
	if (BN_hex2bn(&y, curve->y) == 0)
		errx(1, "BN_hex2bn(y)");

	if ((generator = EC_POINT_new(group)) == NULL)
		errx(1, "EC_POINT_new()");

	if (!EC_POINT_set_affine_coordinates(group, generator, x, y, ctx)) {
		fprintf(stderr, "FAIL: %s EC_POINT_set_affine_coordinates\n",
		    curve->descr);
		ERR_print_errors_fp(stderr);
		goto err;
	}

	if (BN_hex2bn(&order, curve->order) == 0)
		errx(1, "BN_hex2bn(order)");

	/* Don't set cofactor to exercise the cofactor guessing code. */
	if (!EC_GROUP_set_generator(group, generator, order, NULL)) {
		fprintf(stderr, "FAIL: %s EC_GROUP_set_generator\n", curve->descr);
		ERR_print_errors_fp(stderr);
		goto err;
	}

	EC_POINT_free(generator);

	BN_CTX_end(ctx);

	return group;

 err:
	BN_CTX_end(ctx);

	EC_POINT_free(generator);
	EC_GROUP_free(group);

	return NULL;
}

static EC_GROUP *
ec_group_new(const struct curve *curve, const EC_METHOD *method, BN_CTX *ctx)
{
	EC_GROUP *group = NULL;
	BIGNUM *cofactor, *guessed_cofactor;
	int nid;

	BN_CTX_start(ctx);

	if ((nid = OBJ_txt2nid(curve->oid)) == NID_undef)
		nid = OBJ_create(curve->oid, curve->sn, curve->ln);
	if (nid == NID_undef) {
		fprintf(stderr, "FAIL: OBJ_create(%s)\n", curve->descr);
		goto err;
	}

	if ((cofactor = BN_CTX_get(ctx)) == NULL)
		errx(1, "BN_CTX_get");
	if ((guessed_cofactor = BN_CTX_get(ctx)) == NULL)
		errx(1, "BN_CTX_get");

	if (BN_hex2bn(&cofactor, curve->cofactor) == 0)
		errx(1, "BN_hex2bn(cofactor)");

	if ((group = ec_group_from_curve_method(curve, method, ctx)) == NULL) {
		fprintf(stderr, "FAIL: %s ec_group_from_curve_method\n", curve->descr);
		ERR_print_errors_fp(stderr);
		goto err;
	}

	if (!EC_GROUP_get_cofactor(group, guessed_cofactor, ctx)) {
		fprintf(stderr, "FAIL: %s EC_GROUP_get_cofactor\n", curve->descr);
		ERR_print_errors_fp(stderr);
		goto err;
	}

	if (BN_cmp(cofactor, guessed_cofactor) != 0) {
		fprintf(stderr, "FAIL: %s cofactor: want ", curve->descr);
		BN_print_fp(stderr, cofactor);
		fprintf(stderr, ", got ");
		BN_print_fp(stderr, guessed_cofactor);
		fprintf(stderr, "\n");
		goto err;
	}

	if (!EC_GROUP_check(group, ctx)) {
		fprintf(stderr, "FAIL: %s EC_GROUP_check\n", curve->descr);
		ERR_print_errors_fp(stderr);
		goto err;
	}

	EC_GROUP_set_curve_name(group, nid);

	BN_CTX_end(ctx);

	return group;

 err:
	BN_CTX_end(ctx);

	EC_GROUP_free(group);

	return NULL;
}

static int
ec_group_non_builtin_curve(const struct curve *curve, const EC_METHOD *method,
    BN_CTX *ctx)
{
	EC_GROUP *group = NULL, *new_group = NULL;
	const unsigned char *pder;
	unsigned char *der = NULL;
#ifndef OPENSSL_SUPPRESS_DEPRECATED
	long error;
#endif
	int der_len = 0;
	int failed = 1;

	ERR_clear_error();
	BN_CTX_start(ctx);

	if ((group = ec_group_new(curve, method, ctx)) == NULL)
		goto err;

	if (EC_GROUP_get_curve_name(group) == NID_undef) {
		fprintf(stderr, "FAIL: no curve name set for %s\n", curve->descr);
		goto err;
	}

	EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE);

	der = NULL;
	if ((der_len = i2d_ECPKParameters(group, &der)) <= 0) {
		fprintf(stderr, "FAIL: %s i2d_ECPKParameters (named)\n",
		    curve->descr);
		ERR_print_errors_fp(stderr);
		goto err;
	}

	if (compare_data(curve->descr, der, der_len,
	    curve->named, curve->named_len) == -1)
		goto err;

	freezero(der, der_len);
	der = NULL;

	/* Explicit curve parameter encoding should work without NID set. */
	EC_GROUP_set_curve_name(group, NID_undef);
	EC_GROUP_set_asn1_flag(group, OPENSSL_EC_EXPLICIT_CURVE);

	der = NULL;
	if ((der_len = i2d_ECPKParameters(group, &der)) <= 0) {
		fprintf(stderr, "FAIL: i2d_ECPKParameters (explicit) %s\n",
		    curve->descr);
		ERR_print_errors_fp(stderr);
		goto err;
	}

	if (compare_data(curve->descr, der, der_len,
	    curve->param, curve->param_len) == -1)
		goto err;

	freezero(der, der_len);
	der = NULL;

	/* At this point we should have no error on the stack. */
	if (ERR_peek_last_error() != 0) {
		fprintf(stderr, "FAIL: %s unexpected error %lu\n", curve->descr,
		    ERR_peek_last_error());
		goto err;
	}

	pder = curve->named;
	der_len = curve->named_len;
	new_group = d2i_ECPKParameters(NULL, &pder, der_len);
	if (!curve->known_named_curve && new_group != NULL) {
		fprintf(stderr, "FAIL: managed to decode unknown named curve %s\n",
		    curve->descr);
		goto err;
	}
	EC_GROUP_free(new_group);
	new_group = NULL;
#ifndef OPENSSL_SUPPRESS_DEPRECATED
	error = ERR_get_error();
	if (!curve->known_named_curve &&
	    ERR_GET_REASON(error) != EC_R_UNKNOWN_GROUP) {
		fprintf(stderr, "FAIL: %s unexpected error: want %d, got %d\n",
		    curve->descr, EC_R_UNKNOWN_GROUP, ERR_GET_REASON(error));
		goto err;
	}
#endif

	ERR_clear_error();

	pder = curve->param;
	der_len = curve->param_len;
	if ((new_group = d2i_ECPKParameters(NULL, &pder, der_len)) != NULL) {
		fprintf(stderr, "FAIL: managed to decode non-builtin parameters %s\n",
		    curve->descr);
		goto err;
	}

#ifndef OPENSSL_SUPPRESS_DEPRECATED
	error = ERR_peek_last_error();
	if (ERR_GET_REASON(error) != EC_R_PKPARAMETERS2GROUP_FAILURE) {
		fprintf(stderr, "FAIL: %s unexpected error: want %d, got %d\n",
		    curve->descr, EC_R_UNKNOWN_GROUP, ERR_GET_REASON(error));
		goto err;
	}
#endif

	failed = 0;

 err:
	BN_CTX_end(ctx);

	EC_GROUP_free(group);
	EC_GROUP_free(new_group);

	freezero(der, der_len);

	return failed;
}

static int
ec_group_non_builtin_curves(void)
{
	BN_CTX *ctx;
	int failed = 0;

	if ((ctx = BN_CTX_new()) == NULL)
		errx(1, "BN_CTX_new");

	failed |= ec_group_non_builtin_curve(&wei25519, EC_GFp_mont_method(), ctx);
	failed |= ec_group_non_builtin_curve(&wei25519, EC_GFp_simple_method(), ctx);

	failed |= ec_group_non_builtin_curve(&wei25519_2, EC_GFp_mont_method(), ctx);
	failed |= ec_group_non_builtin_curve(&wei25519_2, EC_GFp_simple_method(), ctx);

	failed |= ec_group_non_builtin_curve(&wei25519_3, EC_GFp_mont_method(), ctx);
	failed |= ec_group_non_builtin_curve(&wei25519_3, EC_GFp_simple_method(), ctx);

#if NEGATIVE_CURVE_COEFFICIENTS_ALLOWED
	failed |= ec_group_non_builtin_curve(&wei25519_3_neg, EC_GFp_mont_method(), ctx);
	failed |= ec_group_non_builtin_curve(&wei25519_3_neg, EC_GFp_simple_method(), ctx);
#endif

	failed |= ec_group_non_builtin_curve(&secp256k1_m, EC_GFp_mont_method(), ctx);
	failed |= ec_group_non_builtin_curve(&secp256k1_m, EC_GFp_simple_method(), ctx);

	failed |= ec_group_non_builtin_curve(&bls12_377, EC_GFp_mont_method(), ctx);
	failed |= ec_group_non_builtin_curve(&bls12_377, EC_GFp_simple_method(), ctx);

	BN_CTX_free(ctx);

	return failed;
}

static int
ec_group_check_prime_order(EC_builtin_curve *curve, BN_CTX *ctx)
{
	EC_GROUP *group;
	BIGNUM *order;
	int rv;
	int failed = 0;

	if ((group = EC_GROUP_new_by_curve_name(curve->nid)) == NULL)
		errx(1, "EC_GROUP_new_by_curve_name");

	BN_CTX_start(ctx);

	if ((order = BN_CTX_get(ctx)) == NULL)
		errx(1, "order = BN_CTX_get()");

	if (!EC_GROUP_get_order(group, order, ctx))
		errx(1, "EC_GROUP_get_order");

	if ((rv = BN_is_prime_ex(order, 0, ctx, NULL)) != 1) {
		fprintf(stderr, "%s: nid %d: BN_is_prime_ex() returned %d, want 1\n",
		    __func__, curve->nid, rv);
		failed = 1;
	}

	BN_CTX_end(ctx);
	EC_GROUP_free(group);

	return failed;
}

static int
ec_group_builtin_curves_have_prime_order(void)
{
	BN_CTX *ctx = NULL;
	EC_builtin_curve *all_curves = NULL;
	size_t curve_id, ncurves;
	int failed = 0;

	if ((ctx = BN_CTX_new()) == NULL)
		errx(1, "BN_CTX_new");

	ncurves = EC_get_builtin_curves(NULL, 0);
	if ((all_curves = calloc(ncurves, sizeof(*all_curves))) == NULL)
		err(1, "calloc builtin curves");
	EC_get_builtin_curves(all_curves, ncurves);

	for (curve_id = 0; curve_id < ncurves; curve_id++)
		failed |= ec_group_check_prime_order(&all_curves[curve_id], ctx);

	free(all_curves);
	BN_CTX_free(ctx);

	return failed;
}

static const struct ec_private_key {
	const char *name;
	size_t der_len;
	uint8_t der[256];
	const char *hex;
	int oct_len;
	uint8_t oct[256];
} ec_private_keys[] = {
	{
		.name = "secp224k1",
		.der_len = 107,
		.der = {
			0x30, 0x69, 0x02, 0x01, 0x01, 0x04, 0x1d, 0x00,
			0x32, 0x2b, 0x6d, 0xe3, 0x62, 0x60, 0xda, 0xb2,
			0x62, 0x0a, 0x38, 0x3e, 0xd3, 0x8c, 0x70, 0x9e,
			0x76, 0x38, 0xac, 0x26, 0x17, 0xa9, 0x00, 0xdf,
			0xfb, 0x1e, 0xf3, 0xbd, 0xa0, 0x07, 0x06, 0x05,
			0x2b, 0x81, 0x04, 0x00, 0x20, 0xa1, 0x3c, 0x03,
			0x3a, 0x00, 0x04, 0x51, 0xc4, 0x69, 0xdf, 0x2d,
			0x49, 0x7b, 0x05, 0x6c, 0x12, 0x5f, 0x9a, 0x83,
			0x51, 0x7d, 0xf3, 0x4a, 0x6c, 0xe1, 0x3a, 0xea,
			0x44, 0x35, 0x3e, 0x7a, 0xa4, 0x40, 0xdf, 0xc4,
			0x90, 0x18, 0xfc, 0x2f, 0x5d, 0x4b, 0x12, 0x37,
			0x87, 0x4d, 0x2a, 0xf8, 0xbd, 0x29, 0xfb, 0x13,
			0x34, 0xef, 0xfb, 0x04, 0xa1, 0x28, 0x7d, 0x51,
			0xbe, 0xe7, 0x0b,
		},
		.hex =	"0451C469DF2D497B"
			"056C125F9A83517D"
			"F34A6CE13AEA4435"
			"3E7AA440DFC49018"
			"FC2F5D4B1237874D"
			"2AF8BD29FB1334EF"
			"FB04A1287D51BEE7"
			"0B",
		.oct_len = 57,
		.oct = {
			0x04, 0x51, 0xc4, 0x69, 0xdf, 0x2d, 0x49, 0x7b,
			0x05, 0x6c, 0x12, 0x5f, 0x9a, 0x83, 0x51, 0x7d,
			0xf3, 0x4a, 0x6c, 0xe1, 0x3a, 0xea, 0x44, 0x35,
			0x3e, 0x7a, 0xa4, 0x40, 0xdf, 0xc4, 0x90, 0x18,
			0xfc, 0x2f, 0x5d, 0x4b, 0x12, 0x37, 0x87, 0x4d,
			0x2a, 0xf8, 0xbd, 0x29, 0xfb, 0x13, 0x34, 0xef,
			0xfb, 0x04, 0xa1, 0x28, 0x7d, 0x51, 0xbe, 0xe7,
			0x0b,
		},
	},
	{
		.name = "secp224r1",
		.der_len = 106,
		.der = {
			0x30, 0x68, 0x02, 0x01, 0x01, 0x04, 0x1c, 0x76,
			0x9b, 0x2f, 0x62, 0xff, 0x5f, 0x84, 0x6c, 0x7e,
			0x90, 0xda, 0xfb, 0x70, 0x62, 0xc1, 0xb9, 0xa2,
			0xc9, 0xf7, 0x1b, 0x76, 0x7f, 0xbb, 0xb1, 0xd4,
			0xa4, 0xa0, 0x42, 0xa0, 0x07, 0x06, 0x05, 0x2b,
			0x81, 0x04, 0x00, 0x21, 0xa1, 0x3c, 0x03, 0x3a,
			0x00, 0x04, 0x94, 0x84, 0xb0, 0xcd, 0x65, 0xef,
			0xc5, 0x5d, 0xc9, 0xe4, 0x91, 0x71, 0xcb, 0xc7,
			0xf1, 0x8e, 0x44, 0x39, 0xc2, 0xd3, 0x07, 0xf0,
			0x6c, 0xb6, 0xef, 0x77, 0xc0, 0x84, 0x30, 0x2c,
			0xd2, 0xf2, 0xf0, 0xb5, 0xb6, 0x6f, 0x0a, 0xf4,
			0x43, 0xab, 0x5e, 0x5d, 0xd8, 0x97, 0xbf, 0xab,
			0xf4, 0x2d, 0x34, 0x25, 0xee, 0x4c, 0xec, 0xfb,
			0x4d, 0x0b,
		},
		.hex =	"049484B0CD65EFC5"
			"5DC9E49171CBC7F1"
			"8E4439C2D307F06C"
			"B6EF77C084302CD2"
			"F2F0B5B66F0AF443"
			"AB5E5DD897BFABF4"
			"2D3425EE4CECFB4D"
			"0B",
		.oct_len = 57,
		.oct = {
			0x04, 0x94, 0x84, 0xb0, 0xcd, 0x65, 0xef, 0xc5,
			0x5d, 0xc9, 0xe4, 0x91, 0x71, 0xcb, 0xc7, 0xf1,
			0x8e, 0x44, 0x39, 0xc2, 0xd3, 0x07, 0xf0, 0x6c,
			0xb6, 0xef, 0x77, 0xc0, 0x84, 0x30, 0x2c, 0xd2,
			0xf2, 0xf0, 0xb5, 0xb6, 0x6f, 0x0a, 0xf4, 0x43,
			0xab, 0x5e, 0x5d, 0xd8, 0x97, 0xbf, 0xab, 0xf4,
			0x2d, 0x34, 0x25, 0xee, 0x4c, 0xec, 0xfb, 0x4d,
			0x0b,
		},
	},
	{
		.name = "secp256k1",
		.der_len = 118,
		.der = {
			0x30, 0x74, 0x02, 0x01, 0x01, 0x04, 0x20, 0xf2,
			0xe5, 0x5c, 0x24, 0x66, 0x01, 0x2b, 0x95, 0x96,
			0xbf, 0xbd, 0x0e, 0x33, 0x3d, 0xfd, 0x8a, 0x22,
			0x79, 0x12, 0xc5, 0x93, 0x28, 0x1b, 0x74, 0x39,
			0x61, 0x80, 0x1c, 0x17, 0xb1, 0x36, 0xab, 0xa0,
			0x07, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x0a,
			0xa1, 0x44, 0x03, 0x42, 0x00, 0x04, 0x9f, 0xd2,
			0xbe, 0xcc, 0xf8, 0x00, 0xe8, 0xd0, 0x40, 0x73,
			0x11, 0xb9, 0x34, 0x76, 0x68, 0xb2, 0x6b, 0x88,
			0xea, 0xa6, 0x64, 0x37, 0xe7, 0x06, 0xdf, 0x9f,
			0x20, 0xb8, 0xc3, 0x7f, 0x9f, 0x8f, 0xbc, 0x80,
			0x65, 0xe9, 0x73, 0xcb, 0x1d, 0xa1, 0xfa, 0x34,
			0x23, 0x66, 0xb9, 0x47, 0x89, 0xe9, 0x08, 0x92,
			0x5e, 0xb5, 0x37, 0x44, 0x40, 0x1c, 0x34, 0x6c,
			0xf2, 0xdb, 0x44, 0x71, 0x26, 0xeb,
		},
		.hex =	"049FD2BECCF800E8"
			"D0407311B9347668"
			"B26B88EAA66437E7"
			"06DF9F20B8C37F9F"
			"8FBC8065E973CB1D"
			"A1FA342366B94789"
			"E908925EB5374440"
			"1C346CF2DB447126"
			"EB",
		.oct_len = 65,
		.oct = {
			0x04, 0x9f, 0xd2, 0xbe, 0xcc, 0xf8, 0x00, 0xe8,
			0xd0, 0x40, 0x73, 0x11, 0xb9, 0x34, 0x76, 0x68,
			0xb2, 0x6b, 0x88, 0xea, 0xa6, 0x64, 0x37, 0xe7,
			0x06, 0xdf, 0x9f, 0x20, 0xb8, 0xc3, 0x7f, 0x9f,
			0x8f, 0xbc, 0x80, 0x65, 0xe9, 0x73, 0xcb, 0x1d,
			0xa1, 0xfa, 0x34, 0x23, 0x66, 0xb9, 0x47, 0x89,
			0xe9, 0x08, 0x92, 0x5e, 0xb5, 0x37, 0x44, 0x40,
			0x1c, 0x34, 0x6c, 0xf2, 0xdb, 0x44, 0x71, 0x26,
			0xeb,
		},
	},
	{
		.name = "secp384r1",
		.der_len = 167,
		.der = {
			0x30, 0x81, 0xa4, 0x02, 0x01, 0x01, 0x04, 0x30,
			0xa0, 0xd3, 0x78, 0x23, 0x51, 0xe1, 0x20, 0x5c,
			0xbe, 0x84, 0x11, 0x2f, 0x82, 0x55, 0xfc, 0xd1,
			0x5d, 0xae, 0xfc, 0x72, 0x60, 0x50, 0x3c, 0x2d,
			0x70, 0xb4, 0x00, 0xe2, 0xe6, 0x0a, 0xdf, 0xc5,
			0x56, 0xe6, 0xb8, 0x69, 0xf8, 0xad, 0xf5, 0xfc,
			0x95, 0xb3, 0x5b, 0x3d, 0xda, 0x6c, 0x5f, 0x74,
			0xa0, 0x07, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00,
			0x22, 0xa1, 0x64, 0x03, 0x62, 0x00, 0x04, 0xce,
			0x9a, 0x3b, 0x4b, 0x01, 0xe6, 0xc4, 0x5a, 0xfa,
			0x97, 0x03, 0xc1, 0xce, 0x18, 0xd5, 0x6c, 0x47,
			0x27, 0x4d, 0x6c, 0x9a, 0xbd, 0x47, 0xab, 0x20,
			0x0f, 0x99, 0x83, 0x19, 0x8b, 0xcb, 0x18, 0xd7,
			0xa3, 0xb2, 0xe7, 0x3b, 0xd0, 0xf1, 0xf3, 0x29,
			0xb2, 0x6d, 0x38, 0xd6, 0xcc, 0x8e, 0x5e, 0xf0,
			0xb2, 0xb9, 0xbd, 0x85, 0x2c, 0xab, 0x4b, 0xb6,
			0x9d, 0x98, 0xa1, 0xce, 0xf1, 0x8a, 0xdb, 0x92,
			0x75, 0x7d, 0xf7, 0x82, 0x4c, 0x0a, 0xc7, 0x3b,
			0x52, 0x6e, 0x97, 0xc6, 0x23, 0xc9, 0x6f, 0x3f,
			0xe5, 0xd2, 0xa2, 0x79, 0x47, 0xb0, 0x6e, 0x5f,
			0x85, 0x39, 0x94, 0x57, 0xbf, 0x54, 0x76,
		},
		.hex =	"04CE9A3B4B01E6C4"
			"5AFA9703C1CE18D5"
			"6C47274D6C9ABD47"
			"AB200F9983198BCB"
			"18D7A3B2E73BD0F1"
			"F329B26D38D6CC8E"
			"5EF0B2B9BD852CAB"
			"4BB69D98A1CEF18A"
			"DB92757DF7824C0A"
			"C73B526E97C623C9"
			"6F3FE5D2A27947B0"
			"6E5F85399457BF54"
			"76",
		.oct_len = 97,
		.oct = {
			0x04, 0xce, 0x9a, 0x3b, 0x4b, 0x01, 0xe6, 0xc4,
			0x5a, 0xfa, 0x97, 0x03, 0xc1, 0xce, 0x18, 0xd5,
			0x6c, 0x47, 0x27, 0x4d, 0x6c, 0x9a, 0xbd, 0x47,
			0xab, 0x20, 0x0f, 0x99, 0x83, 0x19, 0x8b, 0xcb,
			0x18, 0xd7, 0xa3, 0xb2, 0xe7, 0x3b, 0xd0, 0xf1,
			0xf3, 0x29, 0xb2, 0x6d, 0x38, 0xd6, 0xcc, 0x8e,
			0x5e, 0xf0, 0xb2, 0xb9, 0xbd, 0x85, 0x2c, 0xab,
			0x4b, 0xb6, 0x9d, 0x98, 0xa1, 0xce, 0xf1, 0x8a,
			0xdb, 0x92, 0x75, 0x7d, 0xf7, 0x82, 0x4c, 0x0a,
			0xc7, 0x3b, 0x52, 0x6e, 0x97, 0xc6, 0x23, 0xc9,
			0x6f, 0x3f, 0xe5, 0xd2, 0xa2, 0x79, 0x47, 0xb0,
			0x6e, 0x5f, 0x85, 0x39, 0x94, 0x57, 0xbf, 0x54,
			0x76,
		},
	},
	{
		.name = "secp521r1",
		.der_len = 223,
		.der = {
			0x30, 0x81, 0xdc, 0x02, 0x01, 0x01, 0x04, 0x42,
			0x01, 0x6e, 0xff, 0x5d, 0x18, 0x50, 0x5b, 0x09,
			0xf8, 0x38, 0x10, 0x6c, 0x54, 0x19, 0x59, 0xdb,
			0x30, 0xc5, 0x60, 0x28, 0xb1, 0x7f, 0xba, 0x22,
			0x06, 0x4d, 0x8a, 0x69, 0x53, 0xb0, 0xc5, 0x8f,
			0x17, 0x4d, 0x51, 0xc6, 0x2f, 0x41, 0x4e, 0xf0,
			0xab, 0xb4, 0x3a, 0x8f, 0x00, 0x6f, 0x32, 0xe7,
			0xe6, 0x56, 0xb7, 0xe9, 0xb1, 0xcd, 0x3a, 0x93,
			0xe6, 0x8f, 0xe6, 0x60, 0xb6, 0x80, 0xbd, 0x02,
			0xfb, 0x90, 0xa0, 0x07, 0x06, 0x05, 0x2b, 0x81,
			0x04, 0x00, 0x23, 0xa1, 0x81, 0x89, 0x03, 0x81,
			0x86, 0x00, 0x04, 0x01, 0xd2, 0xc3, 0x78, 0x41,
			0xb1, 0x86, 0x24, 0xca, 0x6d, 0x80, 0x5c, 0x97,
			0xcf, 0x96, 0xf0, 0x87, 0xb4, 0x25, 0xbe, 0x37,
			0x9a, 0xf3, 0xe5, 0x4a, 0x70, 0xd1, 0xe6, 0x36,
			0x9e, 0x69, 0xcc, 0xfb, 0x83, 0xd6, 0xa1, 0x62,
			0x6d, 0xa8, 0xe6, 0xca, 0xe7, 0x0e, 0x24, 0xe6,
			0x26, 0xcd, 0xc0, 0x0d, 0x2a, 0x01, 0x81, 0x6a,
			0xd6, 0x94, 0xf2, 0x90, 0xcd, 0x26, 0x68, 0x28,
			0x2c, 0x57, 0xd3, 0xf0, 0x37, 0x00, 0xbc, 0x5e,
			0xfa, 0xf9, 0x36, 0xcd, 0x0f, 0xeb, 0x4f, 0x82,
			0x17, 0x6a, 0xa0, 0x73, 0xd2, 0x48, 0xfc, 0xfb,
			0xf0, 0x54, 0xc3, 0x23, 0x29, 0x76, 0xc7, 0x21,
			0x98, 0x09, 0x29, 0x8b, 0xce, 0x6e, 0x6b, 0xe3,
			0x97, 0x94, 0xb2, 0x30, 0xaa, 0xf6, 0x43, 0x5c,
			0x15, 0xd7, 0xb8, 0xdb, 0x06, 0x92, 0xa8, 0x36,
			0x8f, 0x89, 0xb6, 0x39, 0x2c, 0x2c, 0x23, 0x0a,
			0xb6, 0x95, 0x9c, 0x6b, 0xce, 0xc4, 0x8e,
		},
		.hex =	"0401D2C37841B186"
			"24CA6D805C97CF96"
			"F087B425BE379AF3"
			"E54A70D1E6369E69"
			"CCFB83D6A1626DA8"
			"E6CAE70E24E626CD"
			"C00D2A01816AD694"
			"F290CD2668282C57"
			"D3F03700BC5EFAF9"
			"36CD0FEB4F82176A"
			"A073D248FCFBF054"
			"C3232976C7219809"
			"298BCE6E6BE39794"
			"B230AAF6435C15D7"
			"B8DB0692A8368F89"
			"B6392C2C230AB695"
			"9C6BCEC48E",
		.oct_len = 133,
		.oct = {
			0x04, 0x01, 0xd2, 0xc3, 0x78, 0x41, 0xb1, 0x86,
			0x24, 0xca, 0x6d, 0x80, 0x5c, 0x97, 0xcf, 0x96,
			0xf0, 0x87, 0xb4, 0x25, 0xbe, 0x37, 0x9a, 0xf3,
			0xe5, 0x4a, 0x70, 0xd1, 0xe6, 0x36, 0x9e, 0x69,
			0xcc, 0xfb, 0x83, 0xd6, 0xa1, 0x62, 0x6d, 0xa8,
			0xe6, 0xca, 0xe7, 0x0e, 0x24, 0xe6, 0x26, 0xcd,
			0xc0, 0x0d, 0x2a, 0x01, 0x81, 0x6a, 0xd6, 0x94,
			0xf2, 0x90, 0xcd, 0x26, 0x68, 0x28, 0x2c, 0x57,
			0xd3, 0xf0, 0x37, 0x00, 0xbc, 0x5e, 0xfa, 0xf9,
			0x36, 0xcd, 0x0f, 0xeb, 0x4f, 0x82, 0x17, 0x6a,
			0xa0, 0x73, 0xd2, 0x48, 0xfc, 0xfb, 0xf0, 0x54,
			0xc3, 0x23, 0x29, 0x76, 0xc7, 0x21, 0x98, 0x09,
			0x29, 0x8b, 0xce, 0x6e, 0x6b, 0xe3, 0x97, 0x94,
			0xb2, 0x30, 0xaa, 0xf6, 0x43, 0x5c, 0x15, 0xd7,
			0xb8, 0xdb, 0x06, 0x92, 0xa8, 0x36, 0x8f, 0x89,
			0xb6, 0x39, 0x2c, 0x2c, 0x23, 0x0a, 0xb6, 0x95,
			0x9c, 0x6b, 0xce, 0xc4, 0x8e,
		},
	},
	{
		.name = "prime256v1",
		.der_len = 121,
		.der = {
			0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x6c,
			0x83, 0x81, 0x90, 0x65, 0x7b, 0x45, 0x98, 0x66,
			0x4b, 0x91, 0x8e, 0xcf, 0x71, 0x61, 0x22, 0xb6,
			0xd6, 0x93, 0x74, 0x84, 0xa3, 0xc6, 0x44, 0x71,
			0x25, 0xc5, 0xef, 0x77, 0x52, 0xd2, 0x32, 0xa0,
			0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
			0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42, 0x00,
			0x04, 0x96, 0x8a, 0xc3, 0x66, 0x1e, 0xf7, 0xcf,
			0xf6, 0xcc, 0x4e, 0x73, 0xae, 0xe2, 0x64, 0xc4,
			0x56, 0x5f, 0x2d, 0xfe, 0xde, 0xac, 0x92, 0xbe,
			0x10, 0x40, 0x37, 0xce, 0x24, 0x12, 0x30, 0x19,
			0x08, 0x66, 0xcf, 0x90, 0xc9, 0x37, 0x03, 0xd1,
			0xd5, 0x8d, 0xaa, 0x18, 0x2a, 0xbc, 0xed, 0x82,
			0x32, 0xc9, 0x43, 0x4b, 0x98, 0x7f, 0xdc, 0xb1,
			0x0b, 0xa6, 0xdd, 0x16, 0xc5, 0x8d, 0x5a, 0xcf,
			0xe3,
		},
		.hex =	"04968AC3661EF7CF"
			"F6CC4E73AEE264C4"
			"565F2DFEDEAC92BE"
			"104037CE24123019"
			"0866CF90C93703D1"
			"D58DAA182ABCED82"
			"32C9434B987FDCB1"
			"0BA6DD16C58D5ACF"
			"E3",
		.oct_len = 65,
		.oct = {
			0x04, 0x96, 0x8a, 0xc3, 0x66, 0x1e, 0xf7, 0xcf,
			0xf6, 0xcc, 0x4e, 0x73, 0xae, 0xe2, 0x64, 0xc4,
			0x56, 0x5f, 0x2d, 0xfe, 0xde, 0xac, 0x92, 0xbe,
			0x10, 0x40, 0x37, 0xce, 0x24, 0x12, 0x30, 0x19,
			0x08, 0x66, 0xcf, 0x90, 0xc9, 0x37, 0x03, 0xd1,
			0xd5, 0x8d, 0xaa, 0x18, 0x2a, 0xbc, 0xed, 0x82,
			0x32, 0xc9, 0x43, 0x4b, 0x98, 0x7f, 0xdc, 0xb1,
			0x0b, 0xa6, 0xdd, 0x16, 0xc5, 0x8d, 0x5a, 0xcf,
			0xe3,
		},
	},
	{
		.name = "brainpoolP224r1",
		.der_len = 110,
		.der = {
			0x30, 0x6c, 0x02, 0x01, 0x01, 0x04, 0x1c, 0xae,
			0x9c, 0xe1, 0x9c, 0xaf, 0xbd, 0x9d, 0xec, 0x9a,
			0xe4, 0xdc, 0x5a, 0x9f, 0xdb, 0x0d, 0x51, 0x65,
			0xe2, 0x49, 0xa7, 0x35, 0xea, 0xbc, 0x8b, 0x4a,
			0x27, 0xfd, 0xa8, 0xa0, 0x0b, 0x06, 0x09, 0x2b,
			0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x05,
			0xa1, 0x3c, 0x03, 0x3a, 0x00, 0x04, 0x30, 0x88,
			0x20, 0xb3, 0x47, 0x2e, 0x40, 0x1e, 0x68, 0xae,
			0xe1, 0x00, 0x58, 0xa2, 0x4b, 0xb9, 0xac, 0xf7,
			0x5d, 0xc1, 0xee, 0xf5, 0xfb, 0xdd, 0x34, 0xae,
			0xbe, 0x3c, 0x93, 0xdd, 0xc2, 0xd8, 0x3a, 0x9a,
			0x72, 0x65, 0x41, 0xac, 0xcc, 0x25, 0x6a, 0xcf,
			0x71, 0x50, 0x6b, 0xed, 0xe3, 0xc5, 0xd4, 0xe9,
			0x1b, 0x59, 0x92, 0xa4, 0xa8, 0x81,
		},
		.hex =	"04308820B3472E40"
			"1E68AEE10058A24B"
			"B9ACF75DC1EEF5FB"
			"DD34AEBE3C93DDC2"
			"D83A9A726541ACCC"
			"256ACF71506BEDE3"
			"C5D4E91B5992A4A8"
			"81",
		.oct_len = 57,
		.oct = {
			0x04, 0x30, 0x88, 0x20, 0xb3, 0x47, 0x2e, 0x40,
			0x1e, 0x68, 0xae, 0xe1, 0x00, 0x58, 0xa2, 0x4b,
			0xb9, 0xac, 0xf7, 0x5d, 0xc1, 0xee, 0xf5, 0xfb,
			0xdd, 0x34, 0xae, 0xbe, 0x3c, 0x93, 0xdd, 0xc2,
			0xd8, 0x3a, 0x9a, 0x72, 0x65, 0x41, 0xac, 0xcc,
			0x25, 0x6a, 0xcf, 0x71, 0x50, 0x6b, 0xed, 0xe3,
			0xc5, 0xd4, 0xe9, 0x1b, 0x59, 0x92, 0xa4, 0xa8,
			0x81,
		},
	},
	{
		.name = "brainpoolP224t1",
		.der_len = 110,
		.der = {
			0x30, 0x6c, 0x02, 0x01, 0x01, 0x04, 0x1c, 0xc0,
			0x10, 0xc2, 0xf4, 0xab, 0xbb, 0x00, 0xa0, 0x14,
			0x62, 0x13, 0x24, 0xc2, 0x8d, 0x9e, 0x78, 0x92,
			0x24, 0x3b, 0xa8, 0xd0, 0xf1, 0x06, 0x69, 0x77,
			0x1d, 0x9d, 0x6c, 0xa0, 0x0b, 0x06, 0x09, 0x2b,
			0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x06,
			0xa1, 0x3c, 0x03, 0x3a, 0x00, 0x04, 0x0b, 0xbf,
			0x95, 0xea, 0x8b, 0xa8, 0x24, 0x94, 0x68, 0x54,
			0x69, 0xd9, 0x55, 0xa5, 0x36, 0x34, 0xf1, 0x4a,
			0x45, 0xf9, 0x9f, 0x66, 0x7b, 0x5d, 0xc9, 0x8b,
			0x0a, 0x7a, 0x5d, 0xef, 0x25, 0x9a, 0xa3, 0x86,
			0xe1, 0x98, 0x1b, 0x5b, 0xe3, 0xe3, 0x55, 0xa0,
			0x59, 0xb2, 0xfd, 0xe7, 0xdf, 0x41, 0xff, 0x4f,
			0x36, 0xe9, 0x56, 0xe9, 0x07, 0xc2,
		},
		.hex =	"040BBF95EA8BA824"
			"94685469D955A536"
			"34F14A45F99F667B"
			"5DC98B0A7A5DEF25"
			"9AA386E1981B5BE3"
			"E355A059B2FDE7DF"
			"41FF4F36E956E907"
			"C2",
		.oct_len = 57,
		.oct = {
			0x04, 0x0b, 0xbf, 0x95, 0xea, 0x8b, 0xa8, 0x24,
			0x94, 0x68, 0x54, 0x69, 0xd9, 0x55, 0xa5, 0x36,
			0x34, 0xf1, 0x4a, 0x45, 0xf9, 0x9f, 0x66, 0x7b,
			0x5d, 0xc9, 0x8b, 0x0a, 0x7a, 0x5d, 0xef, 0x25,
			0x9a, 0xa3, 0x86, 0xe1, 0x98, 0x1b, 0x5b, 0xe3,
			0xe3, 0x55, 0xa0, 0x59, 0xb2, 0xfd, 0xe7, 0xdf,
			0x41, 0xff, 0x4f, 0x36, 0xe9, 0x56, 0xe9, 0x07,
			0xc2,
		},
	},
	{
		.name = "brainpoolP256r1",
		.der_len = 122,
		.der = {
			0x30, 0x78, 0x02, 0x01, 0x01, 0x04, 0x20, 0x98,
			0x48, 0x86, 0x7a, 0x5b, 0x60, 0xb9, 0xba, 0xab,
			0xa2, 0x34, 0x55, 0x43, 0x17, 0xbc, 0xfd, 0xc2,
			0x18, 0xc9, 0xa8, 0x4b, 0x28, 0xbe, 0x5e, 0xa0,
			0x37, 0xab, 0x0d, 0xe0, 0x54, 0x65, 0x87, 0xa0,
			0x0b, 0x06, 0x09, 0x2b, 0x24, 0x03, 0x03, 0x02,
			0x08, 0x01, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42,
			0x00, 0x04, 0x08, 0xd7, 0x77, 0xf5, 0x10, 0xa9,
			0x83, 0xd9, 0xdf, 0xfd, 0x40, 0xe4, 0x42, 0xce,
			0xd8, 0x3b, 0x9b, 0xef, 0xe6, 0x4d, 0x4e, 0xca,
			0x2d, 0xea, 0xe6, 0x69, 0xfe, 0xd3, 0xa9, 0x3f,
			0x30, 0xfa, 0x7e, 0xa7, 0x14, 0x9d, 0x37, 0x77,
			0xc5, 0xcc, 0x1e, 0x32, 0xf6, 0xce, 0x17, 0x91,
			0x1b, 0xeb, 0xa3, 0x8f, 0xce, 0x70, 0x55, 0xc1,
			0xcf, 0xe3, 0x38, 0xa0, 0xb7, 0x95, 0x85, 0x26,
			0xf5, 0xb2,
		},
		.hex =	"0408D777F510A983"
			"D9DFFD40E442CED8"
			"3B9BEFE64D4ECA2D"
			"EAE669FED3A93F30"
			"FA7EA7149D3777C5"
			"CC1E32F6CE17911B"
			"EBA38FCE7055C1CF"
			"E338A0B7958526F5"
			"B2",
		.oct_len = 65,
		.oct = {
			0x04, 0x08, 0xd7, 0x77, 0xf5, 0x10, 0xa9, 0x83,
			0xd9, 0xdf, 0xfd, 0x40, 0xe4, 0x42, 0xce, 0xd8,
			0x3b, 0x9b, 0xef, 0xe6, 0x4d, 0x4e, 0xca, 0x2d,
			0xea, 0xe6, 0x69, 0xfe, 0xd3, 0xa9, 0x3f, 0x30,
			0xfa, 0x7e, 0xa7, 0x14, 0x9d, 0x37, 0x77, 0xc5,
			0xcc, 0x1e, 0x32, 0xf6, 0xce, 0x17, 0x91, 0x1b,
			0xeb, 0xa3, 0x8f, 0xce, 0x70, 0x55, 0xc1, 0xcf,
			0xe3, 0x38, 0xa0, 0xb7, 0x95, 0x85, 0x26, 0xf5,
			0xb2,
		},
	},
	{
		.name = "brainpoolP256t1",
		.der_len = 122,
		.der = {
			0x30, 0x78, 0x02, 0x01, 0x01, 0x04, 0x20, 0x21,
			0xb0, 0x02, 0x6c, 0xac, 0x68, 0xe7, 0xaf, 0xb6,
			0x8b, 0xb9, 0xe6, 0x68, 0xec, 0x2a, 0xfa, 0x55,
			0xb0, 0xd4, 0x23, 0xaa, 0xb9, 0xfb, 0x7c, 0xf5,
			0xd1, 0x2f, 0x61, 0x52, 0x19, 0xc0, 0x19, 0xa0,
			0x0b, 0x06, 0x09, 0x2b, 0x24, 0x03, 0x03, 0x02,
			0x08, 0x01, 0x01, 0x08, 0xa1, 0x44, 0x03, 0x42,
			0x00, 0x04, 0x7b, 0x1d, 0x55, 0x29, 0x0b, 0x0a,
			0x0d, 0x02, 0x7a, 0x1d, 0x72, 0x53, 0xc1, 0x84,
			0xb9, 0x90, 0x00, 0xb9, 0x45, 0xe5, 0xa5, 0xd4,
			0xee, 0xd6, 0x9a, 0x1d, 0xb0, 0x3a, 0x91, 0xa8,
			0x95, 0x56, 0x58, 0x32, 0xcb, 0xf3, 0x28, 0x95,
			0xa6, 0x82, 0x46, 0xe6, 0x0a, 0x33, 0x00, 0xd1,
			0x0c, 0x61, 0xac, 0x1e, 0xa0, 0xb0, 0xad, 0x3a,
			0xbd, 0x1e, 0x53, 0x8d, 0x26, 0x96, 0xab, 0x44,
			0x6b, 0x84,
		},
		.hex =	"047B1D55290B0A0D"
			"027A1D7253C184B9"
			"9000B945E5A5D4EE"
			"D69A1DB03A91A895"
			"565832CBF32895A6"
			"8246E60A3300D10C"
			"61AC1EA0B0AD3ABD"
			"1E538D2696AB446B"
			"84",
		.oct_len = 65,
		.oct = {
			0x04, 0x7b, 0x1d, 0x55, 0x29, 0x0b, 0x0a, 0x0d,
			0x02, 0x7a, 0x1d, 0x72, 0x53, 0xc1, 0x84, 0xb9,
			0x90, 0x00, 0xb9, 0x45, 0xe5, 0xa5, 0xd4, 0xee,
			0xd6, 0x9a, 0x1d, 0xb0, 0x3a, 0x91, 0xa8, 0x95,
			0x56, 0x58, 0x32, 0xcb, 0xf3, 0x28, 0x95, 0xa6,
			0x82, 0x46, 0xe6, 0x0a, 0x33, 0x00, 0xd1, 0x0c,
			0x61, 0xac, 0x1e, 0xa0, 0xb0, 0xad, 0x3a, 0xbd,
			0x1e, 0x53, 0x8d, 0x26, 0x96, 0xab, 0x44, 0x6b,
			0x84,
		},
	},
	{
		.name = "brainpoolP320r1",
		.der_len = 147,
		.der = {
			0x30, 0x81, 0x90, 0x02, 0x01, 0x01, 0x04, 0x28,
			0x1f, 0x7e, 0x6e, 0x51, 0x13, 0x87, 0x9b, 0x09,
			0x2b, 0x3f, 0x1c, 0x39, 0x0f, 0x9f, 0x48, 0x79,
			0x48, 0xa1, 0x44, 0xe0, 0x5c, 0x73, 0x2a, 0x6c,
			0x6e, 0x60, 0x59, 0xd0, 0xf6, 0x6f, 0x32, 0x0e,
			0x6b, 0x2b, 0x0c, 0xf2, 0x39, 0xbd, 0x42, 0xaf,
			0xa0, 0x0b, 0x06, 0x09, 0x2b, 0x24, 0x03, 0x03,
			0x02, 0x08, 0x01, 0x01, 0x09, 0xa1, 0x54, 0x03,
			0x52, 0x00, 0x04, 0xa3, 0x37, 0x85, 0xe2, 0xf2,
			0x5f, 0xa1, 0x71, 0xa6, 0x75, 0xfe, 0xa1, 0xea,
			0x66, 0x35, 0x7a, 0x53, 0x71, 0x24, 0x83, 0xcd,
			0xc9, 0x5d, 0x3f, 0x43, 0xc4, 0x97, 0x6d, 0xcc,
			0x0c, 0xed, 0x9a, 0x51, 0x51, 0x7d, 0x1e, 0xd0,
			0xea, 0xd2, 0x8c, 0x36, 0xb0, 0x93, 0x62, 0xeb,
			0x26, 0xda, 0xe1, 0xef, 0xc7, 0x1a, 0xfa, 0x0c,
			0xea, 0x84, 0x7a, 0xf1, 0x50, 0x2c, 0xee, 0xf1,
			0xb3, 0xcc, 0xb7, 0xa0, 0x98, 0x5d, 0xde, 0xc2,
			0x54, 0xcc, 0x11, 0x2a, 0x84, 0xc6, 0x79, 0x10,
			0x7b, 0x20, 0x26,
		},
		.hex =	"04A33785E2F25FA1"
			"71A675FEA1EA6635"
			"7A53712483CDC95D"
			"3F43C4976DCC0CED"
			"9A51517D1ED0EAD2"
			"8C36B09362EB26DA"
			"E1EFC71AFA0CEA84"
			"7AF1502CEEF1B3CC"
			"B7A0985DDEC254CC"
			"112A84C679107B20"
			"26",
		.oct_len = 81,
		.oct = {
			0x04, 0xa3, 0x37, 0x85, 0xe2, 0xf2, 0x5f, 0xa1,
			0x71, 0xa6, 0x75, 0xfe, 0xa1, 0xea, 0x66, 0x35,
			0x7a, 0x53, 0x71, 0x24, 0x83, 0xcd, 0xc9, 0x5d,
			0x3f, 0x43, 0xc4, 0x97, 0x6d, 0xcc, 0x0c, 0xed,
			0x9a, 0x51, 0x51, 0x7d, 0x1e, 0xd0, 0xea, 0xd2,
			0x8c, 0x36, 0xb0, 0x93, 0x62, 0xeb, 0x26, 0xda,
			0xe1, 0xef, 0xc7, 0x1a, 0xfa, 0x0c, 0xea, 0x84,
			0x7a, 0xf1, 0x50, 0x2c, 0xee, 0xf1, 0xb3, 0xcc,
			0xb7, 0xa0, 0x98, 0x5d, 0xde, 0xc2, 0x54, 0xcc,
			0x11, 0x2a, 0x84, 0xc6, 0x79, 0x10, 0x7b, 0x20,
			0x26,
		},
	},
	{
		.name = "brainpoolP320t1",
		.der_len = 147,
		.der = {
			0x30, 0x81, 0x90, 0x02, 0x01, 0x01, 0x04, 0x28,
			0x4a, 0x8a, 0x25, 0xd9, 0xfa, 0x04, 0x8f, 0x6b,
			0xd5, 0xa3, 0x83, 0xd6, 0xf2, 0xca, 0x82, 0xd5,
			0xe2, 0x8e, 0x3f, 0xe6, 0x07, 0xcd, 0xa2, 0x22,
			0xa0, 0x3f, 0x0a, 0x7c, 0x09, 0x0f, 0x9f, 0xf4,
			0xe3, 0x59, 0x4b, 0x43, 0x0c, 0xfd, 0x5a, 0x96,
			0xa0, 0x0b, 0x06, 0x09, 0x2b, 0x24, 0x03, 0x03,
			0x02, 0x08, 0x01, 0x01, 0x0a, 0xa1, 0x54, 0x03,
			0x52, 0x00, 0x04, 0x3d, 0x8c, 0x4c, 0xbb, 0x30,
			0x3f, 0xa0, 0x84, 0x61, 0x43, 0x50, 0x23, 0x70,
			0xe3, 0x70, 0xb6, 0x4d, 0x89, 0xc8, 0x95, 0xa0,
			0x09, 0xae, 0xfc, 0x55, 0x9c, 0x2f, 0xef, 0x16,
			0xc0, 0x72, 0x3c, 0x3e, 0x07, 0xa1, 0xbb, 0xd8,
			0x8a, 0xfa, 0xaf, 0x9f, 0xaf, 0x07, 0x7c, 0x15,
			0x4d, 0x75, 0x6b, 0xf5, 0x25, 0x65, 0x5b, 0xc4,
			0x78, 0x59, 0x22, 0xe5, 0x92, 0x5c, 0xc2, 0x8f,
			0xdc, 0x97, 0x59, 0x82, 0xc5, 0x0d, 0x24, 0x70,
			0x03, 0xbe, 0xa5, 0x05, 0x88, 0x16, 0x47, 0x9f,
			0xe5, 0x3b, 0xb8,
		},
		.hex =	"043D8C4CBB303FA0"
			"846143502370E370"
			"B64D89C895A009AE"
			"FC559C2FEF16C072"
			"3C3E07A1BBD88AFA"
			"AF9FAF077C154D75"
			"6BF525655BC47859"
			"22E5925CC28FDC97"
			"5982C50D247003BE"
			"A5058816479FE53B"
			"B8",
		.oct_len = 81,
		.oct = {
			0x04, 0x3d, 0x8c, 0x4c, 0xbb, 0x30, 0x3f, 0xa0,
			0x84, 0x61, 0x43, 0x50, 0x23, 0x70, 0xe3, 0x70,
			0xb6, 0x4d, 0x89, 0xc8, 0x95, 0xa0, 0x09, 0xae,
			0xfc, 0x55, 0x9c, 0x2f, 0xef, 0x16, 0xc0, 0x72,
			0x3c, 0x3e, 0x07, 0xa1, 0xbb, 0xd8, 0x8a, 0xfa,
			0xaf, 0x9f, 0xaf, 0x07, 0x7c, 0x15, 0x4d, 0x75,
			0x6b, 0xf5, 0x25, 0x65, 0x5b, 0xc4, 0x78, 0x59,
			0x22, 0xe5, 0x92, 0x5c, 0xc2, 0x8f, 0xdc, 0x97,
			0x59, 0x82, 0xc5, 0x0d, 0x24, 0x70, 0x03, 0xbe,
			0xa5, 0x05, 0x88, 0x16, 0x47, 0x9f, 0xe5, 0x3b,
			0xb8,
		},
	},
	{
		.name = "brainpoolP384r1",
		.der_len = 171,
		.der = {
			0x30, 0x81, 0xa8, 0x02, 0x01, 0x01, 0x04, 0x30,
			0x02, 0x57, 0xb6, 0xfe, 0x31, 0xda, 0x87, 0xcd,
			0x68, 0x2a, 0x67, 0x98, 0xd1, 0x72, 0x5c, 0xd8,
			0x2e, 0x25, 0xf9, 0x39, 0x36, 0x3b, 0x93, 0x98,
			0x79, 0x81, 0xc0, 0x7e, 0xa3, 0x44, 0x99, 0xd8,
			0xe5, 0x07, 0x1f, 0xea, 0xa1, 0x66, 0x60, 0x00,
			0x29, 0x84, 0xa3, 0x35, 0xdd, 0x64, 0x96, 0x93,
			0xa0, 0x0b, 0x06, 0x09, 0x2b, 0x24, 0x03, 0x03,
			0x02, 0x08, 0x01, 0x01, 0x0b, 0xa1, 0x64, 0x03,
			0x62, 0x00, 0x04, 0x4f, 0x79, 0xe0, 0xe7, 0xf9,
			0x57, 0x33, 0xf9, 0x9d, 0x6a, 0x5c, 0x00, 0x6e,
			0xb8, 0xbc, 0xe6, 0x4f, 0x70, 0x1d, 0x73, 0x02,
			0x5c, 0x87, 0xa1, 0x88, 0xea, 0xe3, 0x57, 0x5c,
			0x1a, 0x27, 0x40, 0xcf, 0xcc, 0x6f, 0x7e, 0x6d,
			0xfd, 0x96, 0x0b, 0xaa, 0xc5, 0x02, 0x92, 0x10,
			0x6d, 0x7e, 0xd5, 0x17, 0xda, 0xab, 0x52, 0x9b,
			0xcd, 0x87, 0x08, 0x64, 0x2a, 0x61, 0x03, 0xc9,
			0xfe, 0x97, 0x79, 0xf0, 0x5c, 0x84, 0x72, 0x50,
			0x53, 0x95, 0x56, 0x7a, 0x97, 0xce, 0x36, 0x13,
			0x23, 0x78, 0x31, 0x82, 0x36, 0x07, 0x45, 0xad,
			0x92, 0x00, 0xaf, 0x3d, 0xe8, 0x5a, 0x7d, 0x7b,
			0x63, 0xc2, 0xde,
		},
		.hex =	"044F79E0E7F95733"
			"F99D6A5C006EB8BC"
			"E64F701D73025C87"
			"A188EAE3575C1A27"
			"40CFCC6F7E6DFD96"
			"0BAAC50292106D7E"
			"D517DAAB529BCD87"
			"08642A6103C9FE97"
			"79F05C8472505395"
			"567A97CE36132378"
			"3182360745AD9200"
			"AF3DE85A7D7B63C2"
			"DE",
		.oct_len = 97,
		.oct = {
			0x04, 0x4f, 0x79, 0xe0, 0xe7, 0xf9, 0x57, 0x33,
			0xf9, 0x9d, 0x6a, 0x5c, 0x00, 0x6e, 0xb8, 0xbc,
			0xe6, 0x4f, 0x70, 0x1d, 0x73, 0x02, 0x5c, 0x87,
			0xa1, 0x88, 0xea, 0xe3, 0x57, 0x5c, 0x1a, 0x27,
			0x40, 0xcf, 0xcc, 0x6f, 0x7e, 0x6d, 0xfd, 0x96,
			0x0b, 0xaa, 0xc5, 0x02, 0x92, 0x10, 0x6d, 0x7e,
			0xd5, 0x17, 0xda, 0xab, 0x52, 0x9b, 0xcd, 0x87,
			0x08, 0x64, 0x2a, 0x61, 0x03, 0xc9, 0xfe, 0x97,
			0x79, 0xf0, 0x5c, 0x84, 0x72, 0x50, 0x53, 0x95,
			0x56, 0x7a, 0x97, 0xce, 0x36, 0x13, 0x23, 0x78,
			0x31, 0x82, 0x36, 0x07, 0x45, 0xad, 0x92, 0x00,
			0xaf, 0x3d, 0xe8, 0x5a, 0x7d, 0x7b, 0x63, 0xc2,
			0xde,
		},
	},
	{
		.name = "brainpoolP384t1",
		.der_len = 171,
		.der = {
			0x30, 0x81, 0xa8, 0x02, 0x01, 0x01, 0x04, 0x30,
			0x35, 0xea, 0xbc, 0x66, 0xd4, 0xa9, 0xc0, 0xe1,
			0xcd, 0xd4, 0xe5, 0xb1, 0xac, 0x8f, 0x66, 0x82,
			0x56, 0xc1, 0xbd, 0xf2, 0xf5, 0x30, 0x95, 0xab,
			0x30, 0xaa, 0xc0, 0xc3, 0x07, 0xca, 0x97, 0xc9,
			0x53, 0x45, 0xd3, 0xff, 0xbf, 0xfe, 0xdf, 0x39,
			0x32, 0x40, 0xe0, 0x45, 0x15, 0xa7, 0x22, 0x5f,
			0xa0, 0x0b, 0x06, 0x09, 0x2b, 0x24, 0x03, 0x03,
			0x02, 0x08, 0x01, 0x01, 0x0c, 0xa1, 0x64, 0x03,
			0x62, 0x00, 0x04, 0x85, 0xdb, 0x57, 0x4b, 0x71,
			0xb1, 0x65, 0x93, 0x51, 0x83, 0x7c, 0xb3, 0x24,
			0x07, 0x6b, 0x7b, 0x57, 0x33, 0x73, 0x3a, 0xa6,
			0x14, 0x86, 0x83, 0xa8, 0x88, 0x81, 0xf2, 0x90,
			0xdf, 0x93, 0x4f, 0x67, 0x41, 0xef, 0xcb, 0x35,
			0x14, 0xad, 0x4c, 0x67, 0x0b, 0xdb, 0x86, 0x03,
			0x5e, 0x6d, 0x5e, 0x7e, 0x4b, 0x0f, 0x73, 0x9e,
			0x73, 0x50, 0x86, 0x29, 0x09, 0x7c, 0x38, 0xfc,
			0xbe, 0xaf, 0x59, 0x9c, 0x69, 0xdf, 0xb4, 0x60,
			0x14, 0x3e, 0xb2, 0x1a, 0x72, 0x86, 0x57, 0xcb,
			0x6b, 0x42, 0x20, 0x67, 0x7f, 0xbc, 0xa8, 0x57,
			0x88, 0x76, 0x72, 0x9a, 0xb4, 0xea, 0xc0, 0x48,
			0x01, 0x5d, 0x8e,
		},
		.hex =	"0485DB574B71B165"
			"9351837CB324076B"
			"7B5733733AA61486"
			"83A88881F290DF93"
			"4F6741EFCB3514AD"
			"4C670BDB86035E6D"
			"5E7E4B0F739E7350"
			"8629097C38FCBEAF"
			"599C69DFB460143E"
			"B21A728657CB6B42"
			"20677FBCA8578876"
			"729AB4EAC048015D"
			"8E",
		.oct_len = 97,
		.oct = {
			0x04, 0x85, 0xdb, 0x57, 0x4b, 0x71, 0xb1, 0x65,
			0x93, 0x51, 0x83, 0x7c, 0xb3, 0x24, 0x07, 0x6b,
			0x7b, 0x57, 0x33, 0x73, 0x3a, 0xa6, 0x14, 0x86,
			0x83, 0xa8, 0x88, 0x81, 0xf2, 0x90, 0xdf, 0x93,
			0x4f, 0x67, 0x41, 0xef, 0xcb, 0x35, 0x14, 0xad,
			0x4c, 0x67, 0x0b, 0xdb, 0x86, 0x03, 0x5e, 0x6d,
			0x5e, 0x7e, 0x4b, 0x0f, 0x73, 0x9e, 0x73, 0x50,
			0x86, 0x29, 0x09, 0x7c, 0x38, 0xfc, 0xbe, 0xaf,
			0x59, 0x9c, 0x69, 0xdf, 0xb4, 0x60, 0x14, 0x3e,
			0xb2, 0x1a, 0x72, 0x86, 0x57, 0xcb, 0x6b, 0x42,
			0x20, 0x67, 0x7f, 0xbc, 0xa8, 0x57, 0x88, 0x76,
			0x72, 0x9a, 0xb4, 0xea, 0xc0, 0x48, 0x01, 0x5d,
			0x8e,
		},
	},
	{
		.name = "brainpoolP512r1",
		.der_len = 221,
		.der = {
			0x30, 0x81, 0xda, 0x02, 0x01, 0x01, 0x04, 0x40,
			0x7e, 0x04, 0x7d, 0xab, 0x42, 0xc6, 0xdb, 0x95,
			0xfb, 0x22, 0x0b, 0xe4, 0x09, 0xff, 0x4a, 0x1e,
			0x7b, 0x42, 0x62, 0x82, 0x41, 0xf4, 0x1e, 0xc2,
			0x1f, 0x9e, 0x52, 0xea, 0xce, 0x1b, 0x75, 0x07,
			0x7c, 0xaf, 0x49, 0xdf, 0xf3, 0x20, 0xfa, 0x88,
			0x23, 0xc4, 0x5e, 0x6d, 0x82, 0x45, 0x32, 0x19,
			0x04, 0x4a, 0x3d, 0x80, 0xa1, 0xa8, 0x99, 0x09,
			0xce, 0x78, 0xde, 0x32, 0x18, 0xf4, 0x83, 0x2c,
			0xa0, 0x0b, 0x06, 0x09, 0x2b, 0x24, 0x03, 0x03,
			0x02, 0x08, 0x01, 0x01, 0x0d, 0xa1, 0x81, 0x85,
			0x03, 0x81, 0x82, 0x00, 0x04, 0x1e, 0x99, 0xea,
			0x54, 0xb6, 0x1a, 0x4f, 0x44, 0x25, 0xf4, 0xf8,
			0xbe, 0x33, 0x7c, 0xd1, 0x62, 0x35, 0xf5, 0xd1,
			0x8e, 0x9f, 0xae, 0xa8, 0x8f, 0x6d, 0x61, 0x27,
			0x2d, 0x2a, 0xb1, 0x96, 0x48, 0x6d, 0xb2, 0x63,
			0x05, 0x9f, 0xec, 0xa1, 0xcd, 0x65, 0x45, 0xc8,
			0xcd, 0xf1, 0xa4, 0xba, 0x20, 0xb7, 0xe4, 0xc7,
			0x92, 0x3c, 0x1f, 0x16, 0xf4, 0x5b, 0x75, 0xe4,
			0x2a, 0x2e, 0x44, 0x72, 0x65, 0x63, 0xc3, 0x78,
			0x54, 0x50, 0xcb, 0x50, 0xe0, 0xbe, 0xe7, 0x6f,
			0x2a, 0xdc, 0x24, 0x7b, 0xf8, 0x4b, 0xa8, 0xe2,
			0x1b, 0x27, 0x00, 0x2d, 0xe8, 0x99, 0xdc, 0x5f,
			0xa4, 0x43, 0xa8, 0xf7, 0xb1, 0x55, 0xea, 0xd7,
			0x02, 0x09, 0x08, 0x97, 0x5f, 0x21, 0x1e, 0x16,
			0xa0, 0xd8, 0x27, 0xe4, 0x5e, 0x3a, 0xa5, 0x51,
			0x68, 0xe7, 0x19, 0xc1, 0x7d, 0xb6, 0x9d, 0xb9,
			0xc6, 0xc2, 0x1b, 0x48, 0x7f,
		},
		.hex =	"041E99EA54B61A4F"
			"4425F4F8BE337CD1"
			"6235F5D18E9FAEA8"
			"8F6D61272D2AB196"
			"486DB263059FECA1"
			"CD6545C8CDF1A4BA"
			"20B7E4C7923C1F16"
			"F45B75E42A2E4472"
			"6563C3785450CB50"
			"E0BEE76F2ADC247B"
			"F84BA8E21B27002D"
			"E899DC5FA443A8F7"
			"B155EAD702090897"
			"5F211E16A0D827E4"
			"5E3AA55168E719C1"
			"7DB69DB9C6C21B48"
			"7F",
		.oct_len = 129,
		.oct = {
			0x04, 0x1e, 0x99, 0xea, 0x54, 0xb6, 0x1a, 0x4f,
			0x44, 0x25, 0xf4, 0xf8, 0xbe, 0x33, 0x7c, 0xd1,
			0x62, 0x35, 0xf5, 0xd1, 0x8e, 0x9f, 0xae, 0xa8,
			0x8f, 0x6d, 0x61, 0x27, 0x2d, 0x2a, 0xb1, 0x96,
			0x48, 0x6d, 0xb2, 0x63, 0x05, 0x9f, 0xec, 0xa1,
			0xcd, 0x65, 0x45, 0xc8, 0xcd, 0xf1, 0xa4, 0xba,
			0x20, 0xb7, 0xe4, 0xc7, 0x92, 0x3c, 0x1f, 0x16,
			0xf4, 0x5b, 0x75, 0xe4, 0x2a, 0x2e, 0x44, 0x72,
			0x65, 0x63, 0xc3, 0x78, 0x54, 0x50, 0xcb, 0x50,
			0xe0, 0xbe, 0xe7, 0x6f, 0x2a, 0xdc, 0x24, 0x7b,
			0xf8, 0x4b, 0xa8, 0xe2, 0x1b, 0x27, 0x00, 0x2d,
			0xe8, 0x99, 0xdc, 0x5f, 0xa4, 0x43, 0xa8, 0xf7,
			0xb1, 0x55, 0xea, 0xd7, 0x02, 0x09, 0x08, 0x97,
			0x5f, 0x21, 0x1e, 0x16, 0xa0, 0xd8, 0x27, 0xe4,
			0x5e, 0x3a, 0xa5, 0x51, 0x68, 0xe7, 0x19, 0xc1,
			0x7d, 0xb6, 0x9d, 0xb9, 0xc6, 0xc2, 0x1b, 0x48,
			0x7f,
		},
	},
	{
		.name = "brainpoolP512t1",
		.der_len = 221,
		.der = {
			0x30, 0x81, 0xda, 0x02, 0x01, 0x01, 0x04, 0x40,
			0xa0, 0xcb, 0xab, 0x2e, 0xdb, 0xb0, 0x17, 0x53,
			0x91, 0x23, 0x8c, 0x86, 0x29, 0x8b, 0x33, 0x27,
			0x27, 0x86, 0x71, 0xdd, 0x9f, 0x92, 0x8a, 0x8a,
			0x28, 0xac, 0x1f, 0x4b, 0x67, 0x8d, 0xd3, 0x7a,
			0x71, 0xd9, 0x95, 0x3c, 0xc2, 0x48, 0x9a, 0x1e,
			0x75, 0xcf, 0x33, 0x6f, 0xdc, 0x88, 0x8c, 0x29,
			0x1d, 0x41, 0xf8, 0xe1, 0xb3, 0x8f, 0xf9, 0x9e,
			0x13, 0x14, 0xbc, 0x4f, 0xa5, 0x8e, 0x06, 0xba,
			0xa0, 0x0b, 0x06, 0x09, 0x2b, 0x24, 0x03, 0x03,
			0x02, 0x08, 0x01, 0x01, 0x0e, 0xa1, 0x81, 0x85,
			0x03, 0x81, 0x82, 0x00, 0x04, 0x5b, 0x2b, 0x47,
			0x83, 0x61, 0xea, 0x80, 0x14, 0x85, 0x06, 0xe7,
			0x03, 0xbd, 0x24, 0x04, 0x47, 0x5d, 0x33, 0x4e,
			0xde, 0x0c, 0x92, 0x09, 0x54, 0x77, 0x53, 0x0e,
			0x33, 0x30, 0x73, 0xc0, 0xc0, 0x6a, 0xf2, 0xb5,
			0xb5, 0xd4, 0xd3, 0x02, 0x2f, 0x20, 0xe2, 0x88,
			0xb2, 0x07, 0x4a, 0x9f, 0x90, 0xbf, 0xba, 0xb5,
			0x3d, 0xc4, 0x5b, 0x65, 0x76, 0xb5, 0xe5, 0xa1,
			0x7d, 0x63, 0x39, 0x57, 0xaa, 0x1d, 0x1f, 0x99,
			0x52, 0x43, 0x5d, 0x0b, 0x58, 0xa6, 0x51, 0x1e,
			0x6f, 0x7b, 0x9e, 0x2f, 0x45, 0x5a, 0x2d, 0x6f,
			0xaa, 0x2b, 0xfc, 0xe1, 0x9f, 0x78, 0x11, 0x70,
			0x80, 0xf8, 0xfe, 0x51, 0x45, 0x12, 0xbf, 0x79,
			0xd8, 0xaf, 0x05, 0x59, 0x14, 0x01, 0x81, 0x9c,
			0x12, 0x5c, 0x8d, 0x4d, 0xed, 0xc7, 0x44, 0x61,
			0x68, 0x0f, 0x3f, 0x34, 0xee, 0x90, 0x4e, 0xab,
			0x80, 0x7c, 0x41, 0xd7, 0x8f,
		},
		.hex =	"045B2B478361EA80"
			"148506E703BD2404"
			"475D334EDE0C9209"
			"5477530E333073C0"
			"C06AF2B5B5D4D302"
			"2F20E288B2074A9F"
			"90BFBAB53DC45B65"
			"76B5E5A17D633957"
			"AA1D1F9952435D0B"
			"58A6511E6F7B9E2F"
			"455A2D6FAA2BFCE1"
			"9F78117080F8FE51"
			"4512BF79D8AF0559"
			"1401819C125C8D4D"
			"EDC74461680F3F34"
			"EE904EAB807C41D7"
			"8F",
		.oct_len = 129,
		.oct = {
			0x04, 0x5b, 0x2b, 0x47, 0x83, 0x61, 0xea, 0x80,
			0x14, 0x85, 0x06, 0xe7, 0x03, 0xbd, 0x24, 0x04,
			0x47, 0x5d, 0x33, 0x4e, 0xde, 0x0c, 0x92, 0x09,
			0x54, 0x77, 0x53, 0x0e, 0x33, 0x30, 0x73, 0xc0,
			0xc0, 0x6a, 0xf2, 0xb5, 0xb5, 0xd4, 0xd3, 0x02,
			0x2f, 0x20, 0xe2, 0x88, 0xb2, 0x07, 0x4a, 0x9f,
			0x90, 0xbf, 0xba, 0xb5, 0x3d, 0xc4, 0x5b, 0x65,
			0x76, 0xb5, 0xe5, 0xa1, 0x7d, 0x63, 0x39, 0x57,
			0xaa, 0x1d, 0x1f, 0x99, 0x52, 0x43, 0x5d, 0x0b,
			0x58, 0xa6, 0x51, 0x1e, 0x6f, 0x7b, 0x9e, 0x2f,
			0x45, 0x5a, 0x2d, 0x6f, 0xaa, 0x2b, 0xfc, 0xe1,
			0x9f, 0x78, 0x11, 0x70, 0x80, 0xf8, 0xfe, 0x51,
			0x45, 0x12, 0xbf, 0x79, 0xd8, 0xaf, 0x05, 0x59,
			0x14, 0x01, 0x81, 0x9c, 0x12, 0x5c, 0x8d, 0x4d,
			0xed, 0xc7, 0x44, 0x61, 0x68, 0x0f, 0x3f, 0x34,
			0xee, 0x90, 0x4e, 0xab, 0x80, 0x7c, 0x41, 0xd7,
			0x8f,
		},
	},
	{
		.name = "FRP256v1",
		.der_len = 123,
		.der = {
			0x30, 0x79, 0x02, 0x01, 0x01, 0x04, 0x20, 0x66,
			0xe4, 0xdb, 0x37, 0x46, 0x8d, 0xa1, 0xc8, 0x20,
			0x0d, 0xdf, 0xcb, 0x3b, 0x5c, 0x5b, 0x84, 0xe1,
			0x89, 0xed, 0x30, 0x37, 0xaa, 0xfa, 0xb2, 0x5b,
			0xf4, 0xf6, 0x13, 0x66, 0xfe, 0xfc, 0x7c, 0xa0,
			0x0c, 0x06, 0x0a, 0x2a, 0x81, 0x7a, 0x01, 0x81,
			0x5f, 0x65, 0x82, 0x00, 0x01, 0xa1, 0x44, 0x03,
			0x42, 0x00, 0x04, 0x17, 0xc7, 0xae, 0x1b, 0xe4,
			0xc6, 0xd6, 0x3a, 0xcf, 0x6b, 0x7e, 0x43, 0x29,
			0x9f, 0xdc, 0xc2, 0xa3, 0x90, 0x53, 0x62, 0x42,
			0x6e, 0xa3, 0xa4, 0xca, 0xd3, 0xf6, 0x53, 0x53,
			0xd4, 0xa6, 0x1f, 0xa8, 0x03, 0x1f, 0x6d, 0xd4,
			0x75, 0x77, 0x23, 0xf7, 0x92, 0xa4, 0x7c, 0x5a,
			0x4c, 0xc7, 0xc6, 0x96, 0x54, 0x62, 0x94, 0x9b,
			0xa7, 0xe0, 0x31, 0x1c, 0x4d, 0x1c, 0xa8, 0x2a,
			0x51, 0x11, 0x24,
		},
		.hex =	"0417C7AE1BE4C6D6"
			"3ACF6B7E43299FDC"
			"C2A3905362426EA3"
			"A4CAD3F65353D4A6"
			"1FA8031F6DD47577"
			"23F792A47C5A4CC7"
			"C6965462949BA7E0"
			"311C4D1CA82A5111"
			"24",
		.oct_len = 65,
		.oct = {
			0x04, 0x17, 0xc7, 0xae, 0x1b, 0xe4, 0xc6, 0xd6,
			0x3a, 0xcf, 0x6b, 0x7e, 0x43, 0x29, 0x9f, 0xdc,
			0xc2, 0xa3, 0x90, 0x53, 0x62, 0x42, 0x6e, 0xa3,
			0xa4, 0xca, 0xd3, 0xf6, 0x53, 0x53, 0xd4, 0xa6,
			0x1f, 0xa8, 0x03, 0x1f, 0x6d, 0xd4, 0x75, 0x77,
			0x23, 0xf7, 0x92, 0xa4, 0x7c, 0x5a, 0x4c, 0xc7,
			0xc6, 0x96, 0x54, 0x62, 0x94, 0x9b, 0xa7, 0xe0,
			0x31, 0x1c, 0x4d, 0x1c, 0xa8, 0x2a, 0x51, 0x11,
			0x24,
		},
	},
};

#define N_EC_PRIVATE_KEYS (sizeof(ec_private_keys) / sizeof(ec_private_keys[0]))

static EC_KEY *
ec_key_check_sanity(const struct ec_private_key *key)
{
	EC_KEY *ec_key;
	const unsigned char *p;
	unsigned char *der = NULL;
	int der_len = 0;
	unsigned int flags;
	uint8_t form;

	p = key->der;
	if ((ec_key = d2i_ECPrivateKey(NULL, &p, key->der_len)) == NULL) {
		fprintf(stderr, "FAIL: d2i_ECPrivateKey for %s\n", key->name);
		goto err;
	}

	if ((flags = EC_KEY_get_enc_flags(ec_key)) != 0) {
		fprintf(stderr, "FAIL: EC_KEY_get_enc_flags() returned %x for %s\n",
		    flags, key->name);
		goto err;
	}
	if ((form = EC_KEY_get_conv_form(ec_key)) != POINT_CONVERSION_UNCOMPRESSED) {
		fprintf(stderr, "FAIL: got conversion form %02x, want %02x\n",
		    form, POINT_CONVERSION_UNCOMPRESSED);
		goto err;
	}

	ERR_clear_error();
	if (!EC_KEY_check_key(ec_key)) {
		fprintf(stderr, "FAIL: EC_KEY_check_key() for %s\n", key->name);
		ERR_print_errors_fp(stderr);
		goto err;
	}

	der = NULL;
	if ((der_len = i2d_ECPrivateKey(ec_key, &der)) <= 0) {
		fprintf(stderr, "FAIL: i2d_ECPrivateKey() for %s\n", key->name);
		der_len = 0;
		goto err;
	}

	if (compare_data(key->name, der, der_len, key->der, key->der_len) == -1)
		goto err;

	freezero(der, der_len);
	der = NULL;

	return ec_key;

 err:
	EC_KEY_free(ec_key);
	freezero(der, der_len);

	return NULL;
}

static int
ec_key_test_point_encoding(const struct ec_private_key *key, const EC_KEY *ec_key)
{
	const EC_GROUP *group;
	const EC_POINT *ec_public_point;
	char *hex = NULL;
	unsigned char *ostr = NULL;
	int hex_len = 0, ostr_len = 0;
	int failed = 1;

	if ((group = EC_KEY_get0_group(ec_key)) == NULL) {
		fprintf(stderr, "FAIL: EC_KEY_get0_group() for %s\n", key->name);
		goto err;
	}
	if ((ec_public_point = EC_KEY_get0_public_key(ec_key)) == NULL) {
		fprintf(stderr, "FAIL: EC_KEY_get0_public_key() for %s\n", key->name);
		goto err;
	}

	if ((hex = EC_POINT_point2hex(group, ec_public_point,
	    POINT_CONVERSION_UNCOMPRESSED, NULL)) == NULL) {
		fprintf(stderr, "FAIL: EC_POINT_point2hex() for %s\n", key->name);
		goto err;
	}

	if ((hex_len = strlen(hex)) != 2 * key->oct_len) {
		fprintf(stderr, "FAIL: hex_len: %d, oct_len %d for %s\n",
		    hex_len, key->oct_len, key->name);
		goto err;
	}

	if (compare_data(key->name, hex, hex_len, key->hex, hex_len) == -1) {
		fprintf(stderr, "FAIL: EC_POINT_point2hex() comparison for %s\n",
		    key->name);
		goto err;
	}

	if ((ostr_len = i2o_ECPublicKey(ec_key, &ostr)) <= 0) {
		fprintf(stderr, "FAIL: i2o_ECPublicKey for %s\n", key->name);
		goto err;
	}

	if (compare_data(key->name, ostr, ostr_len, key->oct, key->oct_len) == -1) {
		fprintf(stderr, "FAIL: i2o_ECPublicKey comparison for %s\n",
		    key->name);
		goto err;
	}

	failed = 0;

 err:
	free(hex);
	freezero(ostr, ostr_len);

	return failed;
}

static int
ec_key_test_point_versus_bn(const struct ec_private_key *key, const EC_KEY *ec_key)
{
	const EC_GROUP *group;
	const EC_POINT *ec_public_point;
	EC_POINT *point = NULL;
	BIGNUM *hex_bn = NULL, *point_bn = NULL;
	int rv;
	int failed = 1;

	if ((group = EC_KEY_get0_group(ec_key)) == NULL) {
		fprintf(stderr, "FAIL: EC_KEY_get0_group() for %s\n", key->name);
		goto err;
	}
	if ((ec_public_point = EC_KEY_get0_public_key(ec_key)) == NULL) {
		fprintf(stderr, "FAIL: EC_KEY_get0_public_key() for %s\n", key->name);
		goto err;
	}

	/*
	 * Check that point2bn matches hex2bn.
	 */

	if ((point_bn = BN_new()) == NULL)
		err(1, "BN_new()");
	if (EC_POINT_point2bn(group, ec_public_point,
	    POINT_CONVERSION_UNCOMPRESSED, point_bn, NULL) == NULL) {
		fprintf(stderr, "FAIL: EC_POINT_point2bn() for %s\n", key->name);
		goto err;
	}

	if (BN_hex2bn(&hex_bn, key->hex) == 0) {
		fprintf(stderr, "FAIL: BN_hex2bn() for %s\n", key->name);
		goto err;
	}

	if (BN_cmp(hex_bn, point_bn) != 0) {
		fprintf(stderr, "FAIL: mismatch between "
		    "hex point and curve point for %s\n", key->name);
		goto err;
	}

	/*
	 * Translate back to a point on the curve.
	 */

	if ((point = EC_POINT_hex2point(group, key->hex, NULL, NULL)) == NULL) {
		fprintf(stderr, "FAIL: EC_POINT_hex2point() failed for %s\n",
		    key->name);
		goto err;
	}

	if ((rv = EC_POINT_cmp(group, ec_public_point, point, NULL)) != 0) {
		fprintf(stderr, "FAIL: EC_POINT_cmp() returned %d for %s\n",
		    rv, key->name);
		goto err;
	}

	/*
	 * Invalidate the point by doubling and inverting it. Then see if
	 * point reuse works.
	 */

	if (!EC_POINT_dbl(group, point, point, NULL)) {
		fprintf(stderr, "FAIL: EC_POINT_dbl() failed for %s\n",
		    key->name);
		goto err;
	}
	if (!EC_POINT_invert(group, point, NULL)) {
		fprintf(stderr, "FAIL: EC_POINT_invert() failed for %s\n",
		    key->name);
		goto err;
	}
	if (!EC_POINT_is_on_curve(group, point, NULL)) {
		fprintf(stderr, "FAIL: EC_POINT_is_on_curve() failed for %s\n",
		    key->name);
		goto err;
	}
	if (EC_POINT_is_at_infinity(group, point)) {
		fprintf(stderr, "FAIL: EC_POINT_is_at_infinity() is true for %s\n",
		    key->name);
		goto err;
	}

	/* The points are now different. */
	if ((rv = EC_POINT_cmp(group, ec_public_point, point, NULL)) == 0) {
		fprintf(stderr, "FAIL: EC_POINT_cmp() returned %d for %s\n",
		    rv, key->name);
		goto err;
	}

	if (EC_POINT_hex2point(group, key->hex, point, NULL) == NULL) {
		fprintf(stderr, "FAIL: EC_POINT_hex2point() 2 failed for %s\n",
		    key->name);
		goto err;
	}

	/* And after reuse they should be the same again. */
	if ((rv = EC_POINT_cmp(group, ec_public_point, point, NULL)) != 0) {
		fprintf(stderr, "FAIL: EC_POINT_cmp() returned %d for %s\n",
		    rv, key->name);
		goto err;
	}

	failed = 0;

 err:
	BN_free(hex_bn);
	BN_free(point_bn);
	EC_POINT_free(point);

	return failed;
}

static int
ec_key_test_i2o_and_o2i(const struct ec_private_key *key, const EC_KEY *ec_key_orig)
{
	EC_KEY *ec_key = NULL, *ec_pub_key = NULL;
	const unsigned char *p;
	unsigned char *ostr = NULL;
	int ostr_len = 0;
	uint8_t form;
	int rv;
	int failed = 1;

	if ((ec_key = EC_KEY_dup(ec_key_orig)) == NULL) {
		fprintf(stderr, "FAIL: EC_KEY_dup failed for %s", key->name);
		goto err;
	}

	EC_KEY_set_conv_form(ec_key, POINT_CONVERSION_COMPRESSED);

	ostr = NULL;
	if ((ostr_len = i2o_ECPublicKey(ec_key, &ostr)) <= 0) {
		fprintf(stderr, "FAIL: i2o_ECPublicKey for %s\n", key->name);
		ostr_len = 0;
		goto err;
	}

	if ((ec_pub_key = EC_KEY_new()) == NULL)
		errx(1, "EC_KEY_new");
	if (!EC_KEY_set_group(ec_pub_key, EC_KEY_get0_group(ec_key))) {
		fprintf(stderr, "FAIL: EC_KEY_set_group() for %s\n", key->name);
		goto err;
	}

	if ((form = EC_KEY_get_conv_form(ec_pub_key)) != POINT_CONVERSION_UNCOMPRESSED) {
		fprintf(stderr, "FAIL: EC_KEY_get_conv_form() for %s:\n"
		    "got %02x, want %02x\n", key->name, form, POINT_CONVERSION_UNCOMPRESSED);
		goto err;
	}

	/* Need to pass in the public key to tell o2i about the group... */
	p = ostr;
	if (o2i_ECPublicKey(&ec_pub_key, &p, ostr_len) == NULL) {
		fprintf(stderr, "FAIL: o2i_ECPublicKey() for %s\n", key->name);
		goto err;
	}

	if ((form = EC_KEY_get_conv_form(ec_pub_key)) != POINT_CONVERSION_COMPRESSED) {
		fprintf(stderr, "FAIL: EC_KEY_get_conv_form() for %s:\n"
		    "got %02x, want %02x\n", key->name, form, POINT_CONVERSION_COMPRESSED);
		goto err;
	}

	if ((rv = EC_POINT_cmp(EC_KEY_get0_group(ec_pub_key),
	    EC_KEY_get0_public_key(ec_pub_key), EC_KEY_get0_public_key(ec_key),
	    NULL)) != 0) {
		fprintf(stderr, "FAIL: EC_POINT_cmp() returned %d for %s\n",
		    rv, key->name);
		goto err;
	}

	failed = 0;

 err:
	EC_KEY_free(ec_key);
	EC_KEY_free(ec_pub_key);
	freezero(ostr, ostr_len);

	return failed;
}

static int
ec_key_test_hybrid_roundtrip(const struct ec_private_key *key,
    const EC_KEY *ec_key_orig)
{
	EC_KEY *ec_key = NULL, *ec_pub_key = NULL;
	const unsigned char *p;
	unsigned char *der = NULL;
	int der_len = 0;
	unsigned int flags;
	int rv;
	uint8_t form;
	int failed = 1;

	if ((ec_key = EC_KEY_new()) == NULL)
		errx(1, "EC_KEY_new()");

	if (EC_KEY_copy(ec_key, ec_key_orig) == NULL) {
		fprintf(stderr, "FAIL: failed to kopy EC_KEY for %s\n", key->name);
		goto err;
	}

	EC_KEY_set_conv_form(ec_key, POINT_CONVERSION_HYBRID);
	EC_KEY_set_enc_flags(ec_key, EC_PKEY_NO_PARAMETERS | EC_PKEY_NO_PUBKEY);

	if ((der_len = i2d_ECPrivateKey(ec_key, &der)) <= 0) {
		fprintf(stderr, "FAIL: i2d_ECPrivateKey(2) for %s\n", key->name);
		der_len = 0;
		goto err;
	}

	if ((ec_pub_key = EC_KEY_new()) == NULL)
		errx(1, "EC_KEY_new");
	if (!EC_KEY_set_group(ec_pub_key, EC_KEY_get0_group(ec_key))) {
		fprintf(stderr, "FAIL: EC_KEY_set_group() for %s\n", key->name);
		goto err;
	}
	/* Change away from the default to see if it changed below. */
	EC_KEY_set_conv_form(ec_pub_key, POINT_CONVERSION_COMPRESSED);

	if ((flags = EC_KEY_get_enc_flags(ec_pub_key)) != 0) {
		fprintf(stderr, "FAIL: EC_KEY_get_enc_flags() returned %x for %s\n",
		    flags, key->name);
		goto err;
	}

	p = der;
	if (d2i_ECPrivateKey(&ec_pub_key, &p, der_len) == NULL) {
		fprintf(stderr, "FAIL: d2i_ECPrivateKey for public %s\n", key->name);
		goto err;
	}

	/* For reasons of inconsistency, only EC_PKEY_NO_PUBKEY is set. */
	if ((flags = EC_KEY_get_enc_flags(ec_pub_key)) != EC_PKEY_NO_PUBKEY) {
		fprintf(stderr, "FAIL: EC_KEY_get_enc_flags() for public %s: "
		    " got %x, want %x\n", key->name, flags, EC_PKEY_NO_PUBKEY);
		goto err;
	}

	/* We had to compute the public key, so point conversion form is unchanged. */
	if ((form = EC_KEY_get_conv_form(ec_pub_key)) != POINT_CONVERSION_COMPRESSED) {
		fprintf(stderr, "FAIL: EC_KEY_get_conv_form() not compressed for %s:\n"
		    "got %02x, want %02x\n", key->name, form, POINT_CONVERSION_COMPRESSED);
		goto err;
	}

	if ((rv = EC_POINT_cmp(EC_KEY_get0_group(ec_pub_key),
	    EC_KEY_get0_public_key(ec_pub_key), EC_KEY_get0_public_key(ec_key),
	    NULL)) != 0) {
		fprintf(stderr, "FAIL: EC_POINT_cmp() returned %d for %s "
		    "after DER roundtrip\n", rv, key->name);
		goto err;
	}

	failed = 0;

 err:
	EC_KEY_free(ec_key);
	EC_KEY_free(ec_pub_key);
	freezero(der, der_len);

	return failed;
}

static int
ec_key_test_parameter_roundtrip(const struct ec_private_key *key,
    EC_KEY *ec_key)
{
	EC_KEY *ec_pub_key = NULL;
	const unsigned char *p;
	unsigned char *der = NULL;
	int der_len = 0;
	int rv;
	int failed = 1;

	if ((der_len = i2d_ECParameters(ec_key, &der)) <= 0) {
		fprintf(stderr, "FAIL: i2d_ECParameters returned %d for %s\n",
		    der_len, key->name);
		goto err;
	}

	/* See if we leak on reuse, whether the curve is right or not. */
	if ((ec_pub_key = EC_KEY_new_by_curve_name(NID_secp256k1)) == NULL)
		errx(1, "EC_KEY_new_by_curve_name");

	p = der;
	if (d2i_ECParameters(&ec_pub_key, &p, der_len) == NULL) {
		fprintf(stderr, "FAIL: d2i_ECParameters for %s\n", key->name);
		goto err;
	}

	if ((rv = EC_GROUP_cmp(EC_KEY_get0_group(ec_key),
	    EC_KEY_get0_group(ec_pub_key), NULL)) != 0) {
		fprintf(stderr, "FAIL: EC_GROUP_cmp returned %d for %s\n",
		    rv, key->name);
		goto err;
	}

	failed = 0;

 err:
	EC_KEY_free(ec_pub_key);
	freezero(der, der_len);

	return failed;
}

static int
ec_group_check_private_key(const struct ec_private_key *key)
{
	EC_KEY *ec_key = NULL;
	int failed = 0;

	if ((ec_key = ec_key_check_sanity(key)) == NULL) {
		fprintf(stderr, "FAIL: ec_key_check_sanity() for %s\n", key->name);
		failed = 1;
		goto err;
	}

	failed |= ec_key_test_point_encoding(key, ec_key);
	failed |= ec_key_test_point_versus_bn(key, ec_key);
	failed |= ec_key_test_i2o_and_o2i(key, ec_key);
	failed |= ec_key_test_hybrid_roundtrip(key, ec_key);
	failed |= ec_key_test_parameter_roundtrip(key, ec_key);

 err:
	EC_KEY_free(ec_key);

	return failed;
}

static int
ec_group_check_private_keys(void)
{
	size_t i;
	int failed = 0;

	for (i = 0; i < N_EC_PRIVATE_KEYS; i++)
		failed |= ec_group_check_private_key(&ec_private_keys[i]);

	return failed;
}

static void
ec_group_sha1_bignum(BIGNUM *out, const BIGNUM *in)
{
	char md[SHA_DIGEST_LENGTH];
	unsigned char *bin;
	size_t bin_len;

	if (BN_num_bytes(in) <= 0)
		errx(1, "%s: invalid bignum", __func__);

	bin_len = BN_num_bytes(in);
	if ((bin = calloc(1, bin_len)) == NULL)
		err(1, "calloc");
	if (BN_bn2bin(in, bin) <= 0)
		errx(1, "BN_bn2bin");

	SHA1(bin, bin_len, md);
	free(bin);

	if (BN_bin2bn(md, sizeof(md), out) == NULL)
		errx(1, "BN_bin2bn");
}

static int
ec_group_check_seed(const EC_builtin_curve *curve, BN_CTX *ctx)
{
	EC_GROUP *group = NULL;
	BIGNUM *p, *a, *b, *pow2, *r, *seed_bn, *w;
	const unsigned char *seed;
	size_t seed_len;
	int i, g, h, s, t;
	int failed = 1;

	if ((group = EC_GROUP_new_by_curve_name(curve->nid)) == NULL)
		errx(1, "EC_GROUP_new_by_curve_name");

	BN_CTX_start(ctx);

	if ((p = BN_CTX_get(ctx)) == NULL)
		errx(1, "p = BN_CTX_get()");
	if ((a = BN_CTX_get(ctx)) == NULL)
		errx(1, "a = BN_CTX_get()");
	if ((b = BN_CTX_get(ctx)) == NULL)
		errx(1, "b = BN_CTX_get()");
	if ((r = BN_CTX_get(ctx)) == NULL)
		errx(1, "r = BN_CTX_get()");
	if ((pow2 = BN_CTX_get(ctx)) == NULL)
		errx(1, "pow2 = BN_CTX_get()");
	if ((seed_bn = BN_CTX_get(ctx)) == NULL)
		errx(1, "seed_bn = BN_CTX_get()");
	if ((w = BN_CTX_get(ctx)) == NULL)
		errx(1, "w = BN_CTX_get()");

	/*
	 * If the curve has a seed, verify that its parameters a and b have
	 * been selected using that seed, loosely following X9.62, F.3.4.b.
	 * Otherwise there's nothing to do.
	 */
	if ((seed = EC_GROUP_get0_seed(group)) == NULL)
		goto done;
	seed_len = EC_GROUP_get_seed_len(group);

	/*
	 * This isn't a requirement but happens to be the case for NIST
	 * curves - the only built-in curves that have a seed.
	 */
	if (seed_len != SHA_DIGEST_LENGTH) {
		fprintf(stderr, "%s FAIL: unexpected seed length. "
		    "want %d, got %zu\n", __func__, SHA_DIGEST_LENGTH, seed_len);
		goto err;
	}

	/* Seed length in bits, per F.3.3.b. */
	g = 8 * seed_len;

	/*
	 * Prepare to build the verifiably random element r of GFp by
	 * concatenating the SHA-1 of modifications of the seed as a number.
	 */
	if (BN_bin2bn(seed, seed_len, seed_bn) == NULL)
		errx(1, "BN_bin2bn");

	if (!EC_GROUP_get_curve(group, p, a, b, ctx))
		errx(1, "EC_GROUP_get_curve");

	t = BN_num_bits(p);	/* bit length needed. */
	s = (t - 1) / 160;	/* number of SHA-1 fitting in bit length. */
	h = t - 160 * s;	/* remaining number of bits in r. */

	/*
	 * Steps 1 - 3: compute hash of the seed and take h - 1 rightmost bits.
	 */

	ec_group_sha1_bignum(r, seed_bn);
	BN_zero(pow2);
	if (!BN_set_bit(pow2, h - 1))
		errx(1, "BN_set_bit");
	if (!BN_mod(r, r, pow2, ctx))
		errx(1, "BN_nnmod");

	/*
	 * Steps 4 - 6: for i from 1 to s do Wi = SHA-1(SEED + i mod 2^g).
	 * With W0 = r as already computed, let r = W0 || W1 || ... || Ws.
	 */

	BN_zero(pow2);
	if (!BN_set_bit(pow2, g))
		errx(1, "BN_set_bit");

	for (i = 0; i < s; i++) {
		/*
		 * This is a bit silly since the seed isn't going to have all
		 * its bits set, so BN_add_word(seed_bn, 1) would do, but for
		 * the sake of correctness...
		 */
		if (!BN_mod_add(seed_bn, seed_bn, BN_value_one(), pow2, ctx))
			errx(1, "BN_mod_add");

		ec_group_sha1_bignum(w, seed_bn);

		if (!BN_lshift(r, r, 8 * SHA_DIGEST_LENGTH))
			errx(1, "BN_lshift");
		if (!BN_add(r, r, w))
			errx(1, "BN_add");
	}

	/*
	 * Step 7: check that r * b^2 == a^3 (mod p)
	 */

	/* Compute r = r * b^2 (mod p). */
	if (!BN_mod_sqr(b, b, p, ctx))
		errx(1, "BN_mod_sqr");
	if (!BN_mod_mul(r, r, b, p, ctx))
		errx(1, "BN_mod_mul");

	/* Compute a = a^3 (mod p). */
	if (!BN_mod_sqr(b, a, p, ctx))
		errx(1, "BN_mod_sqr");
	if (!BN_mod_mul(a, a, b, p, ctx))
		errx(1, "BN_mod_mul");

	/*
	 * XXX - this assumes that a, b, p >= 0, so the results are in [0, p).
	 * This is currently enforced in the EC code.
	 */
	if (BN_cmp(r, a) != 0) {
		fprintf(stderr, "FAIL: %s verification failed for %s\nr * b^2:\t",
		    __func__, curve->comment);
		BN_print_fp(stderr, r);
		fprintf(stderr, "\na^3:\t\t");
		BN_print_fp(stderr, a);
		fprintf(stderr, "\n");
		goto err;
	}

 done:
	failed = 0;

 err:
	BN_CTX_end(ctx);
	EC_GROUP_free(group);

	return failed;
}

static int
ec_group_check_seeds(void)
{
	BN_CTX *ctx = NULL;
	EC_builtin_curve *all_curves = NULL;
	size_t curve_id, ncurves;
	int failed = 0;

	if ((ctx = BN_CTX_new()) == NULL)
		errx(1, "BN_CTX_new");

	ncurves = EC_get_builtin_curves(NULL, 0);
	if ((all_curves = calloc(ncurves, sizeof(*all_curves))) == NULL)
		err(1, "calloc builtin curves");
	EC_get_builtin_curves(all_curves, ncurves);

	for (curve_id = 0; curve_id < ncurves; curve_id++)
		failed |= ec_group_check_seed(&all_curves[curve_id], ctx);

	free(all_curves);
	BN_CTX_free(ctx);

	return failed;
}

int
main(int argc, char **argv)
{
	int failed = 0;

	failed |= ec_group_pkparameters_named_curve_test();
	failed |= ec_group_pkparameters_parameters_test();
	failed |= ec_group_pkparameters_correct_padding_test();
	failed |= ec_group_roundtrip_builtin_curves();
	failed |= ec_group_non_builtin_curves();
	failed |= ec_group_builtin_curves_have_prime_order();
	failed |= ec_group_check_private_keys();
	failed |= ec_group_check_seeds();

	return failed;
}
