l8w8jwt (lightweight jawt)

repository·master·Indexed 41 days ago

https://github.com/glitchedpolygons/l8w8jwt

A minimal, OpenSSL-less JWT library written in C that relies on ARM's MbedTLS for cryptographic operations. It supports encoding and decoding JWT tokens using various algorithms including HMAC (HS256, HS384, HS512), RSA (RS, PS), ECDSA (ES256, ES384, ES512, ES256K), and optionally EdDSA. The library is designed for constrained environments, offering support for small stacks, custom memory allocators, and custom time APIs.

Tokens
3.5K
Snippets
10
Records
14
Agent score
2%

What's inside l8w8jwt

  1. Add l8w8jwt as a git submodule

    master

    The recommended way to use l8w8jwt is to add it as a git submodule within your project's dependency directory (e.g., lib/ or deps/).

    git submodule add https://github.com/GlitchedPolygons/l8w8jwt.git lib/l8w8jwt
    git submodule update --init --recursive
  2. Build l8w8jwt static library

    master

    To build a static library, use CMake with BUILD_SHARED_LIBS set to Off.

    Important: When linking against the static library, you must also link against the MbedTLS libraries. These are located in build/mbedtls/library/ after a successful build.

    mkdir -p build && cd build
    cmake -DBUILD_SHARED_LIBS=Off -DL8W8JWT_PACKAGE=On -DCMAKE_BUILD_TYPE=Release ..
    cmake --build . --config Release
  3. Enable EdDSA support

    master

    EdDSA (using the Ed25519 curve) is supported but disabled by default to minimize dependencies.

    To enable it:

    1. For Compilation: Define the pre-processor macro L8W8JWT_ENABLE_EDDSA (set to 1).
    2. For CMake users: Pass -DL8W8JWT_ENABLE_EDDSA=On to your CMake command.
    3. For Shared Libraries: You must build the library yourself. The pre-built binaries on the GitHub Releases page do not have EdDSA enabled.

    Key Generation: Use the lib/ed25519 library (included as a submodule) to generate keys. For signing, the private key must be in the Ref10 Ed25519 format (compatible with libsodium or NaCl).

  4. Generate PEM-formatted key pairs using OpenSSL

    master

    Although l8w8jwt is OpenSSL-less, you can use the OpenSSL CLI to generate PEM-formatted key pairs for use with the library. The following commands generate both a private.pem and a public.pem file in your current working directory.

    # ES256
    openssl ecparam -name prime256v1 -genkey -noout -out private.pem && openssl ec -in private.pem -pubout -out public.pem
    
    # ES384
    openssl ecparam -name secp384r1 -genkey -noout -out private.pem && openssl ec -in private.pem -pubout -out public.pem
    
    # ES512
    openssl ecparam -name secp521r1 -genkey -noout -out private.pem && openssl ec -in private.pem -pubout -out public.pem
    
    # ES256K
    openssl ecparam -name secp256k1 -genkey -noout -out private.pem && openssl ec -in private.pem -pubout -out public.pem
    
    # RS256, RS384, RS512, PS256, PS384, PS512
    openssl genrsa -out private.pem 4096 && openssl rsa -in private.pem -outform PEM -pubout -out public.pem
  5. Build l8w8jwt shared library (DLL)

    master

    You can build a shared library using the provided build.sh script. This works on Windows via Git Bash. The resulting .tar.gz file will be located in the build/ directory.

    Windows DLL Requirement: If using the shared library on Windows, you must define L8W8JWT_DLL to 1 before including any l8w8jwt headers to ensure correct __declspec(dllimport) declarations.

    bash build.sh
  6. Clone l8w8jwt repository

    master

    To clone the repository, use a recursive git clone to ensure all submodules are included. If you perform a non-recursive clone, you must run git submodule update --init --recursive manually afterward.

    git clone --recursive https://github.com/GlitchedPolygons/l8w8jwt.git
  7. Configure l8w8jwt for small stacks or custom time APIs

    master

    Use the following pre-processor definitions to adapt the library to specific hardware constraints:

    • Small Stack Support: Define L8W8JWT_SMALL_STACK to 1 for devices with very limited stack space.
    • Custom Time API: If your device does not support the standard C time API, define MBEDTLS_PLATFORM_TIME_ALT to 1. You must then provide an alternate time implementation via the l8w8jwt_time function pointer defined in include/l8w8jwt/timehelper.h.
  8. Use custom memory allocators

    master

    To provide your own implementation of malloc, calloc, or realloc, define the corresponding pre-processor macros and provide the implementation via the function pointers defined in include/l8w8jwt/util.h.

    MethodPre-processorFunction pointer
    mallocL8W8JWT_PLATFORM_MALLOC_ALTl8w8jwt_malloc
    callocL8W8JWT_PLATFORM_CALLOC_ALTl8w8jwt_calloc
    reallocL8W8JWT_PLATFORM_REALLOC_ALTl8w8jwt_realloc
  9. Encode and sign a JWT token

    master

    To create a JWT, use the l8w8jwt_encode function with a struct l8w8jwt_encoding_params object. You must initialize the params using l8w8jwt_encoding_params_init and provide the algorithm, secret key, and output pointers.

    Mandatory parameters for encoding:

    • alg: The algorithm to use (e.g., L8W8JWT_ALG_HS512).
    • secret_key: The HMAC secret or key.
    • secret_key_length: Length of the secret key.
    • out: Pointer to a char* where the resulting JWT string will be stored.
    • out_length: Pointer to a size_t to receive the length of the generated JWT.

    Important: You must manually free the generated JWT string using l8w8jwt_free(jwt) to avoid memory leaks.

    #include "l8w8jwt/encode.h"
    
    int main(void)
    {
        char* jwt;
        size_t jwt_length;
    
        struct l8w8jwt_encoding_params params;
        l8w8jwt_encoding_params_init(&params);
    
        params.alg = L8W8JWT_ALG_HS512;
    
        params.sub = "Gordon Freeman";
        params.iss = "Black Mesa";
        params.aud = "Administrator";
    
        params.iat = l8w8jwt_time(NULL);
        params.exp = l8w8jwt_time(NULL) + 600; /* Set to expire after 10 minutes (600 seconds). */
    
        params.secret_key = (unsigned char*)"YoUR sUpEr S3krEt 1337 HMAC kEy HeRE";
        params.secret_key_length = strlen(params.secret_key);
    
        params.out = &jwt;
        params.out_length = &jwt_length;
    
        int r = l8w8jwt_encode(&params);
    
        printf("\n l8w8jwt example HS512 token: %s \n", r == L8W8JWT_SUCCESS ? jwt : " (encoding failure) ");
    
        /* Always free the output jwt string! */
        l8w8jwt_free(jwt);
    
        return 0;
    }
  10. Decode and verify a JWT token

    master

    To decode and validate a JWT, use the l8w8jwt_decode function. This function returns a status code indicating if the parsing succeeded, while a separate enum l8w8jwt_validation_result variable provides details on signature verification and claim validation (like expiration).

    Mandatory parameters for decoding:

    • alg: The algorithm used by the token.
    • jwt: The JWT string to decode.
    • jwt_length: Length of the JWT string.
    • verification_key: The key used to verify the signature.
    • verification_key_length: Length of the verification key.

    Validation Options:

    • validate_iss: Validate the Issuer claim (use NUL-terminated strings).
    • validate_sub: Validate the Subject claim.
    • validate_exp: Validate the Expiration claim (set to 0 to disable).
    • exp_tolerance_seconds: Allowed clock skew for expiration.
    • validate_iat: Validate the Issued At claim.
    • iat_tolerance_seconds: Allowed clock skew for iat.

    Retrieving Claims: To access the claims within the token, pass an array of struct l8w8jwt_claim to the l8w8jwt_decode function instead of NULL. If you allocate this array on the heap, you are responsible for freeing it.

    #include "l8w8jwt/decode.h"
    
    static const char KEY[] = "YoUR sUpEr S3krEt 1337 HMAC kEy HeRE";
    static const char JWT[] = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1ODA5MzczMjksImV4cCI6MTU4MDkzNzkyOSwic3ViIjoiR29yZG9uIEZyZWVtYW4iLCJpc3MiOiJCbGFjayBNZXNhIiwiYXVkIjoiQWRtaW5pc3RyYXRvciJ9.7oNEgWxzs4nCtxOgiyTofP2bxZtL8dS7hgGXRPPDmwQWN1pjcwntsyK4Y5Cr9035Ro6Q16WOLiVAbj7k7TeCDA";
    
    int main(void)
    {
        struct l8w8jwt_decoding_params params;
        l8w8jwt_decoding_params_init(&params);
    
        params.alg = L8W8JWT_ALG_HS512;
    
        params.jwt = (char*)JWT;
        params.jwt_length = strlen(JWT);
    
        params.verification_key = (unsigned char*)KEY;
        params.verification_key_length = strlen(KEY);
    
        params.validate_iss = "Black Mesa"; 
        params.validate_sub = "Gordon Freeman";
    
        params.validate_exp = 0; 
        params.exp_tolerance_seconds = 60;
    
        params.validate_iat = 1;
        params.iat_tolerance_seconds = 60;
    
        enum l8w8jwt_validation_result validation_result;
    
        int decode_result = l8w8jwt_decode(&params, &validation_result, NULL, NULL);
    
        if (decode_result == L8W8JWT_SUCCESS && validation_result == L8W8JWT_VALID) 
        {
            printf("\n Example HS512 token validation successful! \n");
        }
        else
        {
            printf("\n Example HS512 token validation failed! \n");
        }
    
        return 0;
    }