Skip to content

Commit

Permalink
Add key checks for bits and curve
Browse files Browse the repository at this point in the history
Signed-off-by: Ben Collins <[email protected]>
benmcollins committed Jan 5, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent bbac57a commit 9f9b485
Showing 8 changed files with 171 additions and 14 deletions.
1 change: 1 addition & 0 deletions libjwt/jwks.c
Original file line number Diff line number Diff line change
@@ -133,6 +133,7 @@ static int process_octet(json_t *jwk, jwk_item_t *item)
item->provider = JWT_CRYPTO_OPS_ANY;
item->oct.key = bin_k;
item->oct.len = len_k;
item->bits = len_k * 8;

return 0;
}
75 changes: 66 additions & 9 deletions libjwt/jwt.c
Original file line number Diff line number Diff line change
@@ -507,29 +507,84 @@ int jwt_base64uri_encode(char **_dst, const char *plain, int plain_len)

static int __check_hmac(const jwt_t *jwt)
{
int min_len = 0;
int key_len = 0;
int key_bits = 0;

if (jwt->config.jw_key)
key_bits = jwt->config.jw_key->bits;
else
key_bits = jwt->config.key_len * 8;

if (key_bits < 256)
return -1;

switch (jwt->alg) {
case JWT_ALG_HS256:
min_len = 32;
if (key_bits >= 256)
return 0;
break;

case JWT_ALG_HS384:
min_len = 48;
if (key_bits >= 384)
return 0;
break;

case JWT_ALG_HS512:
min_len = 64;
if (key_bits >= 512)
return 0;
break;

default:
return -1;
}

return -1;
}

static int __check_key_bits(const jwt_t *jwt)
{
int key_bits = 0;

if (jwt->config.jw_key)
key_len = jwt->config.jw_key->oct.len;
else
key_len = jwt->config.key_len;
key_bits = jwt->config.jw_key->bits;

/* Ignore if we don't have it */
if (key_bits == 0)
return 0;

switch (jwt->alg) {
case JWT_ALG_RS256:
case JWT_ALG_RS384:
case JWT_ALG_RS512:

return key_len < min_len ? -1 : 0;
case JWT_ALG_PS256:
case JWT_ALG_PS384:
case JWT_ALG_PS512:
if (key_bits >= 2048)
return 0;
break;

case JWT_ALG_EDDSA:
case JWT_ALG_ES256K:
case JWT_ALG_ES256:
if (key_bits == 256)
return 0;
break;

case JWT_ALG_ES384:
if (key_bits == 384)
return 0;
break;

case JWT_ALG_ES512:
if (key_bits == 521)
return 0;
break;

default:
return -1;
}

return -1;
}

int jwt_sign(jwt_t *jwt, char **out, unsigned int *len, const char *str,
@@ -562,6 +617,8 @@ int jwt_sign(jwt_t *jwt, char **out, unsigned int *len, const char *str,

/* EdDSA */
case JWT_ALG_EDDSA:
if (__check_key_bits(jwt))
return EINVAL;
return jwt_ops->sign_sha_pem(jwt, out, len, str, str_len);

/* You wut, mate? */
3 changes: 3 additions & 0 deletions libjwt/openssl/jwk-parse.c
Original file line number Diff line number Diff line change
@@ -176,6 +176,9 @@ static int pctx_to_pem(EVP_PKEY_CTX *pctx, OSSL_PARAM *params,
jwk_ctx->pkey = pkey;
item->provider_data = jwk_ctx;

EVP_PKEY_get_size_t_param(pkey, OSSL_PKEY_PARAM_BITS,
&item->bits);

/* From here after, we don't fail. PEM is optional. */
ret = 0;

31 changes: 27 additions & 4 deletions libjwt/openssl/sign-verify.c
Original file line number Diff line number Diff line change
@@ -103,7 +103,10 @@ static int openssl_verify_sha_hmac(jwt_t *jwt, const char *head,

static int __degree_and_check(EVP_PKEY *pkey, jwt_t *jwt)
{
int degree, curve_nid;
int degree, curve_nid, exp_nid = 0;

if (jwt->config.jw_key && jwt->config.jw_key->bits)
return jwt->config.jw_key->bits;

#if OPENSSL_VERSION_NUMBER < 0x30000000L
const EC_GROUP *group;
@@ -139,9 +142,29 @@ static int __degree_and_check(EVP_PKEY *pkey, jwt_t *jwt)
EC_GROUP_free(group);
#endif

/* We only perform this check for ES256K. All others we just check
* the degree (bits). */
if (jwt->alg == JWT_ALG_ES256K && curve_nid != NID_secp256k1)
switch (jwt->alg) {
case JWT_ALG_ES256:
exp_nid = NID_X9_62_prime256v1;
break;
case JWT_ALG_ES384:
exp_nid = NID_secp384r1;
break;
case JWT_ALG_ES512:
exp_nid = NID_secp521r1;
break;
case JWT_ALG_ES256K:
exp_nid = NID_secp256k1;
break;
case JWT_ALG_EDDSA:
if (curve_nid == NID_ED448 || curve_nid == NID_ED25519)
return degree;
else
EC_ERROR(EINVAL);
default:
EC_ERROR(EINVAL);
}

if (curve_nid != exp_nid)
EC_ERROR(EINVAL);

return degree;
3 changes: 2 additions & 1 deletion tests/jwt_ec.c
Original file line number Diff line number Diff line change
@@ -37,7 +37,8 @@ static const char jwt_es_invalid[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJpY
START_TEST(test_jwt_encode_es256)
{
SET_OPS();
__test_alg_key(JWT_ALG_ES256, "ec_key_prime256v1.pem", "ec_key_prime256v1_pub.pem");
__test_alg_key(JWT_ALG_ES256, "ec_key_prime256v1.pem",
"ec_key_prime256v1_pub.pem");
}
END_TEST

38 changes: 38 additions & 0 deletions tests/jwt_rsa.c
Original file line number Diff line number Diff line change
@@ -190,6 +190,43 @@ START_TEST(test_jwt_encode_rsa_with_ec)
}
END_TEST

START_TEST(test_jwt_encode_rsa_1024)
{
JWT_CONFIG_DECLARE(config);
jwk_set_t *jwk_set = NULL;
jwk_item_t *item;
jwt_t *jwt = NULL;
char *out;
int ret = 0;

SET_OPS_JWK();

read_key("rsa_key_1024.json");
jwk_set = jwks_create(t_config.key);
free_key();

ck_assert_ptr_nonnull(jwk_set);
ck_assert(!jwks_error(jwk_set));

item = jwks_item_get(jwk_set, 0);
ck_assert_ptr_nonnull(item);
ck_assert(!item->error);

config.jw_key = item;
jwt = jwt_create(&config);
ck_assert_ptr_nonnull(jwt);

ret = jwt_add_grant(jwt, "sub", "user0");
ck_assert_int_eq(ret, 0);

/* Should fail from too few bits in key */
out = jwt_encode_str(jwt);
ck_assert_ptr_null(out);

jwt_free(jwt);
}
END_TEST

START_TEST(test_jwt_verify_invalid_token)
{
jwt_t *jwt = NULL;
@@ -308,6 +345,7 @@ static Suite *libjwt_suite(const char *title)
tcase_add_loop_test(tc_core, test_jwt_verify_invalid_cert, 0, i);
tcase_add_loop_test(tc_core, test_jwt_verify_invalid_cert_file, 0, i);
tcase_add_loop_test(tc_core, test_jwt_encode_invalid_key, 0, i);
tcase_add_loop_test(tc_core, test_jwt_encode_rsa_1024, 0, i);

tcase_set_timeout(tc_core, 120);

18 changes: 18 additions & 0 deletions tests/keys/rsa_key_1024.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"use": "sig",
"key_ops": [
"verify",
"sign"
],
"alg": "RS256",
"kid": "0023a6e1-f093-448d-9038-9ff168611b86",
"kty": "RSA",
"n": "tY1EN770i5YmM04J-5TJr4eVjTtmJ7qVZdg46RjMVL9kXmNUSjRZKOoI798IoGhHCOSUOs26lxnegazKIeADuFOpgWXWwcC9jGrCly30zho_pUAHA8LEZjaQtuZatT9XyRSQC6inHIyRZrqq7hzhyFpcL-7sqwITtTSPADVRjPk",
"e": "AQAB",
"d": "QcjNVjHAJLAkaWS2o013gz3qx8ElM_Ic3klp3bSZnX-4W6c8TKMJR5T95gT3-D-wtZ01xNT07nl2MaCtoXatgcku7vPaarOqO_VGE99RFEenHKNg90mx1DqXglqGbGwUSHUleZpNzsbHzn5Kp9G9F-pNPxiR2uGWY6GKq2JAPfE",
"p": "6l9vSEey8h7zq3utvd62pfGrWbPeCIdN4qs_YtfAL2ZOQqBySZVB82wS73q_6EXCcFByMek5UZ6BiXczxIxgHQ",
"q": "xk4MF3FBTRsG7V-LFZYMJg6QMYhRx40TMRbtcGwJ9bqPICMT6dyer4p_YJ-M-JBn2V3ykFcjNvM148tNZTqBjQ",
"dp": "nSUcElSwsRL7wpEMr3Ay-xUNFPo693DxUfgMahPLMnzgIGBAAQJo0_Q0xgmBvxV3B6OIzdpYqlci967LMRAGKQ",
"dq": "t4O4quUTYnC1i2yUwOg32-w0QrJ9w-bpMM-f8h75ZJ8-HqZ3oj-Z1qeb02Yzxj-U61KGzE7nds3CPEmDGT_4bQ",
"qi": "3ckWVl2ALDerVMOZTBBjcUpiGH9a43WhU5_od7OE-F24XDWZlbr3AFlFh1FmS8eWk-wrxs9pXPZG7eBpKlJYGg"
}
16 changes: 16 additions & 0 deletions tests/keys/rsa_key_1024.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-----BEGIN PRIVATE KEY-----
MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALWNRDe+9IuWJjNO
CfuUya+HlY07Zie6lWXYOOkYzFS/ZF5jVEo0WSjqCO/fCKBoRwjklDrNupcZ3oGs
yiHgA7hTqYFl1sHAvYxqwpct9M4aP6VABwPCxGY2kLbmWrU/V8kUkAuopxyMkWa6
qu4c4chaXC/u7KsCE7U0jwA1UYz5AgMBAAECgYBByM1WMcAksCRpZLajTXeDPerH
wSUz8hzeSWndtJmdf7hbpzxMowlHlP3mBPf4P7C1nTXE1PTueXYxoK2hdq2ByS7u
89pqs6o79UYT31EUR6cco2D3SbHUOpeCWoZsbBRIdSV5mk3OxsfOfkqn0b0X6k0/
GJHa4ZZjoYqrYkA98QJBAOpfb0hHsvIe86t7rb3etqXxq1mz3giHTeKrP2LXwC9m
TkKgckmVQfNsEu96v+hFwnBQcjHpOVGegYl3M8SMYB0CQQDGTgwXcUFNGwbtX4sV
lgwmDpAxiFHHjRMxFu1wbAn1uo8gIxPp3J6vin9gn4z4kGfZXfKQVyM28zXjy01l
OoGNAkEAnSUcElSwsRL7wpEMr3Ay+xUNFPo693DxUfgMahPLMnzgIGBAAQJo0/Q0
xgmBvxV3B6OIzdpYqlci967LMRAGKQJBALeDuKrlE2JwtYtslMDoN9vsNEKyfcPm
6TDPn/Ie+WSfPh6md6I/mdanm9NmM8Y/lOtShsxO53bNwjxJgxk/+G0CQQDdyRZW
XYAsN6tUw5lMEGNxSmIYf1rjdaFTn+h3s4T4XbhcNZmVuvcAWUWHUWZLx5aT7CvG
z2lc9kbt4GkqUlga
-----END PRIVATE KEY-----

0 comments on commit 9f9b485

Please sign in to comment.