blob: 5baa0363aaaa0de8dc4dbced354bb063c886ef6f [file] [edit]
/* Public domain, no copyright. Use at your own risk. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jwt_tests.h"
/* jwks_generate() key generation (issue #311). Runs under each compiled crypto
* backend (SET_OPS) and both JSON backends. Backends differ in capability
* (MbedTLS has no EdDSA/ML-DSA; GnuTLS no secp256k1/X-curves), so the
* "optional" types are probed: a clean error is accepted, a success is verified.
*/
/* Generate, then (for a signing alg) round-trip sign+verify the new key.
* Returns 1 if generated OK, 0 if the backend reported it unsupported. */
static int gen_check(jwk_key_type_t kty, const char *param, jwt_alg_t alg,
unsigned int flags, int can_sign)
{
jwk_set_t *set;
const jwk_item_t *k;
int ok;
set = jwks_create_generate(kty, param, alg, flags);
ck_assert_ptr_nonnull(set);
k = jwks_item_get(set, 0);
if (k == NULL || jwks_item_error(k)) {
/* Capability-gated on this backend: must be a clean error. */
ck_assert(jwks_error(set) || (k && jwks_item_error(k)));
jwks_free(set);
return 0;
}
ck_assert_int_eq(jwks_item_kty(k), kty);
ck_assert_int_eq(jwks_item_is_private(k), 1);
if (flags & JWK_KEY_GEN_KID)
ck_assert_ptr_nonnull(jwks_item_kid(k));
else
ck_assert_ptr_null(jwks_item_kid(k));
if (can_sign) {
jwt_builder_auto_t *b = jwt_builder_new();
jwt_checker_auto_t *c = jwt_checker_new();
char_auto *tok = NULL;
ck_assert_int_eq(jwt_builder_setkey(b, alg, k), 0);
tok = jwt_builder_generate(b);
ck_assert_ptr_nonnull(tok);
ck_assert_int_eq(jwt_checker_setkey(c, alg, k), 0);
ck_assert_int_eq(jwt_checker_verify(c, tok), 0);
}
ok = 1;
jwks_free(set);
return ok;
}
/* Core types every backend must support. */
START_TEST(test_generate_core)
{
SET_OPS();
ck_assert_int_eq(gen_check(JWK_KEY_TYPE_EC, "P-256", JWT_ALG_ES256,
JWK_KEY_GEN_KID, 1), 1);
ck_assert_int_eq(gen_check(JWK_KEY_TYPE_EC, "P-384", JWT_ALG_ES384,
JWK_KEY_GEN_KID, 1), 1);
ck_assert_int_eq(gen_check(JWK_KEY_TYPE_EC, "P-521", JWT_ALG_ES512,
JWK_KEY_GEN_KID, 1), 1);
ck_assert_int_eq(gen_check(JWK_KEY_TYPE_RSA, "2048", JWT_ALG_RS256,
JWK_KEY_GEN_KID, 1), 1);
ck_assert_int_eq(gen_check(JWK_KEY_TYPE_RSA, "2048", JWT_ALG_PS256,
JWK_KEY_GEN_KID, 1), 1);
/* The in-key PEM cannot encode PS384 vs PS256; the override must stick. */
ck_assert_int_eq(gen_check(JWK_KEY_TYPE_RSA, "3072", JWT_ALG_PS384,
0, 1), 1);
ck_assert_int_eq(gen_check(JWK_KEY_TYPE_OCT, "256", JWT_ALG_HS256,
JWK_KEY_GEN_KID, 1), 1);
ck_assert_int_eq(gen_check(JWK_KEY_TYPE_OCT, "512", JWT_ALG_HS512,
0, 1), 1);
}
END_TEST
/* Capability-dependent types: a clean error or a verified key, never a crash. */
START_TEST(test_generate_optional)
{
SET_OPS();
gen_check(JWK_KEY_TYPE_EC, "secp256k1", JWT_ALG_ES256K, JWK_KEY_GEN_KID, 1);
gen_check(JWK_KEY_TYPE_OKP, "Ed25519", JWT_ALG_EDDSA, JWK_KEY_GEN_KID, 1);
gen_check(JWK_KEY_TYPE_OKP, "Ed448", JWT_ALG_EDDSA, 0, 1);
gen_check(JWK_KEY_TYPE_OKP, "X25519", JWT_ALG_NONE, 0, 0);
/* AKP/ML-DSA where a capable backend (OpenSSL >= 3.5 / GnuTLS+leancrypto)
* is present; a clean error elsewhere. */
gen_check(JWK_KEY_TYPE_AKP, NULL, JWT_ALG_ML_DSA_44, JWK_KEY_GEN_KID, 1);
gen_check(JWK_KEY_TYPE_AKP, NULL, JWT_ALG_ML_DSA_65, 0, 1);
gen_check(JWK_KEY_TYPE_AKP, NULL, JWT_ALG_ML_DSA_87, 0, 1);
}
END_TEST
/* Bad requests are rejected cleanly (with an error set), never silently. */
START_TEST(test_generate_errors)
{
struct { jwk_key_type_t kty; const char *param; jwt_alg_t alg; } bad[] = {
{ JWK_KEY_TYPE_EC, "P-256", JWT_ALG_RS256 }, /* alg<->kty */
{ JWK_KEY_TYPE_RSA, "1024", JWT_ALG_RS256 }, /* RSA too small */
{ JWK_KEY_TYPE_RSA, "huge", JWT_ALG_RS256 }, /* non-numeric */
{ JWK_KEY_TYPE_EC, "P-999", JWT_ALG_NONE }, /* unknown curve */
{ JWK_KEY_TYPE_OCT, "7", JWT_ALG_NONE }, /* not multiple of 8 */
{ JWK_KEY_TYPE_AKP, NULL, JWT_ALG_NONE }, /* AKP needs a variant */
};
size_t i;
SET_OPS();
for (i = 0; i < ARRAY_SIZE(bad); i++) {
jwk_set_t *set = jwks_create_generate(bad[i].kty, bad[i].param,
bad[i].alg, 0);
const jwk_item_t *k;
ck_assert_ptr_nonnull(set);
k = jwks_item_get(set, 0);
/* Either a set-level error and no item, or an error item. */
ck_assert(jwks_error(set) || (k != NULL && jwks_item_error(k)));
ck_assert_int_eq(k == NULL || jwks_item_error(k), 1);
jwks_free(set);
}
}
END_TEST
/* Append to an existing keyring rather than replacing it. */
START_TEST(test_generate_append)
{
jwk_set_t *set;
SET_OPS();
set = jwks_create_generate(JWK_KEY_TYPE_EC, "P-256", JWT_ALG_ES256, 0);
ck_assert_ptr_nonnull(set);
ck_assert_uint_eq(jwks_item_count(set), 1);
ck_assert_ptr_eq(jwks_generate(set, JWK_KEY_TYPE_RSA, "2048",
JWT_ALG_RS256, 0), set);
ck_assert_uint_eq(jwks_item_count(set), 2);
jwks_free(set);
}
END_TEST
static Suite *libjwt_suite(const char *title)
{
Suite *s;
TCase *tc_core;
int i = ARRAY_SIZE(jwt_test_ops);
s = suite_create(title);
tc_core = tcase_create("jwt_generate");
tcase_add_loop_test(tc_core, test_generate_core, 0, i);
tcase_add_loop_test(tc_core, test_generate_optional, 0, i);
tcase_add_loop_test(tc_core, test_generate_errors, 0, i);
tcase_add_loop_test(tc_core, test_generate_append, 0, i);
tcase_set_timeout(tc_core, 120);
suite_add_tcase(s, tc_core);
return s;
}
int main(void)
{
JWT_TEST_MAIN("LibJWT key generation (#311)");
}