jwt-common: Make jwt-builder and jwt-checker truly generated at build time

Still keeps the code coverage and mingw builds working.

Signed-off-by: Ben Collins <bcollins@libjwt.io>
This commit is contained in:
Ben Collins 2025-02-13 11:45:20 -05:00
parent 032b0d2fa1
commit b9eff948ce
No known key found for this signature in database
GPG key ID: 5D5A57C7242B22CF
6 changed files with 32 additions and 644 deletions

View file

@ -60,16 +60,22 @@ set_target_properties(jwt_static PROPERTIES
COMPILE_FLAGS -DJWT_STATIC_DEFINE)
add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/libjwt/jwt-builder.c
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/libjwt
COMMAND sed -f builder.sed jwt-common.c > jwt-builder.c
DEPENDS libjwt/jwt-common.c libjwt/builder.sed)
OUTPUT jwt-builder.i
COMMAND ${CMAKE_C_COMPILER} -E ${CMAKE_SOURCE_DIR}/libjwt/jwt-common.c -DJWT_BUILDER
-o jwt-builder.i
DEPENDS libjwt/jwt-common.c)
add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/libjwt/jwt-checker.c
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/libjwt
COMMAND sed -f checker.sed jwt-common.c > jwt-checker.c
DEPENDS libjwt/jwt-common.c libjwt/checker.sed)
OUTPUT jwt-checker.i
COMMAND ${CMAKE_C_COMPILER} -E ${CMAKE_SOURCE_DIR}/libjwt/jwt-common.c -DJWT_CHECKER
-o jwt-checker.i
DEPENDS libjwt/jwt-common.c)
add_custom_target(gen_jwt_builder ALL DEPENDS jwt-builder.i)
add_custom_target(gen_jwt_checker ALL DEPENDS jwt-checker.i)
add_dependencies(jwt gen_jwt_builder gen_jwt_checker)
add_dependencies(jwt_static gen_jwt_builder gen_jwt_checker)
set(JWT_SOURCES libjwt/base64.c
libjwt/jwt-memory.c
@ -106,7 +112,6 @@ if (GNUTLS_FOUND)
target_link_libraries(jwt_static PUBLIC PkgConfig::GNUTLS)
list(APPEND JWT_SOURCES
libjwt/gnutls/sign-verify.c)
# libjwt/gnutls/jwk-parse.c
endif()
if (MBEDTLS_FOUND)
@ -116,7 +121,6 @@ if (MBEDTLS_FOUND)
target_link_libraries(jwt_static PUBLIC PkgConfig::MBEDTLS)
list(APPEND JWT_SOURCES
libjwt/mbedtls/sign-verify.c)
# libjwt/mbedtls/jwk-parse.c
endif()
set(HAVE_CRYPTO TRUE)

View file

@ -1,8 +0,0 @@
s/FUNC(\([^)]*\))/jwt_builder_\1/
s/jwt_common_t/jwt_builder_t/g
s/CLAIMS_DEF/JWT_CLAIM_IAT/g
s/.*XXX.*/\/\* XXX This file is generated, do not edit! \*\//
s/__DISABLE/0/
/#ifdef JWT_CHECKER/,/#endif/d
/#ifdef/d
/#endif/d

View file

@ -1,8 +0,0 @@
s/FUNC(\([^)]*\))/jwt_checker_\1/
s/jwt_common_t/jwt_checker_t/g
s/CLAIMS_DEF/(JWT_CLAIM_EXP\|JWT_CLAIM_NBF)/g
s/.*XXX.*/\/\* XXX This file is generated, do not edit! \*\//
s/__DISABLE/-1/
/#ifdef JWT_BUILDER/,/#endif/d
/#ifdef/d
/#endif/d

View file

@ -13,316 +13,4 @@
#include "jwt-private.h"
/* XXX This file is generated, do not edit! */
void jwt_builder_free(jwt_builder_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_builder_t *jwt_builder_new(void)
{
jwt_builder_t *__cmd = jwt_malloc(sizeof(*__cmd));
if (__cmd == NULL)
return NULL; // LCOV_EXCL_LINE
memset(__cmd, 0, sizeof(*__cmd));
__cmd->c.payload = json_object();
__cmd->c.headers = json_object();
__cmd->c.claims = JWT_CLAIM_IAT;
if (!__cmd->c.payload || !__cmd->c.headers)
jwt_freemem(__cmd); // LCOV_EXCL_LINE
return __cmd;
}
static int __setkey_check(jwt_builder_t *__cmd, const jwt_alg_t alg,
const jwk_item_t *key)
{
if (__cmd == NULL)
return 1;
if (key && !key->is_private_key) {
jwt_write_error(__cmd, "Signing requires a private key");
return 1;
}
/* 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 jwt_builder_setkey(jwt_builder_t *__cmd, const jwt_alg_t alg,
const jwk_item_t *key)
{
if (__setkey_check(__cmd, alg, key))
return 1;
__cmd->c.alg = alg;
__cmd->c.key = key;
return 0;
}
int jwt_builder_error(const jwt_builder_t *__cmd)
{
if (__cmd == NULL)
return 1;
return __cmd->error ? 1 : 0;
}
const char *jwt_builder_error_msg(const jwt_builder_t *__cmd)
{
if (__cmd == NULL)
return NULL;
return __cmd->error_msg;
}
void jwt_builder_error_clear(jwt_builder_t *__cmd)
{
if (__cmd == NULL)
return;
__cmd->error = 0;
__cmd->error_msg[0] = '\0';
}
int jwt_builder_enable_iat(jwt_builder_t *__cmd, int enable)
{
int orig;
if (!__cmd)
return -1;
orig = __cmd->c.claims & JWT_CLAIM_IAT ? 1 : 0;
if (enable)
__cmd->c.claims |= JWT_CLAIM_IAT;
else
__cmd->c.claims &= ~JWT_CLAIM_IAT;
return orig;
}
int jwt_builder_setcb(jwt_builder_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 won't work");
return 1;
}
__cmd->c.cb = cb;
__cmd->c.cb_ctx = ctx;
return 0;
}
void *jwt_builder_getctx(jwt_builder_t *__cmd)
{
if (__cmd == NULL)
return NULL;
return __cmd->c.cb_ctx;
}
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_builder_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;
// LCOV_EXCL_START
default:
return value->error = JWT_VALUE_ERR_INVALID;
// LCOV_EXCL_STOP
}
return doer(which, value);
}
/* Claims */
jwt_value_error_t jwt_builder_claim_get(jwt_builder_t *__cmd, jwt_value_t *value)
{
return __run_it(__cmd, __CLAIM, value, __getter);
}
jwt_value_error_t jwt_builder_claim_set(jwt_builder_t *__cmd, jwt_value_t *value)
{
return __run_it(__cmd, __CLAIM, value, __setter);
}
jwt_value_error_t jwt_builder_claim_del(jwt_builder_t *__cmd, const char *claim)
{
if (!__cmd)
return JWT_VALUE_ERR_INVALID;
return __deleter(__cmd->c.payload, claim);
}
/* Headers */
jwt_value_error_t jwt_builder_header_get(jwt_builder_t *__cmd, jwt_value_t *value)
{
return __run_it(__cmd, __HEADER, value, __getter);
}
jwt_value_error_t jwt_builder_header_set(jwt_builder_t *__cmd, jwt_value_t *value)
{
return __run_it(__cmd, __HEADER, value, __setter);
}
jwt_value_error_t jwt_builder_header_del(jwt_builder_t *__cmd, const char *header)
{
if (!__cmd)
return JWT_VALUE_ERR_INVALID;
return __deleter(__cmd->c.headers, header);
}
/* Time offsets */
int jwt_builder_time_offset(jwt_builder_t *__cmd, jwt_claims_t claim, time_t secs)
{
if (!__cmd)
return 1;
switch (claim) {
case JWT_CLAIM_EXP:
__cmd->c.exp = secs;
break;
case JWT_CLAIM_NBF:
__cmd->c.nbf = secs;
break;
default:
return 1;
}
if (secs <= 0)
__cmd->c.claims &= ~claim;
else
__cmd->c.claims |= claim;
return 0;
}
char *jwt_builder_generate(jwt_builder_t *__cmd)
{
JWT_CONFIG_DECLARE(config);
jwt_auto_t *jwt = NULL;
char *out = NULL;
jwt_value_t jval;
time_t tm = time(NULL);
if (__cmd == NULL)
return NULL;
jwt = jwt_malloc(sizeof(*jwt));
if (jwt == NULL)
return NULL; // LCOV_EXCL_LINE
memset(jwt, 0, sizeof(*jwt));
jwt->headers = json_deep_copy(__cmd->c.headers);
jwt->claims = json_deep_copy(__cmd->c.payload);
/* Our internal work first */
if (__cmd->c.claims & JWT_CLAIM_IAT) {
jwt_set_SET_INT(&jval, "iat", (long)tm);
jval.replace = 1;
jwt_claim_set(jwt, &jval);
}
if (__cmd->c.claims & JWT_CLAIM_NBF) {
jwt_set_SET_INT(&jval, "nbf", (long)(tm + __cmd->c.nbf));
jval.replace = 1;
jwt_claim_set(jwt, &jval);
}
if (__cmd->c.claims & JWT_CLAIM_EXP) {
jwt_set_SET_INT(&jval, "exp", (long)(tm + __cmd->c.exp));
jval.replace = 1;
jwt_claim_set(jwt, &jval);
}
/* Alg and key checks */
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 (__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;
if (jwt_head_setup(jwt))
return NULL; // LCOV_EXCL_LINE
out = jwt_encode_str(jwt);
jwt_copy_error(__cmd, jwt);
return out;
}
#include "jwt-builder.i"

View file

@ -13,303 +13,4 @@
#include "jwt-private.h"
/* XXX This file is generated, do not edit! */
void jwt_checker_free(jwt_checker_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_checker_t *jwt_checker_new(void)
{
jwt_checker_t *__cmd = jwt_malloc(sizeof(*__cmd));
if (__cmd == NULL)
return NULL; // LCOV_EXCL_LINE
memset(__cmd, 0, sizeof(*__cmd));
__cmd->c.payload = json_object();
__cmd->c.headers = json_object();
__cmd->c.claims = (JWT_CLAIM_EXP|JWT_CLAIM_NBF);
if (!__cmd->c.payload || !__cmd->c.headers)
jwt_freemem(__cmd); // LCOV_EXCL_LINE
return __cmd;
}
static int __setkey_check(jwt_checker_t *__cmd, const jwt_alg_t alg,
const jwk_item_t *key)
{
if (__cmd == NULL)
return 1;
/* 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 jwt_checker_setkey(jwt_checker_t *__cmd, const jwt_alg_t alg,
const jwk_item_t *key)
{
if (__setkey_check(__cmd, alg, key))
return 1;
__cmd->c.alg = alg;
__cmd->c.key = key;
return 0;
}
int jwt_checker_error(const jwt_checker_t *__cmd)
{
if (__cmd == NULL)
return 1;
return __cmd->error ? 1 : 0;
}
const char *jwt_checker_error_msg(const jwt_checker_t *__cmd)
{
if (__cmd == NULL)
return NULL;
return __cmd->error_msg;
}
void jwt_checker_error_clear(jwt_checker_t *__cmd)
{
if (__cmd == NULL)
return;
__cmd->error = 0;
__cmd->error_msg[0] = '\0';
}
int jwt_checker_setcb(jwt_checker_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 won't work");
return 1;
}
__cmd->c.cb = cb;
__cmd->c.cb_ctx = ctx;
return 0;
}
void *jwt_checker_getctx(jwt_checker_t *__cmd)
{
if (__cmd == NULL)
return NULL;
return __cmd->c.cb_ctx;
}
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_checker_t *__cmd, _setget_type_t type,
jwt_value_t *value, __doer_t doer)
{
json_t *which = NULL;
switch (type) {
case __CLAIM:
which = __cmd->c.payload;
break;
// LCOV_EXCL_START
default:
return value->error = JWT_VALUE_ERR_INVALID;
// LCOV_EXCL_STOP
}
return doer(which, value);
}
/* Just a few types of claims */
static const char *__get_name(jwt_claims_t type)
{
if (type == JWT_CLAIM_ISS)
return "iss";
else if (type == JWT_CLAIM_AUD)
return "aud";
else if (type == JWT_CLAIM_SUB)
return "sub";
return NULL;
}
const char *jwt_checker_claim_get(jwt_checker_t *__cmd, jwt_claims_t type)
{
const char *name = NULL;
jwt_value_t jval;
if (!__cmd)
return NULL;
name = __get_name(type);
if (name == NULL)
return NULL;
jwt_set_GET_STR(&jval, name);
__run_it(__cmd, __CLAIM, &jval, __getter);
/* Ignore errors, just return a string or NULL */
return jval.str_val;
}
int jwt_checker_claim_set(jwt_checker_t *__cmd, jwt_claims_t type, const char *value)
{
const char *name = NULL;
jwt_value_t jval;
if (!__cmd || !value)
return 1;
name = __get_name(type);
if (name == NULL)
return 1;
__cmd->c.claims |= type;
jwt_set_SET_STR(&jval, name, value);
jval.replace = 1;
return __run_it(__cmd, __CLAIM, &jval, __setter) ? 1 : 0;
}
int jwt_checker_claim_del(jwt_checker_t *__cmd, jwt_claims_t type)
{
const char *name = NULL;
if (!__cmd)
return 1;
name = __get_name(type);
if (name == NULL)
return 1;
__cmd->c.claims &= ~type;
return __deleter(__cmd->c.payload, name);
}
/* Time offsets */
int jwt_checker_time_leeway(jwt_checker_t *__cmd, jwt_claims_t claim, time_t secs)
{
if (!__cmd)
return 1;
switch (claim) {
case JWT_CLAIM_EXP:
__cmd->c.exp = secs;
break;
case JWT_CLAIM_NBF:
__cmd->c.nbf = secs;
break;
default:
return 1;
}
if (secs <= -1)
__cmd->c.claims &= ~claim;
else
__cmd->c.claims |= claim;
return 0;
}
int jwt_checker_verify(jwt_checker_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) {
// LCOV_EXCL_START
jwt_write_error(__cmd, "Could not allocate JWT object");
return 1;
// LCOV_EXCL_STOP
}
/* 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 (__setkey_check(__cmd, config.alg, config.key))
return 1;
jwt->key = config.key;
jwt->checker = __cmd;
/* Finish it up */
jwt = jwt_verify_complete(jwt, &config, token, payload_len);
/* Copy any errors back */
jwt_copy_error(__cmd, jwt);
return __cmd->error;
}
#include "jwt-checker.i"

View file

@ -6,14 +6,25 @@
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 <stdlib.h>
#include <string.h>
/* XXX This file is used to generate jwt-builder.i and jwt-checker.i */
#include <jwt.h>
#ifdef JWT_BUILDER
#define jwt_common_t jwt_builder_t
#define FUNC(__x) jwt_builder_##__x
#define CLAIMS_DEF JWT_CLAIM_IAT
#define __DISABLE 0
#endif
#include "jwt-private.h"
#ifdef JWT_CHECKER
#define jwt_common_t jwt_checker_t
#define FUNC(__x) jwt_checker_##__x
#define CLAIMS_DEF (JWT_CLAIM_EXP | JWT_CLAIM_NBF)
#define __DISABLE -1
#endif
/* XXX This file is used to generate jwt-builder.c and jwt-checker.c */
#ifndef jwt_common_t
#error Must have target defined
#endif
void FUNC(free)(jwt_common_t *__cmd)
{