11#include <openssl/evp.h>
12#include <openssl/hmac.h>
20static const char base64_table[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
31static size_t base64_encode(
const uint8_t *input,
size_t input_len,
char *output,
size_t output_size) {
32 if (!input || !output || input_len == 0) {
37 size_t required_size = ((input_len + 2) / 3) * 4 + 1;
38 if (output_size < required_size) {
46 while (i + 2 < input_len) {
49 output[j++] = base64_table[(triple >> 18) & 0x3F];
50 output[j++] = base64_table[(triple >> 12) & 0x3F];
51 output[j++] = base64_table[(triple >> 6) & 0x3F];
52 output[j++] = base64_table[triple & 0x3F];
60 if (i + 1 < input_len) {
61 triple |= (
uint32_t)input[i + 1] << 8;
64 output[j++] = base64_table[(triple >> 18) & 0x3F];
65 output[j++] = base64_table[(triple >> 12) & 0x3F];
67 if (i + 1 < input_len) {
68 output[j++] = base64_table[(triple >> 6) & 0x3F];
91 uint8_t *output,
unsigned int *output_len) {
92 if (!data || !secret || !output || !output_len) {
97#if OPENSSL_VERSION_NUMBER >= 0x30000000L
98 EVP_MAC *mac = EVP_MAC_fetch(NULL,
"HMAC", NULL);
103 EVP_MAC_CTX *ctx = EVP_MAC_CTX_new(mac);
110 OSSL_PARAM params[] = {OSSL_PARAM_construct_utf8_string(
"digest",
"SHA1", 0), OSSL_PARAM_construct_end()};
112 if (EVP_MAC_init(ctx, secret, secret_len, params) != 1) {
113 EVP_MAC_CTX_free(ctx);
118 if (EVP_MAC_update(ctx, data, data_len) != 1) {
119 EVP_MAC_CTX_free(ctx);
125 if (EVP_MAC_final(ctx, output, &out_len, 20) != 1) {
126 EVP_MAC_CTX_free(ctx);
131 *output_len = (
unsigned int)out_len;
133 EVP_MAC_CTX_free(ctx);
137 unsigned char *result = HMAC(EVP_sha1(), secret, (
int)secret_len, data, data_len, output, output_len);
148 if (!
session_id || !secret || !out_credentials) {
152 if (validity_seconds == 0) {
157 time_t now = time(NULL);
158 time_t expires_at = now + (time_t)validity_seconds;
163 if (username_len < 0 || (
size_t)username_len >=
sizeof(out_credentials->
username)) {
169 unsigned int hmac_len = 0;
172 (
const uint8_t *)secret, strlen(secret), hmac_result, &hmac_len);
177 if (hmac_len != 20) {
178 return SET_ERRNO(
ERROR_CRYPTO,
"TURN credentials: unexpected HMAC length %u (expected 20)", hmac_len);
183 base64_encode(hmac_result, hmac_len, out_credentials->
password,
sizeof(out_credentials->
password));
184 if (encoded_len == 0) {
190 log_debug(
"Generated TURN credentials: username=%s, expires_at=%ld", out_credentials->
username, (
long)expires_at);
200 time_t now = time(NULL);
⚠️‼️ Error and/or exit() when things go bad.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
asciichat_error_t
Error and exit codes - unified status values (0-255)
#define log_debug(...)
Log a DEBUG message.
📝 Logging API with multiple log levels and terminal output control
TURN server credentials (username + password)
⏱️ High-precision timing utilities using sokol_time.h and uthash
bool turn_credentials_expired(const turn_credentials_t *credentials)
Check if TURN credentials have expired.
asciichat_error_t turn_generate_credentials(const char *session_id, const char *secret, uint32_t validity_seconds, turn_credentials_t *out_credentials)
Generate time-limited TURN credentials.
TURN server credential generation for WebRTC.