v0.3.5 - nip42 implemented

This commit is contained in:
Your Name
2025-09-13 08:49:09 -04:00
parent 1690b58c67
commit f3d6afead1
24 changed files with 3705 additions and 532 deletions

View File

@@ -1,3 +1,4 @@
#define _GNU_SOURCE
#include "config.h"
#include "default_config_event.h"
#include "../nostr_core_lib/nostr_core/nostr_core.h"
@@ -340,6 +341,83 @@ int get_config_bool(const char* key, int default_value) {
return default_value;
}
// ================================
// NIP-42 KIND-SPECIFIC AUTHENTICATION
// ================================
// Parse comma-separated kind list into array of integers
// Returns number of kinds parsed, or -1 on error
int parse_auth_required_kinds(const char* kinds_str, int* kinds_array, int max_kinds) {
if (!kinds_str || !kinds_array || max_kinds <= 0) {
return -1;
}
// Empty string means no kinds require auth
if (strlen(kinds_str) == 0) {
return 0;
}
char* str_copy = strdup(kinds_str);
if (!str_copy) {
return -1;
}
int count = 0;
char* token = strtok(str_copy, ",");
while (token && count < max_kinds) {
// Trim whitespace
while (*token == ' ' || *token == '\t') token++;
char* end = token + strlen(token) - 1;
while (end > token && (*end == ' ' || *end == '\t')) end--;
*(end + 1) = '\0';
// Convert to integer
char* endptr;
long kind = strtol(token, &endptr, 10);
if (endptr != token && *endptr == '\0' && kind >= 0 && kind <= 65535) {
kinds_array[count] = (int)kind;
count++;
}
token = strtok(NULL, ",");
}
free(str_copy);
return count;
}
// Check if a specific event kind requires NIP-42 authentication
int is_nip42_auth_required_for_kind(int event_kind) {
const char* kinds_str = get_config_value("nip42_auth_required_kinds");
if (!kinds_str) {
return 0; // No authentication required if setting is missing
}
// Parse the kinds list
int required_kinds[100]; // Support up to 100 different kinds
int count = parse_auth_required_kinds(kinds_str, required_kinds, 100);
if (count < 0) {
return 0; // Parse error, default to no auth required
}
// Check if event_kind is in the list
for (int i = 0; i < count; i++) {
if (required_kinds[i] == event_kind) {
return 1; // Authentication required for this kind
}
}
return 0; // Authentication not required for this kind
}
// Get NIP-42 global authentication requirement
int is_nip42_auth_globally_required(void) {
return get_config_bool("nip42_auth_required", 0);
}
// ================================
// FIRST-TIME STARTUP FUNCTIONS
// ================================
@@ -635,15 +713,35 @@ cJSON* create_default_config_event(const unsigned char* admin_privkey_bytes,
int first_time_startup_sequence(const cli_options_t* cli_options) {
log_info("Starting first-time startup sequence...");
// 1. Generate admin keypair using /dev/urandom + nostr_core_lib
// 1. Generate or use provided admin keypair
unsigned char admin_privkey_bytes[32];
char admin_privkey[65], admin_pubkey[65];
if (generate_random_private_key_bytes(admin_privkey_bytes) != 0) {
log_error("Failed to generate admin private key");
return -1;
if (cli_options && strlen(cli_options->admin_privkey_override) == 64) {
// Use provided admin private key
log_info("Using provided admin private key override");
strncpy(admin_privkey, cli_options->admin_privkey_override, sizeof(admin_privkey) - 1);
admin_privkey[sizeof(admin_privkey) - 1] = '\0';
// Convert hex string to bytes
if (nostr_hex_to_bytes(admin_privkey, admin_privkey_bytes, 32) != NOSTR_SUCCESS) {
log_error("Failed to convert admin private key hex to bytes");
return -1;
}
// Validate the private key
if (nostr_ec_private_key_verify(admin_privkey_bytes) != NOSTR_SUCCESS) {
log_error("Provided admin private key is invalid");
return -1;
}
} else {
// Generate random admin keypair using /dev/urandom + nostr_core_lib
if (generate_random_private_key_bytes(admin_privkey_bytes) != 0) {
log_error("Failed to generate admin private key");
return -1;
}
nostr_bytes_to_hex(admin_privkey_bytes, 32, admin_privkey);
}
nostr_bytes_to_hex(admin_privkey_bytes, 32, admin_privkey);
unsigned char admin_pubkey_bytes[32];
if (nostr_ec_public_key_from_private_key(admin_privkey_bytes, admin_pubkey_bytes) != NOSTR_SUCCESS) {
@@ -652,15 +750,35 @@ int first_time_startup_sequence(const cli_options_t* cli_options) {
}
nostr_bytes_to_hex(admin_pubkey_bytes, 32, admin_pubkey);
// 2. Generate relay keypair using /dev/urandom + nostr_core_lib
// 2. Generate or use provided relay keypair
unsigned char relay_privkey_bytes[32];
char relay_privkey[65], relay_pubkey[65];
if (generate_random_private_key_bytes(relay_privkey_bytes) != 0) {
log_error("Failed to generate relay private key");
return -1;
if (cli_options && strlen(cli_options->relay_privkey_override) == 64) {
// Use provided relay private key
log_info("Using provided relay private key override");
strncpy(relay_privkey, cli_options->relay_privkey_override, sizeof(relay_privkey) - 1);
relay_privkey[sizeof(relay_privkey) - 1] = '\0';
// Convert hex string to bytes
if (nostr_hex_to_bytes(relay_privkey, relay_privkey_bytes, 32) != NOSTR_SUCCESS) {
log_error("Failed to convert relay private key hex to bytes");
return -1;
}
// Validate the private key
if (nostr_ec_private_key_verify(relay_privkey_bytes) != NOSTR_SUCCESS) {
log_error("Provided relay private key is invalid");
return -1;
}
} else {
// Generate random relay keypair using /dev/urandom + nostr_core_lib
if (generate_random_private_key_bytes(relay_privkey_bytes) != 0) {
log_error("Failed to generate relay private key");
return -1;
}
nostr_bytes_to_hex(relay_privkey_bytes, 32, relay_privkey);
}
nostr_bytes_to_hex(relay_privkey_bytes, 32, relay_privkey);
unsigned char relay_pubkey_bytes[32];
if (nostr_ec_public_key_from_private_key(relay_privkey_bytes, relay_pubkey_bytes) != NOSTR_SUCCESS) {
@@ -721,7 +839,6 @@ int first_time_startup_sequence(const cli_options_t* cli_options) {
printf("=================================================================\n");
printf("Admin Private Key: %s\n", admin_privkey);
printf("Admin Public Key: %s\n", admin_pubkey);
printf("\nRelay Private Key: %s\n", relay_privkey);
printf("Relay Public Key: %s\n", relay_pubkey);
printf("\nDatabase: %s\n", g_database_path);
printf("\nThis admin private key is needed to update configuration!\n");
@@ -1055,6 +1172,95 @@ static int validate_config_field(const char* key, const char* value, char* error
return 0;
}
// NIP-42 Authentication fields
if (strcmp(key, "nip42_auth_required") == 0) {
if (!is_valid_boolean(value)) {
snprintf(error_msg, error_size, "invalid boolean value '%s' for nip42_auth_required", value);
return -1;
}
return 0;
}
if (strcmp(key, "nip42_auth_required_kinds") == 0) {
// Validate comma-separated list of kind numbers
if (!value || strlen(value) == 0) {
return 0; // Empty list is valid
}
char* value_copy = strdup(value);
if (!value_copy) {
snprintf(error_msg, error_size, "memory allocation failed for kind validation");
return -1;
}
char* token = strtok(value_copy, ",");
while (token) {
// Trim whitespace
while (*token == ' ' || *token == '\t') token++;
char* end = token + strlen(token) - 1;
while (end > token && (*end == ' ' || *end == '\t')) end--;
*(end + 1) = '\0';
// Validate each kind number
if (!is_valid_non_negative_integer(token)) {
free(value_copy);
snprintf(error_msg, error_size, "invalid kind number '%s' in nip42_auth_required_kinds", token);
return -1;
}
int kind = atoi(token);
if (kind < 0 || kind > 65535) {
free(value_copy);
snprintf(error_msg, error_size, "kind number '%s' out of range (0-65535)", token);
return -1;
}
token = strtok(NULL, ",");
}
free(value_copy);
return 0;
}
if (strcmp(key, "nip42_challenge_expiration") == 0) {
if (!is_valid_positive_integer(value)) {
snprintf(error_msg, error_size, "invalid nip42_challenge_expiration '%s' (must be positive integer)", value);
return -1;
}
int val = atoi(value);
if (val < 60 || val > 3600) { // 1 minute to 1 hour
snprintf(error_msg, error_size, "nip42_challenge_expiration '%s' out of range (60-3600 seconds)", value);
return -1;
}
return 0;
}
if (strcmp(key, "nip42_challenge_window") == 0) {
if (!is_valid_positive_integer(value)) {
snprintf(error_msg, error_size, "invalid nip42_challenge_window '%s' (must be positive integer)", value);
return -1;
}
int val = atoi(value);
if (val < 300 || val > 7200) { // 5 minutes to 2 hours
snprintf(error_msg, error_size, "nip42_challenge_window '%s' out of range (300-7200 seconds)", value);
return -1;
}
return 0;
}
if (strcmp(key, "nip42_max_auth_events") == 0) {
if (!is_valid_positive_integer(value)) {
snprintf(error_msg, error_size, "invalid nip42_max_auth_events '%s' (must be positive integer)", value);
return -1;
}
int val = atoi(value);
if (val < 10 || val > 10000) { // 10 to 10,000 auth events
snprintf(error_msg, error_size, "nip42_max_auth_events '%s' out of range (10-10000)", value);
return -1;
}
return 0;
}
// Unknown field - log warning but allow
log_warning("Unknown configuration field");
printf(" Field: %s = %s\n", key, value);