Files
nostr_core_lib/tests/nip44_test.c

467 lines
17 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* NIP-44 Encryption/Decryption Test
*
* Test suite for NIP-44 versioned encryption functionality
* Uses known test vectors and cross-implementation compatibility tests
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "../nostr_core/nostr_core.h"
// Test vectors for NIP-44 with proper key pairs
typedef struct {
const char* name;
const char* sender_private_key_hex;
const char* recipient_private_key_hex; // FIX: Need proper private key, not public key
const char* plaintext;
const char* expected_encrypted; // Optional - for known test vectors
} nip44_test_vector_t;
// Additional test vectors for edge cases (converted to round-trip tests with new 32-bit padding)
// Round-trip test vectors with proper key pairs
static nip44_test_vector_t test_vectors[] = {
{
"Basic short message",
"91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe", // Working keys from simple_nip44_test
"96f6fa197aa07477ab88f6981118466ae3a982faab8ad5db9d5426870c73d220",
"Hello, NIP-44!",
NULL
},
{
"Unicode message",
"1111111111111111111111111111111111111111111111111111111111111111",
"2222222222222222222222222222222222222222222222222222222222222222",
"Hello 🌍 World! 🚀",
NULL
},
{
"Empty message",
"3333333333333333333333333333333333333333333333333333333333333333",
"4444444444444444444444444444444444444444444444444444444444444444",
"",
NULL
},
{
"64KB payload test",
"91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe", // Same keys as basic test
"96f6fa197aa07477ab88f6981118466ae3a982faab8ad5db9d5426870c73d220",
NULL, // Will be generated dynamically
NULL
}
};
static int hex_to_bytes(const char* hex, unsigned char* bytes, size_t len) {
if (strlen(hex) != len * 2) return -1;
for (size_t i = 0; i < len; i++) {
if (sscanf(hex + i * 2, "%2hhx", &bytes[i]) != 1) {
return -1;
}
}
return 0;
}
static int test_nip44_round_trip(const nip44_test_vector_t* tv) {
printf("Test: %s\n", tv->name);
// Parse keys - both private 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];
unsigned char recipient_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;
}
if (nostr_ec_public_key_from_private_key(recipient_private_key, recipient_public_key) != 0) {
printf(" FAIL: Failed to derive recipient public key\n");
return -1;
}
// Special handling for large payload tests
char* test_plaintext;
if (strcmp(tv->name, "64KB payload test") == 0) {
// 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,
recipient_public_key,
test_plaintext,
fixed_nonce,
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 = malloc(65536 + 1); // 64KB + 1 for null terminator
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(
recipient_private_key,
sender_public_key,
encrypted,
decrypted,
65536 + 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(test_plaintext, decrypted) != 0) {
printf(" FAIL: Round-trip mismatch\n");
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;
}
if (strcmp(tv->name, "64KB payload test") == 0) {
printf(" ✅ 64KB 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(" 🎉 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;
}
static int test_nip44_error_conditions() {
printf("Test: NIP-44 error conditions\n");
// Use proper valid secp256k1 private keys
unsigned char valid_sender_key[32];
unsigned char valid_recipient_key[32];
unsigned char valid_recipient_pubkey[32];
hex_to_bytes("0000000000000000000000000000000000000000000000000000000000000001", valid_sender_key, 32);
hex_to_bytes("0000000000000000000000000000000000000000000000000000000000000002", valid_recipient_key, 32);
// Generate the recipient's public key
if (nostr_ec_public_key_from_private_key(valid_recipient_key, valid_recipient_pubkey) != 0) {
printf(" FAIL: Failed to generate recipient public key\n");
return -1;
}
char output[1024];
// Test NULL parameters
int result = nostr_nip44_encrypt(NULL, valid_recipient_pubkey, "test", output, sizeof(output));
if (result != NOSTR_ERROR_INVALID_INPUT) {
printf(" FAIL: NULL sender key - Expected: %d, Actual: %d\n", NOSTR_ERROR_INVALID_INPUT, result);
return -1;
}
result = nostr_nip44_encrypt(valid_sender_key, NULL, "test", output, sizeof(output));
if (result != NOSTR_ERROR_INVALID_INPUT) {
printf(" FAIL: NULL recipient key - Expected: %d, Actual: %d\n", NOSTR_ERROR_INVALID_INPUT, result);
return -1;
}
result = nostr_nip44_encrypt(valid_sender_key, valid_recipient_pubkey, NULL, output, sizeof(output));
if (result != NOSTR_ERROR_INVALID_INPUT) {
printf(" FAIL: NULL plaintext - Expected: %d, Actual: %d\n", NOSTR_ERROR_INVALID_INPUT, result);
return -1;
}
result = nostr_nip44_encrypt(valid_sender_key, valid_recipient_pubkey, "test", NULL, sizeof(output));
if (result != NOSTR_ERROR_INVALID_INPUT) {
printf(" FAIL: NULL output buffer - Expected: %d, Actual: %d\n", NOSTR_ERROR_INVALID_INPUT, result);
return -1;
}
// Test buffer too small
char small_buffer[10];
result = nostr_nip44_encrypt(valid_sender_key, valid_recipient_pubkey, "test message", small_buffer, sizeof(small_buffer));
if (result != NOSTR_ERROR_NIP44_BUFFER_TOO_SMALL) {
printf(" FAIL: Buffer too small - Expected: %d, Actual: %d\n", NOSTR_ERROR_NIP44_BUFFER_TOO_SMALL, result);
return -1;
}
printf(" PASS: All error conditions handled correctly\n");
return 0;
}
static int test_nip44_encryption_variability() {
printf("Test: NIP-44 encryption variability (non-deterministic)\n");
const char* test_message = "Test message for variability";
unsigned char sender_key[32], recipient_key[32];
// Use fixed test keys
hex_to_bytes("1111111111111111111111111111111111111111111111111111111111111111", sender_key, 32);
hex_to_bytes("2222222222222222222222222222222222222222222222222222222222222222", recipient_key, 32);
// Generate recipient public key
unsigned char recipient_pubkey[32];
if (nostr_ec_public_key_from_private_key(recipient_key, recipient_pubkey) != 0) {
printf(" FAIL: Failed to generate recipient public key\n");
return -1;
}
// Encrypt the same message multiple times
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, 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);
return -1;
}
// Verify all ciphertexts are different (non-deterministic)
if (strcmp(encrypted1, encrypted2) == 0 || strcmp(encrypted1, encrypted3) == 0 || strcmp(encrypted2, encrypted3) == 0) {
printf(" FAIL: NIP-44 encryption should produce different ciphertext each time\n");
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;
}
// Verify all decrypt to the same plaintext
unsigned char sender_pubkey[32];
if (nostr_ec_public_key_from_private_key(sender_key, sender_pubkey) != 0) {
printf(" FAIL: Failed to generate sender public key\n");
return -1;
}
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, 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);
return -1;
}
if (strcmp(decrypted1, test_message) != 0 || strcmp(decrypted2, test_message) != 0 || strcmp(decrypted3, test_message) != 0) {
printf(" FAIL: Decryption mismatch\n");
printf(" Expected: \"%s\"\n", test_message);
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;
}
int main() {
printf("NIP-44 Encryption Test Suite\n");
printf("=============================\n");
// Initialize the library
if (nostr_init() != NOSTR_SUCCESS) {
printf("FAIL: Failed to initialize NOSTR library\n");
return 1;
}
int total_tests = 0;
int passed_tests = 0;
// Test all vectors
size_t num_vectors = sizeof(test_vectors) / sizeof(test_vectors[0]);
for (size_t i = 0; i < num_vectors; i++) {
total_tests++;
printf("Test #%d\n", total_tests);
if (test_nip44_round_trip(&test_vectors[i]) == 0) {
passed_tests++;
}
printf("\n");
}
// Additional edge case tests (converted to round-trip tests with new 32-bit padding)
// These test the same plaintexts as the old decryption vectors but with our new format
static nip44_test_vector_t edge_case_test_vectors[] = {
{
"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鷗Œé逍Üߪąñ丂㐀𠀀",
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++;
printf("Test #%d\n", total_tests);
if (test_nip44_round_trip(&edge_case_test_vectors[i]) == 0) {
passed_tests++;
}
printf("\n");
}
// Test encryption variability (NIP-44 non-deterministic behavior)
total_tests++;
printf("Test #%d\n", total_tests);
if (test_nip44_encryption_variability() == 0) {
passed_tests++;
}
printf("\n");
// Test error conditions
total_tests++;
printf("Test #%d\n", total_tests);
if (test_nip44_error_conditions() == 0) {
passed_tests++;
}
printf("\n");
// Final results
printf("\nTest Results: %d/%d passed\n", passed_tests, total_tests);
if (passed_tests == total_tests) {
printf("All NIP-44 tests PASSED\n");
nostr_cleanup();
return 0;
} else {
printf("Some tests FAILED\n");
nostr_cleanup();
return 1;
}
}