mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2026-05-04 12:41:00 +02:00
MINOR: jwt: Convert EC JWK to EVP_PKEY
Convert a JWK with the "EC" key type ("kty") into an EVP_PKEY. The JWK
can either represent a public key if it only contains the "x" and "y"
fields, or a private key if it also contains the "d" field.
This commit is contained in:
parent
e34b633be3
commit
3d9764f4c3
229
src/jwe.c
229
src/jwe.c
@ -1056,11 +1056,11 @@ end:
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void clear_bignums(BIGNUM *nums[RSA_BIGNUM_COUNT])
|
||||
static inline void clear_bignums(BIGNUM **nums, int count)
|
||||
{
|
||||
int idx = 0;
|
||||
|
||||
while (idx < RSA_BIGNUM_COUNT)
|
||||
while (idx < count)
|
||||
BN_free(nums[idx++]);
|
||||
}
|
||||
|
||||
@ -1105,10 +1105,10 @@ static int build_RSA_PKEY_from_buf(struct buffer *jwk, EVP_PKEY **pkey)
|
||||
end:
|
||||
/* The bignums are duplicated with OpenSSL3+ but not with the older API */
|
||||
#if HA_OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
clear_bignums(nums);
|
||||
clear_bignums(nums, RSA_BIGNUM_COUNT);
|
||||
#else
|
||||
if (retval)
|
||||
clear_bignums(nums);
|
||||
clear_bignums(nums, RSA_BIGNUM_COUNT);
|
||||
#endif
|
||||
|
||||
free_trash_chunk(tmpbuf);
|
||||
@ -1120,12 +1120,226 @@ end:
|
||||
return retval;
|
||||
}
|
||||
|
||||
enum {
|
||||
EC_BIGNUM_X,
|
||||
EC_BIGNUM_Y,
|
||||
EC_BIGNUM_D,
|
||||
|
||||
EC_BIGNUM_COUNT
|
||||
};
|
||||
|
||||
|
||||
#if HA_OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
/*
|
||||
* Build an EC pubkey out of its 'x' and 'y' parameters for EC curve with <nid>.
|
||||
* Dump the corresponding pubkey in <out> buffer.
|
||||
* Returns 0 in case of success, 1 otherwise.
|
||||
*/
|
||||
static inline int curve_param_to_pubkey(int nid, BIGNUM *x, BIGNUM *y, struct buffer *out)
|
||||
{
|
||||
EC_POINT *ec_point = NULL;
|
||||
EC_GROUP *group = NULL;
|
||||
BN_CTX *bnctx = NULL;
|
||||
int retval = 1;
|
||||
|
||||
if (!out)
|
||||
goto end;
|
||||
|
||||
group = EC_GROUP_new_by_curve_name(nid);
|
||||
if (!group)
|
||||
goto end;
|
||||
|
||||
bnctx = BN_CTX_new();
|
||||
if (!bnctx)
|
||||
goto end;
|
||||
|
||||
ec_point = EC_POINT_new(group);
|
||||
if (!ec_point)
|
||||
goto end;
|
||||
|
||||
if (EC_POINT_set_affine_coordinates(group, ec_point, x, y, bnctx) < 0)
|
||||
goto end;
|
||||
|
||||
out->data = EC_POINT_point2oct(group, ec_point, POINT_CONVERSION_UNCOMPRESSED,
|
||||
(unsigned char*)b_orig(out), b_size(out), bnctx);
|
||||
|
||||
if (out->data == 0)
|
||||
goto end;
|
||||
|
||||
retval = 0;
|
||||
|
||||
end:
|
||||
EC_POINT_free(ec_point);
|
||||
EC_GROUP_free(group);
|
||||
BN_CTX_free(bnctx);
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Build an EVP_PKEY that contains an EC key (either public or private) out of a
|
||||
* JWK buffer that must have an "EC" key type ("kty" field). A public key must
|
||||
* have valid "x" and "y" fields and a private key must have a "d" field as
|
||||
* well.
|
||||
* Return 0 in case of success, 1 otherwise.
|
||||
*/
|
||||
static int do_build_EC_PKEY(struct buffer *curve, BIGNUM *nums[EC_BIGNUM_COUNT], EVP_PKEY **pkey)
|
||||
#if HA_OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
{
|
||||
int retval = 1;
|
||||
struct buffer *pubkey = NULL;
|
||||
int nid = 0;
|
||||
|
||||
OSSL_PARAM *params = NULL;
|
||||
OSSL_PARAM_BLD *param_bld = NULL;
|
||||
EVP_PKEY_CTX *pctx = NULL;
|
||||
|
||||
nid = curves2nid(b_orig(curve));
|
||||
if (nid == -1)
|
||||
goto end;
|
||||
|
||||
pubkey = alloc_trash_chunk();
|
||||
if (!pubkey)
|
||||
goto end;
|
||||
|
||||
if (curve_param_to_pubkey(nid, nums[EC_BIGNUM_X], nums[EC_BIGNUM_Y], pubkey))
|
||||
goto end;
|
||||
|
||||
param_bld = OSSL_PARAM_BLD_new();
|
||||
|
||||
if (!OSSL_PARAM_BLD_push_utf8_string(param_bld, OSSL_PKEY_PARAM_GROUP_NAME,
|
||||
b_orig(curve), b_data(curve)) ||
|
||||
!OSSL_PARAM_BLD_push_octet_string(param_bld, OSSL_PKEY_PARAM_PUB_KEY,
|
||||
pubkey->area, pubkey->data) ||
|
||||
(nums[EC_BIGNUM_D] && !OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_PRIV_KEY, nums[EC_BIGNUM_D])))
|
||||
goto end;
|
||||
|
||||
params = OSSL_PARAM_BLD_to_param(param_bld);
|
||||
|
||||
if (!params)
|
||||
goto end;
|
||||
|
||||
pctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
|
||||
if (!pctx)
|
||||
goto end;
|
||||
|
||||
if (EVP_PKEY_fromdata_init(pctx) != 1 ||
|
||||
EVP_PKEY_fromdata(pctx, pkey, EVP_PKEY_KEYPAIR, params) != 1)
|
||||
goto end;
|
||||
|
||||
retval = 0;
|
||||
|
||||
end:
|
||||
free_trash_chunk(pubkey);
|
||||
OSSL_PARAM_BLD_free(param_bld);
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
OSSL_PARAM_free(params);
|
||||
return retval;
|
||||
}
|
||||
#else
|
||||
{
|
||||
EC_KEY *ec_key = NULL;
|
||||
int retval = 1;
|
||||
int nid = 0;
|
||||
|
||||
nid = curves2nid(b_orig(curve));
|
||||
if (nid == -1)
|
||||
goto end;
|
||||
|
||||
ec_key = EC_KEY_new_by_curve_name(nid);
|
||||
if (!ec_key)
|
||||
goto end;
|
||||
|
||||
if (nums[EC_BIGNUM_D] && EC_KEY_set_private_key(ec_key, nums[EC_BIGNUM_D]) < 0)
|
||||
goto end;
|
||||
|
||||
if (EC_KEY_set_public_key_affine_coordinates(ec_key, nums[EC_BIGNUM_X], nums[EC_BIGNUM_Y]) < 0)
|
||||
goto end;
|
||||
|
||||
if (EC_KEY_check_key(ec_key) == 0)
|
||||
goto end;
|
||||
|
||||
*pkey = EVP_PKEY_new();
|
||||
if (!*pkey)
|
||||
goto end;
|
||||
|
||||
if (EVP_PKEY_set1_EC_KEY(*pkey, ec_key) == 0)
|
||||
goto end;
|
||||
|
||||
retval = 0;
|
||||
|
||||
end:
|
||||
EC_KEY_free(ec_key);
|
||||
if (retval) {
|
||||
EVP_PKEY_free(*pkey);
|
||||
*pkey = NULL;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Build an EVP_PKEY that contains an EC key out of a JWK buffer that must have
|
||||
* an "EC" key type ("kty" field).
|
||||
* Return 0 in case of success, 1 otherwise.
|
||||
*/
|
||||
static int build_EC_PKEY_from_buf(struct buffer *jwk, EVP_PKEY **pkey)
|
||||
{
|
||||
BIGNUM *nums[EC_BIGNUM_COUNT] = {};
|
||||
int retval = 1;
|
||||
struct buffer *crv = NULL, *tmpbuf = NULL;
|
||||
|
||||
crv = alloc_trash_chunk();
|
||||
if (!crv)
|
||||
goto end;
|
||||
|
||||
tmpbuf = alloc_trash_chunk();
|
||||
if (!tmpbuf)
|
||||
goto end;
|
||||
|
||||
if (get_jwk_field(jwk, "$.x", tmpbuf) || (nums[EC_BIGNUM_X] = base64url_to_BIGNUM(tmpbuf)) == NULL)
|
||||
goto end;
|
||||
if (get_jwk_field(jwk, "$.y", tmpbuf) || (nums[EC_BIGNUM_Y] = base64url_to_BIGNUM(tmpbuf)) == NULL)
|
||||
goto end;
|
||||
/* "d" is optional, we might have a pure public key with only "x" and
|
||||
* "y" provided. */
|
||||
if (get_jwk_field(jwk, "$.d", tmpbuf) == 0 && (nums[EC_BIGNUM_D] = base64url_to_BIGNUM(tmpbuf)) == NULL)
|
||||
goto end;
|
||||
|
||||
if (get_jwk_field(jwk, "$.crv", crv))
|
||||
goto end;
|
||||
|
||||
retval = do_build_EC_PKEY(crv, nums, pkey);
|
||||
|
||||
#if 0
|
||||
if (!retval) {
|
||||
BIO *bp = BIO_new_fp(stdout, BIO_NOCLOSE);
|
||||
EVP_PKEY_print_private(bp, *pkey, 1, NULL);
|
||||
BIO_free(bp);
|
||||
}
|
||||
#endif
|
||||
|
||||
end:
|
||||
clear_bignums(nums, EC_BIGNUM_COUNT);
|
||||
|
||||
free_trash_chunk(crv);
|
||||
free_trash_chunk(tmpbuf);
|
||||
if (retval) {
|
||||
EVP_PKEY_free(*pkey);
|
||||
*pkey = NULL;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef enum {
|
||||
JWK_KTY_OCT,
|
||||
JWK_KTY_RSA,
|
||||
// JWK_KTY_EC
|
||||
JWK_KTY_EC
|
||||
} jwk_type;
|
||||
|
||||
struct jwk {
|
||||
@ -1205,6 +1419,11 @@ static int process_jwk(struct buffer *jwk_buf, struct jwk *jwk)
|
||||
|
||||
if (build_RSA_PKEY_from_buf(jwk_buf, &jwk->pkey))
|
||||
goto end;
|
||||
} else if (chunk_strcmp(kty, "EC") == 0) {
|
||||
jwk->type = JWK_KTY_EC;
|
||||
|
||||
if (build_EC_PKEY_from_buf(jwk_buf, &jwk->pkey))
|
||||
goto end;
|
||||
} else
|
||||
goto end;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user