5#define _POSIX_C_SOURCE 200809L
10#include <cjson/cJSON.h>
15#if !defined(CWIST_PLATFORM_HOSTED)
31#include <emscripten.h>
34#define CWIST_SESSION_DEFAULT_NAME "cwist_session"
35#define CWIST_SESSION_DEFAULT_MAX_AGE 86400
49 var hex =
"0123456789abcdef";
74static const char b64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
83static char *session_base64_encode(
const uint8_t *data,
size_t len) {
84 size_t out_len = 4 * ((len + 2) / 3);
86 if (!out)
return NULL;
87 for (
size_t i = 0,
j = 0;
i < len;
i += 3,
j += 4) {
91 out[
j] = b64[(
v >> 18) & 0x3F];
92 out[
j + 1] = b64[(
v >> 12) & 0x3F];
93 out[
j + 2] = (
i + 1 < len) ? b64[(
v >> 6) & 0x3F] :
'=';
94 out[
j + 3] = (
i + 2 < len) ? b64[
v & 0x3F] :
'=';
109static int base64_decode(
const char *
in,
uint8_t *out,
size_t out_len) {
111 if (
in_len % 4 != 0)
return -1;
116 if (len > out_len)
return -1;
120 for (
int i = 0;
i < 64;
i++)
decode[(
unsigned char)b64[
i]] =
i;
128 if (
a < 0 || b < 0)
return -1;
132 out[
j++] = (
v >> 16) & 0xFF;
133 if (
c >= 0 &&
in[
i + 2] !=
'=') out[
j++] = (
v >> 8) & 0xFF;
134 if (
d >= 0 &&
in[
i + 3] !=
'=') out[
j++] =
v & 0xFF;
150static bool session_hmac_sha256(
const char *key,
size_t key_len,
const char *
msg,
size_t msg_len,
152#if !defined(CWIST_PLATFORM_HOSTED)
155 unsigned int len = 32;
158 return r !=
NULL && len == 32;
173static char *generate_secret(
size_t len) {
187 static const char hex[] =
"0123456789abcdef";
188 for (
size_t i = 0;
i < len;
i++) {
201 static const char hex[] =
"0123456789abcdef";
202 for (
size_t i = 0;
i < len;
i++) {
210#ifdef CWIST_BAREMETAL
213 static const char hex[] =
"0123456789abcdef";
214 for (
size_t i = 0;
i < len;
i++) {
260 if (!app || !name)
return;
275 if (!app ||
seconds <= 0)
return;
292static void add_to_json(
const char *key,
const char *value,
void *ctx) {
293 json_ctx_t *
jctx = ctx;
310 if (base64_decode(
sig_b64,
sig,
sizeof(
sig)) != 32)
return false;
316#if !defined(CWIST_PLATFORM_HOSTED)
318 unsigned int diff = 0;
335 int len = base64_decode(
payload_b64, payload,
sizeof(payload) - 1);
336 if (len < 0)
return NULL;
339 if (!json)
return NULL;
368 if (!app || !req || !res)
return NULL;
372 if (!session)
return NULL;
376 session->modified =
false;
377 session->invalidated =
false;
378 session->loaded =
false;
391 if (verify_signature(app, copy,
dot + 1)) {
393 session->data = parse_payload(copy);
395 session->loaded =
true;
415 if (!session || !session->data || !key)
return NULL;
429 if (!session || !key)
return -1;
430 if (!session->data)
return -1;
432 session->modified =
true;
444 if (!session || !session->data || !key)
return;
446 session->modified =
true;
455 if (!session)
return;
456 session->invalidated =
true;
457 session->modified =
true;
465 if (!session)
return;
472 cwist_http_async_disown_map(session->data);
478 if (!session || !res)
return -1;
479 if (!session->modified && session->loaded)
return 0;
481 if (session->invalidated) {
488 if (!ctx.json)
return -1;
499 if (!session_hmac_sha256(session->app->session_secret,
strlen(session->app->session_secret),
561 return cwist_mw_session_handler;
Memory allocation wrappers backed by libttak.
void cwist_free(void *ptr)
Free memory obtained via cwist_alloc/cwist_strdup/etc.
char * cwist_strdup(const char *src)
Duplicate a string using libttak-backed storage.
#define CWIST_DEFER_FREE
Attach to a pointer variable's declaration to cwist_free() it automatically when that variable's encl...
void * cwist_alloc(size_t size)
Allocate zeroed memory tracked by libttak.
HTTP cookie parsing and construction helpers.
const char * cwist_cookie_get(cwist_query_map *map, const char *name)
Get a cookie value by name from a parsed map.
int cwist_cookie_set(cwist_http_response *res, const char *name, const char *value, const cwist_cookie_options *opts)
Add a Set-Cookie header to the response.
int cwist_cookie_delete(cwist_http_response *res, const char *name)
Add a Set-Cookie header that deletes a cookie.
void cwist_cookie_parse(cwist_query_map *map, const char *header)
Parse a Cookie header value into a query-style map.
#define CWIST_UNUSED(x)
Mark an intentionally unused variable to silence compiler warnings.
mbedTLS TLS backend: OPENSSL_cleanse / CRYPTO_memcmp.
mbedTLS TLS backend: EVP subset (HMAC digest tags, AES-256-CBC).
bool cwist_gc_scope_disown(void *ptr)
Remove ptr from the current thread's pending-sweep list without freeing it, because ownership is bein...
bool cwist_full_gc_enabled(void)
Check whether full-GC mode is currently enabled.
mbedTLS TLS backend: HMAC subset (HMAC-SHA256, one-shot + streaming).
char * cwist_http_header_get(cwist_http_header_node *head, const char *key)
Find a header value by key.
void cwist_query_map_delete(cwist_query_map *map, const char *key)
Delete a key from the map.
const char * cwist_query_map_get(cwist_query_map *map, const char *key)
Get a value by key.
void cwist_query_map_set(cwist_query_map *map, const char *key, const char *value)
Set a key-value pair.
void cwist_query_map_foreach(cwist_query_map *map, cwist_query_map_iter_func cb, void *ctx)
Iterate over all key-value pairs in the map.
cwist_query_map * cwist_query_map_create(void)
Create a new query map.
void cwist_query_map_destroy(cwist_query_map *map)
Destroy a query map.
Hooks a bare-metal firmware supplies to CWIST (issue #447).
int cwist_port_entropy(void *buf, size_t len)
Fill a buffer with cryptographically secure random bytes.
cwist_session_t * cwist_session_start(cwist_app *app, cwist_http_request *req, cwist_http_response *res)
Create a session for a request, loading data from the signed session cookie if present and valid....
void cwist_session_invalidate(cwist_session_t *session)
Invalidate the session: the session cookie is expired on the next commit. Marks the session as modifi...
void cwist_session_delete(cwist_session_t *session, const char *key)
Delete a key from the session. Marks the session as modified so the change is written back on commit....
#define CWIST_SESSION_DEFAULT_NAME
void cwist_app_set_session_max_age(cwist_app *app, int seconds)
Set the session cookie lifetime in seconds. No-op if app is NULL or seconds is not positive.
void cwist_app_set_session_name(cwist_app *app, const char *name)
Set the session cookie name for an app. Replaces any previously set name. No-op if app or name is NUL...
int cwist_app_use_session(cwist_app *app, const char *secret)
Enable signed session cookies for an app. Installs or replaces the app's session secret....
const char * cwist_session_get(cwist_session_t *session, const char *key)
Get a session value by key.
int cwist_session_set(cwist_session_t *session, const char *key, const char *value)
Set a session value; NULL value stores an empty string. Marks the session as modified so it is writte...
cwist_middleware_func cwist_mw_session(cwist_app *app)
Create the session middleware for an app. Ensures the app has a session secret, generating a random o...
#define CWIST_SESSION_DEFAULT_MAX_AGE
void cwist_session_destroy(cwist_session_t *session)
Free a session and its data. Safe to call with NULL.
void cwist_http_async_disown_session(cwist_session_t *session)
int cwist_session_commit(cwist_session_t *session, cwist_http_response *res)
Persist the session to the response cookie.
Signed client-side session cookie API.
struct cwist_session cwist_session_t
Opaque session handle attached to a request.
Minimal self-contained SHA-256 and HMAC-SHA256 (RFC 4634 style).
Main Application Context.
int session_max_age
Session cookie max-age in seconds.
char * session_secret
Session signing secret (HMAC-SHA256).
char * session_name
Session cookie name.
Options for setting a response cookie.
struct cwist_app * app
Owning app context (if any).
void * session
cwist_session_t pointer set by session middleware.
cwist_http_header_node * headers
HTTP Response Object. Supports standard string body or Zero-Copy pointer body.
void(* cwist_handler_func)(cwist_http_request *req, cwist_http_response *res)
Function pointer type for HTTP route handlers.
void(* cwist_middleware_func)(cwist_http_request *req, cwist_http_response *res, cwist_handler_func next)
Middleware type that receives req/res pair and the next stage in the chain.