/* * NIP-44 Encryption/Decryption Test * * Test suite for NIP-44 versioned encryption functionality * Uses known test vectors and cross-implementation compatibility tests */ #include #include #include #include #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; // 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ι·—Ε’Γ©οΌ’ι€ΓœΓŸΒͺąñ丂㐀𠀀", "ArY1I2xC2yDwIbuNHN/1ynXdGgzHLqdCrXUPMwELJPc7s7JqlCMJBAIIjfkpHReBPXeoMCyuClwgbT419jUWU1PwaNl4FEQYKCDKVJz+97Mp3K+Q2YGa77B6gpxB/lr1QgoqpDf7wDVrDmOqGoiPjWDqy8KzLueKDcm9BVP8xeTJIxs=" } }; // 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 }, { "1MB 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, "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, 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(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, 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(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, "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; } 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_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 = 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, 1048576 + 1 ); 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); free(decrypted); 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"); } // Test decryption vectors (cross-compatibility) size_t num_decryption_vectors = sizeof(decryption_test_vectors) / sizeof(decryption_test_vectors[0]); for (size_t i = 0; i < num_decryption_vectors; i++) { total_tests++; printf("Test #%d\n", total_tests); if (test_nip44_decryption_vector(&decryption_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; } }