#define _GNU_SOURCE ///////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////// // NIP-42 AUTHENTICATION FUNCTIONS ///////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////// #include #include "debug.h" #include #include #include #include #include #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", ] 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"); }