root/regress/lib/libcrypto/ec/ec_asn1_test.c
/* $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;
}