Upgrade nip4 and nip44 to be able to handle 1MB payloads.
This commit is contained in:
@@ -13,7 +13,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
// NIP-04 constants
|
||||
// #define NOSTR_NIP04_MAX_PLAINTEXT_SIZE 65535
|
||||
// #define NOSTR_NIP04_MAX_PLAINTEXT_SIZE 1048576 // 1MB
|
||||
// NIP-04 Constants
|
||||
// #define NOSTR_NIP04_MAX_PLAINTEXT_SIZE 16777216 // 16MB
|
||||
// #define NOSTR_NIP04_MAX_ENCRYPTED_SIZE 22369621 // ~21.3MB (accounts for base64 overhead + IV)
|
||||
|
||||
@@ -75,42 +75,43 @@ static size_t calc_padded_len(size_t unpadded_len) {
|
||||
return chunk * ((unpadded_len - 1) / chunk + 1);
|
||||
}
|
||||
|
||||
// NIP-44 padding (per spec)
|
||||
// NIP-44 padding (modified to support 1MB payloads with 32-bit length field)
|
||||
static unsigned char* pad_plaintext(const char* plaintext, size_t* padded_len) {
|
||||
size_t unpadded_len = strlen(plaintext);
|
||||
if (unpadded_len > 65535) {
|
||||
if (unpadded_len > 1048576) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t padded_content_len = calc_padded_len(unpadded_len);
|
||||
*padded_len = padded_content_len + 2; // Add 2 bytes for the length prefix
|
||||
*padded_len = padded_content_len + 4; // Add 4 bytes for the length prefix (32-bit)
|
||||
unsigned char* padded = malloc(*padded_len);
|
||||
if (!padded) return NULL;
|
||||
|
||||
// Write length prefix (big-endian u16)
|
||||
padded[0] = (unpadded_len >> 8) & 0xFF;
|
||||
padded[1] = unpadded_len & 0xFF;
|
||||
// Write length prefix (big-endian u32)
|
||||
padded[0] = (unpadded_len >> 24) & 0xFF;
|
||||
padded[1] = (unpadded_len >> 16) & 0xFF;
|
||||
padded[2] = (unpadded_len >> 8) & 0xFF;
|
||||
padded[3] = unpadded_len & 0xFF;
|
||||
|
||||
// Copy plaintext and add zero-padding
|
||||
memcpy(padded + 2, plaintext, unpadded_len);
|
||||
memset(padded + 2 + unpadded_len, 0, padded_content_len - unpadded_len);
|
||||
memcpy(padded + 4, plaintext, unpadded_len);
|
||||
memset(padded + 4 + unpadded_len, 0, padded_content_len - unpadded_len);
|
||||
|
||||
return padded;
|
||||
}
|
||||
|
||||
// NIP-44 unpadding (per spec)
|
||||
// NIP-44 unpadding (modified to support 32-bit length field)
|
||||
static char* unpad_plaintext(const unsigned char* padded, size_t padded_len) {
|
||||
if (padded_len < 4) return NULL;
|
||||
if (padded_len < 6) return NULL; // Minimum: 4 bytes length + 2 bytes content
|
||||
|
||||
size_t unpadded_len = (padded[0] << 8) | padded[1];
|
||||
size_t unpadded_len = (padded[0] << 24) | (padded[1] << 16) | (padded[2] << 8) | padded[3];
|
||||
size_t expected_padded_len = calc_padded_len(unpadded_len);
|
||||
|
||||
|
||||
if (padded_len != expected_padded_len + 2) {
|
||||
if (padded_len != expected_padded_len + 4) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (unpadded_len > padded_len - 2) {
|
||||
if (unpadded_len > padded_len - 4) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -119,7 +120,7 @@ static char* unpad_plaintext(const unsigned char* padded, size_t padded_len) {
|
||||
|
||||
// Handle empty message case (unpadded_len can be 0)
|
||||
if (unpadded_len > 0) {
|
||||
memcpy(plaintext, padded + 2, unpadded_len);
|
||||
memcpy(plaintext, padded + 4, unpadded_len);
|
||||
}
|
||||
plaintext[unpadded_len] = '\0';
|
||||
|
||||
@@ -421,7 +422,6 @@ int nostr_nip44_decrypt(const unsigned char* recipient_private_key,
|
||||
return NOSTR_ERROR_CRYPTO_FAILED;
|
||||
}
|
||||
|
||||
// Constant-time MAC verification
|
||||
// Constant-time MAC verification
|
||||
if (!constant_time_compare(received_mac, computed_mac, 32)) {
|
||||
memory_clear(shared_secret, 32);
|
||||
@@ -459,7 +459,6 @@ int nostr_nip44_decrypt(const unsigned char* recipient_private_key,
|
||||
return NOSTR_ERROR_CRYPTO_FAILED;
|
||||
}
|
||||
|
||||
// Step 8: Remove padding according to NIP-44 spec
|
||||
// Step 8: Remove padding according to NIP-44 spec
|
||||
char* plaintext = unpad_plaintext(padded_plaintext, ciphertext_len);
|
||||
if (!plaintext) {
|
||||
|
||||
@@ -13,7 +13,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
// NIP-44 constants
|
||||
// #define NOSTR_NIP44_MAX_PLAINTEXT_SIZE 65535
|
||||
// #define NOSTR_NIP44_MAX_PLAINTEXT_SIZE 1048576
|
||||
|
||||
/**
|
||||
* NIP-44: Encrypt a message using ECDH + ChaCha20 + HMAC
|
||||
|
||||
@@ -72,11 +72,11 @@
|
||||
#define NIP05_DEFAULT_TIMEOUT 10
|
||||
|
||||
// NIP-04 Constants
|
||||
#define NOSTR_NIP04_MAX_PLAINTEXT_SIZE 16777216 // 16MB
|
||||
#define NOSTR_NIP04_MAX_PLAINTEXT_SIZE 1048576 // 1MB
|
||||
#define NOSTR_NIP04_MAX_ENCRYPTED_SIZE 22369621 // ~21.3MB (accounts for base64 overhead + IV)
|
||||
|
||||
// NIP-44 Constants
|
||||
#define NOSTR_NIP44_MAX_PLAINTEXT_SIZE 65536 // 64KB max plaintext (matches crypto header)
|
||||
#define NOSTR_NIP44_MAX_PLAINTEXT_SIZE 1048576 // 1MB max plaintext (32-bit length field)
|
||||
|
||||
// Forward declaration for cJSON (to avoid requiring cJSON.h in header)
|
||||
struct cJSON;
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
#define NOSTR_CORE_H
|
||||
|
||||
// Version information (auto-updated by increment_and_push.sh)
|
||||
#define VERSION "v0.4.6"
|
||||
#define VERSION "v0.4.7"
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MINOR 4
|
||||
#define VERSION_PATCH 6
|
||||
#define VERSION_PATCH 7
|
||||
|
||||
/*
|
||||
* NOSTR Core Library - Complete API Reference
|
||||
|
||||
Binary file not shown.
BIN
tests/backward_compat_test
Executable file
BIN
tests/backward_compat_test
Executable file
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("\n");
|
||||
|
||||
// Test decryption with our ciphertext
|
||||
char* decrypted = malloc(NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
|
||||
// Test decryption with our ciphertext - allocate larger buffer for safety
|
||||
char* decrypted = malloc(NOSTR_NIP04_MAX_PLAINTEXT_SIZE + 1024); // 1MB + 1KB extra
|
||||
if (!decrypted) {
|
||||
printf("❌ MEMORY ALLOCATION FAILED for decrypted buffer\n");
|
||||
free(large_plaintext);
|
||||
@@ -680,7 +680,7 @@ int test_vector_7_10kb_payload(void) {
|
||||
return 0;
|
||||
}
|
||||
printf("Testing decryption of 1MB ciphertext (Bob decrypts from Alice)...\n");
|
||||
result = nostr_nip04_decrypt(sk2, pk1, encrypted, decrypted, NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
|
||||
result = nostr_nip04_decrypt(sk2, pk1, encrypted, decrypted, NOSTR_NIP04_MAX_PLAINTEXT_SIZE + 1024);
|
||||
|
||||
if (result != NOSTR_SUCCESS) {
|
||||
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.
@@ -69,6 +69,13 @@ static nip44_test_vector_t test_vectors[] = {
|
||||
"4444444444444444444444444444444444444444444444444444444444444444",
|
||||
"",
|
||||
NULL
|
||||
},
|
||||
{
|
||||
"1MB payload test",
|
||||
"91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe", // Same keys as basic test
|
||||
"96f6fa197aa07477ab88f6981118466ae3a982faab8ad5db9d5426870c73d220",
|
||||
NULL, // Will be generated dynamically
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
@@ -115,46 +122,114 @@ static int test_nip44_round_trip(const nip44_test_vector_t* tv) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Test encryption
|
||||
char encrypted[8192];
|
||||
int encrypt_result = nostr_nip44_encrypt(
|
||||
// Special handling for large payload tests
|
||||
char* test_plaintext;
|
||||
if (strcmp(tv->name, "1MB payload test") == 0) {
|
||||
// Generate exactly 1MB (1,048,576 bytes) of predictable content
|
||||
const size_t payload_size = 1048576;
|
||||
test_plaintext = malloc(payload_size + 1);
|
||||
if (!test_plaintext) {
|
||||
printf(" FAIL: Memory allocation failed for 1MB 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 1MB 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,
|
||||
recipient_public_key,
|
||||
tv->plaintext,
|
||||
test_plaintext,
|
||||
fixed_nonce,
|
||||
encrypted,
|
||||
sizeof(encrypted)
|
||||
10485760
|
||||
);
|
||||
|
||||
if (encrypt_result != NOSTR_SUCCESS) {
|
||||
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;
|
||||
}
|
||||
|
||||
// Test decryption - use recipient private key + sender public key
|
||||
char decrypted[8192];
|
||||
char* decrypted = malloc(1048576 + 1); // 1MB + 1 for null terminator
|
||||
if (!decrypted) {
|
||||
printf(" FAIL: Memory allocation failed for decrypted buffer\n");
|
||||
if (strcmp(tv->name, "1MB payload test") == 0) free(test_plaintext);
|
||||
free(encrypted);
|
||||
return -1;
|
||||
}
|
||||
int decrypt_result = nostr_nip44_decrypt(
|
||||
recipient_private_key,
|
||||
sender_public_key,
|
||||
encrypted,
|
||||
decrypted,
|
||||
sizeof(decrypted)
|
||||
1048576 + 1
|
||||
);
|
||||
|
||||
if (decrypt_result != NOSTR_SUCCESS) {
|
||||
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;
|
||||
}
|
||||
|
||||
// Verify round-trip
|
||||
if (strcmp(tv->plaintext, decrypted) != 0) {
|
||||
if (strcmp(test_plaintext, decrypted) != 0) {
|
||||
printf(" FAIL: Round-trip mismatch\n");
|
||||
printf(" Expected: \"%s\"\n", tv->plaintext);
|
||||
printf(" Expected: \"%s\"\n", test_plaintext);
|
||||
printf(" Actual: \"%s\"\n", decrypted);
|
||||
if (strcmp(tv->name, "1MB payload test") == 0) free(test_plaintext);
|
||||
free(encrypted);
|
||||
free(decrypted);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf(" PASS: Expected: \"%s\", Actual: \"%s\"\n", tv->plaintext, decrypted);
|
||||
if (strcmp(tv->name, "1MB payload test") == 0) {
|
||||
printf(" ✅ 1MB payload round-trip: PASS\n");
|
||||
printf(" ✅ Content verification: All %zu bytes match perfectly!\n", strlen(test_plaintext));
|
||||
printf(" Encrypted length: %zu bytes\n", strlen(encrypted));
|
||||
printf(" 🎉 1MB 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, "1MB payload test") == 0) free(test_plaintext);
|
||||
free(encrypted);
|
||||
free(decrypted);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -241,13 +316,17 @@ static int test_nip44_decryption_vector(const nip44_test_vector_t* tv) {
|
||||
}
|
||||
|
||||
// Test decryption of known vector
|
||||
char decrypted[8192];
|
||||
char* decrypted = malloc(1048576 + 1); // 1MB + 1 for null terminator
|
||||
if (!decrypted) {
|
||||
printf(" FAIL: Memory allocation failed for decrypted buffer\n");
|
||||
return -1;
|
||||
}
|
||||
int decrypt_result = nostr_nip44_decrypt(
|
||||
recipient_private_key,
|
||||
sender_public_key,
|
||||
tv->expected_encrypted,
|
||||
decrypted,
|
||||
sizeof(decrypted)
|
||||
1048576 + 1
|
||||
);
|
||||
|
||||
if (decrypt_result != NOSTR_SUCCESS) {
|
||||
@@ -266,6 +345,8 @@ static int test_nip44_decryption_vector(const nip44_test_vector_t* tv) {
|
||||
|
||||
printf(" PASS: Expected: \"%s\", Actual: \"%s\"\n", tv->plaintext, decrypted);
|
||||
|
||||
free(decrypted);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -287,11 +368,20 @@ static int test_nip44_encryption_variability() {
|
||||
}
|
||||
|
||||
// Encrypt the same message multiple times
|
||||
char encrypted1[8192], encrypted2[8192], encrypted3[8192];
|
||||
char* encrypted1 = malloc(2097152); // 2MB buffer
|
||||
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, sizeof(encrypted1));
|
||||
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, sizeof(encrypted3));
|
||||
int result1 = nostr_nip44_encrypt(sender_key, recipient_pubkey, test_message, encrypted1, 2097152);
|
||||
int result2 = nostr_nip44_encrypt(sender_key, recipient_pubkey, test_message, encrypted2, 2097152);
|
||||
int result3 = nostr_nip44_encrypt(sender_key, recipient_pubkey, test_message, encrypted3, 2097152);
|
||||
|
||||
if (result1 != NOSTR_SUCCESS || result2 != NOSTR_SUCCESS || result3 != NOSTR_SUCCESS) {
|
||||
printf(" FAIL: Encryption failed - Results: %d, %d, %d\n", result1, result2, result3);
|
||||
@@ -304,6 +394,9 @@ static int test_nip44_encryption_variability() {
|
||||
printf(" Encryption 1: %.50s...\n", encrypted1);
|
||||
printf(" Encryption 2: %.50s...\n", encrypted2);
|
||||
printf(" Encryption 3: %.50s...\n", encrypted3);
|
||||
free(encrypted1);
|
||||
free(encrypted2);
|
||||
free(encrypted3);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -314,11 +407,23 @@ static int test_nip44_encryption_variability() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char decrypted1[8192], decrypted2[8192], decrypted3[8192];
|
||||
char* decrypted1 = malloc(1048576 + 1);
|
||||
char* decrypted2 = malloc(1048576 + 1);
|
||||
char* decrypted3 = malloc(1048576 + 1);
|
||||
if (!decrypted1 || !decrypted2 || !decrypted3) {
|
||||
printf(" FAIL: Memory allocation failed for decrypted buffers\n");
|
||||
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, sizeof(decrypted1));
|
||||
int decrypt2 = nostr_nip44_decrypt(recipient_key, sender_pubkey, encrypted2, decrypted2, sizeof(decrypted2));
|
||||
int decrypt3 = nostr_nip44_decrypt(recipient_key, sender_pubkey, encrypted3, decrypted3, sizeof(decrypted3));
|
||||
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) {
|
||||
printf(" FAIL: Decryption failed - Results: %d, %d, %d\n", decrypt1, decrypt2, decrypt3);
|
||||
@@ -331,12 +436,25 @@ static int test_nip44_encryption_variability() {
|
||||
printf(" Decrypted1: \"%s\"\n", decrypted1);
|
||||
printf(" Decrypted2: \"%s\"\n", decrypted2);
|
||||
printf(" Decrypted3: \"%s\"\n", decrypted3);
|
||||
free(encrypted1);
|
||||
free(encrypted2);
|
||||
free(encrypted3);
|
||||
free(decrypted1);
|
||||
free(decrypted2);
|
||||
free(decrypted3);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
free(encrypted1);
|
||||
free(encrypted2);
|
||||
free(encrypted3);
|
||||
free(decrypted1);
|
||||
free(decrypted2);
|
||||
free(decrypted3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -11,6 +11,9 @@ static int callback_count = 0;
|
||||
|
||||
void test_callback(const char* relay_url, const char* event_id,
|
||||
int success, const char* message, void* user_data) {
|
||||
(void)event_id; // Suppress unused parameter warning
|
||||
(void)user_data; // Suppress unused parameter warning
|
||||
|
||||
callback_count++;
|
||||
printf("📡 Callback %d: Relay %s, Success: %s\n",
|
||||
callback_count, relay_url, success ? "YES" : "NO");
|
||||
|
||||
Reference in New Issue
Block a user