From f4820b99194b31649874f60f5e429b82bbf5eab5 Mon Sep 17 00:00:00 2001 From: Tom Hughes Date: Mon, 29 Jul 2024 13:40:36 +0100 Subject: [PATCH] Add helper for making EC key from components --- include/jwt-cpp/jwt.h | 242 ++++++++++++++++++++++++++++++++++++- tests/HelperTest.cpp | 19 ++- tests/OpenSSLErrorTest.cpp | 194 +++++++++++++++++++++++++++++ 3 files changed, 453 insertions(+), 2 deletions(-) diff --git a/include/jwt-cpp/jwt.h b/include/jwt-cpp/jwt.h index 6194d45ba..21960cd0d 100644 --- a/include/jwt-cpp/jwt.h +++ b/include/jwt-cpp/jwt.h @@ -169,7 +169,9 @@ namespace jwt { get_key_failed, write_key_failed, write_cert_failed, - convert_to_pem_failed + convert_to_pem_failed, + unknown_curve, + set_ecdsa_failed }; /** * \brief Error category for ECDSA errors @@ -194,6 +196,8 @@ namespace jwt { case ecdsa_error::write_key_failed: return "error writing key data in PEM format"; case ecdsa_error::write_cert_failed: return "error writing cert data in PEM format"; case ecdsa_error::convert_to_pem_failed: return "failed to convert key to pem"; + case ecdsa_error::unknown_curve: return "unknown curve"; + case ecdsa_error::set_ecdsa_failed: return "set parameters to ECDSA failed"; default: return "unknown ECDSA error"; } } @@ -1085,6 +1089,242 @@ namespace jwt { error::throw_if_error(ec); return res; } + +#if defined(JWT_OPENSSL_3_0) + + /** + * \brief Convert a curve name to a group name. + * + * \param curve string containing curve name + * \param ec error_code for error_detection + * \return group name + */ + inline std::string curve2group(const std::string curve, std::error_code& ec) { + if (curve == "P-256") { + return "prime256v1"; + } else if (curve == "P-384") { + return "secp384r1"; + } else if (curve == "P-521") { + return "secp521r1"; + } else { + ec = jwt::error::ecdsa_error::unknown_curve; + return {}; + } + } + +#else + + /** + * \brief Convert a curve name to an ID. + * + * \param curve string containing curve name + * \param ec error_code for error_detection + * \return ID + */ + inline int curve2nid(const std::string curve, std::error_code& ec) { + if (curve == "P-256") { + return NID_X9_62_prime256v1; + } else if (curve == "P-384") { + return NID_secp384r1; + } else if (curve == "P-521") { + return NID_secp521r1; + } else { + ec = jwt::error::ecdsa_error::unknown_curve; + return {}; + } + } + +#endif + + /** + * Create public key from curve name and coordinates. This is defined in + * [RFC 7518 Section 6.2](https://www.rfc-editor.org/rfc/rfc7518#section-6.2) + * Using the required "crv" (Curve), "x" (X Coordinate) and "y" (Y Coordinate) Parameters. + * + * \tparam Decode is callable, taking a string_type and returns a string_type. + * It should ensure the padding of the input and then base64url decode and + * return the results. + * \param curve string containing curve name + * \param x string containing base64url encoded x coordinate + * \param y string containing base64url encoded y coordinate + * \param decode The function to decode the RSA parameters + * \param ec error_code for error_detection (gets cleared if no error occur + * \return public key in PEM format + */ + template + std::string create_public_key_from_ec_components(const std::string& curve, const std::string& x, + const std::string& y, Decode decode, std::error_code& ec) { + ec.clear(); + auto decoded_x = decode(x); + auto decoded_y = decode(y); + +#if defined(JWT_OPENSSL_3_0) + // OpenSSL deprecated mutable keys and there is a new way for making them + // https://mta.openssl.org/pipermail/openssl-users/2021-July/013994.html + // https://www.openssl.org/docs/man3.1/man3/OSSL_PARAM_BLD_new.html#Example-2 + std::unique_ptr param_bld(OSSL_PARAM_BLD_new(), + OSSL_PARAM_BLD_free); + if (!param_bld) { + ec = error::ecdsa_error::create_context_failed; + return {}; + } + + std::string group = helper::curve2group(curve, ec); + if (ec) return {}; + + // https://github.com/openssl/openssl/issues/16270#issuecomment-895734092 + std::string pub = std::string("\x04").append(decoded_x).append(decoded_y); + + if (OSSL_PARAM_BLD_push_utf8_string(param_bld.get(), "group", group.data(), group.size()) != 1 || + OSSL_PARAM_BLD_push_octet_string(param_bld.get(), "pub", pub.data(), pub.size()) != 1) { + ec = error::ecdsa_error::set_ecdsa_failed; + return {}; + } + + std::unique_ptr params(OSSL_PARAM_BLD_to_param(param_bld.get()), + OSSL_PARAM_free); + if (!params) { + ec = error::ecdsa_error::set_ecdsa_failed; + return {}; + } + + std::unique_ptr ctx( + EVP_PKEY_CTX_new_from_name(nullptr, "EC", nullptr), EVP_PKEY_CTX_free); + if (!ctx) { + ec = error::ecdsa_error::create_context_failed; + return {}; + } + + // https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_fromdata.html#EXAMPLES + // Error codes based on https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_fromdata_init.html#RETURN-VALUES + EVP_PKEY* pkey = NULL; + if (EVP_PKEY_fromdata_init(ctx.get()) <= 0 || + EVP_PKEY_fromdata(ctx.get(), &pkey, EVP_PKEY_KEYPAIR, params.get()) <= 0) { + // It's unclear if this can fail after allocating but free it anyways + // https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_fromdata.html + EVP_PKEY_free(pkey); + + ec = error::ecdsa_error::cert_load_failed; + return {}; + } + + // Transfer ownership so we get ref counter and cleanup + evp_pkey_handle ecdsa(pkey); + +#else + int nid = helper::curve2nid(curve, ec); + if (ec) return {}; + + auto qx = helper::raw2bn(decoded_x, ec); + if (ec) return {}; + auto qy = helper::raw2bn(decoded_y, ec); + if (ec) return {}; + + std::unique_ptr ecgroup(EC_GROUP_new_by_curve_name(nid), EC_GROUP_free); + if (!ecgroup) { + ec = error::ecdsa_error::set_ecdsa_failed; + return {}; + } + + EC_GROUP_set_asn1_flag(ecgroup.get(), OPENSSL_EC_NAMED_CURVE); + + std::unique_ptr ecpoint(EC_POINT_new(ecgroup.get()), EC_POINT_free); + if (!ecpoint || + EC_POINT_set_affine_coordinates_GFp(ecgroup.get(), ecpoint.get(), qx.get(), qy.get(), nullptr) != 1) { + ec = error::ecdsa_error::set_ecdsa_failed; + return {}; + } + + std::unique_ptr ecdsa(EC_KEY_new(), EC_KEY_free); + if (!ecdsa || EC_KEY_set_group(ecdsa.get(), ecgroup.get()) != 1 || + EC_KEY_set_public_key(ecdsa.get(), ecpoint.get()) != 1) { + ec = error::ecdsa_error::set_ecdsa_failed; + return {}; + } + +#endif + + auto pub_key_bio = make_mem_buf_bio(); + if (!pub_key_bio) { + ec = error::ecdsa_error::create_mem_bio_failed; + return {}; + } + + auto write_pem_to_bio = +#if defined(JWT_OPENSSL_3_0) + // https://www.openssl.org/docs/man3.1/man3/PEM_write_bio_EC_PUBKEY.html + &PEM_write_bio_PUBKEY; +#else + &PEM_write_bio_EC_PUBKEY; +#endif + if (write_pem_to_bio(pub_key_bio.get(), ecdsa.get()) != 1) { + ec = error::ecdsa_error::load_key_bio_write; + return {}; + } + + return write_bio_to_string(pub_key_bio, ec); + } + + /** + * Create public key from curve name and coordinates. This is defined in + * [RFC 7518 Section 6.2](https://www.rfc-editor.org/rfc/rfc7518#section-6.2) + * Using the required "crv" (Curve), "x" (X Coordinate) and "y" (Y Coordinate) Parameters. + * + * \tparam Decode is callable, taking a string_type and returns a string_type. + * It should ensure the padding of the input and then base64url decode and + * return the results. + * \param curve string containing curve name + * \param x string containing base64url encoded x coordinate + * \param y string containing base64url encoded y coordinate + * \param decode The function to decode the RSA parameters + * \return public key in PEM format + */ + template + std::string create_public_key_from_ec_components(const std::string& curve, const std::string& x, + const std::string& y, Decode decode) { + std::error_code ec; + auto res = create_public_key_from_ec_components(curve, x, y, decode, ec); + error::throw_if_error(ec); + return res; + } + +#ifndef JWT_DISABLE_BASE64 + /** + * Create public key from curve name and coordinates. This is defined in + * [RFC 7518 Section 6.2](https://www.rfc-editor.org/rfc/rfc7518#section-6.2) + * Using the required "crv" (Curve), "x" (X Coordinate) and "y" (Y Coordinate) Parameters. + * + * \param curve string containing curve name + * \param x string containing base64url encoded x coordinate + * \param y string containing base64url encoded y coordinate + * \param ec error_code for error_detection (gets cleared if no error occur + * \return public key in PEM format + */ + inline std::string create_public_key_from_ec_components(const std::string& curve, const std::string& x, + const std::string& y, std::error_code& ec) { + auto decode = [](const std::string& token) { + return base::decode(base::pad(token)); + }; + return create_public_key_from_ec_components(curve, x, y, std::move(decode), ec); + } + /** + * Create public key from curve name and coordinates. This is defined in + * [RFC 7518 Section 6.2](https://www.rfc-editor.org/rfc/rfc7518#section-6.2) + * Using the required "crv" (Curve), "x" (X Coordinate) and "y" (Y Coordinate) Parameters. + * + * \param curve string containing curve name + * \param x string containing base64url encoded x coordinate + * \param y string containing base64url encoded y coordinate + * \return public key in PEM format + */ + inline std::string create_public_key_from_ec_components(const std::string& curve, const std::string& x, + const std::string& y) { + std::error_code ec; + auto res = create_public_key_from_ec_components(curve, x, y, ec); + error::throw_if_error(ec); + return res; + } +#endif } // namespace helper /** diff --git a/tests/HelperTest.cpp b/tests/HelperTest.cpp index a3a78b96f..dfcc0b0e0 100644 --- a/tests/HelperTest.cpp +++ b/tests/HelperTest.cpp @@ -45,6 +45,23 @@ JQIDAQAB ASSERT_EQ(public_key, public_key_expected); } +TEST(HelperTest, EcFromComponents) { + const std::string public_key_expected = + R"(-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE0uQ1+1P/wmhOuYvVtTogHOSBLC05IvK7 +L6sTPIX8Dl4Bg9nhC3v/FsgifjnXnijUxVJSyWa9SuxwBonUhg6SiCEv+ixb74hj +DesC4D7OwllVcnkDJmOy/NMx4N7yDPJp +-----END PUBLIC KEY----- +)"; + const std::string curve = R"(P-384)"; + const std::string x = R"(0uQ1-1P_wmhOuYvVtTogHOSBLC05IvK7L6sTPIX8Dl4Bg9nhC3v_FsgifjnXnijU)"; + const std::string y = R"(xVJSyWa9SuxwBonUhg6SiCEv-ixb74hjDesC4D7OwllVcnkDJmOy_NMx4N7yDPJp)"; + + const auto public_key = jwt::helper::create_public_key_from_ec_components(curve, x, y); + + ASSERT_EQ(public_key, public_key_expected); +} + TEST(HelperTest, ErrorCodeMessages) { ASSERT_EQ(std::error_code(jwt::error::rsa_error::ok).message(), "no error"); ASSERT_EQ(std::error_code(static_cast(-1)).message(), "unknown RSA error"); @@ -80,7 +97,7 @@ TEST(HelperTest, ErrorCodeMessages) { ASSERT_EQ(std::error_code(static_cast(i)).message(), std::error_code(static_cast(-1)).message()); - for (i = 10; i < 22; i++) { + for (i = 10; i < 24; i++) { ASSERT_NE(std::error_code(static_cast(i)).message(), std::error_code(static_cast(-1)).message()); } diff --git a/tests/OpenSSLErrorTest.cpp b/tests/OpenSSLErrorTest.cpp index 741097d88..467ae6f73 100644 --- a/tests/OpenSSLErrorTest.cpp +++ b/tests/OpenSSLErrorTest.cpp @@ -55,6 +55,8 @@ static uint64_t fail_i2d_ECDSA_SIG = 0; #ifdef JWT_OPENSSL_3_0 static uint64_t fail_OSSL_PARAM_BLD_new = 0; static uint64_t fail_OSSL_PARAM_BLD_push_BN = 0; +static uint64_t fail_OSSL_PARAM_BLD_push_utf8_string = 0; +static uint64_t fail_OSSL_PARAM_BLD_push_octet_string = 0; static uint64_t fail_OSSL_PARAM_BLD_to_param = 0; static uint64_t fail_EVP_PKEY_CTX_new_from_name = 0; static uint64_t fail_EVP_PKEY_fromdata_init = 0; @@ -62,6 +64,15 @@ static uint64_t fail_EVP_PKEY_fromdata = 0; #else static uint64_t fail_PEM_write_bio_RSA_PUBKEY = 0; static uint64_t fail_RSA_set0_key = 0; +static uint64_t fail_PEM_write_bio_EC_PUBKEY = 0; +static uint64_t fail_EC_GROUP_new_by_curve_name = 0; +static uint64_t fail_EC_POINT_new = 0; +static uint64_t fail_EC_POINT_set_affine_coordinates_GFp = 0; +static uint64_t fail_EC_KEY_new = 0; +#ifndef LIBWOLFSSL_VERSION_HEX +static uint64_t fail_EC_KEY_set_group = 0; +#endif +static uint64_t fail_EC_KEY_set_public_key = 0; #endif #ifdef LIBWOLFSSL_VERSION_HEX @@ -448,6 +459,30 @@ int OSSL_PARAM_BLD_push_BN(OSSL_PARAM_BLD* bld, const char* key, const BIGNUM* b return origMethod(bld, key, bn); } +int OSSL_PARAM_BLD_push_utf8_string(OSSL_PARAM_BLD* bld, const char* key, const char* buf, size_t bsize) { + static int (*origMethod)(OSSL_PARAM_BLD * bld, const char* key, const char* buf, size_t bsize) = nullptr; + if (origMethod == nullptr) + origMethod = (decltype(origMethod))dlsym(RTLD_NEXT, SYMBOL_NAME("OSSL_PARAM_BLD_push_utf8_string")); + bool fail = fail_OSSL_PARAM_BLD_push_utf8_string & 1; + fail_OSSL_PARAM_BLD_push_utf8_string = fail_OSSL_PARAM_BLD_push_utf8_string >> 1; + if (fail) + return 0; + else + return origMethod(bld, key, buf, bsize); +} + +int OSSL_PARAM_BLD_push_octet_string(OSSL_PARAM_BLD* bld, const char* key, const void* buf, size_t bsize) { + static int (*origMethod)(OSSL_PARAM_BLD * bld, const char* key, const void* buf, size_t bsize) = nullptr; + if (origMethod == nullptr) + origMethod = (decltype(origMethod))dlsym(RTLD_NEXT, SYMBOL_NAME("OSSL_PARAM_BLD_push_octet_string")); + bool fail = fail_OSSL_PARAM_BLD_push_octet_string & 1; + fail_OSSL_PARAM_BLD_push_octet_string = fail_OSSL_PARAM_BLD_push_octet_string >> 1; + if (fail) + return 0; + else + return origMethod(bld, key, buf, bsize); +} + OSSL_PARAM* OSSL_PARAM_BLD_to_param(OSSL_PARAM_BLD* bld) { static OSSL_PARAM* (*origMethod)(OSSL_PARAM_BLD * bld) = nullptr; if (origMethod == nullptr) @@ -517,6 +552,91 @@ int RSA_set0_key(RSA* r, BIGNUM* n, BIGNUM* e, BIGNUM* d) { else return origMethod(r, n, e, d); } + +int PEM_write_bio_EC_PUBKEY(BIO* bp, EC_KEY* x) { + static int (*origMethod)(BIO * bp, EC_KEY * x) = nullptr; + if (origMethod == nullptr) + origMethod = (decltype(origMethod))dlsym(RTLD_NEXT, SYMBOL_NAME("PEM_write_bio_EC_PUBKEY")); + bool fail = fail_PEM_write_bio_EC_PUBKEY & 1; + fail_PEM_write_bio_EC_PUBKEY = fail_PEM_write_bio_EC_PUBKEY >> 1; + if (fail) + return 0; + else + return origMethod(bp, x); +} + +EC_GROUP* EC_GROUP_new_by_curve_name(int nid) { + static EC_GROUP* (*origMethod)(int nid) = nullptr; + if (origMethod == nullptr) + origMethod = (decltype(origMethod))dlsym(RTLD_NEXT, SYMBOL_NAME("EC_GROUP_new_by_curve_name")); + bool fail = fail_EC_GROUP_new_by_curve_name & 1; + fail_EC_GROUP_new_by_curve_name = fail_EC_GROUP_new_by_curve_name >> 1; + if (fail) + return nullptr; + else + return origMethod(nid); +} + +EC_POINT* EC_POINT_new(const EC_GROUP* group) { + static EC_POINT* (*origMethod)(const EC_GROUP* group) = nullptr; + if (origMethod == nullptr) origMethod = (decltype(origMethod))dlsym(RTLD_NEXT, SYMBOL_NAME("EC_POINT_new")); + bool fail = fail_EC_POINT_new & 1; + fail_EC_POINT_new = fail_EC_POINT_new >> 1; + if (fail) + return nullptr; + else + return origMethod(group); +} + +int EC_POINT_set_affine_coordinates_GFp(const EC_GROUP* group, EC_POINT* point, const BIGNUM* x, const BIGNUM* y, + BN_CTX* ctx) { + static int (*origMethod)(const EC_GROUP* group, EC_POINT* point, const BIGNUM* x, const BIGNUM* y, BN_CTX* ctx) = + nullptr; + if (origMethod == nullptr) + origMethod = (decltype(origMethod))dlsym(RTLD_NEXT, SYMBOL_NAME("EC_POINT_set_affine_coordinates_GFp")); + bool fail = fail_EC_POINT_set_affine_coordinates_GFp & 1; + fail_EC_POINT_set_affine_coordinates_GFp = fail_EC_POINT_set_affine_coordinates_GFp >> 1; + if (fail) + return 0; + else + return origMethod(group, point, x, y, ctx); +} + +EC_KEY* EC_KEY_new() { + static EC_KEY* (*origMethod)() = nullptr; + if (origMethod == nullptr) origMethod = (decltype(origMethod))dlsym(RTLD_NEXT, SYMBOL_NAME("EC_KEY_new")); + bool fail = fail_EC_KEY_new & 1; + fail_EC_KEY_new = fail_EC_KEY_new >> 1; + if (fail) + return nullptr; + else + return origMethod(); +} + +#ifndef LIBWOLFSSL_VERSION_HEX +int EC_KEY_set_group(EC_KEY* eckey, const EC_GROUP* group) { + static int (*origMethod)(EC_KEY * eckey, const EC_GROUP* group) = nullptr; + if (origMethod == nullptr) origMethod = (decltype(origMethod))dlsym(RTLD_NEXT, SYMBOL_NAME("EC_KEY_set_group")); + bool fail = fail_EC_KEY_set_group & 1; + fail_EC_KEY_set_group = fail_EC_KEY_set_group >> 1; + if (fail) + return 0; + else + return origMethod(eckey, group); +} +#endif + +int EC_KEY_set_public_key(EC_KEY* eckey, const EC_POINT* pub) { + static int (*origMethod)(EC_KEY * eckey, const EC_POINT* pub) = nullptr; + if (origMethod == nullptr) + origMethod = (decltype(origMethod))dlsym(RTLD_NEXT, SYMBOL_NAME("EC_KEY_set_public_key")); + bool fail = fail_EC_KEY_set_public_key & 1; + fail_EC_KEY_set_public_key = fail_EC_KEY_set_public_key >> 1; + if (fail) + return 0; + else + return origMethod(eckey, pub); +} #endif /** @@ -703,6 +823,80 @@ TEST(OpenSSLErrorTest, CreateRsaPublicKeyFromComponentsErrorCode) { }); } +TEST(OpenSSLErrorTest, CreateEcPublicKeyFromComponents) { + std::vector mapping{ + {&fail_BIO_new, 1, jwt::error::ecdsa_error::create_mem_bio_failed}, +#ifndef LIBWOLFSSL_VERSION_HEX + {&fail_BIO_get_mem_data, 1, jwt::error::ecdsa_error::convert_to_pem_failed}, +#endif +#ifdef JWT_OPENSSL_3_0 + {&fail_PEM_write_bio_PUBKEY, 1, jwt::error::ecdsa_error::load_key_bio_write}, + {&fail_OSSL_PARAM_BLD_new, 1, jwt::error::ecdsa_error::create_context_failed}, + {&fail_OSSL_PARAM_BLD_push_utf8_string, 1, jwt::error::ecdsa_error::set_ecdsa_failed}, + {&fail_OSSL_PARAM_BLD_push_octet_string, 1, jwt::error::ecdsa_error::set_ecdsa_failed}, + {&fail_OSSL_PARAM_BLD_to_param, 1, jwt::error::ecdsa_error::set_ecdsa_failed}, + {&fail_EVP_PKEY_CTX_new_from_name, 1, jwt::error::ecdsa_error::create_context_failed}, + {&fail_EVP_PKEY_fromdata_init, 1, jwt::error::ecdsa_error::cert_load_failed}, + {&fail_EVP_PKEY_fromdata, 1, jwt::error::ecdsa_error::cert_load_failed} +#else + {&fail_PEM_write_bio_EC_PUBKEY, 1, jwt::error::ecdsa_error::load_key_bio_write}, + {&fail_EC_GROUP_new_by_curve_name, 1, jwt::error::ecdsa_error::set_ecdsa_failed}, + {&fail_EC_POINT_new, 1, jwt::error::ecdsa_error::set_ecdsa_failed}, + {&fail_EC_POINT_set_affine_coordinates_GFp, 1, jwt::error::ecdsa_error::set_ecdsa_failed}, + {&fail_EC_KEY_new, 1, jwt::error::ecdsa_error::set_ecdsa_failed}, +#ifndef LIBWOLFSSL_VERSION_HEX + {&fail_EC_KEY_set_group, 1, jwt::error::ecdsa_error::set_ecdsa_failed}, +#endif + {&fail_EC_KEY_set_public_key, 1, jwt::error::ecdsa_error::set_ecdsa_failed} +#endif + }; + + run_multitest(mapping, [](std::error_code& ec) { + try { + jwt::helper::create_public_key_from_ec_components( + "P-384", "0uQ1-1P_wmhOuYvVtTogHOSBLC05IvK7L6sTPIX8Dl4Bg9nhC3v_FsgifjnXnijU", + "xVJSyWa9SuxwBonUhg6SiCEv-ixb74hjDesC4D7OwllVcnkDJmOy_NMx4N7yDPJp"); + FAIL(); // Should never reach this + } catch (const jwt::error::ecdsa_exception& e) { ec = e.code(); } + }); +} + +TEST(OpenSSLErrorTest, CreateEcPublicKeyFromComponentsErrorCode) { + std::vector mapping{ + {&fail_BIO_new, 1, jwt::error::ecdsa_error::create_mem_bio_failed}, +#ifndef LIBWOLFSSL_VERSION_HEX + {&fail_BIO_get_mem_data, 1, jwt::error::ecdsa_error::convert_to_pem_failed}, +#endif +#ifdef JWT_OPENSSL_3_0 + {&fail_PEM_write_bio_PUBKEY, 1, jwt::error::ecdsa_error::load_key_bio_write}, + {&fail_OSSL_PARAM_BLD_new, 1, jwt::error::ecdsa_error::create_context_failed}, + {&fail_OSSL_PARAM_BLD_push_utf8_string, 1, jwt::error::ecdsa_error::set_ecdsa_failed}, + {&fail_OSSL_PARAM_BLD_push_octet_string, 1, jwt::error::ecdsa_error::set_ecdsa_failed}, + {&fail_OSSL_PARAM_BLD_to_param, 1, jwt::error::ecdsa_error::set_ecdsa_failed}, + {&fail_EVP_PKEY_CTX_new_from_name, 1, jwt::error::ecdsa_error::create_context_failed}, + {&fail_EVP_PKEY_fromdata_init, 1, jwt::error::ecdsa_error::cert_load_failed}, + {&fail_EVP_PKEY_fromdata, 1, jwt::error::ecdsa_error::cert_load_failed} +#else + {&fail_PEM_write_bio_EC_PUBKEY, 1, jwt::error::ecdsa_error::load_key_bio_write}, + {&fail_EC_GROUP_new_by_curve_name, 1, jwt::error::ecdsa_error::set_ecdsa_failed}, + {&fail_EC_POINT_new, 1, jwt::error::ecdsa_error::set_ecdsa_failed}, + {&fail_EC_POINT_set_affine_coordinates_GFp, 1, jwt::error::ecdsa_error::set_ecdsa_failed}, + {&fail_EC_KEY_new, 1, jwt::error::ecdsa_error::set_ecdsa_failed}, +#ifndef LIBWOLFSSL_VERSION_HEX + {&fail_EC_KEY_set_group, 1, jwt::error::ecdsa_error::set_ecdsa_failed}, +#endif + {&fail_EC_KEY_set_public_key, 1, jwt::error::ecdsa_error::set_ecdsa_failed} +#endif + }; + + run_multitest(mapping, [](std::error_code& ec) { + auto res = jwt::helper::create_public_key_from_ec_components( + "P-384", "0uQ1-1P_wmhOuYvVtTogHOSBLC05IvK7L6sTPIX8Dl4Bg9nhC3v_FsgifjnXnijU", + "xVJSyWa9SuxwBonUhg6SiCEv-ixb74hjDesC4D7OwllVcnkDJmOy_NMx4N7yDPJp", ec); + ASSERT_EQ(res, ""); + }); +} + TEST(OpenSSLErrorTest, ConvertCertBase64DerToPem) { std::vector mapping{{&fail_BIO_new, 1, jwt::error::rsa_error::create_mem_bio_failed}, {&fail_PEM_write_bio_cert, 1, jwt::error::rsa_error::write_cert_failed},