v0.3.5 - nip42 implemented
This commit is contained in:
228
src/config.c
228
src/config.c
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user