From 24e731e4ee901f3a201023a128db531b3af95af8 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Tue, 12 Nov 2024 00:10:46 +0100 Subject: [PATCH] ThirdParty: upgrade l8w8jwt library to 2.4.0 Security fixes. --- src/thirdparty/jwt/CMakeLists.txt | 1 + src/thirdparty/jwt/base64.c | 11 +- src/thirdparty/jwt/claim.c | 98 +- src/thirdparty/jwt/decode.c | 1162 ++++++++++++++--------- src/thirdparty/jwt/encode.c | 47 +- src/thirdparty/jwt/include/decode.h | 66 +- src/thirdparty/jwt/include/encode.h | 8 +- src/thirdparty/jwt/include/timehelper.h | 17 + src/thirdparty/jwt/include/util.h | 43 + src/thirdparty/jwt/include/version.h | 19 +- src/thirdparty/jwt/util.c | 2 +- src/thirdparty/jwt/version.c | 29 - 12 files changed, 964 insertions(+), 539 deletions(-) create mode 100644 src/thirdparty/jwt/include/timehelper.h diff --git a/src/thirdparty/jwt/CMakeLists.txt b/src/thirdparty/jwt/CMakeLists.txt index 3f257f5c..d33c8fd6 100644 --- a/src/thirdparty/jwt/CMakeLists.txt +++ b/src/thirdparty/jwt/CMakeLists.txt @@ -19,6 +19,7 @@ add_sources( SOURCE_GROUP "Include" "include/decode.h" "include/encode.h" "include/retcodes.h" + "include/timehelper.h" "include/util.h" "include/version.h" ) diff --git a/src/thirdparty/jwt/base64.c b/src/thirdparty/jwt/base64.c index 2d7fe85b..7b4a9920 100644 --- a/src/thirdparty/jwt/base64.c +++ b/src/thirdparty/jwt/base64.c @@ -104,6 +104,7 @@ after February 11, 2012 is no longer under the GPL v2 option. #include #include +#include "include/util.h" #include "include/base64.h" #include "include/version.h" #include "include/retcodes.h" @@ -133,7 +134,7 @@ int l8w8jwt_base64_encode(const int url, const uint8_t* data, const size_t data_ return L8W8JWT_OVERFLOW; } - *out = (char*)malloc(olen); + *out = l8w8jwt_malloc(olen); if (*out == NULL) { return L8W8JWT_OUT_OF_MEM; @@ -228,14 +229,14 @@ int l8w8jwt_base64_decode(const int url, const char* data, const size_t data_len memset(dtable, 0x80, 256); - for (i = 0; i < 64; i++) + for (i = 0; i < 64; ++i) { dtable[table[i]] = (uint8_t)i; } dtable['='] = 0; - for (i = 0; i < in_length; i++) + for (i = 0; i < in_length; ++i) { if (dtable[(unsigned char)data[i]] != 0x80) count++; @@ -249,7 +250,7 @@ int l8w8jwt_base64_decode(const int url, const char* data, const size_t data_len if (r == 3) r = 1; - *out = (uint8_t*)calloc(count / 4 * 3 + 16, sizeof(uint8_t)); + *out = l8w8jwt_calloc(count / 4 * 3 + 16, sizeof(uint8_t)); if (*out == NULL) { return L8W8JWT_OUT_OF_MEM; @@ -261,7 +262,7 @@ int l8w8jwt_base64_decode(const int url, const char* data, const size_t data_len uint8_t block[4]; uint8_t* pos = *out; - for (i = 0; i < in_length + r; i++) + for (i = 0; i < in_length + r; ++i) { const unsigned char c = i < in_length ? data[i] : '='; diff --git a/src/thirdparty/jwt/claim.c b/src/thirdparty/jwt/claim.c index 8918f0bc..a21408b5 100644 --- a/src/thirdparty/jwt/claim.c +++ b/src/thirdparty/jwt/claim.c @@ -25,45 +25,86 @@ extern "C" { #include #include +#include void l8w8jwt_free_claims(struct l8w8jwt_claim* claims, const size_t claims_count) { - if (claims != NULL && claims_count > 0) + if (claims == NULL) { - for (struct l8w8jwt_claim* claim = claims; claim < claims + claims_count; ++claim) + return; + } + + for (struct l8w8jwt_claim* claim = claims; claim < claims + claims_count; ++claim) + { + if (claim == NULL) { - if (claim == NULL) - continue; - - l8w8jwt_zero(claim->key, claim->key_length); - l8w8jwt_zero(claim->value, claim->value_length); - - l8w8jwt_free(claim->key); - l8w8jwt_free(claim->value); + continue; } - l8w8jwt_zero(claims, claims_count * sizeof(struct l8w8jwt_claim)); - l8w8jwt_free(claims); + mbedtls_platform_zeroize(claim->key, claim->key_length); + mbedtls_platform_zeroize(claim->value, claim->value_length); + + l8w8jwt_free(claim->key); + l8w8jwt_free(claim->value); } + + if (claims_count != 0) + { + mbedtls_platform_zeroize(claims, claims_count * sizeof(struct l8w8jwt_claim)); + } + + l8w8jwt_free(claims); } static inline void l8w8jwt_escape_claim_string(struct chillbuff* stringbuilder, const char* string, const size_t string_length) { + static const char* escape_table[] = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", "\\u0007", + "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r", "\\u000e", "\\u000f", + "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", "\\u0015", "\\u0016", "\\u0017", + "\\u0018", "\\u0019", "\\u001a", "\\u001b", "\\u001c", "\\u001d", "\\u001e", "\\u001f", + NULL, NULL, "\\\"", NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\/", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, "\\\\", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\u007f", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL + }; + for (size_t i = 0; i < string_length; ++i) { const char c = string[i]; + const char* e = escape_table[c]; - switch (c) + if (e) { - case '\\': - chillbuff_push_back(stringbuilder, "\\\\", 2); - break; - case '\"': - chillbuff_push_back(stringbuilder, "\\\"", 2); - break; - default: - chillbuff_push_back(stringbuilder, &c, 1); - break; + chillbuff_push_back(stringbuilder, e, strlen(e)); + } + else + { + chillbuff_push_back(stringbuilder, &c, 1); } } } @@ -110,15 +151,20 @@ int l8w8jwt_write_claims(struct chillbuff* stringbuilder, struct l8w8jwt_claim* chillbuff_push_back(stringbuilder, "\":", 2); if (claim->type == L8W8JWT_CLAIM_TYPE_STRING) + { chillbuff_push_back(stringbuilder, "\"", 1); - chillbuff_clear(&escape_buffer); - l8w8jwt_escape_claim_string(&escape_buffer, claim->value, value_length); + chillbuff_clear(&escape_buffer); + l8w8jwt_escape_claim_string(&escape_buffer, claim->value, value_length); - chillbuff_push_back(stringbuilder, escape_buffer.array,escape_buffer.length); + chillbuff_push_back(stringbuilder, escape_buffer.array, escape_buffer.length); - if (claim->type == L8W8JWT_CLAIM_TYPE_STRING) chillbuff_push_back(stringbuilder, "\"", 1); + } + else + { + chillbuff_push_back(stringbuilder, claim->value, value_length); + } first = 0; } diff --git a/src/thirdparty/jwt/decode.c b/src/thirdparty/jwt/decode.c index 121a2cab..34aea9f2 100644 --- a/src/thirdparty/jwt/decode.c +++ b/src/thirdparty/jwt/decode.c @@ -29,12 +29,9 @@ extern "C" { #include #include - #include #include #include -#include -#include #include #include @@ -80,75 +77,154 @@ static inline void md_info_from_alg(const int alg, mbedtls_md_info_t** md_info, } } -static int l8w8jwt_unescape_claim(struct l8w8jwt_claim* claim, const char* key, const size_t key_length, const char* value, const size_t value_length) +static char* l8w8jwt_unescape_string(char* out, const char* in, const size_t n) { - claim->key_length = 0; - claim->key = (char*)calloc(sizeof(char), key_length + 1); - - claim->value_length = 0; - claim->value = (char*)calloc(sizeof(char), value_length + 1); - - if (claim->key == NULL || claim->value == NULL) + for (size_t i = 0; i < n; ++i) { - free((void*)claim->key); - free(claim->value); - return L8W8JWT_OUT_OF_MEM; - } - - char* out_key = claim->key; - char* out_value = claim->value; - - for (size_t i = 0; i < key_length; ++i) - { - const char c = key[i]; - *out_key = c; - - if (c == '\\' && i != key_length - 1) + char c = in[i]; + if (c == '\\' && i + 1 < n) { - const char nc = key[i + 1]; - if (nc == '\"') + switch (in[i + 1]) { - *out_key = '\"'; - } - ++i; - } - - ++out_key; - } - - for (size_t i = 0; i < value_length; ++i) - { - const char c = value[i]; - *out_value = c; - - if (c == '\\' && i != value_length - 1) - { - const char nc = value[i + 1]; - - switch (nc) - { - case '\"': - *out_value = '\"'; + case '"': + c = '"'; + ++i; + break; + case '\\': + c = '\\'; + ++i; break; case '/': - *out_value = '/'; + c = '/'; + ++i; + break; + case 'b': + c = '\b'; + ++i; + break; + case 'f': + c = '\f'; + ++i; + break; + case 'n': + c = '\n'; + ++i; + break; + case 'r': + c = '\r'; + ++i; + break; + case 't': + c = '\t'; + ++i; break; default: break; } - - ++i; } + *(out++) = c; + } + return out; +} - ++out_value; +static int l8w8jwt_unescape_claim(struct l8w8jwt_claim* claim, const char* key, const size_t key_length, const char* value, const size_t value_length) +{ + claim->key_length = 0; + claim->key = l8w8jwt_calloc(sizeof(char), key_length + 1); + + claim->value_length = 0; + claim->value = l8w8jwt_calloc(sizeof(char), value_length + 1); + + if (claim->key == NULL || claim->value == NULL) + { + free(claim->key); + free(claim->value); + return L8W8JWT_OUT_OF_MEM; } - claim->key_length = (size_t)(out_key - claim->key); - claim->value_length = (size_t)(out_value - claim->value); + char* end_key = l8w8jwt_unescape_string(claim->key, key, key_length); + *end_key = '\0'; + claim->key_length = (size_t)(end_key - claim->key); + + if (claim->type == L8W8JWT_CLAIM_TYPE_STRING) + { + char* end_value = l8w8jwt_unescape_string(claim->value, value, value_length); + *end_value = '\0'; + claim->value_length = (size_t)(end_value - claim->value); + } + else + { + strncpy(claim->value, value, value_length); + claim->value[value_length] = '\0'; + claim->value_length = value_length; + } return L8W8JWT_SUCCESS; } +static int l8w8jwt_decode_segments(const struct l8w8jwt_decoding_params* params, uint8_t** out_header, size_t* out_header_length, uint8_t** out_payload, size_t* out_payload_length, uint8_t** out_signature, size_t* out_signature_length) +{ + int r = L8W8JWT_SUCCESS; + + const int alg = params->alg; + + char* current = params->jwt; + char* next = strchr(params->jwt, '.'); + + if (next == NULL) /* No payload. */ + { + return L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT; + } + + size_t current_length = next - current; + + r = l8w8jwt_base64_decode(true, current, current_length, out_header, out_header_length); + if (r != L8W8JWT_SUCCESS) + { + if (r != L8W8JWT_OUT_OF_MEM) + r = L8W8JWT_BASE64_FAILURE; + goto exit; + } + + current = next + 1; + next = strchr(current, '.'); + + if (next == NULL && alg != -1) /* No signature. */ + { + r = L8W8JWT_DECODE_FAILED_MISSING_SIGNATURE; + goto exit; + } + + current_length = (next != NULL ? next : params->jwt + params->jwt_length) - current; + + r = l8w8jwt_base64_decode(true, current, current_length, out_payload, out_payload_length); + if (r != L8W8JWT_SUCCESS) + { + if (r != L8W8JWT_OUT_OF_MEM) + r = L8W8JWT_BASE64_FAILURE; + goto exit; + } + + if (next != NULL) + { + current = next + 1; + current_length = (params->jwt + params->jwt_length) - current; + + r = l8w8jwt_base64_decode(true, current, current_length, out_signature, out_signature_length); + if (r != L8W8JWT_SUCCESS) + { + if (r != L8W8JWT_OUT_OF_MEM) + r = L8W8JWT_BASE64_FAILURE; + goto exit; + } + } + + r = L8W8JWT_SUCCESS; + +exit: + return r; +} + static int l8w8jwt_parse_claims(chillbuff* buffer, char* json, const size_t json_length) { jsmn_parser parser; @@ -166,7 +242,7 @@ static int l8w8jwt_parse_claims(chillbuff* buffer, char* json, const size_t json } jsmntok_t _tokens[64]; - jsmntok_t* tokens = r <= (sizeof(_tokens) / sizeof(_tokens[0])) ? _tokens : (jsmntok_t*)malloc(r * sizeof(jsmntok_t)); + jsmntok_t* tokens = r <= (sizeof(_tokens) / sizeof(_tokens[0])) ? _tokens : l8w8jwt_malloc(r * sizeof(jsmntok_t)); if (tokens == NULL) { @@ -202,23 +278,40 @@ static int l8w8jwt_parse_claims(chillbuff* buffer, char* json, const size_t json switch (value.type) { - case JSMN_UNDEFINED: { + case JSMN_UNDEFINED: + { claim.type = L8W8JWT_CLAIM_TYPE_OTHER; break; } - case JSMN_OBJECT: { - claim.type = L8W8JWT_CLAIM_TYPE_OBJECT; - break; - } - case JSMN_ARRAY: { - claim.type = L8W8JWT_CLAIM_TYPE_ARRAY; - break; - } - case JSMN_STRING: { + case JSMN_STRING: + { claim.type = L8W8JWT_CLAIM_TYPE_STRING; break; } - case JSMN_PRIMITIVE: { + case JSMN_OBJECT: + { + claim.type = L8W8JWT_CLAIM_TYPE_OBJECT; + + while (tokens[i + 1].end <= value.end && i < r) + { + ++i; + } + + break; + } + case JSMN_ARRAY: + { + claim.type = L8W8JWT_CLAIM_TYPE_ARRAY; + + while (tokens[i + 1].end <= value.end && i < r) + { + ++i; + } + + break; + } + case JSMN_PRIMITIVE: + { const int value_length = value.end - value.start; if (value_length <= 5 && (strncmp(json + value.start, "true", 4) == 0 || strncmp(json + value.start, "false", 5) == 0)) @@ -251,7 +344,8 @@ static int l8w8jwt_parse_claims(chillbuff* buffer, char* json, const size_t json break; } - default: { + default: + { r = L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT; goto exit; } @@ -276,6 +370,382 @@ exit: return r; } +static void l8w8jwt_validate_claims(const struct l8w8jwt_decoding_params* params, const chillbuff* claims, enum l8w8jwt_validation_result* out_validation_result) +{ + size_t validation_length; + + if (params->validate_sub != NULL) + { + struct l8w8jwt_claim* c = l8w8jwt_get_claim(claims->array, claims->length, "sub", 3); + + validation_length = params->validate_sub_length ? params->validate_sub_length : strlen(params->validate_sub); + + if (c == NULL || c->value_length != validation_length || strncmp(c->value, params->validate_sub, validation_length) != 0) + { + *out_validation_result |= (unsigned)L8W8JWT_SUB_FAILURE; + } + } + + if (params->validate_aud != NULL) + { + struct l8w8jwt_claim* c = l8w8jwt_get_claim(claims->array, claims->length, "aud", 3); + + validation_length = params->validate_aud_length ? params->validate_aud_length : strlen(params->validate_aud); + + if (c == NULL || c->value_length != validation_length || strncmp(c->value, params->validate_aud, validation_length) != 0) + { + *out_validation_result |= (unsigned)L8W8JWT_AUD_FAILURE; + } + } + + if (params->validate_iss != NULL) + { + struct l8w8jwt_claim* c = l8w8jwt_get_claim(claims->array, claims->length, "iss", 3); + + validation_length = params->validate_iss_length ? params->validate_iss_length : strlen(params->validate_iss); + + if (c == NULL || c->value_length != validation_length || strncmp(c->value, params->validate_iss, validation_length) != 0) + { + *out_validation_result |= (unsigned)L8W8JWT_ISS_FAILURE; + } + } + + if (params->validate_jti != NULL) + { + struct l8w8jwt_claim* c = l8w8jwt_get_claim(claims->array, claims->length, "jti", 3); + + validation_length = params->validate_jti_length ? params->validate_jti_length : strlen(params->validate_jti); + + if (c == NULL || c->value_length != validation_length || strncmp(c->value, params->validate_jti, validation_length) != 0) + { + *out_validation_result |= (unsigned)L8W8JWT_JTI_FAILURE; + } + } + + const l8w8jwt_time_t ct = l8w8jwt_time(NULL); + + if (params->validate_exp) + { + struct l8w8jwt_claim* c = l8w8jwt_get_claim(claims->array, claims->length, "exp", 3); + if (c == NULL || ct - params->exp_tolerance_seconds > strtoll(c->value, NULL, 10)) + { + *out_validation_result |= (unsigned)L8W8JWT_EXP_FAILURE; + } + } + + if (params->validate_nbf) + { + struct l8w8jwt_claim* c = l8w8jwt_get_claim(claims->array, claims->length, "nbf", 3); + if (c == NULL || ct + params->nbf_tolerance_seconds < strtoll(c->value, NULL, 10)) + { + *out_validation_result |= (unsigned)L8W8JWT_NBF_FAILURE; + } + } + + if (params->validate_iat) + { + struct l8w8jwt_claim* c = l8w8jwt_get_claim(claims->array, claims->length, "iat", 3); + if (c == NULL || ct + params->iat_tolerance_seconds < strtoll(c->value, NULL, 10)) + { + *out_validation_result |= (unsigned)L8W8JWT_IAT_FAILURE; + } + } + + if (params->validate_typ) + { + struct l8w8jwt_claim* c = l8w8jwt_get_claim(claims->array, claims->length, "typ", 3); + if (c == NULL || l8w8jwt_strncmpic(c->value, params->validate_typ, params->validate_typ_length) != 0) + { + *out_validation_result |= (unsigned)L8W8JWT_TYP_FAILURE; + } + } +} + +static int l8w8jwt_verify_signature(const struct l8w8jwt_decoding_params* params, enum l8w8jwt_validation_result* out_validation_res, const uint8_t* signature, const size_t signature_length) +{ + int r = L8W8JWT_SUCCESS; + + const int alg = params->alg; + + if (alg == -1 || signature == NULL || signature_length == 0) + { + return r; + } + + if (params->verification_key == NULL) + { + return L8W8JWT_NULL_ARG; + } + + if (params->verification_key_length == 0 || params->verification_key_length > L8W8JWT_MAX_KEY_SIZE) + { + return L8W8JWT_INVALID_ARG; + } + + int is_cert = 0; // If the validation PEM is a X.509 certificate, this will be set to 1. + + mbedtls_pk_context pk; + mbedtls_pk_init(&pk); + + mbedtls_x509_crt crt; + mbedtls_x509_crt_init(&crt); + +#if L8W8JWT_SMALL_STACK + unsigned char* key = l8w8jwt_calloc(sizeof(unsigned char), L8W8JWT_MAX_KEY_SIZE); + if (key == NULL) + { + r = L8W8JWT_OUT_OF_MEM; + goto exit; + } +#else + unsigned char key[L8W8JWT_MAX_KEY_SIZE] = { 0x00 }; +#endif + + size_t key_length = params->verification_key_length; + memcpy(key, params->verification_key, key_length); + + key_length += key[key_length - 1] != '\0'; + + is_cert = strstr((const char*)key, "-----BEGIN CERTIFICATE-----") != NULL; + if (is_cert) + { + r = mbedtls_x509_crt_parse(&crt, key, key_length); + if (r != 0) + { + r = L8W8JWT_KEY_PARSE_FAILURE; + goto exit; + } + + pk = crt.pk; + } + + size_t md_length = 0; + mbedtls_md_type_t md_type = MBEDTLS_MD_NONE; + mbedtls_md_info_t* md_info = NULL; + + md_info_from_alg(alg, &md_info, &md_type, &md_length); + + unsigned char hash[64] = { 0x00 }; + + char* signature_segment = strchr(params->jwt, '.'); + + if (signature_segment == NULL) /* No payload. */ + { + r = L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT; + goto exit; + } + + signature_segment = strchr(signature_segment + 1, '.'); + + if (signature_segment == NULL) /* No signature. */ + { + r = L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT; + goto exit; + } + + switch (alg) + { + case L8W8JWT_ALG_ES256: + case L8W8JWT_ALG_ES384: + case L8W8JWT_ALG_ES512: + case L8W8JWT_ALG_RS256: + case L8W8JWT_ALG_RS384: + case L8W8JWT_ALG_RS512: + case L8W8JWT_ALG_PS256: + case L8W8JWT_ALG_PS384: + case L8W8JWT_ALG_PS512: + case L8W8JWT_ALG_ES256K: { + + r = mbedtls_md(md_info, (const unsigned char*)params->jwt, signature_segment - params->jwt, hash); + if (r != L8W8JWT_SUCCESS) + { + r = L8W8JWT_SHA2_FAILURE; + goto exit; + } + break; + } + default: + break; + } + + switch (alg) + { + case L8W8JWT_ALG_HS256: + case L8W8JWT_ALG_HS384: + case L8W8JWT_ALG_HS512: { + + unsigned char signature_cmp[64]; + memset(signature_cmp, '\0', sizeof(signature_cmp)); + + r = mbedtls_md_hmac(md_info, key, key_length - 1, (const unsigned char*)params->jwt, signature_segment - params->jwt, signature_cmp); + if (r != 0) + { + *out_validation_res |= (unsigned)L8W8JWT_SIGNATURE_VERIFICATION_FAILURE; + break; + } + + r = memcmp(signature, signature_cmp, 32 + (16 * alg)); + if (r != 0) + { + *out_validation_res |= (unsigned)L8W8JWT_SIGNATURE_VERIFICATION_FAILURE; + break; + } + + break; + } + case L8W8JWT_ALG_RS256: + case L8W8JWT_ALG_RS384: + case L8W8JWT_ALG_RS512: { + + if (!is_cert) + { + r = mbedtls_pk_parse_public_key(&pk, key, key_length); + if (r != 0) + { + r = L8W8JWT_KEY_PARSE_FAILURE; + goto exit; + } + } + + r = mbedtls_pk_verify(&pk, md_type, hash, md_length, (const unsigned char*)signature, signature_length); + if (r != 0) + { + *out_validation_res |= (unsigned)L8W8JWT_SIGNATURE_VERIFICATION_FAILURE; + break; + } + + break; + } + case L8W8JWT_ALG_PS256: + case L8W8JWT_ALG_PS384: + case L8W8JWT_ALG_PS512: { + + if (!is_cert) + { + r = mbedtls_pk_parse_public_key(&pk, key, key_length); + if (r != 0) + { + r = L8W8JWT_KEY_PARSE_FAILURE; + goto exit; + } + } + + mbedtls_rsa_context* rsa = mbedtls_pk_rsa(pk); + mbedtls_rsa_set_padding(rsa, MBEDTLS_RSA_PKCS_V21, md_type); + + r = mbedtls_rsa_rsassa_pss_verify(rsa, md_type, md_length, hash, signature); + if (r != 0) + { + *out_validation_res |= (unsigned)L8W8JWT_SIGNATURE_VERIFICATION_FAILURE; + break; + } + + break; + } + case L8W8JWT_ALG_ES256: + case L8W8JWT_ALG_ES256K: + case L8W8JWT_ALG_ES384: + case L8W8JWT_ALG_ES512: { + + if (!is_cert) + { + r = mbedtls_pk_parse_public_key(&pk, key, key_length); + if (r != 0) + { + r = L8W8JWT_KEY_PARSE_FAILURE; + goto exit; + } + } + + const size_t half_signature_length = signature_length / 2; + + mbedtls_ecdsa_context ecdsa; + mbedtls_ecdsa_init(&ecdsa); + + mbedtls_mpi sig_r, sig_s; + mbedtls_mpi_init(&sig_r); + mbedtls_mpi_init(&sig_s); + + r = mbedtls_ecdsa_from_keypair(&ecdsa, mbedtls_pk_ec(pk)); + + if (r != 0) + { + r = L8W8JWT_KEY_PARSE_FAILURE; + mbedtls_ecdsa_free(&ecdsa); + mbedtls_mpi_free(&sig_r); + mbedtls_mpi_free(&sig_s); + goto exit; + } + + mbedtls_mpi_read_binary(&sig_r, signature, half_signature_length); + mbedtls_mpi_read_binary(&sig_s, signature + half_signature_length, half_signature_length); + + r = mbedtls_ecdsa_verify(&ecdsa.MBEDTLS_PRIVATE(grp), hash, md_length, &ecdsa.MBEDTLS_PRIVATE(Q), &sig_r, &sig_s); + if (r != 0) + { + *out_validation_res |= (unsigned)L8W8JWT_SIGNATURE_VERIFICATION_FAILURE; + } + + mbedtls_ecdsa_free(&ecdsa); + mbedtls_mpi_free(&sig_r); + mbedtls_mpi_free(&sig_s); + + break; + } + case L8W8JWT_ALG_ED25519: { + +#if L8W8JWT_ENABLE_EDDSA + if (key_length != 64 && !(key_length == 65 && key[64] == 0x00)) + { + r = L8W8JWT_WRONG_KEY_TYPE; + goto exit; + } + + unsigned char public_key[32 + 1] = { 0x00 }; + + if (l8w8jwt_hexstr2bin((const char*)key, key_length, public_key, sizeof(public_key), NULL) != 0) + { + r = L8W8JWT_WRONG_KEY_TYPE; + goto exit; + } + + if (!ed25519_verify(signature, (const unsigned char*)params->jwt, signature_segment - params->jwt, public_key)) + { + *out_validation_res |= (unsigned)L8W8JWT_SIGNATURE_VERIFICATION_FAILURE; + break; + } + + break; +#else + r = L8W8JWT_UNSUPPORTED_ALG; + goto exit; +#endif + } + default: + break; + } + + r = L8W8JWT_SUCCESS; + +exit: + mbedtls_platform_zeroize(key, L8W8JWT_MAX_KEY_SIZE); + +#if L8W8JWT_SMALL_STACK + l8w8jwt_free(key); +#endif + + if (is_cert) + { + mbedtls_x509_crt_free(&crt); + } + else + { + mbedtls_pk_free(&pk); + } + + return r; +} + void l8w8jwt_decoding_params_init(struct l8w8jwt_decoding_params* params) { if (params == NULL) @@ -288,12 +758,12 @@ void l8w8jwt_decoding_params_init(struct l8w8jwt_decoding_params* params) int l8w8jwt_validate_decoding_params(struct l8w8jwt_decoding_params* params) { - if (params == NULL || params->jwt == NULL || params->verification_key == NULL) + if (params == NULL || params->jwt == NULL) { return L8W8JWT_NULL_ARG; } - if (params->jwt_length == 0 || params->verification_key_length == 0 || params->verification_key_length > L8W8JWT_MAX_KEY_SIZE) + if (params->jwt_length == 0) { return L8W8JWT_INVALID_ARG; } @@ -315,8 +785,7 @@ int l8w8jwt_decode(struct l8w8jwt_decoding_params* params, enum l8w8jwt_validati return L8W8JWT_NULL_ARG; } - const int alg = params->alg; - unsigned validation_res = L8W8JWT_VALID; + enum l8w8jwt_validation_result validation_res = L8W8JWT_VALID; int r = l8w8jwt_validate_decoding_params(params); if (r != L8W8JWT_SUCCESS) @@ -329,7 +798,7 @@ int l8w8jwt_decode(struct l8w8jwt_decoding_params* params, enum l8w8jwt_validati return L8W8JWT_NULL_ARG; } - *out_validation_result = (enum l8w8jwt_validation_result)~L8W8JWT_VALID; + *out_validation_result = ~L8W8JWT_VALID; char* header = NULL; size_t header_length = 0; @@ -340,47 +809,6 @@ int l8w8jwt_decode(struct l8w8jwt_decoding_params* params, enum l8w8jwt_validati uint8_t* signature = NULL; size_t signature_length = 0; - char* current = params->jwt; - char* next = strchr(params->jwt, '.'); - - size_t current_length; - time_t ct; - - if (next == NULL) /* No payload. */ - { - return L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT; - } - - int is_cert = 0; // If the validation PEM is a X.509 certificate, this will be set to 1. - - mbedtls_pk_context pk; - mbedtls_pk_init(&pk); - - mbedtls_x509_crt crt; - mbedtls_x509_crt_init(&crt); - - mbedtls_entropy_context entropy; - mbedtls_entropy_init(&entropy); - - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_ctr_drbg_init(&ctr_drbg); - -#if L8W8JWT_SMALL_STACK - unsigned char* key = calloc(sizeof(unsigned char), L8W8JWT_MAX_KEY_SIZE); - if (key == NULL) - { - r = L8W8JWT_OUT_OF_MEM; - goto exit; - } -#else - unsigned char key[L8W8JWT_MAX_KEY_SIZE] = { 0x00 }; -#endif - - size_t key_length = params->verification_key_length; - memcpy(key, params->verification_key, key_length); - - key_length += key[key_length - 1] != '\0'; - chillbuff claims; r = chillbuff_init(&claims, 16, sizeof(struct l8w8jwt_claim), CHILLBUFF_GROW_DUPLICATIVE); if (r != CHILLBUFF_SUCCESS) @@ -389,256 +817,18 @@ int l8w8jwt_decode(struct l8w8jwt_decoding_params* params, enum l8w8jwt_validati goto exit; } - current_length = next - current; - - r = l8w8jwt_base64_decode(true, current, current_length, (uint8_t**)&header, &header_length); + r = l8w8jwt_decode_segments(params, (uint8_t**)&header, &header_length, (uint8_t**)&payload, &payload_length, (uint8_t**)&signature, &signature_length); if (r != L8W8JWT_SUCCESS) { - if (r != L8W8JWT_OUT_OF_MEM) - r = L8W8JWT_BASE64_FAILURE; goto exit; } - current = next + 1; - next = strchr(current, '.'); - - if (next == NULL && alg != -1) /* No signature. */ - { - r = L8W8JWT_DECODE_FAILED_MISSING_SIGNATURE; - goto exit; - } - - current_length = (next != NULL ? next : params->jwt + params->jwt_length) - current; - - r = l8w8jwt_base64_decode(true, current, current_length, (uint8_t**)&payload, &payload_length); + r = l8w8jwt_verify_signature(params, &validation_res, signature, signature_length); if (r != L8W8JWT_SUCCESS) { - if (r != L8W8JWT_OUT_OF_MEM) - r = L8W8JWT_BASE64_FAILURE; goto exit; } - if (next != NULL) - { - current = next + 1; - current_length = (params->jwt + params->jwt_length) - current; - - r = l8w8jwt_base64_decode(true, current, current_length, &signature, &signature_length); - if (r != L8W8JWT_SUCCESS) - { - if (r != L8W8JWT_OUT_OF_MEM) - r = L8W8JWT_BASE64_FAILURE; - goto exit; - } - } - - /* Signature verification. */ - if (signature != NULL && signature_length > 0 && alg != -1) - { - is_cert = strstr((const char*)key, "-----BEGIN CERTIFICATE-----") != NULL; - if (is_cert) - { - r = mbedtls_x509_crt_parse(&crt, key, key_length); - if (r != 0) - { - r = L8W8JWT_KEY_PARSE_FAILURE; - goto exit; - } - - pk = crt.pk; - } - - size_t md_length; - mbedtls_md_type_t md_type; - mbedtls_md_info_t* md_info; - - md_info_from_alg(alg, &md_info, &md_type, &md_length); - - unsigned char hash[64] = { 0x00 }; - - switch (alg) - { - case L8W8JWT_ALG_ES256: - case L8W8JWT_ALG_ES384: - case L8W8JWT_ALG_ES512: - case L8W8JWT_ALG_RS256: - case L8W8JWT_ALG_RS384: - case L8W8JWT_ALG_RS512: - case L8W8JWT_ALG_PS256: - case L8W8JWT_ALG_PS384: - case L8W8JWT_ALG_PS512: - case L8W8JWT_ALG_ES256K: { - - r = mbedtls_md(md_info, (const unsigned char*)params->jwt, (current - 1) - params->jwt, hash); - if (r != L8W8JWT_SUCCESS) - { - r = L8W8JWT_SHA2_FAILURE; - goto exit; - } - break; - } - default: - break; - } - - switch (alg) - { - case L8W8JWT_ALG_HS256: - case L8W8JWT_ALG_HS384: - case L8W8JWT_ALG_HS512: { - - unsigned char signature_cmp[64]; - memset(signature_cmp, '\0', sizeof(signature_cmp)); - - r = mbedtls_md_hmac(md_info, key, key_length - 1, (const unsigned char*)params->jwt, (current - 1) - params->jwt, signature_cmp); - if (r != 0) - { - validation_res |= (unsigned)L8W8JWT_SIGNATURE_VERIFICATION_FAILURE; - break; - } - - r = memcmp(signature, signature_cmp, 32 + (16 * alg)); - if (r != 0) - { - validation_res |= (unsigned)L8W8JWT_SIGNATURE_VERIFICATION_FAILURE; - break; - } - - break; - } - case L8W8JWT_ALG_RS256: - case L8W8JWT_ALG_RS384: - case L8W8JWT_ALG_RS512: { - - if (!is_cert) - { - r = mbedtls_pk_parse_public_key(&pk, key, key_length); - if (r != 0) - { - r = L8W8JWT_KEY_PARSE_FAILURE; - goto exit; - } - } - - r = mbedtls_pk_verify(&pk, md_type, hash, md_length, (const unsigned char*)signature, signature_length); - if (r != 0) - { - validation_res |= (unsigned)L8W8JWT_SIGNATURE_VERIFICATION_FAILURE; - break; - } - - break; - } - case L8W8JWT_ALG_PS256: - case L8W8JWT_ALG_PS384: - case L8W8JWT_ALG_PS512: { - - if (!is_cert) - { - r = mbedtls_pk_parse_public_key(&pk, key, key_length); - if (r != 0) - { - r = L8W8JWT_KEY_PARSE_FAILURE; - goto exit; - } - } - - mbedtls_rsa_context* rsa = mbedtls_pk_rsa(pk); - mbedtls_rsa_set_padding(rsa, MBEDTLS_RSA_PKCS_V21, md_type); - - r = mbedtls_rsa_rsassa_pss_verify(rsa, md_type, md_length, hash, signature); - if (r != 0) - { - validation_res |= (unsigned)L8W8JWT_SIGNATURE_VERIFICATION_FAILURE; - break; - } - - break; - } - case L8W8JWT_ALG_ES256: - case L8W8JWT_ALG_ES256K: - case L8W8JWT_ALG_ES384: - case L8W8JWT_ALG_ES512: { - - if (!is_cert) - { - r = mbedtls_pk_parse_public_key(&pk, key, key_length); - if (r != 0) - { - r = L8W8JWT_KEY_PARSE_FAILURE; - goto exit; - } - } - - const size_t half_signature_length = signature_length / 2; - - mbedtls_ecdsa_context ecdsa; - mbedtls_ecdsa_init(&ecdsa); - - mbedtls_mpi sig_r, sig_s; - mbedtls_mpi_init(&sig_r); - mbedtls_mpi_init(&sig_s); - - r = mbedtls_ecdsa_from_keypair(&ecdsa, mbedtls_pk_ec(pk)); - - if (r != 0) - { - r = L8W8JWT_KEY_PARSE_FAILURE; - mbedtls_ecdsa_free(&ecdsa); - mbedtls_mpi_free(&sig_r); - mbedtls_mpi_free(&sig_s); - goto exit; - } - - mbedtls_mpi_read_binary(&sig_r, signature, half_signature_length); - mbedtls_mpi_read_binary(&sig_s, signature + half_signature_length, half_signature_length); - - r = mbedtls_ecdsa_verify(&ecdsa.MBEDTLS_PRIVATE(grp), hash, md_length, &ecdsa.MBEDTLS_PRIVATE(Q), &sig_r, &sig_s); - if (r != 0) - { - validation_res |= (unsigned)L8W8JWT_SIGNATURE_VERIFICATION_FAILURE; - } - - mbedtls_ecdsa_free(&ecdsa); - mbedtls_mpi_free(&sig_r); - mbedtls_mpi_free(&sig_s); - - break; - } - case L8W8JWT_ALG_ED25519: { - -#if L8W8JWT_ENABLE_EDDSA - if (key_length != 64 && !(key_length == 65 && key[64] == 0x00)) - { - r = L8W8JWT_WRONG_KEY_TYPE; - goto exit; - } - - unsigned char public_key[32 + 1] = { 0x00 }; - - if (l8w8jwt_hexstr2bin((const char*)key, key_length, public_key, sizeof(public_key), NULL) != 0) - { - r = L8W8JWT_WRONG_KEY_TYPE; - goto exit; - } - - if (!ed25519_verify(signature, (const unsigned char*)params->jwt, (current - 1) - params->jwt, public_key)) - { - validation_res |= (unsigned)L8W8JWT_SIGNATURE_VERIFICATION_FAILURE; - break; - } - - break; -#else - r = L8W8JWT_UNSUPPORTED_ALG; - goto exit; -#endif - } - default: - break; - } - } - r = l8w8jwt_parse_claims(&claims, header, header_length); if (r != L8W8JWT_SUCCESS) { @@ -653,82 +843,10 @@ int l8w8jwt_decode(struct l8w8jwt_decoding_params* params, enum l8w8jwt_validati goto exit; } - if (params->validate_sub != NULL) - { - struct l8w8jwt_claim* c = l8w8jwt_get_claim((struct l8w8jwt_claim*)claims.array, claims.length, "sub", 3); - if (c == NULL || strncmp(c->value, params->validate_sub, params->validate_sub_length ? params->validate_sub_length : strlen(params->validate_sub)) != 0) - { - validation_res |= (unsigned)L8W8JWT_SUB_FAILURE; - } - } - - if (params->validate_aud != NULL) - { - struct l8w8jwt_claim* c = l8w8jwt_get_claim((struct l8w8jwt_claim*)claims.array, claims.length, "aud", 3); - if (c == NULL || strncmp(c->value, params->validate_aud, params->validate_aud_length ? params->validate_aud_length : strlen(params->validate_aud)) != 0) - { - validation_res |= (unsigned)L8W8JWT_AUD_FAILURE; - } - } - - if (params->validate_iss != NULL) - { - struct l8w8jwt_claim* c = l8w8jwt_get_claim((struct l8w8jwt_claim*)claims.array, claims.length, "iss", 3); - if (c == NULL || strncmp(c->value, params->validate_iss, params->validate_iss_length ? params->validate_iss_length : strlen(params->validate_iss)) != 0) - { - validation_res |= (unsigned)L8W8JWT_ISS_FAILURE; - } - } - - if (params->validate_jti != NULL) - { - struct l8w8jwt_claim* c = l8w8jwt_get_claim((struct l8w8jwt_claim*)claims.array, claims.length, "jti", 3); - if (c == NULL || strncmp(c->value, params->validate_jti, params->validate_jti_length ? params->validate_jti_length : strlen(params->validate_jti)) != 0) - { - validation_res |= (unsigned)L8W8JWT_JTI_FAILURE; - } - } - - ct = time(NULL); - - if (params->validate_exp) - { - struct l8w8jwt_claim* c = l8w8jwt_get_claim((struct l8w8jwt_claim*)claims.array, claims.length, "exp", 3); - if (c == NULL || ct - params->exp_tolerance_seconds > strtoll(c->value, NULL, 10)) - { - validation_res |= (unsigned)L8W8JWT_EXP_FAILURE; - } - } - - if (params->validate_nbf) - { - struct l8w8jwt_claim* c = l8w8jwt_get_claim((struct l8w8jwt_claim*)claims.array, claims.length, "nbf", 3); - if (c == NULL || ct + params->nbf_tolerance_seconds < strtoll(c->value, NULL, 10)) - { - validation_res |= (unsigned)L8W8JWT_NBF_FAILURE; - } - } - - if (params->validate_iat) - { - struct l8w8jwt_claim* c = l8w8jwt_get_claim((struct l8w8jwt_claim*)claims.array, claims.length, "iat", 3); - if (c == NULL || ct + params->iat_tolerance_seconds < strtoll(c->value, NULL, 10)) - { - validation_res |= (unsigned)L8W8JWT_IAT_FAILURE; - } - } - - if (params->validate_typ) - { - struct l8w8jwt_claim* c = l8w8jwt_get_claim((struct l8w8jwt_claim*)claims.array, claims.length, "typ", 3); - if (c == NULL || l8w8jwt_strncmpic(c->value, params->validate_typ, params->validate_typ_length) != 0) - { - validation_res |= (unsigned)L8W8JWT_TYP_FAILURE; - } - } + l8w8jwt_validate_claims(params, &claims, &validation_res); r = L8W8JWT_SUCCESS; - *out_validation_result = (enum l8w8jwt_validation_result)validation_res; + *out_validation_result = validation_res; if (out_claims != NULL && out_claims_length != NULL) { @@ -746,21 +864,191 @@ exit: l8w8jwt_free_claims((struct l8w8jwt_claim*)claims.array, claims.length); } - mbedtls_platform_zeroize(key, L8W8JWT_MAX_KEY_SIZE); -#if L8W8JWT_SMALL_STACK - l8w8jwt_free(key); -#endif + return r; +} - mbedtls_ctr_drbg_free(&ctr_drbg); - mbedtls_entropy_free(&entropy); - - if (is_cert) +int l8w8jwt_decode_raw(struct l8w8jwt_decoding_params* params, enum l8w8jwt_validation_result* out_validation_result, char** out_header, size_t* out_header_length, char** out_payload, size_t* out_payload_length, uint8_t** out_signature, size_t* out_signature_length) +{ + if + ( + params == NULL + || + out_validation_result == NULL + || + (out_header != NULL && out_header_length == NULL) + || + (out_payload != NULL && out_payload_length == NULL) + || + (out_signature != NULL && out_signature_length == NULL) + ) { - mbedtls_x509_crt_free(&crt); + return L8W8JWT_NULL_ARG; + } + + enum l8w8jwt_validation_result validation_res = L8W8JWT_VALID; + + int r = l8w8jwt_validate_decoding_params(params); + if (r != L8W8JWT_SUCCESS) + { + return r; + } + + *out_validation_result = ~L8W8JWT_VALID; + + char* header = NULL; + size_t header_length = 0; + + char* payload = NULL; + size_t payload_length = 0; + + uint8_t* signature = NULL; + size_t signature_length = 0; + + chillbuff claims; + r = chillbuff_init(&claims, 16, sizeof(struct l8w8jwt_claim), CHILLBUFF_GROW_DUPLICATIVE); + if (r != CHILLBUFF_SUCCESS) + { + r = L8W8JWT_OUT_OF_MEM; + goto exit; + } + + r = l8w8jwt_decode_segments(params, (uint8_t**)&header, &header_length, (uint8_t**)&payload, &payload_length, (uint8_t**)&signature, &signature_length); + if (r != L8W8JWT_SUCCESS) + { + goto exit; + } + + r = l8w8jwt_verify_signature(params, &validation_res, signature, signature_length); + if (r != L8W8JWT_SUCCESS) + { + goto exit; + } + + r = l8w8jwt_parse_claims(&claims, header, header_length); + if (r != L8W8JWT_SUCCESS) + { + r = L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT; + goto exit; + } + + r = l8w8jwt_parse_claims(&claims, payload, payload_length); + if (r != L8W8JWT_SUCCESS) + { + r = L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT; + goto exit; + } + + l8w8jwt_validate_claims(params, &claims, &validation_res); + + r = L8W8JWT_SUCCESS; + *out_validation_result = validation_res; + +exit: + if (out_header != NULL) + { + *out_header = header; + *out_header_length = header_length; } else { - mbedtls_pk_free(&pk); + l8w8jwt_free(header); + } + + if (out_payload != NULL) + { + *out_payload = payload; + *out_payload_length = payload_length; + } + else + { + l8w8jwt_free(payload); + } + + if (out_signature != NULL) + { + *out_signature = signature; + *out_signature_length = signature_length; + } + else + { + l8w8jwt_free(signature); + } + + l8w8jwt_free_claims((struct l8w8jwt_claim*)claims.array, claims.length); + + return r; +} + +int l8w8jwt_decode_raw_no_validation(struct l8w8jwt_decoding_params* params, char** out_header, size_t* out_header_length, char** out_payload, size_t* out_payload_length, uint8_t** out_signature, size_t* out_signature_length) +{ + if + ( + params == NULL + || + (out_header == NULL && out_payload == NULL && out_signature == NULL) + || + (out_header != NULL && out_header_length == NULL) + || + (out_payload != NULL && out_payload_length == NULL) + || + (out_signature != NULL && out_signature_length == NULL) + ) + { + return L8W8JWT_NULL_ARG; + } + + int r = l8w8jwt_validate_decoding_params(params); + if (r != L8W8JWT_SUCCESS) + { + return r; + } + + char* header = NULL; + size_t header_length = 0; + + char* payload = NULL; + size_t payload_length = 0; + + uint8_t* signature = NULL; + size_t signature_length = 0; + + r = l8w8jwt_decode_segments(params, (uint8_t**)&header, &header_length, (uint8_t**)&payload, &payload_length, (uint8_t**)&signature, &signature_length); + if (r != L8W8JWT_SUCCESS) + { + goto exit; + } + + r = L8W8JWT_SUCCESS; + +exit: + if (out_header != NULL) + { + *out_header = header; + *out_header_length = header_length; + } + else + { + l8w8jwt_free(header); + } + + if (out_payload != NULL) + { + *out_payload = payload; + *out_payload_length = payload_length; + } + else + { + l8w8jwt_free(payload); + } + + if (out_signature != NULL) + { + *out_signature = signature; + *out_signature_length = signature_length; + } + else + { + l8w8jwt_free(signature); } return r; diff --git a/src/thirdparty/jwt/encode.c b/src/thirdparty/jwt/encode.c index 8abc0962..b870c32e 100644 --- a/src/thirdparty/jwt/encode.c +++ b/src/thirdparty/jwt/encode.c @@ -24,7 +24,6 @@ extern "C" { #include "include/chillbuff.h" #include - #include #include #include @@ -144,7 +143,7 @@ static int write_header_and_payload(chillbuff* stringbuilder, struct l8w8jwt_enc char* segment; size_t segment_length; - r = l8w8jwt_base64_encode(1, (const uint8_t*)buff.array, buff.length, &segment, &segment_length); + r = l8w8jwt_base64_encode(1, buff.array, buff.length, &segment, &segment_length); if (r != L8W8JWT_SUCCESS) { chillbuff_free(&buff); @@ -177,13 +176,13 @@ static int write_header_and_payload(chillbuff* stringbuilder, struct l8w8jwt_enc struct l8w8jwt_claim claims[] = { // Setting l8w8jwt_claim::value_length to 0 makes the encoder use strlen, which in this case is fine. - { *(iatnbfexp + 00) ? (char*)"iat" : NULL, 3, iatnbfexp + 00, 0, L8W8JWT_CLAIM_TYPE_INTEGER }, - { *(iatnbfexp + 21) ? (char*)"nbf" : NULL, 3, iatnbfexp + 21, 0, L8W8JWT_CLAIM_TYPE_INTEGER }, - { *(iatnbfexp + 42) ? (char*)"exp" : NULL, 3, iatnbfexp + 42, 0, L8W8JWT_CLAIM_TYPE_INTEGER }, - { params->sub ? (char*)"sub" : NULL, 3, params->sub, params->sub_length, L8W8JWT_CLAIM_TYPE_STRING }, - { params->iss ? (char*)"iss" : NULL, 3, params->iss, params->iss_length, L8W8JWT_CLAIM_TYPE_STRING }, - { params->aud ? (char*)"aud" : NULL, 3, params->aud, params->aud_length, L8W8JWT_CLAIM_TYPE_STRING }, - { params->jti ? (char*)"jti" : NULL, 3, params->jti, params->jti_length, L8W8JWT_CLAIM_TYPE_STRING }, + { .key = *(iatnbfexp + 00) ? "iat" : NULL, .key_length = 3, .value = iatnbfexp + 00, .value_length = 0, .type = L8W8JWT_CLAIM_TYPE_INTEGER }, + { .key = *(iatnbfexp + 21) ? "nbf" : NULL, .key_length = 3, .value = iatnbfexp + 21, .value_length = 0, .type = L8W8JWT_CLAIM_TYPE_INTEGER }, + { .key = *(iatnbfexp + 42) ? "exp" : NULL, .key_length = 3, .value = iatnbfexp + 42, .value_length = 0, .type = L8W8JWT_CLAIM_TYPE_INTEGER }, + { .key = params->sub ? "sub" : NULL, .key_length = 3, .value = params->sub, .value_length = params->sub_length, .type = L8W8JWT_CLAIM_TYPE_STRING }, + { .key = params->iss ? "iss" : NULL, .key_length = 3, .value = params->iss, .value_length = params->iss_length, .type = L8W8JWT_CLAIM_TYPE_STRING }, + { .key = params->aud ? "aud" : NULL, .key_length = 3, .value = params->aud, .value_length = params->aud_length, .type = L8W8JWT_CLAIM_TYPE_STRING }, + { .key = params->jti ? "jti" : NULL, .key_length = 3, .value = params->jti, .value_length = params->jti_length, .type = L8W8JWT_CLAIM_TYPE_STRING }, }; chillbuff_push_back(&buff, "{", 1); @@ -200,7 +199,7 @@ static int write_header_and_payload(chillbuff* stringbuilder, struct l8w8jwt_enc chillbuff_push_back(&buff, "}", 1); - r = l8w8jwt_base64_encode(1, (const uint8_t*)buff.array, buff.length, &segment, &segment_length); + r = l8w8jwt_base64_encode(1, buff.array, buff.length, &segment, &segment_length); if (r != L8W8JWT_SUCCESS) { chillbuff_free(&buff); @@ -222,8 +221,6 @@ static int write_signature(chillbuff* stringbuilder, struct l8w8jwt_encoding_par int r; const int alg = params->alg; - unsigned char hash[64] = { 0x00 }; - char* signature = NULL; size_t signature_length = 0, signature_bytes_length = 0, key_length = 0; @@ -231,19 +228,13 @@ static int write_signature(chillbuff* stringbuilder, struct l8w8jwt_encoding_par mbedtls_entropy_context entropy; mbedtls_ctr_drbg_context ctr_drbg; - size_t md_length; - mbedtls_md_type_t md_type; - mbedtls_md_info_t* md_info; - - size_t half_signature_bytes_length; - mbedtls_pk_init(&pk); mbedtls_entropy_init(&entropy); mbedtls_ctr_drbg_init(&ctr_drbg); #if L8W8JWT_SMALL_STACK - unsigned char* signature_bytes = calloc(sizeof(unsigned char), 4096); - unsigned char* key = calloc(sizeof(unsigned char), L8W8JWT_MAX_KEY_SIZE); + unsigned char* signature_bytes = l8w8jwt_calloc(sizeof(unsigned char), 4096); + unsigned char* key = l8w8jwt_calloc(sizeof(unsigned char), L8W8JWT_MAX_KEY_SIZE); if (signature_bytes == NULL || key == NULL) { @@ -272,8 +263,14 @@ static int write_signature(chillbuff* stringbuilder, struct l8w8jwt_encoding_par goto exit; } + size_t md_length = 0; + mbedtls_md_type_t md_type = MBEDTLS_MD_NONE; + mbedtls_md_info_t* md_info = NULL; + md_info_from_alg(alg, &md_info, &md_type, &md_length); + unsigned char hash[64] = { 0x00 }; + switch (alg) { case L8W8JWT_ALG_HS256: @@ -495,7 +492,7 @@ static int write_signature(chillbuff* stringbuilder, struct l8w8jwt_encoding_par goto ecdsa_exit; } - half_signature_bytes_length = signature_bytes_length / 2; + const size_t half_signature_bytes_length = signature_bytes_length / 2; r = mbedtls_mpi_write_binary(&sig_r, signature_bytes, half_signature_bytes_length); if (r != 0) @@ -543,7 +540,7 @@ static int write_signature(chillbuff* stringbuilder, struct l8w8jwt_encoding_par ed25519_sign_ref10(signature_bytes, (const unsigned char*)stringbuilder->array, stringbuilder->length, private_key_ref10); signature_bytes_length = 64; - l8w8jwt_zero(private_key_ref10, sizeof(private_key_ref10)); + mbedtls_platform_zeroize(private_key_ref10, sizeof(private_key_ref10)); break; #else r = L8W8JWT_UNSUPPORTED_ALG; @@ -563,7 +560,7 @@ static int write_signature(chillbuff* stringbuilder, struct l8w8jwt_encoding_par } /* - * If this succeeds, it mallocs "signature" and assigns the resulting string length to "signature_length". + * If this succeeds, it mallocs "signature" (using l8w8jwt_malloc) and assigns the resulting string length to "signature_length". */ r = l8w8jwt_base64_encode(1, (uint8_t*)signature_bytes, signature_bytes_length, &signature, &signature_length); if (r != L8W8JWT_SUCCESS) @@ -576,7 +573,7 @@ static int write_signature(chillbuff* stringbuilder, struct l8w8jwt_encoding_par chillbuff_push_back(stringbuilder, signature, signature_length); exit: - l8w8jwt_zero(key, L8W8JWT_MAX_KEY_SIZE); + mbedtls_platform_zeroize(key, L8W8JWT_MAX_KEY_SIZE); mbedtls_ctr_drbg_free(&ctr_drbg); mbedtls_entropy_free(&entropy); mbedtls_pk_free(&pk); @@ -592,7 +589,7 @@ exit: /* Step 3: finalize the token by writing it into the "out" string defined in the l8w8jwt_encoding_params argument. */ static int write_token(chillbuff* stringbuilder, struct l8w8jwt_encoding_params* params) { - *(params->out) = (char*)malloc(stringbuilder->length + 1); + *(params->out) = l8w8jwt_malloc(stringbuilder->length + 1); if (*(params->out) == NULL) { return L8W8JWT_OUT_OF_MEM; diff --git a/src/thirdparty/jwt/include/decode.h b/src/thirdparty/jwt/include/decode.h index dd9ac563..b7ca0478 100644 --- a/src/thirdparty/jwt/include/decode.h +++ b/src/thirdparty/jwt/include/decode.h @@ -31,7 +31,7 @@ extern "C" { #include "claim.h" #include "version.h" #include "retcodes.h" -#include +#include "timehelper.h" #include #include #include @@ -95,7 +95,7 @@ enum l8w8jwt_validation_result { L8W8JWT_TYP_FAILURE = (unsigned)1 << (unsigned)8 }; -static void l8w8jwt_get_validation_result_desc(enum l8w8jwt_validation_result res, char* out_buffer, size_t buffer_size) +static void l8w8jwt_get_validation_result_desc(enum l8w8jwt_validation_result res, char* out_buffer, const size_t buffer_size) { #define JWT_OUTPUT_MSG(msg) snprintf(out_buffer, buffer_size, "%s", msg) #define JWT_FLAG_STATUS(flag, msg) if(res & flag) { JWT_OUTPUT_MSG(msg); return; } @@ -109,7 +109,7 @@ static void l8w8jwt_get_validation_result_desc(enum l8w8jwt_validation_result re JWT_FLAG_STATUS(L8W8JWT_ISS_FAILURE, "Issuer claim is invalid"); JWT_FLAG_STATUS(L8W8JWT_SUB_FAILURE, "Subject claim is invalid"); JWT_FLAG_STATUS(L8W8JWT_AUD_FAILURE, "Audience claim is invalid"); - JWT_FLAG_STATUS(L8W8JWT_JTI_FAILURE, "JWT ID claim is invalid"); + JWT_FLAG_STATUS(L8W8JWT_JTI_FAILURE, "JTI claim is invalid"); JWT_FLAG_STATUS(L8W8JWT_EXP_FAILURE, "Token has expired"); JWT_FLAG_STATUS(L8W8JWT_NBF_FAILURE, "Token is not yet valid"); JWT_FLAG_STATUS(L8W8JWT_IAT_FAILURE, "Token has not been issued yet"); @@ -271,8 +271,11 @@ L8W8JWT_API int l8w8jwt_validate_decoding_params(struct l8w8jwt_decoding_params* * Decode (and validate) a JWT using specific parameters.

* The resulting {@link #l8w8jwt_validation_result} written into the passed "out_validation_result" pointer * contains validation failure flags (see the {@link #l8w8jwt_validation_result} enum docs for more details).

+ * * This only happens if decoding also succeeded: if the token is malformed, nothing will be written into "out_validation_result".

+ * * If validation succeeds, the {@link #l8w8jwt_validation_result} receives the value 0 (enum value L8W8JWT_VALID).

+ * * The same applies to the "out_claims" argument: it is only allocated and written to if it (obviously) isn't NULL and if the decoding was also successful! * * @param params The parameters to use for decoding and validating the token. @@ -293,6 +296,63 @@ L8W8JWT_API int l8w8jwt_validate_decoding_params(struct l8w8jwt_decoding_params* */ L8W8JWT_API int l8w8jwt_decode(struct l8w8jwt_decoding_params* params, enum l8w8jwt_validation_result* out_validation_result, struct l8w8jwt_claim** out_claims, size_t* out_claims_length); +/** + * Decode (and validate) a JWT using specific parameters, + * but instead of writing the collection of claims contained in the payload into an array of l8w8jwt_validation_result + * like in the standard {@link l8w8jwt_decode} function, + * the raw JWT payload's JSON string is written into the {@link #out_payload_json} string, such that it can be parsed externally.

+ * + * The resulting {@link #l8w8jwt_validation_result} written into the passed "out_validation_result" pointer + * contains validation failure flags (see the {@link #l8w8jwt_validation_result} enum docs for more details).

+ * + * This only happens if decoding also succeeded: if the token is malformed, nothing will be written into "out_validation_result".

+ * + * If validation succeeds, the {@link #l8w8jwt_validation_result} receives the value 0 (enum value L8W8JWT_VALID).

+ * + * The same applies to the "out_payload_json" argument: + * it is only allocated and written to if it (obviously) isn't NULL and if the decoding procedure was also successful! + * + * @param params The parameters to use for decoding and validating the token. + * + * @param out_validation_result Where to write the validation result flags into (0 means success). In case of a decoding failure this is set to -1 (or ~L8W8JWT_VALID)! + * + * @param out_header Where to write the decoded JWT header JSON string into. + * + * @param out_header_length Where to write the length of {@link out_header} into. + * + * @param out_payload Where to write the decoded JWT's payload JSON string into, so that it can be parsed externally instead of by l8w8jwt directly. + * + * @param out_payload_length Where to write the length of {@link out_payload} into. + * + * @param out_signature Where to write the JWT's signature into. + * + * @param out_signature_length Where to write the length of {@link out_signature} into. + * + * @return Return code as defined in retcodes.h (this is NOT the validation result that's written into the {@link out_validation_result} argument; the returned int describes whether the actual parsing/decoding part failed). + */ +L8W8JWT_API int l8w8jwt_decode_raw(struct l8w8jwt_decoding_params* params, enum l8w8jwt_validation_result* out_validation_result, char** out_header, size_t* out_header_length, char** out_payload, size_t* out_payload_length, uint8_t** out_signature, size_t* out_signature_length); + +/** + * Decodes a JWT without validating anything: neither claims nor signature. Just raw decoding, no validation! + * + * @param params The parameters to use for decoding and validating the token. + * + * @param out_header Where to write the decoded JWT header JSON string into. + * + * @param out_header_length Where to write the length of {@link out_header} into. + * + * @param out_payload Where to write the decoded JWT's payload JSON string into, so that it can be parsed externally instead of by l8w8jwt directly. + * + * @param out_payload_length Where to write the length of {@link out_payload} into. + * + * @param out_signature Where to write the JWT's signature into. + * + * @param out_signature_length Where to write the length of {@link out_signature} into. + * + * @return Return code as defined in retcodes.h + */ +L8W8JWT_API int l8w8jwt_decode_raw_no_validation(struct l8w8jwt_decoding_params* params, char** out_header, size_t* out_header_length, char** out_payload, size_t* out_payload_length, uint8_t** out_signature, size_t* out_signature_length); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/thirdparty/jwt/include/encode.h b/src/thirdparty/jwt/include/encode.h index a1b8e1c9..6a90c922 100644 --- a/src/thirdparty/jwt/include/encode.h +++ b/src/thirdparty/jwt/include/encode.h @@ -31,7 +31,7 @@ extern "C" { #include "claim.h" #include "version.h" #include "retcodes.h" -#include +#include "timehelper.h" #include #ifndef L8W8JWT_MAX_KEY_SIZE @@ -100,7 +100,7 @@ L8W8JWT_API struct l8w8jwt_encoding_params * otherwise it will be set to whatever random value was in the memory where this variable resides. * @see https://tools.ietf.org/html/rfc7519#section-4.1.4 */ - time_t exp; + l8w8jwt_time_t exp; /** * "Not before" time claim; specifies when this token should start being valid (in seconds since Unix epoch).

@@ -108,7 +108,7 @@ L8W8JWT_API struct l8w8jwt_encoding_params * otherwise it will be set to whatever random value was in the memory where this variable resides. * @see https://tools.ietf.org/html/rfc7519#section-4.1.5 */ - time_t nbf; + l8w8jwt_time_t nbf; /** * "Issued at" timestamp claim; specifies when this token was emitted (in seconds since Unix epoch).

@@ -116,7 +116,7 @@ L8W8JWT_API struct l8w8jwt_encoding_params * otherwise it will be set to whatever random value was in the memory where this variable resides. * @see https://tools.ietf.org/html/rfc7519#section-4.1.6 */ - time_t iat; + l8w8jwt_time_t iat; /** * [OPTIONAL] Array of additional claims to include in the JWT's header like for example "kid" or "cty"; pass NULL if you don't wish to add any!

diff --git a/src/thirdparty/jwt/include/timehelper.h b/src/thirdparty/jwt/include/timehelper.h new file mode 100644 index 00000000..24dce416 --- /dev/null +++ b/src/thirdparty/jwt/include/timehelper.h @@ -0,0 +1,17 @@ +#include "version.h" +#include + +/** + * For now use the standard type for l8w8jwt_time_t. + * Support for adding alternate types could be added in the future, if needed. +*/ +typedef time_t l8w8jwt_time_t; + +/** + * Define alternate implementation of system `time` API +*/ +#if L8W8JWT_PLATFORM_TIME_ALT +extern l8w8jwt_time_t (*l8w8jwt_time)(l8w8jwt_time_t *time); +#else +#define l8w8jwt_time time +#endif diff --git a/src/thirdparty/jwt/include/util.h b/src/thirdparty/jwt/include/util.h index ef986795..1b9c487e 100644 --- a/src/thirdparty/jwt/include/util.h +++ b/src/thirdparty/jwt/include/util.h @@ -27,6 +27,7 @@ extern "C" { #endif +#include #include #include "version.h" @@ -51,6 +52,48 @@ L8W8JWT_API int l8w8jwt_hexstr2bin(const char* hexstr, const size_t hexstr_lengt */ L8W8JWT_API int l8w8jwt_strncmpic(const char* str1, const char* str2, size_t n); +#ifndef L8W8JWT_PLATFORM_MALLOC_ALT +/** + * Set this pre-processor definition to \c 1 if you need to + * provide custom implementation of malloc + */ +#define L8W8JWT_PLATFORM_MALLOC_ALT 0 +#endif + +#ifndef L8W8JWT_PLATFORM_CALLOC_ALT +/** + * Set this pre-processor definition to \c 1 if you need to + * provide custom implementation of calloc + */ +#define L8W8JWT_PLATFORM_CALLOC_ALT 0 +#endif + +#ifndef L8W8JWT_PLATFORM_REALLOC_ALT +/** + * Set this pre-processor definition to \c 1 if you need to + * provide custom implementation of realloc + */ +#define L8W8JWT_PLATFORM_REALLOC_ALT 0 +#endif + +#if L8W8JWT_PLATFORM_MALLOC_ALT +extern void *(*l8w8jwt_malloc) (size_t size); +#else +#define l8w8jwt_malloc malloc +#endif + +#if L8W8JWT_PLATFORM_CALLOC_ALT +extern void *(*l8w8jwt_calloc) (size_t nmemb, size_t size); +#else +#define l8w8jwt_calloc calloc +#endif + +#if L8W8JWT_PLATFORM_REALLOC_ALT +extern void *(*l8w8jwt_realloc) (void *ptr, size_t size); +#else +#define l8w8jwt_realloc realloc +#endif + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/thirdparty/jwt/include/version.h b/src/thirdparty/jwt/include/version.h index 9fce28a0..79fdb559 100644 --- a/src/thirdparty/jwt/include/version.h +++ b/src/thirdparty/jwt/include/version.h @@ -30,12 +30,12 @@ extern "C" { /** * Current l8w8jwt version number. */ -#define L8W8JWT_VERSION 220 +#define L8W8JWT_VERSION 240 /** * Current l8w8jwt version number (as a human-readable string). */ -#define L8W8JWT_VERSION_STR "2.2.0" +#define L8W8JWT_VERSION_STR "2.4.0" #if defined(_WIN32) && defined(L8W8JWT_DLL) #ifdef L8W8JWT_BUILD_DLL @@ -55,19 +55,20 @@ extern "C" { #define L8W8JWT_SMALL_STACK 0 #endif +#ifndef L8W8JWT_PLATFORM_TIME_ALT +/** + * Set this pre-processor definition to \c 1 if you need to + * provide custom implementation of system time API. + */ +#define L8W8JWT_PLATFORM_TIME_ALT 0 +#endif + /** * Free memory that was allocated by L8W8JWT. * @param mem The memory to free. */ L8W8JWT_API void l8w8jwt_free(void* mem); -/** - * Zero memory securely. - * @param mem The memory to zero. - * @param len The length to zero. - */ -L8W8JWT_API void l8w8jwt_zero(void* buf, size_t len); - /** * Gets the l8w8jwt version number as an integer. * @return Version number (e.g. "2.1.4" => 214) diff --git a/src/thirdparty/jwt/util.c b/src/thirdparty/jwt/util.c index 686659e9..3e022021 100644 --- a/src/thirdparty/jwt/util.c +++ b/src/thirdparty/jwt/util.c @@ -42,7 +42,7 @@ int l8w8jwt_hexstr2bin(const char* hexstr, const size_t hexstr_length, unsigned return 3; } - for (size_t i = 0, ii = 0; ii < final_length; i += 2, ii++) + for (size_t i = 0, ii = 0; ii < final_length; i += 2, ++ii) { output[ii] = (hexstr[i] % 32 + 9) % 25 * 16 + (hexstr[i + 1] % 32 + 9) % 25; } diff --git a/src/thirdparty/jwt/version.c b/src/thirdparty/jwt/version.c index 62604c3a..e8583728 100644 --- a/src/thirdparty/jwt/version.c +++ b/src/thirdparty/jwt/version.c @@ -18,10 +18,6 @@ #include #include -#if defined(_WIN32) -#include -#endif - #ifdef __cplusplus extern "C" { #endif @@ -31,31 +27,6 @@ void l8w8jwt_free(void* mem) free(mem); } -void l8w8jwt_zero(void* buf, size_t len) -{ - if (len > 0) - { -#if defined(MBEDTLS_PLATFORM_HAS_EXPLICIT_BZERO) - explicit_bzero(buf, len); -#if defined(HAVE_MEMORY_SANITIZER) - /* You'd think that Msan would recognize explicit_bzero() as - * equivalent to bzero(), but it actually doesn't on several - * platforms, including Linux (Ubuntu 20.04). - * https://github.com/google/sanitizers/issues/1507 - * https://github.com/openssh/openssh-portable/commit/74433a19bb6f4cef607680fa4d1d7d81ca3826aa - */ - __msan_unpoison(buf, len); -#endif -#elif defined(__STDC_LIB_EXT1__) - memset_s(buf, len, 0, len); -#elif defined(_WIN32) - RtlSecureZeroMemory(buf, len); -#else - memset_func(buf, 0, len); -#endif - } -} - int l8w8jwt_get_version_number() { return (int)L8W8JWT_VERSION;