jwks_find_bykid: New function to find keys by "kid"

Signed-off-by: Ben Collins <bcollins@libjwt.io>
This commit is contained in:
Ben Collins 2025-02-13 15:47:31 -05:00
parent 99cd27d258
commit 62a6f564eb
No known key found for this signature in database
GPG key ID: 5D5A57C7242B22CF
7 changed files with 102 additions and 8 deletions

View file

@ -1381,6 +1381,19 @@ static inline void jwks_freep(jwk_set_t **jwks) {
JWT_EXPORT
const jwk_item_t *jwks_item_get(const jwk_set_t *jwk_set, size_t index);
/**
* @brief Find a jwk_item_t with a specific kid (Key ID)
*
* LibJWT does not ensure that kid's are unique in a given keyring, so care
* must be taken. This will return the first match.
*
* @param jwk_set An existing jwk_set_t
* @param kid String representing a ``kid`` to find
* @return A jwk_item_t object or NULL if none found
*/
JWT_EXPORT
jwk_item_t *jwks_find_bykid(jwk_set_t *jwk_set, const char *kid);
/**
* @brief Whether this key is private (or public)
*

View file

@ -310,6 +310,19 @@ static int jwks_item_add(jwk_set_t *jwk_set, jwk_item_t *item)
return 0;
}
jwk_item_t *jwks_find_bykid(jwk_set_t *jwk_set, const char *kid)
{
jwk_item_t *item = NULL;
list_for_each_entry(item, &jwk_set->head, node) {
if (item->kid == NULL || strcmp(item->kid, kid))
continue;
return item;
}
return NULL;
}
static void __item_free(jwk_item_t *todel)
{
if (todel->provider == JWT_CRYPTO_OPS_ANY)

View file

@ -471,8 +471,7 @@ jwt_t *jwt_verify_sig(jwt_t *jwt, const char *head, unsigned int head_len,
// LCOV_EXCL_START
default:
jwt_write_error(jwt, "Unknown algorigthm");
// LCOV_EXCL_STOP
}
} // LCOV_EXCL_STOP
return jwt;
}

View file

@ -329,7 +329,7 @@ static int mbedtls_verify_sha_pem(jwt_t *jwt, const char *head,
/* Verify ECDSA signature */
if (mbedtls_ecdsa_verify(&ecdsa.private_grp, hash,
mbedtls_md_get_size(md_info), &ecdsa.private_Q, &r, &s))
VERIFY_ERROR("ECDSA signature verification failed"); // LCOV_EXCL_LINE
VERIFY_ERROR("Failed to verify signature"); // LCOV_EXCL_LINE
/* Free ECDSA resources */
mbedtls_mpi_free(&r);
@ -343,13 +343,13 @@ static int mbedtls_verify_sha_pem(jwt_t *jwt, const char *head,
mbedtls_md_get_type(md_info),
mbedtls_md_get_size(md_info),
hash, sig))
VERIFY_ERROR("RSASSA-PSS verify failed"); // LCOV_EXCL_LINE
VERIFY_ERROR("Failed to verify signature"); // LCOV_EXCL_LINE
} else {
if (mbedtls_rsa_pkcs1_verify(mbedtls_pk_rsa(pk),
mbedtls_md_get_type(md_info),
mbedtls_md_get_size(md_info),
hash, sig))
VERIFY_ERROR("RSA verify failed"); // LCOV_EXCL_LINE
VERIFY_ERROR("Failed to verify signature"); // LCOV_EXCL_LINE
}
} else {
VERIFY_ERROR("Unexpected key typ"); // LCOV_EXCL_LINE

View file

@ -411,7 +411,7 @@ static int openssl_verify_sha_pem(jwt_t *jwt, const char *head,
/* One-shot update and verify */
if (EVP_DigestVerify(mdctx, sig, slen, (const unsigned char *)head,
head_len) != 1)
VERIFY_ERROR("Signature failed validation");
VERIFY_ERROR("Failed to verify signature");
jwt_verify_sha_pem_done:
BIO_free(bufkey);

View file

@ -774,8 +774,8 @@ START_TEST(verify_ps256_nosig)
read_json("rsa_pss_key_2048.json");
ret = jwt_checker_setkey(NULL, JWT_ALG_PS256, g_item);
ck_assert_int_ne(ret, 0);
ret = jwt_checker_setkey(checker, JWT_ALG_PS256, g_item);
ck_assert_int_eq(ret, 0);
ret = jwt_checker_verify(checker, token);
ck_assert_int_ne(ret, 0);
@ -786,6 +786,67 @@ START_TEST(verify_ps256_nosig)
}
END_TEST
START_TEST(verify_ps256_bad_b64_sig)
{
jwt_checker_auto_t *checker = NULL;
const char token[] = "eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI"
"6ZmFsc2UsImlhdCI6MTczNjY5NDU5NCwiaXNzIjoiaHR0cHM6Ly9zd2lzc2Rp"
"c2suY29tIiwidXNlciI6ImJlbmNvbGxpbnMifQ.eyJhbGciOiJQUzI1N*IsIn"
"R5cCI6I!pXVCJ9";
int ret;
SET_OPS();
checker = jwt_checker_new();
ck_assert_ptr_nonnull(checker);
ck_assert_int_eq(jwt_checker_error(checker), 0);
read_json("rsa_pss_key_2048.json");
ret = jwt_checker_setkey(checker, JWT_ALG_PS256, g_item);
ck_assert_int_eq(ret, 0);
ret = jwt_checker_verify(checker, token);
ck_assert_int_ne(ret, 0);
ck_assert_str_eq(jwt_checker_error_msg(checker),
"Error decoding signature");
free_key();
}
END_TEST
START_TEST(verify_ps256_bad_sig)
{
jwt_checker_auto_t *checker = NULL;
const char token[] = "eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI"
"6ZmFsc2UsImlhdCI6MTczNjY5NDU5NCwiaXNzIjoiaHR0cHM6Ly9zd2lzc2Rp"
"c2suY29tIiwidXNlciI6ImJlbmNvbGxpbnMifQ.eyJhbGciOiJQUzI1NiIsIn"
"R5cCI6IkpXVCJ9";
const char *err;
int ret;
SET_OPS();
checker = jwt_checker_new();
ck_assert_ptr_nonnull(checker);
ck_assert_int_eq(jwt_checker_error(checker), 0);
read_json("rsa_pss_key_2048.json");
ret = jwt_checker_setkey(checker, JWT_ALG_PS256, g_item);
ck_assert_int_eq(ret, 0);
ret = jwt_checker_verify(checker, token);
ck_assert_int_ne(ret, 0);
err = jwt_checker_error_msg(checker);
ck_assert_ptr_nonnull(err);
ck_assert_ptr_nonnull(strstr(err, "Failed to verify signature"));
free_key();
}
END_TEST
static Suite *libjwt_suite(const char *title)
{
Suite *s;
@ -836,6 +897,8 @@ static Suite *libjwt_suite(const char *title)
tc_core = tcase_create("Corner cases");
tcase_add_loop_test(tc_core, verify_ps256_nosig, 0, i);
tcase_add_loop_test(tc_core, verify_ps256_bad_b64_sig, 0, i);
tcase_add_loop_test(tc_core, verify_ps256_bad_sig, 0, i);
suite_add_tcase(s, tc_core);
return s;

View file

@ -53,6 +53,12 @@ START_TEST(test_jwks_keyring_load)
}
ck_assert_int_eq(fails, 0);
item = jwks_find_bykid(g_jwk_set, "SDSDS");
ck_assert_ptr_null(item);
item = jwks_find_bykid(g_jwk_set, "354912a0-b90a-435e-886a-1629f7b2665e");
ck_assert_ptr_nonnull(item);
ck_assert_int_eq(i, 27);
i = jwks_item_count(g_jwk_set);
ck_assert_int_eq(i, 27);