Add secure random number generation

This commit is contained in:
2025-08-17 11:57:01 +02:00
parent 2196ef06eb
commit 504c4582e1
5 changed files with 153 additions and 41 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
EXT = .exe EXT = .exe
LFLAGS = -lws2_32 LFLAGS = -lws2_32 -lbcrypt
else else
EXT = .out EXT = .out
LFLAGS = LFLAGS =
+5 -5
View File
@@ -2,6 +2,7 @@
#include <string.h> #include <string.h>
#include <crypt_blowfish.h> #include <crypt_blowfish.h>
#include "bcrypt.h" #include "bcrypt.h"
#include "random.h"
int hash_password(char *pass, int passlen, int cost, PasswordHash *hash) int hash_password(char *pass, int passlen, int cost, PasswordHash *hash)
{ {
@@ -11,13 +12,12 @@ int hash_password(char *pass, int passlen, int cost, PasswordHash *hash)
memcpy(passzt, pass, passlen); memcpy(passzt, pass, passlen);
passzt[passlen] = '\0'; passzt[passlen] = '\0';
unsigned char random[16] = { char random[16];
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, // TODO int ret = generate_random_bytes(random, (int) sizeof(random));
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, if (ret) return -1;
};
char salt[30]; char salt[30];
if (_crypt_gensalt_blowfish_rn("$2b$", cost, (char*) random, sizeof(random), salt, sizeof(salt)) == NULL) if (_crypt_gensalt_blowfish_rn("$2b$", cost, random, sizeof(random), salt, sizeof(salt)) == NULL)
return -1; return -1;
if (_crypt_blowfish_rn(passzt, salt, hash->data, (int) sizeof(hash->data)) == NULL) if (_crypt_blowfish_rn(passzt, salt, hash->data, (int) sizeof(hash->data)) == NULL)
+108 -34
View File
@@ -316,49 +316,125 @@ int http_getparami(HTTP_String body, HTTP_String str, WL_Arena *arena)
#define SESSION_LIMIT 1024 #define SESSION_LIMIT 1024
#define USERNAME_LIMIT 64 #define USERNAME_LIMIT 64
#define RAW_SESSION_TOKEN_SIZE 32
#define RAW_CSRF_TOKEN_SIZE 32
#define SESSION_TOKEN_SIZE (2 * RAW_SESSION_TOKEN_SIZE)
#define CSRF_TOKEN_SIZE (2 * RAW_CSRF_TOKEN_SIZE)
typedef struct { typedef struct {
int sess_id;
int user_id; int user_id;
char raw_sess_token[RAW_SESSION_TOKEN_SIZE];
char raw_csrf_token[RAW_CSRF_TOKEN_SIZE];
} Session; } Session;
typedef struct { typedef struct {
Session sessions[SESSION_LIMIT]; Session sessions[SESSION_LIMIT];
int count; int count;
int next_sess_id; // TODO: this should be choosen randomly and on 64 bits
} SessionSet; } SessionSet;
#include "random.h"
void session_set_init(SessionSet *set) void session_set_init(SessionSet *set)
{ {
set->count = 0; set->count = 0;
set->next_sess_id = 1;
} }
int session_set_add(SessionSet *set, int user_id) typedef struct {
char data[SESSION_TOKEN_SIZE];
} SessionToken;
typedef struct {
char data[CSRF_TOKEN_SIZE];
} CSRFToken;
static void unpack_token(char *src, int srclen, char *dst, int dstlen)
{
assert(srclen == 2 * dstlen);
for (int i = 0; i < srclen; i++) {
static const char table[] = "0123456789abcdef";
int low = (src[i] & 0x0F) >> 0;
int high = (src[i] & 0xF0) >> 4;
dst[(i << 1) | 0] = table[high];
dst[(i << 1) | 1] = table[low];
}
}
static int pack_token(char *src, int srclen, char *dst, int dstlen)
{
if (srclen & 1)
return -1;
assert(2 * srclen == dstlen);
for (int i = 0; i < srclen; i += 2) {
int high = src[i+0];
int low = src[i+1];
if (!is_hex_digit(high) || !is_hex_digit(low))
return -1;
dst[i] = (hex_digit_to_int(high) << 4) | (hex_digit_to_int(low) << 0);
}
return 0;
}
int session_set_add(SessionSet *set, int user_id, SessionToken *sess_token, CSRFToken *csrf_token)
{ {
if (set->count == SESSION_LIMIT) if (set->count == SESSION_LIMIT)
return -1; return -1;
int sess_id = set->next_sess_id++;
set->sessions[set->count++] = (Session) { Session *session = &set->sessions[set->count];
.sess_id=sess_id,
.user_id=user_id, int ret;
}; ret = generate_random_bytes(session->raw_sess_token, (int) sizeof(session->raw_sess_token));
return sess_id; if (ret) return -1;
ret = generate_random_bytes(session->raw_csrf_token, (int) sizeof(session->raw_csrf_token));
if (ret) return -1;
session->user_id = user_id;
unpack_token(
session->raw_csrf_token, (int) sizeof(session->raw_csrf_token),
csrf_token->data, (int) sizeof(csrf_token->data)
);
unpack_token(
session->raw_sess_token, (int) sizeof(session->raw_sess_token),
sess_token->data, (int) sizeof(sess_token->data)
);
set->count++;
return 0;
} }
void session_set_remove(SessionSet *set, int sess_id) void session_set_remove(SessionSet *set, SessionToken sess_token)
{ {
char raw_sess_token[RAW_SESSION_TOKEN_SIZE];
pack_token(
sess_token.data, (int) sizeof(sess_token.data),
raw_sess_token, (int) sizeof(raw_sess_token)
);
int i = 0; int i = 0;
while (i < set->count && set->sessions[i].sess_id != sess_id) while (i < set->count && memcmp(set->sessions[i].raw_sess_token, raw_sess_token, RAW_SESSION_TOKEN_SIZE))
i++; i++;
if (i == set->count) if (i == set->count)
return; return;
set->sessions[i] = set->sessions[--set->count]; set->sessions[i] = set->sessions[--set->count];
} }
int session_set_find(SessionSet *set, int sess_id) int session_set_find(SessionSet *set, SessionToken sess_token)
{ {
char raw_sess_token[RAW_SESSION_TOKEN_SIZE];
pack_token(
sess_token.data, (int) sizeof(sess_token.data),
raw_sess_token, (int) sizeof(raw_sess_token)
);
int i = 0; int i = 0;
while (i < set->count && set->sessions[i].sess_id != sess_id) while (i < set->count && memcmp(set->sessions[i].raw_sess_token, raw_sess_token, RAW_SESSION_TOKEN_SIZE))
i++; i++;
if (i == set->count) if (i == set->count)
return -1; return -1;
@@ -469,23 +545,19 @@ int main(void)
//printf("%.*s\n", req->raw.len, req->raw.ptr); // TODO //printf("%.*s\n", req->raw.len, req->raw.ptr); // TODO
// If logged in, these are set to non-negative values // If logged in, these are set to non-negative values
int sess_id = -1; SessionToken sess_token;
int user_id = -1; int user_id = -1;
{ {
HTTP_String str = http_getcookie(req, HTTP_STR("sess_id")); HTTP_String str = http_getcookie(req, HTTP_STR("sess_id"));
if (str.len > 0) { if (str.len == SESSION_TOKEN_SIZE) {
char tmp[RAW_SESSION_TOKEN_SIZE];
char tmp[1<<9]; // TODO int ret = pack_token(str.ptr, str.len, tmp, (int) sizeof(tmp));
memcpy(tmp, str.ptr, str.len); if (ret) {
tmp[str.len] = '\0'; memcpy(sess_token.data, str.ptr, str.len);
user_id = session_set_find(&sessions, sess_token);
sess_id = atoi(tmp); }
if (sess_id == 0)
sess_id = -1;
} }
user_id = session_set_find(&sessions, sess_id);
} }
HTTP_String path = req->url.path; HTTP_String path = req->url.path;
@@ -545,8 +617,9 @@ int main(void)
} }
int user_id = ret; int user_id = ret;
int sess_id = session_set_add(&sessions, user_id); CSRFToken csrf_token;
if (sess_id < 0) { SessionToken sess_token;
if (session_set_add(&sessions, user_id, &sess_token, &csrf_token) < 0) {
http_response_builder_status(builder, 200); http_response_builder_status(builder, 200);
http_response_builder_body(builder, HTML_STR(( http_response_builder_body(builder, HTML_STR((
<div class="error"> <div class="error">
@@ -558,7 +631,7 @@ int main(void)
} }
char cookie[1<<9]; char cookie[1<<9];
int cookie_len = snprintf(cookie, sizeof(cookie), "Set-Cookie: sess_id=%d; Path=/; HttpOnly", sess_id); int cookie_len = snprintf(cookie, sizeof(cookie), "Set-Cookie: sess_id=%.*s; Path=/; HttpOnly", (int) sizeof(sess_token.data)-1, sess_token.data);
if (cookie_len < 0 || cookie_len >= (int) sizeof(cookie)) { if (cookie_len < 0 || cookie_len >= (int) sizeof(cookie)) {
http_response_builder_status(builder, 200); http_response_builder_status(builder, 200);
http_response_builder_body(builder, HTML_STR(( http_response_builder_body(builder, HTML_STR((
@@ -646,8 +719,9 @@ int main(void)
} }
user_id = ret; user_id = ret;
int sess_id = session_set_add(&sessions, user_id); CSRFToken csrf_token;
if (sess_id < 0) { SessionToken sess_token;
if (session_set_add(&sessions, user_id, &sess_token, &csrf_token) < 0) {
http_response_builder_status(builder, 200); http_response_builder_status(builder, 200);
http_response_builder_body(builder, HTML_STR(( http_response_builder_body(builder, HTML_STR((
<div class="error"> <div class="error">
@@ -659,7 +733,7 @@ int main(void)
} }
char cookie[1<<9]; char cookie[1<<9];
int cookie_len = snprintf(cookie, sizeof(cookie), "Set-Cookie: sess_id=%d; Path=/; HttpOnly", sess_id); int cookie_len = snprintf(cookie, sizeof(cookie), "Set-Cookie: sess_id=%.*s; Path=/; HttpOnly", (int) sizeof(sess_token.data)-1, sess_token.data);
if (cookie_len < 0 || cookie_len >= (int) sizeof(cookie)) { if (cookie_len < 0 || cookie_len >= (int) sizeof(cookie)) {
http_response_builder_status(builder, 200); http_response_builder_status(builder, 200);
http_response_builder_body(builder, HTML_STR(( http_response_builder_body(builder, HTML_STR((
@@ -683,8 +757,8 @@ int main(void)
} else if (http_streq(path, HTTP_STR("/api/logout"))) { } else if (http_streq(path, HTTP_STR("/api/logout"))) {
if (sess_id != -1) if (user_id != -1)
session_set_remove(&sessions, sess_id); session_set_remove(&sessions, sess_token);
http_response_builder_status(builder, 303); // TODO: Whats the correct code here? http_response_builder_status(builder, 303); // TODO: Whats the correct code here?
http_response_builder_header(builder, HTTP_STR("Set-Cookie: sess_id=; Path=/; HttpOnly")); http_response_builder_header(builder, HTTP_STR("Set-Cookie: sess_id=; Path=/; HttpOnly"));
+32
View File
@@ -0,0 +1,32 @@
#ifdef __linux__
#include <errno.h>
#include <sys/random.h>
#endif
#ifdef _WIN32
#include <windows.h>
#endif
#include "random.h"
int generate_random_bytes(char *dst, int cap)
{
#ifdef __linux__
int copied = 0;
while (copied < cap) {
int ret = getrandom(dst, (size_t) cap, 0);
if (ret < 0) {
if (errno == EINTR)
continue;
return -1;
}
copied += ret;
}
return 0;
#endif
#ifdef _WIN32
NTSTATUS status = BCryptGenRandom(NULL, (unsigned char*) dst, (ULONG) cap, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
return BCRYPT_SUCCESS(status) ? 0 : -1;
#endif
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef RANDOM_INCLUDED
#define RANDOM_INCLUDED
int generate_random_bytes(char *dst, int cap);
#endif // RANDOM_INCLUDED