diff --git a/CMakeLists.txt b/CMakeLists.txt index 286f43ff..88e65c59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,6 @@ project(${LIBJWT_PROJECT} DESCRIPTION ${LIBJWT_DESCRIPTION} HOMEPAGE_URL ${LIBJWT_HOMEPAGE_URL} LANGUAGES C) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") # Must be set after the above include(GNUInstallDirs) @@ -27,17 +26,11 @@ find_package(PkgConfig REQUIRED) pkg_check_modules(JANSSON jansson>=2.0 REQUIRED IMPORTED_TARGET) -if (NOT DEFINED WITH_OPENSSL) - set(OPENSSL_AUTO TRUE) -endif() if (NOT DEFINED WITH_GNUTLS) set(GNUTLS_AUTO TRUE) endif() -option(WITH_OPENSSL "Whether to use OpenSSL (default is auto detect)" ON) -option(WITH_GNUTLS "Whether to use GnuTLS (default is auto detect)" ON) - -# option(WITH_MBEDTLS "Enable using MBedTLS (default is disabled)" ON) +option(WITH_GNUTLS "Whether to use GnuTLS (default is auto detect)" ON) option(WITH_EXAMPLES "Whether to build the example programs (default is OFF)" OFF) option(WITH_TESTS "Whether to build and run the testsuite (default is ON)" ON) @@ -50,13 +43,8 @@ if (WITH_GNUTLS) ${GNUTLS_REQUIRED}) endif() -if (WITH_OPENSSL) - #if (NOT OPENSSL_AUTO) - set(OPENSSL_REQUIRED REQUIRED) - #endif() - pkg_check_modules(OPENSSL openssl>=3.0.0 IMPORTED_TARGET - ${OPENSSL_REQUIRED}) -endif() +pkg_check_modules(OPENSSL openssl>=3.0.0 IMPORTED_TARGET + REQUIRED) add_library(jwt SHARED) add_library(jwt_static STATIC) @@ -65,14 +53,24 @@ set_target_properties(jwt_static PROPERTIES COMPILE_FLAGS -DJWT_STATIC_DEFINE) set(JWT_SOURCES libjwt/base64.c - libjwt/jwt-memory.c - libjwt/jwt.c - libjwt/jwks.c - libjwt/jwt-setget.c - libjwt/jwt-crypto-ops.c - libjwt/jwt-validate.c - libjwt/jwt-encode.c - libjwt/jwt-verify.c) + libjwt/jwt-memory.c + libjwt/jwt.c + libjwt/jwks.c + libjwt/jwt-setget.c + libjwt/jwt-crypto-ops.c + libjwt/jwt-encode.c + libjwt/jwt-verify.c) + +add_library(builder OBJECT) +target_sources(builder PRIVATE libjwt/jwt-common.c) +target_compile_definitions(builder PRIVATE JWT_BUILDER) + +add_library(checker OBJECT) +target_sources(checker PRIVATE libjwt/jwt-common.c) +target_compile_definitions(checker PRIVATE JWT_CHECKER) + +target_link_libraries(jwt PRIVATE builder checker) +target_link_libraries(jwt_static PRIVATE builder checker) # Allow building without deprecated functions (suggested) option(EXCLUDE_DEPRECATED @@ -88,6 +86,8 @@ include_directories(${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR} target_link_libraries(jwt PUBLIC PkgConfig::JANSSON) target_link_libraries(jwt_static PUBLIC PkgConfig::JANSSON) +target_link_libraries(builder PUBLIC PkgConfig::JANSSON) +target_link_libraries(checker PUBLIC PkgConfig::JANSSON) # Process the detected packages set(HAVE_CRYPTO FALSE) @@ -101,15 +101,13 @@ if (GNUTLS_FOUND) # libjwt/gnutls/jwk-parse.c endif() -if (OPENSSL_FOUND) - set(HAVE_CRYPTO TRUE) - add_definitions(-DHAVE_OPENSSL) - target_link_libraries(jwt PUBLIC PkgConfig::OPENSSL) - target_link_libraries(jwt_static PUBLIC PkgConfig::OPENSSL) - list(APPEND JWT_SOURCES - libjwt/openssl/jwk-parse.c - libjwt/openssl/sign-verify.c) -endif() +set(HAVE_CRYPTO TRUE) +add_definitions(-DHAVE_OPENSSL) +target_link_libraries(jwt PUBLIC PkgConfig::OPENSSL) +target_link_libraries(jwt_static PUBLIC PkgConfig::OPENSSL) +list(APPEND JWT_SOURCES + libjwt/openssl/jwk-parse.c + libjwt/openssl/sign-verify.c) function(jwt_add_extra) set(oneValueArgs NAME SRC DIR) @@ -127,7 +125,7 @@ function(jwt_add_tool) cmake_parse_arguments(Tool "" "${oneValueArgs}" "" ${ARGN}) add_executable(${Tool_NAME} ${Tool_SRC}) - target_link_libraries(${Tool_NAME} PRIVATE jwt_static PkgConfig::OPENSSL) + target_link_libraries(${Tool_NAME} PRIVATE jwt_static PkgConfig::OPENSSL) # target_link_libraries(${Tool_NAME} PRIVATE jwt) set_target_properties(${Tool_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY @@ -169,7 +167,7 @@ set_target_properties(jwt PROPERTIES SOVERSION ${LIBJWT_COMPATVERSION} ) -add_definitions(-D_GNU_SOURCE -O3 -Wall -Werror) +add_definitions(-D_GNU_SOURCE -O3 -Wall -Werror -Wextra) add_definitions(-DKEYDIR=\"${CMAKE_SOURCE_DIR}/tests/keys\") # Install header @@ -256,15 +254,14 @@ endfunction() if (CHECK_FOUND) include(CTest) - set (UNIT_TESTS jwt_dump jwt_ec jwt_encode jwt_grant jwt_header - jwt_new jwt_rsa jwt_validate jwt_rsa_pss jwt_eddsa - jwt_crypto jwt_es256k) + set (UNIT_TESTS jwt_crypto) - if (OPENSSL_FOUND) - # For now, only OpenSSL supports JWKS tests - list (APPEND UNIT_TESTS jwt_jwks jwt_jwks_errors - jwt_jwks_ec jwt_jwks_rsa) - endif() + # JWKS Tests + list (APPEND UNIT_TESTS jwt_jwks jwt_jwks_errors + jwt_ec jwt_rsa jwt_hs) + + # Checker and Builder + list (APPEND UNIT_TESTS jwt_builder jwt_checker jwt_flipflop) foreach (TEST ${UNIT_TESTS}) jwt_add_test(NAME ${TEST}) diff --git a/cmake/LibJWTDoxyfile.cmake b/cmake/LibJWTDoxyfile.cmake index e0a81ec8..bbc63cbf 100644 --- a/cmake/LibJWTDoxyfile.cmake +++ b/cmake/LibJWTDoxyfile.cmake @@ -50,6 +50,7 @@ set(DOXYGEN_GENERATE_TAGFILE "${DOXYGEN_OUTPUT_DIRECTORY}/LibJWT.tag") set(DOXYGEN_DOT_IMAGE_FORMAT "svg") set(DOXYGEN_INTERACTIVE_SVG "YES") set(DOXYGEN_SEARCHENGINE "NO") +set(DOXYGEN_LAYOUT_FILE "doxygen/DoxygenLayout.xml") # List of extra files we need for a nice theme set(DOXYGEN_HTML_EXTRA_FILES "doxygen/doxygen-awesome-paragraph-link.js") diff --git a/doxygen/DoxygenLayout.xml b/doxygen/DoxygenLayout.xml new file mode 100644 index 00000000..3d37df9b --- /dev/null +++ b/doxygen/DoxygenLayout.xml @@ -0,0 +1,269 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/main-auth.c b/examples/main-auth.c index bb2ad97e..a707b4c6 100644 --- a/examples/main-auth.c +++ b/examples/main-auth.c @@ -14,7 +14,7 @@ #include #include -void usage(const char *name) +static void usage(const char *name) { printf("%s --key example.json --token eyJhb...\n", name); printf("Options:\n" @@ -24,19 +24,46 @@ void usage(const char *name) exit(0); } +static int __verify_wcb(jwt_t *jwt, jwt_config_t *config) +{ + jwt_value_t jval; + int ret; + + if (config == NULL) + return 1; + + jwt_set_GET_JSON(&jval, NULL); + jval.pretty = 1; + ret = jwt_header_get(jwt, &jval); + if (!ret) { + fprintf(stderr, "HEADER:\n%s\n", jval.json_val); + free(jval.json_val); + } + + jwt_set_GET_JSON(&jval, NULL); + jval.pretty = 1; + ret = jwt_grant_get(jwt, &jval); + if (!ret) { + fprintf(stderr, "PAYLOAD:\n%s\n", jval.json_val); + free(jval.json_val); + } + return 0; +} + int main(int argc, char *argv[]) { + jwt_checker_auto_t *checker = NULL; char *key_file = NULL, *token = NULL; jwt_alg_t opt_alg = JWT_ALG_NONE; - JWT_CONFIG_DECLARE(config); - jwt_auto_t *jwt = NULL; jwk_set_auto_t *jwk_set = NULL; const jwk_item_t *item = NULL; - FILE *key_fp = NULL; - char key_data[BUFSIZ]; - jwt_value_t jval; - int key_len; - int oc, ret; + int oc; + + checker = jwt_checker_new(); + if (checker == NULL) { + fprintf(stderr, "Could not allocate checker context\n"); + exit(EXIT_FAILURE); + } char *optstr = "hk:t:a"; struct option opttbl[] = { @@ -97,71 +124,41 @@ int main(int argc, char *argv[]) /* Load JWK key */ if (key_file) { - key_fp = fopen(key_file, "r"); - if (key_fp == NULL) { - perror(key_file); - exit(EXIT_FAILURE); - } - key_len = fread(key_data, 1, sizeof(key_data), key_fp); - fclose(key_fp); - key_data[key_len] = '\0'; - - /* Setup JWK Set */ - jwk_set = jwks_create(key_data); - if (jwk_set == NULL || jwks_error(jwk_set)) { - fprintf(stderr, "ERR: Could not read JWK: %s\n", - jwks_error_msg(jwk_set)); - exit(EXIT_FAILURE); - } - /* Get the first key */ - item = jwks_item_get(jwk_set, 0); - if (jwks_item_error(item)) { - fprintf(stderr, "ERR: Could not read JWK: %s\n", - jwks_item_error_msg(item)); - exit(EXIT_FAILURE); - } - - if (jwks_item_alg(item) == JWT_ALG_NONE && opt_alg == JWT_ALG_NONE) { - fprintf(stderr, "Cannot find a valid algorithm in the " - " JWK. You need to set it with --alg\n"); - exit(EXIT_FAILURE); - } - - if (jwks_item_alg(item) != JWT_ALG_NONE && opt_alg != JWT_ALG_NONE && - jwks_item_alg(item) != opt_alg) { - fprintf(stderr, "Key algorithm does not match --alg argument\n"); - exit(EXIT_FAILURE); - } + jwk_set = jwks_create_fromfile(key_file); + if (jwk_set == NULL || jwks_error(jwk_set)) { + fprintf(stderr, "ERR: Could not read JWK: %s\n", + jwks_error_msg(jwk_set)); + exit(EXIT_FAILURE); + } + + /* Get the first key */ + item = jwks_item_get(jwk_set, 0); + if (jwks_item_error(item)) { + fprintf(stderr, "ERR: Could not read JWK: %s\n", + jwks_item_error_msg(item)); + exit(EXIT_FAILURE); + } + + if (jwt_checker_setkey(checker, opt_alg, item)) { + fprintf(stderr, "ERR Loading key: %s\n", + jwt_checker_error_msg(checker)); + exit(EXIT_FAILURE); + } } - /* Decode jwt */ - config.jw_key = item; - config.alg = opt_alg; - jwt = jwt_verify(token, &config); - if (jwt == NULL || jwt_error(jwt)) { - fprintf(stderr, "JWT could not be verified: %s\n", - jwt ? jwt_error_msg(jwt) : "Unknown reason"); + if (jwt_checker_setcb(checker, __verify_wcb, NULL)) { + fprintf(stderr, "ERR setting callback: %s\n", + jwt_checker_error_msg(checker)); exit(EXIT_FAILURE); } - fprintf(stderr, "JWT %s successfully!\n", - token ? "verified" : "decoded"); - - jwt_set_GET_JSON(&jval, NULL); - jval.pretty = 1; - ret = jwt_header_get(jwt, &jval); - if (!ret) { - fprintf(stderr, "HEADER:\n%s\n", jval.json_val); - free(jval.json_val); + if (jwt_checker_verify(checker, token)) { + fprintf(stderr, "ERR verifyiung token: %s\n", + jwt_checker_error_msg(checker)); + exit(EXIT_FAILURE); } - jwt_set_GET_JSON(&jval, NULL); - jval.pretty = 1; - ret = jwt_grant_get(jwt, &jval); - if (!ret) { - fprintf(stderr, "PAYLOAD:\n%s\n", jval.json_val); - free(jval.json_val); - } + fprintf(stderr, "JWT verfified successfully\n"); exit(EXIT_SUCCESS); } diff --git a/examples/main-gen.c b/examples/main-gen.c index 8d2d6d5a..7378285e 100644 --- a/examples/main-gen.c +++ b/examples/main-gen.c @@ -18,9 +18,10 @@ void usage(const char *name) { printf("%s OPTIONS\n", name); printf("Options:\n" - " -k --key KEY The private JWK to use for signing (.json)\n" + " -k --key KEY The private key to use for signing (JWT json)\n" " -a --alg ALG The algorithm to use for signing\n" - " -c --claim KEY=VALUE A claim to add to JWT\n" + " -c --claim t:KEY=VALUE A claim to add to JWT\n" + " where t is i, s, or b for integer, string, or boolean\n" " -j --json '{key1:value1}' A json to add to JWT\n" ); exit(0); @@ -43,24 +44,19 @@ int main(int argc, char *argv[]) { NULL, 0, 0, 0 }, }; - char *k = NULL, *v = NULL; - int claims_count = 0; - int i = 0; - char key[BUFSIZ]; - size_t key_len = 0; - FILE *fp_priv_key; - int ret = 0; - jwt_auto_t *jwt = NULL; + char *t = NULL, *k = NULL, *v = NULL; jwk_set_auto_t *jwk_set = NULL; const jwk_item_t *item = NULL; - jwt_value_t jval; - struct kv { - char *key; - char *val; - } opt_claims[100]; - memset(opt_claims, 0, sizeof(opt_claims)); char* opt_json = NULL; - JWT_CONFIG_DECLARE(config); + jwt_builder_auto_t *builder = NULL; + jwt_value_t jval; + char *out; + + builder = jwt_builder_new(); + if (builder == NULL) { + fprintf(stderr, "Could not allocate builder context\n"); + exit(EXIT_FAILURE); + } while ((oc = getopt_long(argc, argv, optstr, opttbl, NULL)) != -1) { switch (oc) { @@ -71,24 +67,52 @@ int main(int argc, char *argv[]) case 'a': opt_alg = jwt_str_alg(optarg); if (opt_alg >= JWT_ALG_INVAL) { - fprintf(stderr, "%s is not supported algorithm\n", optarg); + fprintf(stderr, + "%s is not supported algorithm\n", + optarg); exit(EXIT_FAILURE); } break; case 'c': - k = strtok(optarg, "="); - if (k) { - v = strtok(NULL, "="); - if (v) { - opt_claims[claims_count].key = k; - opt_claims[claims_count].val = v; - claims_count++; - } + t = strtok(optarg, ":"); + if (t == NULL) + usage(basename(argv[0])); + k = strtok(NULL, "="); + if (k == NULL) + usage(basename(argv[0])); + + v = strtok(NULL, "="); + if (v == NULL) + usage(basename(argv[0])); + + switch (t[0]) { + case 's': + jwt_set_ADD_STR(&jval, k, v); + break; + case 'i': + jwt_set_ADD_INT(&jval, k, strtol(v, NULL, 10)); + break; + case 'b': + if (v[0] == 'f' || v[0] == 'F' || v[0] == '0') + jwt_set_ADD_BOOL(&jval, k, 0); + else + jwt_set_ADD_BOOL(&jval, k, 1); + break; + default: + usage(basename(argv[0])); } + if (jwt_builder_claim_add(builder, &jval)) { + fprintf(stderr, "Error adding %s:%s=%s\n", + t, k, v); + exit(EXIT_FAILURE); + } + break; case 'j': - opt_json = optarg; + if (optarg != NULL) { + opt_json = strdup(optarg); + } break; case 'h': @@ -104,24 +128,17 @@ int main(int argc, char *argv[]) fprintf(stderr, "jwtgen: privkey %s algorithm %s\n", opt_key_name, jwt_alg_str(opt_alg)); - if (opt_alg > JWT_ALG_NONE) { - fp_priv_key = fopen(opt_key_name, "r"); - if (fp_priv_key == NULL) { - perror("Failed to open key file"); - goto finish; - } - key_len = fread(key, 1, sizeof(key), fp_priv_key); - fclose(fp_priv_key); - key[key_len] = '\0'; - fprintf(stderr, "priv key loaded %s (%zu)!\n", opt_key_name, key_len); + if (opt_alg != JWT_ALG_NONE && opt_key_name == NULL) + usage(basename(argv[0])); - /* Setup JWK Set */ - jwk_set = jwks_create(key); + if (opt_key_name) { + jwk_set = jwks_create_fromfile(opt_key_name); if (jwk_set == NULL || jwks_error(jwk_set)) { fprintf(stderr, "ERR: Could not read JWK: %s\n", jwks_error_msg(jwk_set)); exit(EXIT_FAILURE); } + /* Get the first key */ item = jwks_item_get(jwk_set, 0); if (jwks_item_error(item)) { @@ -130,67 +147,47 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } - if (jwks_item_alg(item) == JWT_ALG_NONE && opt_alg == JWT_ALG_NONE) { - fprintf(stderr, "Cannot find a valid algorithm in the " - " JWK. You need to set it with --alg\n"); - exit(EXIT_FAILURE); - } - - if (jwks_item_alg(item) != JWT_ALG_NONE && opt_alg != JWT_ALG_NONE && - jwks_item_alg(item) != opt_alg) { - fprintf(stderr, "Key algorithm does not match --alg argument\n"); + if (jwt_builder_setkey(builder, opt_alg, item)) { + fprintf(stderr, "ERR Loading key: %s\n", + jwt_builder_error_msg(builder)); exit(EXIT_FAILURE); } } - config.jw_key = item; - config.alg = opt_alg; - jwt = jwt_create(&config); - if (jwt == NULL) { - fprintf(stderr, "invalid jwt\n"); - goto finish; - } - jwt_set_ADD_INT(&jval, "iat", iat); - jwt_grant_add(jwt, &jval); - for (i = 0; i < claims_count; i++) { - fprintf(stderr, "Adding claim %s with value %s\n", - opt_claims[i].key, opt_claims[i].val); - - jwt_set_ADD_STR(&jval, opt_claims[i].key, opt_claims[i].val); - jwt_grant_add(jwt, &jval); + if (jwt_builder_claim_add(builder, &jval)) { + fprintf(stderr, "Error adding iat\n"); + exit(EXIT_FAILURE); } - if (opt_json != NULL) { + if (opt_json) { jwt_set_ADD_JSON(&jval, NULL, opt_json); - ret = jwt_grant_add(jwt, &jval); - if (ret != 0) { - fprintf(stderr, "Input json is invalid\n"); - goto finish; + if (jwt_builder_claim_add(builder, &jval)) { + fprintf(stderr, "Error adding iat\n"); + exit(EXIT_FAILURE); } } - char *out = jwt_encode_str(jwt); - printf("Token: %s\n", out); - free(out); - jwt_set_GET_JSON(&jval, NULL); jval.pretty = 1; - if (jwt_header_get(jwt, &jval) == JWT_VALUE_ERR_NONE) { - fprintf(stderr, "HEADER: %s\n", jval.json_val); + if (jwt_builder_claim_get(builder, &jval) == JWT_VALUE_ERR_NONE) { + fprintf(stderr, "PAYLOAD: %s\n", jval.json_val); free(jval.json_val); } - jwt_set_GET_JSON(&jval, NULL); - jval.pretty = 1; - if (jwt_grant_get(jwt, &jval) == JWT_VALUE_ERR_NONE) { - fprintf(stderr, "PAYLOAD: %s\n", jval.json_val); - free(jval.json_val); + out = jwt_builder_generate(builder); + if (out == NULL) { + fprintf(stderr, "ERR Generating Token: %s\n", + jwt_builder_error_msg(builder)); + exit(EXIT_FAILURE); } fprintf(stderr, "jwt algo %s!\n", jwt_alg_str(opt_alg)); -finish: + printf("%s\n", out); + + free(out); + return 0; } diff --git a/include/jwt.h b/include/jwt.h index bc2aa0c6..698f1a64 100644 --- a/include/jwt.h +++ b/include/jwt.h @@ -24,34 +24,21 @@ extern "C" { #endif -/** @ingroup jwt_core_grp +/** @ingroup jwt_grp * @brief Opaque JWT object * - * This object is used throughout the JWT functions. - * - * @remark When creating a JWT object (encoding), this stores state until - * you call one of the encoding functions. When dedcoding a JSON Web Token - * this object is returned so you can inspect it further (e.g. retrieve - * grants). + * Used in callbacks when generating or verifying a JWT */ typedef struct jwt jwt_t; -/** @ingroup jwt_valid_grp - * @brief Opaque JWT Validation object - * - * Used in the JWT validation functions. - */ -typedef struct jwt_valid jwt_valid_t; - /** @ingroup jwks_core_grp * @brief Opaque JWKS object * * Used for working with JSON Web Keys and JWK Sets (JWKS). * - * @remark All JWK operations require that you import your JWK into a - * jwk_set_t first. Internal, LibJWT creates a jwk_set_t even for single - * keys. This makes code pretty much the same whether working with one JWK - * or a set of them. + * @remark All JWK operations require that you import your JWK into a jwk_set_t + * first. Internal, LibJWT creates a jwk_set_t even for single keys. This makes + * code pretty much the same whether working with one JWK or a set of them. */ typedef struct jwk_set jwk_set_t; @@ -99,7 +86,7 @@ typedef enum { JWT_CRYPTO_OPS_ANY, /**< Used internally for hmac keys */ } jwt_crypto_provider_t; -/** @ingroup jwks_core_grp +/** @ingroup jwks_item_grp * @brief JWK Key Types * * Corresponds to the ``"kty"`` attribute of the JWK. @@ -115,7 +102,7 @@ typedef enum { JWK_KEY_TYPE_OCT, /**< Octet sequence (e.g. HS256) */ } jwk_key_type_t; -/** @ingroup jwks_core_grp +/** @ingroup jwks_item_grp * @brief Usage types for JWK public keys * * Corresponds to the ``"use"`` attribute in a JWK the represents a public key. @@ -124,16 +111,16 @@ typedef enum { **/ typedef enum { JWK_PUB_KEY_USE_NONE = 0, /**< No usable attribute was set */ - JWK_PUB_KEY_USE_SIG, /**< Signature validation (JWS) */ - JWK_PUB_KEY_USE_ENC, /**< Decryption key (JWE) */ + JWK_PUB_KEY_USE_SIG, /**< Signature key (JWS) */ + JWK_PUB_KEY_USE_ENC, /**< Encryption key (JWE) */ } jwk_pub_key_use_t; -/** @ingroup jwks_core_grp +/** @ingroup jwks_item_grp * @brief Allowed key operations for JWK private keys * * Corresponds to the ``"key_ops"`` attribute in a JWK that represents a private - * key. These can be bitwise compares to the key_ops attribute of a @ref - * jwk_item_t. These flags are used internally to decide if a JWK can be used + * key. These can be bitwise compares to the key_ops attribute of a jwk_item_t. + * These flags are used internally to decide if a JWK can be used * for cartain operations. * * @code @@ -157,35 +144,62 @@ typedef enum { JWK_KEY_OP_INVALID = 0xffff, /**< Invalid key_ops in JWK */ } jwk_key_op_t; -/** @ingroup jwks_core_grp - * @brief Object representation of a JWK - * - * 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. +/** @ingroup jwt_setget_grp + * @brief Value types for grants and headers */ -typedef struct jwk_item jwk_item_t; +typedef enum { + JWT_VALUE_NONE = 0, /**< No type (do not use this) */ + JWT_VALUE_INT, /**< Integer */ + JWT_VALUE_STR, /**< String */ + JWT_VALUE_BOOL, /**< Boolean */ + JWT_VALUE_JSON, /**< JSON String (object format ``{}``) */ + JWT_VALUE_INVALID, /**< Invalid (used internally) */ +} jwt_value_type_t; -/** @ingroup jwt_valid_grp - * @brief Validation exception types for @ref jwt_t objects +/** @ingroup jwt_setget_grp + * @brief Error values for header and grant requests + */ +typedef enum { + JWT_VALUE_ERR_NONE = 0, /**< No error, success */ + JWT_VALUE_ERR_EXIST, /**< Item exists (when adding) */ + JWT_VALUE_ERR_NOEXIST, /**< Item doesn't exist (when getting) */ + JWT_VALUE_ERR_TYPE, /**< Item is not of the type requested */ + JWT_VALUE_ERR_INVALID, /**< Invalid request (general error) */ + JWT_VALUE_ERR_NOMEM, /**< Memory allocation error */ +} jwt_value_error_t; + +/** @ingroup jwt_setget_grp + * @brief Data type for get and add actions for JWT headers and grants * - * These are bitwise values that allow you to check for exceptions when using - * the @ref jwt_valid_t + * This is used for both add and get requests. Specific rules for each type is + * described in more detail for the add and get requests. * - * @todo @rfc_t{7519,4.1.6} ``"iat"`` Issued At - * @todo @rfc_t{7519,4.1.7} ``"jti"`` JWT ID + * @note There are helper macros to simplify settng this structure properly and + * reducing common mistakes. See the jwt_set_{ADD,GET}_{INT,STR,BOOL,JSON} + * definitions. */ -typedef enum { -JWT_VALIDATION_SUCCESS = 0x0000, /**< Validation succeeded */ -JWT_VALIDATION_ERROR = 0x0001, /**< General failures */ -JWT_VALIDATION_ALG_MISMATCH = 0x0002, /**< @rfc_t{7518,3.1} ``"alg"`` Algorithm */ -JWT_VALIDATION_EXPIRED = 0x0004, /**< @rfc_t{7519,4.1.4} ``"exp"`` Expired */ -JWT_VALIDATION_TOO_NEW = 0x0008, /**< @rfc_t{7519,4.1.5} ``"nbf"`` Not Before */ -JWT_VALIDATION_ISS_MISMATCH = 0x0010, /**< @rfc_t{7519,4.1.1} ``"iss"`` Issuer */ -JWT_VALIDATION_SUB_MISMATCH = 0x0020, /**< @rfc_t{7519,4.1.2} ``"sub"`` Subject */ -JWT_VALIDATION_AUD_MISMATCH = 0x0040, /**< @rfc_t{7519,4.1.3} ``"aud"`` Audience */ -JWT_VALIDATION_GRANT_MISSING = 0x0080, /**< User-defined Grant missing */ -JWT_VALIDATION_GRANT_MISMATCH = 0x0100, /**< User-defined Grant mismatch */ -} jwt_valid_exception_t; +typedef struct { + jwt_value_type_t type; + char *name; + union { + long int_val; + const char *str_val; + int bool_val; + char *json_val; + }; + int replace; + int pretty; + jwt_value_error_t error; +} jwt_value_t; + +/** @ingroup jwks_item_grp + * @brief Object representation of a JWK + * + * This object is produced by importing a JWK or JWKS into a @ref jwk_set_t + * object. It represents single key and is used when generating or verifying + * JWT. + */ +typedef struct jwk_item jwk_item_t; /** @ingroup jwt_memory_grp * @brief Prototype for malloc(3) @@ -208,299 +222,442 @@ typedef void (*jwt_free_t)(void *); */ /** - * @defgroup jwt_core_grp Utility functions + * Get the jwt_alg_t set for this JWT object. * - * Utility functions for JWT objects. - * @{ + * Returns the jwt_alg_t type for this JWT object. + * + * @param jwt Pointer to a JWT object. + * @returns Returns a jwt_alg_t type for this object. */ +JWT_EXPORT +jwt_alg_t jwt_get_alg(const jwt_t *jwt); /** - * @brief Check JWT for error condition - * - * The relevance of this is dependent on whether this is a JWT being created, - * or one being verified. See those functions for more information. Either way, - * if a JWT has an error, it cannot be trusted. + * @brief Structure used to pass state with a user callback + */ +typedef struct { + const jwk_item_t *key; /**< A JWK to use for key */ + jwt_alg_t alg; /**< For algorithm matching */ + void *ctx; /**< User controlled context */ +} jwt_config_t; + +/** + * @brief General callback for generation and verification of JWT + */ +typedef int (*jwt_callback_t)(jwt_t *, jwt_config_t *); + +/** + * @brief WFC defined claims + */ +typedef enum { + JWT_CLAIM_DEFAULT = 0x0000, /**< Nothing set, default claims */ + JWT_CLAIM_NONE = 0x0001, /**< No checks */ + JWT_CLAIM_ISS = 0x0002, /**< @rfc_t{7519,4.1.1} ``"iss"`` */ + JWT_CLAIM_SUB = 0x0004, /**< @rfc_t{7519,4.1.2} ``"sub"`` */ + JWT_CLAIM_AUD = 0x0008, /**< @rfc_t{7519,4.1.3} ``"aud"`` */ + JWT_CLAIM_EXP = 0x0010, /**< @rfc_t{7519,4.1.4} ``"exp"`` */ + JWT_CLAIM_NBF = 0x0020, /**< @rfc_t{7519,4.1.5} ``"nbf"`` */ + JWT_CLAIM_IAT = 0x0040, /**< @rfc_t{7519,4.1.6} ``"iat"`` */ + JWT_CLAIM_JTI = 0x0080, /**< @rfc_t{7519,4.1.7} ``"nbf"`` */ + JWT_CLAIMS_ENFORCE = 0x8000, /**< Fail if claim is missing */ + JWT_CLAIMS_ALL = 0x80fe, /**< Mask of all claims */ +} jwt_claims_t; + +/** + * @brief Default validations * - * @param jwt Pointer to a jwt_t object - * @return 0 if no error, 1 if there is + * Beyond the normal validations (e.g. algorithm, and signature checks) these + * are the ones that will be performed if the grants exist in the JWT. If the + * grants do not exist, they validation will be ignores. * - * @remark When creating a JWT and verifying one, you shoudl always check this - * state. + * @note If you do not set any validation flags (JWT_VALIDATION_EMPTY), these + * will be used. If you do not want them used, them you must set + * JWT_VALIDATION_NONE to override it. */ -JWT_EXPORT -int jwt_error(const jwt_t *jwt); +#define JWT_CHECKER_CLAIMS (JWT_CLAIM_EXP|JWT_CLAIM_NBF) + +#define JWT_BUILDER_CLAIMS (JWT_CLAIM_IAT) /** - * @brief Print an error message from a JWT + * @defgroup jwt_builder_grp Builder * - * If jwt_error() shows an an error condition, this will give you a better idea - * of the actual error being reported. + * Creating a JWT token involves several steps. First is creating a + * jwt_builder_t object, which can be thought of as a JWT factory. Once + * configured, you can use it to create tokens with pre-defined claims. + * @{ + */ + +/** + * @brief Opaque Builder + */ +typedef struct jwt_builder jwt_builder_t; + +/** + * @brief Function to create a new builder instance * - * @param jwt Pointer to a jwt_t object - * @return A string message. The string may be empty. + * @return Pointer to a builder object on success, NULL on failure */ JWT_EXPORT -const char *jwt_error_msg(const jwt_t *jwt); +jwt_builder_t *jwt_builder_new(void); /** - * @brief Free a JWT object and any other resources it is using. + * @brief Frees a previously created builder object * - * After calling, the JWT object referenced will no longer be valid and - * its memory will be freed. - * - * @param jwt Pointer to a JWT object previously created object + * @param builder Pointer to a builder object */ JWT_EXPORT -void jwt_free(jwt_t *jwt); +void jwt_builder_free(jwt_builder_t *builder); #if defined(__GNUC__) || defined(__clang__) /** - * @brief Helper function to free a JWT and set the pointer to NULL + * @brief Helper function to free a builder and set the pointer to NULL * - * This is mainly to use with the jwt_auto_t type. + * This is mainly to use with the jwt_builder_auto_t type. * - * @param Pointer to a pointer for a jwt_t object + * @param Pointer to a pointer for a jwt_builder_t object */ -static inline void jwt_freep(jwt_t **jwt) { - if (jwt) { - jwt_free(*jwt); - *jwt = NULL; +static inline void jwt_builder_freep(jwt_builder_t **builder) { + if (builder) { + jwt_builder_free(*builder); + *builder = NULL; } } +#define jwt_builder_auto_t jwt_builder_t \ + __attribute__((cleanup(jwt_builder_freep))) +#endif + /** - * @brief Scoped cleanup type for jwt_t - * - * Declaring a jwt_t with jwt_auto_t will ensure that the memory used by it is - * cleaned up when the variable goes our of scope (e.g. when a function - * returns). - * - * @warning Make sure to initialize thsi to NULL when declaring with this type. - * - * @code - * void my_app_check_token(const char *token) - * { - * jwt_auto_t *myjwt = NULL; - * - * // ... + * @brief Checks error state of builder object * - * myjwt = jwt_create(NULL); - * - * // ... - * if (error_val) - * return -1; // myjwt will be freed here automatically + * @param builder Pointer to a builder object + * @return 0 if no errors exist, non-zero otherwise + */ +JWT_EXPORT +int jwt_builder_error(const jwt_builder_t *builder); + +/** + * @brief Get the error message contained in a builder object * - * return 0; // myjwt will be freed here automatically - * } - * @endcode + * @param builder Pointer to a builder object + * @return Pointer to a string with the error message. Can be an empty string + * if there is no error. Never returns NULL. */ -#define jwt_auto_t jwt_t __attribute__((cleanup(jwt_freep))) -#endif +JWT_EXPORT +const char *jwt_builder_error_msg(const jwt_builder_t *builder); /** - * @brief Duplicate an existing JWT object. + * @brief Sets a key and algorithm for a builder * - * Copies all grants and algorithm specific bits to a new JWT object. This - * includes the jw_key that is associated with it, if it exists. However, the - * key is only copied by reference, and is not, itself, duplicated. + * The values here must make sense. This table shows what will or wont pass as + * far as algorithm matching between the alg param and the alg in jwk_item_t. + * Where ``alg-A`` means one specific algorithm (not none) and ``alg-B`` + * represents another (also not none). The ``none`` is used to represent no + * algorithm being set. ``NULL`` represents that jwk_item_t pointer is NULL. * - * @note If the jwt_t has an error, that error state will also be copied. + * | alg | jwt_item_t | Result + * :-------: | :--------: | :-----------------------: + * ``alg-A`` | ``alg-A`` | \emoji :white_check_mark: + * ``none`` | ``alg-A`` | \emoji :white_check_mark: + * ``alg-A`` | ``none`` | \emoji :white_check_mark: + * ``none`` | ``NULL`` | \emoji :warning: + * ``alg-A`` | ``alg-B`` | \emoji :x: + * ``alg-A`` | ``NULL`` | \emoji :x: * - * @param jwt Pointer to a JWT object. - * @return A new object on success, NULL on error + * @warning The warning represents an insecure token. Using insecure tokens is + * not very useful and strongly discouraged. + * + * @param builder Pointer to a builder object + * @param alg A valid jwt_alg_t type + * @param key A JWK key object + * @return 0 on success, non-zero otherwise with error set in the builder */ JWT_EXPORT -jwt_t *jwt_dup(jwt_t *jwt); +int jwt_builder_setkey(jwt_builder_t *builder, const jwt_alg_t alg, + const jwk_item_t *key); /** - * @} - * @noop jwt_core_grp + * @brief Clear error state in a builder object + * + * @param builder Pointer to a builder object */ +JWT_EXPORT +void jwt_builder_error_clear(jwt_builder_t *builder); /** - * @defgroup jwt_config_grp Configuration Type + * @brief Set claims for a builder object * - * The JWT configuration type is setup to allow an agnostic way to handle - * state between different functions. The specific uses of the type varies - * according to whether you are providing or consuming tokens. These aspects - * are documented in the other sections. + * These only apply to the RFC defined claims. The ``iat`` claim is the + * only one that's automated, and will default to the time at which + * jwt_builder_generate() was called to create the token. * - * This section is a light intro of config type and common usage. + * The ``nbf`` and ``exp`` claims need to have the offsets set as well. The + * Others can be set, but will need values added with jwt_builder_claim_add() + * in order to be enforced. * - * @remark LibJWT does not internally modify or set information in the - * @ref jwt_config_t object. Certain values will determine how LibJWT - * handles various functions. - * @{ - */ - -/** - * @brief Structure used to manage configuration state + * @param builder Pointer to a builder object + * @param grants A bitwise set of values in jwt_claims_t + * @return 0 on success, non-zero otherwise with error set in the builder */ -typedef struct { - 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; +JWT_EXPORT +int jwt_builder_setclaims(jwt_builder_t *builder, jwt_claims_t grants); /** - * @brief Intialize @ref jwt_config_t to a clean state. + * @brief Set a callback for generating tokens + * + * When generating a token, this callback will be run after jwt_t has been + * created, but before the token is encoded. During this, the callback can add, + * change, or remove claims and header attributes. It can also use the + * jwt_value_t structure to set a key and alg to use when signing the token. * - * To ensure a @ref jwt_config_t is at a known state, this will clear - * values in the config. It will not free memory that might be associated - * with internal pointers. + * The ctx value is also passed to the callback as part of the jwt_value_t + * struct. * - * @param config Pointer to config to be cleared + * @param builder Pointer to a builder object + * @param cb Pointer to a callback function + * @param ctx Pointer to data to pass to the callback function + * @return 0 on success, non-zero otherwise with error set in the builder */ JWT_EXPORT -void jwt_config_init(jwt_config_t *config); +int jwt_builder_setcb(jwt_builder_t *builder, jwt_callback_t cb, void *ctx); /** - * @brief Decleration of a @ref jwt_config_t + * @brief Generate a token * - * This is useful for scoped usage to avoid declaring it and running the - * @ref jwt_config_init function. + * The result of this function is to generate a string containing a JWT. A + * token is represetned by 3 parts: ``header``.``payload``.``sig``. Each part is + * Base64url Encoded. An example would be: * * @code - * void some_function(const char *token) - * { - * JWT_CONFIG_DECLARE(my_config); - * jwt_auto_t *my_jwt; - * int ret; + * eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MzY0MzI0MzR9.iDn6N9JsAdUPF11ow0skIfc9eJc2wGRIq30RSRZ8_68 + * @endcode + * + * When decoded, the header and payload woild look like this (excluding the + * signature block):: * - * // Setup my_config with key, alg type, etc + * @code + * {"alg":"HS256","typ":"JWT"}.{"iat":1736432434} + * @endcode * - * my_jwt = jwt_verify(token, &my_config); - * if (my_jwt == NULL || jwt_error(my_jwt)) - * ...; + * If pretty printed: * - * // Success + * @code + * { + * "alg": "HS256", + * "typ": "JWT" + * } + * . + * { + * "iat": 1736432434 * } * @endcode - */ -#define JWT_CONFIG_DECLARE(__name) \ - jwt_config_t __name = { NULL, JWT_ALG_NONE, NULL} - -/** - * @brief Callback for operations involving verification of tokens. * - * Further details can be found in @ref jwt_verify_grp, specifically - * for @ref jwt_verify_wcb + * The signature block is a cryptographic has. It's length and format is + * dependent on the algorithm being used. + * + * @param builder Pointer to a builder object + * @return A string containing a JWT. Caller is respondible for freeing the + * memory for this string. On error, NULL is returned and error is set in + * the builder object. */ -typedef int (*jwt_callback_t)(const jwt_t *, jwt_config_t *); +JWT_EXPORT +char *jwt_builder_generate(jwt_builder_t *builder); /** * @} - * @noop jwt_config_grp + * @noop jwt_builder_grp */ /** - * @defgroup jwt_create_grp Creation + * @defgroup jwt_checker_grp Checker * - * Creating a JWT token involves several steps. First is creating the jwt_t - * object. Next would be adding grants relevant to the usage and scope of this - * token, and finally encoding (signing and generating). + * Creating a JWT token involves several steps. First is creating a + * jwt_builder_t object, which can be thought of as a JWT factory. Once + * configured, you can use it to create tokens with pre-defined claims. * @{ */ -/** - * @brief Initial function to create a new JWT - * - * Create a new jwt_t object in preperation for encoding a new token. The - * config passed should contain an alg and a key that will be used to sign the - * token once done. If config.jw_key has an alg other than none, it must match - * config.alg. - * - * The resulting jwt_t object would then be used to add grants and other - * relevant information before finally passing it to one of the jwt_encode - * functions. - * - * This function rarely returns NULL. Usually only when memory cannot be - * allocated for the new jwt_t object. Callers are required to check for errors - * with jwt_error() after calling this function. - * - * If you want to create an insecure key (not suggested), either pass NULL as - * the config, or pass a config with just alg set as JWT_ALG_NONE, and jw_key - * set as NULL. - * - * @warning Creating insecure tokens is not very useful and strongly - * discouraged. - * - * @param config A jwt_config_t with settings for creating the token - * @return A new jwt_t object, or NULL of there was an error allocating it - */ +typedef struct jwt_checker jwt_checker_t; +JWT_EXPORT +jwt_checker_t *jwt_checker_new(void); + +JWT_EXPORT +void jwt_checker_free(jwt_checker_t *checker); + +#if defined(__GNUC__) || defined(__clang__) +static inline void jwt_checker_freep(jwt_checker_t **checker) { + if (checker) { + jwt_checker_free(*checker); + *checker = NULL; + } +} +#define jwt_checker_auto_t jwt_checker_t \ + __attribute__((cleanup(jwt_checker_freep))) +#endif + +JWT_EXPORT +int jwt_checker_error(const jwt_checker_t *checker); +JWT_EXPORT +const char *jwt_checker_error_msg(const jwt_checker_t *checker); JWT_EXPORT -jwt_t *jwt_create(jwt_config_t *config); +void jwt_checker_error_clear(jwt_checker_t *checker); + +JWT_EXPORT +int jwt_checker_setkey(jwt_checker_t *checker, const jwt_alg_t alg, const + jwk_item_t *key); +JWT_EXPORT +int jwt_checker_setclaims(jwt_checker_t *checker, jwt_claims_t grants); +JWT_EXPORT +int jwt_checker_setcb(jwt_checker_t *checker, jwt_callback_t cb, void *ctx); +JWT_EXPORT +int jwt_checker_verify(jwt_checker_t *checker, const char *token); /** - * @brief Fully encode a JWT object and return as a string. - * - * Similar to jwt_encode_fp() except that a string is returned. The string - * must be freed by the caller. - * - * @param jwt Pointer to a JWT object. - * @return A null terminated string on success, NULL on error with errno - * set appropriately. + * @} + * @noop jwt_checker_grp */ -JWT_EXPORT -char *jwt_encode_str(jwt_t *jwt); /** - * @brief Fully encode a JWT object and write it to FILE. - * - * This will create and write the complete JWT object to FILE. All parts - * will be Base64 encoded and signatures or encryption will be applied if - * the algorithm specified requires it. - * - * @param jwt Pointer to a JWT object. - * @param fp Valid FILE pointer to write data to. - * @return Returns 0 on success, valid errno otherwise. + * @defgroup jwt_claims_grp Working with Claims + * @{ */ + +JWT_EXPORT +jwt_value_error_t jwt_builder_header_add(jwt_builder_t *builder, jwt_value_t + *value); +JWT_EXPORT +jwt_value_error_t jwt_builder_header_get(jwt_builder_t *builder, jwt_value_t + *value); +JWT_EXPORT +jwt_value_error_t jwt_builder_header_del(jwt_builder_t *builder, const char + *header); +JWT_EXPORT +jwt_value_error_t jwt_builder_claim_add(jwt_builder_t *builder, jwt_value_t + *value); +JWT_EXPORT +jwt_value_error_t jwt_builder_claim_get(jwt_builder_t *builder, jwt_value_t + *value); +JWT_EXPORT +jwt_value_error_t jwt_builder_claim_del(jwt_builder_t *builder, const char + *header); + +JWT_EXPORT +jwt_value_error_t jwt_checker_header_add(jwt_checker_t *checker, jwt_value_t + *value); +JWT_EXPORT +jwt_value_error_t jwt_checker_header_get(jwt_checker_t *checker, jwt_value_t + *value); +JWT_EXPORT +jwt_value_error_t jwt_checker_header_del(jwt_checker_t *checker, const char + *header); JWT_EXPORT -int jwt_encode_fp(jwt_t *jwt, FILE *fp); +jwt_value_error_t jwt_checker_claim_add(jwt_checker_t *checker, jwt_value_t + *value); +JWT_EXPORT +jwt_value_error_t jwt_checker_claim_get(jwt_checker_t *checker, jwt_value_t + *value); +JWT_EXPORT +jwt_value_error_t jwt_checker_claim_del(jwt_checker_t *checker, const char + *header); /** * @} - * @noop jwt_create_grp + * @noop jwt_claims_grp */ /** * @defgroup jwt_verify_grp Verification * - * LibJWT provides mechanisms to verify a JWT including the signature block. + * LibJWT provides mechanisms to verify a JWT, including the signature block. * Many aspects of this verification are defined by the relevant RFCs. * - * @raisewarning Need more indepth information + * A ``token`` is a string that represents an encoded (and usually signed) JWT. + * It contains 3 parts: ``header``.``payload``.``sig`` + * + * Each part is Base64url Encoded. An example would be: + * + * @code + * eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MzY0MzI0MzR9.iDn6N9JsAdUPF11ow0skIfc9eJc2wGRIq30RSRZ8_68 + * @endcode + * + * When decoded, the header and payload look like: * + * @code + * {"alg":"HS256","typ":"JWT"}.{"iat":1736432434} + * @endcode + * + * Or with pretty printing: + * + * @code + * { + * "alg": "HS256", + * "typ": "JWT" + * } + * . + * { + * "iat": 1736432434 + * } + * @endcode + * + * The signature block decodes into binary bytes that represent the + * cryptographic signature. The length and format depends on the algorithm + * used. In the above example this is ``HS256``, or HMAC with SHA-256 hashing. + * + * In order to verify a token, jwt_config_t.key MUST be supplied, and either + * jwt_config_t.alg or the alg in jwk_item_t must be set to something other than + * ``none``. On return from verification, you should inspect the resulting + * jwt_t for error using jwt_error(). You can retrieve the error message with + * jwt_error_msg(). + * + * This table shows what will or wont pass as far as algorithm matching between + * jwt_config_t, jwt_t, and jwk_item_t, where ``alg-A`` means one specific + * algorithm (not none) and ``alg-B`` represents another (also not none). The + * ``none`` is used to represent no algorithm being set, and ``any`` represents + * any algorithm setting, ``none`` or otherwise. This table assumes that the + * JWT contains a signature block. + * + * jwt_config_t | jwk_item_t | jwt_t | Result + * :----------: | :--------: | :-------: | :-----------------------: + * ``alg-A`` | ``alg-A`` | ``alg-A`` | \emoji :white_check_mark: + * ``none`` | ``alg-A`` | ``alg-A`` | \emoji :white_check_mark: + * ``alg-A`` | ``none`` | ``alg-A`` | \emoji :white_check_mark: + * ``none`` | ``none`` | ``any`` | \emoji :x: + * ``any`` | ``any`` | ``none`` | \emoji :x: + * ``alg-A`` | ``alg-A`` | ``alg-B`` | \emoji :x: + * ``alg-A`` | ``none`` | ``alg-B`` | \emoji :x: + * ``none`` | ``alg-A`` | ``alg-B`` | \emoji :x: + * ``alg-A`` | ``alg-B`` | ``any`` | \emoji :x: + * + * @note For any JWT containing a signature block, jwt_config_t.key MUST + * point to a valid jwk_item_t key. + * + * For a JWT that does not contain a signature block, it will only pass + * verification if all of the following are true: + * + * jwt_config_t.alg | jwt_config_t.key | jwt_t alg | jwt_t sig | Result + * :-------- --: | :-----------------: | :-------: | :-------: | :-----------------------: + * ``none`` | ``NULL`` | ``none`` | ``empty`` | \emoji :white_check_mark: + * + * Anything else with an empty ``sig`` block will fail. This is to ensure + * security. Refer to the following links for security considerations: + * + * @rfc{7515,10} @rfc{7516,11} @rfc{7517,9} @rfc{7518,8} + * + * @warning Using insecure tokens is not very useful and strongly discouraged. + * + * @remark If you want to inspect a signed token, you MUST use jwt_verify_wcb() + * and supply a callback function. * @{ */ /** * @brief Decode and verify a JWT * - * In order to verify a token, the config param MUST set jw_key and alg. Both - * are required. On return, you should inspect the resulting jwt_t for error - * using jwt_error(). You can print a message with jwt_error_msg(). - * - * In order to verify, several things must be true: - * - The value of config.alg MUST match the value of alg in the token. - * - The value of config.jw_key.alg, if not "none" must also match the token - * - The token MUST have a signature block. - * - The key MUST be usable for the operation, either via the "use" attribute - * being "sig" or the "key_ops" attribute have the "verify" bit set. - * - The defined signature MUST pass. - * - * If you want to decode an unsigned JWT, these MUST be true: - * - The config.alg and jwt.alg MUST be "none" - * - The config.jw_key MUST be NULL - * - The signature block in the token MUST be empty - * - * If you want to inspect a signed token, you should use jwt_verify_wcb() and - * use a callback function. - * * @param token Pointer to a nil terminated JWT string * @param config Pointer to a config structure to define how to verify the - * token. This can be NULL, in which case the token is simply decoded. - * @return Pointer to a jwt_t object or NULL. Generally a NULL is unlikely. The - * object should be checked with jwt_error() to check for errors. + * token + * @return Pointer to a jwt_t object or NULL. Generally, a NULL is unlikely. The + * object should be checked with jwt_error() to check for errors. The jwt_t + * must be freed by the caller with jwt_free(). */ JWT_EXPORT jwt_t *jwt_verify(const char *token, jwt_config_t *config); @@ -508,19 +665,20 @@ jwt_t *jwt_verify(const char *token, jwt_config_t *config); /** * @brief Decode and verify a JWT, with user callback * - * This operates the same as @ref jwt_verify, with the addition of calling - * a user defined callback function between the decode and verification step. - * This allows the user to perform some extra verification, and even provide a - * key after decoding (e.g. to match a ``"kid"``). + * This operates the same as jwt_verify(), with the addition of calling a + * user-defined function between the parsing and verification step. This allows + * the user to perform some extra verification, and even provide a key after + * (e.g. to match a ``"kid"``). * * The callback function is performed after initial parsing of the head and * body parts of the token, but before verification. The callback can then * inspect portions of the JWT, update the config (e.g. to set an alg or a - * jw_key). + * key). None of the rules in jwt_verify() apply until after this callback + * function has completed. * - * The callback function can return non-zero to stop processing immediately. - * If the callback function returns zero, it does not mean that further - * verification will succeed. All aspects of jwt_verify() must still be + * The callback function can return non-zero to stop processing immediately. If + * the callback function returns zero, it does not mean that further + * verification will succeed. All aspects of verification must still be * followed. * * @param token Pointer to a nil terminated JWT string @@ -540,60 +698,42 @@ jwt_t *jwt_verify_wcb(const char *token, jwt_config_t *config, */ /** - * @defgroup jwt_head_grant_grp Grants and Headers + * @defgroup jwt_setget_grp Headers, Claims and Grants * - * These functions allow you to add, get, and delete items in the headers and - * grants of a JWT that is being prepared for encoding. - * @{ - */ - -/** - * @brief Value types for grants and headers - */ -typedef enum { - JWT_VALUE_NONE = 0, /**< No type (do not use this) */ - JWT_VALUE_INT, /**< Integer */ - JWT_VALUE_STR, /**< String */ - JWT_VALUE_BOOL, /**< Boolean */ - JWT_VALUE_JSON, /**< JSON String (object format ``{}``) */ - JWT_VALUE_INVALID, /**< Invalid (used internally) */ -} jwt_value_type_t; - -/** - * @brief Error values for header and grant requests - */ -typedef enum { - JWT_VALUE_ERR_NONE = 0, /**< No error, success */ - JWT_VALUE_ERR_EXIST, /**< Item exists (when adding) */ - JWT_VALUE_ERR_NOEXIST, /**< Item doesn't exist (when getting) */ - JWT_VALUE_ERR_TYPE, /**< Item is not of the type requested */ - JWT_VALUE_ERR_INVALID, /**< Invalid request (general error) */ - JWT_VALUE_ERR_NOMEM, /**< Memory allocation error */ -} jwt_value_error_t; - -/** - * @brief Data type for get and add actions for JWT headers and grants + * When dealing with JWT there are 3 main topics. Each has specific expectations + * depending if you are producing or consuming JWT. * - * This is used for both add and get requests. Specific rules for each type is - * described in more detail for the add and get requests. + * @par Headers + * Generally, you do not need to worry about the header of a JWT, except in + * special (and usually custom) applications. For the most part, you will never + * access this part of a JWT. Internally, LibJWT is mostly concerned about the + * ``typ`` attribute being ``JWT`` and the ``alg`` attribute. LibJWT will add + * both of these when creating a token, and parse both when verifying them. * - * @note There are helper macros to simplify settng this structure properly and - * reducing common mistakes. See the jwt_set_{ADD,GET}_{INT,STR,BOOL,JSON} - * definitions. + * The functionality to get or add header values is mostly for convenience and + * special applications. + * + * @par Claims + * Claims are contained in the payload of a JWT. These are attributes that the + * JWT claims to have access to. It could be a hostname, access privileges, or + * even timestamps that ensure the token hasn't expired. + * + * Claims are verified by LibJWT for known types (e.g. ``nbf`` or ``exp``). + * Other claims can be enforced using the jwt_config_t structure. In this way, a + * single jwt_config_t can be created to repeat verification of tokens using a + * set of rules defined once. + * + * There is also a function to read the claims from the JWT after parsing. There + * is no method for modifying claims of a jwt_t. + * + * @par Grants + * Grants are defined as the attributes you give to a JWT you are creating. It + * + * Claims are set in the jwt_config_t structure and used to generate tokens. In + * this way, the jwt_config_t can be reused to create tokens on demand from a + * known source + * @{ */ -typedef struct { - jwt_value_type_t type; - char *name; - union { - long int_val; - const char *str_val; - int bool_val; - char *json_val; - }; - int replace; - int pretty; - jwt_value_error_t error; -} jwt_value_t; /** * @brief Add a value to the header of a JWT @@ -814,31 +954,16 @@ jwt_value_error_t jwt_grant_del(jwt_t *jwt, const char *header); /** * @} - * @noop jwt_head_grant_grp + * @noop jwt_setget_grp */ /** * @defgroup jwt_alg_grp Algorithms - * Set and check algorithms and algorithm specific values. * - * When working with functions that require a key, the underlying library - * takes care to scrub memory when the key is no longer used (e.g. when - * calling jwt_free() or when changing the algorithm, the old key, if it - * exists, is scrubbed). + * Utility functions to convert between string and type for ``alg`` * @{ */ -/** - * Get the jwt_alg_t set for this JWT object. - * - * Returns the jwt_alg_t type for this JWT object. - * - * @param jwt Pointer to a JWT object. - * @returns Returns a jwt_alg_t type for this object. - */ -JWT_EXPORT -jwt_alg_t jwt_get_alg(const jwt_t *jwt); - /** * Convert alg type to it's string representation. * @@ -878,7 +1003,12 @@ jwt_alg_t jwt_str_alg(const char *alg); */ /** - * @defgroup jwks_core_grp JSON Web Key Management + * @defgroup jwks_grp JSON Web Keys + * @{ + */ + +/** + * @defgroup jwks_core_grp JWK Management * * Functions to handle JSON that represents JWK and JWKS for use in validating * or signing JWT objects. @@ -1233,311 +1363,9 @@ int jwks_item_free_all(jwk_set_t *jwk_set); * @noop jwks_item_grp */ -/** @ingroup jwt_grp - * @defgroup jwt_valid_grp Validation Functions - * These functions allow you to define requirements for JWT validation. - * - * The most basic validation is that the JWT uses the expected algorithm. - * - * When replicating claims in header (usually for encrypted JWT), validation - * tests that they match claims in the body (iss, sub, aud). - * - * Time-based claims can also be validated (nbf, exp). - * - * Finally, validation can test that claims be present and have certain value. - * - * @{ - */ - -/** - * Validate a JWT object with a validation object. - * - * @param jwt Pointer to a JWT object. - * @param jwt_valid Pointer to a JWT validation object. - * - * @return bitwide OR if possible validation errors or 0 on success - */ -JWT_EXPORT -jwt_valid_exception_t jwt_validate(jwt_t *jwt, jwt_valid_t *jwt_valid); - -/** - * Allocate a new, JWT validation object. - * - * This is used to create a new object for a JWT validation. After you have - * finished with the object, use jwt_valid_free() to clean up the memory used by - * it. - * - * @param jwt_valid Pointer to a JWT validation object pointer. Will be allocated - * on success. - * @param alg A valid jwt_alg_t specifier. - * @return 0 on success, valid errno otherwise. - */ -JWT_EXPORT -int jwt_valid_new(jwt_valid_t **jwt_valid, jwt_alg_t alg); - -/** - * Free a JWT validation object and any other resources it is using. - * - * After calling, the JWT validation object referenced will no longer be valid - * and its memory will be freed. - * - * @param jwt_valid Pointer to a JWT validation object previously created with - * jwt_valid_new(). - */ -JWT_EXPORT -void jwt_valid_free(jwt_valid_t *jwt_valid); - -/** - * Return the status string for the validation object. - * - * The status of validation object is primarily for describing the reason - * jwt_validate() failed. - * - * @param jwt_valid Pointer to a JWT validation object. - * @return Returns current validation status as a bitwise OR of possible - * errors, or 0 if validation is currently successful. - */ -JWT_EXPORT -jwt_valid_exception_t jwt_valid_get_status(jwt_valid_t *jwt_valid); - -/** - * Return the nbf_leeway value set. - * - * @param jwt_valid Pointer to a JWT validation object. - * @return Returns current nbf_leeway value - */ -JWT_EXPORT -time_t jwt_valid_get_nbf_leeway(jwt_valid_t *jwt_valid); - -/** - * Return the exp_leeway value set. - * - * @param jwt_valid Pointer to a JWT validation object. - * @return Returns current exp_leeway value - */ -JWT_EXPORT -time_t jwt_valid_get_exp_leeway(jwt_valid_t *jwt_valid); - -/** - * Add a new string grant requirement to this JWT validation object. - * - * @param jwt_valid Pointer to a JWT validation object. - * @param grant String containing the name of the grant to add. - * @param val String containing the value to be saved for grant. Can be - * an empty string, but cannot be NULL. - * @return Returns 0 on success, valid errno otherwise. - * - * Note, this only allows for string based grants. If you wish to add - * integer grants, then use jwt_valid_add_grant_int(). If you wish to add more - * complex grants (e.g. an array), then use jwt_valid_add_grants_json(). - */ -JWT_EXPORT -int jwt_valid_add_grant(jwt_valid_t *jwt_valid, const char *grant, const char *val); - -/** - * Return the value of a string required grant. - * - * Returns the string value for a grant (e.g. "iss"). If it does not exist, - * NULL will be returned. - * - * @param jwt_valid Pointer to a JWT validation object. - * @param grant String containing the name of the grant to return a value - * for. - * @return Returns a string for the value, or NULL when not found. - * - * Note, this will only return grants with JSON string values. Use - * jwt_valid_get_grants_json() to get the JSON representation of more complex - * values (e.g. arrays) or use jwt_valid_get_grant_int() to get simple integer - * values. - */ -JWT_EXPORT -const char *jwt_valid_get_grant(jwt_valid_t *jwt_valid, const char *grant); - -/** - * Add a new integer grant requirement to this JWT validation object. - * - * @param jwt_valid Pointer to a JWT validation object. - * @param grant String containing the name of the grant to add. - * @param val int containing the value to be saved for grant. - * @return Returns 0 on success, valid errno otherwise. - * - * @remark This only allows for integer based grants. If you wish to add - * string grants, then use jwt_valid_add_grant(). If you wish to add more - * complex grants (e.g. an array), then use jwt_valid_add_grants_json(). - */ -JWT_EXPORT -int jwt_valid_add_grant_int(jwt_valid_t *jwt_valid, const char *grant, long val); - -/** - * Return the value of an integer required grant. - * - * Returns the int value for a grant (e.g. "exp"). If it does not exist, - * 0 will be returned. - * - * @param jwt_valid Pointer to a JWT validation object. - * @param grant String containing the name of the grant to return a value - * for. - * @return Returns an int for the value. Sets errno to ENOENT when not - * found. - * - * @remark This will only return grants with JSON integer values. Use - * jwt_valid_get_grants_json() to get the JSON representation of more complex - * values (e.g. arrays) or use jwt_valid_get_grant() to get string values. - */ -JWT_EXPORT -long jwt_valid_get_grant_int(jwt_valid_t *jwt_valid, const char *grant); - -/** - * Add a new boolean required grant to this JWT validation object. - * - * Creates a new grant for this object. The string for grant - * is copied internally, so do not require that the pointer or string - * remain valid for the lifetime of this object. It is an error if you - * try to add a grant that already exists. - * - * @param jwt_valid Pointer to a JWT validation object. - * @param grant String containing the name of the grant to add. - * @param val boolean containing the value to be saved for grant. - * @return Returns 0 on success, valid errno otherwise. - * - * @remark This only allows for boolean based grants. If you wish to add - * string grants, then use jwt_valid_add_grant(). If you wish to add more - * complex grants (e.g. an array), then use jwt_valid_add_grants_json(). - */ -JWT_EXPORT -int jwt_valid_add_grant_bool(jwt_valid_t *jwt_valid, const char *grant, int val); - -/** - * Return the value of an boolean required grant. - * - * Returns the int value for a grant (e.g. "exp"). If it does not exist, - * 0 will be returned. - * - * @param jwt_valid Pointer to a JWT validation object. - * @param grant String containing the name of the grant to return a value - * for. - * @return Returns a boolean for the value. Sets errno to ENOENT when not - * found. - * - * @remark This will only return grants with JSON boolean values. Use - * jwt_valid_get_grants_json() to get the JSON representation of more complex - * values (e.g. arrays) or use jwt_valid_get_grant() to get string values. - */ -JWT_EXPORT -int jwt_valid_get_grant_bool(jwt_valid_t *jwt_valid, const char *grant); - -/** - * Add required grants from a JSON encoded object string. - * - * Loads a grant from an existing JSON encoded object string. Overwrites - * existing grant. - * - * @param jwt_valid Pointer to a JWT validation object. - * @param json String containing a JSON encoded object of grants. - * @return Returns 0 on success, valid errno otherwise. - */ -JWT_EXPORT -int jwt_valid_add_grants_json(jwt_valid_t *jwt_valid, const char *json); - -/** - * Return the value of a grant as JSON encoded object string. - * - * Returns the JSON encoded string value for a grant (e.g. "iss"). If it - * does not exist, NULL will be returned. - * - * @param jwt_valid Pointer to a JWT validation object. - * @param grant String containing the name of the grant to return a value - * for. - * @return Returns a string for the value, or NULL when not found. The - * returned string must be freed by the caller. - */ -JWT_EXPORT -char* jwt_valid_get_grants_json(jwt_valid_t *jwt_valid, const char *grant); - -/** - * Delete a grant from this JWT object. - * - * Deletes the named grant from this object. It is not an error if there - * is no grant matching the passed name. If grant is NULL, then all grants - * are deleted from this JWT. - * - * @param jwt_valid Pointer to a JWT validation object. - * @param grant String containing the name of the grant to delete. If this - * is NULL, then all grants are deleted. - * @return Returns 0 on success, valid errno otherwise. - */ -JWT_EXPORT -int jwt_valid_del_grants(jwt_valid_t *jwt_valid, const char *grant); - -/** - * Set the time for which expires and not-before claims should be evaluated. - * - * @param jwt_valid Pointer to a JWT validation object. - * @param now Time to use when considering nbf and exp claims. - * @return Returns 0 on success, valid errno otherwise. - * - * @remark jwt_validate() will not fail based on time if no expires or - * not-before claims exist in a JWT object. - */ -JWT_EXPORT -int jwt_valid_set_now(jwt_valid_t *jwt_valid, const time_t now); - -/** - * Set the nbf_leeway value as defined in: https://www.rfc-editor.org/rfc/rfc7519#section-4.1.5. - * - * @param jwt_valid Pointer to a JWT validation object. - * @param nbf_leeway leeway for nbf value. - * @return Returns 0 on success, valid errno otherwise. - * - */ -JWT_EXPORT -int jwt_valid_set_nbf_leeway(jwt_valid_t *jwt_valid, const time_t nbf_leeway); - -/** - * Set the exp_leeway value as defined in: https://www.rfc-editor.org/rfc/rfc7519#section-4.1.4. - * - * @param jwt_valid Pointer to a JWT validation object. - * @param exp_leeway leeway for exp value. - * @return Returns 0 on success, valid errno otherwise. - * - */ -JWT_EXPORT -int jwt_valid_set_exp_leeway(jwt_valid_t *jwt_valid, const time_t exp_leeway); - -/** - * Set validation for replicated claims in headers. - * - * When set, validation tests for presence of iss, sub, aud in jwt headers and - * tests match for same claims in body. - * - * @param jwt_valid Pointer to a JWT validation object. - * @param hdr When true, test header claims - * @return Returns 0 on success, valid errno otherwise. - * - * @remark jwt_validate() will not fail if iss, sub, aud are not present in JWT - * header or body. - */ -JWT_EXPORT -int jwt_valid_set_headers(jwt_valid_t *jwt_valid, int hdr); - -/** - * Parses exceptions and returns a comma delimited and human-readable string. - * - * The returned string must be freed by the caller. - * - * @remark This string is currently en-US ASCII only. Language support will come in the - * future. - * - * @param exceptions Integer containing the exception flags. - * @return A null terminated string on success, NULL on error with errno - * set appropriately. - */ -JWT_EXPORT -char *jwt_exception_str(jwt_valid_exception_t exceptions); - /** * @} - * @noop jwt_valid_grp + * @noop jwks_grp */ /** @@ -1596,7 +1424,7 @@ void jwt_get_alloc(jwt_malloc_t *pmalloc, jwt_realloc_t *prealloc, */ /** - * @defgroup jwt_crypto_grp Cryptographic Operations + * @defgroup jwt_crypto_grp Cryptographic Ops * Functions used to set and get which crypto operations are used * * LibJWT supports several crypto libraries, mainly "openssl" and "gnutls". @@ -1669,7 +1497,7 @@ int jwt_crypto_ops_supports_jwk(void); /** * @} - * @noop advanced_grp + * @noop jwt_advanced_grp */ #ifdef __cplusplus diff --git a/libjwt/gnutls/sign-verify.c b/libjwt/gnutls/sign-verify.c index de293890..4f1d25b8 100644 --- a/libjwt/gnutls/sign-verify.c +++ b/libjwt/gnutls/sign-verify.c @@ -31,11 +31,11 @@ static int gnutls_sign_sha_hmac(jwt_t *jwt, char **out, unsigned int *len, void *key; size_t key_len; - if (!ops_compat(jwt->jw_key, JWT_CRYPTO_OPS_OPENSSL)) + if (!ops_compat(jwt->key, JWT_CRYPTO_OPS_OPENSSL)) return 1; // LCOV_EXCL_LINE - key = jwt->jw_key->oct.key; - key_len = jwt->jw_key->oct.len; + key = jwt->key->oct.key; + key_len = jwt->key->oct.len; switch (jwt->alg) { case JWT_ALG_HS256: @@ -102,13 +102,13 @@ static int gnutls_sign_sha_pem(jwt_t *jwt, char **out, unsigned int *len, return 1; } - if (jwt->jw_key->pem == NULL) + if (jwt->key->pem == NULL) return 1; // LCOV_EXCL_LINE gnutls_privkey_t privkey; gnutls_datum_t key_dat = { - (unsigned char *)jwt->jw_key->pem, - strlen(jwt->jw_key->pem) + (unsigned char *)jwt->key->pem, + strlen(jwt->key->pem) }; gnutls_datum_t body_dat = { (unsigned char *)str, @@ -191,9 +191,6 @@ static int gnutls_sign_sha_pem(jwt_t *jwt, char **out, unsigned int *len, if (pk_alg == GNUTLS_PK_EDDSA_ED25519) alg = GNUTLS_DIG_SHA512; else if (pk_alg == GNUTLS_PK_EDDSA_ED448) { - /* Not implemented in GnuTLS yet, will fail XXX */ - jwt_write_error(jwt, - "ED448 is not yet implemented in GnuTLS"); alg = GNUTLS_DIG_SHAKE_256; } else { jwt_write_error(jwt, "Unknown EdDSA key type"); @@ -211,15 +208,12 @@ static int gnutls_sign_sha_pem(jwt_t *jwt, char **out, unsigned int *len, } if (pk_alg != gnutls_privkey_get_pk_algorithm(privkey, NULL)) { - jwt_write_error(jwt, "Alg mismatch with key during signing"); + jwt_write_error(jwt, "Alg mismatch with key during signing: %d", + pk_alg); ret = 1; goto sign_clean_privkey; } - /* XXX Get curve name for ES256K case and make sure it's secp256k1 */ - - /* XXX Get EC curve bits and make sure it matches ES* alg type */ - /* Sign data */ if (gnutls_privkey_sign_data(privkey, alg, 0, &body_dat, &sig_dat)) { // LCOV_EXCL_START @@ -317,12 +311,12 @@ static int gnutls_verify_sha_pem(jwt_t *jwt, const char *head, int alg, ret = 0, sig_len; unsigned char *sig = NULL; - if (jwt->jw_key->pem == NULL) + if (jwt->key->pem == NULL) return 1; gnutls_datum_t cert_dat = { - (unsigned char *)jwt->jw_key->pem, - strlen(jwt->jw_key->pem) + (unsigned char *)jwt->key->pem, + strlen(jwt->key->pem) }; if (jwt->alg == JWT_ALG_ES256K) { @@ -332,6 +326,48 @@ static int gnutls_verify_sha_pem(jwt_t *jwt, const char *head, // LCOV_EXCL_STOP } + if (gnutls_pubkey_init(&pubkey)) { + // LCOV_EXCL_START + ret = 1; + goto verify_clean_sig; + // LCOV_EXCL_STOP + } + + ret = gnutls_pubkey_import(pubkey, &cert_dat, GNUTLS_X509_FMT_PEM); + if (ret) { + gnutls_privkey_t privkey; + + /* Try loading as a private key, and extracting the pubkey. This is pefectly + * legit. A JWK can have a private key with key_ops of SIGN and VERIFY. */ + if (gnutls_privkey_init(&privkey)) { + // LCOV_EXCL_START + ret = 1; + goto verify_clean_pubkey; + // LCOV_EXCL_STOP + } + + /* Try loading as a private key, and extracting the pubkey */ + if (gnutls_privkey_import_x509_raw(privkey, &cert_dat, + GNUTLS_X509_FMT_PEM, + NULL, 0)) { + // LCOV_EXCL_START + ret = 1; + gnutls_privkey_deinit(privkey); + goto verify_clean_pubkey; + // LCOV_EXCL_STOP + } + + ret = gnutls_pubkey_import_privkey(pubkey, privkey, 0, 0); + gnutls_privkey_deinit(privkey); + + if (ret) { + // LCOV_EXCL_START + ret = 1; + goto verify_clean_pubkey; + // LCOV_EXCL_STOP + } + } + switch (jwt->alg) { /* RSA */ case JWT_ALG_RS256: @@ -369,58 +405,28 @@ static int gnutls_verify_sha_pem(jwt_t *jwt, const char *head, /* EdDSA */ case JWT_ALG_EDDSA: - alg = GNUTLS_SIGN_EDDSA_ED25519; + alg = gnutls_pubkey_get_pk_algorithm(pubkey, NULL); + if (alg == GNUTLS_PK_EDDSA_ED25519) + alg = GNUTLS_SIGN_EDDSA_ED25519; + else if (alg == GNUTLS_PK_EDDSA_ED448) { + alg = GNUTLS_SIGN_EDDSA_ED448; + } else { + jwt_write_error(jwt, "Unknown EdDSA key type"); + ret = 1; + goto verify_clean_pubkey; + } break; default: - return 1; // LCOV_EXCL_LINE + ret = 1; + goto verify_clean_pubkey; } sig = (unsigned char *)jwt_base64uri_decode(sig_b64, &sig_len); - if (sig == NULL) - return 1; - - if (gnutls_pubkey_init(&pubkey)) { - // LCOV_EXCL_START + if (sig == NULL) { ret = 1; - goto verify_clean_sig; - // LCOV_EXCL_STOP - } - - ret = gnutls_pubkey_import(pubkey, &cert_dat, GNUTLS_X509_FMT_PEM); - if (ret) { - gnutls_privkey_t privkey; - - /* Try loading as a private key, and extracting the pubkey. This is pefectly - * legit. A JWK can have a private key with key_ops of SIGN and VERIFY. */ - if (gnutls_privkey_init(&privkey)) { - // LCOV_EXCL_START - ret = 1; - goto verify_clean_pubkey; - // LCOV_EXCL_STOP - } - - /* Try loading as a private key, and extracting the pubkey */ - if (gnutls_privkey_import_x509_raw(privkey, &cert_dat, - GNUTLS_X509_FMT_PEM, - NULL, 0)) { - // LCOV_EXCL_START - ret = 1; - gnutls_privkey_deinit(privkey); - goto verify_clean_pubkey; - // LCOV_EXCL_STOP - } - - ret = gnutls_pubkey_import_privkey(pubkey, privkey, 0, 0); - gnutls_privkey_deinit(privkey); - - if (ret) { - // LCOV_EXCL_START - ret = 1; - goto verify_clean_pubkey; - // LCOV_EXCL_STOP - } + goto verify_clean_pubkey; } /* Rebuild signature using r and s extracted from sig when jwt->alg diff --git a/libjwt/jwks.c b/libjwt/jwks.c index 0b77b757..d0403566 100644 --- a/libjwt/jwks.c +++ b/libjwt/jwks.c @@ -225,7 +225,7 @@ const char *jwks_item_curve(const jwk_item_t *item) const char *jwks_item_kid(const jwk_item_t *item) { - return item->kid[0] ? item->kid : NULL; + return item->kid; } jwt_alg_t jwks_item_alg(const jwk_item_t *item) diff --git a/libjwt/jwt-common.c b/libjwt/jwt-common.c new file mode 100644 index 00000000..d6029c1b --- /dev/null +++ b/libjwt/jwt-common.c @@ -0,0 +1,359 @@ +/* Copyright (C) 2015-2025 maClara, LLC + This file is part of the JWT C Library + + SPDX-License-Identifier: MPL-2.0 + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include + +#include + +#include "base64.h" + +#include "jwt-private.h" + +#ifdef JWT_BUILDER +#define jwt_common_t jwt_builder_t +#define FUNC(__x) jwt_builder_##__x +#define CLAIMS_DEF JWT_BUILDER_CLAIMS +#endif +#ifdef JWT_CHECKER +#define jwt_common_t jwt_checker_t +#define FUNC(__x) jwt_checker_##__x +#define CLAIMS_DEF JWT_CHECKER_CLAIMS +#endif + +#ifndef jwt_common_t +#error Must have target defined +#endif + +void FUNC(free)(jwt_common_t *__cmd) +{ + if (__cmd == NULL) + return; + + json_decref(__cmd->c.payload); + json_decref(__cmd->c.headers); + + memset(__cmd, 0, sizeof(*__cmd)); + + jwt_freemem(__cmd); +} + +jwt_common_t *FUNC(new)(void) +{ + jwt_common_t *__cmd = jwt_malloc(sizeof(*__cmd)); + + if (__cmd == NULL) + return NULL; + + memset(__cmd, 0, sizeof(*__cmd)); + + __cmd->c.payload = json_object(); + __cmd->c.headers = json_object(); + __cmd->c.claims = CLAIMS_DEF; + + if (!__cmd->c.payload || !__cmd->c.headers) + jwt_freemem(__cmd); + + return __cmd; +} + +int FUNC(setkey_check)(jwt_common_t *__cmd, const jwt_alg_t alg, + const jwk_item_t *key) +{ + if (__cmd == NULL) + return 1; + +#ifdef JWT_BUILDER + if (key && !key->is_private_key) { + jwt_write_error(__cmd, "Signing requires a private key"); + return 1; + } +#endif + /* TODO: Check key_ops and use */ + + if (key == NULL) { + if (alg == JWT_ALG_NONE) + return 0; + + jwt_write_error(__cmd, "Cannot set alg without a key"); + } else if (key->alg == JWT_ALG_NONE) { + if (alg != JWT_ALG_NONE) + return 0; + + jwt_write_error(__cmd, "Key provided, but could not find alg"); + } else { + if (alg == JWT_ALG_NONE) + return 0; + + if (alg == key->alg) + return 0; + + jwt_write_error(__cmd, "Alg mismatch"); + } + + return 1; +} + +int FUNC(setkey)(jwt_common_t *__cmd, const jwt_alg_t alg, + const jwk_item_t *key) +{ + if (FUNC(setkey_check)(__cmd, alg, key)) + return 1; + + __cmd->c.alg = alg; + __cmd->c.key = key; + + return 0; +} + +int FUNC(error)(const jwt_common_t *__cmd) +{ + if (__cmd == NULL) + return 1; + + return __cmd->error ? 1 : 0; +} + +const char *FUNC(error_msg)(const jwt_common_t *__cmd) +{ + if (__cmd == NULL) + return NULL; + + return __cmd->error_msg; +} + +void FUNC(error_clear)(jwt_common_t *__cmd) +{ + if (__cmd == NULL) + return; + + __cmd->error = 0; + __cmd->error_msg[0] = '\0'; +} + +int FUNC(setclaims)(jwt_common_t *__cmd, jwt_claims_t claims) +{ + if (claims == JWT_CLAIM_DEFAULT) { + __cmd->c.claims = CLAIMS_DEF; + return 0; + } + + if (claims == JWT_CLAIM_NONE) { + __cmd->c.claims = claims; + return 0; + } + + if (claims & JWT_CLAIM_NONE) { + jwt_write_error(__cmd, + "NONE claim cannot be set with others"); + return 1; + } + + if (claims & ~JWT_CLAIMS_ALL) { + jwt_write_error(__cmd, "Unknown claim(s) in set"); + return 1; + } + + __cmd->c.claims = claims; + + return 0; +} + +int FUNC(setcb)(jwt_common_t *__cmd, jwt_callback_t cb, void *ctx) +{ + if (__cmd == NULL) + return 1; + + if (cb == NULL && ctx != NULL) { + jwt_write_error(__cmd, "Setting ctx without a cb wont work"); + return 1; + } + + __cmd->c.cb = cb; + __cmd->c.cb_ctx = ctx; + + return 0; +} + +typedef enum { + __HEADER, + __CLAIM, +} _setget_type_t; + +typedef jwt_value_error_t (*__doer_t)(json_t *, jwt_value_t *); + +static jwt_value_error_t __run_it(jwt_common_t *__cmd, _setget_type_t type, + jwt_value_t *value, __doer_t doer) +{ + json_t *which = NULL; + + if (!__cmd || !value) { + if (value) + return value->error = JWT_VALUE_ERR_INVALID; + return JWT_VALUE_ERR_INVALID; + } + + switch (type) { + case __HEADER: + which = __cmd->c.headers; + break; + case __CLAIM: + which = __cmd->c.payload; + break; + default: + return value->error = JWT_VALUE_ERR_INVALID; + } + + return doer(which, value); +} + +/* Claims */ +jwt_value_error_t FUNC(claim_get)(jwt_common_t *__cmd, jwt_value_t *value) +{ + return __run_it(__cmd, __CLAIM, value, __getter); +} + +jwt_value_error_t FUNC(claim_add)(jwt_common_t *__cmd, jwt_value_t *value) +{ + return __run_it(__cmd, __CLAIM, value, __adder); +} + +jwt_value_error_t FUNC(claim_del)(jwt_common_t *__cmd, const char *header) +{ + if (!__cmd) + return JWT_VALUE_ERR_INVALID; + return __deleter(__cmd->c.payload, header); +} + +/* Headers */ +jwt_value_error_t FUNC(header_get)(jwt_common_t *__cmd, jwt_value_t *value) +{ + return __run_it(__cmd, __HEADER, value, __getter); +} + +jwt_value_error_t FUNC(header_add)(jwt_common_t *__cmd, jwt_value_t *value) +{ + return __run_it(__cmd, __HEADER, value, __adder); +} + +jwt_value_error_t FUNC(header_del)(jwt_common_t *__cmd, const char *header) +{ + if (!__cmd) + return JWT_VALUE_ERR_INVALID; + return __deleter(__cmd->c.headers, header); +} + +#ifdef JWT_CHECKER +int FUNC(verify)(jwt_common_t *__cmd, const char *token) +{ + JWT_CONFIG_DECLARE(config); + unsigned int payload_len; + jwt_auto_t *jwt = NULL; + + if (__cmd == NULL) + return 1; + + if (token == NULL || !strlen(token)) { + jwt_write_error(__cmd, "Must pass a token"); + return 1; + } + + jwt = jwt_new(); + if (jwt == NULL) { + jwt_write_error(__cmd, "Could not allocate JWT object"); + return 1; + } + + /* First parsing pass, error will be set for us */ + if (jwt_parse(jwt, token, &payload_len)) { + jwt_copy_error(__cmd, jwt); + return 1; + }; + + config.key = __cmd->c.key; + config.alg = __cmd->c.alg; + config.ctx = __cmd->c.cb_ctx; + + /* Let the user handle this and update config */ + if (__cmd->c.cb && __cmd->c.cb(jwt, &config)) { + jwt_write_error(__cmd, "User callback returned error"); + return 1; + } + + /* Callback may have changed this */ + if (FUNC(setkey)(__cmd, config.alg, config.key)) + return 1; + + jwt->key = config.key; + + /* Finish it up */ + jwt = jwt_verify_complete(jwt, &config, token, payload_len); + + /* Copy any errors back */ + jwt_copy_error(__cmd, jwt); + + return __cmd->error; +} +#endif + +#ifdef JWT_BUILDER +char *FUNC(generate)(jwt_common_t *__cmd) +{ + JWT_CONFIG_DECLARE(config); + jwt_auto_t *jwt = NULL; + char *out = NULL; + jwt_value_t jval; + + if (__cmd == NULL) + return NULL; + + jwt = jwt_malloc(sizeof(*jwt)); + if (jwt == NULL) + return NULL; + + memset(jwt, 0, sizeof(*jwt)); + + jwt->headers = json_deep_copy(__cmd->c.headers); + jwt->grants = json_deep_copy(__cmd->c.payload); + + /* Our internal work first */ + if (__cmd->c.claims & JWT_CLAIM_IAT) { + jwt_set_ADD_INT(&jval, "iat", (long)time(NULL)); + jval.replace = 1; + jwt_grant_add(jwt, &jval); + } + + config.alg = __cmd->c.alg; + if (config.alg == JWT_ALG_NONE && __cmd->c.key) + config.alg = __cmd->c.key->alg; + config.key = __cmd->c.key; + config.ctx = __cmd->c.cb_ctx; + + /* Let the callback do it's thing */ + if (__cmd->c.cb && __cmd->c.cb(jwt, &config)) { + jwt_write_error(__cmd, "User callback returned error"); + return NULL; + } + + /* Callback may have changed this */ + if (FUNC(setkey_check)(__cmd, config.alg, config.key)) { + jwt_write_error(__cmd, "Algorithm and key returned by callback invalid"); + return NULL; + } + + jwt->alg = config.alg; + jwt->key = config.key; + + out = jwt_encode_str(jwt); + __cmd->error = jwt->error; + strcpy(__cmd->error_msg, jwt->error_msg); + + return out; +} +#endif diff --git a/libjwt/jwt-encode.c b/libjwt/jwt-encode.c index 3ba553e0..eb9933fa 100644 --- a/libjwt/jwt-encode.c +++ b/libjwt/jwt-encode.c @@ -12,40 +12,18 @@ #include -/* https://github.com/zhicheng/base64 */ #include "base64.h" #include "jwt-private.h" -#define APPEND_STR(__buf, __str) do { \ - if (__append_str(__buf, __str)) \ - return 1; \ -} while (0) - -static int write_js(const json_t *js, char **buf, int pretty) +static int write_js(const json_t *js, char **buf) { - /* Sort keys for repeatability */ - size_t flags = JSON_SORT_KEYS; - char_auto *serial = NULL; - - if (pretty) { - APPEND_STR(buf, "\n"); - flags |= JSON_INDENT(4); - } else { - flags |= JSON_COMPACT; - } - - serial = json_dumps(js, flags); + *buf = json_dumps(js, JSON_SORT_KEYS | JSON_COMPACT); - APPEND_STR(buf, serial); - - if (pretty) - APPEND_STR(buf, "\n"); - - return 0; + return *buf == NULL ? 1 : 0; } -static int jwt_write_head(jwt_t *jwt, char **buf, int pretty) +static int jwt_write_head(jwt_t *jwt, char **buf) { jwt_value_t jval; @@ -72,12 +50,7 @@ static int jwt_write_head(jwt_t *jwt, char **buf, int pretty) return 1; } - return write_js(jwt->headers, buf, pretty); -} - -static int jwt_write_body(jwt_t *jwt, char **buf, int pretty) -{ - return write_js(jwt->grants, buf, pretty); + return write_js(jwt->headers, buf); } static int jwt_encode(jwt_t *jwt, char **out) @@ -94,7 +67,7 @@ static int jwt_encode(jwt_t *jwt, char **out) *out = NULL; /* First the header. */ - ret = jwt_write_head(jwt, &buf, 0); + ret = jwt_write_head(jwt, &buf); if (ret) return 1; /* Encode it */ @@ -107,7 +80,7 @@ static int jwt_encode(jwt_t *jwt, char **out) } /* Now the body. */ - ret = jwt_write_body(jwt, &buf, 0); + ret = write_js(jwt->grants, &buf); if (ret) { jwt_write_error(jwt, "Error writing body"); return 1; @@ -175,19 +148,6 @@ static int jwt_encode(jwt_t *jwt, char **out) return ret; } -int jwt_encode_fp(jwt_t *jwt, FILE *fp) -{ - char_auto *str = NULL; - - if (jwt_encode(jwt, &str)) - return 1; - - if (fputs(str, fp) == EOF) - return 1; - - return 0; -} - char *jwt_encode_str(jwt_t *jwt) { char *str = NULL; diff --git a/libjwt/jwt-private.h b/libjwt/jwt-private.h index 611663f2..69e9e68e 100644 --- a/libjwt/jwt-private.h +++ b/libjwt/jwt-private.h @@ -28,45 +28,69 @@ # endif #endif +#define JWT_CONFIG_DECLARE(__name) \ + jwt_config_t __name = { NULL, JWT_ALG_NONE, NULL} + +#define JWT_ERR_LEN 256 + JWT_NO_EXPORT extern struct jwt_crypto_ops *jwt_ops; -/* This can be used for jwt_t, jwk_set_t and jwk_item_t */ +/* This can be used on anything with an error and error_msg field */ #define jwt_write_error(__obj, __fmt, __args...) \ ({ \ - if (!strlen(__obj->error_msg)) \ - snprintf(__obj->error_msg, \ - sizeof(__obj->error_msg), \ + if (!strlen((__obj)->error_msg)) \ + snprintf((__obj)->error_msg, \ + sizeof((__obj)->error_msg), \ __fmt, ##__args); \ - __obj->error = 1; \ + (__obj)->error = 1; \ }) -struct jwt { - jwt_alg_t alg; +#define jwt_copy_error(__dst, __src) \ +({ \ + strcpy((__dst)->error_msg, (__src)->error_msg); \ + (__dst)->error = (__src)->error; \ +}) - json_t *grants; +/******************************/ + +struct jwt_common { + jwt_alg_t alg; + const jwk_item_t *key; + json_t *payload; json_t *headers; + jwt_claims_t claims; + jwt_callback_t cb; + void *cb_ctx; +}; - const jwk_item_t *jw_key; +struct jwt_builder { + struct jwt_common c; + int error; + char error_msg[JWT_ERR_LEN]; +}; +struct jwt_checker { + struct jwt_common c; int error; - char error_msg[256]; + char error_msg[JWT_ERR_LEN]; }; -struct jwt_valid { +/*****************************/ + +struct jwt { + const jwk_item_t *key; + json_t *grants; + json_t *headers; jwt_alg_t alg; - time_t now; - time_t nbf_leeway; - time_t exp_leeway; - int hdr; - json_t *req_grants; - jwt_valid_exception_t status; + int error; + char error_msg[JWT_ERR_LEN]; }; struct jwk_set { ll_t head; int error; - char error_msg[256]; + char error_msg[JWT_ERR_LEN]; }; /** @@ -95,7 +119,7 @@ struct jwk_item { 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 */ + char error_msg[JWT_ERR_LEN];/**< 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 */ @@ -166,6 +190,16 @@ static inline void jwt_freememp(char **mem) { } #define char_auto char __attribute__((cleanup(jwt_freememp))) +JWT_EXPORT +void jwt_free(jwt_t *jwt); +static inline void jwt_freep(jwt_t **jwt) { + if (jwt) { + jwt_free(*jwt); + *jwt = NULL; + } +} +#define jwt_auto_t jwt_t __attribute__((cleanup(jwt_freep))) + /* Helper routines to handle base64url encoding without percent padding * as defined in RFC-4648. */ JWT_NO_EXPORT @@ -191,7 +225,27 @@ int jwt_sign(jwt_t *jwt, char **out, unsigned int *len, const char *str, unsigned int str_len); JWT_NO_EXPORT -int __append_str(char **buf, const char *str); +jwt_value_error_t __deleter(json_t *which, const char *field); +JWT_NO_EXPORT +jwt_value_error_t __adder(json_t *which, jwt_value_t *value); +JWT_NO_EXPORT +jwt_value_error_t __getter(json_t *which, jwt_value_t *value); + +JWT_NO_EXPORT +int jwt_parse(jwt_t *jwt, const char *token, unsigned int *len); +JWT_NO_EXPORT +jwt_t *jwt_verify_complete(jwt_t *jwt, const jwt_config_t *config, + const char *token, unsigned int payload_len); + +JWT_NO_EXPORT +int jwt_builder_setkey_check(jwt_builder_t *builder, const jwt_alg_t alg, + const jwk_item_t *key); +JWT_NO_EXPORT +int jwt_checker_setkey_check(jwt_checker_t *checker, const jwt_alg_t alg, + const jwk_item_t *key); + +JWT_NO_EXPORT +char *jwt_encode_str(jwt_t *jwt); #define __trace() fprintf(stderr, "%s:%d\n", __func__, __LINE__) diff --git a/libjwt/jwt-validate.c b/libjwt/jwt-validate.c deleted file mode 100644 index a4a5fe9d..00000000 --- a/libjwt/jwt-validate.c +++ /dev/null @@ -1,424 +0,0 @@ -/* Copyright (C) 2015-2025 maClara, LLC - This file is part of the JWT C Library - - SPDX-License-Identifier: MPL-2.0 - This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include -#include -#include - -#include - -#include "jwt-private.h" - -static const char *get_js_string(const json_t *js, const char *key) -{ - const char *val = NULL; - json_t *js_val; - - js_val = json_object_get(js, key); - if (js_val) { - if (json_is_string(js_val)) - val = json_string_value(js_val); - else - errno = EINVAL; - } else { - errno = ENOENT; - } - - return val; -} - -static long get_js_int(const json_t *js, const char *key) -{ - long val = -1; - json_t *js_val; - - js_val = json_object_get(js, key); - if (js_val) { - if (json_is_integer(js_val)) - val = (long)json_integer_value(js_val); - else - errno = EINVAL; - } else { - errno = ENOENT; - } - - return val; -} - -static int get_js_bool(const json_t *js, const char *key) -{ - int val = -1; - json_t *js_val; - - js_val = json_object_get(js, key); - if (js_val) { - switch (json_typeof(js_val)) { - case JSON_TRUE: - val = 1; - break; - case JSON_FALSE: - val = 0; - break; - default: - errno = EINVAL; - } - } else { - errno = ENOENT; - } - return val; -} - -int jwt_valid_new(jwt_valid_t **jwt_valid, jwt_alg_t alg) -{ - if (!jwt_valid) - return EINVAL; - - *jwt_valid = jwt_malloc(sizeof(jwt_valid_t)); - if (!*jwt_valid) - return ENOMEM; // LCOV_EXCL_LINE - - memset(*jwt_valid, 0, sizeof(jwt_valid_t)); - (*jwt_valid)->alg = alg; - - (*jwt_valid)->status = JWT_VALIDATION_ERROR; - - (*jwt_valid)->nbf_leeway = 0; - (*jwt_valid)->exp_leeway = 0; - - (*jwt_valid)->req_grants = json_object(); - if (!(*jwt_valid)->req_grants) { - jwt_freemem(*jwt_valid); - return ENOMEM; - } - - return 0; -} - -void jwt_valid_free(jwt_valid_t *jwt_valid) -{ - if (!jwt_valid) - return; - - json_decref(jwt_valid->req_grants); - - jwt_freemem(jwt_valid); -} - -jwt_valid_exception_t jwt_valid_get_status(jwt_valid_t *jwt_valid) -{ - if (!jwt_valid) - return JWT_VALIDATION_ERROR; - - return jwt_valid->status; -} - -time_t jwt_valid_get_nbf_leeway(jwt_valid_t *jwt_valid) -{ - if (!jwt_valid) - return EINVAL; - - return jwt_valid->nbf_leeway; -} - -time_t jwt_valid_get_exp_leeway(jwt_valid_t *jwt_valid) -{ - if (!jwt_valid) - return EINVAL; - - return jwt_valid->exp_leeway; -} - -int jwt_valid_add_grant(jwt_valid_t *jwt_valid, const char *grant, const char *val) -{ - if (!jwt_valid || !grant || !strlen(grant) || !val) - return EINVAL; - - if (get_js_string(jwt_valid->req_grants, grant) != NULL) - return EEXIST; - - if (json_object_set_new(jwt_valid->req_grants, grant, json_string(val))) - return EINVAL; - - return 0; -} - -int jwt_valid_add_grant_int(jwt_valid_t *jwt_valid, const char *grant, long val) -{ - if (!jwt_valid || !grant || !strlen(grant)) - return EINVAL; - - if (get_js_int(jwt_valid->req_grants, grant) != -1) - return EEXIST; - - if (json_object_set_new(jwt_valid->req_grants, grant, json_integer((json_int_t)val))) - return EINVAL; - - return 0; -} - -int jwt_valid_add_grant_bool(jwt_valid_t *jwt_valid, const char *grant, int val) -{ - if (!jwt_valid || !grant || !strlen(grant)) - return EINVAL; - - if (get_js_bool(jwt_valid->req_grants, grant) != -1) - return EEXIST; - - if (json_object_set_new(jwt_valid->req_grants, grant, json_boolean(val))) - return EINVAL; - - return 0; -} - -int jwt_valid_add_grants_json(jwt_valid_t *jwt_valid, const char *json) -{ - json_auto_t *js_val = NULL; - int ret = -1; - - if (!jwt_valid) - return EINVAL; - - js_val = json_loads(json, JSON_REJECT_DUPLICATES, NULL); - - if (json_is_object(js_val)) - ret = json_object_update(jwt_valid->req_grants, js_val); - - return ret ? EINVAL : 0; -} - -char *jwt_valid_get_grants_json(jwt_valid_t *jwt_valid, const char *grant) -{ - json_t *js_val = NULL; - - errno = EINVAL; - - if (!jwt_valid) - return NULL; - - if (grant && strlen(grant)) - js_val = json_object_get(jwt_valid->req_grants, grant); - else - js_val = jwt_valid->req_grants; - - if (js_val == NULL) - return NULL; - - errno = 0; - - return json_dumps(js_val, JSON_SORT_KEYS | JSON_COMPACT | JSON_ENCODE_ANY); -} - -const char *jwt_valid_get_grant(jwt_valid_t *jwt_valid, const char *grant) -{ - if (!jwt_valid || !grant || !strlen(grant)) { - errno = EINVAL; - return NULL; - } - - errno = 0; - - return get_js_string(jwt_valid->req_grants, grant); -} - -long jwt_valid_get_grant_int(jwt_valid_t *jwt_valid, const char *grant) -{ - if (!jwt_valid || !grant || !strlen(grant)) { - errno = EINVAL; - return 0; - } - - errno = 0; - - return get_js_int(jwt_valid->req_grants, grant); -} - -int jwt_valid_get_grant_bool(jwt_valid_t *jwt_valid, const char *grant) -{ - if (!jwt_valid || !grant || !strlen(grant)) { - errno = EINVAL; - return 0; - } - - errno = 0; - - return get_js_bool(jwt_valid->req_grants, grant); -} - -int jwt_valid_set_now(jwt_valid_t *jwt_valid, const time_t now) -{ - if (!jwt_valid) - return EINVAL; - - jwt_valid->now = now; - - return 0; -} - -int jwt_valid_set_nbf_leeway(jwt_valid_t *jwt_valid, const time_t nbf_leeway) -{ - if (!jwt_valid) - return EINVAL; - - jwt_valid->nbf_leeway = nbf_leeway; - - return 0; -} - -int jwt_valid_set_exp_leeway(jwt_valid_t *jwt_valid, const time_t exp_leeway) -{ - if (!jwt_valid) - return EINVAL; - - jwt_valid->exp_leeway = exp_leeway; - - return 0; -} - -int jwt_valid_set_headers(jwt_valid_t *jwt_valid, int hdr) -{ - if (!jwt_valid) - return EINVAL; - - jwt_valid->hdr = hdr; - - return 0; -} - -int jwt_valid_del_grants(jwt_valid_t *jwt_valid, const char *grant) -{ - if (!jwt_valid) - return EINVAL; - - if (grant == NULL || !strlen(grant)) - json_object_clear(jwt_valid->req_grants); - else - json_object_del(jwt_valid->req_grants, grant); - - return 0; -} - -#define _SET_AND_RET(__v, __e) do { \ - __v->status |= __e; \ - return __v->status; \ -} while (0) - -jwt_valid_exception_t jwt_validate(jwt_t *jwt, jwt_valid_t *jwt_valid) -{ - const char *jwt_hdr_str, *jwt_body_str, *req_grant; - json_t *js_val_1, *js_val_2; - time_t t; - - if (!jwt_valid) - return JWT_VALIDATION_ERROR; - - if (!jwt) { - jwt_valid->status = JWT_VALIDATION_ERROR; - return jwt_valid->status; - } - - jwt_valid->status = JWT_VALIDATION_SUCCESS; - - /* Validate algorithm */ - if (jwt_valid->alg != jwt_get_alg(jwt)) - jwt_valid->status |= JWT_VALIDATION_ALG_MISMATCH; - - /* Validate expires */ - t = get_js_int(jwt->grants, "exp"); - if (jwt_valid->now && t != -1 && jwt_valid->now - jwt_valid->exp_leeway >= t) - jwt_valid->status |= JWT_VALIDATION_EXPIRED; - - /* Validate not-before */ - t = get_js_int(jwt->grants, "nbf"); - if (jwt_valid->now && t != -1 && jwt_valid->now + jwt_valid->nbf_leeway < t) - jwt_valid->status |= JWT_VALIDATION_TOO_NEW; - - /* Validate replicated issuer */ - jwt_hdr_str = get_js_string(jwt->headers, "iss"); - jwt_body_str = get_js_string(jwt->grants, "iss"); - if (jwt_hdr_str && jwt_body_str && jwt_strcmp(jwt_hdr_str, jwt_body_str)) - jwt_valid->status |= JWT_VALIDATION_ISS_MISMATCH; - - /* Validate replicated subject */ - jwt_hdr_str = get_js_string(jwt->headers, "sub"); - jwt_body_str = get_js_string(jwt->grants, "sub"); - if (jwt_hdr_str && jwt_body_str && jwt_strcmp(jwt_hdr_str, jwt_body_str)) - jwt_valid->status |= JWT_VALIDATION_SUB_MISMATCH; - - /* Validate replicated audience (might be array or string) */ - js_val_1 = json_object_get(jwt->headers, "aud"); - js_val_2 = json_object_get(jwt->grants, "aud"); - if (js_val_1 && js_val_2 && !json_equal(js_val_1, js_val_2)) - jwt_valid->status |= JWT_VALIDATION_AUD_MISMATCH; - - /* Validate required grants */ - json_object_foreach(jwt_valid->req_grants, req_grant, js_val_1) { - json_t *act_js_val = json_object_get(jwt->grants, req_grant); - - if (act_js_val && json_equal(js_val_1, act_js_val)) - continue; - - if (act_js_val) - jwt_valid->status |= JWT_VALIDATION_GRANT_MISMATCH; - else - jwt_valid->status |= JWT_VALIDATION_GRANT_MISSING; - } - - return jwt_valid->status; -} - -typedef struct { - int error; - char *str; -} jwt_exception_dict_t; - -static jwt_exception_dict_t jwt_exceptions[] = { - /* { JWT_VALIDATION_SUCCESS, "SUCCESS" }, */ - { JWT_VALIDATION_ERROR, "general failures" }, - { JWT_VALIDATION_ALG_MISMATCH, "algorithm mismatch" }, - { JWT_VALIDATION_EXPIRED, "token expired" }, - { JWT_VALIDATION_TOO_NEW, "token future dated" }, - { JWT_VALIDATION_ISS_MISMATCH, "issuer mismatch" }, - { JWT_VALIDATION_SUB_MISMATCH, "subject mismatch" }, - { JWT_VALIDATION_AUD_MISMATCH, "audience mismatch" }, - { JWT_VALIDATION_GRANT_MISSING, "grant missing" }, - { JWT_VALIDATION_GRANT_MISMATCH, "grant mismatch" }, -}; - -char *jwt_exception_str(jwt_valid_exception_t exceptions) -{ - size_t i; - int rc; - char *str = NULL; - - if (exceptions == JWT_VALIDATION_SUCCESS) { - if ((rc = __append_str(&str, "success"))) - goto fail; - return str; - } - - for (i = 0; i < ARRAY_SIZE(jwt_exceptions); i++) { - if (!(jwt_exceptions[i].error & exceptions)) - continue; - - if (str && (rc = __append_str(&str, ", "))) - goto fail; - - if ((rc = __append_str(&str, jwt_exceptions[i].str))) - goto fail; - } - - /* check if none of the exceptions matched? */ - if (!str && (rc = __append_str(&str, "unknown exceptions"))) - goto fail; - - return str; -fail: - errno = rc; - jwt_freemem(str); - return NULL; -} diff --git a/libjwt/jwt-verify.c b/libjwt/jwt-verify.c index a5d08629..f50edf47 100644 --- a/libjwt/jwt-verify.c +++ b/libjwt/jwt-verify.c @@ -78,7 +78,7 @@ static int jwt_parse_head(jwt_t *jwt, char *head) return 0; } -static int jwt_parse(jwt_t *jwt, const char *token, unsigned int *len) +int jwt_parse(jwt_t *jwt, const char *token, unsigned int *len) { char_auto *head = NULL; char *body, *sig; @@ -129,38 +129,44 @@ static int jwt_parse(jwt_t *jwt, const char *token, unsigned int *len) static int __verify_config_post(jwt_t *jwt, const jwt_config_t *config, unsigned int sig_len) { - /* The easy out; insecure JWT, and the caller expects it. */ - if (config->alg == JWT_ALG_NONE && config->jw_key == NULL && !sig_len && - jwt->alg == JWT_ALG_NONE) - return 0; + if (!sig_len) { + if (config->key || config->alg != JWT_ALG_NONE) { + jwt_write_error(jwt, + "Expected a signature, but JWT has none"); + return 1; + } - /* The quick fail. The caller and JWT disagree. */ - if (config->alg != jwt->alg) { - jwt_write_error(jwt, "JWT alg does not match expected value"); - return 1; + return 0; } - /* At this point, someone is expecting a sig and we also know the - * caller and the JWT token agree on the alg. */ - - /* We require a key and a signature. */ - if (config->jw_key == NULL || !sig_len) { - jwt_write_error(jwt, "JWT does not contain a signature"); + /* Signature is known to be present from this point */ + if (config->key == NULL) { + jwt_write_error(jwt, + "JWT has signature, but no key was given"); return 1; } - /* If the key has an alg, it must match the caller. */ - if (config->jw_key->alg != JWT_ALG_NONE && - config->jw_key->alg != config->alg) { - jwt_write_error(jwt, "JWT alg does not much the key being used"); + /* Key is known to be given at this point */ + if (config->alg == JWT_ALG_NONE) { + if (config->key->alg != jwt->alg) { + jwt_write_error(jwt, "Key alg does not match JWT"); + return 1; + } + } else if (config->key->alg == JWT_ALG_NONE) { + if (config->alg != jwt->alg) { + jwt_write_error(jwt, "Config alg does not match JWT"); + return 1; + } + } else if (config->alg != config->key->alg) { + jwt_write_error(jwt, "Config and key alg does not match"); return 1; } return 0; } -static jwt_t *jwt_verify_complete(jwt_t *jwt, const jwt_config_t *config, - const char *token, unsigned int payload_len) +jwt_t *jwt_verify_complete(jwt_t *jwt, const jwt_config_t *config, + const char *token, unsigned int payload_len) { const char *sig; unsigned int sig_len; @@ -177,42 +183,7 @@ static jwt_t *jwt_verify_complete(jwt_t *jwt, const jwt_config_t *config, return jwt; /* At this point, config is never NULL */ - jwt->jw_key = config->jw_key; + jwt->key = config->key; return jwt_verify_sig(jwt, token, payload_len, sig); } - -/* - * If no callback then we act just like jwt_verify(). - */ -jwt_t *jwt_verify_wcb(const char *token, jwt_config_t *config, - jwt_callback_t cb) -{ - unsigned int payload_len; - jwt_t *jwt = NULL; - - if (config == NULL) - return NULL; - - jwt = jwt_new(); - if (jwt == NULL) - return NULL; - - /* First parsing pass, error will be set for us */ - if (jwt_parse(jwt, token, &payload_len)) - return jwt; - - /* Let the user handle this and update config */ - if (cb && cb(jwt, config)) { - jwt_write_error(jwt, "User callback returned error"); - return jwt; - } - - /* Finish it up */ - return jwt_verify_complete(jwt, config, token, payload_len); -} - -jwt_t *jwt_verify(const char *token, jwt_config_t *config) -{ - return jwt_verify_wcb(token, config, NULL); -} diff --git a/libjwt/jwt.c b/libjwt/jwt.c index cdc55485..5e69b387 100644 --- a/libjwt/jwt.c +++ b/libjwt/jwt.c @@ -17,33 +17,6 @@ #include "jwt-private.h" -int __append_str(char **buf, const char *str) -{ - char *new; - - if (str == NULL || str[0] == '\0') - return 0; - - if (*buf == NULL) { - new = jwt_malloc(strlen(str) + 1); - if (new) - new[0] = '\0'; - } else { - new = jwt_realloc(*buf, strlen(*buf) + strlen(str) + 1); - } - - if (new == NULL) { - jwt_freemem(*buf); - return 1; - } - - strcat(new, str); - - *buf = new; - - return 0; -} - const char *jwt_alg_str(jwt_alg_t alg) { switch (alg) { @@ -121,23 +94,6 @@ jwt_alg_t jwt_str_alg(const char *alg) return JWT_ALG_INVAL; } -int jwt_error(const jwt_t *jwt) -{ - return jwt->error; -} - -const char *jwt_error_msg(const jwt_t *jwt) -{ - return jwt->error_msg; -} - -JWT_NO_EXPORT -void jwt_scrub_key(jwt_t *jwt) -{ - jwt->jw_key = NULL; - jwt->alg = JWT_ALG_NONE; -} - JWT_NO_EXPORT jwt_t *jwt_new(void) { @@ -157,43 +113,6 @@ jwt_t *jwt_new(void) return jwt; } -jwt_t *jwt_create(jwt_config_t *config) -{ - jwt_t *jwt = jwt_new(); - - if (jwt == NULL) - return NULL; - - /* An insecure JWT */ - if (config == NULL) - return jwt; - - if (config->alg == JWT_ALG_NONE) { - /* Also insecure */ - if (config->jw_key == NULL) - return jwt; - - /* This is an error. */ - jwt_write_error(jwt, "Config alg must be set to other than " - "none when supplying a key"); - return jwt; - } - - /* At this point, we expect a key. */ - - /* If the key has it set, it must match. */ - if (config->jw_key->alg != JWT_ALG_NONE && - config->alg != config->jw_key->alg) { - jwt_write_error(jwt, "Config alg does not match key alg"); - return jwt; - } - - jwt->jw_key = config->jw_key; - jwt->alg = config->alg; - - return jwt; -} - jwt_alg_t jwt_get_alg(const jwt_t *jwt) { if (jwt == NULL) @@ -207,8 +126,6 @@ void jwt_free(jwt_t *jwt) if (!jwt) return; - jwt_scrub_key(jwt); - json_decref(jwt->grants); json_decref(jwt->headers); @@ -217,35 +134,6 @@ void jwt_free(jwt_t *jwt) jwt_freemem(jwt); } -jwt_t *jwt_dup(jwt_t *jwt) -{ - jwt_t *new = NULL; - - if (jwt == NULL) - return NULL; - - new = jwt_malloc(sizeof(*new)); - if (!new) - return NULL; - - memset(new, 0, sizeof(jwt_t)); - - new->jw_key = jwt->jw_key; - new->alg = jwt->alg; - new->error = jwt->error; - strcpy(new->error_msg, jwt->error_msg); - - new->grants = json_deep_copy(jwt->grants); - new->headers = json_deep_copy(jwt->headers); - if (!new->headers || !new->grants) { - json_decref(new->headers); - json_decref(new->grants); - jwt_freep(&new); - } - - return new; -} - void *jwt_base64uri_decode(const char *src, int *ret_len) { void *buf; @@ -361,7 +249,7 @@ int jwt_base64uri_encode(char **_dst, const char *plain, int plain_len) static int __check_hmac(jwt_t *jwt) { - int key_bits = jwt->jw_key->bits; + int key_bits = jwt->key->bits; if (key_bits < 256) { jwt_write_error(jwt, "Key too short for HS algs: %d bits", @@ -400,7 +288,7 @@ static int __check_hmac(jwt_t *jwt) static int __check_key_bits(jwt_t *jwt) { - int key_bits = jwt->jw_key->bits; + int key_bits = jwt->key->bits; /* Ignore if we don't have it */ if (key_bits == 0) @@ -542,8 +430,3 @@ jwt_t *jwt_verify_sig(jwt_t *jwt, const char *head, unsigned int head_len, return jwt; } - -void jwt_config_init(jwt_config_t *config) -{ - memset(config, 0, sizeof(*config)); -} diff --git a/libjwt/openssl/sign-verify.c b/libjwt/openssl/sign-verify.c index cb18fec7..32940104 100644 --- a/libjwt/openssl/sign-verify.c +++ b/libjwt/openssl/sign-verify.c @@ -33,11 +33,11 @@ static int openssl_sign_sha_hmac(jwt_t *jwt, char **out, unsigned int *len, void *key; size_t key_len; - if (!ops_compat(jwt->jw_key, JWT_CRYPTO_OPS_OPENSSL)) + if (!ops_compat(jwt->key, JWT_CRYPTO_OPS_OPENSSL)) return 1; // LCOV_EXCL_LINE - key = jwt->jw_key->oct.key; - key_len = jwt->jw_key->oct.len; + key = jwt->key->oct.key; + key_len = jwt->key->oct.len; *out = NULL; @@ -102,26 +102,26 @@ static int openssl_verify_sha_hmac(jwt_t *jwt, const char *head, static int __degree_and_check(jwt_t *jwt) { - int bits = jwt->jw_key->bits; + int bits = jwt->key->bits; switch (jwt->alg) { case JWT_ALG_ES256: - if (bits != 256 ||jwt_strcmp(jwt->jw_key->curve, "P-256")) + if (bits != 256 ||jwt_strcmp(jwt->key->curve, "P-256")) return 0; break; case JWT_ALG_ES384: - if (bits != 384 ||jwt_strcmp(jwt->jw_key->curve, "P-384")) + if (bits != 384 ||jwt_strcmp(jwt->key->curve, "P-384")) return 0; break; case JWT_ALG_ES512: - if (bits != 521 ||jwt_strcmp(jwt->jw_key->curve, "P-521")) + if (bits != 521 ||jwt_strcmp(jwt->key->curve, "P-521")) return 0; break; case JWT_ALG_ES256K: - if (bits != 256 ||jwt_strcmp(jwt->jw_key->curve, "secp256k1")) + if (bits != 256 ||jwt_strcmp(jwt->key->curve, "secp256k1")) return 0; break; @@ -200,10 +200,10 @@ static int openssl_sign_sha_pem(jwt_t *jwt, char **out, unsigned int *len, int ret = 0; size_t slen; - if (!ops_compat(jwt->jw_key, JWT_CRYPTO_OPS_OPENSSL)) + if (!ops_compat(jwt->key, JWT_CRYPTO_OPS_OPENSSL)) return 1; // LCOV_EXCL_LINE - pkey = jwt->jw_key->provider_data; + pkey = jwt->key->provider_data; switch (jwt->alg) { /* RSA */ @@ -263,7 +263,11 @@ static int openssl_sign_sha_pem(jwt_t *jwt, char **out, unsigned int *len, return 1; // LCOV_EXCL_LINE } - if (type != EVP_PKEY_id(pkey)) + if (type == EVP_PKEY_RSA_PSS) { + if (EVP_PKEY_id(pkey) != EVP_PKEY_RSA_PSS && + EVP_PKEY_id(pkey) != EVP_PKEY_RSA) + SIGN_ERROR(); // LCOV_EXCL_LINE + } else if (type != EVP_PKEY_id(pkey)) SIGN_ERROR(); // LCOV_EXCL_LINE mdctx = EVP_MD_CTX_create(); @@ -277,10 +281,10 @@ static int openssl_sign_sha_pem(jwt_t *jwt, char **out, unsigned int *len, /* Required for RSA-PSS */ if (type == EVP_PKEY_RSA_PSS) { if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, - RSA_PKCS1_PSS_PADDING) < 0) + RSA_PKCS1_PSS_PADDING) < 0) SIGN_ERROR(); // LCOV_EXCL_LINE if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, - RSA_PSS_SALTLEN_DIGEST) < 0) + RSA_PSS_SALTLEN_DIGEST) < 0) SIGN_ERROR(); // LCOV_EXCL_LINE } @@ -341,9 +345,9 @@ static int openssl_verify_sha_pem(jwt_t *jwt, const char *head, int ret = 0; int slen; - if (!ops_compat(jwt->jw_key, JWT_CRYPTO_OPS_OPENSSL)) + if (!ops_compat(jwt->key, JWT_CRYPTO_OPS_OPENSSL)) return 1; // LCOV_EXCL_LINE - pkey = jwt->jw_key->provider_data; + pkey = jwt->key->provider_data; switch (jwt->alg) { /* RSA */ @@ -407,11 +411,15 @@ static int openssl_verify_sha_pem(jwt_t *jwt, const char *head, if (sig == NULL) VERIFY_ERROR(); - if (type != EVP_PKEY_id(pkey)) + if (type == EVP_PKEY_RSA_PSS) { + if (EVP_PKEY_id(pkey) != EVP_PKEY_RSA_PSS && + EVP_PKEY_id(pkey) != EVP_PKEY_RSA) + VERIFY_ERROR(); // LCOV_EXCL_LINE + } else if (type != EVP_PKEY_id(pkey)) VERIFY_ERROR(); - /* Convert EC sigs back to ASN1. */ - if (type == EVP_PKEY_EC) { + if (type == EVP_PKEY_EC) { + /* Convert EC sigs back to ASN1. */ unsigned int bn_len; int degree; unsigned char *p; @@ -462,7 +470,7 @@ static int openssl_verify_sha_pem(jwt_t *jwt, const char *head, RSA_PKCS1_PSS_PADDING) < 0) VERIFY_ERROR(); // LCOV_EXCL_LINE if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, - RSA_PSS_SALTLEN_DIGEST) < 0) + RSA_PSS_SALTLEN_AUTO) < 0) VERIFY_ERROR(); // LCOV_EXCL_LINE } diff --git a/tests/jwt_builder.c b/tests/jwt_builder.c new file mode 100644 index 00000000..8985e380 --- /dev/null +++ b/tests/jwt_builder.c @@ -0,0 +1,638 @@ +/* Public domain, no copyright. Use at your own risk. */ + +#include +#include +#include +#include +#include + +#include "jwt_tests.h" + +START_TEST(new) +{ + jwt_builder_auto_t *builder = NULL; + + SET_OPS(); + + builder = jwt_builder_new(); + ck_assert_ptr_nonnull(builder); + ck_assert_int_eq(jwt_builder_error(builder), 0); + +} +END_TEST + +START_TEST(gen) +{ + const char exp[] = "eyJhbGciOiJub25lIn0."; + jwt_builder_auto_t *builder = NULL; + char_auto *out = NULL; + + SET_OPS(); + + builder = jwt_builder_new(); + ck_assert_ptr_nonnull(builder); + ck_assert_int_eq(jwt_builder_error(builder), 0); + + out = jwt_builder_generate(builder); + ck_assert_ptr_nonnull(out); + ck_assert_mem_eq(out, exp, strlen(exp)); +} +END_TEST + +static int __gen_wcb(jwt_t *jwt, jwt_config_t *config) +{ + jwt_value_t jval; + + ck_assert_ptr_nonnull(jwt); + ck_assert_ptr_nonnull(config); + + ck_assert_str_eq(config->ctx, "testing"); + jwt_set_ADD_INT(&jval, "exp", TS_CONST + 480); + jwt_grant_add(jwt, &jval); + + return 0; +} + +START_TEST(gen_wcb) +{ + const char exp[] = "eyJhbGciOiJub25lIn0.eyJleHAiOjE0NzU5ODEwMjV9."; + jwt_builder_auto_t *builder = NULL; + char_auto *out = NULL; + int ret; + + SET_OPS(); + + builder = jwt_builder_new(); + ck_assert_ptr_nonnull(builder); + ck_assert_int_eq(jwt_builder_error(builder), 0); + + ret = jwt_builder_setclaims(builder, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + ret = jwt_builder_setcb(builder, __gen_wcb, "testing"); + ck_assert_int_eq(ret, 0); + + out = jwt_builder_generate(builder); + ck_assert_ptr_nonnull(out); + ck_assert_str_eq(out, exp); +} +END_TEST + +START_TEST(gen_stress) +{ + const char exp[] = "eyJhbGciOiJub25lIn0."; + jwt_builder_auto_t *builder = NULL; + int i; + + SET_OPS(); + + builder = jwt_builder_new(); + ck_assert_ptr_nonnull(builder); + ck_assert_int_eq(jwt_builder_error(builder), 0); + + for (i = 0; i < 1000; i++) { + char_auto *out = jwt_builder_generate(builder); + ck_assert_ptr_nonnull(out); + ck_assert_mem_eq(out, exp, strlen(exp)); + } + + ck_assert_int_eq(i, 1000); +} +END_TEST + +START_TEST(null_handling) +{ + jwt_builder_t *builder = NULL; + const char *out; + jwk_item_t *key = NULL; + int ret; + + builder = jwt_builder_new(); + ck_assert_ptr_nonnull(builder); + ck_assert_int_eq(jwt_builder_error(builder), 0); + + jwt_builder_free(NULL); + + ret = jwt_builder_setkey(NULL, JWT_ALG_HS256, NULL); + ck_assert_int_ne(ret, 0); + + /* Create and clear an error */ + ret = jwt_builder_setkey(builder, JWT_ALG_HS256, NULL); + ck_assert_int_ne(ret, 0); + /* Check error exists */ + ck_assert_int_ne(jwt_builder_error(builder), 0); + out = jwt_builder_error_msg(builder); + ck_assert_ptr_nonnull(out); + ck_assert_int_ne(strlen(out), 0); + /* Clear it */ + jwt_builder_error_clear(builder); + /* Check that its cleared */ + ck_assert_int_eq(jwt_builder_error(builder), 0); + out = jwt_builder_error_msg(builder); + ck_assert_ptr_nonnull(out); + ck_assert_int_eq(strlen(out), 0); + + /* Fake it */ + key = (void *)builder; + ret = jwt_builder_setkey(NULL, 0, key); + ck_assert_int_ne(ret, 0); + + ret = jwt_builder_error(NULL); + ck_assert_int_ne(ret, 0); + + out = jwt_builder_error_msg(NULL); + ck_assert_ptr_null(out); + + out = jwt_builder_error_msg(builder); + ck_assert_ptr_nonnull(out); + ck_assert_int_eq(strlen(out), 0); + + jwt_builder_error_clear(NULL); + + out = jwt_builder_generate(NULL); + ck_assert_ptr_null(out); +} +END_TEST + +START_TEST(gen_hs256) +{ + jwt_builder_auto_t *builder = NULL; + char *out = NULL; + const char exp[] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.CM4dD95Nj" + "0vSfMGtDas432AUW1HAo7feCiAbt5Yjuds"; + int ret; + + SET_OPS(); + + builder = jwt_builder_new(); + ck_assert_ptr_nonnull(builder); + ck_assert_int_eq(jwt_builder_error(builder), 0); + + ret = jwt_builder_setclaims(builder, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + read_json("oct_key_256.json"); + ret = jwt_builder_setkey(builder, JWT_ALG_HS256, g_item); + ck_assert_int_eq(ret, 0); + + out = jwt_builder_generate(builder); + ck_assert_ptr_nonnull(out); + ck_assert_str_eq(out, exp); + + free_key(); +} +END_TEST + +START_TEST(gen_es384_pub) +{ + jwt_builder_auto_t *builder = NULL; + int ret; + + SET_OPS(); + + builder = jwt_builder_new(); + ck_assert_ptr_nonnull(builder); + ck_assert_int_eq(jwt_builder_error(builder), 0); + + ret = jwt_builder_setclaims(builder, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + /* Pub key will fail to set */ + read_json("ec_key_secp384r1_pub.json"); + ret = jwt_builder_setkey(builder, JWT_ALG_ES384, g_item); + ck_assert_int_ne(ret, 0); + ck_assert_str_eq(jwt_builder_error_msg(builder), + "Signing requires a private key"); + + free_key(); +} +END_TEST + +static int __gen_hs256_wcb(jwt_t *jwt, jwt_config_t *config) +{ + ck_assert_ptr_nonnull(jwt); + ck_assert_ptr_nonnull(config); + ck_assert_int_eq(jwt_get_alg(jwt), JWT_ALG_NONE); + + if (config->ctx != NULL) { + ck_assert_int_eq(jwt_get_alg(jwt), JWT_ALG_NONE); + config->key = g_item; + config->alg = JWT_ALG_HS256; + } else { + config->key = NULL; + config->alg = JWT_ALG_HS256; + } + + return 0; +} + +START_TEST(gen_hs256_wcb) +{ + jwt_builder_auto_t *builder = NULL; + char *out = NULL; + const char exp[] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.CM4dD95Nj" + "0vSfMGtDas432AUW1HAo7feCiAbt5Yjuds"; + int ret; + + SET_OPS(); + + builder = jwt_builder_new(); + ck_assert_ptr_nonnull(builder); + ck_assert_int_eq(jwt_builder_error(builder), 0); + + ret = jwt_builder_setclaims(builder, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + read_json("oct_key_256.json"); + + ret = jwt_builder_setcb(builder, __gen_hs256_wcb, "testing"); + ck_assert_int_eq(ret, 0); + + out = jwt_builder_generate(builder); + ck_assert_ptr_nonnull(out); + ck_assert_str_eq(out, exp); + free(out); + + ret = jwt_builder_setcb(builder, __gen_hs256_wcb, NULL); + ck_assert_int_eq(ret, 0); + + out = jwt_builder_generate(builder); + ck_assert_ptr_null(out); + ck_assert_int_eq(jwt_builder_error(builder), 1); + + free_key(); +} +END_TEST + +START_TEST(gen_hs256_stress) +{ + jwt_builder_auto_t *builder = NULL; + const char exp[] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.CM4dD95Nj" + "0vSfMGtDas432AUW1HAo7feCiAbt5Yjuds"; + int ret, i; + + SET_OPS(); + + builder = jwt_builder_new(); + ck_assert_ptr_nonnull(builder); + ck_assert_int_eq(jwt_builder_error(builder), 0); + + ret = jwt_builder_setclaims(builder, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + read_json("oct_key_256.json"); + ret = jwt_builder_setkey(builder, JWT_ALG_HS256, g_item); + ck_assert_int_eq(ret, 0); + + for (i = 0; i < 1000; i++) { + char_auto *out = jwt_builder_generate(builder); + ck_assert_ptr_nonnull(out); + ck_assert_str_eq(out, exp); + } + + free_key(); +} +END_TEST + +START_TEST(claim_str_addgetdel) +{ + const char exp[] = "eyJhbGciOiJub25lIn0.eyJpc3MiOiJka" + "XNrLnN3aXNzZGlzay5jb20ifQ."; + jwt_builder_auto_t *builder = NULL; + char_auto *out = NULL; + jwt_value_t jval; + int ret; + + SET_OPS(); + + builder = jwt_builder_new(); + ck_assert_ptr_nonnull(builder); + ck_assert_int_eq(jwt_builder_error(builder), 0); + + ret = jwt_builder_setclaims(builder, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_STR(&jval, "iss", "disk.swissdisk.com"); + ret = jwt_builder_claim_add(builder, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_STR(&jval, "aud", "public"); + ret = jwt_builder_claim_add(builder, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_STR(&jval, "aud", "private"); + ret = jwt_builder_claim_add(builder, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_EXIST); + + jwt_set_ADD_STR(&jval, "aud", "employees"); + jval.replace = 1; + ret = jwt_builder_claim_add(builder, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_GET_STR(&jval, "aud"); + ret = jwt_builder_claim_get(builder, &jval); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jval.str_val); + ck_assert_str_eq(jval.str_val, "employees"); + + jwt_set_GET_INT(&jval, "aud"); + ret = jwt_builder_claim_get(builder, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_TYPE); + + jwt_set_GET_BOOL(&jval, "aud"); + ret = jwt_builder_claim_get(builder, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_TYPE); + + ret = jwt_builder_claim_del(builder, "aud"); + ck_assert_int_eq(ret, 0); + + jwt_set_GET_STR(&jval, "aud"); + ret = jwt_builder_claim_get(builder, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_NOEXIST); + + out = jwt_builder_generate(builder); + ck_assert_ptr_nonnull(out); + ck_assert_str_eq(out, exp); +} +END_TEST + +START_TEST(claim_int_addgetdel) +{ + const char exp[] = "eyJhbGciOiJub25lIn0.eyJuYmYiOjE0NzU5ODA1NDV9."; + jwt_builder_auto_t *builder = NULL; + char_auto *out = NULL; + jwt_value_t jval; + int ret; + + SET_OPS(); + + builder = jwt_builder_new(); + ck_assert_ptr_nonnull(builder); + ck_assert_int_eq(jwt_builder_error(builder), 0); + + ret = jwt_builder_setclaims(builder, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_INT(&jval, "nbf", TS_CONST); + ret = jwt_builder_claim_add(builder, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_INT(&jval, "exp", TS_CONST); + ret = jwt_builder_claim_add(builder, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_INT(&jval, "exp", TS_CONST + 360); + ret = jwt_builder_claim_add(builder, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_EXIST); + + jwt_set_ADD_INT(&jval, "exp", TS_CONST + 480); + jval.replace = 1; + ret = jwt_builder_claim_add(builder, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_GET_INT(&jval, "exp"); + ret = jwt_builder_claim_get(builder, &jval); + ck_assert_int_eq(ret, 0); + ck_assert_int_eq(jval.int_val, TS_CONST + 480); + + jwt_set_GET_STR(&jval, "exp"); + ret = jwt_builder_claim_get(builder, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_TYPE); + + jwt_set_GET_BOOL(&jval, "exp"); + ret = jwt_builder_claim_get(builder, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_TYPE); + + ret = jwt_builder_claim_del(builder, "exp"); + ck_assert_int_eq(ret, 0); + + jwt_set_GET_INT(&jval, "exp"); + ret = jwt_builder_claim_get(builder, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_NOEXIST); + + out = jwt_builder_generate(builder); + ck_assert_ptr_nonnull(out); + ck_assert_str_eq(out, exp); +} +END_TEST + +START_TEST(claim_bool_addgetdel) +{ + const char exp[] = "eyJhbGciOiJub25lIn0.eyJhZG1pbiI6dHJ1ZX0."; + jwt_builder_auto_t *builder = NULL; + char_auto *out = NULL; + jwt_value_t jval; + int ret; + + SET_OPS(); + + builder = jwt_builder_new(); + ck_assert_ptr_nonnull(builder); + ck_assert_int_eq(jwt_builder_error(builder), 0); + + ret = jwt_builder_setclaims(builder, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_BOOL(&jval, "admin", 1); + ret = jwt_builder_claim_add(builder, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_BOOL(&jval, "sudo", 1); + ret = jwt_builder_claim_add(builder, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_BOOL(&jval, "sudo", 0); + ret = jwt_builder_claim_add(builder, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_EXIST); + + jwt_set_ADD_BOOL(&jval, "sudo", 0); + jval.replace = 1; + ret = jwt_builder_claim_add(builder, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_GET_BOOL(&jval, "sudo"); + ret = jwt_builder_claim_get(builder, &jval); + ck_assert_int_eq(ret, 0); + ck_assert_int_eq(jval.bool_val, 0); + + jwt_set_GET_STR(&jval, "sudo"); + ret = jwt_builder_claim_get(builder, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_TYPE); + + jwt_set_GET_INT(&jval, "sudo"); + ret = jwt_builder_claim_get(builder, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_TYPE); + + ret = jwt_builder_claim_del(builder, "sudo"); + ck_assert_int_eq(ret, 0); + + jwt_set_GET_BOOL(&jval, "sudo"); + ret = jwt_builder_claim_get(builder, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_NOEXIST); + + out = jwt_builder_generate(builder); + ck_assert_ptr_nonnull(out); + ck_assert_str_eq(out, exp); +} +END_TEST + +START_TEST(claim_json_addgetdel) +{ + const char exp[] = "eyJhbGciOiJub25lIn0.eyJyb29tcyI6WyJvZ" + "mZpY2UiLCJ3YXItcm9vbSJdfQ."; + jwt_builder_auto_t *builder = NULL; + char_auto *out = NULL; + jwt_value_t jval; + int ret; + + SET_OPS(); + + builder = jwt_builder_new(); + ck_assert_ptr_nonnull(builder); + ck_assert_int_eq(jwt_builder_error(builder), 0); + + ret = jwt_builder_setclaims(builder, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_JSON(&jval, "rooms", + "[\"office\",\"war-room\"]"); + ret = jwt_builder_claim_add(builder, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_GET_JSON(&jval, "rooms"); + ret = jwt_builder_claim_get(builder, &jval); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jval.json_val); + ck_assert_str_eq(jval.json_val, "[\"office\",\"war-room\"]"); + free(jval.json_val); + + jwt_set_ADD_JSON(&jval, "buildings", + "{\"main\":\"dallas\",\"accounting\":\"houston\"}"); + ret = jwt_builder_claim_add(builder, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_JSON(&jval, "buildings", "{\"hq\": 0}"); + ret = jwt_builder_claim_add(builder, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_EXIST); + + jwt_set_ADD_JSON(&jval, "buildings", "{\"hq\": 1}"); + jval.replace = 1; + ret = jwt_builder_claim_add(builder, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_GET_JSON(&jval, "buildings"); + ret = jwt_builder_claim_get(builder, &jval); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jval.json_val); + ck_assert_str_eq(jval.json_val, "{\"hq\":1}"); + free(jval.json_val); + + jwt_set_GET_STR(&jval, "buildings"); + ret = jwt_builder_claim_get(builder, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_TYPE); + + jwt_set_GET_INT(&jval, "buildings"); + ret = jwt_builder_claim_get(builder, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_TYPE); + + ret = jwt_builder_claim_del(builder, "buildings"); + ck_assert_int_eq(ret, 0); + + jwt_set_GET_JSON(&jval, "buildings"); + ret = jwt_builder_claim_get(builder, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_NOEXIST); + + out = jwt_builder_generate(builder); + ck_assert_ptr_nonnull(out); + ck_assert_str_eq(out, exp); +} +END_TEST + +START_TEST(header_str_addgetdel) +{ + const char exp[] = "eyJhbGciOiJub25lIiwidHlwIjoiQ3VzdG9tIn0.e30."; + jwt_builder_auto_t *builder = NULL; + char_auto *out = NULL; + jwt_value_t jval; + int ret; + + SET_OPS(); + + builder = jwt_builder_new(); + ck_assert_ptr_nonnull(builder); + ck_assert_int_eq(jwt_builder_error(builder), 0); + + ret = jwt_builder_setclaims(builder, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_STR(&jval, "typ", "Custom"); + ret = jwt_builder_header_add(builder, &jval); + ck_assert_int_eq(ret, 0); + + out = jwt_builder_generate(builder); + ck_assert_ptr_nonnull(out); + ck_assert_str_eq(out, exp); + + jwt_set_GET_STR(&jval, "typ"); + ret = jwt_builder_header_get(builder, &jval); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jval.str_val); + ck_assert_str_eq(jval.str_val, "Custom"); + + ret = jwt_builder_header_del(builder, "typ"); + + jwt_set_GET_STR(&jval, "typ"); + ret = jwt_builder_header_get(builder, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_NOEXIST); +} +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("New"); + tcase_add_loop_test(tc_core, new, 0, i); + suite_add_tcase(s, tc_core); + + tc_core = tcase_create("Gen"); + tcase_add_loop_test(tc_core, gen, 0, i); + tcase_add_loop_test(tc_core, gen_stress, 0, i); + tcase_add_loop_test(tc_core, gen_wcb, 0, i); + tcase_add_loop_test(tc_core, gen_es384_pub, 0, i); + suite_add_tcase(s, tc_core); + + tc_core = tcase_create("Error Handling"); + tcase_add_loop_test(tc_core, null_handling, 0, i); + suite_add_tcase(s, tc_core); + + tc_core = tcase_create("HS256 Key Gen"); + tcase_add_loop_test(tc_core, gen_hs256, 0, i); + tcase_add_loop_test(tc_core, gen_hs256_wcb, 0, i); + tcase_add_loop_test(tc_core, gen_hs256_stress, 0, i); + suite_add_tcase(s, tc_core); + + tc_core = tcase_create("Claims AddGetDel"); + tcase_add_loop_test(tc_core, claim_str_addgetdel, 0, i); + tcase_add_loop_test(tc_core, claim_int_addgetdel, 0, i); + tcase_add_loop_test(tc_core, claim_bool_addgetdel, 0, i); + tcase_add_loop_test(tc_core, claim_json_addgetdel, 0, i); + suite_add_tcase(s, tc_core); + + tc_core = tcase_create("Header AddGetDel"); + /* All of the code paths for str/int/bool/json have been covered. We + * just run this to ensure add/get/del works on headers */ + tcase_add_loop_test(tc_core, header_str_addgetdel, 0, i); + suite_add_tcase(s, tc_core); + + return s; +} + +int main(void) +{ + JWT_TEST_MAIN("LibJWT Builder"); +} diff --git a/tests/jwt_checker.c b/tests/jwt_checker.c new file mode 100644 index 00000000..2918cae2 --- /dev/null +++ b/tests/jwt_checker.c @@ -0,0 +1,737 @@ +/* Public domain, no copyright. Use at your own risk. */ + +#include +#include +#include +#include +#include + +#include "jwt_tests.h" + +START_TEST(new) +{ + jwt_checker_auto_t *checker = NULL; + + SET_OPS(); + + checker = jwt_checker_new(); + ck_assert_ptr_nonnull(checker); + ck_assert_int_eq(jwt_checker_error(checker), 0); + +} +END_TEST + +START_TEST(verify) +{ + const char token[] = "eyJhbGciOiJub25lIn0.eyJpc3MiOiJka" + "XNrLnN3aXNzZGlzay5jb20ifQ."; + jwt_checker_auto_t *checker = NULL; + int ret; + + SET_OPS(); + + checker = jwt_checker_new(); + ck_assert_ptr_nonnull(checker); + ck_assert_int_eq(jwt_checker_error(checker), 0); + + ret = jwt_checker_verify(checker, token); + ck_assert_int_eq(ret, 0); +} +END_TEST + +static int __verify_wcb(jwt_t *jwt, jwt_config_t *config) +{ + ck_assert_ptr_nonnull(jwt); + ck_assert_ptr_nonnull(config); + + ck_assert_str_eq(config->ctx, "testing"); + + return 0; +} + +START_TEST(verify_wcb) +{ + const char token[] = "eyJhbGciOiJub25lIn0.eyJpc3MiOiJka" + "XNrLnN3aXNzZGlzay5jb20ifQ."; + jwt_checker_auto_t *checker = NULL; + int ret; + + SET_OPS(); + + checker = jwt_checker_new(); + ck_assert_ptr_nonnull(checker); + ck_assert_int_eq(jwt_checker_error(checker), 0); + + ret = jwt_checker_setclaims(checker, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + ret = jwt_checker_setcb(checker, __verify_wcb, "testing"); + ck_assert_int_eq(ret, 0); + + ret = jwt_checker_verify(checker, token); + ck_assert_int_eq(ret, 0); +} +END_TEST + +START_TEST(verify_stress) +{ + const char token[] = "eyJhbGciOiJub25lIn0.eyJpc3MiOiJka" + "XNrLnN3aXNzZGlzay5jb20ifQ."; + jwt_checker_auto_t *checker = NULL; + int i; + + SET_OPS(); + + checker = jwt_checker_new(); + ck_assert_ptr_nonnull(checker); + ck_assert_int_eq(jwt_checker_error(checker), 0); + + for (i = 0; i < 1000; i++) { + int ret = jwt_checker_verify(checker, token); + ck_assert_int_eq(ret, 0); + } +} +END_TEST + +START_TEST(null_handling) +{ + jwt_checker_t *checker = NULL; + const char *out; + jwk_item_t *key = NULL; + int ret; + + checker = jwt_checker_new(); + ck_assert_ptr_nonnull(checker); + ck_assert_int_eq(jwt_checker_error(checker), 0); + + jwt_checker_free(NULL); + + ret = jwt_checker_setkey(NULL, JWT_ALG_HS256, NULL); + ck_assert_int_ne(ret, 0); + + /* Create and clear an error */ + ret = jwt_checker_setkey(checker, JWT_ALG_HS256, NULL); + ck_assert_int_ne(ret, 0); + /* Check error exists */ + ck_assert_int_ne(jwt_checker_error(checker), 0); + out = jwt_checker_error_msg(checker); + ck_assert_ptr_nonnull(out); + ck_assert_int_ne(strlen(out), 0); + /* Clear it */ + jwt_checker_error_clear(checker); + /* Check that its cleared */ + ck_assert_int_eq(jwt_checker_error(checker), 0); + out = jwt_checker_error_msg(checker); + ck_assert_ptr_nonnull(out); + ck_assert_int_eq(strlen(out), 0); + + /* Fake it */ + key = (void *)checker; + ret = jwt_checker_setkey(NULL, 0, key); + ck_assert_int_ne(ret, 0); + + ret = jwt_checker_error(NULL); + ck_assert_int_ne(ret, 0); + + out = jwt_checker_error_msg(NULL); + ck_assert_ptr_null(out); + + out = jwt_checker_error_msg(checker); + ck_assert_ptr_nonnull(out); + ck_assert_int_eq(strlen(out), 0); + + jwt_checker_error_clear(NULL); + + ret = jwt_checker_verify(NULL, NULL); + ck_assert_int_ne(ret, 0); + + ret = jwt_checker_verify(checker, NULL); + ck_assert_int_ne(ret, 0); + ck_assert_int_ne(jwt_checker_error(checker), 0); + + /* Fake it */ + out = (void *)checker; + ret = jwt_checker_verify(NULL, out);; + ck_assert_int_ne(ret, 0); + ck_assert_int_ne(jwt_checker_error(checker), 0); +} +END_TEST + +START_TEST(verify_hs256) +{ + jwt_checker_auto_t *checker = NULL; + const char token[] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.CM4dD95Nj" + "0vSfMGtDas432AUW1HAo7feCiAbt5Yjuds"; + int ret; + + SET_OPS(); + + checker = jwt_checker_new(); + ck_assert_ptr_nonnull(checker); + ck_assert_int_eq(jwt_checker_error(checker), 0); + + ret = jwt_checker_setclaims(checker, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + read_json("oct_key_256.json"); + ret = jwt_checker_setkey(checker, JWT_ALG_HS256, g_item); + ck_assert_int_eq(ret, 0); + + ret = jwt_checker_verify(checker, token); + ck_assert_int_eq(ret, 0); + + free_key(); +} +END_TEST + +START_TEST(verify_rsapss384) +{ + jwt_checker_auto_t *checker = NULL; + // GnuTLS created + const char *token1 = "eyJhbGciOiJQUzM4NCIsInR5cCI6IkpXVCJ9.e30.g_OJbEk" + "tbb721dPDZ5hDZHnf8Uk6PiZ8IoatdEGxRc3GBW8xef1jRm_jZYfWh5cEz9Mg" + "mzN0xN3q9wYCjoBrB_UUV4sonbUX4QEmUW5B5M0JJ3KyFhzJtcVrl9pVGT6ZB" + "FLV-Pwmlus7cq73xDVNrdIX0CkZQ-3pkesiOuUsPK62cs6cQS_TrRQe58JWk0" + "CoLIIpaiwZ56uerdPK2uAyDaxRzlVQ_2uKkLjSRCnz4eDHRYJriGtR_bfqIWo" + "_gQHowDh_tTeGcWiMugtp9aU6_ES7VSuS7cQuH_-oYEKwnIcM4O8zV5J9EuYl" + "JDx0M3C2E13dyUxFEKw3nGEcdiZhcA"; + // OpenSSL created + const char *token2 = "eyJhbGciOiJQUzM4NCIsInR5cCI6IkpXVCJ9.e30.gNMKG8F" + "GbaYNmH5CHHfr-ApOoD5AdtCyasjGaIdphyTrfBXZEqMBet3-C3-Bw1N3hPta" + "eN-HpFj5XlDQAy3mmyO0oAiP--NHPKMo09pNNGU18BsAH5ht9SE5Y50AB8Wr1" + "vArRZnds3MDmAVwjcG-YBAy8q8jdUP1G9DyItd32bETq-5xlixCW1Jqk8n5Px" + "6jMalpbYIwGYYr1vcUUbwOSagVu8crtmRaXt_PSy4kUpI4sKtggIYTjoezwy5" + "_B0Tu_cO9xgBe-uOYvJ5rEQk5jen84pBcJ5G8OLorrefX81Vw-AKdD5kdbbqh" + "UXSooe803Mt5G2IpDHAXmOwvVBixjA"; + // JWT.io created + const char *token3 = "eyJhbGciOiJQUzM4NCIsInR5cCI6IkpXVCJ9.e30.XZN5m3p" + "HZnDsdRlR8bmZ1XuSqRBALy5Ikl12KtmtFSCYF22eeIJVwuk_DpKLyhZsiXF_" + "FIQqoKU8wOO984vK1r8nlQyv_6C0TbC-fH5bjdZ8w5esBOyihEjtG5AScbjpw" + "FYCPz_6kayusQqA_FUS_fsjHtg3gKb6viyotlMD6CCengg9aV6TyFMchBL-0_" + "S6u-gBZC_1IJ6-ibPy8ILSVKgi6D8ucI2ZcSP79z8BZv8-HWPSJU70Ef4CEVn" + "Owo3Grx7zAaAuzDEBELggZeW51bOypBCmmaTy3G2txeYhBE5TI88IeYOh-lrE" + "KVQ-ZwOB8dGr0g8wbPl_i9WPdaJV7Q"; + int ret; + + SET_OPS(); + + checker = jwt_checker_new(); + ck_assert_ptr_nonnull(checker); + ck_assert_int_eq(jwt_checker_error(checker), 0); + + ret = jwt_checker_setclaims(checker, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + read_json("rsa_pss_key_2048_384_pub.json"); + ret = jwt_checker_setkey(checker, JWT_ALG_PS384, g_item); + ck_assert_int_eq(ret, 0); + + ret = jwt_checker_verify(checker, token3); + ck_assert_int_eq(ret, 0); + + ret = jwt_checker_verify(checker, token2); + ck_assert_int_eq(ret, 0); + + ret = jwt_checker_verify(checker, token1); + ck_assert_int_eq(ret, 0); + + free_key(); +} +END_TEST + +static int __verify_hs256_wcb(jwt_t *jwt, jwt_config_t *config) +{ + ck_assert_ptr_nonnull(jwt); + ck_assert_ptr_nonnull(config); + ck_assert_int_eq(jwt_get_alg(jwt), JWT_ALG_HS256); + + if (config->ctx != NULL) { + config->key = g_item; + config->alg = JWT_ALG_HS256; + } else { + config->key = NULL; + config->alg = JWT_ALG_HS256; + } + + return 0; +} + +START_TEST(verify_hs256_wcb) +{ + jwt_checker_auto_t *checker = NULL; + const char token[] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.CM4dD95Nj" + "0vSfMGtDas432AUW1HAo7feCiAbt5Yjuds"; + int ret; + + SET_OPS(); + + checker = jwt_checker_new(); + ck_assert_ptr_nonnull(checker); + ck_assert_int_eq(jwt_checker_error(checker), 0); + + ret = jwt_checker_setclaims(checker, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + read_json("oct_key_256.json"); + + ret = jwt_checker_setcb(checker, __verify_hs256_wcb, "testing"); + ck_assert_int_eq(ret, 0); + + ret = jwt_checker_verify(checker, token); + ck_assert_int_eq(ret, 0); + + ret = jwt_checker_setcb(checker, __verify_hs256_wcb, NULL); + ck_assert_int_eq(ret, 0); + + ret = jwt_checker_verify(checker, token); + ck_assert_int_ne(ret, 0); + + free_key(); +} +END_TEST + +START_TEST(verify_hs256_stress) +{ + jwt_checker_auto_t *checker = NULL; + const char token[] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.CM4dD95Nj" + "0vSfMGtDas432AUW1HAo7feCiAbt5Yjuds"; + int ret, i; + + SET_OPS(); + + checker = jwt_checker_new(); + ck_assert_ptr_nonnull(checker); + ck_assert_int_eq(jwt_checker_error(checker), 0); + + ret = jwt_checker_setclaims(checker, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + read_json("oct_key_256.json"); + ret = jwt_checker_setkey(checker, JWT_ALG_HS256, g_item); + ck_assert_int_eq(ret, 0); + + for (i = 0; i < 1000; i++) { + ret = jwt_checker_verify(checker, token); + ck_assert_int_eq(ret, 0); + } + + free_key(); +} +END_TEST + +START_TEST(verify_hs256_fail) +{ + jwt_checker_auto_t *checker = NULL; + const char token[] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.CM4dD95Nj" + "0vSfMGtDas432AUW1HAo7feCiAbt5Yjuds"; + int ret; + + SET_OPS(); + + checker = jwt_checker_new(); + ck_assert_ptr_nonnull(checker); + ck_assert_int_eq(jwt_checker_error(checker), 0); + + ret = jwt_checker_setclaims(checker, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + read_json("oct_key_256_issue1.json"); + ret = jwt_checker_setkey(checker, JWT_ALG_HS256, 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), + "Token failed verification"); + + free_key(); +} +END_TEST + +START_TEST(verify_hs256_fail_stress) +{ + jwt_checker_auto_t *checker = NULL; + const char token[] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.CM4dD95Nj" + "0vSfMGtDas432AUW1HAo7feCiAbt5Yjuds"; + int ret, i; + + SET_OPS(); + + checker = jwt_checker_new(); + ck_assert_ptr_nonnull(checker); + ck_assert_int_eq(jwt_checker_error(checker), 0); + + ret = jwt_checker_setclaims(checker, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + read_json("oct_key_256_issue1.json"); + ret = jwt_checker_setkey(checker, JWT_ALG_HS256, g_item); + ck_assert_int_eq(ret, 0); + + for (i = 0; i < 1000; i++) { + ret = jwt_checker_verify(checker, token); + ck_assert_int_ne(ret, 0); + ck_assert_str_eq(jwt_checker_error_msg(checker), + "Token failed verification"); + } + + free_key(); +} +END_TEST + +START_TEST(claim_str_addgetdel) +{ + const char exp[] = "{\"iss\":\"disk.swissdisk.com\"}"; + jwt_checker_auto_t *checker = NULL; + char_auto *out = NULL; + jwt_value_t jval; + int ret; + + SET_OPS(); + + checker = jwt_checker_new(); + ck_assert_ptr_nonnull(checker); + ck_assert_int_eq(jwt_checker_error(checker), 0); + + ret = jwt_checker_setclaims(checker, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_STR(&jval, "iss", "disk.swissdisk.com"); + ret = jwt_checker_claim_add(checker, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_STR(&jval, "aud", "public"); + ret = jwt_checker_claim_add(checker, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_STR(&jval, "aud", "private"); + ret = jwt_checker_claim_add(checker, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_EXIST); + + jwt_set_ADD_STR(&jval, "aud", "employees"); + jval.replace = 1; + ret = jwt_checker_claim_add(checker, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_GET_STR(&jval, "aud"); + ret = jwt_checker_claim_get(checker, &jval); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jval.str_val); + ck_assert_str_eq(jval.str_val, "employees"); + + jwt_set_GET_INT(&jval, "aud"); + ret = jwt_checker_claim_get(checker, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_TYPE); + + jwt_set_GET_BOOL(&jval, "aud"); + ret = jwt_checker_claim_get(checker, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_TYPE); + + ret = jwt_checker_claim_del(checker, "aud"); + ck_assert_int_eq(ret, 0); + + jwt_set_GET_STR(&jval, "aud"); + ret = jwt_checker_claim_get(checker, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_NOEXIST); + + jwt_set_GET_JSON(&jval, NULL); + ret = jwt_checker_claim_get(checker, &jval); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jval.str_val); + ck_assert_str_eq(jval.json_val, exp); + free(jval.json_val); +} +END_TEST + +START_TEST(claim_int_addgetdel) +{ + const char exp[] = "{\"nbf\":1475980545}"; + jwt_checker_auto_t *checker = NULL; + char_auto *out = NULL; + jwt_value_t jval; + int ret; + + SET_OPS(); + + checker = jwt_checker_new(); + ck_assert_ptr_nonnull(checker); + ck_assert_int_eq(jwt_checker_error(checker), 0); + + ret = jwt_checker_setclaims(checker, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_INT(&jval, "nbf", TS_CONST); + ret = jwt_checker_claim_add(checker, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_INT(&jval, "exp", TS_CONST); + ret = jwt_checker_claim_add(checker, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_INT(&jval, "exp", TS_CONST + 360); + ret = jwt_checker_claim_add(checker, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_EXIST); + + jwt_set_ADD_INT(&jval, "exp", TS_CONST + 480); + jval.replace = 1; + ret = jwt_checker_claim_add(checker, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_GET_INT(&jval, "exp"); + ret = jwt_checker_claim_get(checker, &jval); + ck_assert_int_eq(ret, 0); + ck_assert_int_eq(jval.int_val, TS_CONST + 480); + + jwt_set_GET_STR(&jval, "exp"); + ret = jwt_checker_claim_get(checker, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_TYPE); + + jwt_set_GET_BOOL(&jval, "exp"); + ret = jwt_checker_claim_get(checker, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_TYPE); + + ret = jwt_checker_claim_del(checker, "exp"); + ck_assert_int_eq(ret, 0); + + jwt_set_GET_INT(&jval, "exp"); + ret = jwt_checker_claim_get(checker, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_NOEXIST); + + jwt_set_GET_JSON(&jval, NULL); + ret = jwt_checker_claim_get(checker, &jval); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jval.str_val); + ck_assert_str_eq(jval.json_val, exp); + free(jval.json_val); +} +END_TEST + +START_TEST(claim_bool_addgetdel) +{ + const char exp[] = "{\"admin\":true}"; + jwt_checker_auto_t *checker = NULL; + char_auto *out = NULL; + jwt_value_t jval; + int ret; + + SET_OPS(); + + checker = jwt_checker_new(); + ck_assert_ptr_nonnull(checker); + ck_assert_int_eq(jwt_checker_error(checker), 0); + + ret = jwt_checker_setclaims(checker, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_BOOL(&jval, "admin", 1); + ret = jwt_checker_claim_add(checker, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_BOOL(&jval, "sudo", 1); + ret = jwt_checker_claim_add(checker, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_BOOL(&jval, "sudo", 0); + ret = jwt_checker_claim_add(checker, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_EXIST); + + jwt_set_ADD_BOOL(&jval, "sudo", 0); + jval.replace = 1; + ret = jwt_checker_claim_add(checker, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_GET_BOOL(&jval, "sudo"); + ret = jwt_checker_claim_get(checker, &jval); + ck_assert_int_eq(ret, 0); + ck_assert_int_eq(jval.bool_val, 0); + + jwt_set_GET_STR(&jval, "sudo"); + ret = jwt_checker_claim_get(checker, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_TYPE); + + jwt_set_GET_INT(&jval, "sudo"); + ret = jwt_checker_claim_get(checker, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_TYPE); + + ret = jwt_checker_claim_del(checker, "sudo"); + ck_assert_int_eq(ret, 0); + + jwt_set_GET_BOOL(&jval, "sudo"); + ret = jwt_checker_claim_get(checker, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_NOEXIST); + + jwt_set_GET_JSON(&jval, NULL); + ret = jwt_checker_claim_get(checker, &jval); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jval.str_val); + ck_assert_str_eq(jval.json_val, exp); + free(jval.json_val); +} +END_TEST + +START_TEST(claim_json_addgetdel) +{ + const char exp[] = "{\"rooms\":[\"office\",\"war-room\"]}"; + jwt_checker_auto_t *checker = NULL; + char_auto *out = NULL; + jwt_value_t jval; + int ret; + + SET_OPS(); + + checker = jwt_checker_new(); + ck_assert_ptr_nonnull(checker); + ck_assert_int_eq(jwt_checker_error(checker), 0); + + ret = jwt_checker_setclaims(checker, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_JSON(&jval, "rooms", + "[\"office\",\"war-room\"]"); + ret = jwt_checker_claim_add(checker, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_GET_JSON(&jval, "rooms"); + ret = jwt_checker_claim_get(checker, &jval); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jval.json_val); + ck_assert_str_eq(jval.json_val, "[\"office\",\"war-room\"]"); + free(jval.json_val); + + jwt_set_ADD_JSON(&jval, "buildings", + "{\"main\":\"dallas\",\"accounting\":\"houston\"}"); + ret = jwt_checker_claim_add(checker, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_JSON(&jval, "buildings", "{\"hq\": 0}"); + ret = jwt_checker_claim_add(checker, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_EXIST); + + jwt_set_ADD_JSON(&jval, "buildings", "{\"hq\": 1}"); + jval.replace = 1; + ret = jwt_checker_claim_add(checker, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_GET_JSON(&jval, "buildings"); + ret = jwt_checker_claim_get(checker, &jval); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jval.json_val); + ck_assert_str_eq(jval.json_val, "{\"hq\":1}"); + free(jval.json_val); + + jwt_set_GET_STR(&jval, "buildings"); + ret = jwt_checker_claim_get(checker, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_TYPE); + + jwt_set_GET_INT(&jval, "buildings"); + ret = jwt_checker_claim_get(checker, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_TYPE); + + ret = jwt_checker_claim_del(checker, "buildings"); + ck_assert_int_eq(ret, 0); + + jwt_set_GET_JSON(&jval, "buildings"); + ret = jwt_checker_claim_get(checker, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_NOEXIST); + + jwt_set_GET_JSON(&jval, NULL); + ret = jwt_checker_claim_get(checker, &jval); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jval.str_val); + ck_assert_str_eq(jval.json_val, exp); + free(jval.json_val); +} + +START_TEST(header_str_addgetdel) +{ + const char exp[] = "{}"; + jwt_checker_auto_t *checker = NULL; + char_auto *out = NULL; + jwt_value_t jval; + int ret; + + SET_OPS(); + + checker = jwt_checker_new(); + ck_assert_ptr_nonnull(checker); + ck_assert_int_eq(jwt_checker_error(checker), 0); + + ret = jwt_checker_setclaims(checker, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + jwt_set_ADD_STR(&jval, "typ", "Custom"); + ret = jwt_checker_header_add(checker, &jval); + ck_assert_int_eq(ret, 0); + + jwt_set_GET_JSON(&jval, NULL); + ret = jwt_checker_claim_get(checker, &jval); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jval.str_val); + ck_assert_str_eq(jval.json_val, exp); + free(jval.json_val); + + jwt_set_GET_STR(&jval, "typ"); + ret = jwt_checker_header_get(checker, &jval); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jval.str_val); + ck_assert_str_eq(jval.str_val, "Custom"); + + ret = jwt_checker_header_del(checker, "typ"); + + jwt_set_GET_STR(&jval, "typ"); + ret = jwt_checker_header_get(checker, &jval); + ck_assert_int_eq(ret, JWT_VALUE_ERR_NOEXIST); +} +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("New"); + tcase_add_loop_test(tc_core, new, 0, i); + suite_add_tcase(s, tc_core); + + tc_core = tcase_create("Verify"); + tcase_add_loop_test(tc_core, verify, 0, i); + tcase_add_loop_test(tc_core, verify_rsapss384, 0, i); + tcase_add_loop_test(tc_core, verify_wcb, 0, i); + tcase_add_loop_test(tc_core, verify_stress, 0, i); + suite_add_tcase(s, tc_core); + + tc_core = tcase_create("Error Handling"); + tcase_add_loop_test(tc_core, null_handling, 0, i); + suite_add_tcase(s, tc_core); + + tc_core = tcase_create("HS256 Key Verify"); + tcase_add_loop_test(tc_core, verify_hs256, 0, i); + tcase_add_loop_test(tc_core, verify_hs256_wcb, 0, i); + tcase_add_loop_test(tc_core, verify_hs256_stress, 0, i); + tcase_add_loop_test(tc_core, verify_hs256_fail, 0, i); + tcase_add_loop_test(tc_core, verify_hs256_fail_stress, 0, i); + suite_add_tcase(s, tc_core); + + tc_core = tcase_create("Claims AddGetDel"); + tcase_add_loop_test(tc_core, claim_str_addgetdel, 0, i); + tcase_add_loop_test(tc_core, claim_int_addgetdel, 0, i); + tcase_add_loop_test(tc_core, claim_bool_addgetdel, 0, i); + tcase_add_loop_test(tc_core, claim_json_addgetdel, 0, i); + suite_add_tcase(s, tc_core); + + tc_core = tcase_create("Header AddGetDel"); + /* All of the code paths for str/int/bool/json have been covered. We + * just run this to ensure add/get/del works on headers */ + tcase_add_loop_test(tc_core, header_str_addgetdel, 0, i); + suite_add_tcase(s, tc_core); + + return s; +} + +int main(void) +{ + JWT_TEST_MAIN("LibJWT Checker"); +} diff --git a/tests/jwt_dump.c b/tests/jwt_dump.c deleted file mode 100644 index c7356eab..00000000 --- a/tests/jwt_dump.c +++ /dev/null @@ -1,327 +0,0 @@ -/* Public domain, no copyright. Use at your own risk. */ - -#include -#include -#include -#include -#include -#include - -#include "jwt_tests.h" - -static void *test_malloc(size_t size) -{ - return malloc(size); -} - -static void test_free(void *ptr) -{ - free(ptr); -} - -static void *test_realloc(void *ptr, size_t size) -{ - return realloc(ptr, size); -} - -static int test_set_alloc(void) -{ - return jwt_set_alloc(test_malloc, test_realloc, test_free); -} - -#ifdef JWT_CONSTRUCTOR -START_TEST(test_jwt_crypto_ops) -{ - const char *msg = getenv("JWT_CRYPTO"); - - ck_assert_str_eq(msg, "NONEXISTENT"); -} -END_TEST -#endif - -START_TEST(test_alloc_funcs) -{ - jwt_malloc_t m = NULL; - jwt_realloc_t r = NULL; - jwt_free_t f = NULL; - int ret; - - SET_OPS(); - - jwt_get_alloc(&m, &r, &f); - ck_assert_ptr_null(m); - ck_assert_ptr_null(r); - ck_assert_ptr_null(f); - - ret = test_set_alloc(); - ck_assert_int_eq(ret, 0); - - jwt_get_alloc(&m, &r, &f); - ck_assert(m == test_malloc); - ck_assert(r == test_realloc); - ck_assert(f == test_free); -} -END_TEST - -START_TEST(test_jwt_dump_str) -{ - jwt_value_t jval; - jwt_t *jwt = NULL; - int ret = 0; - - SET_OPS(); - - ret = test_set_alloc(); - ck_assert_int_eq(ret, 0); - - jwt = jwt_create(NULL); - ck_assert_ptr_nonnull(jwt); - - jwt_set_ADD_STR(&jval, "iss", "files.maclara-llc.com"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "sub", "user0"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_INT(&jval, "iat", (long)time(NULL)); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - /* Test 'typ' header: should not be present, cause 'alg' is JWT_ALG_NONE. */ - jwt_set_GET_STR(&jval, "typ"); - ret = jwt_header_get(jwt, &jval); - ck_assert_int_eq(ret, JWT_VALUE_ERR_NOEXIST); - ck_assert_ptr_null(jval.str_val); - - /* Test 'typ' header: should not be present, cause 'alg' is JWT_ALG_NONE. */ - jwt_set_GET_STR(&jval, "typ"); - ret = jwt_header_get(jwt, &jval); - ck_assert_int_eq(ret, JWT_VALUE_ERR_NOEXIST); - ck_assert_ptr_null(jval.str_val); - - jwt_free(jwt); -} -END_TEST - -#define JSON_GRANTS_PRETTY "{\n" \ - " \"%s\": %ld,\n" \ - " \"%s\": \"%s\",\n" \ - " \"%s\": \"%s\",\n" \ - " \"%s\": \"%s\"\n" \ - "}" - -#define JSON_GRANTS_COMPACT "{\"%s\":%ld,\"%s\":\"%s\"," \ - "\"%s\":\"%s\",\"%s\":\"%s\"}" - - -START_TEST(test_jwt_dump_grants_str) -{ - jwt_value_t jval; - jwt_t *jwt = NULL; - int ret = 0; - char *out; - long timestamp = (long)time(NULL); - char buf[1024]; - - SET_OPS(); - - ret = test_set_alloc(); - ck_assert_int_eq(ret, 0); - - jwt = jwt_create(NULL); - ck_assert_ptr_nonnull(jwt); - - jwt_set_ADD_STR(&jval, "iss", "files.maclara-llc.com"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "sub", "user0"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_INT(&jval, "iat", timestamp); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_GET_JSON(&jval, NULL); - jval.pretty = 1; - ret = jwt_grant_get(jwt, &jval); - ck_assert_int_eq(ret, 0); - out = jval.json_val; - ck_assert_ptr_nonnull(out); - - /* Sorted Keys are expected */ - snprintf(buf, sizeof(buf), JSON_GRANTS_PRETTY, - "iat", timestamp, - "iss", "files.maclara-llc.com", - "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC", - "sub", "user0"); - ck_assert_str_eq(out, buf); - - free(out); - - jwt_set_GET_JSON(&jval, NULL); - ret = jwt_grant_get(jwt, &jval); - ck_assert_int_eq(ret, 0); - out = jval.json_val; - ck_assert_ptr_nonnull(out); - - /* Sorted Keys are expected */ - snprintf(buf, sizeof(buf), JSON_GRANTS_COMPACT, - "iat", timestamp, - "iss", "files.maclara-llc.com", - "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC", - "sub", "user0"); - ck_assert_str_eq(out, buf); - - free(out); - - jwt_free(jwt); -} -END_TEST - -START_TEST(test_jwt_dump_str_alg_default_typ_header) -{ - jwt_value_t jval; - jwt_test_auto_t *jwt = NULL; - char *out = NULL; - int ret = 0; - - SET_OPS(); - - ret = test_set_alloc(); - ck_assert_int_eq(ret, 0); - - CREATE_JWT(jwt, "oct_key_256.json", JWT_ALG_HS256); - - jwt_set_ADD_STR(&jval, "iss", "files.maclara-llc.com"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "sub", "user0"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_INT(&jval, "iat", (long)time(NULL)); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - /* - * Test 'typ' header: should not be present, cause jwt's header has - * not been touched yet by jwt_write_head, this is only called as a - * result of calling jwt_dump* methods. - */ - jwt_set_GET_STR(&jval, "typ"); - ret = jwt_header_get(jwt, &jval); - ck_assert_int_ne(ret, 0); - ck_assert_ptr_null(jval.str_val); - - out = jwt_encode_str(jwt); - ck_assert_ptr_nonnull(out); - - /* - * Test 'typ' header: should be added with default value of 'JWT', - * cause 'alg' is set explicitly and jwt's header has been processed - * by jwt_write_head. - */ - jwt_set_GET_STR(&jval, "typ"); - ret = jwt_header_get(jwt, &jval); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jval.str_val); - ck_assert_str_eq(jval.str_val, "JWT"); -} -END_TEST - -START_TEST(test_jwt_dump_str_alg_custom_typ_header) -{ - jwt_value_t jval; - jwt_test_auto_t *jwt = NULL; - int ret = 0; - - SET_OPS(); - - ret = test_set_alloc(); - ck_assert_int_eq(ret, 0); - - CREATE_JWT(jwt, "oct_key_256.json", JWT_ALG_HS256); - - jwt_set_ADD_STR(&jval, "iss", "files.maclara-llc.com"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "sub", "user0"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_INT(&jval, "iat", (long)time(NULL)); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "typ", "favourite"); - ret = jwt_header_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - /* Test that 'typ' header has been added. */ - jwt_set_GET_STR(&jval, "typ"); - ret = jwt_header_get(jwt, &jval); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jval.str_val); - ck_assert_str_eq(jval.str_val, "favourite"); - - /* Test 'typ' header: should be left untouched. */ - jwt_set_GET_STR(&jval, "typ"); - ret = jwt_header_get(jwt, &jval); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jval.str_val); - ck_assert_str_eq(jval.str_val, "favourite"); -} -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_dump"); - -#ifdef JWT_CONSTRUCTOR - tcase_add_test(tc_core, test_jwt_crypto_ops); -#endif - tcase_add_loop_test(tc_core, test_alloc_funcs, 0, i); - tcase_add_loop_test(tc_core, test_jwt_dump_str, 0, i); - tcase_add_loop_test(tc_core, test_jwt_dump_grants_str, 0, i); - tcase_add_loop_test(tc_core, test_jwt_dump_str_alg_default_typ_header, 0, i); - tcase_add_loop_test(tc_core, test_jwt_dump_str_alg_custom_typ_header, 0, i); - - tcase_set_timeout(tc_core, 30); - - suite_add_tcase(s, tc_core); - - return s; -} - -int main(void) -{ - JWT_TEST_MAIN("LibJWT Dump"); -} diff --git a/tests/jwt_ec.c b/tests/jwt_ec.c index 64f00a85..9ffa1576 100644 --- a/tests/jwt_ec.c +++ b/tests/jwt_ec.c @@ -8,132 +8,99 @@ #include "jwt_tests.h" -/* NOTE: ES signing will generate a different signature every time, so can't - * be simply string compared for verification like we do with RS. */ - -static const char jwt_es256[] = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQ" - "iOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJlZiI6Ilh" - "YWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0.IONoUPo6QhHwcx1" - "N1TD4DnrjvmB-9lSX6qrn_WPrh3DBum-qKP66MIF9tgymy7hCoU6dvUW8zKK0AyVH3iD" - "1uA"; - -static const char jwt_es384[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzM4NCJ9.eyJpYXQ" - "iOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLmN5cGhyZS5jb20iLCJyZWYiOiJYWFhYLVl" - "ZWVktWlpaWi1BQUFBLUNDQ0MiLCJzdWIiOiJ1c2VyMCJ9.p6McjolhuIqel0DWaI2OrD" - "oRYcxgSMnGFirdKT5jXpe9L801HBkouKBJSae8F7LLFUKiE2VVX_514WzkuExLQs2eB1" - "L2Qahid5VFOK3hc7HcBL-rcCXa8d2tf_MudyrM"; - -static const char jwt_es512[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJpYXQ" - "iOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLmN5cGhyZS5jb20iLCJyZWYiOiJYWFhYLVl" - "ZWVktWlpaWi1BQUFBLUNDQ0MiLCJzdWIiOiJ1c2VyMCJ9.Abs-SriTqd9NAO-bJb-B3U" - "zF1W8JmoutfHQpMqJnkPHyasVVuKN-I-6RibSv-qxgTxuzlo0u5dCt4mOw7w8mgEnMAS" - "zsjm-NlOPUBjIUD9T592lse9OOF6TjPOQbijqeMc6qFZ8q5YhxvxBXHO6PuImkJpEWj4" - "Zda8lNTxqHol7vorg9"; - -static const char jwt_es_invalid[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJpYXQ" - "iOjE0NzU5ODA1IAmCornholio6ImZpbGVzLmN5cGhyZS5jb20iLCJyZWYiOiJYWFhYLVl" - "PN9G9tV75ylfWvcwkF20bQA9m1vDbUIl8PIK8Q"; - -START_TEST(test_jwt_encode_es256) +START_TEST(test_jwks_ec_pub_missing) { - SET_OPS(); - __test_alg_key(JWT_ALG_ES256, "ec_key_prime256v1.json", - "ec_key_prime256v1_pub.json"); -} -END_TEST + const char *json = "{\"kty\":\"EC\"}"; + jwk_set_t *jwk_set = NULL; + const jwk_item_t *item; + const char exp[] = "Missing or invalid type for one of crv, x, or y for pub key"; -START_TEST(test_jwt_verify_es256) -{ SET_OPS(); - __verify_jwt(jwt_es256, JWT_ALG_ES256, "ec_key_prime256v1_pub.json"); -} -END_TEST -START_TEST(test_jwt_encode_es384) -{ - SET_OPS(); - __test_alg_key(JWT_ALG_ES384, "ec_key_secp384r1.json", "ec_key_secp384r1_pub.json"); -} -END_TEST + jwk_set = jwks_create(json); -START_TEST(test_jwt_verify_es384) -{ - SET_OPS(); - __verify_jwt(jwt_es384, JWT_ALG_ES384, "ec_key_secp384r1_pub.json"); -} -END_TEST + ck_assert_ptr_nonnull(jwk_set); + ck_assert(!jwks_error(jwk_set)); -START_TEST(test_jwt_encode_es512) -{ - SET_OPS(); - __test_alg_key(JWT_ALG_ES512, "ec_key_secp521r1.json", "ec_key_secp521r1_pub.json"); -} -END_TEST + item = jwks_item_get(jwk_set, 0); + ck_assert_ptr_nonnull(item); + ck_assert_int_ne(jwks_item_error(item), 0); -START_TEST(test_jwt_verify_es512) -{ - SET_OPS(); - __verify_jwt(jwt_es512, JWT_ALG_ES512, "ec_key_secp521r1_pub.json"); + ck_assert_str_eq(exp, jwks_item_error_msg(item)); + + jwks_free(jwk_set); } END_TEST -START_TEST(test_jwt_encode_ec_with_rsa) +START_TEST(test_jwks_ec_pub_bad_type) { - JWT_CONFIG_DECLARE(config); - jwt_test_auto_t *jwt = NULL; + const char *json = "{\"kty\":\"EC\",\"crv\":\"prime6v1\",\"x\":\"sd+#(@#($(ada\",\"y\":1}"; + jwk_set_t *jwk_set = NULL; + const jwk_item_t *item; + const char exp[] = "Missing or invalid type for one of crv, x, or y for pub key"; SET_OPS(); - read_key("rsa_key_4096.json"); - config.alg = JWT_ALG_ES384; - config.jw_key = g_item; - jwt = jwt_create(&config); - ck_assert_int_ne(jwt_error(jwt), 0); - ck_assert_ptr_nonnull(jwt); - ck_assert_str_eq(jwt_error_msg(jwt), - "Config alg does not match key alg"); -} -END_TEST + jwk_set = jwks_create(json); -START_TEST(test_jwt_verify_invalid_token) -{ - jwt_t *jwt = NULL; + ck_assert_ptr_nonnull(jwk_set); + ck_assert(!jwks_error(jwk_set)); - SET_OPS(); + item = jwks_item_get(jwk_set, 0); + ck_assert_ptr_nonnull(item); + ck_assert_int_ne(jwks_item_error(item), 0); + + ck_assert_str_eq(exp, jwks_item_error_msg(item)); - read_key("ec_key_secp384r1.json"); - jwt = jwt_verify(jwt_es_invalid, &t_config); - free_key(); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_ne(jwt_error(jwt), 0); + jwks_free(jwk_set); } END_TEST -START_TEST(test_jwt_verify_invalid_alg) +START_TEST(test_jwks_ec_pub_bad64) { - jwt_t *jwt = NULL; + const char *json = "{\"kty\":\"EC\",\"crv\":\"prime6v1\",\"x\":\"\",\"y\":\"asaad\"}"; + jwk_set_t *jwk_set = NULL; + const jwk_item_t *item; + const char exp[] = "Error generating pub key from components"; SET_OPS(); - read_key("ec_key_secp384r1.json"); - jwt = jwt_verify(jwt_es256, &t_config); - free_key(); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_ne(jwt_error(jwt), 0); + jwk_set = jwks_create(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(jwks_item_error(item), 0); + + ck_assert_str_eq(exp, jwks_item_error_msg(item)); + + jwks_free(jwk_set); } END_TEST -START_TEST(test_jwt_verify_invalid_cert) +START_TEST(test_jwks_ec_pub_bad_points) { - jwt_t *jwt = NULL; + const char *json = "{\"kty\":\"EC\",\"crv\":\"prime256v1\",\"x\":\"YmFkdmFsdWUK\",\"y\":\"YmFkdmFsdWUK\"}"; + jwk_set_t *jwk_set = NULL; + const jwk_item_t *item; + const char exp[] = "Error generating pub key from components"; SET_OPS(); - read_key("ec_key_secp521r1_pub.json"); - jwt = jwt_verify(jwt_es256, &t_config); - free_key(); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_ne(jwt_error(jwt), 0); + jwk_set = jwks_create(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(jwks_item_error(item), 0); + + ck_assert_str_eq(exp, jwks_item_error_msg(item)); + + jwks_free(jwk_set); } END_TEST @@ -145,18 +112,13 @@ static Suite *libjwt_suite(const char *title) s = suite_create(title); - tc_core = tcase_create("jwt_ec"); + tc_core = tcase_create("jwt_jwks_ec"); - tcase_add_loop_test(tc_core, test_jwt_encode_es256, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_es256, 0, i); - tcase_add_loop_test(tc_core, test_jwt_encode_es384, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_es384, 0, i); - tcase_add_loop_test(tc_core, test_jwt_encode_es512, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_es512, 0, i); - tcase_add_loop_test(tc_core, test_jwt_encode_ec_with_rsa, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_invalid_token, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_invalid_alg, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_invalid_cert, 0, i); + /* EC specific error path tests */ + tcase_add_loop_test(tc_core, test_jwks_ec_pub_missing, 0, i); + tcase_add_loop_test(tc_core, test_jwks_ec_pub_bad64, 0, i); + tcase_add_loop_test(tc_core, test_jwks_ec_pub_bad_type, 0, i); + tcase_add_loop_test(tc_core, test_jwks_ec_pub_bad_points, 0, i); tcase_set_timeout(tc_core, 30); @@ -167,5 +129,5 @@ static Suite *libjwt_suite(const char *title) int main(void) { - JWT_TEST_MAIN("LibJWT EC Sign/Verify"); + JWT_TEST_MAIN("LibJWT JWKS Error Path Testing EC"); } diff --git a/tests/jwt_eddsa.c b/tests/jwt_eddsa.c deleted file mode 100644 index 24487ad8..00000000 --- a/tests/jwt_eddsa.c +++ /dev/null @@ -1,73 +0,0 @@ -/* Public domain, no copyright. Use at your own risk. */ - -#include -#include -#include -#include -#include - -#include "jwt_tests.h" - -static const char jwt_eddsa[] = "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpY" - "XQiOjE3MzYxMjA1OTIsImlzcyI6ImRpc2suc3dpc3NkaXNrLmNvbSIsInN1YiI6InV" - "zZXIwIn0.m28yyGiulqE9CUbZ64oSlec7TglR6DvVWohayJvJsJzk65RLx99gycRig" - "aYjKKNe0e0Fff3BsIAlh3A-ptkmAg"; - -START_TEST(test_jwt_encode_eddsa) -{ - SET_OPS(); - __test_alg_key(JWT_ALG_EDDSA, "eddsa_key_ed25519.json", - "eddsa_key_ed25519_pub.json"); -} -END_TEST - -START_TEST(test_jwt_verify_eddsa) -{ - SET_OPS(); - __verify_jwt(jwt_eddsa, JWT_ALG_EDDSA, "eddsa_key_ed25519_pub.json"); -} -END_TEST - -START_TEST(test_jwt_encode_eddsa_with_rsa) -{ - JWT_CONFIG_DECLARE(config); - jwt_test_auto_t *jwt = NULL; - - SET_OPS(); - - read_key("rsa_key_4096.json"); - config.alg = JWT_ALG_EDDSA; - config.jw_key = g_item; - jwt = jwt_create(&config); - ck_assert_int_ne(jwt_error(jwt), 0); - ck_assert_ptr_nonnull(jwt); - ck_assert_str_eq(jwt_error_msg(jwt), - "Config alg does not match key alg"); -} -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_eddsa"); - - tcase_add_loop_test(tc_core, test_jwt_encode_eddsa, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_eddsa, 0, i); - tcase_add_loop_test(tc_core, test_jwt_encode_eddsa_with_rsa, 0, i); - - tcase_set_timeout(tc_core, 30); - - suite_add_tcase(s, tc_core); - - return s; -} - -int main(void) -{ - JWT_TEST_MAIN("LibJWT EdDSA Sign/Verify"); -} diff --git a/tests/jwt_encode.c b/tests/jwt_encode.c deleted file mode 100644 index adba113a..00000000 --- a/tests/jwt_encode.c +++ /dev/null @@ -1,386 +0,0 @@ -/* Public domain, no copyright. Use at your own risk. */ - -#include -#include -#include -#include -#include -#include - -#include "jwt_tests.h" -START_TEST(test_jwt_encode_fp) -{ - const char exp[] = "eyJhbGciOiJub25lIn0.eyJpYXQiOjE0NzU5ODA1NDUsIml" - "zcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJlZiI6IlhYWFgtWVlZW" - "S1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0."; - char read_back[BUFSIZ]; - jwt_value_t jval; - FILE *out; - jwt_t *jwt = NULL; - int ret = 0; - - SET_OPS(); - - EMPTY_JWT(jwt); - - jwt_set_ADD_STR(&jval, "iss", "files.maclara-llc.com"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "sub", "user0"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_INT(&jval, "iat", TS_CONST); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - out = fopen("test_outfile.txt", "w"); - ck_assert_ptr_nonnull(out); - - ret = jwt_encode_fp(jwt, out); - ck_assert_int_eq(ret, 0); - fclose(out); - - out = fopen("test_outfile.txt", "r"); - ck_assert_ptr_nonnull(out); - ret = fread(read_back, 1, sizeof(read_back), out); - ck_assert_int_gt(ret, 0); - read_back[ret] = '\0'; - fclose(out); - unlink("test_outfile.txt"); - - ck_assert_str_eq(exp, read_back); - - jwt_free(jwt); -} -END_TEST - -START_TEST(test_jwt_encode_str) -{ - const char res[] = "eyJhbGciOiJub25lIn0.eyJpYXQiOjE0NzU5ODA1NDUsIml" - "zcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJlZiI6IlhYWFgtWVlZW" - "S1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0."; - jwt_value_t jval; - jwt_t *jwt = NULL; - int ret = 0; - char *out; - - SET_OPS(); - - EMPTY_JWT(jwt); - - jwt_set_ADD_STR(&jval, "iss", "files.maclara-llc.com"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "sub", "user0"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_INT(&jval, "iat", TS_CONST); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - out = jwt_encode_str(jwt); - ck_assert_ptr_ne(out, NULL); - - ck_assert_str_eq(out, res); - - free(out); - - jwt_free(jwt); -} -END_TEST - -START_TEST(test_jwt_encode_alg_none) -{ - const char res[] = "eyJhbGciOiJub25lIn0.eyJhdWQiOiJ3d3cucGx1Z2dlcnM" - "ubmwiLCJleHAiOjE0Nzc1MTQ4MTIsInN1YiI6IlBsdWdnZXJzIFNvZnR3Y" - "XJlIn0."; - jwt_t *jwt = NULL; - jwt_value_t jval; - int ret = 0; - char *out; - - SET_OPS(); - - EMPTY_JWT(jwt); - - jwt_set_ADD_STR(&jval, "aud", "www.pluggers.nl"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_INT(&jval, "exp", 1477514812); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "sub", "Pluggers Software"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - out = jwt_encode_str(jwt); - ck_assert_ptr_ne(out, NULL); - - ck_assert_str_eq(out, res); - - free(out); - - jwt_free(jwt); -} -END_TEST - -START_TEST(test_jwt_encode_hs256) -{ - const char res[] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOj" - "E0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJl" - "ZiI6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn" - "0.JgSDx8Xwc6tjMDglRndhLeAbjPPrTNoK6uc_E_TDu_o"; - jwt_test_auto_t *jwt = NULL; - jwt_value_t jval; - int ret = 0; - char *out; - - SET_OPS(); - - CREATE_JWT(jwt, "oct_key_256.json", JWT_ALG_HS256); - - jwt_set_ADD_STR(&jval, "iss", "files.maclara-llc.com"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "sub", "user0"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_INT(&jval, "iat", TS_CONST); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - out = jwt_encode_str(jwt); - ck_assert_ptr_ne(out, NULL); - - ck_assert_str_eq(out, res); - - free(out); -} -END_TEST - -START_TEST(test_jwt_encode_hs384) -{ - const char res[] = "eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJpYXQiOj" - "E0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJl" - "ZiI6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn" - "0.sI0hzVmaMsnfKjEGsANdMNPUfe_Pk1JPY_aixKCxVvCy25B0ADUBQdKz" - "6VIUPmG_"; - jwt_test_auto_t *jwt = NULL; - jwt_value_t jval; - int ret = 0; - char *out; - - SET_OPS(); - - CREATE_JWT(jwt, "oct_key_384.json", JWT_ALG_HS384); - - jwt_set_ADD_STR(&jval, "iss", "files.maclara-llc.com"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "sub", "user0"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_INT(&jval, "iat", TS_CONST); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - out = jwt_encode_str(jwt); - ck_assert_ptr_ne(out, NULL); - - ck_assert_str_eq(out, res); - - free(out); -} -END_TEST - -START_TEST(test_jwt_encode_hs512) -{ - const char res[] = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQiOj" - "E0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJl" - "ZiI6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn" - "0.qQ1ghQaPvzIRnzGgUPmvqEk0NlcMYjeZuna8xQLfKtZ52VHCaT-FS8T0" - "O2O_O9NQyqnA3sNnDaSsTxq1fEuDLA"; - jwt_test_auto_t *jwt = NULL; - jwt_value_t jval; - int ret = 0; - char *out; - - SET_OPS(); - - CREATE_JWT(jwt, "oct_key_512.json", JWT_ALG_HS512); - - jwt_set_ADD_STR(&jval, "iss", "files.maclara-llc.com"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "sub", "user0"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_INT(&jval, "iat", TS_CONST); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - out = jwt_encode_str(jwt); - ck_assert_ptr_ne(out, NULL); - - ck_assert_str_eq(out, res); - - free(out); -} -END_TEST - -START_TEST(test_jwt_encode_change_alg) -{ - const char res[] = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQiOj" - "E0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJl" - "ZiI6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn" - "0.qQ1ghQaPvzIRnzGgUPmvqEk0NlcMYjeZuna8xQLfKtZ52VHCaT-FS8T0" - "O2O_O9NQyqnA3sNnDaSsTxq1fEuDLA"; - jwt_test_auto_t *jwt = NULL; - jwt_value_t jval; - int ret = 0; - char *out; - - SET_OPS(); - - CREATE_JWT(jwt, "oct_key_512.json", JWT_ALG_HS512); - - jwt_set_ADD_STR(&jval, "iss", "files.maclara-llc.com"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "sub", "user0"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_INT(&jval, "iat", TS_CONST); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - out = jwt_encode_str(jwt); - ck_assert_ptr_ne(out, NULL); - - ck_assert_str_eq(out, res); - - free(out); -} -END_TEST - -START_TEST(test_jwt_encode_decode) -{ - jwt_test_auto_t *mytoken = NULL; - jwt_auto_t *ymtoken = NULL; - jwt_value_t jval; - char *encoded; - - SET_OPS(); - - CREATE_JWT(mytoken, "oct_key_256.json", JWT_ALG_HS256); - jwt_set_ADD_STR(&jval, "sub", "user0"); - jwt_grant_add(mytoken, &jval); - jwt_set_ADD_INT(&jval, "iat", 1619130517); - jwt_grant_add(mytoken, &jval); - jwt_set_ADD_INT(&jval, "exp", 1619216917); - jwt_grant_add(mytoken, &jval); - - encoded = jwt_encode_str(mytoken); - - t_config.alg = JWT_ALG_HS256; - ymtoken = jwt_verify(encoded, &t_config); - ck_assert_ptr_nonnull(ymtoken); - ck_assert_int_eq(jwt_error(ymtoken), 0); - - free(encoded); -} -END_TEST - -START_TEST(test_jwt_encode_too_short) -{ - jwt_test_auto_t *mytoken; - jwt_value_t jval; - char *encoded; - - SET_OPS(); - - CREATE_JWT(mytoken, "oct_key_512_bad.json", JWT_ALG_HS512); - jwt_set_ADD_STR(&jval, "sub", "user0"); - jwt_grant_add(mytoken, &jval); - jwt_set_ADD_INT(&jval, "iat", 1619130517); - jwt_grant_add(mytoken, &jval); - jwt_set_ADD_INT(&jval, "exp", 1619216917); - jwt_grant_add(mytoken, &jval); - - encoded = jwt_encode_str(mytoken); - ck_assert_ptr_null(encoded); - ck_assert_int_ne(jwt_error(mytoken), 0); - ck_assert_str_eq(jwt_error_msg(mytoken), - "Key too short for HS512: 256 bits"); -} -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_encode"); - - tcase_add_loop_test(tc_core, test_jwt_encode_fp, 0, i); - tcase_add_loop_test(tc_core, test_jwt_encode_str, 0, i); - tcase_add_loop_test(tc_core, test_jwt_encode_alg_none, 0, i); - tcase_add_loop_test(tc_core, test_jwt_encode_hs256, 0, i); - tcase_add_loop_test(tc_core, test_jwt_encode_hs384, 0, i); - tcase_add_loop_test(tc_core, test_jwt_encode_hs512, 0, i); - tcase_add_loop_test(tc_core, test_jwt_encode_change_alg, 0, i); - tcase_add_loop_test(tc_core, test_jwt_encode_decode, 0, i); - tcase_add_loop_test(tc_core, test_jwt_encode_too_short, 0, i); - - tcase_set_timeout(tc_core, 30); - - suite_add_tcase(s, tc_core); - - return s; -} - -int main(void) -{ - JWT_TEST_MAIN("LibJWT Encode"); -} diff --git a/tests/jwt_es256k.c b/tests/jwt_es256k.c deleted file mode 100644 index 2983ef6b..00000000 --- a/tests/jwt_es256k.c +++ /dev/null @@ -1,62 +0,0 @@ -/* Public domain, no copyright. Use at your own risk. */ - -#include -#include -#include -#include -#include - -#include "jwt_tests.h" - -static const char jwt_es256k[] = "eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QifQ.e" - "yJpYXQiOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsIn" - "JlZiI6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0.u_E" - "sClxS3Z8AFYude9vRupmOZ35646zAgc1xgTf_g1ImJV_1B6kqrg0IS1ckHimgUjd4" - "-DBR1UMibSCdByZngw"; - -#define SKIP_IF(opname) ({ \ - if (!strcmp(opname, jwt_get_crypto_ops())) \ - return; \ -}) - -START_TEST(test_jwt_encode_es256k) -{ - SET_OPS(); - SKIP_IF("gnutls"); - __test_alg_key(JWT_ALG_ES256K, "ec_key_secp256k1.json", - "ec_key_secp256k1_pub.json"); -} -END_TEST - -START_TEST(test_jwt_verify_es256k) -{ - SET_OPS(); - SKIP_IF("gnutls"); - __verify_jwt(jwt_es256k, JWT_ALG_ES256K, "ec_key_secp256k1_pub.json"); -} -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_es256k"); - - tcase_add_loop_test(tc_core, test_jwt_encode_es256k, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_es256k, 0, i); - - tcase_set_timeout(tc_core, 30); - - suite_add_tcase(s, tc_core); - - return s; -} - -int main(void) -{ - JWT_TEST_MAIN("LibJWT ES256K Sign/Verify"); -} diff --git a/tests/jwt_flipflop.c b/tests/jwt_flipflop.c new file mode 100644 index 00000000..d291fe77 --- /dev/null +++ b/tests/jwt_flipflop.c @@ -0,0 +1,257 @@ +/* Public domain, no copyright. Use at your own risk. */ + +#include +#include +#include +#include +#include + +#include "jwt_tests.h" + + +static void *test_malloc(size_t size) +{ + return malloc(size); +} + +static void test_free(void *ptr) +{ + free(ptr); +} + +static void *test_realloc(void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +static int test_set_alloc(void) +{ + return jwt_set_alloc(test_malloc, test_realloc, test_free); +} + +#ifdef JWT_CONSTRUCTOR +START_TEST(test_jwt_crypto_ops) +{ + const char *msg = getenv("JWT_CRYPTO"); + + ck_assert_str_eq(msg, "NONEXISTENT"); +} +END_TEST +#endif + +START_TEST(test_alloc_funcs) +{ + jwt_malloc_t m = NULL; + jwt_realloc_t r = NULL; + jwt_free_t f = NULL; + int ret; + + SET_OPS(); + + jwt_get_alloc(&m, &r, &f); + ck_assert_ptr_null(m); + ck_assert_ptr_null(r); + ck_assert_ptr_null(f); + + ret = test_set_alloc(); + ck_assert_int_eq(ret, 0); + + jwt_get_alloc(&m, &r, &f); + ck_assert(m == test_malloc); + ck_assert(r == test_realloc); + ck_assert(f == test_free); + + /* XXX Need to do a build/verify to excercise the functions */ +} +END_TEST + +static char *__builder(const char *priv, jwt_alg_t alg) +{ + jwt_builder_auto_t *builder; + jwt_alg_t a_check = JWT_ALG_NONE; + char *out; + int ret; + + builder = jwt_builder_new(); + ck_assert_ptr_nonnull(builder); + ck_assert_int_eq(jwt_builder_error(builder), 0); + + read_json(priv); + + if (jwks_item_alg(g_item) == JWT_ALG_NONE) + a_check = alg; + + ret = jwt_builder_setkey(builder, a_check, g_item); + ck_assert_int_eq(ret, 0); + + out = jwt_builder_generate(builder); + ck_assert_ptr_nonnull(out); + + free_key(); + + return out; +} + +static void __checker(const char *pub, jwt_alg_t alg, char *token) +{ + jwt_checker_auto_t *checker; + int ret; + + checker = jwt_checker_new(); + ck_assert_ptr_nonnull(checker); + ck_assert_int_eq(jwt_checker_error(checker), 0); + + read_json(pub); + + ret = jwt_checker_setkey(checker, alg, g_item); + ck_assert_int_eq(ret, 0); + + ret = jwt_checker_verify(checker, token); + if (ret) + fprintf(stderr, "E: %s\n", jwt_checker_error_msg(checker)); + ck_assert_int_eq(ret, 0); + free(token); + + free_key(); +} + +static void __flip_one(const char *priv, const char *pub, jwt_alg_t alg) +{ + char *out = NULL; + int ret; + + /* If this doesn't work, we wont even bother */ + ret = jwt_set_crypto_ops("gnutls"); + if (ret) + return; + + /* This one is mandated */ + ret = jwt_set_crypto_ops("openssl"); + ck_assert_int_eq(ret, 0); + + /* Generate on OpenSSL */ + out = __builder(priv, alg); + + /* Switch to GnuTLS */ + ret = jwt_set_crypto_ops("gnutls"); + ck_assert_int_eq(ret, 0); + + /* Verify the OpenSSL token on GnuTLS */ + __checker(pub, alg, out); + + /* Midway through, we switch memory ops */ + test_set_alloc(); + + /* Generate on GnuTLS */ + out = __builder(priv, alg); + + /* Switch back to OpenSSL */ + ret = jwt_set_crypto_ops("openssl"); + ck_assert_int_eq(ret, 0); + + /* And verify the GnuTLS token on OpenSSL */ + __checker(pub, alg, out); + + free_key(); +} + +#define FLIPFLOP_KEY(__name, __pub, __alg) \ +START_TEST(__name) \ +{ \ + __flip_one(#__name ".json", \ + #__pub ".json", __alg); \ +} \ +END_TEST + +FLIPFLOP_KEY(ec_key_prime256v1, + ec_key_prime256v1_pub, + JWT_ALG_ES256); +FLIPFLOP_KEY(ec_key_secp384r1, + ec_key_secp384r1_pub, + JWT_ALG_ES384); +FLIPFLOP_KEY(ec_key_secp521r1, + ec_key_secp521r1, + JWT_ALG_ES512); + +FLIPFLOP_KEY(eddsa_key_ed25519, + eddsa_key_ed25519, + JWT_ALG_EDDSA); +FLIPFLOP_KEY(eddsa_key_ed448, + eddsa_key_ed448_pub, + JWT_ALG_EDDSA); + +FLIPFLOP_KEY(rsa_key_2048, + rsa_key_2048_pub, + JWT_ALG_RS256); +FLIPFLOP_KEY(rsa_key_4096, + rsa_key_4096, + JWT_ALG_RS384); +FLIPFLOP_KEY(rsa_key_8192, + rsa_key_8192_pub, + JWT_ALG_RS512); + +FLIPFLOP_KEY(rsa_pss_key_2048, + rsa_pss_key_2048, + JWT_ALG_PS256); +FLIPFLOP_KEY(rsa_pss_key_2048_384, + rsa_pss_key_2048_384_pub, + JWT_ALG_PS384); +FLIPFLOP_KEY(rsa_pss_key_2048_512, + rsa_pss_key_2048_512_pub, + JWT_ALG_PS512); + +FLIPFLOP_KEY(oct_key_256, + oct_key_256, + JWT_ALG_HS256); +FLIPFLOP_KEY(oct_key_384, + oct_key_384, + JWT_ALG_HS384); +FLIPFLOP_KEY(oct_key_512, + oct_key_512, + JWT_ALG_HS512); + +static Suite *libjwt_suite(const char *title) +{ + Suite *s; + TCase *tc_core; + + s = suite_create(title); + + tc_core = tcase_create("Keys"); + + tcase_add_test(tc_core, ec_key_prime256v1); + tcase_add_test(tc_core, ec_key_secp384r1); + tcase_add_test(tc_core, ec_key_secp521r1); + + tcase_add_test(tc_core, eddsa_key_ed25519); + tcase_add_test(tc_core, eddsa_key_ed448); + + tcase_add_test(tc_core, rsa_key_2048); + tcase_add_test(tc_core, rsa_key_4096); + tcase_add_test(tc_core, rsa_key_8192); + + tcase_add_test(tc_core, rsa_pss_key_2048); + tcase_add_test(tc_core, rsa_pss_key_2048_384); + tcase_add_test(tc_core, rsa_pss_key_2048_512); + + tcase_add_test(tc_core, oct_key_256); + tcase_add_test(tc_core, oct_key_384); + tcase_add_test(tc_core, oct_key_512); + + suite_add_tcase(s, tc_core); + + /* We run this here so we get some usage out of it */ + tc_core = tcase_create("Utility"); +#ifdef JWT_CONSTRUCTOR + tcase_add_test(tc_core, test_jwt_crypto_ops); +#endif + tcase_add_test(tc_core, test_alloc_funcs); + suite_add_tcase(s, tc_core); + + return s; +} + +int main(void) +{ + JWT_TEST_MAIN("OpenSSL / GnuTLS Cross Testing"); +} diff --git a/tests/jwt_grant.c b/tests/jwt_grant.c deleted file mode 100644 index e8797262..00000000 --- a/tests/jwt_grant.c +++ /dev/null @@ -1,283 +0,0 @@ -/* Public domain, no copyright. Use at your own risk. */ - -#include -#include -#include -#include - -#include "jwt_tests.h" - -START_TEST(test_jwt_grant_add) -{ - jwt_auto_t *jwt = NULL; - jwt_value_t jval; - int ret = 0; - - SET_OPS(); - - EMPTY_JWT(jwt); - - jwt_set_ADD_STR(&jval, "iss", "test"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - /* No duplicates */ - jwt_set_ADD_STR(&jval, "iss", "other"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, JWT_VALUE_ERR_EXIST); - - /* No duplicates for int */ - jwt_set_ADD_INT(&jval, "iat", (long)time(NULL)); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_INT(&jval, "iat", (long)time(NULL)); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, JWT_VALUE_ERR_EXIST); -} -END_TEST - -START_TEST(test_jwt_grant_get) -{ - jwt_auto_t *jwt = NULL; - jwt_value_t jval; - const char *val; - const char testval[] = "testing"; - int ret = 0; - - SET_OPS(); - - EMPTY_JWT(jwt); - - jwt_set_ADD_STR(&jval, "iss", testval); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_GET_STR(&jval, "iss"); - ret = jwt_grant_get(jwt, &jval); - val = jval.str_val; - ck_assert_ptr_nonnull(val); - ck_assert_str_eq(val, testval); -} -END_TEST - -START_TEST(test_jwt_grant_add_int) -{ - jwt_auto_t *jwt = NULL; - jwt_value_t jval; - int ret = 0; - - SET_OPS(); - - EMPTY_JWT(jwt); - - jwt_set_ADD_INT(&jval, "int", 1); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_GET_INT(&jval, "int"); - ret = jwt_grant_get(jwt, &jval); - ck_assert_int_eq(ret, 0); - ck_assert_int_eq(jval.int_val, 1); - - jwt_set_GET_INT(&jval, "not found"); - ret = jwt_grant_get(jwt, &jval); - ck_assert_int_eq(ret, JWT_VALUE_ERR_NOEXIST); -} -END_TEST - -START_TEST(test_jwt_grant_add_bool) -{ - jwt_auto_t *jwt = NULL; - jwt_value_t jval; - int val; - int ret = 0; - - SET_OPS(); - - EMPTY_JWT(jwt); - - jwt_set_ADD_BOOL(&jval, "admin", 1); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_GET_BOOL(&jval, "admin"); - ret = jwt_grant_get(jwt, &jval); - val = jval.bool_val; - ck_assert(val); - - jwt_set_ADD_BOOL(&jval, "test", 0); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_GET_BOOL(&jval, "test"); - ret = jwt_grant_get(jwt, &jval); - val = jval.bool_val; - ck_assert(!val); - - jwt_set_GET_BOOL(&jval, "not found"); - ret = jwt_grant_get(jwt, &jval); - val = jval.bool_val; - ck_assert_int_eq(ret, JWT_VALUE_ERR_NOEXIST); -} -END_TEST - -START_TEST(test_jwt_grant_del) -{ - jwt_auto_t *jwt = NULL; - jwt_value_t jval; - const char *val; - const char testval[] = "testing"; - int ret = 0; - - SET_OPS(); - - EMPTY_JWT(jwt); - - jwt_set_ADD_STR(&jval, "iss", testval); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "other", testval); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - ret = jwt_grant_del(jwt, "iss"); - ck_assert_int_eq(ret, 0); - - jwt_set_GET_STR(&jval, "iss"); - ret = jwt_grant_get(jwt, &jval); - val = jval.str_val; - ck_assert_ptr_null(val); - - /* Delete non existent. */ - ret = jwt_grant_del(jwt, "iss"); - ck_assert_int_eq(ret, 0); - - /* Delete all grants. */ - ret = jwt_grant_del(jwt, NULL); - ck_assert_int_eq(ret, 0); - - jwt_set_GET_STR(&jval, "other"); - ret = jwt_grant_get(jwt, &jval); - val = jval.str_val; - ck_assert_ptr_null(val); -} -END_TEST - -START_TEST(test_jwt_grant_invalid) -{ - jwt_auto_t *jwt = NULL; - jwt_value_t jval; - const char *val; - long valint = 0; - int valbool = 0; - int ret = 0; - - SET_OPS(); - - EMPTY_JWT(jwt); - - jwt_set_ADD_STR(&jval, "iss", NULL); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, JWT_VALUE_ERR_INVALID); - - jwt_set_ADD_INT(&jval, "", (long)time(NULL)); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, JWT_VALUE_ERR_INVALID); - - jwt_set_GET_STR(&jval, NULL); - ret = jwt_grant_get(jwt, &jval); - val = jval.str_val; - ck_assert_int_eq(ret, JWT_VALUE_ERR_INVALID); - ck_assert_ptr_null(val); - - jwt_set_GET_INT(&jval, NULL); - ret = jwt_grant_get(jwt, &jval); - valint = jval.int_val; - ck_assert_int_eq(ret, JWT_VALUE_ERR_INVALID); - ck_assert(valint == 0); - - jwt_set_GET_BOOL(&jval, NULL); - ret = jwt_grant_get(jwt, &jval); - valbool = jval.bool_val; - ck_assert_int_eq(ret, JWT_VALUE_ERR_INVALID); - ck_assert(valbool == 0); -} -END_TEST - -START_TEST(test_jwt_grants_json) -{ - char *json = "{\"id\":\"FVvGYTr3FhiURCFebsBOpBqTbzHdX/DvImiA2yheXr8=\"," - "\"iss\":\"localhost\",\"other\":[\"foo\",\"bar\"]," - "\"ref\":\"385d6518-fb73-45fc-b649-0527d8576130\"," - "\"scopes\":\"storage\",\"sub\":\"user0\"}"; - jwt_auto_t *jwt = NULL; - jwt_value_t jval; - const char *val; - int ret = 0; - - SET_OPS(); - - EMPTY_JWT(jwt); - - jwt_set_ADD_JSON(&jval, NULL, json); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_GET_STR(&jval, "ref"); - ret = jwt_grant_get(jwt, &jval); - val = jval.str_val; - ck_assert_ptr_nonnull(val); - ck_assert_str_eq(val, "385d6518-fb73-45fc-b649-0527d8576130"); - - jwt_set_GET_JSON(&jval, "other"); - ret = jwt_grant_get(NULL, &jval); - ck_assert_ptr_null(jval.json_val); - ck_assert_int_eq(ret, JWT_VALUE_ERR_INVALID); - - jwt_set_GET_JSON(&jval, "other"); - ret = jwt_grant_get(jwt, &jval); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jval.json_val); - ck_assert_str_eq(jval.json_val, "[\"foo\",\"bar\"]"); - - free(jval.json_val); - - jwt_set_GET_JSON(&jval, "other"); - ret = jwt_grant_get(jwt, NULL); - ck_assert_int_eq(ret, JWT_VALUE_ERR_INVALID); - - free(jval.json_val); -} -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_grant"); - - tcase_add_loop_test(tc_core, test_jwt_grant_add, 0, i); - tcase_add_loop_test(tc_core, test_jwt_grant_add_int, 0, i); - tcase_add_loop_test(tc_core, test_jwt_grant_add_bool, 0, i); - tcase_add_loop_test(tc_core, test_jwt_grant_get, 0, i); - tcase_add_loop_test(tc_core, test_jwt_grant_del, 0, i); - tcase_add_loop_test(tc_core, test_jwt_grant_invalid, 0, i); - tcase_add_loop_test(tc_core, test_jwt_grants_json, 0, i); - - tcase_set_timeout(tc_core, 30); - - suite_add_tcase(s, tc_core); - - return s; -} - -int main(void) -{ - JWT_TEST_MAIN("LibJWT Grant"); -} diff --git a/tests/jwt_header.c b/tests/jwt_header.c deleted file mode 100644 index b0119652..00000000 --- a/tests/jwt_header.c +++ /dev/null @@ -1,271 +0,0 @@ -/* Public domain, no copyright. Use at your own risk. */ - -#include -#include -#include -#include -#include - -#include "jwt_tests.h" - -START_TEST(test_jwt_header_add) -{ - jwt_value_t jval; - jwt_auto_t *jwt = NULL; - int ret = 0; - - SET_OPS(); - - EMPTY_JWT(jwt); - - jwt_set_ADD_STR(&jval, "iss", "test"); - ret = jwt_header_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - /* No duplicates */ - jwt_set_ADD_STR(&jval, "iss", "other"); - ret = jwt_header_add(jwt, &jval); - ck_assert_int_ne(ret, 0); - - /* No duplicates for int */ - jwt_set_ADD_INT(&jval, "iat", (long)time(NULL)); - ret = jwt_header_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - ret = jwt_header_add(jwt, &jval); - ck_assert_int_ne(ret, 0); -} -END_TEST - -START_TEST(test_jwt_header_get) -{ - jwt_value_t jval; - jwt_auto_t *jwt = NULL; - const char testval[] = "testing"; - int ret = 0; - - SET_OPS(); - - EMPTY_JWT(jwt); - - jwt_set_ADD_STR(&jval, "iss", testval); - ret = jwt_header_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_GET_STR(&jval, "iss"); - ret = jwt_header_get(jwt, &jval); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jval.str_val); - ck_assert_str_eq(jval.str_val, testval); -} -END_TEST - -START_TEST(test_jwt_header_add_int) -{ - jwt_value_t jval; - jwt_auto_t *jwt = NULL; - int ret = 0; - - SET_OPS(); - - EMPTY_JWT(jwt); - - jwt_set_ADD_INT(&jval, "int", 1); - ret = jwt_header_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_GET_INT(&jval, "int"); - ret = jwt_header_get(jwt, &jval); - ck_assert_int_eq(ret, 0); - ck_assert_int_eq(jval.int_val, 1); - - jwt_set_GET_STR(&jval, "not found"); - ret = jwt_header_get(jwt, &jval); - ck_assert_int_eq(ret, JWT_VALUE_ERR_NOEXIST); -} -END_TEST - -START_TEST(test_jwt_header_add_bool) -{ - jwt_value_t jval; - jwt_auto_t *jwt = NULL; - int ret = 0; - - SET_OPS(); - - EMPTY_JWT(jwt); - - jwt_set_ADD_BOOL(&jval, "admin", 1); - ret = jwt_header_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_GET_BOOL(&jval, "admin"); - ret = jwt_header_get(jwt, &jval); - ck_assert_int_eq(ret, 0); - ck_assert_int_ne(jval.bool_val, 0); - - jwt_set_ADD_BOOL(&jval, "test", 0); - ret = jwt_header_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_GET_BOOL(&jval, "test"); - ret = jwt_header_get(jwt, &jval); - ck_assert_int_eq(ret, 0); - ck_assert_int_eq(jval.bool_val, 0); - - jwt_set_GET_BOOL(&jval, "not found"); - ret = jwt_header_get(jwt, &jval); - ck_assert_int_eq(ret, JWT_VALUE_ERR_NOEXIST); -} -END_TEST - -START_TEST(test_jwt_header_del) -{ - jwt_value_t jval; - jwt_auto_t *jwt = NULL; - const char testval[] = "testing"; - int ret = 0; - - SET_OPS(); - - EMPTY_JWT(jwt); - - jwt_set_ADD_STR(&jval, "iss", testval); - ret = jwt_header_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "other", testval); - ret = jwt_header_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - ret = jwt_header_del(jwt, "iss"); - ck_assert_int_eq(ret, 0); - - jwt_set_GET_STR(&jval, "iss"); - ret = jwt_header_get(jwt, &jval); - ck_assert_int_eq(ret, JWT_VALUE_ERR_NOEXIST); - ck_assert_ptr_null(jval.str_val); - - /* Delete non existent. */ - ret = jwt_header_del(jwt, "iss"); - ck_assert_int_eq(ret, 0); - - /* Delete all headers. */ - ret = jwt_header_del(jwt, NULL); - ck_assert_int_eq(ret, 0); - - jwt_set_GET_STR(&jval, "other"); - ret = jwt_header_get(jwt, &jval); - ck_assert_int_ne(ret, 0); - ck_assert_ptr_null(jval.str_val); -} -END_TEST - -START_TEST(test_jwt_header_invalid) -{ - jwt_value_t jval; - jwt_auto_t *jwt = NULL; - int ret = 0; - - SET_OPS(); - - EMPTY_JWT(jwt); - - jwt_set_ADD_STR(&jval, "iss", NULL); - ret = jwt_header_add(jwt, &jval); - ck_assert_int_ne(ret, 0); - - jwt_set_ADD_INT(&jval, "", 0); - ret = jwt_header_add(jwt, &jval); - ck_assert_int_ne(ret, 0); - - jwt_set_GET_STR(&jval, NULL); - ret = jwt_header_get(jwt, &jval); - ck_assert_int_eq(ret, JWT_VALUE_ERR_INVALID); - ck_assert_ptr_null(jval.str_val); - - jwt_set_GET_INT(&jval, NULL); - ret = jwt_header_get(jwt, &jval); - ck_assert_int_eq(ret, JWT_VALUE_ERR_INVALID); - - jwt_set_GET_BOOL(&jval, NULL); - ret = jwt_header_get(jwt, &jval); - ck_assert_int_eq(ret, JWT_VALUE_ERR_INVALID); -} -END_TEST - -START_TEST(test_jwt_headers_json) -{ - char *json = "{\"id\":\"FVvGYTr3FhiURCFebsBOpBqTbzHdX/DvImiA2yheXr8=\"," - "\"iss\":\"localhost\",\"other\":[\"foo\",\"bar\"]," - "\"ref\":\"385d6518-fb73-45fc-b649-0527d8576130\"," - "\"scopes\":\"storage\",\"sub\":\"user0\"}"; - jwt_value_t jval; - jwt_auto_t *jwt = NULL; - int ret = 0; - - SET_OPS(); - - EMPTY_JWT(jwt); - - jwt_set_ADD_JSON(&jval, NULL, json); - ret = jwt_header_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_GET_STR(&jval, "ref"); - ret = jwt_header_get(jwt, &jval); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jval.str_val); - ck_assert_str_eq(jval.str_val, "385d6518-fb73-45fc-b649-0527d8576130"); - - jwt_set_GET_STR(&jval, "other"); - ret = jwt_header_get(NULL, &jval); - ck_assert_int_eq(ret, JWT_VALUE_ERR_INVALID); - - jwt_set_GET_JSON(&jval, "other"); - ret = jwt_header_get(jwt, &jval); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jval.json_val); - ck_assert_str_eq(jval.json_val, "[\"foo\",\"bar\"]"); - - free(jval.json_val); - - jwt_set_GET_JSON(&jval, NULL); - ret = jwt_header_get(jwt, &jval); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jval.json_val); - ck_assert_str_eq(jval.json_val, json); - - free(jval.json_val); -} -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_header"); - - tcase_add_loop_test(tc_core, test_jwt_header_add, 0, i); - tcase_add_loop_test(tc_core, test_jwt_header_add_int, 0, i); - tcase_add_loop_test(tc_core, test_jwt_header_add_bool, 0, i); - tcase_add_loop_test(tc_core, test_jwt_header_get, 0, i); - tcase_add_loop_test(tc_core, test_jwt_header_del, 0, i); - tcase_add_loop_test(tc_core, test_jwt_header_invalid, 0, i); - tcase_add_loop_test(tc_core, test_jwt_headers_json, 0, i); - - tcase_set_timeout(tc_core, 30); - - suite_add_tcase(s, tc_core); - - return s; -} - -int main(void) -{ - JWT_TEST_MAIN("LibJWT Header"); -} diff --git a/tests/jwt_hs.c b/tests/jwt_hs.c new file mode 100644 index 00000000..b961e2f7 --- /dev/null +++ b/tests/jwt_hs.c @@ -0,0 +1,112 @@ +/* Public domain, no copyright. Use at your own risk. */ + +#include +#include +#include +#include +#include + +#include "jwt_tests.h" + +static void __verify_token(const char *token, jwt_alg_t alg) +{ + jwt_checker_auto_t *checker = NULL; + int ret; + + checker = jwt_checker_new(); + ck_assert_ptr_nonnull(checker); + ck_assert_int_eq(jwt_checker_error(checker), 0); + + ret = jwt_checker_setclaims(checker, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + ret = jwt_checker_setkey(checker, alg, g_item); + ck_assert_int_eq(ret, 0); + + ret = jwt_checker_verify(checker, token); + ck_assert_int_eq(ret, 0); +} + +static void __test_alg(const char *key_file, jwt_alg_t alg, const char *expected) +{ + jwt_builder_auto_t *builder = NULL; + char *out = NULL; + int ret; + + builder = jwt_builder_new(); + ck_assert_ptr_nonnull(builder); + ck_assert_int_eq(jwt_builder_error(builder), 0); + + ret = jwt_builder_setclaims(builder, JWT_CLAIM_NONE); + ck_assert_int_eq(ret, 0); + + read_json(key_file); + ret = jwt_builder_setkey(builder, alg, g_item); + ck_assert_int_eq(ret, 0); + + out = jwt_builder_generate(builder); + ck_assert_ptr_nonnull(out); + ck_assert_str_eq(out, expected); + + __verify_token(out, alg); + + free(out); + + free_key(); +} + +START_TEST(hs256) +{ + const char exp[] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.CM4dD95Nj" + "0vSfMGtDas432AUW1HAo7feCiAbt5Yjuds"; + + SET_OPS(); + + __test_alg("oct_key_256.json", JWT_ALG_HS256, exp); +} +END_TEST + +START_TEST(hs384) +{ + const char exp[] = "eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.e30.GKapA5V0" + "PiRn-pNHK1Vj-E01pYv1Gx0VOkzzgp-SbfWQaOz6q6MiiCyVM0P69idm"; + + SET_OPS(); + + __test_alg("oct_key_384.json", JWT_ALG_HS384, exp); +} +END_TEST + +START_TEST(hs512) +{ + const char exp[] = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.e30.yeSPkrpAm" + "_6UGtg2SpdPUV0tsrhVosfxKLuUIqMRwhEEyg6jAWe4J-qKiPdJZfC1MVeMwk" + "zwB_k-o9RDi_gSbA"; + + SET_OPS(); + + __test_alg("oct_key_512.json", JWT_ALG_HS512, exp); +} +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("HS Key Gen/Ver"); + tcase_add_loop_test(tc_core, hs256, 0, i); + tcase_add_loop_test(tc_core, hs384, 0, i); + tcase_add_loop_test(tc_core, hs512, 0, i); + suite_add_tcase(s, tc_core); + + return s; +} + +int main(void) +{ + JWT_TEST_MAIN("LibJWT HMAC Algorithms"); +} diff --git a/tests/jwt_jwks.c b/tests/jwt_jwks.c index d8254c5c..d6a8eca6 100644 --- a/tests/jwt_jwks.c +++ b/tests/jwt_jwks.c @@ -8,151 +8,47 @@ #include "jwt_tests.h" -#define JWKS_KEY_TEST(__name) \ -START_TEST(test_jwks_##__name) \ -{ \ - SET_OPS(); \ - __jwks_check(#__name ".json", "pem-files/" \ - #__name ".pem"); \ -} \ -END_TEST - -static void __jwks_check(const char *json, const char *pem) +START_TEST(test_jwks_keyring_load) { - JWT_CONFIG_DECLARE(config); - jwk_set_auto_t *jwk_set = NULL; - const jwk_item_t *item = NULL; - jwt_auto_t *jwt = NULL; - jwt_value_t jval; - char *out = NULL; - int ret; - - /* Load up the JWKS */ - read_key(json); - jwk_set = jwks_create(test_data.key); - free_key(); - ck_assert_ptr_nonnull(jwk_set); + const jwk_item_t *item; + int i, ret; - ck_assert(!jwks_error(jwk_set)); + SET_OPS(); - /* Make sure we have one item */ - item = jwks_item_get(jwk_set, 0); - ck_assert_ptr_nonnull(item); + read_json("jwks_keyring.json"); - if (jwks_item_pem(item) != NULL) { - read_key(pem); - ret = strcmp(jwks_item_pem(item), test_data.key); - free_key(); - ck_assert_int_eq(ret, 0); + for (i = 0; (item = jwks_item_get(g_jwk_set, i)); i++) { + jwt_builder_auto_t *builder = NULL; + char_auto *out = NULL; + jwt_alg_t alg; - if (jwks_item_alg(item) == JWT_ALG_ES256) { - ck_assert_str_eq(jwks_item_curve(item), "P-256"); - ck_assert_int_eq(jwks_item_kty(item), JWK_KEY_TYPE_EC); - ck_assert_int_eq(jwks_item_key_bits(item), 256); + if (jwks_item_error(item)) { + fprintf(stderr, "Err KID: %s\n", + jwks_item_kid(item)); } - } + ck_assert_int_eq(jwks_item_error(item), 0); - /* Should only be one key in the set */ - item = jwks_item_get(jwk_set, 1); - ck_assert_ptr_null(item); + alg = jwks_item_alg(item); - /* Now create a token */ - item = jwks_item_get(jwk_set, 0); - ck_assert_ptr_nonnull(item); + if (alg == JWT_ALG_ES256K) + continue; - if (!jwks_item_is_private(item)) - return; + if (alg == JWT_ALG_NONE || !jwks_item_is_private(item)) + continue; - 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 = jwks_item_alg(item); - } + builder = jwt_builder_new(); + ck_assert_ptr_nonnull(builder); - /* Use our JWK */ - config.jw_key = item; - jwt = jwt_create(&config); - ck_assert_ptr_nonnull(jwt); - - /* Add some grants */ - jwt_set_ADD_STR(&jval, "iss", "files.maclara-llc.com"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "sub", "user0"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_INT(&jval, "iat", TS_CONST); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - /* Encode it */ - out = jwt_encode_str(jwt); - /* We allow this for now. */ - if (jwt_error(jwt)) { - if (!strcmp(jwt_error_msg(jwt), "ES256K Not Supported on GnuTLS")) - return; - if (!strcmp(jwt_error_msg(jwt), "ED448 is not yet implemented in GnuTLS")) - return; - } - - ck_assert_int_eq(jwt_error(jwt), 0); - ck_assert_ptr_nonnull(out); - - /* Verify it using our JWK */ - __verify_jwk(out, item); - - free(out); -} - -JWKS_KEY_TEST(ec_key_prime256v1); -JWKS_KEY_TEST(ec_key_prime256v1_pub); -JWKS_KEY_TEST(ec_key_secp256k1); -JWKS_KEY_TEST(ec_key_secp256k1_pub); -JWKS_KEY_TEST(ec_key_secp384r1); -JWKS_KEY_TEST(ec_key_secp384r1_pub); -JWKS_KEY_TEST(ec_key_secp521r1); -JWKS_KEY_TEST(ec_key_secp521r1_pub); - -JWKS_KEY_TEST(eddsa_key_ed25519); -JWKS_KEY_TEST(eddsa_key_ed25519_pub); -JWKS_KEY_TEST(eddsa_key_ed448); -JWKS_KEY_TEST(eddsa_key_ed448_pub); - -JWKS_KEY_TEST(rsa_key_2048); -JWKS_KEY_TEST(rsa_key_2048_pub); -JWKS_KEY_TEST(rsa_key_4096); -JWKS_KEY_TEST(rsa_key_4096_pub); -JWKS_KEY_TEST(rsa_key_8192); -JWKS_KEY_TEST(rsa_key_8192_pub); - -JWKS_KEY_TEST(rsa_key_i37_pub); - -JWKS_KEY_TEST(rsa_pss_key_2048); -JWKS_KEY_TEST(rsa_pss_key_2048_pub); - -JWKS_KEY_TEST(oct_key_256); -JWKS_KEY_TEST(oct_key_384); -JWKS_KEY_TEST(oct_key_512); - -START_TEST(test_jwks_keyring_load) -{ - const jwk_item_t *item; - int i; - - SET_OPS(); - - read_json("jwks_keyring.json"); + ret = jwt_builder_setkey(builder, alg, item); + ck_assert_int_eq(ret, 0); - for (i = 0; (item = jwks_item_get(g_jwk_set, i)); i++) - ck_assert(!jwks_item_error(item)); + out = jwt_builder_generate(builder); + if (out == NULL) { + fprintf(stderr, "Gen KID(%d/%d): %s\n", i, alg, + jwt_builder_error_msg(builder)); + } + ck_assert_ptr_nonnull(out); + } ck_assert_int_eq(i, 22); @@ -215,7 +111,7 @@ START_TEST(test_jwks_key_op_bad_type) SET_OPS(); - read_key("jwks_test-2.json"); + read_json("jwks_test-2.json"); item = jwks_item_get(g_jwk_set, 0); ck_assert_ptr_nonnull(item); @@ -246,42 +142,11 @@ static Suite *libjwt_suite(const char *title) tc_core = tcase_create("jwt_jwks"); - /* Testing individual keys */ - tcase_add_loop_test(tc_core, test_jwks_ec_key_prime256v1, 0, i); - tcase_add_loop_test(tc_core, test_jwks_ec_key_prime256v1_pub, 0, i); - tcase_add_loop_test(tc_core, test_jwks_ec_key_secp256k1, 0, i); - tcase_add_loop_test(tc_core, test_jwks_ec_key_secp256k1_pub, 0, i); - tcase_add_loop_test(tc_core, test_jwks_ec_key_secp384r1, 0, i); - tcase_add_loop_test(tc_core, test_jwks_ec_key_secp384r1_pub, 0, i); - tcase_add_loop_test(tc_core, test_jwks_ec_key_secp521r1, 0, i); - tcase_add_loop_test(tc_core, test_jwks_ec_key_secp521r1_pub, 0, i); - - tcase_add_loop_test(tc_core, test_jwks_eddsa_key_ed25519, 0, i); - tcase_add_loop_test(tc_core, test_jwks_eddsa_key_ed25519_pub, 0, i); - tcase_add_loop_test(tc_core, test_jwks_eddsa_key_ed448, 0, i); - tcase_add_loop_test(tc_core, test_jwks_eddsa_key_ed448_pub, 0, i); - - tcase_add_loop_test(tc_core, test_jwks_rsa_key_2048, 0, i); - tcase_add_loop_test(tc_core, test_jwks_rsa_key_2048_pub, 0, i); - tcase_add_loop_test(tc_core, test_jwks_rsa_key_4096, 0, i); - tcase_add_loop_test(tc_core, test_jwks_rsa_key_4096_pub, 0, i); - tcase_add_loop_test(tc_core, test_jwks_rsa_key_8192, 0, i); - tcase_add_loop_test(tc_core, test_jwks_rsa_key_8192_pub, 0, i); - - tcase_add_loop_test(tc_core, test_jwks_rsa_key_i37_pub, 0, i); - - tcase_add_loop_test(tc_core, test_jwks_rsa_pss_key_2048, 0, i); - tcase_add_loop_test(tc_core, test_jwks_rsa_pss_key_2048_pub, 0, i); - - tcase_add_loop_test(tc_core, test_jwks_oct_key_256, 0, i); - tcase_add_loop_test(tc_core, test_jwks_oct_key_384, 0, i); - tcase_add_loop_test(tc_core, test_jwks_oct_key_512, 0, i); - - /* Load a whole keyring of all of the above. */ + /* Load a whole keyring */ tcase_add_loop_test(tc_core, test_jwks_keyring_load, 0, i); tcase_add_loop_test(tc_core, test_jwks_keyring_all_bad, 0, i); - /* Some coverage attempts. */ + /* Some coverage attempts */ tcase_add_loop_test(tc_core, test_jwks_key_op_all_types, 0, i); tcase_add_loop_test(tc_core, test_jwks_key_op_bad_type, 0, i); diff --git a/tests/jwt_jwks_ec.c b/tests/jwt_jwks_ec.c deleted file mode 100644 index 9ffa1576..00000000 --- a/tests/jwt_jwks_ec.c +++ /dev/null @@ -1,133 +0,0 @@ -/* Public domain, no copyright. Use at your own risk. */ - -#include -#include -#include -#include -#include - -#include "jwt_tests.h" - -START_TEST(test_jwks_ec_pub_missing) -{ - const char *json = "{\"kty\":\"EC\"}"; - jwk_set_t *jwk_set = NULL; - 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_set = jwks_create(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(jwks_item_error(item), 0); - - ck_assert_str_eq(exp, jwks_item_error_msg(item)); - - jwks_free(jwk_set); -} -END_TEST - -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; - 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_set = jwks_create(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(jwks_item_error(item), 0); - - ck_assert_str_eq(exp, jwks_item_error_msg(item)); - - jwks_free(jwk_set); -} -END_TEST - -START_TEST(test_jwks_ec_pub_bad64) -{ - const char *json = "{\"kty\":\"EC\",\"crv\":\"prime6v1\",\"x\":\"\",\"y\":\"asaad\"}"; - jwk_set_t *jwk_set = NULL; - const jwk_item_t *item; - const char exp[] = "Error generating pub key from components"; - - SET_OPS(); - - jwk_set = jwks_create(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(jwks_item_error(item), 0); - - ck_assert_str_eq(exp, jwks_item_error_msg(item)); - - jwks_free(jwk_set); -} -END_TEST - -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; - const jwk_item_t *item; - const char exp[] = "Error generating pub key from components"; - - SET_OPS(); - - jwk_set = jwks_create(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(jwks_item_error(item), 0); - - ck_assert_str_eq(exp, jwks_item_error_msg(item)); - - jwks_free(jwk_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_jwks_ec"); - - /* EC specific error path tests */ - tcase_add_loop_test(tc_core, test_jwks_ec_pub_missing, 0, i); - tcase_add_loop_test(tc_core, test_jwks_ec_pub_bad64, 0, i); - tcase_add_loop_test(tc_core, test_jwks_ec_pub_bad_type, 0, i); - tcase_add_loop_test(tc_core, test_jwks_ec_pub_bad_points, 0, i); - - tcase_set_timeout(tc_core, 30); - - suite_add_tcase(s, tc_core); - - return s; -} - -int main(void) -{ - JWT_TEST_MAIN("LibJWT JWKS Error Path Testing EC"); -} diff --git a/tests/jwt_jwks_rsa.c b/tests/jwt_jwks_rsa.c deleted file mode 100644 index 72c3e4e0..00000000 --- a/tests/jwt_jwks_rsa.c +++ /dev/null @@ -1,187 +0,0 @@ -/* Public domain, no copyright. Use at your own risk. */ - -#include -#include -#include -#include -#include - -#include "jwt_tests.h" - -START_TEST(test_jwks_rsa_pub_missing) -{ - const char *json = "{\"kty\":\"RSA\"}"; - jwk_set_t *jwk_set = NULL; - const jwk_item_t *item; - const char exp[] = "Missing required RSA component: n or e"; - - SET_OPS(); - - jwk_set = jwks_create(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(jwks_item_error(item), 0); - - ck_assert_str_eq(exp, jwks_item_error_msg(item)); - - jwks_free(jwk_set); -} -END_TEST - -START_TEST(test_jwks_rsa_pub_bad_type) -{ - const char *json = "{\"kty\":\"RSA\",\"n\":\"YmFkdmFsdWUK\",\"e\":1}"; - jwk_set_t *jwk_set = NULL; - const jwk_item_t *item; - const char exp[] = "Error decoding pub components"; - - SET_OPS(); - - jwk_set = jwks_create(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(jwks_item_error(item), 0); - - ck_assert_str_eq(exp, jwks_item_error_msg(item)); - - jwks_free(jwk_set); -} -END_TEST - -START_TEST(test_jwks_rsa_pub_bad64) -{ - const char *json = "{\"kty\":\"RSA\",\"n\":\"\",\"e\":\"asaadaaaaaa\"}"; - jwk_set_t *jwk_set = NULL; - const jwk_item_t *item; - const char exp[] = "Error decoding pub components"; - - SET_OPS(); - - jwk_set = jwks_create(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(jwks_item_error(item), 0); - - ck_assert_str_eq(exp, jwks_item_error_msg(item)); - - jwks_free(jwk_set); -} -END_TEST - -START_TEST(test_jwks_rsa_pub_binary64) -{ - const char *json = "{\"kty\":\"RSA\",\"n\":" - "\"2fyxRFHaYP2a4pbdTK/s9x4YWV7qAWwJMXMkbRmy51w\"," - "\"e\":\"2fyxRFHaYP2a4pbdTK/s9x4YWV7qAWwJMXMkbRmy51w\"}"; - jwk_set_t *jwk_set = NULL; - const jwk_item_t *item; - - SET_OPS(); - - jwk_set = jwks_create(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(jwks_item_pem(item)); - ck_assert_int_eq(jwks_item_error(item), 0); - - jwks_free(jwk_set); -} -END_TEST - -START_TEST(test_jwks_rsa_priv_missing) -{ - const char *json = "{\"kty\":\"RSA\",\"n\":\"YmFkdmFsdWUK\"," - "\"e\":\"YmFkdmFsdWUK\",\"d\":\"YmFkdmFsdWUK\"}"; - jwk_set_t *jwk_set = NULL; - const jwk_item_t *item; - const char exp[] = "Some priv key components exist, but some are missing"; - - SET_OPS(); - - jwk_set = jwks_create(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(jwks_item_error(item), 0); - - ck_assert_str_eq(exp, jwks_item_error_msg(item)); - - jwks_free(jwk_set); -} -END_TEST - -START_TEST(test_jwks_rsa_priv_bad64) -{ - const char *json = "{\"kty\":\"RSA\",\"n\":\"YmFkdmFsdWUK\"," - "\"e\":\"YmFkdmFsdWUK\",\"d\":" - "\"2fyxRFHaYP2a4pbdTK/s9x4YWV7qAWwJMXMkbRmy51w\"," - "\"p\":\"\",\"q\":\"=\",\"dp\":\"\",\"dq\":\"\",\"qi\":\"\"}"; - jwk_set_t *jwk_set = NULL; - const jwk_item_t *item; - const char exp[] = "Error decoding priv components"; - - SET_OPS(); - - jwk_set = jwks_create(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(jwks_item_error(item), 0); - - ck_assert_str_eq(exp, jwks_item_error_msg(item)); - - jwks_free(jwk_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_jwks_rsa"); - - /* RSA specific error path tests */ - tcase_add_loop_test(tc_core, test_jwks_rsa_pub_missing, 0, i); - tcase_add_loop_test(tc_core, test_jwks_rsa_pub_bad64, 0, i); - tcase_add_loop_test(tc_core, test_jwks_rsa_pub_bad_type, 0, i); - tcase_add_loop_test(tc_core, test_jwks_rsa_pub_binary64, 0, i); - tcase_add_loop_test(tc_core, test_jwks_rsa_priv_missing, 0, i); - tcase_add_loop_test(tc_core, test_jwks_rsa_priv_bad64, 0, i); - - tcase_set_timeout(tc_core, 30); - - suite_add_tcase(s, tc_core); - - return s; -} - -int main(void) -{ - JWT_TEST_MAIN("LibJWT JWKS Error Path Testing RSA"); -} diff --git a/tests/jwt_new.c b/tests/jwt_new.c deleted file mode 100644 index 17c6031f..00000000 --- a/tests/jwt_new.c +++ /dev/null @@ -1,603 +0,0 @@ -/* Public domain, no copyright. Use at your own risk. */ - -#include -#include -#include -#include -#include - -#include "jwt_tests.h" - -#ifdef JWT_CONSTRUCTOR -START_TEST(test_jwt_crypto_ops) -{ - const char *msg = getenv("JWT_CRYPTO"); - - ck_assert_str_eq(msg, "openssl"); -} -END_TEST -#endif - -/* The simplest of tests */ -START_TEST(test_jwt_new) -{ - jwt_auto_t *jwt = NULL; - - SET_OPS(); - - EMPTY_JWT(jwt); -} -END_TEST - -START_TEST(test_jwt_dup) -{ - jwt_auto_t *jwt = NULL, *new = NULL; - jwt_value_t jval; - int ret = 0; - const char *val = NULL; - time_t now; - long valint; - - SET_OPS(); - - new = jwt_dup(NULL); - ck_assert_ptr_null(new); - - EMPTY_JWT(jwt); - - jwt_set_ADD_STR(&jval, "iss", "test"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - new = jwt_dup(jwt); - ck_assert_ptr_nonnull(new); - - jwt_set_GET_STR(&jval, "iss"); - ret = jwt_grant_get(new, &jval); - val = jval.str_val; - ck_assert_ptr_nonnull(val); - ck_assert_str_eq(val, "test"); - - ck_assert_int_eq(jwt_get_alg(new), JWT_ALG_NONE); - - now = time(NULL); - jwt_set_ADD_INT(&jval, "iat", (long)now); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_GET_INT(&jval, "iat"); - ret = jwt_grant_get(jwt, &jval); - valint = jval.int_val; - ck_assert(((long)now) == valint); -} -END_TEST - -START_TEST(test_jwt_dup_signed) -{ - jwt_test_auto_t *jwt = NULL; - jwt_auto_t *new = NULL; - jwt_value_t jval; - int ret = 0; - const char *val = NULL; - - SET_OPS(); - - CREATE_JWT(jwt, "oct_key_256.json", JWT_ALG_HS256); - - jwt_set_ADD_STR(&jval, "iss", "test"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - new = jwt_dup(jwt); - ck_assert_ptr_nonnull(new); - - jwt_set_GET_STR(&jval, "iss"); - ret = jwt_grant_get(new, &jval); - val = jval.str_val; - ck_assert_ptr_nonnull(val); - ck_assert_str_eq(val, "test"); - - ck_assert_int_eq(jwt_get_alg(new), JWT_ALG_HS256); -} -END_TEST - -START_TEST(test_jwt_verify) -{ - JWT_CONFIG_DECLARE(config); - const char token[] = "eyJhbGciOiJub25lIn0.eyJpc3MiOiJmaWxlcy5jeXBo" - "cmUuY29tIiwic3ViIjoidXNlcjAifQ."; - jwt_alg_t alg; - jwt_auto_t *jwt = NULL; - - SET_OPS(); - - jwt = jwt_verify(token, &config); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_eq(jwt_error(jwt), 0); - - alg = jwt_get_alg(jwt); - ck_assert_int_eq(alg, JWT_ALG_NONE); -} -END_TEST - -static int test_jwt_verify_alg_none_cb(const jwt_t *jwt, jwt_config_t *config) -{ - jwt_alg_t alg = jwt_get_alg(jwt); - jwt_alg_t *test = config->ctx; - - ck_assert_ptr_nonnull(test); - ck_assert_ptr_null(config->jw_key); - - /* Passed to us. */ - ck_assert_int_eq(*test, JWT_ALG_HS256); - - *test = JWT_ALG_NONE; - - return (alg == JWT_ALG_NONE) ? 0 : -1; -} - -START_TEST(test_jwt_verify_wcb_alg_none) -{ - const char token[] = "eyJhbGciOiJub25lIn0.eyJpc3MiOiJmaWxlcy5jeXBo" - "cmUuY29tIiwic3ViIjoidXNlcjAifQ."; - JWT_CONFIG_DECLARE(config); - jwt_alg_t alg = JWT_ALG_HS256; - jwt_auto_t *jwt = NULL; - - SET_OPS(); - - config.ctx = &alg; - - jwt = jwt_verify_wcb(token, &config, test_jwt_verify_alg_none_cb); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_eq(jwt_error(jwt), 0); - - ck_assert_int_eq(alg, JWT_ALG_NONE); - - alg = jwt_get_alg(jwt); - ck_assert_int_eq(alg, JWT_ALG_NONE); -} - -START_TEST(test_jwt_verify_invalid_final_dot) -{ - JWT_CONFIG_DECLARE(config); - const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzM4NCJ9." - "eyJpc3MiOiJmaWxlcy5jeXBocmUuY29tIiwic" - "3ViIjoidXNlcjAifQ"; - jwt_auto_t *jwt = NULL; - - SET_OPS(); - - jwt = jwt_verify(token, &config); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_ne(jwt_error(jwt), 0); -} -END_TEST - -START_TEST(test_jwt_verify_invalid_alg) -{ - JWT_CONFIG_DECLARE(config); - const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIQUhBSCJ9." - "eyJpc3MiOiJmaWxlcy5jeXBocmUuY29tIiwic" - "3ViIjoidXNlcjAifQ."; - jwt_auto_t *jwt = NULL; - - SET_OPS(); - - jwt = jwt_verify(token, &config); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_ne(jwt_error(jwt), 0); -} -END_TEST - -/* { "typ": "JWT", "alg": "none" } . . */ -START_TEST(test_jwt_verify_dot_dot) -{ - JWT_CONFIG_DECLARE(config); - char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.."; - jwt_auto_t *jwt = NULL; - int ret; - - SET_OPS(); - - /* Two dots */ - jwt = jwt_verify(token, &config); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_ne(jwt_error(jwt), 0); - - /* One dot */ - ret = strlen(token); - token[ret - 1] = '\0'; - - jwt = jwt_verify(token, &config); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_ne(jwt_error(jwt), 0); - - /* No dot */ - ret = strlen(token); - token[ret - 1] = '\0'; - - jwt = jwt_verify(token, &config); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_ne(jwt_error(jwt), 0); -} - -/* { "typ": "JWT", "alg": "none" } . {} . */ -START_TEST(test_jwt_verify_empty_body) -{ - JWT_CONFIG_DECLARE(config); - const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.e30."; - jwt_auto_t *jwt = NULL; - - SET_OPS(); - - jwt = jwt_verify(token, &config); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_eq(jwt_error(jwt), 0); -} - -/* { "typ": "JWT", "alg": "HS256" } . { "test": 1 } . */ -START_TEST(test_jwt_verify_nokey_alg_hs256) -{ - JWT_CONFIG_DECLARE(config); - const char token[] = "eyJ0eXAiOiJBTEwiLCJhbGciOiJOT05FIn0.eyJ0ZXN0IjoxfQ."; - jwt_auto_t *jwt = NULL; - - SET_OPS(); - - jwt = jwt_verify(token, &config); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_ne(jwt_error(jwt), 0); -} -END_TEST - -/* { "typ": "ALL", "alg": "none" } . { "test": 1 } */ -START_TEST(test_jwt_verify_ignore_typ) -{ - JWT_CONFIG_DECLARE(config); - const char token[] = "eyJ0eXAiOiJBTEwiLCJhbGciOiJub25lIn0.eyJ0ZXN0IjoxfQ."; - jwt_auto_t *jwt = NULL; - - SET_OPS(); - - jwt = jwt_verify(token, &config); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_eq(jwt_error(jwt), 0); -} -END_TEST - -START_TEST(test_jwt_verify_invalid_head) -{ - JWT_CONFIG_DECLARE(config); - const char token[] = "yJ0eXAiOiJKV1QiLCJhbGciOiJIUzM4NCJ9." - "eyJpc3MiOiJmaWxlcy5jeXBocmUuY29tIiwic" - "3ViIjoidXNlcjAifQ."; - jwt_auto_t *jwt = NULL; - - SET_OPS(); - - jwt = jwt_verify(token, &config); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_ne(jwt_error(jwt), 0); -} -END_TEST - -START_TEST(test_jwt_verify_alg_none_with_key) -{ - const char token[] = "eyJhbGciOiJub25lIn0." - "eyJpc3MiOiJmaWxlcy5jeXBocmUuY29tIiwic" - "3ViIjoidXNlcjAifQ."; - JWT_CONFIG_DECLARE(config); - jwt_auto_t *jwt = NULL; - - SET_OPS(); - - read_key("oct_key_256.json"); - ck_assert_ptr_nonnull(g_item); - config.jw_key = g_item; - - jwt = jwt_verify(token, &config); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_ne(jwt_error(jwt), 0); - - free_key(); -} -END_TEST - -START_TEST(test_jwt_verify_invalid_body) -{ - JWT_CONFIG_DECLARE(config); - const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzM4NCJ9." - "eyJpc3MiOiJmaWxlcy5jeBocmUuY29tIiwic" - "3ViIjoidXNlcjAifQ."; - jwt_auto_t *jwt = NULL; - - SET_OPS(); - - jwt = jwt_verify(token, &config); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_ne(jwt_error(jwt), 0); -} -END_TEST - -START_TEST(test_jwt_verify_hs256) -{ - JWT_CONFIG_DECLARE(config); - const char token[] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQi" - "OjE3MzYxMTM5OTIsInN1YiI6InVzZXIwIn0.74dWICs1ezbHNBrPSmop3o" - "zc4GQ8YdZISXCxZX8LAjk"; - jwt_auto_t *jwt = NULL; - - SET_OPS(); - - read_key("oct_key_256.json"); - config.jw_key = g_item; - config.alg = JWT_ALG_HS256; - - jwt = jwt_verify(token, &config); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_eq(jwt_error(jwt), 0); - - free_key(); -} -END_TEST - -/* Fron issue #201. Adding tests for alg checks. */ -/* { "typ": "JWT", "alg": "HS256" } . { ... } . sig */ -START_TEST(test_jwt_verify_hs256_no_key_alg) -{ - JWT_CONFIG_DECLARE(config); - const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3Mi" - "OiJmaWxlcy5jeXBocmUuY29tIiwic3ViIjoidXNlcjAif" - "Q.dLFbrHVViu1e3VD1yeCd9aaLNed-bfXhSsF0Gh56fBg"; - jwt_auto_t *jwt = NULL; - - SET_OPS(); - - jwt = jwt_verify(token, &config); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_ne(jwt_error(jwt), 0); -} -END_TEST - -START_TEST(test_jwt_verify_hs256_issue_1) -{ - JWT_CONFIG_DECLARE(config); - const char token[] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIi" - "OiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE" - "5hbWUiOiJ3b3JsZCIsInJvbGVzIjpbInRoaXMiLCJ0aGF0IiwidGhlb3Ro" - "ZXIiXSwiaXNzIjoiaXNzdWVyIiwicGVyc29uSWQiOiI3NWJiM2NjNy1iOT" - "MzLTQ0ZjAtOTNjNi0xNDdiMDgyZmFkYjUiLCJleHAiOjE5MDg4MzUyMDAs" - "ImlhdCI6MTQ4ODgxOTYwMCwidXNlcm5hbWUiOiJoZWxsby53b3JsZCJ9.t" - "JoAl_pvq95hK7GKqsp5TU462pLTbmSYZc1fAHzcqWM"; - jwt_auto_t *jwt = NULL; - - SET_OPS(); - - read_key("oct_key_256_issue1.json"); - - config.jw_key = g_item; - config.alg = JWT_ALG_HS256; - - jwt = jwt_verify(token, &config); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_eq(jwt_error(jwt), 0); - - free_key(); -} -END_TEST - -START_TEST(test_jwt_verify_hs256_issue_2) -{ - JWT_CONFIG_DECLARE(config); - const char token[] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQi" - "OjE3MzYxMTQyOTIsInN1YiI6InVzZXIwIn0.CjttUxGvPjKIh1Cz8RAOoB" - "9i6xHKexJAsWzKwd6C7uM"; - jwt_auto_t *jwt = NULL; - - SET_OPS(); - - read_key("oct_key_256_issue2.json"); - config.jw_key = g_item; - config.alg = JWT_ALG_HS256; - - jwt = jwt_verify(token, &config); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_eq(jwt_error(jwt), 0); - - free_key(); -} -END_TEST - -START_TEST(test_jwt_verify_hs384) -{ - JWT_CONFIG_DECLARE(config); - const char token[] = "eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJpYXQi" - "OjE3MzYxMTQxMTAsInN1YiI6InVzZXIwIn0.9bqi8aIAAS1L8uaOCFum-z" - "oHC96tJOuDHW9GzE7uUQbXPyg6FmXQ9LS92D1aQi82"; - jwt_auto_t *jwt = NULL; - - SET_OPS(); - - read_key("oct_key_384.json"); - config.jw_key = g_item; - config.alg = JWT_ALG_HS384; - - jwt = jwt_verify(token, &config); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_eq(jwt_error(jwt), 0); - - free_key(); -} -END_TEST - -START_TEST(test_jwt_verify_hs512) -{ - JWT_CONFIG_DECLARE(config); - const char token[] = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQi" - "OjE3MzYxMTQxNzAsInN1YiI6InVzZXIwIn0.WSD1YRRDD58uG-EeNGVhZt" - "kDHgi_EVbJBHvCt72mE7oVFJ9qU8WCx5ACMbyIuWdgvOozTUVCSHL6RtDo" - "JMq4JQ"; - jwt_auto_t *jwt = NULL; - - SET_OPS(); - - read_key("oct_key_512.json"); - config.jw_key = g_item; - config.alg = JWT_ALG_HS512; - - jwt = jwt_verify(token, &config); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_eq(jwt_error(jwt), 0); - - free_key(); -} -END_TEST - -static int test_jwt_verify_wcb_hs512_kp(const jwt_t *jwt, jwt_config_t *config) -{ - if (jwt_get_alg(jwt) != JWT_ALG_HS512) - return EINVAL; - - config->jw_key = g_item; - - return 0; -} - -START_TEST(test_jwt_verify_wcb_hs512) -{ - const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3Mi" - "OiJmaWxlcy5jeXBocmUuY29tIiwic3ViIjoidXNlcjAif" - "Q.u-4XQB1xlYV8SgAnKBof8fOWOtfyNtc1ytTlc_vHo0U" - "lh5uGT238te6kSacnVzBbC6qwzVMT1806oa1Y8_8EOg"; - JWT_CONFIG_DECLARE(config); - jwt_auto_t *jwt = NULL; - - SET_OPS(); - - read_key("oct_key_512_wcb.json"); - - config.alg = JWT_ALG_HS512; - - jwt = jwt_verify_wcb(token, &config, &test_jwt_verify_wcb_hs512_kp); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_eq(jwt_error(jwt), 0); - - ck_assert_ptr_eq(config.jw_key, g_item); - - free_key(); -} -END_TEST - -START_TEST(test_jwt_verify_wcb_invalid) -{ - const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzM4NCJ9." - "eyJpc3MiOiJmaWxlcy5jeXBocmUuY29tIiwic" - "3ViIjoidXNlcjAifQ.xqea3OVgPEMxsCgyikr" - "R3gGv4H2yqMyXMm7xhOlQWpA-NpT6n2a1d7TD" - "GgU6LOe4"; - JWT_CONFIG_DECLARE(config); - jwt_auto_t *jwt = NULL; - - SET_OPS(); - - config.alg = JWT_ALG_HS512; - - jwt = jwt_verify_wcb(token, &config, &test_jwt_verify_wcb_hs512_kp); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_ne(jwt_error(jwt), 0); - ck_assert_str_eq(jwt_error_msg(jwt), "User callback returned error"); -} -END_TEST - -START_TEST(test_jwt_verify_wcb_invalid_body) -{ - const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzM4NCJ9." - "eyJpc3MiOiJmaWxlcy5jeBocmUuY29tIiwic" - "3ViIjoidXNlcjAifQ."; - JWT_CONFIG_DECLARE(config); - jwt_auto_t *jwt = NULL; - - SET_OPS(); - - config.alg = JWT_ALG_HS512; - - jwt = jwt_verify_wcb(token, &config, - &test_jwt_verify_wcb_hs512_kp); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_ne(jwt_error(jwt), 0); - ck_assert_str_eq(jwt_error_msg(jwt), "Error parsing body"); -} -END_TEST - -START_TEST(test_jwt_verify_invalid_base64) -{ - JWT_CONFIG_DECLARE(config); - const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3Mi" - "OiJmaWxlcy5jeXBocmUuY29tIiwic3ViIjoidXNlcjAif" - "Q.dLFbrHVViu1e3VD1yeCd9aaLNed-bfXhSsF0Gh56fBga"; - jwt_auto_t *jwt = NULL; - - SET_OPS(); - - read_key("oct_key_256_invalid_base64.json"); - config.jw_key = g_item; - config.alg = JWT_ALG_HS256; - - jwt = jwt_verify(token, &config); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_ne(jwt_error(jwt), 0); -} -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_new"); - -#ifdef JWT_CONSTRUCTOR - tcase_add_test(tc_core, test_jwt_crypto_ops); -#endif - - tcase_add_loop_test(tc_core, test_jwt_new, 0, i); - tcase_add_loop_test(tc_core, test_jwt_dup, 0, i); - tcase_add_loop_test(tc_core, test_jwt_dup_signed, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_wcb_alg_none, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_invalid_alg, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_ignore_typ, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_dot_dot, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_empty_body, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_nokey_alg_hs256, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_invalid_head, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_alg_none_with_key, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_invalid_body, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_wcb_invalid_body, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_invalid_final_dot, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_hs256, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_hs256_no_key_alg, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_hs384, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_hs512, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_wcb_hs512, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_wcb_invalid, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_invalid_base64, 0, i); - - tcase_add_loop_test(tc_core, test_jwt_verify_hs256_issue_1, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_hs256_issue_2, 0, i); - - tcase_set_timeout(tc_core, 30); - - suite_add_tcase(s, tc_core); - - return s; -} - -int main(void) -{ - JWT_TEST_MAIN("LibJWT New"); -} diff --git a/tests/jwt_rsa.c b/tests/jwt_rsa.c index 3a8ea77f..72c3e4e0 100644 --- a/tests/jwt_rsa.c +++ b/tests/jwt_rsa.c @@ -8,217 +8,151 @@ #include "jwt_tests.h" -static const char jwt_rs256_2048[] = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.ey" - "JpYXQiOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJlZi" - "I6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0.RlJPQst_lp" - "MJUsbnzlT2Mf3xzlHyUlVaQM_PJ1_vpBf1gHkhv-0hm3pa1_HRvpqg5UdDF3iOMLT0GU" - "j3W8JveaSvXKFeZdRpQGqmC7MZ7NzaYtyaDT7asniIVDf0JomD8Cfq8IdOn2ZREpbuJ6" - "moPwwvJ2zwL3vY-7w5A7ZQ3fxUedPuzn9n6tbEnuXcbDMyWQjen5poYmmvoIrDbzK0Zb" - "KbAJ5VrJwME_fZnPHS4c3b8rZGdBJCPI8oT2On6a9LrVqY3riqqHeiSqewfjDsox4tL2" - "G5KUpqK0oJmnZPGTnNY774PGabpcPBNbfMJqi8o8r0a7pa7sy6B59P7slUdw"; - -static const char jwt_rs384_4096[] = "eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.ey" - "JpYXQiOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJlZi" - "I6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0.E9e6Chv39H" - "FQqhfUFcCxUzyS6yHQvBex_OaQNIPqeoPR_FDquiLhQUJj45IGd8_aVT8DvfzTFSHOP1" - "cP_UbzCYtZ8VC0o-idxyjTnlnqMJy75xNBzPUy6OSpTeX9yVQCtIkT6kIom4j35ABibk" - "_MdCVqnWG2fijDEeTlLD7uDRcCTGckqQhVOI3t4iIytiUVPnboFiaPLfei_mPcJ6CiOI" - "QYF4VOWlDllgUFrQ0M0nKm4Pq6bVaBIMzF0hJrPn_7GCV3XmVLthcObljfydaNm_CcIY" - "g7y_8OT_8yAvDlbKBe5jVeq-7_lLCinarkGUZ5ryA2lbC6yPBgtaAU3g6XxP60n6To7z" - "akV_5dgcPJFDlTkoBI6pPH3Zf50UYsf-wR4D2J3fP4rcYco4HGtxZ89tfoNYCB8z5-GA" - "ImSJRbVSsdadwSKKgldlG9XR13Cq-Ox8Hc7qCd1tTC-NeY31XMuWxd9981bQMeGhBkyJ" - "fFnIksh8xyyO0uOPvgmchOtG8bSImfZZaeBI6TcPJ89Oo3iD6NmPdO5AUqv6NB7Y46zH" - "GUXCeTuumVk7I_PJ9laICW4-cx1zHDvPY3TphVVTudSqWdUDMjwhrRI23569DByMJE9J" - "XpTg7HV_17EPTDWExc6TQVkmmY4QbS0pVbKyJTwwKmnu2F9o2fl1N0NOw1SOM"; - -static const char jwt_rs512_8192[] = "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey" - "JpYXQiOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJlZi" - "I6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0.F3_fF6RGJy" - "B_apyq_ljxLP7luYC78wT23pPKAyRSMiDi0g-7Yfohv5_p0rBOCsT1H-1_rImWiIcsdJ" - "87oYfQw95G_pHcK56ag7_a1i-jQCV7ZyRAuesDcM1YRealKvdnof08Jw392h685XNK2l" - "mqvZ436Hz4oCcQimwuKeR0ndAgm38y-_FswOlb3POwHBJcDtDR3UTvCONyqgcCD5hl5J" - "edjA8GA0Mvlp4JV2-ctsNYaDOMQRw3S8hJOXvKySQsbckEj8pC_bCrcbSR8BUJBrjM5Q" - "SLslcYGJopyCYAApGsq3t4-uY3Dx-QjWBMgSIgD9BONjv4VXvsfrllWUQmK88qJ4WQFO" - "L0PUSJy57lZjpjXReafJcJgdrGI-tFHgqkcb1VxmqnnIFRqadgjJ1BkfFvdExJiOgnvN" - "SEQ16AYy2FlxZeaabqyOjc8IlGi7Z9hYZgImL_qn0REJXgvxNtabA9-A4cEMWVDtYkCn" - "F_VyYeMOMYKMuoFW5ubLfOhYhDnd_yvkrLzZ76BGXpHtg7cfpnN8dNWp8irwjSByBH4H" - "lB0F9NFZydNB8wylFsKesNhmkQYcLnchhA4dBZV888NKVfqIBcM-GWXExtmON0SXg_HX" - "YmbO6zTgQ4tGAk4HglRWJYlfgORdcBBtnlwUHXm5L7_0J8KHoyNUGbA2XU1krVhBN-A7" - "sxtLof6MZP8c7p65Oc6DH8FulpmnBt8yVID2GnXPfcLs-RgM8QnlSGbNhn9WT2kmBTIV" - "eniVfp_IJ40Kd4SROGa_XbXMTufympqyYSSxtqHblGgtolVGdpN05FDvInG0J0dojYxT" - "_puZ-fvBAMXRPeoC_t_1ScfER4CjsUedbReQLZmE-9nJKhCZKqiba5qCbq8riZYiROGJ" - "rtVryTywLzw7XX4D-s9oJsEk6ELSRI-buXuCqyCmbdRpFz-i2-VPmNQIalk_0Pq_dOZN" - "y0GCvcezkhx1quGKPDmFskJnvKZmC70er0DvOQl1A909kFivxIXfTzlu8jUJt8PPi0gU" - "-nnOGSYxC8tD16vwHvAP3KPYvUzmC2N0r6_yM2_Y-JH5Vypeecbuh66cx18Bqk1nQYfg" - "BLjuJwQSIKRNNBLtgU7mcyI0Lj4-TWbE-22dYYvKcPMxSmfwAmJUm7ZFUAq_Ok-46AmV" - "RYg-h7bZZlfutOiWuoBrmnqQ6dEDGjXiEgWhtAx5HG3qn1_vmA3JQxJAWEfuhHa3IWac" - "MDRrJehImeyDE0H0rpOsxSXOjnDqiFBsf9d0-zJNFvo9tWlK_-d-N40BIy5eZm37FKG7" - "g2rFmXtuicUs6jiwu0_tHSi1fPKO7YN2ezQc9HAoBvvrur1z_XGbDSmFTQNTv0Cg"; - -static const char jwt_rs256_invalid[] = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9" - ".eyJpYXQiOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLmN5cGhyZS5jb20iLCJyZWYiOiJ" - "YWFhYLVlZWVktWlpaWi1BQUFBLUNDQ0MiLCJzdWIiOiJ1c2VyMCJ9.IAmCornholio"; - -START_TEST(test_jwt_encode_rs256) +START_TEST(test_jwks_rsa_pub_missing) { - SET_OPS(); - __compare_alg_key("rsa_key_2048.json", jwt_rs256_2048, JWT_ALG_RS256); -} -END_TEST + const char *json = "{\"kty\":\"RSA\"}"; + jwk_set_t *jwk_set = NULL; + const jwk_item_t *item; + const char exp[] = "Missing required RSA component: n or e"; -START_TEST(test_jwt_verify_rs256) -{ SET_OPS(); - __verify_alg_key("rsa_key_2048_pub.json", jwt_rs256_2048, JWT_ALG_RS256); + + jwk_set = jwks_create(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(jwks_item_error(item), 0); + + ck_assert_str_eq(exp, jwks_item_error_msg(item)); + + jwks_free(jwk_set); } END_TEST -START_TEST(test_jwt_validate_rs256) +START_TEST(test_jwks_rsa_pub_bad_type) { - jwt_t *jwt = NULL; - jwt_valid_t *jwt_valid = NULL; - int ret = 0; + const char *json = "{\"kty\":\"RSA\",\"n\":\"YmFkdmFsdWUK\",\"e\":1}"; + jwk_set_t *jwk_set = NULL; + const jwk_item_t *item; + const char exp[] = "Error decoding pub components"; SET_OPS(); - t_config.alg = JWT_ALG_RS256; - - read_key("rsa_key_2048_pub.json"); - jwt = jwt_verify(jwt_rs256_2048, &t_config); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_eq(jwt_error(jwt), 0); + jwk_set = jwks_create(json); - jwt_valid_new(&jwt_valid, JWT_ALG_RS256); - ck_assert_ptr_nonnull(jwt_valid); + ck_assert_ptr_nonnull(jwk_set); + ck_assert(!jwks_error(jwk_set)); - ret = jwt_valid_add_grant(jwt_valid, "iss", "files.maclara-llc.com"); - ck_assert_int_eq(ret, 0); + item = jwks_item_get(jwk_set, 0); + ck_assert_ptr_nonnull(item); + ck_assert_int_ne(jwks_item_error(item), 0); - ret = jwt_valid_add_grant_int(jwt_valid, "iat", TS_CONST); - ck_assert_int_eq(ret, 0); + ck_assert_str_eq(exp, jwks_item_error_msg(item)); - ck_assert_int_eq(JWT_VALIDATION_SUCCESS, jwt_validate(jwt, jwt_valid)); - - jwt_valid_free(jwt_valid); - jwt_free(jwt); - free_key(); + jwks_free(jwk_set); } END_TEST -START_TEST(test_jwt_encode_rs384) +START_TEST(test_jwks_rsa_pub_bad64) { - SET_OPS(); - __compare_alg_key("rsa_key_4096.json", jwt_rs384_4096, JWT_ALG_RS384); -} -END_TEST + const char *json = "{\"kty\":\"RSA\",\"n\":\"\",\"e\":\"asaadaaaaaa\"}"; + jwk_set_t *jwk_set = NULL; + const jwk_item_t *item; + const char exp[] = "Error decoding pub components"; -START_TEST(test_jwt_verify_rs384) -{ SET_OPS(); - __verify_alg_key("rsa_key_4096_pub.json", jwt_rs384_4096, JWT_ALG_RS384); -} -END_TEST -START_TEST(test_jwt_encode_rs512) -{ - SET_OPS(); - __compare_alg_key("rsa_key_8192.json", jwt_rs512_8192, JWT_ALG_RS512); -} -END_TEST + jwk_set = jwks_create(json); -START_TEST(test_jwt_verify_rs512) -{ - SET_OPS(); - __verify_alg_key("rsa_key_8192_pub.json", jwt_rs512_8192, JWT_ALG_RS512); -} -END_TEST + ck_assert_ptr_nonnull(jwk_set); + ck_assert(!jwks_error(jwk_set)); -static const char jwt_rsa_i37[] = "eyJraWQiOiJkWUoxTDVnbWd0eDlWVU9xbVpyd2F6cW" - "NhK3B5c1lHNUl3N3RSUXB6a3Z3PSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJhMDQyZj" - "Y4My0xODNiLTQ1ZWUtOTZiYy1lNDdlYjhiMzc2MTYiLCJ0b2tlbl91c2UiOiJhY2Nlc3" - "MiLCJzY29wZSI6ImF3cy5jb2duaXRvLnNpZ25pbi51c2VyLmFkbWluIiwiaXNzIjoiaH" - "R0cHM6XC9cL2NvZ25pdG8taWRwLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tXC91cy1lYX" - "N0LTFfUWJvMXlMZ0ZIIiwiZXhwIjoxNDg1ODgyNDg5LCJpYXQiOjE0ODU4Nzg4ODksIm" - "p0aSI6Ijg1MTBlMGVkLWU3N2UtNDJmZS1hMmI2LTgyMjAzMDcxZWQyOCIsImNsaWVudF" - "9pZCI6IjdicTVhanV0czM1anVmamVnMGYwcmhzNnRpIiwidXNlcm5hbWUiOiJhZG1pbj" - "MifQ.IZqzZEuwKCVT0acHk3p5DnzPSNxg1tLISt8wZCMAHJAnLSdtbtVibrCTZkTLP5z" - "PD16MgzgsID_CFF2wZXPGBihhyihu1B5W8GimY4eQOKrt4qiLJgK-D8tG6MSZ2K_9DC3" - "RwhMjrNL4lpu2YoSOgugRdKpJWy4zadtHKptFkKrkI8qjnDoDSkF0kt4I6S1xOcEPuVh" - "EOrGsfKr5Bm1N3wX9OVQhcTiVugKrpU8x0Mv1AJYdaxKASOQ6fFlNquwfohgLDwy3By3" - "xU6RoY6ZWhKm5dcGW7H9gqmr9X4aBmHDmYG5KQtodwf0LOYtprPAXCs9X7Ja-7ddJvko" - "8mDObTA"; - -START_TEST(test_jwt_verify_rsa_i37) -{ - SET_OPS(); - __verify_alg_key("rsa_key_i37_pub.json", jwt_rsa_i37, JWT_ALG_RS256); + item = jwks_item_get(jwk_set, 0); + ck_assert_ptr_nonnull(item); + ck_assert_int_ne(jwks_item_error(item), 0); + + ck_assert_str_eq(exp, jwks_item_error_msg(item)); + + jwks_free(jwk_set); } END_TEST -START_TEST(test_jwt_encode_rsa_with_ec) +START_TEST(test_jwks_rsa_pub_binary64) { - JWT_CONFIG_DECLARE(config); - jwt_test_auto_t *jwt = NULL; + const char *json = "{\"kty\":\"RSA\",\"n\":" + "\"2fyxRFHaYP2a4pbdTK/s9x4YWV7qAWwJMXMkbRmy51w\"," + "\"e\":\"2fyxRFHaYP2a4pbdTK/s9x4YWV7qAWwJMXMkbRmy51w\"}"; + jwk_set_t *jwk_set = NULL; + const jwk_item_t *item; SET_OPS(); - read_key("ec_key_secp384r1.json"); - config.alg = JWT_ALG_RS384; - config.jw_key = g_item; - jwt = jwt_create(&config); - ck_assert_int_ne(jwt_error(jwt), 0); - ck_assert_ptr_nonnull(jwt); - ck_assert_str_eq(jwt_error_msg(jwt), - "Config alg does not match key alg"); + jwk_set = jwks_create(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(jwks_item_pem(item)); + ck_assert_int_eq(jwks_item_error(item), 0); + + jwks_free(jwk_set); } END_TEST -START_TEST(test_jwt_encode_rsa_1024) +START_TEST(test_jwks_rsa_priv_missing) { - jwt_test_auto_t *jwt = NULL; - jwt_value_t jval; - char *out; - int ret = 0; + const char *json = "{\"kty\":\"RSA\",\"n\":\"YmFkdmFsdWUK\"," + "\"e\":\"YmFkdmFsdWUK\",\"d\":\"YmFkdmFsdWUK\"}"; + jwk_set_t *jwk_set = NULL; + const jwk_item_t *item; + const char exp[] = "Some priv key components exist, but some are missing"; SET_OPS(); - CREATE_JWT(jwt, "rsa_key_1024.json", JWT_ALG_RS256); + jwk_set = jwks_create(json); + + ck_assert_ptr_nonnull(jwk_set); + ck_assert(!jwks_error(jwk_set)); - jwt_set_ADD_STR(&jval, "sub", "user0"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); + item = jwks_item_get(jwk_set, 0); + ck_assert_ptr_nonnull(item); + ck_assert_int_ne(jwks_item_error(item), 0); - /* Should fail from too few bits in key */ - out = jwt_encode_str(jwt); - ck_assert_ptr_null(out); + ck_assert_str_eq(exp, jwks_item_error_msg(item)); + + jwks_free(jwk_set); } END_TEST -START_TEST(test_jwt_verify_invalid_token) +START_TEST(test_jwks_rsa_priv_bad64) { - jwt_t *jwt = NULL; + const char *json = "{\"kty\":\"RSA\",\"n\":\"YmFkdmFsdWUK\"," + "\"e\":\"YmFkdmFsdWUK\",\"d\":" + "\"2fyxRFHaYP2a4pbdTK/s9x4YWV7qAWwJMXMkbRmy51w\"," + "\"p\":\"\",\"q\":\"=\",\"dp\":\"\",\"dq\":\"\",\"qi\":\"\"}"; + jwk_set_t *jwk_set = NULL; + const jwk_item_t *item; + const char exp[] = "Error decoding priv components"; SET_OPS(); - read_key("rsa_key_2048.json"); - jwt = jwt_verify(jwt_rs256_invalid, &t_config); - free_key(); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_ne(jwt_error(jwt), 0); -} -END_TEST + jwk_set = jwks_create(json); -START_TEST(test_jwt_verify_invalid_cert) -{ - jwt_t *jwt = NULL; + ck_assert_ptr_nonnull(jwk_set); + ck_assert(!jwks_error(jwk_set)); - SET_OPS(); + item = jwks_item_get(jwk_set, 0); + ck_assert_ptr_nonnull(item); + ck_assert_int_ne(jwks_item_error(item), 0); + + ck_assert_str_eq(exp, jwks_item_error_msg(item)); - read_key("rsa_key_8192_pub.json"); - jwt = jwt_verify(jwt_rs256_2048, &t_config); - free_key(); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_ne(jwt_error(jwt), 0); + jwks_free(jwk_set); } END_TEST @@ -230,22 +164,17 @@ static Suite *libjwt_suite(const char *title) s = suite_create(title); - tc_core = tcase_create("jwt_rsa"); + tc_core = tcase_create("jwt_jwks_rsa"); - tcase_add_loop_test(tc_core, test_jwt_encode_rs256, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_rs256, 0, i); - tcase_add_loop_test(tc_core, test_jwt_validate_rs256, 0, i); - tcase_add_loop_test(tc_core, test_jwt_encode_rs384, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_rs384, 0, i); - tcase_add_loop_test(tc_core, test_jwt_encode_rs512, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_rs512, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_rsa_i37, 0, i); - tcase_add_loop_test(tc_core, test_jwt_encode_rsa_with_ec, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_invalid_token, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_invalid_cert, 0, i); - tcase_add_loop_test(tc_core, test_jwt_encode_rsa_1024, 0, i); + /* RSA specific error path tests */ + tcase_add_loop_test(tc_core, test_jwks_rsa_pub_missing, 0, i); + tcase_add_loop_test(tc_core, test_jwks_rsa_pub_bad64, 0, i); + tcase_add_loop_test(tc_core, test_jwks_rsa_pub_bad_type, 0, i); + tcase_add_loop_test(tc_core, test_jwks_rsa_pub_binary64, 0, i); + tcase_add_loop_test(tc_core, test_jwks_rsa_priv_missing, 0, i); + tcase_add_loop_test(tc_core, test_jwks_rsa_priv_bad64, 0, i); - tcase_set_timeout(tc_core, 120); + tcase_set_timeout(tc_core, 30); suite_add_tcase(s, tc_core); @@ -254,5 +183,5 @@ static Suite *libjwt_suite(const char *title) int main(void) { - JWT_TEST_MAIN("LibJWT RSA Sign/Verify"); + JWT_TEST_MAIN("LibJWT JWKS Error Path Testing RSA"); } diff --git a/tests/jwt_rsa_pss.c b/tests/jwt_rsa_pss.c deleted file mode 100644 index 5d581048..00000000 --- a/tests/jwt_rsa_pss.c +++ /dev/null @@ -1,180 +0,0 @@ -/* Public domain, no copyright. Use at your own risk. */ - -#include -#include -#include -#include -#include - -#include "jwt_tests.h" - -static const char jwt_ps256_2048[] = "eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.ey" - "JpYXQiOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJlZi" - "I6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0.B9gxqtbZae" - "9PyGkjQaBMyBITOieALP39yCDSqmynmvnE2L8JJzNxOKjm5dy_ORhYjagghE18ti90v2" - "whAwRFFvA7MlQC2rQm-4pXrHqAyhT7Dl1_lSeL98WGToZgJ646WLjr-SwbMNjp3RWwZz" - "F-IwnB1D1f-RoA9yUoaNEFHUYVuL4okVj4ImnUE07pW-l2eal3bxUg6lzqGWSctbT46t" - "y8qFlsOyrifev3y_z6-eKPHUruYEbWb1zw3-snBtcPfGMWAQ91PVoNkPLTO6G56I8FAF" - "IufXyyp6k9VuKQ_WRzRQhwO8zBOto4RsTUjYbDJEY2FSFYVZUdPctwojNlCw"; - -static const char jwt_ps384_2048[] = "eyJhbGciOiJQUzM4NCIsInR5cCI6IkpXVCJ9.ey" - "JpYXQiOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJlZi" - "I6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0.GY8aZobXTy" - "6DzooRUt6vwgBbWwWvTchFtDCVMto_NM68aqT_OI8_X1MAHwE7ppS1S-yxg1aEeGzZEG" - "VEAdeIzswd7ilCpQrUQ2Qcym6SuK3NAKLtr6NyUZwdaEPTeEx3GWQbmvY66hVs7g2o4c" - "luSfp3I4McgLCm-HS5Dl_xHoyV_1ympz_n3n7YDoe5l0EoHaX3-XPMtUvL4kxeMV5pLh" - "72Yj2qNM5Dbbe9F_WSxoeQsyktg8MmPb22LWAAW7uafazr7TinJvPtBhPqT7hc2sUFbA" - "Jui_TSM60Kjfqg15QQELifywNvgW0ZO6xKEI5GKgaIi2S9F2iqQehBBkjMrg"; - -static const char jwt_ps512_2048[] = "eyJhbGciOiJQUzUxMiIsInR5cCI6IkpXVCJ9.ey" - "JpYXQiOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJlZi" - "I6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0.OxnjxVNAEC" - "xEnNVg6S6sx-JIxOq3sJimEefq4OONsYomWz1TAM8_42bmAnvda0bhC8LTmIogQwnYj3" - "qIYrjef3s7nrs5USS3_ffqeMuog_Xp7cH1YhVwvkXEWzfeT-SLZiEdxGBrPvEASxwzv0" - "CitQrfDGvFe20UXkhAvOKIc_1K5Fzv9IQiaKaPR2Jg8Ub0qQ6qZq1whnwDbjutWCFlW3" - "62UOQbhA2WtE72Q60OFXMr2J0PYrScGgTRRrL6V2G7cNRend14FzDFG586dGUCwp9iKF" - "nCrshFefpaFsOJYHG70Ka6CNIDG4LDiLatjjz1UCtAgbnHfy9qyJEpcJYPWg"; - -static const char jwt_ps256_2048_invalid[] = "eyJhbGciOiJQUzI1NiIsInR5cCI6Ikp" - "XVCJ9.eyJpYXQiOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbS" - "IsInJlZiI6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0.WX" - "41yYTKxf6lDg7toDAAnwuLKCUSdEWUsJ-5neEbOPE4l09EEIDW2cjK4NZkAgySgCZHCa" - "NUSn8XaOouoLoEMVua5f0g6U-_-c380KRfmiqFGe39vjHCqiw8j-WkdxHisi7eXw3fvL" - "kp0VoyeWA6Fnp2x-shfHU5Br67Wagp7OgCk-SvVL08xyfvgZr6fzEqc486zdNhQE71Pv" - "in5dRQ75Lg3rr1W8Xmx2zRrFKZALsEwGMhRL7e-x46mt6KF1UlwTYAW6FYoKTrrW62sH" - "OgpgvsIwhE93RfCmJ_xvZNkKrqnB6RxfpHEbZYTS8iAI3va2S8IBEL_pH-2etsr1fqAg"; - -#define RSA_PSS_KEY_PRE "rsa_pss_key_2048" - -#define PS_KEY_PRIV_256 RSA_PSS_KEY_PRE ".json" -#define PS_KEY_PUB_256 RSA_PSS_KEY_PRE "_pub.json" - -#define PS_KEY_PRIV_384 RSA_PSS_KEY_PRE "-384.json" -#define PS_KEY_PUB_384 RSA_PSS_KEY_PRE "-384_pub.json" - -#define PS_KEY_PRIV_512 RSA_PSS_KEY_PRE "-512.json" -#define PS_KEY_PUB_512 RSA_PSS_KEY_PRE "-512_pub.json" - -static void __test_rsa_pss_encode(const char *priv_key_file, - const char *pub_key_file, - const jwt_alg_t alg) -{ - jwt_auto_t *jwt = NULL; - jwt_value_t jval; - int ret; - char *out; - - CREATE_JWT(jwt, priv_key_file, alg); - - jwt_set_ADD_STR(&jval, "iss", "files.maclara-llc.com"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "sub", "user0"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_INT(&jval, "iat", TS_CONST); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - out = jwt_encode_str(jwt); - ck_assert_ptr_ne(out, NULL); - - free_key(); - - __verify_alg_key(pub_key_file, out, alg); - - free(out); -} - -START_TEST(test_jwt_encode_ps256) -{ - SET_OPS(); - __test_alg_key(JWT_ALG_PS256, PS_KEY_PRIV_256, PS_KEY_PUB_256); -} -END_TEST - -START_TEST(test_jwt_encode_ps384) -{ - SET_OPS(); - __test_rsa_pss_encode(PS_KEY_PRIV_384, PS_KEY_PUB_384, JWT_ALG_PS384); -} -END_TEST - -START_TEST(test_jwt_encode_ps512) -{ - SET_OPS(); - __test_rsa_pss_encode(PS_KEY_PRIV_512, PS_KEY_PUB_512, JWT_ALG_PS512); -} -END_TEST - -START_TEST(test_jwt_verify_ps256) -{ - SET_OPS(); - __verify_alg_key(PS_KEY_PUB_256, jwt_ps256_2048, JWT_ALG_PS256); -} -END_TEST - -START_TEST(test_jwt_verify_ps384) -{ - SET_OPS(); - __verify_alg_key(PS_KEY_PUB_384, jwt_ps384_2048, JWT_ALG_PS384); -} -END_TEST - -START_TEST(test_jwt_verify_ps512) -{ - SET_OPS(); - __verify_alg_key(PS_KEY_PUB_512, jwt_ps512_2048, JWT_ALG_PS512); -} -END_TEST - -START_TEST(test_jwt_verify_invalid_rsa_pss) -{ - jwt_t *jwt = NULL; - - SET_OPS(); - - read_key(PS_KEY_PUB_256); - t_config.alg = JWT_ALG_PS256; - jwt = jwt_verify(jwt_ps256_2048_invalid, &t_config); - free_key(); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_ne(jwt_error(jwt), 0); -} -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_rsa_pss"); - - tcase_add_loop_test(tc_core, test_jwt_encode_ps256, 0, i); - tcase_add_loop_test(tc_core, test_jwt_encode_ps384, 0, i); - tcase_add_loop_test(tc_core, test_jwt_encode_ps512, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_ps256, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_ps384, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_ps512, 0, i); - tcase_add_loop_test(tc_core, test_jwt_verify_invalid_rsa_pss, 0, i); - - tcase_set_timeout(tc_core, 120); - - suite_add_tcase(s, tc_core); - - return s; -} - -int main(void) -{ - JWT_TEST_MAIN("LibJWT RSA-PSS Sign/Verify"); -} diff --git a/tests/jwt_tests.h b/tests/jwt_tests.h index b5175271..6d0f82a4 100644 --- a/tests/jwt_tests.h +++ b/tests/jwt_tests.h @@ -46,22 +46,6 @@ static jwt_test_op_t jwt_test_ops[] = { #endif }; -#define EMPTY_JWT(__jwt) do { \ - __jwt = jwt_create(NULL); \ - ck_assert_ptr_nonnull(__jwt); \ -} while(0) - -#define jwt_test_auto_t jwt_t __attribute__((cleanup(jwt_test_free))) - -#define CREATE_JWT(__j, __f, __a) do { \ - JWT_CONFIG_DECLARE(__c); \ - read_key(__f); \ - __c.alg = __a; \ - __c.jw_key = g_item; \ - __j = jwt_create(&__c); \ - ck_assert_ptr_nonnull(__j); \ -} while(0) - #define JWT_TEST_MAIN(__title) ({ \ int number_failed = 0; \ SRunner *sr; \ @@ -84,11 +68,21 @@ static jwt_test_op_t jwt_test_ops[] = { ck_assert_str_eq(ops, jwt_test_ops[_i].name); \ }) +#define jwt_freemem(__ptr) ({ \ + if (__ptr) { \ + free(__ptr); \ + __ptr = NULL; \ + } \ +}) + +static inline void jwt_freememp(char **mem) { + jwt_freemem(*mem); +} +#define char_auto char __attribute__((cleanup(jwt_freememp))) + __attribute__((unused)) static jwk_set_t *g_jwk_set; __attribute__((unused)) static const jwk_item_t *g_item; -__attribute__((unused)) static JWT_CONFIG_DECLARE(t_config); - __attribute__((unused)) static struct { char *key; @@ -173,25 +167,12 @@ static void read_key(const char *key_file) ck_assert_int_eq(ferror(fp), 0); fclose(fp); - - if (strstr(key_file, ".pem") != NULL) - return; - - g_jwk_set = jwks_create_strn(test_data.key, test_data.key_len); - 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); - - t_config.jw_key = g_item; } __attribute__((unused)) static void free_key(void) { jwks_free(g_jwk_set); - jwt_config_init(&t_config); g_jwk_set = NULL; g_item = NULL; test_data.key_len = 0; @@ -199,145 +180,22 @@ static void free_key(void) test_data.key = NULL; } -__attribute__((unused)) -static void jwt_test_free(jwt_t **jwt) -{ - free_key(); - jwt_freep(jwt); -} - -__attribute__((unused)) -static void __verify_jwt(const char *jwt_str, const jwt_alg_t alg, - const char *file) -{ - jwt_auto_t *jwt = NULL; - - read_key(file); - - t_config.alg = alg; - - jwt = jwt_verify(jwt_str, &t_config); - free_key(); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_eq(jwt_error(jwt), 0); - - ck_assert_int_eq(jwt_get_alg(jwt), alg); - - free_key(); -} - __attribute__((unused)) static void __verify_jwk(const char *jwt_str, const jwk_item_t *item) { - JWT_CONFIG_DECLARE(config); - jwt_auto_t *jwt = NULL; - - config.jw_key = item; - config.alg = jwks_item_alg(item); - jwt = jwt_verify(jwt_str, &config); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_eq(jwt_error(jwt), 0); -} - -__attribute__((unused)) -static void __test_alg_key(const jwt_alg_t alg, const char *file, const char *pub) -{ - jwt_value_t jval; - jwt_auto_t *jwt = NULL; - int ret = 0; - char *out; - - CREATE_JWT(jwt, file, alg); - - jwt_set_ADD_STR(&jval, "iss", "files.maclara-llc.com"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "sub", "user0"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_INT(&jval, "iat", TS_CONST); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - out = jwt_encode_str(jwt); - ck_assert_ptr_nonnull(out); - - free_key(); - - __verify_jwt(out, alg, pub); - - free(out); - - /* auto free */ -} - -__attribute__((unused)) -static void __verify_alg_key(const char *key_file, const char *jwt_str, - const jwt_alg_t alg) -{ - jwt_valid_t *jwt_valid = NULL; - jwt_auto_t *jwt = NULL; - int ret = 0; - - read_key(key_file); - - t_config.alg = alg; - - jwt = jwt_verify(jwt_str, &t_config); - ck_assert_ptr_nonnull(jwt); - ck_assert_int_eq(jwt_error(jwt), 0); - - ck_assert_int_eq(alg, jwt_get_alg(jwt)); - - jwt_valid_new(&jwt_valid, alg); - - ret = jwt_validate(jwt, jwt_valid); - ck_assert_int_eq(JWT_VALIDATION_SUCCESS, ret); - - jwt_valid_free(jwt_valid); - - free_key(); -} - -__attribute__((unused)) -static void __compare_alg_key(const char *key_file, const char *jwt_str, - const jwt_alg_t alg) -{ - jwt_test_auto_t *jwt = NULL; - jwt_value_t jval; - int ret = 0; - char *out; + jwt_checker_auto_t *checker = NULL; + jwt_alg_t alg = JWT_ALG_NONE; // jwks_item_alg(item); + int ret; - CREATE_JWT(jwt, key_file, alg); + checker = jwt_checker_new(); + ck_assert_ptr_nonnull(checker); + ck_assert_int_eq(jwt_checker_error(checker), 0); - jwt_set_ADD_STR(&jval, "iss", "files.maclara-llc.com"); - ret = jwt_grant_add(jwt, &jval); + ret = jwt_checker_setkey(checker, alg, item); ck_assert_int_eq(ret, 0); - jwt_set_ADD_STR(&jval, "sub", "user0"); - ret = jwt_grant_add(jwt, &jval); + ret = jwt_checker_verify(checker, jwt_str); ck_assert_int_eq(ret, 0); - - jwt_set_ADD_STR(&jval, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - jwt_set_ADD_INT(&jval, "iat", TS_CONST); - ret = jwt_grant_add(jwt, &jval); - ck_assert_int_eq(ret, 0); - - out = jwt_encode_str(jwt); - ck_assert_ptr_nonnull(out); - - ck_assert_str_eq(out, jwt_str); - - free(out); } #endif /* JWT_TESTS_H */ diff --git a/tests/jwt_validate.c b/tests/jwt_validate.c deleted file mode 100644 index a9319631..00000000 --- a/tests/jwt_validate.c +++ /dev/null @@ -1,702 +0,0 @@ -/* Public domain, no copyright. Use at your own risk. */ - -#include -#include -#include -#include -#include - -#include "jwt_tests.h" - -static jwt_t *jwt; - -static const time_t iat = TS_CONST; -static const time_t not_before = TS_CONST + 60L; -static const time_t expires = TS_CONST + 600L; - -static void __setup_jwt() -{ - jwt_value_t jval; - - EMPTY_JWT(jwt); - jwt_set_ADD_STR(&jval, "iss", "test"); - jwt_grant_add(jwt, &jval); - - jwt_set_ADD_STR(&jval, "sub", "user0"); - jwt_grant_add(jwt, &jval); - - jwt_set_ADD_JSON(&jval, NULL, "{\"aud\": [\"svc1\",\"svc2\"]}"); - jwt_grant_add(jwt, &jval); - - jwt_set_ADD_INT(&jval, "iat", iat); - jwt_grant_add(jwt, &jval); - - jwt_set_ADD_BOOL(&jval, "admin", 1); - jwt_grant_add(jwt, &jval); -} - -static void __teardown_jwt() -{ - jwt_free(jwt); - jwt = NULL; -} - -#define __VAL_EQ(__v, __e, __str) do { \ - unsigned int __r = jwt_validate(jwt, __v); \ - char *__s; \ - ck_assert_int_eq(__r, __e); \ - __r = jwt_valid_get_status(__v); \ - ck_assert_int_eq(__e, __r); \ - __s = jwt_exception_str(__r); \ - ck_assert_str_eq(__str, __s); \ - free(__s); \ -} while(0); - -START_TEST(test_jwt_validate_errno) -{ - jwt_valid_t *jwt_valid = NULL; - unsigned int ret = 0; - char *exc; - - SET_OPS(); - - ck_assert_ptr_nonnull(jwt); - - ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jwt_valid); - - /* Validate fails with NULL jwt */ - ret = jwt_validate(NULL, jwt_valid); - ck_assert_int_eq(ret, JWT_VALIDATION_ERROR); - ret = jwt_valid_get_status(jwt_valid); - ck_assert_int_eq(ret, JWT_VALIDATION_ERROR); - exc = jwt_exception_str(ret); - ck_assert_str_eq(exc, "general failures"); - free(exc); - - - /* Validate fails with NULL jwt_valid */ - ret = jwt_validate(jwt, NULL); - ck_assert_int_eq(ret, JWT_VALIDATION_ERROR); - - /* Get status fails with NULL jwt_valid */ - ck_assert_int_eq(JWT_VALIDATION_ERROR, jwt_valid_get_status(NULL)); - - jwt_valid_free(jwt_valid); -} -END_TEST - -START_TEST(test_jwt_valid_algorithm) -{ - jwt_valid_t *jwt_valid = NULL; - unsigned int ret = 0; - - SET_OPS(); - - /* Matching algorithm is valid */ - ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jwt_valid); - - __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); - - jwt_valid_free(jwt_valid); - - /* Wrong algorithm is not valid */ - ret = jwt_valid_new(&jwt_valid, JWT_ALG_HS256); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jwt_valid); - - /* Starts with invalid */ - ck_assert_int_eq(JWT_VALIDATION_ERROR, jwt_valid_get_status(jwt_valid)); - - __VAL_EQ(jwt_valid, JWT_VALIDATION_ALG_MISMATCH, "algorithm mismatch"); - - jwt_valid_free(jwt_valid); -} -END_TEST - -START_TEST(test_jwt_valid_require_grant) -{ - jwt_valid_t *jwt_valid = NULL; - unsigned int ret = 0; - const char *valstr = NULL; - int valnum = 0; - - SET_OPS(); - - /* Valid when alg matches and all required grants match */ - ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jwt_valid); - - ret = jwt_valid_add_grant(jwt_valid, "iss", "test"); - ck_assert_int_eq(ret, 0); - - /* No duplicates */ - ret = jwt_valid_add_grant(jwt_valid, "iss", "other"); - ck_assert_int_eq(ret, EEXIST); - - /* Grant has expected value */ - valstr = jwt_valid_get_grant(jwt_valid, "iss"); - ck_assert_ptr_nonnull(valstr); - ck_assert_str_eq(valstr, "test"); - - ret = jwt_valid_add_grant_int(jwt_valid, "iat", (long)iat); - ck_assert_int_eq(ret, 0); - - /* No duplicates for int */ - ret = jwt_valid_add_grant_int(jwt_valid, "iat", (long)time(NULL)); - ck_assert_int_eq(ret, EEXIST); - - /* Grant has expected value */ - valnum = jwt_valid_get_grant_int(jwt_valid, "iat"); - ck_assert_int_eq(valnum, (long)iat); - - ret = jwt_valid_add_grant_bool(jwt_valid, "admin", 1); - ck_assert_int_eq(ret, 0); - - /* No duplicates for bool */ - ret = jwt_valid_add_grant_bool(jwt_valid, "admin", 0); - ck_assert_int_eq(ret, EEXIST); - - /* Grant has expected value */ - valnum = jwt_valid_get_grant_bool(jwt_valid, "admin"); - ck_assert_int_eq(valnum, 1); - - ret = jwt_valid_add_grants_json(jwt_valid, "{\"aud\": [\"svc1\",\"svc2\"]}"); - ck_assert_int_eq(ret, 0); - - __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); - - jwt_valid_free(jwt_valid); -} -END_TEST - -START_TEST(test_jwt_valid_nonmatch_grant) -{ - jwt_valid_t *jwt_valid = NULL; - unsigned int ret = 0; - - SET_OPS(); - - ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jwt_valid); - - /* Invalid when required grants don't match */ - ret = jwt_valid_add_grant(jwt_valid, "iss", "wrong"); - ck_assert_int_eq(ret, 0); - - __VAL_EQ(jwt_valid, JWT_VALIDATION_GRANT_MISMATCH, "grant mismatch"); - - jwt_valid_del_grants(jwt_valid, NULL); - - /* Invalid when required grants don't match (int) */ - ret = jwt_valid_add_grant_int(jwt_valid, "iat", (long)time(NULL) + 1); - ck_assert_int_eq(ret, 0); - - __VAL_EQ(jwt_valid, JWT_VALIDATION_GRANT_MISMATCH, "grant mismatch"); - - jwt_valid_del_grants(jwt_valid, NULL); - - /* Invalid when required grants don't match (bool) */ - ret = jwt_valid_add_grant_bool(jwt_valid, "admin", 0); - ck_assert_int_eq(ret, 0); - - __VAL_EQ(jwt_valid, JWT_VALIDATION_GRANT_MISMATCH, "grant mismatch"); - - jwt_valid_del_grants(jwt_valid, NULL); - - /* Invalid when required grants don't match (json) */ - ret = jwt_valid_add_grants_json(jwt_valid, "{\"aud\": [\"svc3\",\"svc4\"]}"); - ck_assert_int_eq(ret, 0); - - __VAL_EQ(jwt_valid, JWT_VALIDATION_GRANT_MISMATCH, "grant mismatch"); - - jwt_valid_free(jwt_valid); -} -END_TEST - -START_TEST(test_jwt_valid_grant_bool) -{ - jwt_valid_t *jwt_valid = NULL; - int val; - unsigned int ret = 0; - - SET_OPS(); - - ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jwt_valid); - - ret = jwt_valid_add_grant_bool(jwt_valid, "admin", 1); - ck_assert_int_eq(ret, 0); - - val = jwt_valid_get_grant_bool(jwt_valid, "admin"); - ck_assert(val); - - ret = jwt_valid_add_grant_bool(jwt_valid, "test", 0); - ck_assert_int_eq(ret, 0); - - val = jwt_valid_get_grant_bool(jwt_valid, "test"); - ck_assert(!val); - - val = jwt_valid_get_grant_bool(jwt_valid, "not found"); - ck_assert_int_eq(errno, ENOENT); - - jwt_valid_free(jwt_valid); -} -END_TEST - -START_TEST(test_jwt_valid_del_grants) -{ - jwt_valid_t *jwt_valid = NULL; - const char *val; - const char testval[] = "testing"; - unsigned int ret = 0; - - SET_OPS(); - - ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jwt_valid); - - ret = jwt_valid_add_grant(jwt_valid, "iss", testval); - ck_assert_int_eq(ret, 0); - - ret = jwt_valid_add_grant(jwt_valid, "other", testval); - ck_assert_int_eq(ret, 0); - - ret = jwt_valid_del_grants(jwt_valid, "iss"); - ck_assert_int_eq(ret, 0); - - val = jwt_valid_get_grant(jwt_valid, "iss"); - ck_assert_ptr_null(val); - - /* Delete non existent. */ - ret = jwt_valid_del_grants(jwt_valid, "iss"); - ck_assert_int_eq(ret, 0); - - /* Delete all grants. */ - ret = jwt_valid_del_grants(jwt_valid, NULL); - ck_assert_int_eq(ret, 0); - - val = jwt_valid_get_grant(jwt_valid, "other"); - ck_assert_ptr_null(val); - - jwt_valid_free(jwt_valid); -} -END_TEST - -START_TEST(test_jwt_valid_invalid_grant) -{ - jwt_valid_t *jwt_valid = NULL; - const char *val; - long valint = 0; - long valbool = 0; - unsigned int ret = 0; - - SET_OPS(); - - ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jwt_valid); - - ret = jwt_valid_add_grant(jwt_valid, "iss", NULL); - ck_assert_int_eq(ret, EINVAL); - - ret = jwt_valid_add_grant_int(jwt_valid, "", (long)time(NULL)); - ck_assert_int_eq(ret, EINVAL); - - val = jwt_valid_get_grant(jwt_valid, NULL); - ck_assert_int_eq(errno, EINVAL); - ck_assert_ptr_null(val); - - valint = jwt_valid_get_grant_int(jwt_valid, NULL); - ck_assert_int_eq(errno, EINVAL); - ck_assert(valint == 0); - - valbool = jwt_valid_get_grant_bool(jwt_valid, NULL); - ck_assert_int_eq(errno, EINVAL); - ck_assert(valbool == 0); - - jwt_valid_free(jwt_valid); -} -END_TEST - -START_TEST(test_jwt_valid_missing_grant) -{ - jwt_valid_t *jwt_valid = NULL; - unsigned int ret = 0; - - SET_OPS(); - - ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jwt_valid); - - /* JWT is invalid when required grants are not present */ - ret = jwt_valid_add_grant(jwt_valid, "np-str", "test"); - ck_assert_int_eq(ret, 0); - __VAL_EQ(jwt_valid, JWT_VALIDATION_GRANT_MISSING, "grant missing"); - - jwt_valid_del_grants(jwt_valid, NULL); - - /* JWT is invalid when required grants are not present (int) */ - ret = jwt_valid_add_grant_int(jwt_valid, "np-int", 7); - ck_assert_int_eq(ret, 0); - __VAL_EQ(jwt_valid, JWT_VALIDATION_GRANT_MISSING, "grant missing"); - - jwt_valid_del_grants(jwt_valid, NULL); - - /* JWT is invalid when required grants are not present (bool) */ - ret = jwt_valid_add_grant_int(jwt_valid, "np-bool", 1); - ck_assert_int_eq(ret, 0); - __VAL_EQ(jwt_valid, JWT_VALIDATION_GRANT_MISSING, "grant missing"); - - jwt_valid_del_grants(jwt_valid, NULL); - - /* JWT is invalid when required grants are not present (json) */ - ret = jwt_valid_add_grants_json(jwt_valid, "{\"np-other\": [\"foo\",\"bar\"]}"); - ck_assert_int_eq(ret, 0); - __VAL_EQ(jwt_valid, JWT_VALIDATION_GRANT_MISSING, "grant missing"); - - jwt_valid_free(jwt_valid); -} -END_TEST - -START_TEST(test_jwt_valid_not_before) -{ - jwt_valid_t *jwt_valid = NULL; - unsigned int ret = 0; - jwt_value_t jval; - - SET_OPS(); - - jwt_set_ADD_INT(&jval, "nbf", not_before); - jwt_grant_add(jwt, &jval); - - ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jwt_valid); - - /* JWT is invalid when now < not-before */ - ret = jwt_valid_set_now(jwt_valid, not_before - 1); - ck_assert_int_eq(ret, 0); - - __VAL_EQ(jwt_valid, JWT_VALIDATION_TOO_NEW, "token future dated"); - - /* JWT is valid when now >= not-before */ - ret = jwt_valid_set_now(jwt_valid, not_before); - ck_assert_int_eq(ret, 0); - - __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); - - jwt_valid_free(jwt_valid); -} -END_TEST - -START_TEST(test_jwt_valid_set_nbf_leeway) -{ - jwt_valid_t *jwt_valid = NULL; - unsigned int ret = 0; - - SET_OPS(); - - ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jwt_valid); - - /* 0 by default */ - time_t init_nbf_leeway = jwt_valid_get_nbf_leeway(jwt_valid); - ck_assert_int_eq(init_nbf_leeway, 0); - - /* Setting nbf_leeway */ - ret = jwt_valid_set_nbf_leeway(jwt_valid, 1); - ck_assert_int_eq(ret, 0); - - time_t set_nbf_leeway = jwt_valid_get_nbf_leeway(jwt_valid); - ck_assert_int_eq(set_nbf_leeway, 1); - - jwt_valid_free(jwt_valid); -} -END_TEST - -START_TEST(test_jwt_valid_not_before_leeway) -{ - jwt_valid_t *jwt_valid = NULL; - unsigned int ret = 0; - jwt_value_t jval; - - SET_OPS(); - - jwt_set_ADD_INT(&jval, "nbf", not_before); - jwt_grant_add(jwt, &jval); - - ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jwt_valid); - - /* Setting nbf_leeway */ - ret = jwt_valid_set_nbf_leeway(jwt_valid, 10); - ck_assert_int_eq(ret, 0); - - /* JWT is invalid when now < not-before - nbf_leeway */ - ret = jwt_valid_set_now(jwt_valid, (long)not_before - 15); - ck_assert_int_eq(ret, 0); - - __VAL_EQ(jwt_valid, JWT_VALIDATION_TOO_NEW, "token future dated"); - - /* JWT is valid when now >= not-before - nbf_leeway */ - ret = jwt_valid_set_now(jwt_valid, (long)not_before - 5); - ck_assert_int_eq(ret, 0); - - __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); - - jwt_valid_free(jwt_valid); -} -END_TEST - -START_TEST(test_jwt_valid_expires) -{ - jwt_valid_t *jwt_valid = NULL; - unsigned int ret = 0; - jwt_value_t jval; - - SET_OPS(); - - jwt_set_ADD_INT(&jval, "exp", expires); - jwt_grant_add(jwt, &jval); - - ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jwt_valid); - - /* JWT is valid when now < expires */ - ret = jwt_valid_set_now(jwt_valid, (long)expires - 1); - ck_assert_int_eq(ret, 0); - - __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); - - /* JWT is invalid when now >= expires */ - ret = jwt_valid_set_now(jwt_valid, (long)expires); - ck_assert_int_eq(ret, 0); - - __VAL_EQ(jwt_valid, JWT_VALIDATION_EXPIRED, "token expired"); - - jwt_valid_free(jwt_valid); -} -END_TEST - -START_TEST(test_jwt_valid_set_exp_leeway) -{ - jwt_valid_t *jwt_valid = NULL; - unsigned int ret = 0; - - SET_OPS(); - - ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jwt_valid); - - /* 0 by default */ - time_t init_exp_leeway = jwt_valid_get_exp_leeway(jwt_valid); - ck_assert_int_eq(init_exp_leeway, 0); - - /* Setting exp_leeway */ - ret = jwt_valid_set_exp_leeway(jwt_valid, 1); - ck_assert_int_eq(ret, 0); - - time_t set_exp_leeway = jwt_valid_get_exp_leeway(jwt_valid); - ck_assert_int_eq(set_exp_leeway, 1); - - jwt_valid_free(jwt_valid); -} -END_TEST - -START_TEST(test_jwt_valid_expires_leeway) -{ - jwt_valid_t *jwt_valid = NULL; - unsigned int ret = 0; - jwt_value_t jval; - - SET_OPS(); - - jwt_set_ADD_INT(&jval, "exp", expires); - jwt_grant_add(jwt, &jval); - - ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jwt_valid); - - /* Setting exp_leeway */ - ret = jwt_valid_set_exp_leeway(jwt_valid, 10); - ck_assert_int_eq(ret, 0); - - /* JWT is valid when now < expires + exp_leeway */ - ret = jwt_valid_set_now(jwt_valid, (long)expires + 5); - ck_assert_int_eq(ret, 0); - - __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); - - /* JWT is invalid when now >= expires + exp_leeway */ - ret = jwt_valid_set_now(jwt_valid, (long)expires + 15); - ck_assert_int_eq(ret, 0); - - __VAL_EQ(jwt_valid, JWT_VALIDATION_EXPIRED, "token expired"); - - jwt_valid_free(jwt_valid); -} -END_TEST - -START_TEST(test_jwt_valid_headers) -{ - jwt_value_t jval; - jwt_valid_t *jwt_valid = NULL; - unsigned int ret = 0; - - SET_OPS(); - - ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jwt_valid); - - ret = jwt_valid_set_headers(jwt_valid, 1); - ck_assert_int_eq(ret, 0); - - /* JWT is valid when iss in hdr matches iss in body */ - jwt_set_ADD_STR(&jval, "iss", "test"); - jwt_header_add(jwt, &jval); - __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); - - /* JWT is invalid when iss in hdr does not match iss in body */ - jwt_header_del(jwt, "iss"); - jwt_set_ADD_STR(&jval, "iss", "wrong"); - jwt_header_add(jwt, &jval); - __VAL_EQ(jwt_valid, JWT_VALIDATION_ISS_MISMATCH, "issuer mismatch"); - - /* JWT is valid when checking hdr and iss not replicated */ - jwt_header_del(jwt, "iss"); - __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); - - /* JWT is valid when iss in hdr matches iss in body */ - jwt_set_ADD_STR(&jval, "sub", "user0"); - jwt_header_add(jwt, &jval); - __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); - - /* JWT is invalid when iss in hdr does not match iss in body */ - jwt_header_del(jwt, "sub"); - jwt_set_ADD_STR(&jval, "sub", "wrong"); - jwt_header_add(jwt, &jval); - __VAL_EQ(jwt_valid, JWT_VALIDATION_SUB_MISMATCH, "subject mismatch"); - - /* JWT is valid when checking hdr and sub not replicated */ - jwt_header_del(jwt, "sub"); - __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); - - /* JWT is valid when checking hdr and aud matches */ - jwt_set_ADD_JSON(&jval, NULL, "{\"aud\": [\"svc1\",\"svc2\"]}"); - jwt_header_add(jwt, &jval); - __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); - - /* JWT is invalid when checking hdr and aud does not match */ - jwt_header_del(jwt, "aud"); - jwt_set_ADD_JSON(&jval, NULL, "{\"aud\": [\"svc1\",\"svc2\",\"svc3\"]}"); - jwt_header_add(jwt, &jval); - __VAL_EQ(jwt_valid, JWT_VALIDATION_AUD_MISMATCH, "audience mismatch"); - - /* JWT is invalid when checking hdr and aud does not match */ - jwt_header_del(jwt, "aud"); - __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); - - jwt_valid_free(jwt_valid); -} -END_TEST - -START_TEST(test_jwt_valid_grants_json) -{ - const char *json = "{\"id\":\"FVvGYTr3FhiURCFebsBOpBqTbzHdX/DvImiA2yheXr8=\"," - "\"iss\":\"localhost\",\"other\":[\"foo\",\"bar\"]," - "\"ref\":\"385d6518-fb73-45fc-b649-0527d8576130\"," - "\"scopes\":\"storage\",\"sub\":\"user0\"}"; - jwt_valid_t *jwt_valid = NULL; - const char *val; - char *json_val; - unsigned int ret = 0; - - SET_OPS(); - - ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); - ck_assert_int_eq(ret, 0); - ck_assert_ptr_nonnull(jwt_valid); - - ret = jwt_valid_add_grants_json(jwt_valid, json); - ck_assert_int_eq(ret, 0); - - val = jwt_valid_get_grant(jwt_valid, "ref"); - ck_assert_ptr_nonnull(val); - ck_assert_str_eq(val, "385d6518-fb73-45fc-b649-0527d8576130"); - - json_val = jwt_valid_get_grants_json(NULL, "other"); - ck_assert_ptr_null(json_val); - ck_assert_int_eq(errno, EINVAL); - - json_val = jwt_valid_get_grants_json(jwt_valid, "other"); - ck_assert_ptr_nonnull(json_val); - ck_assert_str_eq(json_val, "[\"foo\",\"bar\"]"); - - free(json_val); - - json_val = jwt_valid_get_grants_json(jwt_valid, NULL); - ck_assert_ptr_nonnull(json_val); - ck_assert_str_eq(json_val, json); - - free(json_val); - - jwt_valid_free(jwt_valid); -} -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_validate"); - - /* Run before and after each unit test. */ - tcase_add_checked_fixture(tc_core, __setup_jwt, __teardown_jwt); - - tcase_add_loop_test(tc_core, test_jwt_validate_errno, 0, i); - tcase_add_loop_test(tc_core, test_jwt_valid_algorithm, 0, i); - tcase_add_loop_test(tc_core, test_jwt_valid_require_grant, 0, i); - tcase_add_loop_test(tc_core, test_jwt_valid_nonmatch_grant, 0, i); - tcase_add_loop_test(tc_core, test_jwt_valid_invalid_grant, 0, i); - tcase_add_loop_test(tc_core, test_jwt_valid_missing_grant, 0, i); - tcase_add_loop_test(tc_core, test_jwt_valid_grant_bool, 0, i); - tcase_add_loop_test(tc_core, test_jwt_valid_grants_json, 0, i); - tcase_add_loop_test(tc_core, test_jwt_valid_del_grants, 0, i); - tcase_add_loop_test(tc_core, test_jwt_valid_not_before, 0, i); - tcase_add_loop_test(tc_core, test_jwt_valid_set_nbf_leeway, 0, i); - tcase_add_loop_test(tc_core, test_jwt_valid_not_before_leeway, 0, i); - tcase_add_loop_test(tc_core, test_jwt_valid_expires, 0, i); - tcase_add_loop_test(tc_core, test_jwt_valid_set_exp_leeway, 0, i); - tcase_add_loop_test(tc_core, test_jwt_valid_expires_leeway, 0, i); - tcase_add_loop_test(tc_core, test_jwt_valid_headers, 0, i); - - tcase_set_timeout(tc_core, 30); - - suite_add_tcase(s, tc_core); - - return s; -} - -int main(void) -{ - JWT_TEST_MAIN("LibJWT Validate"); -} diff --git a/tests/keys/eddsa_key_ed25519.json b/tests/keys/eddsa_key_ed25519.json index 983d10d1..5958cc2e 100644 --- a/tests/keys/eddsa_key_ed25519.json +++ b/tests/keys/eddsa_key_ed25519.json @@ -9,7 +9,6 @@ "kid": "7dc4a07e-51b6-47dd-ae8c-2da0ef921818", "kty": "OKP", "crv": "Ed25519", - "alg": "EdDSA", "x": "HUj-14kN6N4i5qNVkfEhwKiCf-tSrvRHstQdtV8a5QM", "d": "XY5oUZqGWVZhX7J09hG-rRnAKXiw1g_aBh-Bc52KZ_Y" } diff --git a/tests/keys/eddsa_key_ed25519_pub.json b/tests/keys/eddsa_key_ed25519_pub.json index b17b50f2..3f13c40c 100644 --- a/tests/keys/eddsa_key_ed25519_pub.json +++ b/tests/keys/eddsa_key_ed25519_pub.json @@ -8,7 +8,6 @@ "kid": "bf160513-a400-4bc9-ba52-bc90c5e55f60", "kty": "OKP", "crv": "Ed25519", - "alg": "EdDSA", "x": "HUj-14kN6N4i5qNVkfEhwKiCf-tSrvRHstQdtV8a5QM" } ] diff --git a/tests/keys/jwks_keyring.json b/tests/keys/jwks_keyring.json index 518cf8ee..8089ac4d 100644 --- a/tests/keys/jwks_keyring.json +++ b/tests/keys/jwks_keyring.json @@ -250,7 +250,7 @@ { "kty": "oct", "alg": "HS512", - "k": "5VQGqfFoKucG-LLYFmW500OiaKIJtedo_RHjOvt4d1qJoTVi_4k95CKc_iF8xNR3NSbyndUvCPSp" + "k": "vPnfAG10Y09YGh+DQQwQ-n1lye8hfaO1PYdh8qr5oOI5gxKaX1GNBgwtSWsFyt7txFpuMs4kf_3wPWIefC2rQg" }, { "kty": "oct", diff --git a/tests/keys/oct_key_256_invalid_base64.json b/tests/keys/oct_key_256_invalid_base64.json index c70b752e..2d184162 100644 --- a/tests/keys/oct_key_256_invalid_base64.json +++ b/tests/keys/oct_key_256_invalid_base64.json @@ -4,7 +4,7 @@ "key_ops": [ "sign" ], - "kid": "8b5a52de-6449-4745-9712-64cd79a0b837", + "kid": "test:invalid-base64", "kty": "oct", "k": "MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5WFk", "alg": "HS256" diff --git a/tests/keys/oct_key_512.json b/tests/keys/oct_key_512.json index eef6b202..5f946f30 100644 --- a/tests/keys/oct_key_512.json +++ b/tests/keys/oct_key_512.json @@ -3,7 +3,7 @@ { "kty": "oct", "alg": "HS512", - "k": "TVS/FVl4hPeR7OnxPKoMriFov2XeVrOBAJLdPf-Uq-9x5/HG6Z2nZkpDWp1Y6RYytSYfNE5nyLWxx8CDi5oAvw" + "k": "vPnfAG10Y09YGh+DQQwQ-n1lye8hfaO1PYdh8qr5oOI5gxKaX1GNBgwtSWsFyt7txFpuMs4kf_3wPWIefC2rQg" } ] } diff --git a/tests/keys/rsa_pss_key_2048-384.json b/tests/keys/rsa_pss_key_2048_384.json similarity index 100% rename from tests/keys/rsa_pss_key_2048-384.json rename to tests/keys/rsa_pss_key_2048_384.json diff --git a/tests/keys/rsa_pss_key_2048-384_pub.json b/tests/keys/rsa_pss_key_2048_384_pub.json similarity index 100% rename from tests/keys/rsa_pss_key_2048-384_pub.json rename to tests/keys/rsa_pss_key_2048_384_pub.json diff --git a/tests/keys/rsa_pss_key_2048-512.json b/tests/keys/rsa_pss_key_2048_512.json similarity index 100% rename from tests/keys/rsa_pss_key_2048-512.json rename to tests/keys/rsa_pss_key_2048_512.json diff --git a/tests/keys/rsa_pss_key_2048-512_pub.json b/tests/keys/rsa_pss_key_2048_512_pub.json similarity index 100% rename from tests/keys/rsa_pss_key_2048-512_pub.json rename to tests/keys/rsa_pss_key_2048_512_pub.json diff --git a/tests/keys/rsa_pss_key_2048_noalg.json b/tests/keys/rsa_pss_key_2048_noalg.json new file mode 100644 index 00000000..ebfe2fd2 --- /dev/null +++ b/tests/keys/rsa_pss_key_2048_noalg.json @@ -0,0 +1,18 @@ + { + "use": "sig", + "key_ops": [ + "verify", + "sign" + ], + "kid": "a9a0eb5b-67e9-4495-9030-a97d063d4bb4", + "kty": "RSA", + "alg": "PS384", + "n": "k2H-nc6pcHePEz7liCxy4sYXBbrks3jgmTyQo3nwfpEKecbPu8qraXg5Ogjm9LzfIapk3Jm_eFtY-zEhqgOTDipc_aXRhTZg71AHh-GgF7EtatrDwXs0Cs6M-dhF0MZpgqrUt2RgkjMD3CfgztfJVLi6KOeEcwfZ7zbjuvEGc7pvQrivNksvpxlOVVDYtF0o_kEWNGJB4djr5iJek2ZOO3Ij3iPw3bBx_nBO4p2NXb7j8mFcnbzyABNPGwr94SyXmWTNhtMYUrfYpjWcBp79H6FxXHRu0JZajYCIe95RrxQ23L1zBIZwhD1VJgYVGdhMfB9mfbo7FmxDwYaAmDoNYQ", + "e": "AQAB", + "d": "DHjrOhthnAdtWVF_mi9sxen0_wKkbqQiZniWIu3jhZdGCpOVEKbWxhuq-B4FvlAqr9r1vMENtfFBDZNIVO6gtL7hoYOCJu-Fpertxp8eh0xZDbH96_M-e1JxSR_dmwyxCoix0xbdbbHmHmzvg05s_TL1NuRbNJYSNUJXZLWxK2NKmlw-lPRb360n7H5KFSXECNWBsI9rkJuPvO_HmEYotiAotpxlC3N9cYmFc-G-W6JHTcF7CR2DZOauE3MI5jJpa-qzLXKKCphTq-PkbeM0jwgXciQ11RaC39KVy4YquWDTptilb4LgGQCMDqVIeW5VnBBmhtJvAJhPo_E5RK-MCw", + "p": "y6sKp6asFY-kv8hFEh_MPx-eTmZqr-g-CDEq7xgap9fI4LFGvemJv3isQcYJjPAFWreB0PF3uz6XlcK6x1CwjqWlNecQYUmDWq0kyJSvQ7Gn2txsIF6An5lkhSG_AQ-qLWay_WPmyjPXsxaF9aFM-QjTSW12wWcIKGCP0KSrhsM", + "q": "uUCYSXKi-4h0Z43Ol_jqy1mCD-yaICt4CtJeZTMbC7CI9qVPUaNHni3fUBxTDgOdH6xJGMZ1XuUFn68fjPB_mPmKUimYCrqlBzhRh7axT6tdD4WQc8rpEHvaucGjp2dBXrCDve_RPyMI8HYRS5kKGmvqhVpnwT0iVq5KRCERgQs", + "dp": "HFEzsguicXz_UwrtT4_MPhqPUCc9Pif0N_9eENAxJJUEAPmk9FK8LDsC-EIoocfddrd8_SgVlZsmSFRC0-OMKMkvJ0dxJ5WpBbp9GsZReAADbpKnFfkNuSMCrt_6RpN-_cEBselp9UMwAl5nUbeTlCx98_-cO1ev8q06UbATiTE", + "dq": "bmEPnAvDEs6OVTlIVILLG5jchzJ57xsXbpNEDVZzEfcA1L1Q0prQTG6WtXv6_MmocDvOXgW5323keO32hZqy3GorQaNo5VOqiu_CnHN9mLPJQjtaA7RuRBUYEUBQi6lZaNsR1DU1X0I3zFb9HSc1vuJ4HTbtxVTwxecZdxig_ls", + "qi": "RhNgmhA5ZNiHU0DoDoMzeaXVpwGxUZ_QkAmC1mc11SROp0khJmM-XfQBW5x6f8sYmX0zAj9EUh01dk1cKrg0dPX42c5nYItgo6ynFSJoSUkl_nu4tJysoCSh-3HtUtstooogDPQKUf4A9UAzgf1vrl95SRl-oL8MaVXlHm_o1KQ" + } diff --git a/tests/test-env.sh b/tests/test-env.sh index b9864ad9..d783cb21 100644 --- a/tests/test-env.sh +++ b/tests/test-env.sh @@ -1,5 +1,5 @@ if [ "$TEST" = "jwt_new" ]; then export JWT_CRYPTO=openssl -elif [ "$TEST" = "jwt_dump" ]; then +elif [ "$TEST" = "jwt_flipflop" ]; then export JWT_CRYPTO=NONEXISTENT fi