Compare commits
1 Commits
v0.4.8
...
5066ba8dd0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5066ba8dd0 |
3
build.sh
3
build.sh
@@ -530,7 +530,8 @@ for nip in $NEEDED_NIPS; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
# Build flags
|
# Build flags
|
||||||
CFLAGS="-Wall -Wextra -std=c99 -fPIC -O2"
|
# Disable fortification for MUSL compatibility (prevents __*_chk symbol issues)
|
||||||
|
CFLAGS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 -Wall -Wextra -std=c99 -fPIC -O2"
|
||||||
CFLAGS="$CFLAGS -DENABLE_FILE_LOGGING -DENABLE_WEBSOCKET_LOGGING -DENABLE_DEBUG_LOGGING"
|
CFLAGS="$CFLAGS -DENABLE_FILE_LOGGING -DENABLE_WEBSOCKET_LOGGING -DENABLE_DEBUG_LOGGING"
|
||||||
INCLUDES="-I. -Inostr_core -Inostr_core/crypto -Icjson -Inostr_websocket"
|
INCLUDES="-I. -Inostr_core -Inostr_core/crypto -Icjson -Inostr_websocket"
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// NIP-04 constants
|
// NIP-04 constants
|
||||||
// #define NOSTR_NIP04_MAX_PLAINTEXT_SIZE 1048576 // 1MB
|
// #define NOSTR_NIP04_MAX_PLAINTEXT_SIZE 65535
|
||||||
// NIP-04 Constants
|
// NIP-04 Constants
|
||||||
// #define NOSTR_NIP04_MAX_PLAINTEXT_SIZE 16777216 // 16MB
|
// #define NOSTR_NIP04_MAX_PLAINTEXT_SIZE 16777216 // 16MB
|
||||||
// #define NOSTR_NIP04_MAX_ENCRYPTED_SIZE 22369621 // ~21.3MB (accounts for base64 overhead + IV)
|
// #define NOSTR_NIP04_MAX_ENCRYPTED_SIZE 22369621 // ~21.3MB (accounts for base64 overhead + IV)
|
||||||
|
|||||||
@@ -258,12 +258,11 @@ cJSON* nostr_nip17_create_relay_list_event(const char** relay_urls,
|
|||||||
* NIP-17: Send a direct message to recipients
|
* NIP-17: Send a direct message to recipients
|
||||||
*/
|
*/
|
||||||
int nostr_nip17_send_dm(cJSON* dm_event,
|
int nostr_nip17_send_dm(cJSON* dm_event,
|
||||||
const char** recipient_pubkeys,
|
const char** recipient_pubkeys,
|
||||||
int num_recipients,
|
int num_recipients,
|
||||||
const unsigned char* sender_private_key,
|
const unsigned char* sender_private_key,
|
||||||
cJSON** gift_wraps_out,
|
cJSON** gift_wraps_out,
|
||||||
int max_gift_wraps,
|
int max_gift_wraps) {
|
||||||
long max_delay_sec) {
|
|
||||||
if (!dm_event || !recipient_pubkeys || num_recipients <= 0 ||
|
if (!dm_event || !recipient_pubkeys || num_recipients <= 0 ||
|
||||||
!sender_private_key || !gift_wraps_out || max_gift_wraps <= 0) {
|
!sender_private_key || !gift_wraps_out || max_gift_wraps <= 0) {
|
||||||
return -1;
|
return -1;
|
||||||
@@ -279,13 +278,13 @@ int nostr_nip17_send_dm(cJSON* dm_event,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create seal for this recipient
|
// Create seal for this recipient
|
||||||
cJSON* seal = nostr_nip59_create_seal(dm_event, sender_private_key, recipient_public_key, max_delay_sec);
|
cJSON* seal = nostr_nip59_create_seal(dm_event, sender_private_key, recipient_public_key);
|
||||||
if (!seal) {
|
if (!seal) {
|
||||||
continue; // Skip if sealing fails
|
continue; // Skip if sealing fails
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create gift wrap for this recipient
|
// Create gift wrap for this recipient
|
||||||
cJSON* gift_wrap = nostr_nip59_create_gift_wrap(seal, recipient_pubkeys[i], max_delay_sec);
|
cJSON* gift_wrap = nostr_nip59_create_gift_wrap(seal, recipient_pubkeys[i]);
|
||||||
cJSON_Delete(seal); // Seal is now wrapped
|
cJSON_Delete(seal); // Seal is now wrapped
|
||||||
|
|
||||||
if (!gift_wrap) {
|
if (!gift_wrap) {
|
||||||
@@ -304,10 +303,10 @@ int nostr_nip17_send_dm(cJSON* dm_event,
|
|||||||
nostr_bytes_to_hex(sender_public_key, 32, sender_pubkey_hex);
|
nostr_bytes_to_hex(sender_public_key, 32, sender_pubkey_hex);
|
||||||
|
|
||||||
// Create seal for sender
|
// Create seal for sender
|
||||||
cJSON* sender_seal = nostr_nip59_create_seal(dm_event, sender_private_key, sender_public_key, max_delay_sec);
|
cJSON* sender_seal = nostr_nip59_create_seal(dm_event, sender_private_key, sender_public_key);
|
||||||
if (sender_seal) {
|
if (sender_seal) {
|
||||||
// Create gift wrap for sender
|
// Create gift wrap for sender
|
||||||
cJSON* sender_gift_wrap = nostr_nip59_create_gift_wrap(sender_seal, sender_pubkey_hex, max_delay_sec);
|
cJSON* sender_gift_wrap = nostr_nip59_create_gift_wrap(sender_seal, sender_pubkey_hex);
|
||||||
cJSON_Delete(sender_seal);
|
cJSON_Delete(sender_seal);
|
||||||
|
|
||||||
if (sender_gift_wrap) {
|
if (sender_gift_wrap) {
|
||||||
|
|||||||
@@ -97,7 +97,6 @@ cJSON* nostr_nip17_create_relay_list_event(const char** relay_urls,
|
|||||||
* @param sender_private_key 32-byte sender private key
|
* @param sender_private_key 32-byte sender private key
|
||||||
* @param gift_wraps_out Array to store resulting gift wrap events (caller must free)
|
* @param gift_wraps_out Array to store resulting gift wrap events (caller must free)
|
||||||
* @param max_gift_wraps Maximum number of gift wraps to create
|
* @param max_gift_wraps Maximum number of gift wraps to create
|
||||||
* @param max_delay_sec Maximum random timestamp delay in seconds (0 = no randomization)
|
|
||||||
* @return Number of gift wrap events created, or -1 on error
|
* @return Number of gift wrap events created, or -1 on error
|
||||||
*/
|
*/
|
||||||
int nostr_nip17_send_dm(cJSON* dm_event,
|
int nostr_nip17_send_dm(cJSON* dm_event,
|
||||||
@@ -105,8 +104,7 @@ int nostr_nip17_send_dm(cJSON* dm_event,
|
|||||||
int num_recipients,
|
int num_recipients,
|
||||||
const unsigned char* sender_private_key,
|
const unsigned char* sender_private_key,
|
||||||
cJSON** gift_wraps_out,
|
cJSON** gift_wraps_out,
|
||||||
int max_gift_wraps,
|
int max_gift_wraps);
|
||||||
long max_delay_sec);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NIP-17: Receive and decrypt a direct message
|
* NIP-17: Receive and decrypt a direct message
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// NIP-44 constants
|
// NIP-44 constants
|
||||||
// #define NOSTR_NIP44_MAX_PLAINTEXT_SIZE 1048576
|
// #define NOSTR_NIP44_MAX_PLAINTEXT_SIZE 65535
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NIP-44: Encrypt a message using ECDH + ChaCha20 + HMAC
|
* NIP-44: Encrypt a message using ECDH + ChaCha20 + HMAC
|
||||||
|
|||||||
@@ -26,18 +26,12 @@ static void memory_clear(const void *p, size_t len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a random timestamp within max_delay_sec in the past (configurable)
|
* Create a random timestamp within 2 days in the past (as per NIP-59 spec)
|
||||||
*/
|
*/
|
||||||
static time_t random_past_timestamp(long max_delay_sec) {
|
static time_t random_past_timestamp(void) {
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
|
// Random time up to 2 days (172800 seconds) in the past
|
||||||
// If max_delay_sec is 0, return current timestamp (no randomization)
|
long random_offset = (long)(rand() % 172800);
|
||||||
if (max_delay_sec == 0) {
|
|
||||||
return now;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Random time up to max_delay_sec in the past
|
|
||||||
long random_offset = (long)(rand() % max_delay_sec);
|
|
||||||
return now - random_offset;
|
return now - random_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,8 +104,8 @@ cJSON* nostr_nip59_create_rumor(int kind, const char* content, cJSON* tags,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use provided timestamp or random past timestamp (default to 0 for compatibility)
|
// Use provided timestamp or random past timestamp
|
||||||
time_t event_time = (created_at == 0) ? random_past_timestamp(0) : created_at;
|
time_t event_time = (created_at == 0) ? random_past_timestamp() : created_at;
|
||||||
|
|
||||||
// Create event structure (without id and sig - that's what makes it a rumor)
|
// Create event structure (without id and sig - that's what makes it a rumor)
|
||||||
cJSON* rumor = cJSON_CreateObject();
|
cJSON* rumor = cJSON_CreateObject();
|
||||||
@@ -148,7 +142,7 @@ cJSON* nostr_nip59_create_rumor(int kind, const char* content, cJSON* tags,
|
|||||||
* NIP-59: Create a seal (kind 13) wrapping a rumor
|
* NIP-59: Create a seal (kind 13) wrapping a rumor
|
||||||
*/
|
*/
|
||||||
cJSON* nostr_nip59_create_seal(cJSON* rumor, const unsigned char* sender_private_key,
|
cJSON* nostr_nip59_create_seal(cJSON* rumor, const unsigned char* sender_private_key,
|
||||||
const unsigned char* recipient_public_key, long max_delay_sec) {
|
const unsigned char* recipient_public_key) {
|
||||||
if (!rumor || !sender_private_key || !recipient_public_key) {
|
if (!rumor || !sender_private_key || !recipient_public_key) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -184,7 +178,7 @@ cJSON* nostr_nip59_create_seal(cJSON* rumor, const unsigned char* sender_private
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
time_t seal_time = random_past_timestamp(max_delay_sec);
|
time_t seal_time = random_past_timestamp();
|
||||||
|
|
||||||
cJSON_AddStringToObject(seal, "pubkey", sender_pubkey_hex);
|
cJSON_AddStringToObject(seal, "pubkey", sender_pubkey_hex);
|
||||||
cJSON_AddNumberToObject(seal, "created_at", (double)seal_time);
|
cJSON_AddNumberToObject(seal, "created_at", (double)seal_time);
|
||||||
@@ -223,7 +217,7 @@ cJSON* nostr_nip59_create_seal(cJSON* rumor, const unsigned char* sender_private
|
|||||||
/**
|
/**
|
||||||
* NIP-59: Create a gift wrap (kind 1059) wrapping a seal
|
* NIP-59: Create a gift wrap (kind 1059) wrapping a seal
|
||||||
*/
|
*/
|
||||||
cJSON* nostr_nip59_create_gift_wrap(cJSON* seal, const char* recipient_public_key_hex, long max_delay_sec) {
|
cJSON* nostr_nip59_create_gift_wrap(cJSON* seal, const char* recipient_public_key_hex) {
|
||||||
if (!seal || !recipient_public_key_hex) {
|
if (!seal || !recipient_public_key_hex) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -278,7 +272,7 @@ cJSON* nostr_nip59_create_gift_wrap(cJSON* seal, const char* recipient_public_ke
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
time_t wrap_time = random_past_timestamp(max_delay_sec);
|
time_t wrap_time = random_past_timestamp();
|
||||||
|
|
||||||
cJSON_AddStringToObject(gift_wrap, "pubkey", random_pubkey_hex);
|
cJSON_AddStringToObject(gift_wrap, "pubkey", random_pubkey_hex);
|
||||||
cJSON_AddNumberToObject(gift_wrap, "created_at", (double)wrap_time);
|
cJSON_AddNumberToObject(gift_wrap, "created_at", (double)wrap_time);
|
||||||
|
|||||||
@@ -33,21 +33,19 @@ cJSON* nostr_nip59_create_rumor(int kind, const char* content, cJSON* tags,
|
|||||||
* @param rumor The rumor event to seal (cJSON object)
|
* @param rumor The rumor event to seal (cJSON object)
|
||||||
* @param sender_private_key 32-byte sender private key
|
* @param sender_private_key 32-byte sender private key
|
||||||
* @param recipient_public_key 32-byte recipient public key (x-only)
|
* @param recipient_public_key 32-byte recipient public key (x-only)
|
||||||
* @param max_delay_sec Maximum random timestamp delay in seconds (0 = no randomization)
|
|
||||||
* @return cJSON object representing the seal event, or NULL on error
|
* @return cJSON object representing the seal event, or NULL on error
|
||||||
*/
|
*/
|
||||||
cJSON* nostr_nip59_create_seal(cJSON* rumor, const unsigned char* sender_private_key,
|
cJSON* nostr_nip59_create_seal(cJSON* rumor, const unsigned char* sender_private_key,
|
||||||
const unsigned char* recipient_public_key, long max_delay_sec);
|
const unsigned char* recipient_public_key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NIP-59: Create a gift wrap (kind 1059) wrapping a seal
|
* NIP-59: Create a gift wrap (kind 1059) wrapping a seal
|
||||||
*
|
*
|
||||||
* @param seal The seal event to wrap (cJSON object)
|
* @param seal The seal event to wrap (cJSON object)
|
||||||
* @param recipient_public_key_hex Recipient's public key in hex format
|
* @param recipient_public_key_hex Recipient's public key in hex format
|
||||||
* @param max_delay_sec Maximum random timestamp delay in seconds (0 = no randomization)
|
|
||||||
* @return cJSON object representing the gift wrap event, or NULL on error
|
* @return cJSON object representing the gift wrap event, or NULL on error
|
||||||
*/
|
*/
|
||||||
cJSON* nostr_nip59_create_gift_wrap(cJSON* seal, const char* recipient_public_key_hex, long max_delay_sec);
|
cJSON* nostr_nip59_create_gift_wrap(cJSON* seal, const char* recipient_public_key_hex);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NIP-59: Unwrap a gift wrap to get the seal
|
* NIP-59: Unwrap a gift wrap to get the seal
|
||||||
|
|||||||
@@ -72,11 +72,11 @@
|
|||||||
#define NIP05_DEFAULT_TIMEOUT 10
|
#define NIP05_DEFAULT_TIMEOUT 10
|
||||||
|
|
||||||
// NIP-04 Constants
|
// NIP-04 Constants
|
||||||
#define NOSTR_NIP04_MAX_PLAINTEXT_SIZE 1048576 // 1MB
|
#define NOSTR_NIP04_MAX_PLAINTEXT_SIZE 16777216 // 16MB
|
||||||
#define NOSTR_NIP04_MAX_ENCRYPTED_SIZE 22369621 // ~21.3MB (accounts for base64 overhead + IV)
|
#define NOSTR_NIP04_MAX_ENCRYPTED_SIZE 22369621 // ~21.3MB (accounts for base64 overhead + IV)
|
||||||
|
|
||||||
// NIP-44 Constants
|
// NIP-44 Constants
|
||||||
#define NOSTR_NIP44_MAX_PLAINTEXT_SIZE 65535 // 64KB - 1 (NIP-44 spec compliant)
|
#define NOSTR_NIP44_MAX_PLAINTEXT_SIZE 65536 // 64KB max plaintext (matches crypto header)
|
||||||
|
|
||||||
// Forward declaration for cJSON (to avoid requiring cJSON.h in header)
|
// Forward declaration for cJSON (to avoid requiring cJSON.h in header)
|
||||||
struct cJSON;
|
struct cJSON;
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
#define NOSTR_CORE_H
|
#define NOSTR_CORE_H
|
||||||
|
|
||||||
// Version information (auto-updated by increment_and_push.sh)
|
// Version information (auto-updated by increment_and_push.sh)
|
||||||
#define VERSION "v0.4.8"
|
#define VERSION "v0.4.6"
|
||||||
#define VERSION_MAJOR 0
|
#define VERSION_MAJOR 0
|
||||||
#define VERSION_MINOR 4
|
#define VERSION_MINOR 4
|
||||||
#define VERSION_PATCH 8
|
#define VERSION_PATCH 6
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NOSTR Core Library - Complete API Reference
|
* NOSTR Core Library - Complete API Reference
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
BIN
tests/nip04_test
BIN
tests/nip04_test
Binary file not shown.
@@ -671,8 +671,8 @@ int test_vector_7_10kb_payload(void) {
|
|||||||
printf("Last 80 chars: \"...%.80s\"\n", encrypted + encrypted_len - 80);
|
printf("Last 80 chars: \"...%.80s\"\n", encrypted + encrypted_len - 80);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
// Test decryption with our ciphertext - allocate larger buffer for safety
|
// Test decryption with our ciphertext
|
||||||
char* decrypted = malloc(NOSTR_NIP04_MAX_PLAINTEXT_SIZE + 1024); // 1MB + 1KB extra
|
char* decrypted = malloc(NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
|
||||||
if (!decrypted) {
|
if (!decrypted) {
|
||||||
printf("❌ MEMORY ALLOCATION FAILED for decrypted buffer\n");
|
printf("❌ MEMORY ALLOCATION FAILED for decrypted buffer\n");
|
||||||
free(large_plaintext);
|
free(large_plaintext);
|
||||||
@@ -680,7 +680,7 @@ int test_vector_7_10kb_payload(void) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
printf("Testing decryption of 1MB ciphertext (Bob decrypts from Alice)...\n");
|
printf("Testing decryption of 1MB ciphertext (Bob decrypts from Alice)...\n");
|
||||||
result = nostr_nip04_decrypt(sk2, pk1, encrypted, decrypted, NOSTR_NIP04_MAX_PLAINTEXT_SIZE + 1024);
|
result = nostr_nip04_decrypt(sk2, pk1, encrypted, decrypted, NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
|
||||||
|
|
||||||
if (result != NOSTR_SUCCESS) {
|
if (result != NOSTR_SUCCESS) {
|
||||||
printf("❌ 1MB DECRYPTION FAILED: %s\n", nostr_strerror(result));
|
printf("❌ 1MB DECRYPTION FAILED: %s\n", nostr_strerror(result));
|
||||||
|
|||||||
BIN
tests/nip17_test
BIN
tests/nip17_test
Binary file not shown.
BIN
tests/nip44_test
BIN
tests/nip44_test
Binary file not shown.
@@ -20,7 +20,32 @@ typedef struct {
|
|||||||
const char* expected_encrypted; // Optional - for known test vectors
|
const char* expected_encrypted; // Optional - for known test vectors
|
||||||
} nip44_test_vector_t;
|
} nip44_test_vector_t;
|
||||||
|
|
||||||
// Additional test vectors for edge cases (converted to round-trip tests with new 32-bit padding)
|
// Known decryption-only test vectors from nostr-tools (for cross-compatibility testing)
|
||||||
|
// Note: NIP-44 encryption is non-deterministic - ciphertext varies each time
|
||||||
|
// These vectors test our ability to decrypt known good ciphertext from reference implementations
|
||||||
|
static nip44_test_vector_t decryption_test_vectors[] = {
|
||||||
|
{
|
||||||
|
"Decryption test: single char 'a'",
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001", // sec1
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002", // sec2
|
||||||
|
"a",
|
||||||
|
"AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABee0G5VSK0/9YypIObAtDKfYEAjD35uVkHyB0F4DwrcNaCXlCWZKaArsGrY6M9wnuTMxWfp1RTN9Xga8no+kF5Vsb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Decryption test: emoji",
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002", // sec1
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001", // sec2
|
||||||
|
"🍕🫃",
|
||||||
|
"AvAAAAAAAAAAAAAAAAAAAPAAAAAAAAAAAAAAAAAAAAAPSKSK6is9ngkX2+cSq85Th16oRTISAOfhStnixqZziKMDvB0QQzgFZdjLTPicCJaV8nDITO+QfaQ61+KbWQIOO2Yj"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Decryption test: wide unicode",
|
||||||
|
"5c0c523f52a5b6fad39ed2403092df8cebc36318b39383bca6c00808626fab3a", // sec1
|
||||||
|
"4b22aa260e4acb7021e32f38a6cdf4b673c6a277755bfce287e370c924dc936d", // sec2
|
||||||
|
"表ポあA鷗ŒéB逍Üߪąñ丂㐀𠀀",
|
||||||
|
"ArY1I2xC2yDwIbuNHN/1ynXdGgzHLqdCrXUPMwELJPc7s7JqlCMJBAIIjfkpHReBPXeoMCyuClwgbT419jUWU1PwaNl4FEQYKCDKVJz+97Mp3K+Q2YGa77B6gpxB/lr1QgoqpDf7wDVrDmOqGoiPjWDqy8KzLueKDcm9BVP8xeTJIxs="
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Round-trip test vectors with proper key pairs
|
// Round-trip test vectors with proper key pairs
|
||||||
static nip44_test_vector_t test_vectors[] = {
|
static nip44_test_vector_t test_vectors[] = {
|
||||||
@@ -44,13 +69,6 @@ static nip44_test_vector_t test_vectors[] = {
|
|||||||
"4444444444444444444444444444444444444444444444444444444444444444",
|
"4444444444444444444444444444444444444444444444444444444444444444",
|
||||||
"",
|
"",
|
||||||
NULL
|
NULL
|
||||||
},
|
|
||||||
{
|
|
||||||
"64KB payload test",
|
|
||||||
"91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe", // Same keys as basic test
|
|
||||||
"96f6fa197aa07477ab88f6981118466ae3a982faab8ad5db9d5426870c73d220",
|
|
||||||
NULL, // Will be generated dynamically
|
|
||||||
NULL
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -68,144 +86,76 @@ static int hex_to_bytes(const char* hex, unsigned char* bytes, size_t len) {
|
|||||||
|
|
||||||
static int test_nip44_round_trip(const nip44_test_vector_t* tv) {
|
static int test_nip44_round_trip(const nip44_test_vector_t* tv) {
|
||||||
printf("Test: %s\n", tv->name);
|
printf("Test: %s\n", tv->name);
|
||||||
|
|
||||||
// Parse keys - both private keys
|
// Parse keys - both private keys
|
||||||
unsigned char sender_private_key[32];
|
unsigned char sender_private_key[32];
|
||||||
unsigned char recipient_private_key[32];
|
unsigned char recipient_private_key[32];
|
||||||
|
|
||||||
if (hex_to_bytes(tv->sender_private_key_hex, sender_private_key, 32) != 0) {
|
if (hex_to_bytes(tv->sender_private_key_hex, sender_private_key, 32) != 0) {
|
||||||
printf(" FAIL: Failed to parse sender private key\n");
|
printf(" FAIL: Failed to parse sender private key\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hex_to_bytes(tv->recipient_private_key_hex, recipient_private_key, 32) != 0) {
|
if (hex_to_bytes(tv->recipient_private_key_hex, recipient_private_key, 32) != 0) {
|
||||||
printf(" FAIL: Failed to parse recipient private key\n");
|
printf(" FAIL: Failed to parse recipient private key\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate the public keys from the private keys
|
// Generate the public keys from the private keys
|
||||||
unsigned char sender_public_key[32];
|
unsigned char sender_public_key[32];
|
||||||
unsigned char recipient_public_key[32];
|
unsigned char recipient_public_key[32];
|
||||||
|
|
||||||
if (nostr_ec_public_key_from_private_key(sender_private_key, sender_public_key) != 0) {
|
if (nostr_ec_public_key_from_private_key(sender_private_key, sender_public_key) != 0) {
|
||||||
printf(" FAIL: Failed to derive sender public key\n");
|
printf(" FAIL: Failed to derive sender public key\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nostr_ec_public_key_from_private_key(recipient_private_key, recipient_public_key) != 0) {
|
if (nostr_ec_public_key_from_private_key(recipient_private_key, recipient_public_key) != 0) {
|
||||||
printf(" FAIL: Failed to derive recipient public key\n");
|
printf(" FAIL: Failed to derive recipient public key\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special handling for large payload tests
|
// Test encryption
|
||||||
char* test_plaintext;
|
char encrypted[8192];
|
||||||
if (strcmp(tv->name, "64KB payload test") == 0) {
|
int encrypt_result = nostr_nip44_encrypt(
|
||||||
// Generate exactly 64KB (65,535 bytes) of predictable content - max NIP-44 size
|
|
||||||
const size_t payload_size = 65535;
|
|
||||||
test_plaintext = malloc(payload_size + 1);
|
|
||||||
if (!test_plaintext) {
|
|
||||||
printf(" FAIL: Memory allocation failed for 64KB test payload\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill with a predictable pattern: "ABCDEFGH01234567" repeated
|
|
||||||
const char* pattern = "ABCDEFGH01234567"; // 16 bytes
|
|
||||||
const size_t pattern_len = 16;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < payload_size; i += pattern_len) {
|
|
||||||
size_t copy_len = (i + pattern_len <= payload_size) ? pattern_len : payload_size - i;
|
|
||||||
memcpy(test_plaintext + i, pattern, copy_len);
|
|
||||||
}
|
|
||||||
test_plaintext[payload_size] = '\0';
|
|
||||||
|
|
||||||
printf(" Generated 64KB test payload (%zu bytes)\n", payload_size);
|
|
||||||
printf(" Pattern: \"%s\" repeated\n", pattern);
|
|
||||||
printf(" First 64 chars: \"%.64s...\"\n", test_plaintext);
|
|
||||||
printf(" Last 64 chars: \"...%.64s\"\n", test_plaintext + payload_size - 64);
|
|
||||||
} else {
|
|
||||||
test_plaintext = (char*)tv->plaintext;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug: Check plaintext length
|
|
||||||
size_t plaintext_len = strlen(test_plaintext);
|
|
||||||
printf(" Plaintext length: %zu bytes\n", plaintext_len);
|
|
||||||
printf(" Output buffer size: %zu bytes\n", (size_t)10485760);
|
|
||||||
|
|
||||||
// Test encryption - use larger buffer for 1MB+ payloads (10MB for NIP-44 overhead)
|
|
||||||
char* encrypted = malloc(10485760); // 10MB buffer for large payloads
|
|
||||||
if (!encrypted) {
|
|
||||||
printf(" FAIL: Memory allocation failed for encrypted buffer\n");
|
|
||||||
if (strcmp(tv->name, "0.5MB payload test") == 0) free(test_plaintext);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For large payloads, use _with_nonce to avoid random generation issues
|
|
||||||
unsigned char fixed_nonce[32] = {0};
|
|
||||||
int encrypt_result = nostr_nip44_encrypt_with_nonce(
|
|
||||||
sender_private_key,
|
sender_private_key,
|
||||||
recipient_public_key,
|
recipient_public_key,
|
||||||
test_plaintext,
|
tv->plaintext,
|
||||||
fixed_nonce,
|
|
||||||
encrypted,
|
encrypted,
|
||||||
10485760
|
sizeof(encrypted)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (encrypt_result != NOSTR_SUCCESS) {
|
if (encrypt_result != NOSTR_SUCCESS) {
|
||||||
printf(" FAIL: Encryption - Expected: %d, Actual: %d\n", NOSTR_SUCCESS, encrypt_result);
|
printf(" FAIL: Encryption - Expected: %d, Actual: %d\n", NOSTR_SUCCESS, encrypt_result);
|
||||||
if (strcmp(tv->name, "1MB payload test") == 0) free(test_plaintext);
|
|
||||||
free(encrypted);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test decryption - use recipient private key + sender public key
|
// Test decryption - use recipient private key + sender public key
|
||||||
char* decrypted = malloc(65536 + 1); // 64KB + 1 for null terminator
|
char decrypted[8192];
|
||||||
if (!decrypted) {
|
|
||||||
printf(" FAIL: Memory allocation failed for decrypted buffer\n");
|
|
||||||
if (strcmp(tv->name, "64KB payload test") == 0) free(test_plaintext);
|
|
||||||
free(encrypted);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
int decrypt_result = nostr_nip44_decrypt(
|
int decrypt_result = nostr_nip44_decrypt(
|
||||||
recipient_private_key,
|
recipient_private_key,
|
||||||
sender_public_key,
|
sender_public_key,
|
||||||
encrypted,
|
encrypted,
|
||||||
decrypted,
|
decrypted,
|
||||||
65536 + 1
|
sizeof(decrypted)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (decrypt_result != NOSTR_SUCCESS) {
|
if (decrypt_result != NOSTR_SUCCESS) {
|
||||||
printf(" FAIL: Decryption - Expected: %d, Actual: %d\n", NOSTR_SUCCESS, decrypt_result);
|
printf(" FAIL: Decryption - Expected: %d, Actual: %d\n", NOSTR_SUCCESS, decrypt_result);
|
||||||
if (strcmp(tv->name, "1MB payload test") == 0) free(test_plaintext);
|
|
||||||
free(encrypted);
|
|
||||||
free(decrypted);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify round-trip
|
// Verify round-trip
|
||||||
if (strcmp(test_plaintext, decrypted) != 0) {
|
if (strcmp(tv->plaintext, decrypted) != 0) {
|
||||||
printf(" FAIL: Round-trip mismatch\n");
|
printf(" FAIL: Round-trip mismatch\n");
|
||||||
printf(" Expected: \"%s\"\n", test_plaintext);
|
printf(" Expected: \"%s\"\n", tv->plaintext);
|
||||||
printf(" Actual: \"%s\"\n", decrypted);
|
printf(" Actual: \"%s\"\n", decrypted);
|
||||||
if (strcmp(tv->name, "1MB payload test") == 0) free(test_plaintext);
|
|
||||||
free(encrypted);
|
|
||||||
free(decrypted);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(tv->name, "64KB payload test") == 0) {
|
printf(" PASS: Expected: \"%s\", Actual: \"%s\"\n", tv->plaintext, decrypted);
|
||||||
printf(" ✅ 64KB payload round-trip: PASS\n");
|
printf(" Encrypted output: %s\n", encrypted);
|
||||||
printf(" ✅ Content verification: All %zu bytes match perfectly!\n", strlen(test_plaintext));
|
|
||||||
printf(" Encrypted length: %zu bytes\n", strlen(encrypted));
|
|
||||||
printf(" 🎉 64KB NIP-44 STRESS TEST COMPLETED SUCCESSFULLY! 🎉\n");
|
|
||||||
} else {
|
|
||||||
printf(" PASS: Expected: \"%s\", Actual: \"%s\"\n", test_plaintext, decrypted);
|
|
||||||
printf(" Encrypted output: %s\n", encrypted);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(tv->name, "64KB payload test") == 0) free(test_plaintext);
|
|
||||||
free(encrypted);
|
|
||||||
free(decrypted);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,6 +215,59 @@ static int test_nip44_error_conditions() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int test_nip44_decryption_vector(const nip44_test_vector_t* tv) {
|
||||||
|
printf("Test: %s\n", tv->name);
|
||||||
|
|
||||||
|
// Parse keys
|
||||||
|
unsigned char sender_private_key[32];
|
||||||
|
unsigned char recipient_private_key[32];
|
||||||
|
|
||||||
|
if (hex_to_bytes(tv->sender_private_key_hex, sender_private_key, 32) != 0) {
|
||||||
|
printf(" FAIL: Failed to parse sender private key\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hex_to_bytes(tv->recipient_private_key_hex, recipient_private_key, 32) != 0) {
|
||||||
|
printf(" FAIL: Failed to parse recipient private key\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the public keys from the private keys
|
||||||
|
unsigned char sender_public_key[32];
|
||||||
|
|
||||||
|
if (nostr_ec_public_key_from_private_key(sender_private_key, sender_public_key) != 0) {
|
||||||
|
printf(" FAIL: Failed to derive sender public key\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test decryption of known vector
|
||||||
|
char decrypted[8192];
|
||||||
|
int decrypt_result = nostr_nip44_decrypt(
|
||||||
|
recipient_private_key,
|
||||||
|
sender_public_key,
|
||||||
|
tv->expected_encrypted,
|
||||||
|
decrypted,
|
||||||
|
sizeof(decrypted)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (decrypt_result != NOSTR_SUCCESS) {
|
||||||
|
printf(" FAIL: Decryption - Expected: %d, Actual: %d\n", NOSTR_SUCCESS, decrypt_result);
|
||||||
|
printf(" Input payload: %s\n", tv->expected_encrypted);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify decrypted plaintext matches expected
|
||||||
|
if (strcmp(tv->plaintext, decrypted) != 0) {
|
||||||
|
printf(" FAIL: Plaintext mismatch\n");
|
||||||
|
printf(" Expected: \"%s\"\n", tv->plaintext);
|
||||||
|
printf(" Actual: \"%s\"\n", decrypted);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(" PASS: Expected: \"%s\", Actual: \"%s\"\n", tv->plaintext, decrypted);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int test_nip44_encryption_variability() {
|
static int test_nip44_encryption_variability() {
|
||||||
printf("Test: NIP-44 encryption variability (non-deterministic)\n");
|
printf("Test: NIP-44 encryption variability (non-deterministic)\n");
|
||||||
@@ -284,20 +287,11 @@ static int test_nip44_encryption_variability() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt the same message multiple times
|
// Encrypt the same message multiple times
|
||||||
char* encrypted1 = malloc(2097152); // 2MB buffer
|
char encrypted1[8192], encrypted2[8192], encrypted3[8192];
|
||||||
char* encrypted2 = malloc(2097152);
|
|
||||||
char* encrypted3 = malloc(2097152);
|
|
||||||
if (!encrypted1 || !encrypted2 || !encrypted3) {
|
|
||||||
printf(" FAIL: Memory allocation failed for encrypted buffers\n");
|
|
||||||
free(encrypted1);
|
|
||||||
free(encrypted2);
|
|
||||||
free(encrypted3);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int result1 = nostr_nip44_encrypt(sender_key, recipient_pubkey, test_message, encrypted1, 2097152);
|
int result1 = nostr_nip44_encrypt(sender_key, recipient_pubkey, test_message, encrypted1, sizeof(encrypted1));
|
||||||
int result2 = nostr_nip44_encrypt(sender_key, recipient_pubkey, test_message, encrypted2, 2097152);
|
int result2 = nostr_nip44_encrypt(sender_key, recipient_pubkey, test_message, encrypted2, sizeof(encrypted2));
|
||||||
int result3 = nostr_nip44_encrypt(sender_key, recipient_pubkey, test_message, encrypted3, 2097152);
|
int result3 = nostr_nip44_encrypt(sender_key, recipient_pubkey, test_message, encrypted3, sizeof(encrypted3));
|
||||||
|
|
||||||
if (result1 != NOSTR_SUCCESS || result2 != NOSTR_SUCCESS || result3 != NOSTR_SUCCESS) {
|
if (result1 != NOSTR_SUCCESS || result2 != NOSTR_SUCCESS || result3 != NOSTR_SUCCESS) {
|
||||||
printf(" FAIL: Encryption failed - Results: %d, %d, %d\n", result1, result2, result3);
|
printf(" FAIL: Encryption failed - Results: %d, %d, %d\n", result1, result2, result3);
|
||||||
@@ -310,9 +304,6 @@ static int test_nip44_encryption_variability() {
|
|||||||
printf(" Encryption 1: %.50s...\n", encrypted1);
|
printf(" Encryption 1: %.50s...\n", encrypted1);
|
||||||
printf(" Encryption 2: %.50s...\n", encrypted2);
|
printf(" Encryption 2: %.50s...\n", encrypted2);
|
||||||
printf(" Encryption 3: %.50s...\n", encrypted3);
|
printf(" Encryption 3: %.50s...\n", encrypted3);
|
||||||
free(encrypted1);
|
|
||||||
free(encrypted2);
|
|
||||||
free(encrypted3);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,23 +314,11 @@ static int test_nip44_encryption_variability() {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* decrypted1 = malloc(1048576 + 1);
|
char decrypted1[8192], decrypted2[8192], decrypted3[8192];
|
||||||
char* decrypted2 = malloc(1048576 + 1);
|
|
||||||
char* decrypted3 = malloc(1048576 + 1);
|
int decrypt1 = nostr_nip44_decrypt(recipient_key, sender_pubkey, encrypted1, decrypted1, sizeof(decrypted1));
|
||||||
if (!decrypted1 || !decrypted2 || !decrypted3) {
|
int decrypt2 = nostr_nip44_decrypt(recipient_key, sender_pubkey, encrypted2, decrypted2, sizeof(decrypted2));
|
||||||
printf(" FAIL: Memory allocation failed for decrypted buffers\n");
|
int decrypt3 = nostr_nip44_decrypt(recipient_key, sender_pubkey, encrypted3, decrypted3, sizeof(decrypted3));
|
||||||
free(encrypted1);
|
|
||||||
free(encrypted2);
|
|
||||||
free(encrypted3);
|
|
||||||
free(decrypted1);
|
|
||||||
free(decrypted2);
|
|
||||||
free(decrypted3);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int decrypt1 = nostr_nip44_decrypt(recipient_key, sender_pubkey, encrypted1, decrypted1, 1048576 + 1);
|
|
||||||
int decrypt2 = nostr_nip44_decrypt(recipient_key, sender_pubkey, encrypted2, decrypted2, 1048576 + 1);
|
|
||||||
int decrypt3 = nostr_nip44_decrypt(recipient_key, sender_pubkey, encrypted3, decrypted3, 1048576 + 1);
|
|
||||||
|
|
||||||
if (decrypt1 != NOSTR_SUCCESS || decrypt2 != NOSTR_SUCCESS || decrypt3 != NOSTR_SUCCESS) {
|
if (decrypt1 != NOSTR_SUCCESS || decrypt2 != NOSTR_SUCCESS || decrypt3 != NOSTR_SUCCESS) {
|
||||||
printf(" FAIL: Decryption failed - Results: %d, %d, %d\n", decrypt1, decrypt2, decrypt3);
|
printf(" FAIL: Decryption failed - Results: %d, %d, %d\n", decrypt1, decrypt2, decrypt3);
|
||||||
@@ -352,25 +331,12 @@ static int test_nip44_encryption_variability() {
|
|||||||
printf(" Decrypted1: \"%s\"\n", decrypted1);
|
printf(" Decrypted1: \"%s\"\n", decrypted1);
|
||||||
printf(" Decrypted2: \"%s\"\n", decrypted2);
|
printf(" Decrypted2: \"%s\"\n", decrypted2);
|
||||||
printf(" Decrypted3: \"%s\"\n", decrypted3);
|
printf(" Decrypted3: \"%s\"\n", decrypted3);
|
||||||
free(encrypted1);
|
|
||||||
free(encrypted2);
|
|
||||||
free(encrypted3);
|
|
||||||
free(decrypted1);
|
|
||||||
free(decrypted2);
|
|
||||||
free(decrypted3);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf(" PASS: All encryptions different, all decrypt to: \"%s\"\n", test_message);
|
printf(" PASS: All encryptions different, all decrypt to: \"%s\"\n", test_message);
|
||||||
printf(" Sample ciphertext lengths: %zu, %zu, %zu bytes\n", strlen(encrypted1), strlen(encrypted2), strlen(encrypted3));
|
printf(" Sample ciphertext lengths: %zu, %zu, %zu bytes\n", strlen(encrypted1), strlen(encrypted2), strlen(encrypted3));
|
||||||
|
|
||||||
free(encrypted1);
|
|
||||||
free(encrypted2);
|
|
||||||
free(encrypted3);
|
|
||||||
free(decrypted1);
|
|
||||||
free(decrypted2);
|
|
||||||
free(decrypted3);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,37 +365,12 @@ int main() {
|
|||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Additional edge case tests (converted to round-trip tests with new 32-bit padding)
|
// Test decryption vectors (cross-compatibility)
|
||||||
// These test the same plaintexts as the old decryption vectors but with our new format
|
size_t num_decryption_vectors = sizeof(decryption_test_vectors) / sizeof(decryption_test_vectors[0]);
|
||||||
static nip44_test_vector_t edge_case_test_vectors[] = {
|
for (size_t i = 0; i < num_decryption_vectors; i++) {
|
||||||
{
|
|
||||||
"Edge case: single char 'a'",
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001", // sec1
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002", // sec2
|
|
||||||
"a",
|
|
||||||
NULL
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Edge case: emoji",
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002", // sec1
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001", // sec2
|
|
||||||
"🍕🫃",
|
|
||||||
NULL
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Edge case: wide unicode",
|
|
||||||
"5c0c523f52a5b6fad39ed2403092df8cebc36318b39383bca6c00808626fab3a", // sec1
|
|
||||||
"4b22aa260e4acb7021e32f38a6cdf4b673c6a277755bfce287e370c924dc936d", // sec2
|
|
||||||
"表ポあA鷗ŒéB逍Üߪąñ丂㐀𠀀",
|
|
||||||
NULL
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t num_edge_case_vectors = sizeof(edge_case_test_vectors) / sizeof(edge_case_test_vectors[0]);
|
|
||||||
for (size_t i = 0; i < num_edge_case_vectors; i++) {
|
|
||||||
total_tests++;
|
total_tests++;
|
||||||
printf("Test #%d\n", total_tests);
|
printf("Test #%d\n", total_tests);
|
||||||
if (test_nip44_round_trip(&edge_case_test_vectors[i]) == 0) {
|
if (test_nip44_decryption_vector(&decryption_test_vectors[i]) == 0) {
|
||||||
passed_tests++;
|
passed_tests++;
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -9,13 +9,10 @@
|
|||||||
// Test callback function
|
// Test callback function
|
||||||
static int callback_count = 0;
|
static int callback_count = 0;
|
||||||
|
|
||||||
void test_callback(const char* relay_url, const char* event_id,
|
void test_callback(const char* relay_url, const char* event_id,
|
||||||
int success, const char* message, void* user_data) {
|
int success, const char* message, void* user_data) {
|
||||||
(void)event_id; // Suppress unused parameter warning
|
|
||||||
(void)user_data; // Suppress unused parameter warning
|
|
||||||
|
|
||||||
callback_count++;
|
callback_count++;
|
||||||
printf("📡 Callback %d: Relay %s, Success: %s\n",
|
printf("📡 Callback %d: Relay %s, Success: %s\n",
|
||||||
callback_count, relay_url, success ? "YES" : "NO");
|
callback_count, relay_url, success ? "YES" : "NO");
|
||||||
if (message) {
|
if (message) {
|
||||||
printf(" Message: %s\n", message);
|
printf(" Message: %s\n", message);
|
||||||
|
|||||||
Reference in New Issue
Block a user