From ee4aa3db48091344bf4fd4a7e0848eec5c55a35c Mon Sep 17 00:00:00 2001 From: Ben Collins Date: Tue, 7 Jan 2025 03:21:36 +0000 Subject: [PATCH] jwk_item_t: Make this opaque and add accessor functions This needs protecting for no other reason than security. We need to avoid users being able to manipulate things. Signed-off-by: Ben Collins --- examples/main-auth.c | 2 +- examples/main-gen.c | 2 +- include/jwt.h | 271 ++++++++++++++++++++++++-------------- libjwt/jwks.c | 88 ++++++++++--- libjwt/jwt-private.h | 34 +++++ tests/jwt_jwks.c | 43 +++--- tests/jwt_jwks_ec.c | 32 ++--- tests/jwt_jwks_errors.c | 54 ++------ tests/jwt_jwks_rsa.c | 48 +++---- tests/jwt_tests.h | 14 +- tests/keys/jwks/jwk2key.c | 22 ++-- 11 files changed, 371 insertions(+), 239 deletions(-) diff --git a/examples/main-auth.c b/examples/main-auth.c index 09cb6802..cab578a6 100644 --- a/examples/main-auth.c +++ b/examples/main-auth.c @@ -106,7 +106,7 @@ int main(int argc, char *argv[]) key_data[key_len] = '\0'; /* Setup JWK Set */ - jwk_set = jwks_create(key_data); + jwk_set = jwks_create(NULL, key_data); if (jwk_set == NULL || jwks_error(jwk_set)) { fprintf(stderr, "ERR: Could not read JWK: %s\n", jwks_error_msg(jwk_set)); diff --git a/examples/main-gen.c b/examples/main-gen.c index c4f7392f..df09d3e9 100644 --- a/examples/main-gen.c +++ b/examples/main-gen.c @@ -115,7 +115,7 @@ int main(int argc, char *argv[]) fprintf(stderr, "priv key loaded %s (%zu)!\n", opt_key_name, key_len); /* Setup JWK Set */ - jwk_set = jwks_create(key); + jwk_set = jwks_create(NULL, key); if (jwk_set == NULL || jwks_error(jwk_set)) { fprintf(stderr, "ERR: Could not read JWK: %s\n", jwks_error_msg(jwk_set)); diff --git a/include/jwt.h b/include/jwt.h index 6a63d50b..c2b3f584 100644 --- a/include/jwt.h +++ b/include/jwt.h @@ -158,43 +158,12 @@ typedef enum { } jwk_key_op_t; /** @ingroup jwks_core_grp - * @brief Structural representation of a JWK + * @brief Object representation of a JWK * - * This data structure is produced by importing a JWK or JWKS into a - * @ref jwk_set_t object. Generally, you would not change any values here - * and only use this to probe the internal parser and possibly to - * decide whether a key applies to certain jwt_t for verification - * or signing. - * - * @remark If the @ref jwk_item_t.pem field is not NULL, then it contains - * a nil terminated string of the key. The underlying crypto algorith may - * or may not support this. It's provided as a convenience. - * - * @raisewarning Decide if we need to make this an opaque object. Also, about - * that JSON... + * This object is produced by importing a JWK or JWKS into a @ref jwk_set_t + * object. It is passed functions that either producr or consume JWT. */ -typedef struct { - char *pem; /**< If not NULL, contains PEM string of this key */ - jwt_crypto_provider_t provider; /**< Crypto provider that owns this key */ - union { - void *provider_data; /**< Internal data used by the provider */ - struct { - void *key; /**< Used for HMAC key material */ - size_t len; /**< Length of HMAC key material */ - } oct; - }; - int is_private_key; /**< Whether this is a public or private key */ - char curve[256]; /**< Curve name of an ``"EC"`` or ``"OKP"`` key */ - size_t bits; /**< The number of bits in the key (may be 0) */ - int error; /**< There was an error parsing this key (unusable) */ - char error_msg[256]; /**< Descriptive message for @ref jwk_item_t.error */ - jwk_key_type_t kty; /**< @rfc{7517,4.1} The key type of this key */ - jwk_pub_key_use_t use; /**< @rfc{7517,4.2} How this key can be used */ - jwk_key_op_t key_ops; /**< @rfc{7517,4.3} Key operations supported */ - jwt_alg_t alg; /**< @rfc{7517,4.4} JWA Algorithm supported */ - char *kid; /**< @rfc{7517,4.5} Key ID */ - const char *json; /**< The entire JSON string for this key */ -} jwk_item_t; +typedef struct jwk_item jwk_item_t; /** @ingroup jwt_valid_grp * @brief Validation exception types for @ref jwt_t objects @@ -335,9 +304,9 @@ jwt_t *jwt_dup(jwt_t *jwt); * @brief Structure used to manage configuration state */ typedef struct { - jwk_item_t *jw_key; /**< A JWK to use for key */ - jwt_alg_t alg; /**< For algorithm matching */ - void *ctx; /**< User controlled context */ + const jwk_item_t *jw_key; /**< A JWK to use for key */ + jwt_alg_t alg; /**< For algorithm matching */ + void *ctx; /**< User controlled context */ } jwt_config_t; /** @@ -414,31 +383,6 @@ jwt_t *jwt_create(jwt_config_t *config); * * @raisewarning Complete the details of this information * - * Lots of cases to deal with. - * - * -# If the caller passes a key/len pair: - * - Then config.alg MUST NOT be none, and - * - The config.alg MUST match jwt.alg - * -# If the user passes a jw_key: - * - config.alg is not used (jw_key.alg is used if available) - * - It's valid for jw_key.alg to be none (missing) (RFC-7517:4.4) - * - If jw_key.alg is not none, it MUST match the JWT - * - If jw_key.alg is none, then jwt.alg is compared against jwk.kty for - * compatibility. e.g: - * - jwt.alg RS256, RS384, and RS512 are suitable with jwk.kty RSA - * - jwt.alg ES384 is not suitable with jwk.kty OKP - * -# The user SHOULD NOT pass both types, but we allow it. However, - * checks for both keys MUST pass. - * -# If the user did not pass a key of any kind: - * - Then jwt.alg MUST be none, and - * - The sig_len MUST be zero - * - If the caller wishes to "peek" at a JWA token, (without verifying) - * then they MUST use a callback function to inspect it. Information - * from the callback can be passed between the cb and original caller - * with the @ref jwt_config_t.ctx pointer. - * -# If jwt.alg is none then sig_len MUST be zero, regardless of (4) - * - * * @{ */ @@ -1028,23 +972,31 @@ jwt_alg_t jwt_str_alg(const char *alg); /** * @defgroup jwks_core_grp JSON Web Key and Sets + * * Functions to handle JSON that represents JWK and JWKS for use * in validating JWT objects. + * * @{ */ /** - * @brief Create a new JWKS object from a null terminated string + * @brief Create and add to a keyring of JSON Web Keys * - * 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 - * defined in JWKS specifications). + * This function, and the utility versions, allow you to create a keyring + * used to verify and/or create JSON Web Tokens. It accepts either single + * JWK or a JWKS (JSON Web Token Set). + * + * If you want to create a new set, then pass NULL as the first argument. If + * you want to add to an existing keyring, then pass that as the first + * argument. * * If non-NULL is returned, you should then check to make sure there * is no error with jwks_error(). There may be errors on individual * JWK items in the set. You can check if there are any with * jwks_error_any(). * + * @param jwk_set Either NULL to create a new set, or an existing jwt_set + * to add new keys to it. * @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. Must be null terminated. @@ -1052,7 +1004,7 @@ jwt_alg_t jwt_str_alg(const char *alg); * or a jwt_set_t with error set. NULL generally means ENOMEM. */ JWT_EXPORT -jwk_set_t *jwks_create(const char *jwk_json_str); +jwk_set_t *jwks_create(jwk_set_t *jwk_set, const char *jwk_json_str); /** * @brief Create a new JWKS object from a string of known lenght @@ -1060,6 +1012,8 @@ jwk_set_t *jwks_create(const char *jwk_json_str); * Useful if the string is not null terminated. Otherwise, it works the same * as jwks_create(). * + * @param jwk_set Either NULL to create a new set, or an existing jwt_set + * to add new keys to it. * @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 @@ -1068,7 +1022,8 @@ jwk_set_t *jwks_create(const char *jwk_json_str); * 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); +jwk_set_t *jwks_create_strb(jwk_set_t *jwk_set, const char *jwk_json_str, + const size_t len); /** * @brief Create a new JWKS object from a file @@ -1076,13 +1031,15 @@ jwk_set_t *jwks_create_strlen(const char *jwk_json_str, const size_t len); * 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 jwk_set Either NULL to create a new set, or an existing jwt_set + * to add new keys to it. * @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); +jwk_set_t *jwks_create_fromfile(jwk_set_t *jwk_set, const char *file_name); /** * @brief Create a new JWKS object from a FILE pointer @@ -1092,23 +1049,15 @@ jwk_set_t *jwks_create_fromfile(const char *file_name); * position of the JWK data. This function will read until it reaches EOF or * invalid JSON data. * + * @param jwk_set Either NULL to create a new set, or an existing jwt_set + * to add new keys to it. * @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 - * - * @param jwk_set An existing jwk_set_t - * @param item A JWK item to add to the set - * @return 0 on success, valid errno otherwise. - */ -JWT_EXPORT -int jwks_item_add(jwk_set_t *jwk_set, jwk_item_t *item); +jwk_set_t *jwks_create_fromfp(jwk_set_t *jwk_set, FILE *input); /** * Check if there is an error within the jwk_set @@ -1139,10 +1088,46 @@ int jwks_error_any(jwk_set_t *jwk_set); * @return NULL on error, valid string otherwise */ JWT_EXPORT -const char *jwks_error_msg(jwk_set_t *jwk_set); +const char *jwks_error_msg(const jwk_set_t *jwk_set); + +/** + * Free all memory associated with a jwt_set_t, including any jwk_item_t in + * the set. + * + * @param jwk_set An existing jwk_set_t + */ +JWT_EXPORT +void jwks_free(jwk_set_t *jwk_set); + +#if defined(__GNUC__) || defined(__clang__) +/** + * @raisewarning Document jwks_freep + */ +static inline void jwks_freep(jwk_set_t **jwks) { + if (jwks) { + jwks_free(*jwks); + *jwks = NULL; + } +} +#define jwk_set_auto_t jwk_set_t __attribute__((cleanup(jwks_freep))) +#endif + +/** + * @} + * @noop jwks_core_grp + */ + +/** + * @defgroup jwks_item_grp JSON Web Key and Sets + * + * Functions to handle JSON that represents JWK and JWKS for use + * in validating JWT objects. + * + * @{ + */ /** - * Return the index'th jwk_item in the jwk_set + * @brief Return the index'th jwk_item in the jwk_set * * Allows you to obtain the raw jwk_item. NOTE, this is not a copy * of the item, so any changes to it will be reflected to it in the @@ -1159,29 +1144,117 @@ const char *jwks_error_msg(jwk_set_t *jwk_set); * from the jwk_set. */ JWT_EXPORT -jwk_item_t *jwks_item_get(jwk_set_t *jwk_set, size_t index); +const jwk_item_t *jwks_item_get(const jwk_set_t *jwk_set, size_t index); /** - * Free all memory associated with a jwt_set_t, including any - * jwk_item_t in the set + * @brief Whther this key is private (or public) * - * @param jwk_set An existing jwk_set_t + * @param item A JWK Item + * @return 1 for true, 0 for false */ JWT_EXPORT -void jwks_free(jwk_set_t *jwk_set); +int jwks_item_is_private(const jwk_item_t *item); -#if defined(__GNUC__) || defined(__clang__) /** - * @raisewarning Document jwks_freep + * @brief Check the error condition for this JWK + * + * @param item A JWK Item + * @return 1 for true, 0 for false */ -static inline void jwks_freep(jwk_set_t **jwks) { - if (jwks) { - jwks_free(*jwks); - *jwks = NULL; - } -} -#define jwk_set_auto_t jwk_set_t __attribute__((cleanup(jwks_freep))) -#endif +JWT_EXPORT +int jwks_item_error(const jwk_item_t *item); + +/** + * @brief Check the error message for a JWK Item + * + * @param item A JWK Item + * @return A string message. Empty string if not error. + */ +JWT_EXPORT +const char *jwks_item_error_msg(const jwk_item_t *item); + +/** + * @brief A curve name, if applicable, for this key + * + * Mainly applies to EC and OKP (EdDSA) type keys. + * + * @param item A JWK Item + * @return A string of the curve name if one exists. NULL otherwise. + */ +JWT_EXPORT +const char *jwks_item_curve(const jwk_item_t *item); + +/** + * @brief A kid (Key ID) for this JWK + * + * @param item A JWK Item + * @return A string of the kid if one exists. NULL otherwise. + */ +JWT_EXPORT +const char *jwks_item_kid(const jwk_item_t *item); + +/** + * @brief The algorithm for this JWK + * + * It is perfectly valid for this to be JWT_ALG_NONE. + * + * @param item A JWK Item + * @return A jwt_alg_t type of this key + */ +JWT_EXPORT +jwt_alg_t jwks_item_alg(const jwk_item_t *item); + +/** + * @brief The Key Type of this JWK + * + * @param item A JWK Item + * @return A jwk_key_type_t type for this key + */ +JWT_EXPORT +jwk_key_type_t jwks_item_kty(const jwk_item_t *item); + +/** + * @brief The ``"use"`` field for this JWK + * + * @param item A JWK Item + * @return A jwk_pub_key_use_t type for this key + */ +JWT_EXPORT +jwk_pub_key_use_t jwks_item_use(const jwk_item_t *item); + +/** + * @brief The ``"key_ops"`` field for this JWK + * + * @param item A JWK Item + * @return A jwk_key_op_t type for this key which represents all of the + * ``"key_ops"`` supported as a bit field. + */ +JWT_EXPORT +jwk_key_op_t jwks_item_key_ops(const jwk_item_t *item); + +/** + * @brief The PEM generated for the JWK + * + * THis is an optional field that may or may not be supported depending on + * which crypto backend is in use. It is provided as a courtesy. + * + * @param item A JWK Item + * @return A string of the PEM file for this key or NULL if none exists + */ +JWT_EXPORT +const char *jwks_item_pem(const jwk_item_t *item); + +/** + * @brief The number of bits in this JWK + * + * This is relevant to the key type (kty). E.g. an RSA key would have atleast + * 2048 bits, and EC key would be 256, 384, or 521 bits, etc. + * + * @param item A JWK Item + * @return The number of bits for the key + */ +JWT_EXPORT +int jwks_item_key_bits(const jwk_item_t *item); /** * Free all memory associated with the nth jwk_item_t in a jwk_set @@ -1205,7 +1278,7 @@ int jwks_item_free_all(jwk_set_t *jwk_set); /** * @} - * @noop jwks_core_grp + * @noop jwks_item_grp */ /** @ingroup jwt_grp diff --git a/libjwt/jwks.c b/libjwt/jwks.c index 44262a55..f5d5489b 100644 --- a/libjwt/jwks.c +++ b/libjwt/jwks.c @@ -185,7 +185,7 @@ static jwk_item_t *jwk_process_one(jwk_set_t *jwk_set, json_t *jwk) return item; } -jwk_item_t *jwks_item_get(jwk_set_t *jwk_set, size_t index) +const jwk_item_t *jwks_item_get(const jwk_set_t *jwk_set, size_t index) { struct jwk_list_item *item = NULL; int i = 0; @@ -199,12 +199,67 @@ jwk_item_t *jwks_item_get(jwk_set_t *jwk_set, size_t index) return NULL; } +int jwks_item_is_private(const jwk_item_t *item) +{ + return item->is_private_key ? 1 : 0; +} + +int jwks_item_error(const jwk_item_t *item) +{ + return item->error; +} + +const char *jwks_item_error_msg(const jwk_item_t *item) +{ + return item->error_msg; +} + +const char *jwks_item_curve(const jwk_item_t *item) +{ + return item->curve[0] ? item->curve : NULL; +} + +const char *jwks_item_kid(const jwk_item_t *item) +{ + return item->kid[0] ? item->kid : NULL; +} + +jwt_alg_t jwks_item_alg(const jwk_item_t *item) +{ + return item->alg; +} + +jwk_key_type_t jwks_item_kty(const jwk_item_t *item) +{ + return item->kty; +} + +jwk_pub_key_use_t jwks_item_use(const jwk_item_t *item) +{ + return item->use; +} + +jwk_key_op_t jwks_item_key_ops(const jwk_item_t *item) +{ + return item->key_ops; +} + +const char *jwks_item_pem(const jwk_item_t *item) +{ + return item->pem; +} + +int jwks_item_key_bits(const jwk_item_t *item) +{ + return item->bits; +} + int jwks_error(jwk_set_t *jwk_set) { return jwk_set->error ? 1 : 0; } -const char *jwks_error_msg(jwk_set_t *jwk_set) +const char *jwks_error_msg(const jwk_set_t *jwk_set) { if (jwk_set == NULL) return "Unknown error"; @@ -212,7 +267,7 @@ const char *jwks_error_msg(jwk_set_t *jwk_set) return jwk_set->error_msg; } -int jwks_item_add(jwk_set_t *jwk_set, jwk_item_t *item) +static int jwks_item_add(jwk_set_t *jwk_set, jwk_item_t *item) { struct jwk_list_item *new; @@ -225,12 +280,12 @@ int jwks_item_add(jwk_set_t *jwk_set, jwk_item_t *item) new->item = item; - list_add(&new->node, &jwk_set->head); + list_add_tail(&new->node, &jwk_set->head); return 0; } -int jwks_item_free(jwk_set_t *jwk_set, size_t index) +int jwks_item_free(jwk_set_t *jwk_set, const size_t index) { struct jwk_list_item *list_item = NULL, *todel = NULL; jwk_item_t *item; @@ -333,16 +388,17 @@ static jwk_set_t *jwks_process(jwk_set_t *jwk_set, json_t *j_all, json_error_t * return jwk_set; } #define __FLAG_EMPTY (void *)0xfffff00d -jwk_set_t *jwks_create_strlen(const char *jwk_json_str, const size_t len) +jwk_set_t *jwks_create_strb(jwk_set_t *jwk_set, 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) + jwk_set = jwks_new(); if (jwk_set == NULL) return NULL; @@ -356,7 +412,7 @@ jwk_set_t *jwks_create_strlen(const char *jwk_json_str, const size_t len) return jwks_process(jwk_set, j_all, &error); } -jwk_set_t *jwks_create(const char *jwk_json_str) +jwk_set_t *jwks_create(jwk_set_t *jwk_set, const char *jwk_json_str) { const char *real_str = jwk_json_str; size_t len; @@ -368,19 +424,19 @@ jwk_set_t *jwks_create(const char *jwk_json_str) len = strlen(real_str); } - return jwks_create_strlen(real_str, len); + return jwks_create_strb(jwk_set, real_str, len); } -jwk_set_t *jwks_create_fromfile(const char *file_name) +jwk_set_t *jwks_create_fromfile(jwk_set_t *jwk_set, 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) + jwk_set = jwks_new(); if (jwk_set == NULL) return NULL; @@ -390,16 +446,16 @@ jwk_set_t *jwks_create_fromfile(const char *file_name) return jwks_process(jwk_set, j_all, &error); } -jwk_set_t *jwks_create_fromfp(FILE *input) +jwk_set_t *jwks_create_fromfp(jwk_set_t *jwk_set, 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) + jwk_set = jwks_new(); if (jwk_set == NULL) return NULL; diff --git a/libjwt/jwt-private.h b/libjwt/jwt-private.h index 12ce5b3b..92fc722f 100644 --- a/libjwt/jwt-private.h +++ b/libjwt/jwt-private.h @@ -71,6 +71,40 @@ struct jwk_set { char error_msg[256]; }; +/** + * This data structure is produced by importing a JWK or JWKS into a + * @ref jwk_set_t object. Generally, you would not change any values here + * and only use this to probe the internal parser and possibly to + * decide whether a key applies to certain jwt_t for verification + * or signing. + * + * If the jwk_item_t.pem field is not NULL, then it contains a nil terminated + * string of the key. The underlying crypto algorith may or may not support + * this. It's provided as a convenience. + */ +struct jwk_item { + char *pem; /**< If not NULL, contains PEM string of this key */ + jwt_crypto_provider_t provider; /**< Crypto provider that owns this key */ + union { + void *provider_data; /**< Internal data used by the provider */ + struct { + void *key; /**< Used for HMAC key material */ + size_t len; /**< Length of HMAC key material */ + } oct; + }; + int is_private_key; /**< Whether this is a public or private key */ + char curve[256]; /**< Curve name of an ``"EC"`` or ``"OKP"`` key */ + size_t bits; /**< The number of bits in the key (may be 0) */ + int error; /**< There was an error parsing this key (unusable) */ + char error_msg[256]; /**< Descriptive message for @ref jwk_item_t.error */ + jwk_key_type_t kty; /**< @rfc{7517,4.1} The key type of this key */ + jwk_pub_key_use_t use; /**< @rfc{7517,4.2} How this key can be used */ + jwk_key_op_t key_ops; /**< @rfc{7517,4.3} Key operations supported */ + jwt_alg_t alg; /**< @rfc{7517,4.4} JWA Algorithm supported */ + char *kid; /**< @rfc{7517,4.5} Key ID */ + const char *json; /**< The entire JSON string for this key */ +}; + /* Crypto operations */ struct jwt_crypto_ops { const char *name; diff --git a/tests/jwt_jwks.c b/tests/jwt_jwks.c index ae92ddcd..8a6e0bf1 100644 --- a/tests/jwt_jwks.c +++ b/tests/jwt_jwks.c @@ -21,14 +21,14 @@ static void __jwks_check(const char *json, const char *pem) { JWT_CONFIG_DECLARE(config); jwk_set_auto_t *jwk_set = NULL; - jwk_item_t *item = NULL; + const jwk_item_t *item = NULL; jwt_auto_t *jwt = NULL; char *out = NULL; int ret; /* Load up the JWKS */ read_key(json); - jwk_set = jwks_create(test_data.key); + jwk_set = jwks_create(NULL, test_data.key); free_key(); ck_assert_ptr_nonnull(jwk_set); @@ -38,16 +38,16 @@ static void __jwks_check(const char *json, const char *pem) item = jwks_item_get(jwk_set, 0); ck_assert_ptr_nonnull(item); - /* If the key is not any provider, we need ops support. */ - if (item->provider != JWT_CRYPTO_OPS_ANY && + /* If the key is not octet, we need ops support. */ + if (jwks_item_kty(item) != JWK_KEY_TYPE_OCT && !jwt_crypto_ops_supports_jwk()) { - ck_assert(item->error); + ck_assert(jwks_item_error(item)); return; } - if (item->kty != JWK_KEY_TYPE_OCT) { + if (jwks_item_kty(item) != JWK_KEY_TYPE_OCT) { read_key(pem); - ret = strcmp(item->pem, test_data.key); + ret = strcmp(jwks_item_pem(item), test_data.key); free_key(); ck_assert_int_eq(ret, 0); } @@ -60,14 +60,15 @@ static void __jwks_check(const char *json, const char *pem) item = jwks_item_get(jwk_set, 0); ck_assert_ptr_nonnull(item); - if (!item->is_private_key) + if (!jwks_item_is_private(item)) return; - if (item->alg == JWT_ALG_NONE && item->kty == JWK_KEY_TYPE_RSA) { + if (jwks_item_alg(item) == JWT_ALG_NONE && + jwks_item_kty(item) == JWK_KEY_TYPE_RSA) { /* "alg" is optional, and it's missing in a few keys */ config.alg = JWT_ALG_RS256; } else { - config.alg = item->alg; + config.alg = jwks_item_alg(item); } /* Use our JWK */ @@ -128,7 +129,7 @@ JWKS_KEY_TEST(oct_key_512); START_TEST(test_jwks_keyring_load) { - jwk_item_t *item; + const jwk_item_t *item; int i; SET_OPS_JWK(); @@ -136,7 +137,7 @@ START_TEST(test_jwks_keyring_load) read_json("jwks_keyring.json"); for (i = 0; (item = jwks_item_get(g_jwk_set, i)); i++) - ck_assert(!item->error); + ck_assert(!jwks_item_error(item)); ck_assert_int_eq(i, 22); @@ -153,7 +154,7 @@ START_TEST(test_jwks_key_op_all_types) JWK_KEY_OP_UNWRAP | JWK_KEY_OP_DERIVE_KEY | JWK_KEY_OP_DERIVE_BITS; - jwk_item_t *item; + const jwk_item_t *item; SET_OPS_JWK(); @@ -161,9 +162,9 @@ START_TEST(test_jwks_key_op_all_types) item = jwks_item_get(g_jwk_set, 0); ck_assert_ptr_nonnull(item); - ck_assert(!item->error); + ck_assert(!jwks_item_error(item)); - ck_assert_int_eq(item->key_ops, key_ops); + ck_assert_int_eq(jwks_item_key_ops(item), key_ops); free_key(); } @@ -171,7 +172,7 @@ END_TEST START_TEST(test_jwks_key_op_bad_type) { - jwk_item_t *item; + const jwk_item_t *item; const char *msg = "JWK has an invalid value in key_op"; const char *kid = "264265c2-4ef0-4751-adbd-9739550afe5b"; @@ -183,17 +184,17 @@ START_TEST(test_jwks_key_op_bad_type) ck_assert_ptr_nonnull(item); /* One item had a bad type (numeric). */ - ck_assert(item->error); - ck_assert_str_eq(item->error_msg, msg); + ck_assert(jwks_item_error(item)); + ck_assert_str_eq(jwks_item_error_msg(item), msg); /* Only these ops set. */ - ck_assert_int_eq(item->key_ops, + ck_assert_int_eq(jwks_item_key_ops(item), JWK_KEY_OP_VERIFY | JWK_KEY_OP_DERIVE_BITS); - ck_assert_int_eq(item->use, JWK_PUB_KEY_USE_ENC); + ck_assert_int_eq(jwks_item_use(item), JWK_PUB_KEY_USE_ENC); /* Check this key ID. */ - ck_assert_str_eq(item->kid, kid); + ck_assert_str_eq(jwks_item_kid(item), kid); free_key(); } diff --git a/tests/jwt_jwks_ec.c b/tests/jwt_jwks_ec.c index 27cdf36d..c32db6b9 100644 --- a/tests/jwt_jwks_ec.c +++ b/tests/jwt_jwks_ec.c @@ -12,21 +12,21 @@ START_TEST(test_jwks_ec_pub_missing) { const char *json = "{\"kty\":\"EC\"}"; jwk_set_t *jwk_set = NULL; - jwk_item_t *item; + const jwk_item_t *item; const char exp[] = "Missing or invalid type for one of crv, x, or y for pub key"; SET_OPS_JWK(); - jwk_set = jwks_create(json); + jwk_set = jwks_create(NULL, json); 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_int_ne(item->error, 0); + ck_assert_int_ne(jwks_item_error(item), 0); - ck_assert_str_eq(exp, item->error_msg); + ck_assert_str_eq(exp, jwks_item_error_msg(item)); jwks_free(jwk_set); } @@ -36,21 +36,21 @@ START_TEST(test_jwks_ec_pub_bad_type) { const char *json = "{\"kty\":\"EC\",\"crv\":\"prime6v1\",\"x\":\"sd+#(@#($(ada\",\"y\":1}"; jwk_set_t *jwk_set = NULL; - jwk_item_t *item; + const jwk_item_t *item; const char exp[] = "Missing or invalid type for one of crv, x, or y for pub key"; SET_OPS_JWK(); - jwk_set = jwks_create(json); + jwk_set = jwks_create(NULL, json); 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_int_ne(item->error, 0); + ck_assert_int_ne(jwks_item_error(item), 0); - ck_assert_str_eq(exp, item->error_msg); + ck_assert_str_eq(exp, jwks_item_error_msg(item)); jwks_free(jwk_set); } @@ -60,21 +60,21 @@ START_TEST(test_jwks_ec_pub_bad64) { const char *json = "{\"kty\":\"EC\",\"crv\":\"prime6v1\",\"x\":\"\",\"y\":\"asaad\"}"; jwk_set_t *jwk_set = NULL; - jwk_item_t *item; + const jwk_item_t *item; const char exp[] = "Error generating pub key from components"; SET_OPS_JWK(); - jwk_set = jwks_create(json); + jwk_set = jwks_create(NULL, json); 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_int_ne(item->error, 0); + ck_assert_int_ne(jwks_item_error(item), 0); - ck_assert_str_eq(exp, item->error_msg); + ck_assert_str_eq(exp, jwks_item_error_msg(item)); jwks_free(jwk_set); } @@ -84,21 +84,21 @@ START_TEST(test_jwks_ec_pub_bad_points) { const char *json = "{\"kty\":\"EC\",\"crv\":\"prime256v1\",\"x\":\"YmFkdmFsdWUK\",\"y\":\"YmFkdmFsdWUK\"}"; jwk_set_t *jwk_set = NULL; - jwk_item_t *item; + const jwk_item_t *item; const char exp[] = "Error generating pub key from components"; SET_OPS_JWK(); - jwk_set = jwks_create(json); + jwk_set = jwks_create(NULL, json); 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_int_ne(item->error, 0); + ck_assert_int_ne(jwks_item_error(item), 0); - ck_assert_str_eq(exp, item->error_msg); + ck_assert_str_eq(exp, jwks_item_error_msg(item)); jwks_free(jwk_set); } diff --git a/tests/jwt_jwks_errors.c b/tests/jwt_jwks_errors.c index 22a945fe..af288539 100644 --- a/tests/jwt_jwks_errors.c +++ b/tests/jwt_jwks_errors.c @@ -16,7 +16,7 @@ START_TEST(test_jwks_bad_json) SET_OPS_JWK(); - jwk_set = jwks_create(json); + jwk_set = jwks_create(NULL, json); ck_assert_ptr_nonnull(jwk_set); ck_assert(jwks_error(jwk_set)); @@ -33,22 +33,22 @@ START_TEST(test_jwks_unknown_kty) { const char *json = "{\"kty\":\"INVALID\"}"; jwk_set_t *jwk_set = NULL; - jwk_item_t *item; + const jwk_item_t *item; const char exp[] = "Unknown or unsupported kty type"; int ret; SET_OPS_JWK(); - jwk_set = jwks_create(json); + jwk_set = jwks_create(NULL, json); 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_int_ne(item->error, 0); + ck_assert_int_ne(jwks_item_error(item), 0); - ret = strncmp(exp, item->error_msg, strlen(exp)); + ret = strncmp(exp, jwks_item_error_msg(item), strlen(exp)); ck_assert_int_eq(ret, 0); jwks_free(jwk_set); @@ -59,22 +59,22 @@ START_TEST(test_jwks_missing_kty) { const char *json = "{\"NOT-kty\":\"INVALID\"}"; jwk_set_t *jwk_set = NULL; - jwk_item_t *item; + const jwk_item_t *item; const char exp[] = "Invalid JWK: missing kty value"; int ret; SET_OPS_JWK(); - jwk_set = jwks_create(json); + jwk_set = jwks_create(NULL, json); 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_int_ne(item->error, 0); + ck_assert_int_ne(jwks_item_error(item), 0); - ret = strncmp(exp, item->error_msg, strlen(exp)); + ret = strncmp(exp, jwks_item_error_msg(item), strlen(exp)); ck_assert_int_eq(ret, 0); jwks_free(jwk_set); @@ -84,11 +84,11 @@ END_TEST START_TEST(test_jwks_empty) { jwk_set_t *jwk_set = NULL; - jwk_item_t *item = NULL; + const jwk_item_t *item = NULL; SET_OPS_JWK(); - jwk_set = jwks_create(NULL); + jwk_set = jwks_create(NULL, NULL); ck_assert_ptr_nonnull(jwk_set); ck_assert(!jwks_error(jwk_set)); @@ -100,37 +100,6 @@ START_TEST(test_jwks_empty) } END_TEST -START_TEST(test_jwks_item_add) -{ - jwk_set_t *jwk_set = NULL; - jwk_item_t *item, *test; - int ret; - - SET_OPS_JWK(); - - jwk_set = jwks_create(NULL); - - ck_assert_ptr_nonnull(jwk_set); - ck_assert(!jwks_error(jwk_set)); - - ret = jwks_item_add(NULL, NULL); - ck_assert_int_ne(ret, 0); - - item = malloc(sizeof(*item)); - ck_assert_ptr_nonnull(item); - - memset(item, 0, sizeof(*item)); - - ret = jwks_item_add(jwk_set, item); - ck_assert_int_eq(ret, 0); - - test = jwks_item_get(jwk_set, 0); - ck_assert_ptr_eq(item, test); - - jwks_free(jwk_set); -} -END_TEST - static Suite *libjwt_suite(const char *title) { Suite *s; @@ -144,7 +113,6 @@ static Suite *libjwt_suite(const char *title) /* Core JWKS Error path tests */ tcase_add_loop_test(tc_core, test_jwks_bad_json, 0, i); tcase_add_loop_test(tc_core, test_jwks_empty, 0, i); - tcase_add_loop_test(tc_core, test_jwks_item_add, 0, i); tcase_add_loop_test(tc_core, test_jwks_unknown_kty, 0, i); tcase_add_loop_test(tc_core, test_jwks_missing_kty, 0, i); diff --git a/tests/jwt_jwks_rsa.c b/tests/jwt_jwks_rsa.c index 97c4dc5d..5523e867 100644 --- a/tests/jwt_jwks_rsa.c +++ b/tests/jwt_jwks_rsa.c @@ -12,21 +12,21 @@ START_TEST(test_jwks_rsa_pub_missing) { const char *json = "{\"kty\":\"RSA\"}"; jwk_set_t *jwk_set = NULL; - jwk_item_t *item; + const jwk_item_t *item; const char exp[] = "Missing required RSA component: n or e"; SET_OPS_JWK(); - jwk_set = jwks_create(json); + jwk_set = jwks_create(NULL, json); 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_int_ne(item->error, 0); + ck_assert_int_ne(jwks_item_error(item), 0); - ck_assert_str_eq(exp, item->error_msg); + ck_assert_str_eq(exp, jwks_item_error_msg(item)); jwks_free(jwk_set); } @@ -36,21 +36,21 @@ START_TEST(test_jwks_rsa_pub_bad_type) { const char *json = "{\"kty\":\"RSA\",\"n\":\"YmFkdmFsdWUK\",\"e\":1}"; jwk_set_t *jwk_set = NULL; - jwk_item_t *item; + const jwk_item_t *item; const char exp[] = "Error decoding pub components"; SET_OPS_JWK(); - jwk_set = jwks_create(json); + jwk_set = jwks_create(NULL, json); 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_int_ne(item->error, 0); + ck_assert_int_ne(jwks_item_error(item), 0); - ck_assert_str_eq(exp, item->error_msg); + ck_assert_str_eq(exp, jwks_item_error_msg(item)); jwks_free(jwk_set); } @@ -60,21 +60,21 @@ START_TEST(test_jwks_rsa_pub_bad64) { const char *json = "{\"kty\":\"RSA\",\"n\":\"\",\"e\":\"asaadaaaaaa\"}"; jwk_set_t *jwk_set = NULL; - jwk_item_t *item; + const jwk_item_t *item; const char exp[] = "Error decoding pub components"; SET_OPS_JWK(); - jwk_set = jwks_create(json); + jwk_set = jwks_create(NULL, json); 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_int_ne(item->error, 0); + ck_assert_int_ne(jwks_item_error(item), 0); - ck_assert_str_eq(exp, item->error_msg); + ck_assert_str_eq(exp, jwks_item_error_msg(item)); jwks_free(jwk_set); } @@ -86,19 +86,19 @@ START_TEST(test_jwks_rsa_pub_binary64) "\"2fyxRFHaYP2a4pbdTK/s9x4YWV7qAWwJMXMkbRmy51w\"," "\"e\":\"2fyxRFHaYP2a4pbdTK/s9x4YWV7qAWwJMXMkbRmy51w\"}"; jwk_set_t *jwk_set = NULL; - jwk_item_t *item; + const jwk_item_t *item; SET_OPS_JWK(); - jwk_set = jwks_create(json); + jwk_set = jwks_create(NULL, json); 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_ptr_nonnull(item->pem); - ck_assert_int_eq(item->error, 0); + ck_assert_ptr_nonnull(jwks_item_pem(item)); + ck_assert_int_eq(jwks_item_error(item), 0); jwks_free(jwk_set); } @@ -109,21 +109,21 @@ START_TEST(test_jwks_rsa_priv_missing) const char *json = "{\"kty\":\"RSA\",\"n\":\"YmFkdmFsdWUK\"," "\"e\":\"YmFkdmFsdWUK\",\"d\":\"YmFkdmFsdWUK\"}"; jwk_set_t *jwk_set = NULL; - jwk_item_t *item; + const jwk_item_t *item; const char exp[] = "Some priv key components exist, but some are missing"; SET_OPS_JWK(); - jwk_set = jwks_create(json); + jwk_set = jwks_create(NULL, json); 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_int_ne(item->error, 0); + ck_assert_int_ne(jwks_item_error(item), 0); - ck_assert_str_eq(exp, item->error_msg); + ck_assert_str_eq(exp, jwks_item_error_msg(item)); jwks_free(jwk_set); } @@ -136,21 +136,21 @@ START_TEST(test_jwks_rsa_priv_bad64) "\"2fyxRFHaYP2a4pbdTK/s9x4YWV7qAWwJMXMkbRmy51w\"," "\"p\":\"\",\"q\":\"=\",\"dp\":\"\",\"dq\":\"\",\"qi\":\"\"}"; jwk_set_t *jwk_set = NULL; - jwk_item_t *item; + const jwk_item_t *item; const char exp[] = "Error decoding priv components"; SET_OPS_JWK(); - jwk_set = jwks_create(json); + jwk_set = jwks_create(NULL, json); 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_int_ne(item->error, 0); + ck_assert_int_ne(jwks_item_error(item), 0); - ck_assert_str_eq(exp, item->error_msg); + ck_assert_str_eq(exp, jwks_item_error_msg(item)); jwks_free(jwk_set); } diff --git a/tests/jwt_tests.h b/tests/jwt_tests.h index 186bf18d..dd0ccf41 100644 --- a/tests/jwt_tests.h +++ b/tests/jwt_tests.h @@ -89,7 +89,7 @@ static jwt_test_op_t jwt_test_ops[] = { ck_assert_int_eq(r, 0); \ if (!jwt_crypto_ops_supports_jwk()) { \ errno = 0; \ - jwk_set_t *jwks = jwks_create(NULL); \ + jwk_set_t *jwks = jwks_create(NULL, NULL); \ ck_assert_ptr_nonnull(jwks); \ ck_assert(!jwks_error(jwks)); \ return; \ @@ -97,7 +97,7 @@ static jwt_test_op_t jwt_test_ops[] = { }) __attribute__((unused)) static jwk_set_t *g_jwk_set; -__attribute__((unused)) static jwk_item_t *g_item; +__attribute__((unused)) static const jwk_item_t *g_item; __attribute__((unused)) static JWT_CONFIG_DECLARE(t_config); @@ -116,7 +116,7 @@ static void read_json(const char *key_file) ret = asprintf(&key_path, KEYDIR "/%s", key_file); ck_assert_int_gt(ret, 0); - g_jwk_set = jwks_create_fromfile(key_path); + g_jwk_set = jwks_create_fromfile(NULL, key_path); free(key_path); ck_assert_ptr_nonnull(g_jwk_set); ck_assert(!jwks_error(g_jwk_set)); @@ -139,7 +139,7 @@ static void read_jsonfp(const char *key_file) ck_assert_ptr_nonnull(fp); free(key_path); - g_jwk_set = jwks_create_fromfp(fp); + g_jwk_set = jwks_create_fromfp(NULL, fp); fclose(fp); ck_assert_ptr_nonnull(g_jwk_set); ck_assert(!jwks_error(g_jwk_set)); @@ -186,7 +186,7 @@ static void read_key(const char *key_file) if (strstr(key_file, ".pem") != NULL) return; - g_jwk_set = jwks_create(test_data.key); + g_jwk_set = jwks_create(NULL, test_data.key); ck_assert_ptr_nonnull(g_jwk_set); ck_assert(!jwks_error(g_jwk_set)); @@ -237,14 +237,14 @@ static void __verify_jwt(const char *jwt_str, const jwt_alg_t alg, } __attribute__((unused)) -static void __verify_jwk(const char *jwt_str, jwk_item_t *item) +static void __verify_jwk(const char *jwt_str, const jwk_item_t *item) { JWT_CONFIG_DECLARE(config); jwt_auto_t *jwt = NULL; int ret = 0; config.jw_key = item; - config.alg = item->alg; + config.alg = jwks_item_alg(item); ret = jwt_verify(&jwt, jwt_str, &config); ck_assert_int_eq(ret, 0); ck_assert_ptr_nonnull(jwt); diff --git a/tests/keys/jwks/jwk2key.c b/tests/keys/jwks/jwk2key.c index 28788841..f059af1b 100644 --- a/tests/keys/jwks/jwk2key.c +++ b/tests/keys/jwks/jwk2key.c @@ -13,23 +13,23 @@ #include -static void write_key_file(jwk_item_t *item) +static void write_key_file(const jwk_item_t *item) { const char *pre, *name; - int priv = item->is_private_key;; + int priv = jwks_item_is_private(item); char file_name[BUFSIZ]; FILE *fp; - if (item->error || !item->pem) + if (jwks_item_error(item) || jwks_item_pem(item) == NULL) return; - switch (item->kty) { + switch (jwks_item_kty(item)) { case JWK_KEY_TYPE_EC: pre = "ec"; - name = item->curve; + name = jwks_item_curve(item); break; case JWK_KEY_TYPE_RSA: - switch (item->alg) { + switch (jwks_item_alg(item)) { case JWT_ALG_PS256: case JWT_ALG_PS384: case JWT_ALG_PS512: @@ -50,12 +50,12 @@ static void write_key_file(jwk_item_t *item) return; } - if (item->kid == NULL) { + if (jwks_item_kid(item) == NULL) { snprintf(file_name, sizeof(file_name), "pems/%s_%s%s.pem", pre, name, priv ? "" : "_pub"); } else { snprintf(file_name, sizeof(file_name), "pems/%s_%s_%s%s.pem", - pre, name, item->kid, priv ? "" : "_pub"); + pre, name, jwks_item_kid(item), priv ? "" : "_pub"); } fp = fopen(file_name, "wx"); @@ -64,14 +64,14 @@ static void write_key_file(jwk_item_t *item) return; } - fputs(item->pem, fp); + fputs(jwks_item_pem(item), fp); fclose(fp); } int main(int argc, char **argv) { jwk_set_t *jwk_set = NULL; - jwk_item_t *item; + const jwk_item_t *item; char *json_str; char *file; FILE *fp; @@ -101,7 +101,7 @@ int main(int argc, char **argv) fclose(fp); json_str[len] = '\0'; - jwk_set = jwks_create(json_str); + jwk_set = jwks_create(NULL, json_str); free(json_str); if (jwk_set == NULL) { perror("Failed to load JWKS");