ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
turn_credentials.c
Go to the documentation of this file.
1
11#include <ascii-chat/common.h>
12#include <ascii-chat/log/log.h>
14#include <stdio.h>
15#include <string.h>
16#include <time.h>
17
21static const char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
22
32static size_t base64_encode(const uint8_t *input, size_t input_len, char *output, size_t output_size) {
33 if (!input || !output || input_len == 0) {
34 return 0;
35 }
36
37 // Calculate required output size: ((n + 2) / 3) * 4 + 1 for null terminator
38 size_t required_size = ((input_len + 2) / 3) * 4 + 1;
39 if (output_size < required_size) {
40 return 0;
41 }
42
43 size_t i = 0;
44 size_t j = 0;
45
46 // Process input in 3-byte chunks
47 while (i + 2 < input_len) {
48 uint32_t triple = ((uint32_t)input[i] << 16) | ((uint32_t)input[i + 1] << 8) | (uint32_t)input[i + 2];
49
50 output[j++] = base64_table[(triple >> 18) & 0x3F];
51 output[j++] = base64_table[(triple >> 12) & 0x3F];
52 output[j++] = base64_table[(triple >> 6) & 0x3F];
53 output[j++] = base64_table[triple & 0x3F];
54
55 i += 3;
56 }
57
58 // Handle remaining bytes (1 or 2)
59 if (i < input_len) {
60 uint32_t triple = (uint32_t)input[i] << 16;
61 if (i + 1 < input_len) {
62 triple |= (uint32_t)input[i + 1] << 8;
63 }
64
65 output[j++] = base64_table[(triple >> 18) & 0x3F];
66 output[j++] = base64_table[(triple >> 12) & 0x3F];
67
68 if (i + 1 < input_len) {
69 output[j++] = base64_table[(triple >> 6) & 0x3F];
70 } else {
71 output[j++] = '=';
72 }
73 output[j++] = '=';
74 }
75
76 output[j] = '\0';
77 return j;
78}
79
94static asciichat_error_t hmac_sha1(const uint8_t *data, size_t data_len, const uint8_t *secret, size_t secret_len,
95 uint8_t *output, unsigned int *output_len) {
96 if (!data || !secret || !output || !output_len) {
97 return SET_ERRNO(ERROR_INVALID_PARAM, "HMAC-SHA1: NULL parameter");
98 }
99
100 SHA1_CTX ctx;
103 uint8_t inner_hash[SHA1_DIGEST_LENGTH];
104
105 // Prepare key: pad or truncate to block length
107 memset(key, 0, sizeof(key));
108
109 if (secret_len > SHA1_BLOCK_LENGTH) {
110 // If key is longer than block size, hash it first
111 SHA1_CTX key_ctx;
112 SHA1Init(&key_ctx);
113 SHA1Update(&key_ctx, secret, secret_len);
114 SHA1Final(key, &key_ctx);
115 } else {
116 memcpy(key, secret, secret_len);
117 }
118
119 // Prepare ipad and opad (HMAC construction per RFC 2104)
120 for (int i = 0; i < SHA1_BLOCK_LENGTH; i++) {
121 ipad[i] = key[i] ^ 0x36;
122 opad[i] = key[i] ^ 0x5c;
123 }
124
125 // Compute inner hash: H(ipad || message)
126 SHA1Init(&ctx);
127 SHA1Update(&ctx, ipad, SHA1_BLOCK_LENGTH);
128 SHA1Update(&ctx, data, data_len);
129 SHA1Final(inner_hash, &ctx);
130
131 // Compute outer hash: H(opad || inner_hash)
132 SHA1Init(&ctx);
133 SHA1Update(&ctx, opad, SHA1_BLOCK_LENGTH);
134 SHA1Update(&ctx, inner_hash, SHA1_DIGEST_LENGTH);
135 SHA1Final(output, &ctx);
136
137 *output_len = SHA1_DIGEST_LENGTH;
138
139 return ASCIICHAT_OK;
140}
141
142asciichat_error_t turn_generate_credentials(const char *session_id, const char *secret, uint32_t validity_seconds,
143 turn_credentials_t *out_credentials) {
144 if (!session_id || !secret || !out_credentials) {
145 return SET_ERRNO(ERROR_INVALID_PARAM, "TURN credentials: NULL parameter");
146 }
147
148 if (validity_seconds == 0) {
149 return SET_ERRNO(ERROR_INVALID_PARAM, "TURN credentials: validity_seconds must be > 0");
150 }
151
152 // Calculate expiration timestamp
153 time_t now = time(NULL);
154 time_t expires_at = now + (time_t)validity_seconds;
155
156 // Format username: "{timestamp}:{session_id}"
157 int username_len = safe_snprintf(out_credentials->username, sizeof(out_credentials->username), "%ld:%s",
158 (long)expires_at, session_id);
159 if (username_len < 0 || (size_t)username_len >= sizeof(out_credentials->username)) {
160 return SET_ERRNO(ERROR_BUFFER_OVERFLOW, "TURN credentials: username too long");
161 }
162
163 // Compute HMAC-SHA1(secret, username)
164 uint8_t hmac_result[SHA1_DIGEST_LENGTH];
165 unsigned int hmac_len = 0;
166
167 asciichat_error_t result = hmac_sha1((const uint8_t *)out_credentials->username, (size_t)username_len,
168 (const uint8_t *)secret, strlen(secret), hmac_result, &hmac_len);
169 if (result != ASCIICHAT_OK) {
170 return result;
171 }
172
173 if (hmac_len != SHA1_DIGEST_LENGTH) {
174 return SET_ERRNO(ERROR_CRYPTO, "TURN credentials: unexpected HMAC length %u (expected %u)", hmac_len,
176 }
177
178 // Base64-encode the HMAC to get the password
179 size_t encoded_len =
180 base64_encode(hmac_result, hmac_len, out_credentials->password, sizeof(out_credentials->password));
181 if (encoded_len == 0) {
182 return SET_ERRNO(ERROR_BUFFER_OVERFLOW, "TURN credentials: password encoding failed");
183 }
184
185 out_credentials->expires_at = expires_at;
186
187 log_debug("Generated TURN credentials: username=%s, expires_at=%ld", out_credentials->username, (long)expires_at);
188
189 return ASCIICHAT_OK;
190}
191
193 if (!credentials) {
194 return true;
195 }
196
197 time_t now = time(NULL);
198 return now >= credentials->expires_at;
199}
⚠️‼️ Comprehensive thread-local error context system for ascii-chat
⚙️ Common definitions, error codes, macros, and types shared throughout the application
unsigned int uint32_t
Definition common.h:58
unsigned char uint8_t
Definition common.h:56
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_CRYPTO
Definition error_codes.h:96
@ ERROR_INVALID_PARAM
@ ERROR_BUFFER_OVERFLOW
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:148
📝 Logging API with multiple log levels and terminal output control
void SHA1Update(SHA1_CTX *context, const void *dataptr, unsigned int len)
Definition sha1.c:198
void SHA1Init(SHA1_CTX *context)
Definition sha1.c:186
void SHA1Final(unsigned char digest[SHA1_DIGEST_LENGTH], SHA1_CTX *context)
Definition sha1.c:219
#define SHA1_BLOCK_LENGTH
Definition sha1.h:16
#define SHA1_DIGEST_LENGTH
Definition sha1.h:17
uint8_t session_id[16]
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.