WIP: add JWT library

Currently does not compile, pending integration into SDK.
This commit is contained in:
Kawe Mazidjatari 2024-04-05 16:05:56 +02:00
parent 3b81e9e589
commit cc3b91a3b6
17 changed files with 3496 additions and 0 deletions

View File

@ -29,6 +29,7 @@ add_subdirectory( thirdparty/curl )
add_subdirectory( thirdparty/sdl )
add_subdirectory( thirdparty/imgui )
add_subdirectory( thirdparty/spdlog )
add_subdirectory( thirdparty/jwt )
add_subdirectory( thirdparty/lzham )
add_subdirectory( thirdparty/fastlz )

27
r5dev/thirdparty/jwt/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,27 @@
cmake_minimum_required( VERSION 3.16 )
add_module( "lib" "libjwt" "" ${FOLDER_CONTEXT} TRUE TRUE )
start_sources()
add_sources( SOURCE_GROUP "Source"
"base64.c"
"claim.c"
"decode.c"
"encode.c"
"util.c"
"version.c"
)
add_sources( SOURCE_GROUP "Include"
"include/algs.h"
"include/base64.h"
"include/claim.h"
"include/decode.h"
"include/encode.h"
"include/retcodes.h"
"include/util.h"
"include/version.h"
)
end_sources()
thirdparty_suppress_warnings()

315
r5dev/thirdparty/jwt/base64.c vendored Normal file
View File

@ -0,0 +1,315 @@
/*
* Base64 encoding/decoding (RFC1341)
* Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
/*
The above mentioned README can be found under: http://web.mit.edu/freebsd/head/contrib/wpa/
Here's a full paste of it (18. January 2020), in case the URL goes numb:
wpa_supplicant and hostapd
--------------------------
Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
These programs are licensed under the BSD license (the one with
advertisement clause removed).
If you are submitting changes to the project, please see CONTRIBUTIONS
file for more instructions.
This package may include either wpa_supplicant, hostapd, or both. See
README file respective subdirectories (wpa_supplicant/README or
hostapd/README) for more details.
Source code files were moved around in v0.6.x releases and compared to
earlier releases, the programs are now built by first going to a
subdirectory (wpa_supplicant or hostapd) and creating build
configuration (.config) and running 'make' there (for Linux/BSD/cygwin
builds).
License
-------
This software may be distributed, used, and modified under the terms of
BSD license:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name(s) of the above-listed copyright holder(s) nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
http://web.mit.edu/freebsd/head/contrib/wpa/COPYING (at the time of writing, 18. January 2020)
wpa_supplicant and hostapd
--------------------------
Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
See the README file for the current license terms.
This software was previously distributed under BSD/GPL v2 dual license
terms that allowed either of those license alternatives to be
selected. As of February 11, 2012, the project has chosen to use only
the BSD license option for future distribution. As such, the GPL v2
license option is no longer used. It should be noted that the BSD
license option (the one with advertisement clause removed) is compatible
with GPL and as such, does not prevent use of this software in projects
that use GPL.
Some of the files may still include pointers to GPL version 2 license
terms. However, such copyright and license notifications are maintained
only for attribution purposes and any distribution of this software
after February 11, 2012 is no longer under the GPL v2 option.
*/
/* https://github.com/gaspardpetit/base64 */
#include <stdio.h>
#include <stdlib.h>
#include "include/base64.h"
#include "include/version.h"
#include "include/retcodes.h"
static const uint8_t TABLE[64 + 1] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const uint8_t URL_SAFE_TABLE[64 + 1] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
int l8w8jwt_base64_encode(const int url, const uint8_t* data, const size_t data_length, char** out, size_t* out_length)
{
if (out == NULL || data == NULL || out_length == NULL)
{
return L8W8JWT_NULL_ARG;
}
if (data_length == 0)
{
return L8W8JWT_INVALID_ARG;
}
size_t olen = data_length * 4 / 3 + 4;
olen += olen / 72;
olen++;
if (olen < data_length)
{
return L8W8JWT_OVERFLOW;
}
*out = malloc(olen);
if (*out == NULL)
{
return L8W8JWT_OUT_OF_MEM;
}
uint8_t* pos = (uint8_t*)(*out);
uint8_t* in = (uint8_t*)data;
uint8_t* end = (uint8_t*)data + data_length;
int line_length = 0;
const uint8_t* table = url ? URL_SAFE_TABLE : TABLE;
while (end - in >= 3)
{
*pos++ = table[in[0] >> 2];
*pos++ = table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
*pos++ = table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
*pos++ = table[in[2] & 0x3f];
in += 3;
line_length += 4;
if (line_length >= 72 && !url)
{
*pos++ = '\n';
line_length = 0;
}
}
int sub = 0;
if (end - in)
{
*pos++ = table[in[0] >> 2];
if (end - in == 1)
{
*pos++ = table[(in[0] & 0x03) << 4];
*pos++ = url ? '\0' : '=';
if (url)
{
sub++;
}
}
else
{
*pos++ = table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
*pos++ = table[(in[1] & 0x0f) << 2];
}
*pos++ = url ? '\0' : '=';
line_length += 4;
if (url)
{
sub++;
}
}
if (line_length && !url)
{
*pos++ = '\n';
}
*pos = '\0';
*out_length = (pos - (uint8_t*)*out) - sub;
return L8W8JWT_SUCCESS;
}
int l8w8jwt_base64_decode(const int url, const char* data, const size_t data_length, uint8_t** out, size_t* out_length)
{
if (data == NULL || out == NULL || out_length == NULL)
{
return L8W8JWT_NULL_ARG;
}
size_t in_length = data_length;
if (in_length == 0)
{
return L8W8JWT_INVALID_ARG;
}
if (*(data + in_length - 1) == '\0')
{
in_length--;
}
size_t i;
size_t count = 0;
uint8_t dtable[256];
const uint8_t* table = url ? URL_SAFE_TABLE : TABLE;
memset(dtable, 0x80, 256);
for (i = 0; i < 64; i++)
{
dtable[table[i]] = (uint8_t)i;
}
dtable['='] = 0;
for (i = 0; i < in_length; i++)
{
if (dtable[(unsigned char)data[i]] != 0x80)
count++;
}
int r = (int)(count % 4);
if (count == 0 || r == 1 || (!url && r > 0)) // Invalid input string (format or padding).
return L8W8JWT_INVALID_ARG;
if (r == 3)
r = 1;
*out = calloc(count / 4 * 3 + 16, sizeof(uint8_t));
if (*out == NULL)
{
return L8W8JWT_OUT_OF_MEM;
}
count = 0;
int pad = 0;
uint8_t tmp;
uint8_t block[4];
uint8_t* pos = *out;
for (i = 0; i < in_length + r; i++)
{
const unsigned char c = i < in_length ? data[i] : '=';
tmp = dtable[c];
if (tmp == 0x80)
continue;
if (c == '=')
pad++;
block[count] = tmp;
count++;
if (count == 4)
{
*pos++ = (block[0] << 2) | (block[1] >> 4);
*pos++ = (block[1] << 4) | (block[2] >> 2);
*pos++ = (block[2] << 6) | block[3];
count = 0;
if (pad)
{
if (pad == 1)
{
pos--;
}
else if (pad == 2)
{
pos -= 2;
}
else
{
l8w8jwt_free(*out);
*out = NULL;
return L8W8JWT_INVALID_ARG; // Invalid padding...
}
break;
}
}
}
*out_length = pos - *out;
return L8W8JWT_SUCCESS;
}
/*
* All credits for this base-64 encoding/decoding implementation go to Jouni Malinen.
* I take no credit for this (not even the modifications I made to it!) whatsoever.
* More information at the top of this file.
*/

147
r5dev/thirdparty/jwt/claim.c vendored Normal file
View File

@ -0,0 +1,147 @@
/*
Copyright 2020 Raphael Beck
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "include/claim.h"
#include "include/version.h"
#include "include/retcodes.h"
#include "include/chillbuff.h"
#include <string.h>
#include <mbedtls/md.h> // SChannel???
void l8w8jwt_free_claims(struct l8w8jwt_claim* claims, const size_t claims_count)
{
if (claims != NULL && claims_count > 0)
{
for (struct l8w8jwt_claim* claim = claims; claim < claims + claims_count; ++claim)
{
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);
}
l8w8jwt_zero(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)
{
for (size_t i = 0; i < string_length; ++i)
{
const char c = string[i];
switch (c)
{
case '\\':
chillbuff_push_back(stringbuilder, "\\\\", 2);
break;
case '\"':
chillbuff_push_back(stringbuilder, "\\\"", 2);
break;
default:
chillbuff_push_back(stringbuilder, &c, 1);
break;
}
}
}
int l8w8jwt_write_claims(struct chillbuff* stringbuilder, struct l8w8jwt_claim* claims, const size_t claims_count)
{
if (stringbuilder == NULL || claims == NULL)
{
return L8W8JWT_NULL_ARG;
}
if (claims_count == 0)
{
return L8W8JWT_INVALID_ARG;
}
struct chillbuff escape_buffer;
if (chillbuff_init(&escape_buffer, 256, sizeof(char), CHILLBUFF_GROW_LINEAR) != 0)
{
return L8W8JWT_OUT_OF_MEM;
}
int first = 1;
for (struct l8w8jwt_claim* claim = claims; claim < claims + claims_count; ++claim)
{
if (claim->key == NULL)
{
continue;
}
if (!first)
{
chillbuff_push_back(stringbuilder, ",", 1);
}
const size_t key_length = claim->key_length ? claim->key_length : strlen(claim->key);
const size_t value_length = claim->value_length ? claim->value_length : strlen(claim->value);
chillbuff_clear(&escape_buffer);
l8w8jwt_escape_claim_string(&escape_buffer, claim->key, key_length);
chillbuff_push_back(stringbuilder, "\"", 1);
chillbuff_push_back(stringbuilder, escape_buffer.array, escape_buffer.length);
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_push_back(stringbuilder, escape_buffer.array,escape_buffer.length);
if (claim->type == L8W8JWT_CLAIM_TYPE_STRING)
chillbuff_push_back(stringbuilder, "\"", 1);
first = 0;
}
chillbuff_free(&escape_buffer);
return L8W8JWT_SUCCESS;
}
struct l8w8jwt_claim* l8w8jwt_get_claim(struct l8w8jwt_claim* claims, const size_t claims_count, const char* key, const size_t key_length)
{
if (claims == NULL || key == NULL || claims_count == 0 || key_length == 0)
return NULL;
for (struct l8w8jwt_claim* claim = claims; claim < claims + claims_count; ++claim)
{
if (strncmp(claim->key, key, key_length) == 0)
return claim;
}
return NULL;
}
#ifdef __cplusplus
} // extern "C"
#endif

769
r5dev/thirdparty/jwt/decode.c vendored Normal file
View File

@ -0,0 +1,769 @@
/*
Copyright 2020 Raphael Beck
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifdef __cplusplus
extern "C" {
#endif
#define JSMN_STATIC
#include "include/util.h"
#include "include/decode.h"
#include "include/base64.h"
#include "include/chillbuff.h"
#include <jsmn.h> // RapidJSON?
#include <string.h>
#include <inttypes.h>
#include <checknum.h>
#include <mbedtls/pk.h>
#include <mbedtls/md.h>
#include <mbedtls/platform_util.h>
#include <mbedtls/entropy.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/pk.h>
#include <mbedtls/x509_crt.h>
#if L8W8JWT_ENABLE_EDDSA
#include <ed25519.h>
#endif
static inline void md_info_from_alg(const int alg, mbedtls_md_info_t** md_info, mbedtls_md_type_t* md_type, size_t* md_length)
{
switch (alg)
{
case L8W8JWT_ALG_HS256:
case L8W8JWT_ALG_RS256:
case L8W8JWT_ALG_PS256:
case L8W8JWT_ALG_ES256:
case L8W8JWT_ALG_ES256K:
*md_length = 32;
*md_type = MBEDTLS_MD_SHA256;
*md_info = (mbedtls_md_info_t*)mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
break;
case L8W8JWT_ALG_HS384:
case L8W8JWT_ALG_RS384:
case L8W8JWT_ALG_PS384:
case L8W8JWT_ALG_ES384:
*md_length = 48;
*md_type = MBEDTLS_MD_SHA384;
*md_info = (mbedtls_md_info_t*)mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
break;
case L8W8JWT_ALG_HS512:
case L8W8JWT_ALG_RS512:
case L8W8JWT_ALG_PS512:
case L8W8JWT_ALG_ES512:
case L8W8JWT_ALG_ED25519:
*md_length = 64;
*md_type = MBEDTLS_MD_SHA512;
*md_info = (mbedtls_md_info_t*)mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
break;
default:
break;
}
}
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 = calloc(sizeof(char), key_length + 1);
claim->value_length = 0;
claim->value = calloc(sizeof(char), value_length + 1);
if (claim->key == NULL || claim->value == NULL)
{
free(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)
{
const char nc = key[i + 1];
if (nc == '\"')
{
*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 = '\"';
break;
case '/':
*out_value = '/';
break;
default:
break;
}
++i;
}
++out_value;
}
claim->key_length = (size_t)(out_key - claim->key);
claim->value_length = (size_t)(out_value - claim->value);
return L8W8JWT_SUCCESS;
}
static int l8w8jwt_parse_claims(chillbuff* buffer, char* json, const size_t json_length)
{
jsmn_parser parser;
jsmn_init(&parser);
int r = jsmn_parse(&parser, json, json_length, NULL, 0);
if (r == 0)
{
return L8W8JWT_SUCCESS;
}
else if (r < 0)
{
return L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT;
}
jsmntok_t _tokens[64];
jsmntok_t* tokens = r <= (sizeof(_tokens) / sizeof(_tokens[0])) ? _tokens : malloc(r * sizeof(jsmntok_t));
if (tokens == NULL)
{
return L8W8JWT_OUT_OF_MEM;
}
jsmn_init(&parser);
r = jsmn_parse(&parser, json, json_length, tokens, r);
if (r < 0)
{
return L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT;
}
if (tokens->type != JSMN_OBJECT)
{
r = L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT;
goto exit;
}
for (size_t i = 1; i < r; ++i)
{
struct l8w8jwt_claim claim;
const jsmntok_t key = tokens[i];
const jsmntok_t value = tokens[++i];
if (i >= r || key.type != JSMN_STRING)
{
r = L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT;
goto exit;
}
switch (value.type)
{
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: {
claim.type = L8W8JWT_CLAIM_TYPE_STRING;
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))
{
claim.type = L8W8JWT_CLAIM_TYPE_BOOLEAN;
break;
}
if (value_length == 4 && strncmp(json + value.start, "null", 4) == 0)
{
claim.type = L8W8JWT_CLAIM_TYPE_NULL;
break;
}
switch (checknum(json + value.start, value_length))
{
case 1: {
claim.type = L8W8JWT_CLAIM_TYPE_INTEGER;
break;
}
case 2: {
claim.type = L8W8JWT_CLAIM_TYPE_NUMBER;
break;
}
default: {
r = L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT;
goto exit;
}
}
break;
}
default: {
r = L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT;
goto exit;
}
}
int ur = l8w8jwt_unescape_claim(&claim, json + key.start, (size_t)key.end - key.start, json + value.start, (size_t)value.end - value.start);
if (ur != L8W8JWT_SUCCESS)
{
r = ur;
goto exit;
}
chillbuff_push_back(buffer, &claim, 1);
}
r = L8W8JWT_SUCCESS;
exit:
if (tokens != _tokens)
{
l8w8jwt_free(tokens);
}
return r;
}
void l8w8jwt_decoding_params_init(struct l8w8jwt_decoding_params* params)
{
if (params == NULL)
{
return;
}
memset(params, 0x00, sizeof(struct l8w8jwt_decoding_params));
params->alg = -2;
}
int l8w8jwt_validate_decoding_params(struct l8w8jwt_decoding_params* params)
{
if (params == NULL || params->jwt == NULL || params->verification_key == NULL)
{
return L8W8JWT_NULL_ARG;
}
if (params->jwt_length == 0 || params->verification_key_length == 0 || params->verification_key_length > L8W8JWT_MAX_KEY_SIZE)
{
return L8W8JWT_INVALID_ARG;
}
#if !L8W8JWT_ENABLE_EDDSA
if (params->alg == L8W8JWT_ALG_ED25519)
{
return L8W8JWT_UNSUPPORTED_ALG;
}
#endif
return L8W8JWT_SUCCESS;
}
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)
{
if (params == NULL || (out_claims != NULL && out_claims_length == NULL))
{
return L8W8JWT_NULL_ARG;
}
const int alg = params->alg;
enum l8w8jwt_validation_result validation_res = L8W8JWT_VALID;
int r = l8w8jwt_validate_decoding_params(params);
if (r != L8W8JWT_SUCCESS)
{
return r;
}
if (out_validation_result == NULL)
{
return L8W8JWT_NULL_ARG;
}
*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;
char* current = params->jwt;
char* next = strchr(params->jwt, '.');
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)
{
r = L8W8JWT_OUT_OF_MEM;
goto exit;
}
size_t current_length = next - current;
r = l8w8jwt_base64_decode(true, current, current_length, (uint8_t**)&header, &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, (uint8_t**)&payload, &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, &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)
{
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;
}
if (params->validate_sub != NULL)
{
struct l8w8jwt_claim* c = l8w8jwt_get_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(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(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(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;
}
}
const time_t ct = 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))
{
validation_res |= (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))
{
validation_res |= (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))
{
validation_res |= (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)
{
validation_res |= (unsigned)L8W8JWT_TYP_FAILURE;
}
}
r = L8W8JWT_SUCCESS;
*out_validation_result = validation_res;
if (out_claims != NULL && out_claims_length != NULL)
{
*out_claims_length = claims.length;
*out_claims = (struct l8w8jwt_claim*)claims.array;
}
exit:
l8w8jwt_free(header);
l8w8jwt_free(payload);
l8w8jwt_free(signature);
if (out_claims == NULL || r != L8W8JWT_SUCCESS)
{
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
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
if (is_cert)
{
mbedtls_x509_crt_free(&crt);
}
else
{
mbedtls_pk_free(&pk);
}
return r;
}
#undef JSMN_STATIC
#ifdef __cplusplus
} // extern "C"
#endif

685
r5dev/thirdparty/jwt/encode.c vendored Normal file
View File

@ -0,0 +1,685 @@
/*
Copyright 2020 Raphael Beck
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "include/util.h"
#include "include/encode.h"
#include "include/base64.h"
#include "include/chillbuff.h"
#include <inttypes.h>
#include <mbedtls/pk.h>
#include <mbedtls/entropy.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/md.h>
#include <mbedtls/platform_util.h>
#if L8W8JWT_ENABLE_EDDSA
#include <ed25519.h>
#endif
static inline void md_info_from_alg(const int alg, mbedtls_md_info_t** md_info, mbedtls_md_type_t* md_type, size_t* md_length)
{
switch (alg)
{
case L8W8JWT_ALG_HS256:
case L8W8JWT_ALG_RS256:
case L8W8JWT_ALG_PS256:
case L8W8JWT_ALG_ES256:
case L8W8JWT_ALG_ES256K:
*md_length = 32;
*md_type = MBEDTLS_MD_SHA256;
*md_info = (mbedtls_md_info_t*)mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
break;
case L8W8JWT_ALG_HS384:
case L8W8JWT_ALG_RS384:
case L8W8JWT_ALG_PS384:
case L8W8JWT_ALG_ES384:
*md_length = 48;
*md_type = MBEDTLS_MD_SHA384;
*md_info = (mbedtls_md_info_t*)mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
break;
case L8W8JWT_ALG_HS512:
case L8W8JWT_ALG_RS512:
case L8W8JWT_ALG_PS512:
case L8W8JWT_ALG_ES512:
*md_length = 64;
*md_type = MBEDTLS_MD_SHA512;
*md_info = (mbedtls_md_info_t*)mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
break;
default:
break;
}
}
/* Step 1: prepare the token by encoding header + payload claims into a stringbuilder, ready to be signed! */
static int write_header_and_payload(chillbuff* stringbuilder, struct l8w8jwt_encoding_params* params)
{
int r;
chillbuff buff;
r = chillbuff_init(&buff, 256, sizeof(char), CHILLBUFF_GROW_DUPLICATIVE);
if (r != CHILLBUFF_SUCCESS)
{
return L8W8JWT_OUT_OF_MEM;
}
switch (params->alg)
{
case L8W8JWT_ALG_HS256:
chillbuff_push_back(&buff, "{\"alg\":\"HS256\",\"typ\":\"JWT\"", 26);
break;
case L8W8JWT_ALG_HS384:
chillbuff_push_back(&buff, "{\"alg\":\"HS384\",\"typ\":\"JWT\"", 26);
break;
case L8W8JWT_ALG_HS512:
chillbuff_push_back(&buff, "{\"alg\":\"HS512\",\"typ\":\"JWT\"", 26);
break;
case L8W8JWT_ALG_RS256:
chillbuff_push_back(&buff, "{\"alg\":\"RS256\",\"typ\":\"JWT\"", 26);
break;
case L8W8JWT_ALG_RS384:
chillbuff_push_back(&buff, "{\"alg\":\"RS384\",\"typ\":\"JWT\"", 26);
break;
case L8W8JWT_ALG_RS512:
chillbuff_push_back(&buff, "{\"alg\":\"RS512\",\"typ\":\"JWT\"", 26);
break;
case L8W8JWT_ALG_PS256:
chillbuff_push_back(&buff, "{\"alg\":\"PS256\",\"typ\":\"JWT\"", 26);
break;
case L8W8JWT_ALG_PS384:
chillbuff_push_back(&buff, "{\"alg\":\"PS384\",\"typ\":\"JWT\"", 26);
break;
case L8W8JWT_ALG_PS512:
chillbuff_push_back(&buff, "{\"alg\":\"PS512\",\"typ\":\"JWT\"", 26);
break;
case L8W8JWT_ALG_ES256:
chillbuff_push_back(&buff, "{\"alg\":\"ES256\",\"typ\":\"JWT\"", 26);
break;
case L8W8JWT_ALG_ES384:
chillbuff_push_back(&buff, "{\"alg\":\"ES384\",\"typ\":\"JWT\"", 26);
break;
case L8W8JWT_ALG_ES512:
chillbuff_push_back(&buff, "{\"alg\":\"ES512\",\"typ\":\"JWT\"", 26);
break;
case L8W8JWT_ALG_ES256K:
chillbuff_push_back(&buff, "{\"alg\":\"ES256K\",\"typ\":\"JWT\",\"kty\":\"EC\",\"crv\":\"secp256k1\"", 56);
break;
case L8W8JWT_ALG_ED25519:
chillbuff_push_back(&buff, "{\"alg\":\"EdDSA\",\"typ\":\"JWT\",\"kty\":\"EC\",\"crv\":\"Ed25519\"", 53);
break;
default:
chillbuff_free(&buff);
return L8W8JWT_INVALID_ARG;
}
if (params->additional_header_claims_count > 0)
{
chillbuff_push_back(&buff, ",", 1);
l8w8jwt_write_claims(&buff, params->additional_header_claims, params->additional_header_claims_count);
}
chillbuff_push_back(&buff, "}", 1);
char* segment;
size_t segment_length;
r = l8w8jwt_base64_encode(1, buff.array, buff.length, &segment, &segment_length);
if (r != L8W8JWT_SUCCESS)
{
chillbuff_free(&buff);
return r;
}
chillbuff_push_back(stringbuilder, segment, segment_length);
chillbuff_clear(&buff);
l8w8jwt_free(segment);
segment = NULL;
char iatnbfexp[64] = { 0x00 };
if (params->iat)
{
snprintf(iatnbfexp + 00, 21, "%" PRIu64 "", (uint64_t)params->iat);
}
if (params->nbf)
{
snprintf(iatnbfexp + 21, 21, "%" PRIu64 "", (uint64_t)params->nbf);
}
if (params->exp)
{
snprintf(iatnbfexp + 42, 21, "%" PRIu64 "", (uint64_t)params->exp);
}
struct l8w8jwt_claim claims[] = {
// Setting l8w8jwt_claim::value_length to 0 makes the encoder use strlen, which in this case is fine.
{ .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);
l8w8jwt_write_claims(&buff, claims, sizeof(claims) / sizeof(struct l8w8jwt_claim));
if (params->additional_payload_claims_count > 0)
{
if (params->iat || params->exp || params->nbf || params->iss_length || params->sub_length || params->jti_length || params->aud_length)
chillbuff_push_back(&buff, ",", 1);
l8w8jwt_write_claims(&buff, params->additional_payload_claims, params->additional_payload_claims_count);
}
chillbuff_push_back(&buff, "}", 1);
r = l8w8jwt_base64_encode(1, buff.array, buff.length, &segment, &segment_length);
if (r != L8W8JWT_SUCCESS)
{
chillbuff_free(&buff);
return r;
}
chillbuff_push_back(stringbuilder, ".", 1);
chillbuff_push_back(stringbuilder, segment, segment_length);
l8w8jwt_free(segment);
chillbuff_free(&buff);
return L8W8JWT_SUCCESS;
}
/* Step 2: call write_header_and_payload before you call this! */
static int write_signature(chillbuff* stringbuilder, struct l8w8jwt_encoding_params* params)
{
int r;
const int alg = params->alg;
char* signature = NULL;
size_t signature_length = 0, signature_bytes_length = 0, key_length = 0;
mbedtls_pk_context pk;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
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);
if (signature_bytes == NULL || key == NULL)
{
r = L8W8JWT_OUT_OF_MEM;
goto exit;
}
#else
unsigned char signature_bytes[4096] = { 0x00 };
unsigned char key[L8W8JWT_MAX_KEY_SIZE] = { 0x00 };
#endif
key_length = params->secret_key_length;
memcpy(key, params->secret_key, key_length);
/*
* MbedTLS requires the NUL-terminator to be included
* in the PEM-formatted key string passed to the key parse function.
* HMAC-key variants should subtract 1 from key_length again to compensate.
*/
key_length += key[key_length - 1] != '\0';
r = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char*)"l8w8jwt_mbedtls_pers.!#@", 24);
if (r != 0)
{
r = L8W8JWT_MBEDTLS_CTR_DRBG_SEED_FAILURE;
goto exit;
}
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_HS256:
case L8W8JWT_ALG_HS384:
case L8W8JWT_ALG_HS512: {
/*
* "key_length - 1" because the MbedTLS implementation of HMAC
* does not require its key string to include the NUL-terminator,
* unlike the RSA/ECC PEM key parse function "mbedtls_pk_parse_key",
* which MUST include the '\0' in the PEM-formatted key string.
*/
r = mbedtls_md_hmac(md_info, key, key_length - 1, (const unsigned char*)stringbuilder->array, stringbuilder->length, signature_bytes);
if (r != 0)
{
r = L8W8JWT_SIGNATURE_CREATION_FAILURE;
goto exit;
}
signature_bytes_length = 32 + (16 * params->alg);
break;
}
case L8W8JWT_ALG_RS256:
case L8W8JWT_ALG_RS384:
case L8W8JWT_ALG_RS512: {
/* Parse & load the key string into the mbedtls pk instance. */
r = mbedtls_pk_parse_key(&pk, key, key_length, params->secret_key_pw, params->secret_key_pw_length, mbedtls_ctr_drbg_random, &ctr_drbg);
if (r != 0)
{
r = L8W8JWT_KEY_PARSE_FAILURE;
goto exit;
}
/* Ensure RSA functionality. */
if (!mbedtls_pk_can_do(&pk, MBEDTLS_PK_RSA) && !mbedtls_pk_can_do(&pk, MBEDTLS_PK_RSA_ALT))
{
r = L8W8JWT_WRONG_KEY_TYPE;
goto exit;
}
/* Weak RSA keys are forbidden! */
if (mbedtls_pk_get_bitlen(&pk) < 2048)
{
r = L8W8JWT_WRONG_KEY_TYPE;
goto exit;
}
/* Hash the JWT header + payload. */
r = mbedtls_md(md_info, (const unsigned char*)stringbuilder->array, stringbuilder->length, hash);
if (r != L8W8JWT_SUCCESS)
{
r = L8W8JWT_SHA2_FAILURE;
goto exit;
}
/* Sign the hash using the provided private key. */
r = mbedtls_pk_sign(&pk, md_type, hash, md_length, signature_bytes, 4096, &signature_bytes_length, mbedtls_ctr_drbg_random, &ctr_drbg);
if (r != L8W8JWT_SUCCESS)
{
r = L8W8JWT_SIGNATURE_CREATION_FAILURE;
goto exit;
}
break;
}
case L8W8JWT_ALG_PS256:
case L8W8JWT_ALG_PS384:
case L8W8JWT_ALG_PS512: {
r = mbedtls_pk_parse_key(&pk, key, key_length, params->secret_key_pw, params->secret_key_pw_length, mbedtls_ctr_drbg_random, &ctr_drbg);
if (r != 0)
{
r = L8W8JWT_KEY_PARSE_FAILURE;
goto exit;
}
if (!mbedtls_pk_can_do(&pk, MBEDTLS_PK_RSASSA_PSS))
{
r = L8W8JWT_WRONG_KEY_TYPE;
goto exit;
}
if (mbedtls_pk_get_bitlen(&pk) < 2048)
{
r = L8W8JWT_WRONG_KEY_TYPE;
goto exit;
}
r = mbedtls_md(md_info, (const unsigned char*)stringbuilder->array, stringbuilder->length, hash);
if (r != L8W8JWT_SUCCESS)
{
r = L8W8JWT_SHA2_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_sign(rsa, mbedtls_ctr_drbg_random, &ctr_drbg, md_type, (unsigned int)md_length, hash, signature_bytes);
if (r != 0)
{
r = L8W8JWT_SIGNATURE_CREATION_FAILURE;
goto exit;
}
signature_bytes_length = mbedtls_pk_get_bitlen(&pk) / 8;
break;
}
case L8W8JWT_ALG_ES256:
case L8W8JWT_ALG_ES384:
case L8W8JWT_ALG_ES512:
case L8W8JWT_ALG_ES256K: {
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_pk_parse_key(&pk, key, key_length, params->secret_key_pw, params->secret_key_pw_length, mbedtls_ctr_drbg_random, &ctr_drbg);
if (r != 0)
{
r = L8W8JWT_KEY_PARSE_FAILURE;
goto ecdsa_exit;
}
if (!mbedtls_pk_can_do(&pk, MBEDTLS_PK_ECDSA))
{
r = L8W8JWT_WRONG_KEY_TYPE;
goto ecdsa_exit;
}
r = mbedtls_ecdsa_from_keypair(&ecdsa, mbedtls_pk_ec(pk));
if (r != 0)
{
r = L8W8JWT_KEY_PARSE_FAILURE;
goto ecdsa_exit;
}
r = mbedtls_md(md_info, (const unsigned char*)stringbuilder->array, stringbuilder->length, hash);
if (r != L8W8JWT_SUCCESS)
{
r = L8W8JWT_SHA2_FAILURE;
goto ecdsa_exit;
}
r = 0;
switch (alg)
{
case L8W8JWT_ALG_ES256: {
if (ecdsa.MBEDTLS_PRIVATE(grp).id != MBEDTLS_ECP_DP_SECP256R1)
{
r = L8W8JWT_WRONG_KEY_TYPE;
goto ecdsa_exit;
}
signature_bytes_length = 64;
r = mbedtls_pk_get_bitlen(&pk) == 256;
break;
}
case L8W8JWT_ALG_ES256K: {
if (ecdsa.MBEDTLS_PRIVATE(grp).id != MBEDTLS_ECP_DP_SECP256K1)
{
r = L8W8JWT_WRONG_KEY_TYPE;
goto ecdsa_exit;
}
signature_bytes_length = 64;
r = mbedtls_pk_get_bitlen(&pk) == 256;
break;
}
case L8W8JWT_ALG_ES384: {
if (ecdsa.MBEDTLS_PRIVATE(grp).id != MBEDTLS_ECP_DP_SECP384R1)
{
r = L8W8JWT_WRONG_KEY_TYPE;
goto ecdsa_exit;
}
signature_bytes_length = 96;
r = mbedtls_pk_get_bitlen(&pk) == 384;
break;
}
case L8W8JWT_ALG_ES512: {
if (ecdsa.MBEDTLS_PRIVATE(grp).id != MBEDTLS_ECP_DP_SECP521R1)
{
r = L8W8JWT_WRONG_KEY_TYPE;
goto ecdsa_exit;
}
signature_bytes_length = 132;
r = mbedtls_pk_get_bitlen(&pk) == 521;
break;
}
default:
break;
}
/*
* Ensure that the passed elliptic-curve cryptography key
* has a size that is valid and compatible with the selected JWT alg.
*/
if (r == 0)
{
r = L8W8JWT_WRONG_KEY_TYPE;
goto ecdsa_exit;
}
r = mbedtls_ecdsa_sign(&ecdsa.MBEDTLS_PRIVATE(grp), &sig_r, &sig_s, &ecdsa.MBEDTLS_PRIVATE(d), hash, md_length, mbedtls_ctr_drbg_random, &ctr_drbg);
if (r != 0)
{
r = L8W8JWT_SIGNATURE_CREATION_FAILURE;
goto ecdsa_exit;
}
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)
{
r = L8W8JWT_SIGNATURE_CREATION_FAILURE;
goto ecdsa_exit;
}
r = mbedtls_mpi_write_binary(&sig_s, signature_bytes + half_signature_bytes_length, half_signature_bytes_length);
if (r != 0)
{
r = L8W8JWT_SIGNATURE_CREATION_FAILURE;
goto ecdsa_exit;
}
r = L8W8JWT_SUCCESS;
ecdsa_exit:
mbedtls_mpi_free(&sig_r);
mbedtls_mpi_free(&sig_s);
mbedtls_ecdsa_free(&ecdsa);
if (r != L8W8JWT_SUCCESS)
{
goto exit;
}
break;
}
case L8W8JWT_ALG_ED25519: {
#if L8W8JWT_ENABLE_EDDSA
if (params->secret_key_length != 128 && !(params->secret_key_length == 129 && params->secret_key[128] == 0x00))
{
r = L8W8JWT_WRONG_KEY_TYPE;
goto exit;
}
unsigned char private_key_ref10[64 + 1] = { 0x00 };
if (l8w8jwt_hexstr2bin((const char*)params->secret_key, params->secret_key_length, private_key_ref10, sizeof(private_key_ref10), NULL) != 0)
{
r = L8W8JWT_WRONG_KEY_TYPE;
goto exit;
}
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));
break;
#else
r = L8W8JWT_UNSUPPORTED_ALG;
goto exit;
#endif
}
default: {
r = L8W8JWT_INVALID_ARG;
goto exit;
}
}
if (signature_bytes_length == 0)
{
r = L8W8JWT_SIGNATURE_CREATION_FAILURE;
goto exit;
}
/*
* If this succeeds, it mallocs "signature" 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)
{
r = L8W8JWT_BASE64_FAILURE;
goto exit;
}
chillbuff_push_back(stringbuilder, ".", 1);
chillbuff_push_back(stringbuilder, signature, signature_length);
exit:
l8w8jwt_zero(key, L8W8JWT_MAX_KEY_SIZE);
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
mbedtls_pk_free(&pk);
l8w8jwt_free(signature);
#if L8W8JWT_SMALL_STACK
l8w8jwt_free(key);
l8w8jwt_free(signature_bytes);
#endif
return r;
}
/* 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) = malloc(stringbuilder->length + 1);
if (*(params->out) == NULL)
{
return L8W8JWT_OUT_OF_MEM;
}
*(params->out_length) = stringbuilder->length;
(*(params->out))[stringbuilder->length] = '\0';
memcpy(*(params->out), stringbuilder->array, stringbuilder->length);
return L8W8JWT_SUCCESS;
}
void l8w8jwt_encoding_params_init(struct l8w8jwt_encoding_params* params)
{
if (params == NULL)
{
return;
}
memset(params, 0x00, sizeof(struct l8w8jwt_encoding_params));
params->alg = -2;
}
int l8w8jwt_validate_encoding_params(struct l8w8jwt_encoding_params* params)
{
if (params == NULL || params->secret_key == NULL || params->out == NULL || params->out_length == NULL)
{
return L8W8JWT_NULL_ARG;
}
if (params->secret_key_length == 0 || params->secret_key_length > L8W8JWT_MAX_KEY_SIZE)
{
return L8W8JWT_INVALID_ARG;
}
if ((params->additional_payload_claims != NULL && params->additional_payload_claims_count == 0))
{
return L8W8JWT_INVALID_ARG;
}
if ((params->additional_header_claims != NULL && params->additional_header_claims_count == 0))
{
return L8W8JWT_INVALID_ARG;
}
return L8W8JWT_SUCCESS;
}
int l8w8jwt_encode(struct l8w8jwt_encoding_params* params)
{
int r;
chillbuff stringbuilder;
r = l8w8jwt_validate_encoding_params(params);
if (r != L8W8JWT_SUCCESS)
{
return r;
}
r = chillbuff_init(&stringbuilder, 1024, sizeof(char), CHILLBUFF_GROW_DUPLICATIVE);
if (r != CHILLBUFF_SUCCESS)
{
return L8W8JWT_OUT_OF_MEM;
}
r = write_header_and_payload(&stringbuilder, params);
if (r != L8W8JWT_SUCCESS)
{
goto exit;
}
if (params->alg != -1)
{
r = write_signature(&stringbuilder, params);
if (r != L8W8JWT_SUCCESS)
{
goto exit;
}
}
r = write_token(&stringbuilder, params);
if (r != L8W8JWT_SUCCESS)
{
goto exit;
}
exit:
chillbuff_free(&stringbuilder);
return r;
}
#ifdef __cplusplus
} // extern "C"
#endif

111
r5dev/thirdparty/jwt/include/algs.h vendored Normal file
View File

@ -0,0 +1,111 @@
/*
Copyright 2020 Raphael Beck
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file algs.h
* @author Raphael Beck
* @brief JWT algorithms as defined in https://tools.ietf.org/html/rfc7518#section-3.1
*/
#ifndef L8W8JWT_ALGS_H
#define L8W8JWT_ALGS_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* HMAC-SHA256 signing algorithm.
*/
#define L8W8JWT_ALG_HS256 0
/**
* HMAC-SHA384 signing algorithm.
*/
#define L8W8JWT_ALG_HS384 1
/**
* HMAC-SHA512 signing algorithm.
*/
#define L8W8JWT_ALG_HS512 2
/**
* RSASSA-PKCS1-v1_5-SHA256 signing algorithm.
*/
#define L8W8JWT_ALG_RS256 3
/**
* RSASSA-PKCS1-v1_5-SHA384 signing algorithm.
*/
#define L8W8JWT_ALG_RS384 4
/**
* RSASSA-PKCS1-v1_5-SHA512 signing algorithm.
*/
#define L8W8JWT_ALG_RS512 5
/**
* RSASSA-PSS MGF1 SHA-256 signing algorithm.
*/
#define L8W8JWT_ALG_PS256 6
/**
* RSASSA-PSS MGF1 SHA-384 signing algorithm.
*/
#define L8W8JWT_ALG_PS384 7
/**
* RSASSA-PSS MGF1 SHA-512 signing algorithm.
*/
#define L8W8JWT_ALG_PS512 8
/**
* ECDSA + P-256 + SHA256 signing algorithm.
*/
#define L8W8JWT_ALG_ES256 9
/**
* ECDSA + P-384 + SHA384 signing algorithm.
*/
#define L8W8JWT_ALG_ES384 10
/**
* ECDSA + P-521 + SHA512 signing algorithm.
*/
#define L8W8JWT_ALG_ES512 11
/**
* ECDSA over secp256k1 + SHA256 signing algorithm.
*/
#define L8W8JWT_ALG_ES256K 12
/**
* EdDSA over ed25519 + SHA512 signing algorithm.
*/
#define L8W8JWT_ALG_ED25519 13
#ifndef L8W8JWT_ENABLE_EDDSA
/**
* Set this to \c 1 if you want to enable the EdDSA signing algorithm
*/
#define L8W8JWT_ENABLE_EDDSA 0
#endif
#ifdef __cplusplus
} // extern "C"
#endif
#endif // L8W8JWT_ALGS_H

77
r5dev/thirdparty/jwt/include/base64.h vendored Normal file
View File

@ -0,0 +1,77 @@
/*
Copyright 2020 Raphael Beck
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file base64.h
* @author Raphael Beck
* @brief Base-64 encode and decode strings/bytes. <p>
* @warning The caller is responsible for freeing the returned buffers! <p>
* Pass <code>true</code> as first parameter if you want to use base64url encoding instead of base64.
* @see https://en.wikipedia.org/wiki/Base64#URL_applications
*/
#ifndef L8W8JWT_BASE64_H
#define L8W8JWT_BASE64_H
#ifdef __cplusplus
extern "C" {
#endif
#include "version.h"
#include <stdint.h>
#include <string.h>
/**
* Encodes a byte array to a base-64 string. <p>
* If you're encoding a string, don't include the NUL terminator
* (pass <code>strlen(data)</code> instead of the array's size to the <code>data_length</code> parameter). <p>
*
* @note The output buffer is NUL-terminated to make it easier to use as a C string.
* @note The NUL terminator is NOT included in the <code>out_length</code>.
* @note DO NOT FORGET to free the output buffer once you're done using it!
*
* @param url base64url encode instead of base64? Set to \c 0 for \c false; anything else for \c true.
* @param data The data (array of bytes) to base-64 encode.
* @param data_length The length of the input data array (in case of a C string: array size - 1 in order to omit the NUL terminator).
* @param out Output where the base-64 encoded string should be written into (will be malloc'ed, so make sure to <code>free()</code> this as soon as you're done using it!).
* @param out_length Pointer to a <code>size_t</code> variable containing the length of the output buffer minus the NUL terminator.
*
* @return Return code as defined in retcodes.h
*/
L8W8JWT_API int l8w8jwt_base64_encode(int url, const uint8_t* data, size_t data_length, char** out, size_t* out_length);
/**
* Decodes a base-64 encoded string to an array of bytes. <p>
*
* @note The returned bytes buffer is NUL-terminated to allow usage as a C string.
* @note The NUL terminator is NOT included in the <code>out_length</code>.
* @note DO NOT FORGET to free the output buffer once you're done using it!
*
* @param url Decode using base64url instead of base64? Set to \c 0 for \c false; anything else for \c true.
* @param data The base-64 encoded string to decode (obtained via {@link #l8w8jwt_base64_encode}).
* @param data_length The length of the string to decode.
* @param out Output where the decoded bytes should be written into (will be malloc'ed, so make sure to <code>free()</code> this as soon as you're done using it!).
* @param out_length Pointer to a <code>size_t</code> variable into which to write the output buffer's length.
*
* @return Return code as defined in retcodes.h
*/
L8W8JWT_API int l8w8jwt_base64_decode(int url, const char* data, size_t data_length, uint8_t** out, size_t* out_length);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // L8W8JWT_BASE64_H

326
r5dev/thirdparty/jwt/include/chillbuff.h vendored Normal file
View File

@ -0,0 +1,326 @@
/*
Copyright 2019 Raphael Beck
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file chillbuff.h
* @author Raphael Beck
* @date 27. December 2019
* @brief Array. Dynamic size. Push back 'n' chill. Buffer stuff. Dynamic stuff that's buff. Dynamically reallocating buff.. Yeah!
* @see https://github.com/GlitchedPolygons/chillbuff
*/
#ifndef CHILLBUFF_H
#define CHILLBUFF_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
/* The following are the chillbuff exit codes returned from the various chillbuff functions. */
/**
* Returned from a chillbuff function when everything went smooth 'n' chill. Time to get Schwifty!
*/
#define CHILLBUFF_SUCCESS 0
/**
* Chill time is over, you're out of memory... Time to reconsider memory usage.
*/
#define CHILLBUFF_OUT_OF_MEM 100
/**
* Error code returned by a chillbuff function if you passed a NULL argument that shouldn't have been NULL.
*/
#define CHILLBUFF_NULL_ARG 200
/**
* This error code is returned by a chillbuff function if you passed an invalid parameter into it.
*/
#define CHILLBUFF_INVALID_ARG 300
/**
* Not good...
*/
#define CHILLBUFF_OVERFLOW 400
/** @private */
static void (*_chillbuff_error_callback)(const char*) = NULL;
/**
* How should the chillbuff's underlying array grow in size
* once its maximum capacity is reached during a push_back?
*/
typedef enum chillbuff_growth_method
{
/**
* Double the capacity.
*/
CHILLBUFF_GROW_DUPLICATIVE = 0,
/**
* Triple the capacity.
*/
CHILLBUFF_GROW_TRIPLICATIVE = 1,
/**
* Grow by the same capacity every time the buffer is full.
*/
CHILLBUFF_GROW_LINEAR = 2,
/**
* Multiplies the capacity by itself. Not the greatest idea... Use carefully!
*/
CHILLBUFF_GROW_EXPONENTIAL = 3
} chillbuff_growth_method;
/**
* Self-reallocating dynamic size array of no strictly defined type.
* Easy 'n' "chill" (hope you like segmentation fault errors).
*/
typedef struct chillbuff
{
/**
* The buffer's underlying array that stores the data.
*/
void* array;
/**
* The current amount of elements stored in the chillbuff. DO NOT touch this yourself, only read!
*/
size_t length;
/**
* The current buffer capacity. This grows dynamically according to the specified {@link #chillbuff_growth_method}.
*/
size_t capacity;
/**
* The size of each stored element. DO NOT CHANGE THIS! Only read (if necessary)...
*/
size_t element_size;
/**
* The way the buffer's capacity is increased when it's full.
*/
chillbuff_growth_method growth_method;
} chillbuff;
/** @private */
static inline void _chillbuff_printerr(const char* error, const char* origin)
{
const size_t error_length = 64 + strlen(error) + strlen(origin);
char* error_msg = (char*)malloc(error_length * sizeof(char)); // cast malloc because of compat with C++ D:
if (error_msg != NULL)
{
snprintf(error_msg, error_length, "\nCHILLBUFF ERROR: (%s) %s\n", origin, error);
if (_chillbuff_error_callback != NULL)
{
_chillbuff_error_callback(error_msg);
}
free(error_msg);
}
}
/**
* Sets the chillbuff error callback. <p>
* If errors occur, they'll be passed as a string into the provided callback function.
* @param error_callback The function to call when errors occur.
* @return Whether the callback was set up correctly or not (chillbuff exit code, see top of chillbuff.h file for more details).
*/
static inline int chillbuff_set_error_callback(void (*error_callback)(const char*))
{
if (error_callback == NULL)
{
_chillbuff_printerr("The passed error callback is empty; Operation cancelled!", __func__);
return CHILLBUFF_NULL_ARG;
}
_chillbuff_error_callback = error_callback;
return CHILLBUFF_SUCCESS;
}
/**
* Clears the chillbuff error callback (errors won't be printed anymore).
*/
static inline void chillbuff_unset_error_callback()
{
_chillbuff_error_callback = NULL;
}
/**
* Initializes a chillbuff instance and makes it ready to accept data.
* @param buff The chillbuff instance to init (or rather, a pointer to it).
* @param initial_capacity The initial capacity of the underlying array. If you pass <code>0</code> here, <code>16</code> is used by default.
* @param element_size How big should every array element be? E.g. if you're storing <code>int</code> you should pass <code>sizeof(int)</code>.
* @param growth_method How should the buffer grow once its maximum capacity is reached? @see chillbuff_growth_method
* @return Chillbuff exit code as defined at the top of the chillbuff.h header file. <code>0</code> means success.
*/
static inline int chillbuff_init(chillbuff* buff, const size_t initial_capacity, const size_t element_size, const chillbuff_growth_method growth_method)
{
if (buff == NULL)
{
_chillbuff_printerr("Tried to init a NULL chillbuff instance; wouldn't end well. Cancelled...", __func__);
return CHILLBUFF_NULL_ARG;
}
if (element_size == 0)
{
_chillbuff_printerr("Storing elements of size \"0\" makes no sense...", __func__);
return CHILLBUFF_INVALID_ARG;
}
if (growth_method < 0 || growth_method > 3)
{
_chillbuff_printerr("Invalid grow method! Please use the appropriate chillbuff_growth_method enum!", __func__);
return CHILLBUFF_INVALID_ARG;
}
buff->length = 0;
buff->element_size = element_size;
buff->growth_method = growth_method;
buff->capacity = initial_capacity == 0 ? 16 : initial_capacity;
buff->array = calloc(buff->capacity, buff->element_size);
if (buff->array == NULL)
{
_chillbuff_printerr("OUT OF MEMORY!", __func__);
return CHILLBUFF_OUT_OF_MEM;
}
return CHILLBUFF_SUCCESS;
}
/**
* Frees a chillbuff instance.
* @param buff The chillbuff to deallocate. If this is <code>NULL</code>, nothing happens at all.
*/
static inline void chillbuff_free(chillbuff* buff)
{
if (buff == NULL)
{
return;
}
memset(buff->array, '\0', buff->length);
free(buff->array);
buff->array = NULL;
buff->length = buff->capacity = buff->element_size = 0;
}
/**
* Clears a chillbuff's data. <p>
* Deletes all of the underlying array's elements and resets the length to <code>0</code>. <p>
* Leaves the array allocated at the current capacity.
* @param buff The chillbuff to clear. If this is <code>NULL</code>, nothing happens at all.
*/
static inline void chillbuff_clear(chillbuff* buff)
{
if (buff == NULL)
{
return;
}
memset(buff->array, '\0', buff->capacity);
buff->length = 0;
}
/**
* Appends one or more elements to the buffer.
* If the buffer is full, it will be expanded automatically.
* @param buff The buffer into which to insert the elements.
* @param elements The array of elements to insert (pointer to the first element).
* @param elements_count Amount of elements to add (for example: if your buffer stores the type <code>uint32_t</code>, you'd pass <code>sizeof(elements_to_add) / sizeof(uint32_t)</code> here). If you're only adding a single element, pass <code>1</code>.
* @return Chillbuff exit code that describes the insertion's outcome.
*/
static int chillbuff_push_back(chillbuff* buff, const void* elements, const size_t elements_count)
{
if (buff == NULL)
{
_chillbuff_printerr("Tried to append to a NULL chillbuff instance!", __func__);
return CHILLBUFF_NULL_ARG;
}
if (elements == NULL)
{
_chillbuff_printerr("Tried to append NULL element(s) to a chillbuff instance!", __func__);
return CHILLBUFF_NULL_ARG;
}
if (elements_count == 0)
{
_chillbuff_printerr("The passed \"elements_count\" argument is zero; nothing to append!", __func__);
return CHILLBUFF_INVALID_ARG;
}
for (size_t i = 0; i < elements_count; i++)
{
if (buff->length == buff->capacity)
{
size_t new_capacity;
switch (buff->growth_method)
{
default:
_chillbuff_printerr("Invalid grow method! Please use the appropriate chillbuff_growth_method enum!", __func__);
return CHILLBUFF_INVALID_ARG;
case CHILLBUFF_GROW_DUPLICATIVE:
new_capacity = (buff->capacity * 2);
break;
case CHILLBUFF_GROW_TRIPLICATIVE:
new_capacity = (buff->capacity * 3);
break;
case CHILLBUFF_GROW_LINEAR:
new_capacity = (buff->capacity + buff->element_size);
break;
case CHILLBUFF_GROW_EXPONENTIAL:
new_capacity = (buff->capacity * buff->capacity);
break;
}
if (new_capacity <= buff->capacity || new_capacity >= UINT64_MAX / buff->element_size)
{
_chillbuff_printerr("Couldn't push back due to buffer OVERFLOW!", __func__);
return CHILLBUFF_OVERFLOW;
}
void* new_array = realloc(buff->array, new_capacity * buff->element_size);
if (new_array == NULL)
{
_chillbuff_printerr("Couldn't resize chillbuff underlying array; OUT OF MEMORY!", __func__);
return CHILLBUFF_OUT_OF_MEM;
}
memset((char*)new_array + (buff->element_size * buff->length), '\0', (new_capacity - buff->length) * buff->element_size);
buff->array = new_array;
buff->capacity = new_capacity;
}
memcpy((char*)buff->array + (buff->element_size * buff->length++), (char*)elements + (i * buff->element_size), buff->element_size);
}
return CHILLBUFF_SUCCESS;
}
#ifdef __cplusplus
} // extern "C"
#endif
#endif // CHILLBUFF_H

146
r5dev/thirdparty/jwt/include/claim.h vendored Normal file
View File

@ -0,0 +1,146 @@
/*
Copyright 2020 Raphael Beck
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file claim.h
* @author Raphael Beck
* @brief JWT claims as described in https://auth0.com/docs/tokens/concepts/jwt-claims
*/
#ifndef L8W8JWT_CLAIM_H
#define L8W8JWT_CLAIM_H
#ifdef __cplusplus
extern "C" {
#endif
#include "version.h"
#include <stdlib.h>
// Forward declare chillbuff
/** @private */
struct chillbuff;
/**
* JWT claim value is a string (e.g. <code>"iss": "glitchedpolygons.com"</code>).
*/
#define L8W8JWT_CLAIM_TYPE_STRING 0
/**
* JWT claim value is an integer (e.g. <code>"exp": 1579610629</code>)
*/
#define L8W8JWT_CLAIM_TYPE_INTEGER 1
/**
* JWT claim value type number (e.g. <code>"size": 1.85</code>).
*/
#define L8W8JWT_CLAIM_TYPE_NUMBER 2
/**
* JWT claim value is a boolean (e.g. <code>"done": true</code>).
*/
#define L8W8JWT_CLAIM_TYPE_BOOLEAN 3
/**
* JWT claim value is null (e.g. <code>"ref": null</code>).
*/
#define L8W8JWT_CLAIM_TYPE_NULL 4
/**
* JWT claim value type JSON array (e.g. <code>"ids": [2, 4, 8, 16]</code>).
*/
#define L8W8JWT_CLAIM_TYPE_ARRAY 5
/**
* JWT claim value type is a JSON object (e.g. <code>"objs": { "name": "GMan", "id": 420 }</code>).
*/
#define L8W8JWT_CLAIM_TYPE_OBJECT 6
/**
* JWT claim value is some other type.
*/
#define L8W8JWT_CLAIM_TYPE_OTHER 7
/**
* Struct containing a jwt claim key-value pair.<p>
* If allocated on the heap by the decode function,
* remember to call <code>l8w8jwt_claims_free()</code> on it once you're done using it.
*/
struct l8w8jwt_claim
{
/**
* The token claim key (e.g. "iss", "iat", "sub", etc...). <p>
* NUL-terminated C-string!
*/
char* key;
/**
* key string length. <p>
* Set this to <code>0</code> if you want to make the encoder use <code>strlen(key)</code> instead.
*/
size_t key_length;
/**
* The claim's value as a NUL-terminated C-string.
*/
char* value;
/**
* value string length. <p>
* Set this to <code>0</code> if you want to make the encoder use <code>strlen(value)</code> instead.
*/
size_t value_length;
/**
* The type of the claim's value. <p>
* 0 = string, 1 = integer, 2 = number, 3 = boolean, 4 = null, 5 = array, 6 = object, 7 = other.
* @see https://www.w3schools.com/js/js_json_datatypes.asp
*/
int type;
};
/**
* Frees a heap-allocated <code>l8w8jwt_claim</code> array.
* @param claims The claims to free.
* @param claims_count The size of the passed claims array.
*/
L8W8JWT_API void l8w8jwt_free_claims(struct l8w8jwt_claim* claims, size_t claims_count);
/**
* Writes a bunch of JWT claims into a chillbuff stringbuilder. <p>
* Curly braces and trailing commas won't be written; only the "key":"value" pairs!
* @param stringbuilder The buffer into which to write the claims.
* @param claims The l8w8jwt_claim array of claims to write.
* @param claims_count The claims array size.
* @return Return code as specified inside retcodes.h
*/
L8W8JWT_API int l8w8jwt_write_claims(struct chillbuff* stringbuilder, struct l8w8jwt_claim* claims, size_t claims_count);
/**
* Gets a claim by key from a l8w8jwt_claim array.
* @param claims The array to look in.
* @param claims_count The claims array size.
* @param key The claim key (e.g. "sub") to look for.
* @param key_length The claim key's string length.
* @return The found claim; <code>NULL</code> if no such claim was found in the array.
*/
L8W8JWT_API struct l8w8jwt_claim* l8w8jwt_get_claim(struct l8w8jwt_claim* claims, size_t claims_count, const char* key, size_t key_length);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // L8W8JWT_CLAIM_H

274
r5dev/thirdparty/jwt/include/decode.h vendored Normal file
View File

@ -0,0 +1,274 @@
/*
Copyright 2020 Raphael Beck
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file decode.h
* @author Raphael Beck
* @brief Core DECODE function for l8w8jwt. Use this to decode and validate a JWT!
*/
#ifndef L8W8JWT_DECODE_H
#define L8W8JWT_DECODE_H
#ifdef __cplusplus
extern "C" {
#endif
#include "algs.h"
#include "claim.h"
#include "version.h"
#include "retcodes.h"
#include <time.h>
#include <stddef.h>
#include <stdint.h>
#ifndef L8W8JWT_MAX_KEY_SIZE
#define L8W8JWT_MAX_KEY_SIZE 8192
#endif
/**
* Enum containing the validation result flags.
*/
enum l8w8jwt_validation_result {
/**
* The JWT is valid (according to the passed validation parameters).
*/
L8W8JWT_VALID = (unsigned)0,
/**
* The issuer claim is invalid.
*/
L8W8JWT_ISS_FAILURE = (unsigned)1 << (unsigned)0,
/**
* The subject claim is invalid.
*/
L8W8JWT_SUB_FAILURE = (unsigned)1 << (unsigned)1,
/**
* The audience claim is invalid.
*/
L8W8JWT_AUD_FAILURE = (unsigned)1 << (unsigned)2,
/**
* The JWT ID claim is invalid.
*/
L8W8JWT_JTI_FAILURE = (unsigned)1 << (unsigned)3,
/**
* The token is expired.
*/
L8W8JWT_EXP_FAILURE = (unsigned)1 << (unsigned)4,
/**
* The token is not yet valid.
*/
L8W8JWT_NBF_FAILURE = (unsigned)1 << (unsigned)5,
/**
* The token was not issued yet, are you from the future?
*/
L8W8JWT_IAT_FAILURE = (unsigned)1 << (unsigned)6,
/**
* The token was potentially tampered with: its signature couldn't be verified.
*/
L8W8JWT_SIGNATURE_VERIFICATION_FAILURE = (unsigned)1 << (unsigned)7,
/**
* The token's "typ" claim validation failed.
*/
L8W8JWT_TYP_FAILURE = (unsigned)1 << (unsigned)8
};
/**
* Struct containing the parameters to use for decoding and validating a JWT.
*/
struct l8w8jwt_decoding_params
{
/**
* The token to decode and validate.
*/
char* jwt;
/**
* The jwt string length.
*/
size_t jwt_length;
/**
* The signature algorithm ID. <p>
* [0;2] = HS256/384/512 | [3;5] = RS256/384/512 | [6;8] = PS256/384/512 | [9;11] = ES256/384/512 <p>
* This affects what should be the value of {@link #verification_key}
*/
int alg;
/**
* [OPTIONAL] The issuer claim (who issued the JWT?). <p>
* Set to <code>NULL</code> if you don't want to validate the issuer. <p>
* The JWT will only pass verification if its <code>iss</code> claim matches this string.
* @see https://tools.ietf.org/html/rfc7519#section-4.1.1
*/
char* validate_iss;
/**
* validate_iss string length.
*/
size_t validate_iss_length;
/**
* [OPTIONAL] The subject claim (who is the JWT about?). <p>
* Set to <code>NULL</code> if you don't want to validate the subject claim. <p>
* The JWT will only pass verification if its <code>sub</code> matches this string.
* @see https://tools.ietf.org/html/rfc7519#section-4.1.2
*/
char* validate_sub;
/**
* validate_sub string length.
*/
size_t validate_sub_length;
/**
* [OPTIONAL] The audience claim (who is the JWT intended for? Who is the intended JWT's recipient?). <p>
* Set to <code>NULL</code> if you don't want to validate the audience. <p>
* The JWT will only pass verification if its <code>aud</code> matches this string.
* @see https://tools.ietf.org/html/rfc7519#section-4.1.3
*/
char* validate_aud;
/**
* validate_aud string length.
*/
size_t validate_aud_length;
/**
* [OPTIONAL] The JWT ID. Provides a unique identifier for the token. <p>
* Set to <code>NULL</code> if you don't want to validate the jti claim. <p>
* The JWT will only pass verification if its <code>jti</code> matches this string.
* @see https://tools.ietf.org/html/rfc7519#section-4.1.7
*/
char* validate_jti;
/**
* validate_jti claim length.
*/
size_t validate_jti_length;
/**
* Should the expiration claim be verified?
* If this is set to <code>1</code>, the <code>exp</code> claim will be compared to the current date and time + {@link #exp_tolerance_seconds}
*/
int validate_exp;
/**
* Should the "not before" claim be verified?
* If this is set to <code>1</code>, the <code>nbf</code> claim will be compared to the current date and time + {@link #nbf_tolerance_seconds}
*/
int validate_nbf;
/**
* Should the "issued at" claim be verified?
* If this is set to <code>1</code>, the <code>iat</code> claim will be compared to the current date and time + {@link #iat_tolerance_seconds}
*/
int validate_iat;
/**
* Small inconsistencies in time can happen, or also latency between clients and servers.
* That's just life. You can forgive a few seconds of expiration, but don't exaggerate this! <p>
* Only taken into consideration if {@link #validate_exp} is set to <code>1</code>.
*/
uint8_t exp_tolerance_seconds;
/**
* The amount of seconds to subtract from the current time when comparing the "not before" claim, to allow for a small tolerance time frame.
* Only taken into consideration if {@link #validate_nbf} is set to <code>1</code>.
*/
uint8_t nbf_tolerance_seconds;
/**
* The amount of seconds to subtract from the current time when comparing the "issued at" claim, to allow for a small tolerance time frame.
* Only taken into consideration if {@link #validate_iat} is set to <code>1</code>.
*/
uint8_t iat_tolerance_seconds;
/**
* The key to use for verifying the token's signature
* (e.g. if you chose HS256 as algorithm, this will be the HMAC secret; for RS512 this will be the PEM-formatted public RSA key string, etc...).
*/
unsigned char* verification_key;
/**
* Length of the {@link #verification_key}
*/
size_t verification_key_length;
/**
* [OPTIONAL] The typ claim (what type is the token?). <p>
* Set to <code>NULL</code> if you don't want to validate the "typ" claim. <p>
*/
char* validate_typ;
/**
* validate_typ string length.
*/
size_t validate_typ_length;
};
/**
* Initializes a {@link #l8w8jwt_decoding_params} instance by setting its fields to default values.
* @param params The l8w8jwt_decoding_params to initialize (set to default values).
*/
L8W8JWT_API void l8w8jwt_decoding_params_init(struct l8w8jwt_decoding_params* params);
/**
* Validates a set of l8w8jwt_decoding_params.
* @param params The l8w8jwt_decoding_params to validate.
* @return Return code as defined in retcodes.h
*/
L8W8JWT_API int l8w8jwt_validate_decoding_params(struct l8w8jwt_decoding_params* params);
/**
* Decode (and validate) a JWT using specific parameters. <p>
* 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). <p>
* This only happens if decoding also succeeded: if the token is malformed, nothing will be written into "out_validation_result". <p>
* If validation succeeds, the {@link #l8w8jwt_validation_result} receives the value 0 (enum value <code>L8W8JWT_VALID</code>). <p>
* The same applies to the "out_claims" argument: it is only allocated and written to if it (obviously) isn't <code>NULL</code> and if the decoding 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 <code>~L8W8JWT_VALID</code>)!
*
* @param out_claims
* [OPTIONAL] Where the decoded claims (header + payload claims together) should be written into.
* This pointer will be dereferenced + allocated, so make sure to pass a fresh pointer!
* If you don't need the claims, set this to <code>NULL</code> (they will only be validated, e.g. signature, exp, etc...).
* Check the note down below for more infos!
*
* @param out_claims_length Where to write the decoded claims count into. This will receive the value of how many claims were written into "out_claims" (0 if you decided to set "out_claims" to <code>NULL</code>).
*
* @note If you decide to keep the claims stored in the <code>out_claims</code> parameter, REMEMBER to call {@link #l8w8jwt_free_claims()} on it once you're done using them!
*
* @return Return code as defined in retcodes.h (this is NOT the validation result that's written into the out_validation_result argument; the returned int describes whether the actual parsing/decoding part failed).
*/
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);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // L8W8JWT_DECODE_H

206
r5dev/thirdparty/jwt/include/encode.h vendored Normal file
View File

@ -0,0 +1,206 @@
/*
Copyright 2020 Raphael Beck
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file encode.h
* @author Raphael Beck
* @brief Core ENCODE function for l8w8jwt. Use this to encode a JWT header + payload WITHOUT signing.
*/
#ifndef L8W8JWT_ENCODE_H
#define L8W8JWT_ENCODE_H
#ifdef __cplusplus
extern "C" {
#endif
#include "algs.h"
#include "claim.h"
#include "version.h"
#include "retcodes.h"
#include <time.h>
#include <stddef.h>
#ifndef L8W8JWT_MAX_KEY_SIZE
#define L8W8JWT_MAX_KEY_SIZE 8192
#endif
/**
* Struct containing the parameters to use for creating a JWT with l8w8jwt.
*/
L8W8JWT_API struct l8w8jwt_encoding_params
{
/**
* The signature algorithm ID. <p>
* [0;2] = HS256/384/512 | [3;5] = RS256/384/512 | [6;8] = PS256/384/512 | [9;11] = ES256/384/512
*/
int alg;
/**
* [OPTIONAL] The issuer claim (who issued the JWT?). Can be omitted by setting this to <code>NULL</code>.
* @see https://tools.ietf.org/html/rfc7519#section-4.1.1
*/
char* iss;
/**
* iss claim string length.
*/
size_t iss_length;
/**
* [OPTIONAL] The subject claim (who is the JWT about?). Set to <code>NULL</code> if you don't want it in your token.
* @see https://tools.ietf.org/html/rfc7519#section-4.1.2
*/
char* sub;
/**
* sub claim string length.
*/
size_t sub_length;
/**
* [OPTIONAL] The audience claim (who is the JWT intended for? Who is the intended JWT's recipient?).
* Set this to <code>NULL</code> if you don't wish to add this claim to the token.
* @see https://tools.ietf.org/html/rfc7519#section-4.1.3
*/
char* aud;
/**
* aud claim string length.
*/
size_t aud_length;
/**
* [OPTIONAL] The JWT ID. Provides a unique identifier for the token. Can be omitted by setting this to <code>NULL</code>.
* @see https://tools.ietf.org/html/rfc7519#section-4.1.7
*/
char* jti;
/**
* jti claim string length.
*/
size_t jti_length;
/**
* Expiration time claim; specifies when this token should stop being valid (in seconds since Unix epoch). <p>
* If you want to omit this, set this to <code>0</code>, but do NOT FORGET to set it to something,
* 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;
/**
* "Not before" time claim; specifies when this token should start being valid (in seconds since Unix epoch). <p>
* If you want to omit this, set this to <code>0</code>, but do NOT FORGET to set it to something,
* 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;
/**
* "Issued at" timestamp claim; specifies when this token was emitted (in seconds since Unix epoch). <p>
* If you want to omit this, set this to <code>0</code>, but do NOT FORGET to set it to something,
* 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;
/**
* [OPTIONAL] Array of additional claims to include in the JWT's header like for example "kid" or "cty"; pass <code>NULL</code> if you don't wish to add any! <p>
* Avoid header claims such as <code>typ</code> and <code>alg</code>, since those are written by the encoding function itself.
* @see https://tools.ietf.org/html/rfc7519#section-4.1.7
*/
struct l8w8jwt_claim* additional_header_claims;
/**
* [OPTIONAL] The additional_header_claims array size; pass <code>0</code> if you don't wish to include any custom claims!
*/
size_t additional_header_claims_count;
/**
* [OPTIONAL] Array of additional claims to include in the JWT's payload; pass <code>NULL</code> if you don't wish to add any! <p>
* Registered claim names such as "iss", "exp", etc... have their own dedicated field within this struct: do not include those in this array to prevent uncomfortable duplicates!
* @see https://tools.ietf.org/html/rfc7519#section-4
*/
struct l8w8jwt_claim* additional_payload_claims;
/**
* [OPTIONAL] The additional_payload_claims array size; pass <code>0</code> if you don't wish to include any custom claims!
*/
size_t additional_payload_claims_count;
/**
* The secret key to use for signing the token
* (e.g. if you chose HS256 as algorithm, this will be the HMAC secret; for RS512 this will be the private PEM-formatted RSA key string, and so on...).
*/
unsigned char* secret_key;
/**
* Length of the secret_key
*/
size_t secret_key_length;
/**
* If the secret key requires a password for usage, please assign it to this field. <p>
* You can only omit this when using JWT algorithms "HS256", "HS384" or "HS512" (it's ignored in that case actually). <p>
* Every other algorithm requires you to at least set this to <code>NULL</code> if the {@link #secret_key} isn't password-protected.
*/
unsigned char* secret_key_pw;
/**
* The secret key's password length (if there is any). If there's none, set this to zero!
*/
size_t secret_key_pw_length;
/**
* Where the encoded token should be written into
* (will be malloc'ed, so make sure to <code>l8w8jwt_free()</code> this as soon as you're done using it!).
*/
char** out;
/**
* Where the output token string length should be written into.
*/
size_t* out_length;
};
/**
* Initializes a {@link #l8w8jwt_encoding_params} instance by setting its fields to default values.
* @param params The l8w8jwt_encoding_params to initialize (set to default values).
*/
L8W8JWT_API void l8w8jwt_encoding_params_init(struct l8w8jwt_encoding_params* params);
/**
* Validates a set of l8w8jwt_encoding_params.
* @param params The l8w8jwt_encoding_params to validate.
* @return Return code as defined in retcodes.h
*/
L8W8JWT_API int l8w8jwt_validate_encoding_params(struct l8w8jwt_encoding_params* params);
/**
* Creates, signs and encodes a Json-Web-Token. <p>
* An example output could be: <code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNvbWUta2V5LWlkLWhlcmUtMDEyMzQ1NiJ9.eyJpYXQiOjE1Nzk2NDUzNTUsImV4cCI6MTU3OTY0NTk1NSwic3ViIjoiR29yZG9uIEZyZWVtYW4iLCJpc3MiOiJCbGFjayBNZXNhIiwiYXVkIjoiQWRtaW5pc3RyYXRvciJ9.uk4EEoq0ql_SguLto5EWzklakpzO-6GE2U26crB8vUY</code> <p>
* @param params The token encoding parameters (e.g. "alg", "iss", "exp", etc...).
* @return Return code as defined in retcodes.h
* @see l8w8jwt_encoding_params
*/
L8W8JWT_API int l8w8jwt_encode(struct l8w8jwt_encoding_params* params);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // L8W8JWT_ENCODE_H

107
r5dev/thirdparty/jwt/include/retcodes.h vendored Normal file
View File

@ -0,0 +1,107 @@
/*
Copyright 2020 Raphael Beck
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file retcodes.h
* @author Raphael Beck
* @brief Macros for possible integer codes returned by the various l8w8jwt functions.
*/
#ifndef L8W8JWT_RETCODES_H
#define L8W8JWT_RETCODES_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* Returned from a l8w8jwt function when everything went smooth 'n' chill. Time to get Schwifty, Morteyy!
*/
#define L8W8JWT_SUCCESS 0
/**
* Error code returned by a l8w8jwt function if you passed a NULL argument that shouldn't have been NULL.
*/
#define L8W8JWT_NULL_ARG 100
/**
* This error code is returned by a l8w8jwt function if you passed an invalid parameter into it.
*/
#define L8W8JWT_INVALID_ARG 200
/**
* This is returned if some allocation inside a l8w8jwt function failed: you're out of memory at this point.
*/
#define L8W8JWT_OUT_OF_MEM 300
/**
* Not good...
*/
#define L8W8JWT_OVERFLOW 310
/**
* Returned if signing a JWT failed.
*/
#define L8W8JWT_SIGNATURE_CREATION_FAILURE 400
/**
* If one of the SHA-2 functions fails (e.g. SHA-256).
*/
#define L8W8JWT_SHA2_FAILURE 410
/**
* Returned if some PEM-formatted key string couldn't be parsed.
*/
#define L8W8JWT_KEY_PARSE_FAILURE 420
/**
* Base64(URL) encoding or decoding error.
*/
#define L8W8JWT_BASE64_FAILURE 425
/**
* Returned if you passed the wrong private or public key type (e.g. trying to use an RSA key for ECDSA tokens, etc...). <p>
* Especially for the ECDSA algorithms like ES256, ES384 and ES512 double-check that you passed keys of the correct curve! <p>
* Only use the P-256 curve for ES256, P-384 (a.k.a. secp384r1) for ES384 and P-521 (a.k.a. secp521r1) for ES512.
*/
#define L8W8JWT_WRONG_KEY_TYPE 450
/**
* When the <code>mbedtls_ctr_drbg_seed()</code> function fails...
*/
#define L8W8JWT_MBEDTLS_CTR_DRBG_SEED_FAILURE 500
/**
* Returned if the token is invalid (format-wise).
*/
#define L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT 600
/**
* Returned if the token is invalid because it's missing the signature (despite having specified an alg that isn't "none").
*/
#define L8W8JWT_DECODE_FAILED_MISSING_SIGNATURE 700
/**
* Returned if the JWT signing alg parameter that was passed is not supported (e.g. the used l8w8jwt library was built without support for that algo, e.g. Ed25519).
* See the README.md for more details!
*/
#define L8W8JWT_UNSUPPORTED_ALG 800
#ifdef __cplusplus
} // extern "C"
#endif
#endif // L8W8JWT_RETCODES_H

58
r5dev/thirdparty/jwt/include/util.h vendored Normal file
View File

@ -0,0 +1,58 @@
/*
Copyright 2020 Raphael Beck
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file util.h
* @author Raphael Beck
* @brief Useful utility functions.
*/
#ifndef L8W8JWT_UTIL_H
#define L8W8JWT_UTIL_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include "version.h"
/**
* Converts a hex-encoded string to a binary array. <p>
* A NUL-terminator is appended at the end of the output buffer, so make sure to allocate at least <c>(hexstr_length / 2) + 1</c> bytes!
* @param hexstr The hex string to convert.
* @param hexstr_length Length of the \p hexstr
* @param output Where to write the converted binary data into.
* @param output_size Size of the output buffer (make sure to allocate at least <c>(hexstr_length / 2) + 1</c> bytes!).
* @param output_length [OPTIONAL] Where to write the output array length into. This is always gonna be <c>hexstr_length / 2</c>, but you can still choose to write it out just to be sure. If you want to omit this: no problem.. just pass <c>NULL</c>!
* @return <c>0</c> if conversion succeeded. <c>1</c> if one or more required arguments were <c>NULL</c> or invalid. <c>2</c> if the hexadecimal string is in an invalid format (e.g. not divisible by 2). <c>3</c> if output buffer size was insufficient (needs to be at least <c>(hexstr_length / 2) + 1</c> bytes).
*/
L8W8JWT_API int l8w8jwt_hexstr2bin(const char* hexstr, size_t hexstr_length, unsigned char* output, size_t output_size, size_t* output_length);
/**
* Compares two strings ignoring UPPER vs. lowercase.
* @param str1 String to compare.
* @param str2 String to compare to.
* @param n How many characters of the string should be compared (starting from index 0)?
* @return If the strings are equal, <code>0</code> is returned. Otherwise, something else.
*/
L8W8JWT_API int l8w8jwt_strncmpic(const char* str1, const char* str2, size_t n);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // L8W8JWT_UTIL_H

87
r5dev/thirdparty/jwt/include/version.h vendored Normal file
View File

@ -0,0 +1,87 @@
/*
Copyright 2020 Raphael Beck
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file version.h
* @author Raphael Beck
* @brief l8w8jwt version checking.
*/
#ifndef L8W8JWT_VERSION_H
#define L8W8JWT_VERSION_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* Current l8w8jwt version number.
*/
#define L8W8JWT_VERSION 220
/**
* Current l8w8jwt version number (as a human-readable string).
*/
#define L8W8JWT_VERSION_STR "2.2.0"
#if defined(_WIN32) && defined(L8W8JWT_DLL)
#ifdef L8W8JWT_BUILD_DLL
#define L8W8JWT_API __declspec(dllexport)
#else
#define L8W8JWT_API __declspec(dllimport)
#endif
#else
#define L8W8JWT_API
#endif
#ifndef L8W8JWT_SMALL_STACK
/**
* Set this pre-processor definition to \c 1 if you're using this
* on a low-memory device with increased risk of stack overflow.
*/
#define L8W8JWT_SMALL_STACK 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)
*/
L8W8JWT_API int l8w8jwt_get_version_number(void);
/**
* Gets the l8w8jwt version number as a nicely formatted string.
* @param out A writable \c char buffer of at least 32B where to write the version number string into. The string will be NUL-terminated, no worries! Passing \c NULL here is a very bad idea. Undefined, unpleasant, and just... just don't!
*/
L8W8JWT_API void l8w8jwt_get_version_string(char out[32]);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // L8W8JWT_VERSION_H

87
r5dev/thirdparty/jwt/util.c vendored Normal file
View File

@ -0,0 +1,87 @@
/*
Copyright 2020 Raphael Beck
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "include/util.h"
#include <ctype.h>
#ifdef __cplusplus
extern "C" {
#endif
int l8w8jwt_hexstr2bin(const char* hexstr, const size_t hexstr_length, unsigned char* output, const size_t output_size, size_t* output_length)
{
if (hexstr == NULL || output == NULL || hexstr_length == 0)
{
return 1;
}
const size_t hl = hexstr[hexstr_length - 1] ? hexstr_length : hexstr_length - 1;
if (hl % 2 != 0)
{
return 2;
}
const size_t final_length = hl / 2;
if (output_size < final_length + 1)
{
return 3;
}
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;
}
output[final_length] = '\0';
if (output_length != NULL)
{
*output_length = final_length;
}
return 0;
}
int l8w8jwt_strncmpic(const char* str1, const char* str2, size_t n)
{
size_t cmp = 0;
int ret = -1;
if (str1 == NULL || str2 == NULL)
{
return ret;
}
while ((*str1 || *str2) && cmp < n)
{
if ((ret = tolower((int)(*str1)) - tolower((int)(*str2))) != 0)
{
break;
}
cmp++;
str1++;
str2++;
}
return ret;
}
#ifdef __cplusplus
} // extern "C"
#endif

73
r5dev/thirdparty/jwt/version.c vendored Normal file
View File

@ -0,0 +1,73 @@
/*
Copyright 2020 Raphael Beck
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "include/version.h"
#include <stdlib.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
void l8w8jwt_free(void* mem)
{
free(mem);
}
void l8w8jwt_zero(void* buf, size_t len)
{
MBEDTLS_INTERNAL_VALIDATE(len == 0 || buf != NULL);
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;
}
void l8w8jwt_get_version_string(char out[32])
{
const char version_string[] = L8W8JWT_VERSION_STR;
const size_t version_string_length = sizeof(version_string) - 1;
memcpy(out, version_string, version_string_length);
out[version_string_length] = '\0';
}
#ifdef __cplusplus
} // extern "C"
#endif