151 lines
6.3 KiB
C
151 lines
6.3 KiB
C
#define _GNU_SOURCE
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
// NIP-42 AUTHENTICATION FUNCTIONS
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
#include <pthread.h>
|
|
#include "debug.h"
|
|
#include <cjson/cJSON.h>
|
|
#include <libwebsockets.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include "websockets.h"
|
|
|
|
|
|
// Forward declaration for notice message function
|
|
void send_notice_message(struct lws* wsi, const char* message);
|
|
|
|
// Forward declarations for NIP-42 functions from request_validator.c
|
|
int nostr_nip42_generate_challenge(char *challenge_buffer, size_t buffer_size);
|
|
int nostr_nip42_verify_auth_event(cJSON *event, const char *challenge_id,
|
|
const char *relay_url, int time_tolerance_seconds);
|
|
|
|
// Forward declaration for per_session_data struct (defined in websockets.h)
|
|
|
|
|
|
// Send NIP-42 authentication challenge to client
|
|
void send_nip42_auth_challenge(struct lws* wsi, struct per_session_data* pss) {
|
|
if (!wsi || !pss) return;
|
|
|
|
// Generate challenge using existing request_validator function
|
|
char challenge[65];
|
|
if (nostr_nip42_generate_challenge(challenge, sizeof(challenge)) != 0) {
|
|
DEBUG_ERROR("Failed to generate NIP-42 challenge");
|
|
send_notice_message(wsi, "Authentication temporarily unavailable");
|
|
return;
|
|
}
|
|
|
|
// Store challenge in session
|
|
pthread_mutex_lock(&pss->session_lock);
|
|
strncpy(pss->active_challenge, challenge, sizeof(pss->active_challenge) - 1);
|
|
pss->active_challenge[sizeof(pss->active_challenge) - 1] = '\0';
|
|
pss->challenge_created = time(NULL);
|
|
pss->challenge_expires = pss->challenge_created + 600; // 10 minutes
|
|
pss->auth_challenge_sent = 1;
|
|
pthread_mutex_unlock(&pss->session_lock);
|
|
|
|
// Send AUTH challenge message: ["AUTH", <challenge>]
|
|
cJSON* auth_msg = cJSON_CreateArray();
|
|
cJSON_AddItemToArray(auth_msg, cJSON_CreateString("AUTH"));
|
|
cJSON_AddItemToArray(auth_msg, cJSON_CreateString(challenge));
|
|
|
|
char* msg_str = cJSON_Print(auth_msg);
|
|
if (msg_str) {
|
|
size_t msg_len = strlen(msg_str);
|
|
// Use proper message queue system instead of direct lws_write
|
|
if (queue_message(wsi, pss, msg_str, msg_len, LWS_WRITE_TEXT) != 0) {
|
|
DEBUG_ERROR("Failed to queue AUTH challenge message");
|
|
}
|
|
free(msg_str);
|
|
}
|
|
cJSON_Delete(auth_msg);
|
|
}
|
|
|
|
// Handle NIP-42 signed authentication event from client
|
|
void handle_nip42_auth_signed_event(struct lws* wsi, struct per_session_data* pss, cJSON* auth_event) {
|
|
if (!wsi || !pss || !auth_event) return;
|
|
|
|
// Serialize event for validation
|
|
char* event_json = cJSON_Print(auth_event);
|
|
if (!event_json) {
|
|
send_notice_message(wsi, "Invalid authentication event format");
|
|
return;
|
|
}
|
|
|
|
pthread_mutex_lock(&pss->session_lock);
|
|
char challenge_copy[65];
|
|
strncpy(challenge_copy, pss->active_challenge, sizeof(challenge_copy) - 1);
|
|
challenge_copy[sizeof(challenge_copy) - 1] = '\0';
|
|
time_t challenge_expires = pss->challenge_expires;
|
|
pthread_mutex_unlock(&pss->session_lock);
|
|
|
|
// Check if challenge has expired
|
|
time_t current_time = time(NULL);
|
|
if (current_time > challenge_expires) {
|
|
free(event_json);
|
|
send_notice_message(wsi, "Authentication challenge expired, please retry");
|
|
DEBUG_WARN("NIP-42 authentication failed: challenge expired");
|
|
return;
|
|
}
|
|
|
|
// Verify authentication using existing request_validator function
|
|
// Note: nostr_nip42_verify_auth_event doesn't extract pubkey, we need to do that separately
|
|
int result = nostr_nip42_verify_auth_event(auth_event, challenge_copy,
|
|
"ws://localhost:8888", 600); // 10 minutes tolerance
|
|
|
|
char authenticated_pubkey[65] = {0};
|
|
if (result == 0) {
|
|
// Extract pubkey from the auth event
|
|
cJSON* pubkey_json = cJSON_GetObjectItem(auth_event, "pubkey");
|
|
if (pubkey_json && cJSON_IsString(pubkey_json)) {
|
|
const char* pubkey_str = cJSON_GetStringValue(pubkey_json);
|
|
if (pubkey_str && strlen(pubkey_str) == 64) {
|
|
strncpy(authenticated_pubkey, pubkey_str, sizeof(authenticated_pubkey) - 1);
|
|
authenticated_pubkey[sizeof(authenticated_pubkey) - 1] = '\0';
|
|
} else {
|
|
result = -1; // Invalid pubkey format
|
|
}
|
|
} else {
|
|
result = -1; // Missing pubkey
|
|
}
|
|
}
|
|
|
|
free(event_json);
|
|
|
|
if (result == 0) {
|
|
// Authentication successful
|
|
pthread_mutex_lock(&pss->session_lock);
|
|
pss->authenticated = 1;
|
|
strncpy(pss->authenticated_pubkey, authenticated_pubkey, sizeof(pss->authenticated_pubkey) - 1);
|
|
pss->authenticated_pubkey[sizeof(pss->authenticated_pubkey) - 1] = '\0';
|
|
// Clear challenge
|
|
memset(pss->active_challenge, 0, sizeof(pss->active_challenge));
|
|
pss->challenge_expires = 0;
|
|
pss->auth_challenge_sent = 0;
|
|
pthread_mutex_unlock(&pss->session_lock);
|
|
|
|
send_notice_message(wsi, "NIP-42 authentication successful");
|
|
} else {
|
|
// Authentication failed
|
|
char error_msg[256];
|
|
snprintf(error_msg, sizeof(error_msg),
|
|
"NIP-42 authentication failed (error code: %d)", result);
|
|
DEBUG_WARN(error_msg);
|
|
|
|
send_notice_message(wsi, "NIP-42 authentication failed - invalid signature or challenge");
|
|
}
|
|
}
|
|
|
|
// Handle challenge response (not typically used in NIP-42, but included for completeness)
|
|
void handle_nip42_auth_challenge_response(struct lws* wsi, struct per_session_data* pss, const char* challenge) {
|
|
(void)wsi; (void)pss; (void)challenge; // Mark as intentionally unused
|
|
|
|
// NIP-42 doesn't typically use challenge responses from client to server
|
|
// This is reserved for potential future use or protocol extensions
|
|
DEBUG_WARN("Received unexpected challenge response from client (not part of standard NIP-42 flow)");
|
|
send_notice_message(wsi, "Challenge responses are not supported - please send signed authentication event");
|
|
}
|