Skip to content

Commit

Permalink
jwks_create_*: Added new functions
Browse files Browse the repository at this point in the history
Load str with len
Load from file
Load from FILE*

Included test coverage

Signed-off-by: Ben Collins <[email protected]>
  • Loading branch information
benmcollins committed Jan 6, 2025
1 parent f9e4e4b commit 438d50b
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 41 deletions.
50 changes: 48 additions & 2 deletions include/jwt.h
Original file line number Diff line number Diff line change
Expand Up @@ -1034,7 +1034,7 @@ jwt_alg_t jwt_str_alg(const char *alg);
*/

/**
* Create a new JWKS object for later use in validating JWTs.
* @brief Create a new JWKS object from a null terminated string
*
* This function expects a JSON string either as a single object
* for one JWK or as an array of objects under a key of "keys" (as
Expand All @@ -1047,13 +1047,59 @@ jwt_alg_t jwt_str_alg(const char *alg);
*
* @param jwk_json_str JSON string representation of a single key
* or array of "keys". If NULL is passed, an empty jwk_set_t is
* created.
* created. Must be null terminated.
* @return A valid jwt_set_t on success. On failure, either NULL
* or a jwt_set_t with error set. NULL generally means ENOMEM.
*/
JWT_EXPORT
jwk_set_t *jwks_create(const char *jwk_json_str);

/**
* @brief Create a new JWKS object from a string of known lenght
*
* Useful if the string is not null terminated. Otherwise, it works the same
* as jwks_create().
*
* @param jwk_json_str JSON string representation of a single key
* or array of "keys".
* @param len The length of jwk_json_str that represents the key(s) being
* read.
* @return A valid jwt_set_t on success. On failure, either NULL
* or a jwt_set_t with error set. NULL generally means ENOMEM.
*/
JWT_EXPORT
jwk_set_t *jwks_create_strlen(const char *jwk_json_str, const size_t len);

/**
* @brief Create a new JWKS object from a file
*
* The JSON will be read from a file on the system. Must be readable by the
* running process. The end result of this function is the same as jwks_create.
*
* @param file_name A file containing a JSON representation of a single key
* or array of "keys".
* @return A valid jwt_set_t on success. On failure, either NULL
* or a jwt_set_t with error set. NULL generally means ENOMEM.
*/
JWT_EXPORT
jwk_set_t *jwks_create_fromfile(const char *file_name);

/**
* @brief Create a new JWKS object from a FILE pointer
*
* The JSON will be read from a FILE pointer. The end result of this function
* is the same as jwks_create. The FILE pointer must be set to the starting
* position of the JWK data. This function will read until it reaches EOF or
* invalid JSON data.
*
* @param input A FILE pointer where the JSON representation of a single key
* or array of "keys" can be fread() from.
* @return A valid jwt_set_t on success. On failure, either NULL
* or a jwt_set_t with error set. NULL generally means ENOMEM.
*/
JWT_EXPORT
jwk_set_t *jwks_create_fromfp(FILE *input);

/**
* Add a jwk_item_t to an existing jwk_set_t
*
Expand Down
132 changes: 99 additions & 33 deletions libjwt/jwks.c
Original file line number Diff line number Diff line change
Expand Up @@ -290,55 +290,121 @@ void jwks_free(jwk_set_t *jwk_set)
jwt_freemem(jwk_set);
}

jwk_set_t *jwks_create(const char *jwk_json_str)
static jwk_set_t *jwks_new(void)
{
json_auto_t *j_all = NULL;
json_t *j_array = NULL, *j_item = NULL;
json_error_t error;
jwk_set_t *jwk_set;
jwk_item_t *jwk_item;
size_t i;

errno = 0;

jwk_set = jwt_malloc(sizeof *jwk_set);
if (jwk_set == NULL) {
/* Yes, malloc(3) will set this, but just in case. */
// LCOV_EXCL_START
errno = ENOMEM;
if (jwk_set == NULL)
return NULL;
// LCOV_EXCL_STOP
}

memset(jwk_set, 0, sizeof(*jwk_set));
INIT_LIST_HEAD(&jwk_set->head);

/* Just an empty set */
if (jwk_json_str == NULL) {
return jwk_set;
}
return jwk_set;
}

static jwk_set_t *jwks_process(jwk_set_t *jwk_set, json_t *j_all, json_error_t *error)
{
json_t *j_array = NULL, *j_item = NULL;
jwk_item_t *jwk_item;
size_t i;

/* Parse the JSON string. */
j_all = json_loads(jwk_json_str, JSON_DECODE_ANY, &error);
if (j_all == NULL) {
jwks_write_error(jwk_set, "%s: %s", error.source, error.text);
jwks_write_error(jwk_set, "%s: %s", error->source, error->text);
return jwk_set;
}

/* Check for "keys" as in a JWKS */
j_array = json_object_get(j_all, "keys");
/* Check for "keys" as in a JWKS */
j_array = json_object_get(j_all, "keys");

if (j_array == NULL) {
/* Assume a single JSON Object for one JWK */
jwk_item = jwk_process_one(jwk_set, j_all);
jwks_item_add(jwk_set, jwk_item);
} else {
/* We have a list, so parse them all. */
json_array_foreach(j_array, i, j_item) {
jwk_item = jwk_process_one(jwk_set, j_item);
jwks_item_add(jwk_set, jwk_item);
}
}

return jwk_set;
}
#define __FLAG_EMPTY (void *)0xfffff00d
jwk_set_t *jwks_create_strlen(const char *jwk_json_str, const size_t len)
{
json_auto_t *j_all = NULL;
json_error_t error;
jwk_set_t *jwk_set;

if (jwk_json_str == NULL)
return NULL;

jwk_set = jwks_new();
if (jwk_set == NULL)
return NULL;

/* Just an empty set. */
if (jwk_json_str == __FLAG_EMPTY)
return jwk_set;

/* Parse the JSON string. */
j_all = json_loadb(jwk_json_str, len, JSON_DECODE_ANY, &error);

return jwks_process(jwk_set, j_all, &error);
}

jwk_set_t *jwks_create(const char *jwk_json_str)
{
const char *real_str = jwk_json_str;
size_t len;

if (j_array == NULL) {
/* Assume a single JSON Object for one JWK */
jwk_item = jwk_process_one(jwk_set, j_all);
jwks_item_add(jwk_set, jwk_item);
if (real_str == NULL) {
real_str = __FLAG_EMPTY;
len = 0;
} else {
/* We have a list, so parse them all. */
json_array_foreach(j_array, i, j_item) {
jwk_item = jwk_process_one(jwk_set, j_item);
jwks_item_add(jwk_set, jwk_item);
}
len = strlen(real_str);
}

return jwk_set;
return jwks_create_strlen(real_str, len);
}

jwk_set_t *jwks_create_fromfile(const char *file_name)
{
json_auto_t *j_all = NULL;
json_error_t error;
jwk_set_t *jwk_set;

if (file_name == NULL)
return NULL;

jwk_set = jwks_new();
if (jwk_set == NULL)
return NULL;

/* Parse the JSON string. */
j_all = json_load_file(file_name, JSON_DECODE_ANY, &error);

return jwks_process(jwk_set, j_all, &error);
}

jwk_set_t *jwks_create_fromfp(FILE *input)
{
json_auto_t *j_all = NULL;
json_error_t error;
jwk_set_t *jwk_set;

if (input == NULL)
return NULL;

jwk_set = jwks_new();
if (jwk_set == NULL)
return NULL;

/* Parse the JSON string. */
j_all = json_loadf(input, JSON_DECODE_ANY, &error);

return jwks_process(jwk_set, j_all, &error);
}
6 changes: 2 additions & 4 deletions tests/jwt_jwks.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,7 @@ START_TEST(test_jwks_keyring_load)

SET_OPS_JWK();

read_key("jwks_keyring.json");

ck_assert(!jwks_error(g_jwk_set));
read_json("jwks_keyring.json");

for (i = 0; (item = jwks_item_get(g_jwk_set, i)); i++)
ck_assert(!item->error);
Expand All @@ -159,7 +157,7 @@ START_TEST(test_jwks_key_op_all_types)

SET_OPS_JWK();

read_key("jwks_test-1.json");
read_jsonfp("jwks_test-1.json");

item = jwks_item_get(g_jwk_set, 0);
ck_assert_ptr_nonnull(item);
Expand Down
46 changes: 44 additions & 2 deletions tests/jwt_tests.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ static jwt_test_op_t jwt_test_ops[] = {
errno = 0; \
jwk_set_t *jwks = jwks_create(NULL); \
ck_assert_ptr_nonnull(jwks); \
ck_assert_int_eq(errno, 0); \
ck_assert(!jwks_error(jwks)); \
return; \
} \
})
Expand All @@ -107,10 +107,51 @@ static struct {
size_t key_len;
} test_data;

__attribute__((unused))
static void read_json(const char *key_file)
{
char *key_path;
int ret;

ret = asprintf(&key_path, KEYDIR "/%s", key_file);
ck_assert_int_gt(ret, 0);

g_jwk_set = jwks_create_fromfile(key_path);
free(key_path);
ck_assert_ptr_nonnull(g_jwk_set);
ck_assert(!jwks_error(g_jwk_set));

g_item = jwks_item_get(g_jwk_set, 0);
ck_assert_ptr_nonnull(g_item);
}

__attribute__((unused))
static void read_jsonfp(const char *key_file)
{
FILE *fp = NULL;
char *key_path;
int ret;

ret = asprintf(&key_path, KEYDIR "/%s", key_file);
ck_assert_int_gt(ret, 0);

fp = fopen(key_path, "r");
ck_assert_ptr_nonnull(fp);
free(key_path);

g_jwk_set = jwks_create_fromfp(fp);
fclose(fp);
ck_assert_ptr_nonnull(g_jwk_set);
ck_assert(!jwks_error(g_jwk_set));

g_item = jwks_item_get(g_jwk_set, 0);
ck_assert_ptr_nonnull(g_item);
}

__attribute__((unused))
static void read_key(const char *key_file)
{
FILE *fp = fopen(key_file, "r");
FILE *fp = NULL;
char *key_path;
int ret = 0;

Expand Down Expand Up @@ -147,6 +188,7 @@ static void read_key(const char *key_file)

g_jwk_set = jwks_create(test_data.key);
ck_assert_ptr_nonnull(g_jwk_set);
ck_assert(!jwks_error(g_jwk_set));

g_item = jwks_item_get(g_jwk_set, 0);
ck_assert_ptr_nonnull(g_item);
Expand Down

0 comments on commit 438d50b

Please sign in to comment.