Refactor and consolidate test suite
- Moved old tests from tests/old/ to main tests/ directory - Renamed nostr_test_bip32.c to bip32_test.c for consistency - Renamed nostr_crypto_test.c to crypto_test.c for consistency - Renamed wss_test.c and moved from old directory - Fixed unused variable warning in bip32_test.c - Updated build system and workspace rules - Cleaned up old compiled test executables - Updated nostr_common.h and core_relays.c - Removed obsolete nostr_core.h.old backup file This consolidates all active tests into the main directory and removes outdated test files while maintaining a clean, organized structure.
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,461 +0,0 @@
|
||||
/*
|
||||
* NOSTR Crypto Test Suite
|
||||
* Tests all cryptographic primitives and BIP implementations
|
||||
* with known good test vectors
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "../nostr_core/utils.h"
|
||||
|
||||
// Helper function to convert hex string to bytes
|
||||
static void hex_to_bytes(const char* hex, unsigned char* bytes, size_t len) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
sscanf(hex + i * 2, "%02hhx", &bytes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to compare byte arrays and print results
|
||||
static int test_bytes_equal(const char* test_name,
|
||||
const unsigned char* result,
|
||||
const unsigned char* expected,
|
||||
size_t len) {
|
||||
if (memcmp(result, expected, len) == 0) {
|
||||
printf("✓ %s: PASSED\n", test_name);
|
||||
return 1;
|
||||
} else {
|
||||
printf("❌ %s: FAILED\n", test_name);
|
||||
printf(" Expected: ");
|
||||
for (size_t i = 0; i < len; i++) printf("%02x", expected[i]);
|
||||
printf("\n Got: ");
|
||||
for (size_t i = 0; i < len; i++) printf("%02x", result[i]);
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// SHA-256 TESTS
|
||||
// =============================================================================
|
||||
|
||||
static int test_sha256_empty_string() {
|
||||
unsigned char result[32];
|
||||
unsigned char expected[32];
|
||||
|
||||
// Empty string SHA-256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
||||
hex_to_bytes("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", expected, 32);
|
||||
|
||||
nostr_sha256((const unsigned char*)"", 0, result);
|
||||
|
||||
return test_bytes_equal("SHA-256 empty string", result, expected, 32);
|
||||
}
|
||||
|
||||
static int test_sha256_abc() {
|
||||
unsigned char result[32];
|
||||
unsigned char expected[32];
|
||||
|
||||
// "abc" SHA-256: ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
|
||||
hex_to_bytes("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", expected, 32);
|
||||
|
||||
nostr_sha256((const unsigned char*)"abc", 3, result);
|
||||
|
||||
return test_bytes_equal("SHA-256 'abc'", result, expected, 32);
|
||||
}
|
||||
|
||||
static int test_sha256_hello_world() {
|
||||
unsigned char result[32];
|
||||
unsigned char expected[32];
|
||||
|
||||
// "hello world" SHA-256: b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
|
||||
hex_to_bytes("b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9", expected, 32);
|
||||
|
||||
nostr_sha256((const unsigned char*)"hello world", 11, result);
|
||||
|
||||
return test_bytes_equal("SHA-256 'hello world'", result, expected, 32);
|
||||
}
|
||||
|
||||
static int test_sha256_long_string() {
|
||||
unsigned char result[32];
|
||||
unsigned char expected[32];
|
||||
|
||||
// "The quick brown fox jumps over the lazy dog" SHA-256: d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592
|
||||
hex_to_bytes("d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592", expected, 32);
|
||||
|
||||
const char* msg = "The quick brown fox jumps over the lazy dog";
|
||||
nostr_sha256((const unsigned char*)msg, strlen(msg), result);
|
||||
|
||||
return test_bytes_equal("SHA-256 long string", result, expected, 32);
|
||||
}
|
||||
|
||||
static int test_sha256_vectors() {
|
||||
printf("\n=== SHA-256 Tests ===\n");
|
||||
int passed = 0;
|
||||
|
||||
passed += test_sha256_empty_string();
|
||||
passed += test_sha256_abc();
|
||||
passed += test_sha256_hello_world();
|
||||
passed += test_sha256_long_string();
|
||||
|
||||
printf("SHA-256: %d/4 tests passed\n", passed);
|
||||
return (passed == 4) ? 1 : 0;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// HMAC-SHA256 TESTS
|
||||
// =============================================================================
|
||||
|
||||
static int test_hmac_rfc4231_test1() {
|
||||
unsigned char result[32];
|
||||
unsigned char expected[32];
|
||||
|
||||
// RFC 4231 Test Case 1
|
||||
// Key = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b (20 bytes)
|
||||
// Data = "Hi There"
|
||||
// HMAC-SHA256 = b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7
|
||||
|
||||
unsigned char key[20];
|
||||
memset(key, 0x0b, 20);
|
||||
|
||||
hex_to_bytes("b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7", expected, 32);
|
||||
|
||||
nostr_hmac_sha256(key, 20, (const unsigned char*)"Hi There", 8, result);
|
||||
|
||||
return test_bytes_equal("HMAC-SHA256 RFC4231 Test 1", result, expected, 32);
|
||||
}
|
||||
|
||||
static int test_hmac_rfc4231_test2() {
|
||||
unsigned char result[32];
|
||||
unsigned char expected[32];
|
||||
|
||||
// RFC 4231 Test Case 2
|
||||
// Key = "Jefe"
|
||||
// Data = "what do ya want for nothing?"
|
||||
// HMAC-SHA256 = 5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843
|
||||
|
||||
hex_to_bytes("5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843", expected, 32);
|
||||
|
||||
const char* data = "what do ya want for nothing?";
|
||||
nostr_hmac_sha256((const unsigned char*)"Jefe", 4, (const unsigned char*)data, strlen(data), result);
|
||||
|
||||
return test_bytes_equal("HMAC-SHA256 RFC4231 Test 2", result, expected, 32);
|
||||
}
|
||||
|
||||
static int test_hmac_vectors() {
|
||||
printf("\n=== HMAC-SHA256 Tests ===\n");
|
||||
int passed = 0;
|
||||
|
||||
passed += test_hmac_rfc4231_test1();
|
||||
passed += test_hmac_rfc4231_test2();
|
||||
|
||||
printf("HMAC-SHA256: %d/2 tests passed\n", passed);
|
||||
return (passed == 2) ? 1 : 0;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// PBKDF2 TESTS
|
||||
// =============================================================================
|
||||
|
||||
static int test_pbkdf2_bip39_example() {
|
||||
unsigned char result[64];
|
||||
|
||||
// Test BIP39 seed generation with empty passphrase
|
||||
// This should not crash and should produce 64 bytes
|
||||
const char* mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
|
||||
|
||||
int ret = nostr_pbkdf2_hmac_sha512((const unsigned char*)mnemonic, strlen(mnemonic),
|
||||
(const unsigned char*)"mnemonic", 8,
|
||||
2048, result, 64);
|
||||
|
||||
if (ret == 0) {
|
||||
printf("✓ PBKDF2 BIP39 seed generation: PASSED\n");
|
||||
return 1;
|
||||
} else {
|
||||
printf("❌ PBKDF2 BIP39 seed generation: FAILED\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int test_pbkdf2_vectors() {
|
||||
printf("\n=== PBKDF2 Tests ===\n");
|
||||
int passed = 0;
|
||||
|
||||
// Note: RFC 6070 test may not match exactly due to PBKDF2-SHA512 vs PBKDF2-SHA1
|
||||
// but we test that it doesn't crash and produces reasonable output
|
||||
passed += test_pbkdf2_bip39_example();
|
||||
|
||||
printf("PBKDF2: %d/1 tests passed\n", passed);
|
||||
return (passed == 1) ? 1 : 0;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// BIP39 TESTS
|
||||
// =============================================================================
|
||||
|
||||
static int test_bip39_entropy_to_mnemonic() {
|
||||
// Test with known entropy
|
||||
unsigned char entropy[16] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
char mnemonic[256];
|
||||
|
||||
int ret = nostr_bip39_mnemonic_from_bytes(entropy, 16, mnemonic);
|
||||
|
||||
// Should generate a valid 12-word mnemonic from zero entropy
|
||||
if (ret == 0 && strlen(mnemonic) > 0) {
|
||||
printf("✓ BIP39 entropy to mnemonic: PASSED (%s)\n", mnemonic);
|
||||
return 1;
|
||||
} else {
|
||||
printf("❌ BIP39 entropy to mnemonic: FAILED\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int test_bip39_mnemonic_validation() {
|
||||
// Test valid mnemonic
|
||||
const char* valid_mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
|
||||
|
||||
if (nostr_bip39_mnemonic_validate(valid_mnemonic) == 0) {
|
||||
printf("✓ BIP39 mnemonic validation (valid): PASSED\n");
|
||||
} else {
|
||||
printf("❌ BIP39 mnemonic validation (valid): FAILED\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Test invalid mnemonic
|
||||
const char* invalid_mnemonic = "invalid words that are not in wordlist";
|
||||
|
||||
if (nostr_bip39_mnemonic_validate(invalid_mnemonic) != 0) {
|
||||
printf("✓ BIP39 mnemonic validation (invalid): PASSED\n");
|
||||
return 1;
|
||||
} else {
|
||||
printf("❌ BIP39 mnemonic validation (invalid): FAILED\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int test_bip39_mnemonic_to_seed() {
|
||||
const char* mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
|
||||
unsigned char seed[64];
|
||||
|
||||
int ret = nostr_bip39_mnemonic_to_seed(mnemonic, "", seed, sizeof(seed));
|
||||
|
||||
if (ret == 0) {
|
||||
printf("✓ BIP39 mnemonic to seed: PASSED\n");
|
||||
return 1;
|
||||
} else {
|
||||
printf("❌ BIP39 mnemonic to seed: FAILED\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int test_bip39_vectors() {
|
||||
printf("\n=== BIP39 Tests ===\n");
|
||||
int passed = 0;
|
||||
|
||||
passed += test_bip39_entropy_to_mnemonic();
|
||||
passed += test_bip39_mnemonic_validation();
|
||||
passed += test_bip39_mnemonic_to_seed();
|
||||
|
||||
printf("BIP39: %d/3 tests passed\n", passed);
|
||||
return (passed == 3) ? 1 : 0;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// BIP32 TESTS
|
||||
// =============================================================================
|
||||
|
||||
static int test_bip32_seed_to_master_key() {
|
||||
// Test seed to master key derivation
|
||||
unsigned char seed[64];
|
||||
memset(seed, 0x01, 64); // Simple test seed
|
||||
|
||||
nostr_hd_key_t master_key;
|
||||
|
||||
int ret = nostr_bip32_key_from_seed(seed, 64, &master_key);
|
||||
|
||||
if (ret == 0) {
|
||||
printf("✓ BIP32 seed to master key: PASSED\n");
|
||||
return 1;
|
||||
} else {
|
||||
printf("❌ BIP32 seed to master key: FAILED\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int test_bip32_key_derivation() {
|
||||
// Test key derivation path
|
||||
unsigned char seed[64];
|
||||
memset(seed, 0x01, 64);
|
||||
|
||||
nostr_hd_key_t master_key;
|
||||
if (nostr_bip32_key_from_seed(seed, 64, &master_key) != 0) {
|
||||
printf("❌ BIP32 key derivation setup: FAILED\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Test NIP-06 derivation path: m/44'/1237'/0'/0/0
|
||||
nostr_hd_key_t derived_key;
|
||||
uint32_t path[] = {
|
||||
0x80000000 + 44, // 44' (hardened)
|
||||
0x80000000 + 1237, // 1237' (hardened)
|
||||
0x80000000 + 0, // 0' (hardened)
|
||||
0, // 0 (not hardened)
|
||||
0 // 0 (not hardened)
|
||||
};
|
||||
|
||||
int ret = nostr_bip32_derive_path(&master_key, path, 5, &derived_key);
|
||||
|
||||
if (ret == 0) {
|
||||
printf("✓ BIP32 NIP-06 key derivation: PASSED\n");
|
||||
return 1;
|
||||
} else {
|
||||
printf("❌ BIP32 NIP-06 key derivation: FAILED\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int test_bip32_vectors() {
|
||||
printf("\n=== BIP32 Tests ===\n");
|
||||
int passed = 0;
|
||||
|
||||
passed += test_bip32_seed_to_master_key();
|
||||
passed += test_bip32_key_derivation();
|
||||
|
||||
printf("BIP32: %d/2 tests passed\n", passed);
|
||||
return (passed == 2) ? 1 : 0;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// SECP256K1 TESTS
|
||||
// =============================================================================
|
||||
|
||||
static int test_secp256k1_private_key_validation() {
|
||||
// Test valid private key
|
||||
unsigned char valid_key[32];
|
||||
memset(valid_key, 0x01, 32); // Simple valid key
|
||||
|
||||
if (nostr_ec_private_key_verify(valid_key) == 0) {
|
||||
printf("✓ secp256k1 private key validation (valid): PASSED\n");
|
||||
} else {
|
||||
printf("❌ secp256k1 private key validation (valid): FAILED\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Test invalid private key (all zeros)
|
||||
unsigned char invalid_key[32];
|
||||
memset(invalid_key, 0x00, 32);
|
||||
|
||||
if (nostr_ec_private_key_verify(invalid_key) != 0) {
|
||||
printf("✓ secp256k1 private key validation (invalid): PASSED\n");
|
||||
return 1;
|
||||
} else {
|
||||
printf("❌ secp256k1 private key validation (invalid): FAILED\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int test_secp256k1_public_key_generation() {
|
||||
unsigned char private_key[32];
|
||||
unsigned char public_key[32];
|
||||
|
||||
// Use a known private key
|
||||
memset(private_key, 0x01, 32);
|
||||
|
||||
int ret = nostr_ec_public_key_from_private_key(private_key, public_key);
|
||||
|
||||
if (ret == 0) {
|
||||
printf("✓ secp256k1 public key generation: PASSED\n");
|
||||
return 1;
|
||||
} else {
|
||||
printf("❌ secp256k1 public key generation: FAILED\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int test_secp256k1_sign_verify() {
|
||||
unsigned char private_key[32];
|
||||
unsigned char message[32];
|
||||
unsigned char signature[64];
|
||||
|
||||
// Simple test data
|
||||
memset(private_key, 0x01, 32);
|
||||
memset(message, 0x02, 32);
|
||||
|
||||
// Test signing
|
||||
int ret = nostr_ec_sign(private_key, message, signature);
|
||||
|
||||
if (ret == 0) {
|
||||
printf("✓ secp256k1 signing: PASSED\n");
|
||||
return 1;
|
||||
} else {
|
||||
printf("❌ secp256k1 signing: FAILED\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int test_secp256k1_vectors() {
|
||||
printf("\n=== secp256k1 Tests ===\n");
|
||||
int passed = 0;
|
||||
|
||||
passed += test_secp256k1_private_key_validation();
|
||||
passed += test_secp256k1_public_key_generation();
|
||||
passed += test_secp256k1_sign_verify();
|
||||
|
||||
printf("secp256k1: %d/3 tests passed\n", passed);
|
||||
return (passed == 3) ? 1 : 0;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// MAIN TEST RUNNER
|
||||
// =============================================================================
|
||||
|
||||
int main() {
|
||||
printf("NOSTR Crypto Library Test Suite\n");
|
||||
printf("==============================\n");
|
||||
|
||||
// Initialize crypto
|
||||
if (nostr_crypto_init() != 0) {
|
||||
printf("❌ Failed to initialize crypto library\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int passed = 0, total = 0;
|
||||
|
||||
// Run all test suites
|
||||
if (test_sha256_vectors()) passed++;
|
||||
total++;
|
||||
|
||||
if (test_hmac_vectors()) passed++;
|
||||
total++;
|
||||
|
||||
if (test_pbkdf2_vectors()) passed++;
|
||||
total++;
|
||||
|
||||
if (test_bip39_vectors()) passed++;
|
||||
total++;
|
||||
|
||||
if (test_bip32_vectors()) passed++;
|
||||
total++;
|
||||
|
||||
if (test_secp256k1_vectors()) passed++;
|
||||
total++;
|
||||
|
||||
// Print final results
|
||||
printf("\n==============================\n");
|
||||
printf("FINAL RESULTS: %d/%d test suites passed\n", passed, total);
|
||||
|
||||
if (passed == total) {
|
||||
printf("🎉 ALL TESTS PASSED! Crypto implementation is working correctly.\n");
|
||||
} else {
|
||||
printf("❌ Some tests failed. Please review the implementation.\n");
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
nostr_crypto_cleanup();
|
||||
|
||||
return (passed == total) ? 0 : 1;
|
||||
}
|
||||
Binary file not shown.
@@ -1,434 +0,0 @@
|
||||
/*
|
||||
* NOSTR Event Generation Test Suite
|
||||
* Tests complete workflow from mnemonic to signed event using specific test vectors
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "../nostr_core/nostr_core.h"
|
||||
#include "../cjson/cJSON.h"
|
||||
|
||||
// Test vector structure
|
||||
typedef struct {
|
||||
const char* mnemonic;
|
||||
const char* expected_nsec_hex;
|
||||
const char* expected_nsec;
|
||||
const char* expected_npub_hex;
|
||||
const char* expected_npub;
|
||||
const char* name;
|
||||
} test_vector_t;
|
||||
|
||||
// Test vectors to validate against
|
||||
static const test_vector_t TEST_VECTORS[] = {
|
||||
{
|
||||
.name = "Vector 1",
|
||||
.mnemonic = "fetch risk mention yellow cluster hunt voyage acquire leader caution romance solid",
|
||||
.expected_nsec_hex = "b46173ac0cc222f73246d6be63f5c0bd90d92b118f99f582cd11d077490d0794",
|
||||
.expected_nsec = "nsec1k3sh8tqvcg30wvjx66lx8awqhkgdj2c337vltqkdz8g8wjgdq72q3mrze9",
|
||||
.expected_npub_hex = "a11258677dd416ca4c9e352e0e02ad2d8784a18c3a963604d0c63dc7b74eec66",
|
||||
.expected_npub = "npub15yf9sema6stv5ny7x5hquq4d9krcfgvv82trvpxscc7u0d6wa3nqmvcv3a"
|
||||
},
|
||||
{
|
||||
.name = "Vector 2",
|
||||
.mnemonic = "leader monkey parrot ring guide accident before fence cannon height naive bean",
|
||||
.expected_nsec_hex = "7f7ff03d123792d6ac594bfa67bf6d0c0ab55b6b1fdb6249303fe861f1ccba9a",
|
||||
.expected_nsec = "nsec10allq0gjx7fddtzef0ax00mdps9t2kmtrldkyjfs8l5xruwvh2dq0lhhkp",
|
||||
.expected_npub_hex = "17162c921dc4d2518f9a101db33695df1afb56ab82f5ff3e5da6eec3ca5cd917",
|
||||
.expected_npub = "npub1zutzeysacnf9rru6zqwmxd54mud0k44tst6l70ja5mhv8jjumytsd2x7nu"
|
||||
},
|
||||
{
|
||||
.name = "Vector 3",
|
||||
.mnemonic = "what bleak badge arrange retreat wolf trade produce cricket blur garlic valid proud rude strong choose busy staff weather area salt hollow arm fade",
|
||||
.expected_nsec_hex = "c15d739894c81a2fcfd3a2df85a0d2c0dbc47a280d092799f144d73d7ae78add",
|
||||
.expected_nsec = "nsec1c9wh8xy5eqdzln7n5t0ctgxjcrdug73gp5yj0x03gntn67h83twssdfhel",
|
||||
.expected_npub_hex = "d41b22899549e1f3d335a31002cfd382174006e166d3e658e3a5eecdb6463573",
|
||||
.expected_npub = "npub16sdj9zv4f8sl85e45vgq9n7nsgt5qphpvmf7vk8r5hhvmdjxx4es8rq74h"
|
||||
}
|
||||
};
|
||||
|
||||
static const size_t NUM_TEST_VECTORS = sizeof(TEST_VECTORS) / sizeof(TEST_VECTORS[0]);
|
||||
|
||||
// Constants for event generation test
|
||||
static const uint32_t TEST_CREATED_AT = 1698623783;
|
||||
static const char* TEST_CONTENT = "Hello";
|
||||
|
||||
// Expected events for each test vector
|
||||
typedef struct {
|
||||
const char* expected_event_id;
|
||||
const char* expected_signature;
|
||||
const char* expected_json;
|
||||
} expected_event_t;
|
||||
|
||||
static const expected_event_t EXPECTED_EVENTS[] = {
|
||||
{
|
||||
// Vector 1 expected event
|
||||
.expected_event_id = "c790e29519cc43ad87a4e061c36b4740cf1085e2c9eabb6971ea97f3859eb008",
|
||||
.expected_signature = "9acb3e409a8b329316bd4184ad74a50db7764a4370ad863f97fb37858d87c380c9299a7adef19dfd29481f51eb81e28ebba2a6d2bbcc4085a1b07ca8339e8d0c",
|
||||
.expected_json = "{\n"
|
||||
"\t\"pubkey\":\t\"a11258677dd416ca4c9e352e0e02ad2d8784a18c3a963604d0c63dc7b74eec66\",\n"
|
||||
"\t\"created_at\":\t1698623783,\n"
|
||||
"\t\"kind\":\t1,\n"
|
||||
"\t\"tags\":\t[],\n"
|
||||
"\t\"content\":\t\"Hello\",\n"
|
||||
"\t\"id\":\t\"c790e29519cc43ad87a4e061c36b4740cf1085e2c9eabb6971ea97f3859eb008\",\n"
|
||||
"\t\"sig\":\t\"9acb3e409a8b329316bd4184ad74a50db7764a4370ad863f97fb37858d87c380c9299a7adef19dfd29481f51eb81e28ebba2a6d2bbcc4085a1b07ca8339e8d0c\"\n"
|
||||
"}"
|
||||
},
|
||||
{
|
||||
// Vector 2 expected event
|
||||
.expected_event_id = "e28fda46caa56eb6f62c7871409e6c76cd43a47fca14878b91e49d8ee8e52c27",
|
||||
.expected_signature = "7a7ce178e18b1065a9642985a3fb815ed52772c34fc6e67515de012558968f6428509b9cf93cf6faf17db387b833196a5be48ed1154c1c2dffb1c30293318e3d",
|
||||
.expected_json = "{\n"
|
||||
"\t\"pubkey\":\t\"17162c921dc4d2518f9a101db33695df1afb56ab82f5ff3e5da6eec3ca5cd917\",\n"
|
||||
"\t\"created_at\":\t1698623783,\n"
|
||||
"\t\"kind\":\t1,\n"
|
||||
"\t\"tags\":\t[],\n"
|
||||
"\t\"content\":\t\"Hello\",\n"
|
||||
"\t\"id\":\t\"e28fda46caa56eb6f62c7871409e6c76cd43a47fca14878b91e49d8ee8e52c27\",\n"
|
||||
"\t\"sig\":\t\"7a7ce178e18b1065a9642985a3fb815ed52772c34fc6e67515de012558968f6428509b9cf93cf6faf17db387b833196a5be48ed1154c1c2dffb1c30293318e3d\"\n"
|
||||
"}"
|
||||
},
|
||||
{
|
||||
// Vector 3 expected event
|
||||
.expected_event_id = "ad349fdb162ea874d8b685e682b9dcc84b5bd72c4efac51e295db39b7623cde0",
|
||||
.expected_signature = "11e2280cca6f2e0f638fbf60f8aa744a4c228ba19f4d787a51298ec23be4a226e5046477cf6444a804c81aa08dd287e9647f0b45f8a02700da4a387187d4b3dc",
|
||||
.expected_json = "{\n"
|
||||
"\t\"pubkey\":\t\"d41b22899549e1f3d335a31002cfd382174006e166d3e658e3a5eecdb6463573\",\n"
|
||||
"\t\"created_at\":\t1698623783,\n"
|
||||
"\t\"kind\":\t1,\n"
|
||||
"\t\"tags\":\t[],\n"
|
||||
"\t\"content\":\t\"Hello\",\n"
|
||||
"\t\"id\":\t\"ad349fdb162ea874d8b685e682b9dcc84b5bd72c4efac51e295db39b7623cde0\",\n"
|
||||
"\t\"sig\":\t\"11e2280cca6f2e0f638fbf60f8aa744a4c228ba19f4d787a51298ec23be4a226e5046477cf6444a804c81aa08dd287e9647f0b45f8a02700da4a387187d4b3dc\"\n"
|
||||
"}"
|
||||
}
|
||||
};
|
||||
|
||||
// Helper functions
|
||||
static void print_test_result(const char* test_name, int passed, const char* expected, const char* actual) {
|
||||
if (passed) {
|
||||
printf("✓ %s: PASSED\n", test_name);
|
||||
} else {
|
||||
printf("❌ %s: FAILED\n", test_name);
|
||||
printf(" Expected: %s\n", expected);
|
||||
printf(" Actual: %s\n", actual);
|
||||
}
|
||||
}
|
||||
|
||||
static void bytes_to_hex_lowercase(const unsigned char* bytes, size_t len, char* hex_out) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
sprintf(hex_out + i * 2, "%02x", bytes[i]);
|
||||
}
|
||||
hex_out[len * 2] = '\0';
|
||||
}
|
||||
|
||||
// Test single vector for mnemonic to keys
|
||||
static int test_single_vector_mnemonic_to_keys(const test_vector_t* vector, unsigned char* private_key_out, unsigned char* public_key_out) {
|
||||
printf("\n=== Testing %s: Mnemonic to Keys ===\n", vector->name);
|
||||
printf("Input mnemonic: %s\n", vector->mnemonic);
|
||||
printf("Input account: 0\n");
|
||||
|
||||
unsigned char private_key[32];
|
||||
unsigned char public_key[32];
|
||||
|
||||
// Derive keys from mnemonic using account 0
|
||||
printf("Calling nostr_derive_keys_from_mnemonic()...\n");
|
||||
int ret = nostr_derive_keys_from_mnemonic(vector->mnemonic, 0, private_key, public_key);
|
||||
printf("Function returned: %d\n", ret);
|
||||
|
||||
if (ret != NOSTR_SUCCESS) {
|
||||
printf("❌ Key derivation failed with code: %d\n", ret);
|
||||
printf("Expected: Success (0)\n");
|
||||
printf("Actual: Error (%d)\n", ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Convert private key to hex (lowercase)
|
||||
char nsec_hex[65];
|
||||
bytes_to_hex_lowercase(private_key, 32, nsec_hex);
|
||||
|
||||
// Convert public key to hex (lowercase)
|
||||
char npub_hex[65];
|
||||
bytes_to_hex_lowercase(public_key, 32, npub_hex);
|
||||
|
||||
// Test nsecHex
|
||||
printf("\nPrivate Key (nsecHex):\n");
|
||||
printf("Expected: %s\n", vector->expected_nsec_hex);
|
||||
printf("Actual: %s\n", nsec_hex);
|
||||
int nsec_hex_match = (strcmp(nsec_hex, vector->expected_nsec_hex) == 0);
|
||||
printf("Result: %s\n", nsec_hex_match ? "✓ PASS" : "❌ FAIL");
|
||||
|
||||
// Test npubHex
|
||||
printf("\nPublic Key (npubHex):\n");
|
||||
printf("Expected: %s\n", vector->expected_npub_hex);
|
||||
printf("Actual: %s\n", npub_hex);
|
||||
int npub_hex_match = (strcmp(npub_hex, vector->expected_npub_hex) == 0);
|
||||
printf("Result: %s\n", npub_hex_match ? "✓ PASS" : "❌ FAIL");
|
||||
|
||||
// Copy keys for use in other tests
|
||||
if (private_key_out) memcpy(private_key_out, private_key, 32);
|
||||
if (public_key_out) memcpy(public_key_out, public_key, 32);
|
||||
|
||||
return nsec_hex_match && npub_hex_match;
|
||||
}
|
||||
|
||||
// Test single vector for nsec encoding
|
||||
static int test_single_vector_nsec_encoding(const test_vector_t* vector, const unsigned char* private_key) {
|
||||
printf("\n=== Testing %s: nsec Encoding ===\n", vector->name);
|
||||
|
||||
// Show input private key in hex
|
||||
char private_key_hex[65];
|
||||
bytes_to_hex_lowercase(private_key, 32, private_key_hex);
|
||||
printf("Input private key (hex): %s\n", private_key_hex);
|
||||
|
||||
char nsec[100];
|
||||
printf("Calling nostr_key_to_bech32() with hrp='nsec'...\n");
|
||||
int ret = nostr_key_to_bech32(private_key, "nsec", nsec);
|
||||
printf("Function returned: %d\n", ret);
|
||||
|
||||
if (ret != NOSTR_SUCCESS) {
|
||||
printf("❌ nsec encoding failed with code: %d\n", ret);
|
||||
printf("Expected: Success (0)\n");
|
||||
printf("Actual: Error (%d)\n", ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("\nnsec Encoding:\n");
|
||||
printf("Expected: %s\n", vector->expected_nsec);
|
||||
printf("Actual: %s\n", nsec);
|
||||
int nsec_match = (strcmp(nsec, vector->expected_nsec) == 0);
|
||||
printf("Result: %s\n", nsec_match ? "✓ PASS" : "❌ FAIL");
|
||||
|
||||
return nsec_match;
|
||||
}
|
||||
|
||||
// Test single vector for npub encoding
|
||||
static int test_single_vector_npub_encoding(const test_vector_t* vector, const unsigned char* public_key) {
|
||||
printf("\n=== Testing %s: npub Encoding ===\n", vector->name);
|
||||
|
||||
char npub[100];
|
||||
int ret = nostr_key_to_bech32(public_key, "npub", npub);
|
||||
|
||||
if (ret != NOSTR_SUCCESS) {
|
||||
printf("❌ npub encoding failed with code: %d\n", ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int npub_match = (strcmp(npub, vector->expected_npub) == 0);
|
||||
print_test_result("npub encoding", npub_match, vector->expected_npub, npub);
|
||||
|
||||
return npub_match;
|
||||
}
|
||||
|
||||
// Test single vector for event generation
|
||||
static int test_single_vector_event_generation(const test_vector_t* vector, const unsigned char* private_key, size_t vector_index) {
|
||||
printf("\n=== Testing %s: Signed Event Generation ===\n", vector->name);
|
||||
|
||||
// Create and sign event with fixed timestamp
|
||||
cJSON* event = nostr_create_and_sign_event(1, TEST_CONTENT, NULL, 0, private_key, TEST_CREATED_AT);
|
||||
|
||||
if (!event) {
|
||||
printf("❌ Event creation failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Extract event fields
|
||||
cJSON* id_item = cJSON_GetObjectItem(event, "id");
|
||||
cJSON* pubkey_item = cJSON_GetObjectItem(event, "pubkey");
|
||||
cJSON* created_at_item = cJSON_GetObjectItem(event, "created_at");
|
||||
cJSON* kind_item = cJSON_GetObjectItem(event, "kind");
|
||||
cJSON* content_item = cJSON_GetObjectItem(event, "content");
|
||||
cJSON* sig_item = cJSON_GetObjectItem(event, "sig");
|
||||
cJSON* tags_item = cJSON_GetObjectItem(event, "tags");
|
||||
|
||||
if (!id_item || !pubkey_item || !created_at_item || !kind_item ||
|
||||
!content_item || !sig_item || !tags_item) {
|
||||
printf("❌ Event missing required fields\n");
|
||||
cJSON_Delete(event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Validate field types
|
||||
if (!cJSON_IsString(id_item) || !cJSON_IsString(pubkey_item) ||
|
||||
!cJSON_IsNumber(created_at_item) || !cJSON_IsNumber(kind_item) ||
|
||||
!cJSON_IsString(content_item) || !cJSON_IsString(sig_item) ||
|
||||
!cJSON_IsArray(tags_item)) {
|
||||
printf("❌ Event fields have wrong types\n");
|
||||
cJSON_Delete(event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Extract values
|
||||
const char* event_id = cJSON_GetStringValue(id_item);
|
||||
const char* pubkey = cJSON_GetStringValue(pubkey_item);
|
||||
uint32_t created_at = (uint32_t)cJSON_GetNumberValue(created_at_item);
|
||||
int kind = (int)cJSON_GetNumberValue(kind_item);
|
||||
const char* content = cJSON_GetStringValue(content_item);
|
||||
const char* signature = cJSON_GetStringValue(sig_item);
|
||||
|
||||
// Test each field
|
||||
int tests_passed = 0;
|
||||
int total_tests = 7;
|
||||
|
||||
// Test kind
|
||||
if (kind == 1) {
|
||||
printf("✓ Event kind: PASSED (1)\n");
|
||||
tests_passed++;
|
||||
} else {
|
||||
printf("❌ Event kind: FAILED (expected 1, got %d)\n", kind);
|
||||
}
|
||||
|
||||
// Test pubkey - only check for first vector since we have expected values for that one
|
||||
if (strcmp(vector->name, "Vector 1") == 0) {
|
||||
int pubkey_match = (strcmp(pubkey, vector->expected_npub_hex) == 0);
|
||||
print_test_result("Event pubkey", pubkey_match, vector->expected_npub_hex, pubkey);
|
||||
if (pubkey_match) tests_passed++;
|
||||
} else {
|
||||
// For other vectors, just check that pubkey matches the expected public key
|
||||
int pubkey_match = (strcmp(pubkey, vector->expected_npub_hex) == 0);
|
||||
print_test_result("Event pubkey", pubkey_match, vector->expected_npub_hex, pubkey);
|
||||
if (pubkey_match) tests_passed++;
|
||||
}
|
||||
|
||||
// Test created_at
|
||||
if (created_at == TEST_CREATED_AT) {
|
||||
printf("✓ Event created_at: PASSED (%u)\n", created_at);
|
||||
tests_passed++;
|
||||
} else {
|
||||
printf("❌ Event created_at: FAILED (expected %u, got %u)\n", TEST_CREATED_AT, created_at);
|
||||
}
|
||||
|
||||
// Test content
|
||||
int content_match = (strcmp(content, TEST_CONTENT) == 0);
|
||||
print_test_result("Event content", content_match, TEST_CONTENT, content);
|
||||
if (content_match) tests_passed++;
|
||||
|
||||
// Test tags (should be empty array)
|
||||
int tags_empty = (cJSON_GetArraySize(tags_item) == 0);
|
||||
if (tags_empty) {
|
||||
printf("✓ Event tags: PASSED (empty array)\n");
|
||||
tests_passed++;
|
||||
} else {
|
||||
printf("❌ Event tags: FAILED (expected empty array, got %d items)\n",
|
||||
cJSON_GetArraySize(tags_item));
|
||||
}
|
||||
|
||||
// Get expected event for this vector
|
||||
const expected_event_t* expected = &EXPECTED_EVENTS[vector_index];
|
||||
|
||||
// Test event ID and signature
|
||||
int id_match = (strcmp(event_id, expected->expected_event_id) == 0);
|
||||
print_test_result("Event ID", id_match, expected->expected_event_id, event_id);
|
||||
if (id_match) tests_passed++;
|
||||
|
||||
int sig_match = (strcmp(signature, expected->expected_signature) == 0);
|
||||
print_test_result("Event signature", sig_match, expected->expected_signature, signature);
|
||||
if (sig_match) tests_passed++;
|
||||
|
||||
// Print expected vs generated event JSONs side by side
|
||||
printf("\n=== EXPECTED EVENT JSON ===\n");
|
||||
printf("%s\n", expected->expected_json);
|
||||
|
||||
printf("\n=== GENERATED EVENT JSON ===\n");
|
||||
char* event_json = cJSON_Print(event);
|
||||
if (event_json) {
|
||||
printf("%s\n", event_json);
|
||||
free(event_json);
|
||||
}
|
||||
|
||||
cJSON_Delete(event);
|
||||
|
||||
printf("\nEvent generation: %d/%d tests passed\n", tests_passed, total_tests);
|
||||
return (tests_passed == total_tests);
|
||||
}
|
||||
|
||||
// Test all vectors
|
||||
static int test_all_vectors() {
|
||||
printf("\n=== Testing All Vectors ===\n");
|
||||
|
||||
int total_vectors_passed = 0;
|
||||
|
||||
for (size_t i = 0; i < NUM_TEST_VECTORS; i++) {
|
||||
const test_vector_t* vector = &TEST_VECTORS[i];
|
||||
printf("\n" "==========================================\n");
|
||||
printf("Testing %s\n", vector->name);
|
||||
printf("==========================================\n");
|
||||
|
||||
unsigned char private_key[32];
|
||||
unsigned char public_key[32];
|
||||
|
||||
// Step 1: Test mnemonic to keys
|
||||
int keys_passed = test_single_vector_mnemonic_to_keys(vector, private_key, public_key);
|
||||
|
||||
// Step 2: Test nsec encoding
|
||||
int nsec_passed = test_single_vector_nsec_encoding(vector, private_key);
|
||||
|
||||
// Step 3: Test npub encoding
|
||||
int npub_passed = test_single_vector_npub_encoding(vector, public_key);
|
||||
|
||||
// Step 4: Test event generation (only if keys work)
|
||||
int event_passed = 0;
|
||||
if (keys_passed) {
|
||||
event_passed = test_single_vector_event_generation(vector, private_key, i);
|
||||
}
|
||||
|
||||
// Summary for this vector
|
||||
printf("\n%s Summary:\n", vector->name);
|
||||
printf(" Keys: %s\n", keys_passed ? "✓ PASS" : "❌ FAIL");
|
||||
printf(" nsec: %s\n", nsec_passed ? "✓ PASS" : "❌ FAIL");
|
||||
printf(" npub: %s\n", npub_passed ? "✓ PASS" : "❌ FAIL");
|
||||
printf(" Event: %s\n", event_passed ? "✓ PASS" : "❌ FAIL");
|
||||
|
||||
if (keys_passed && nsec_passed && npub_passed && event_passed) {
|
||||
printf(" Overall: ✓ PASS\n");
|
||||
total_vectors_passed++;
|
||||
} else {
|
||||
printf(" Overall: ❌ FAIL\n");
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n" "==========================================\n");
|
||||
printf("FINAL RESULTS: %d/%zu vectors passed\n", total_vectors_passed, NUM_TEST_VECTORS);
|
||||
printf("==========================================\n");
|
||||
|
||||
return (total_vectors_passed == (int)NUM_TEST_VECTORS);
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("NOSTR Event Generation Test Suite\n");
|
||||
printf("=================================\n");
|
||||
printf("Testing against multiple test vectors for ecosystem compatibility\n");
|
||||
|
||||
// Initialize NOSTR library
|
||||
if (nostr_init() != NOSTR_SUCCESS) {
|
||||
printf("❌ Failed to initialize NOSTR library\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Run all vector tests
|
||||
int success = test_all_vectors();
|
||||
|
||||
// Print final results
|
||||
printf("\n=================================\n");
|
||||
if (success) {
|
||||
printf("🎉 ALL TEST VECTORS PASSED!\n");
|
||||
printf("✅ Your NOSTR implementation produces the exact same results as all test vectors\n");
|
||||
printf("✅ This confirms compatibility with other NOSTR tools\n");
|
||||
} else {
|
||||
printf("❌ SOME TEST VECTORS FAILED\n");
|
||||
printf("❌ Your implementation produces different results than expected\n");
|
||||
printf("❌ This indicates compatibility issues with other NOSTR tools\n");
|
||||
|
||||
printf("\nFor debugging purposes, review the detailed output above to see:\n");
|
||||
printf(" - Which vectors passed/failed\n");
|
||||
printf(" - Expected vs actual values for each test\n");
|
||||
printf(" - Whether the issue is in key derivation, encoding, or event generation\n");
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
nostr_cleanup();
|
||||
|
||||
return success ? 0 : 1;
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* WebSocket SSL Test - Test OpenSSL WebSocket implementation
|
||||
* Connect to a NOSTR relay and fetch one type 1 event
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "../nostr_core/nostr_core.h"
|
||||
#include "../cjson/cJSON.h"
|
||||
|
||||
// Progress callback to show connection status
|
||||
static void progress_callback(
|
||||
const char* relay_url,
|
||||
const char* status,
|
||||
const char* event_id,
|
||||
int events_received,
|
||||
int total_relays,
|
||||
int completed_relays,
|
||||
void* user_data)
|
||||
{
|
||||
printf("Progress: %s - %s", relay_url ? relay_url : "Summary", status);
|
||||
if (event_id) {
|
||||
printf(" (Event: %.12s...)", event_id);
|
||||
}
|
||||
printf(" [%d/%d events, %d/%d relays]\n",
|
||||
events_received, *(int*)user_data, completed_relays, total_relays);
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("WebSocket SSL Test - Testing OpenSSL WebSocket with NOSTR relay\n");
|
||||
printf("================================================================\n");
|
||||
|
||||
// Initialize NOSTR library
|
||||
if (nostr_init() != NOSTR_SUCCESS) {
|
||||
printf("❌ Failed to initialize NOSTR library\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("✅ NOSTR library initialized\n");
|
||||
|
||||
// Setup relay and filter
|
||||
const char* relay_urls[] = {"wss://nostr.mom"};
|
||||
int relay_count = 1;
|
||||
|
||||
// Create filter for type 1 events (text notes), limit to 1 event
|
||||
cJSON* filter = cJSON_CreateObject();
|
||||
cJSON* kinds = cJSON_CreateArray();
|
||||
cJSON_AddItemToArray(kinds, cJSON_CreateNumber(1));
|
||||
cJSON_AddItemToObject(filter, "kinds", kinds);
|
||||
cJSON_AddItemToObject(filter, "limit", cJSON_CreateNumber(1));
|
||||
|
||||
printf("📡 Connecting to %s...\n", relay_urls[0]);
|
||||
printf("🔍 Requesting 1 type 1 event (text note)...\n\n");
|
||||
|
||||
// Query the relay
|
||||
int result_count = 0;
|
||||
int expected_events = 1;
|
||||
cJSON** events = synchronous_query_relays_with_progress(
|
||||
relay_urls,
|
||||
relay_count,
|
||||
filter,
|
||||
RELAY_QUERY_FIRST_RESULT, // Return as soon as we get the first event
|
||||
&result_count,
|
||||
10, // 10 second timeout
|
||||
progress_callback,
|
||||
&expected_events
|
||||
);
|
||||
|
||||
// Process results
|
||||
if (events && result_count > 0) {
|
||||
printf("\n✅ Successfully received %d event(s)!\n", result_count);
|
||||
printf("📄 Raw JSON event data:\n");
|
||||
printf("========================\n");
|
||||
|
||||
for (int i = 0; i < result_count; i++) {
|
||||
char* json_string = cJSON_Print(events[i]);
|
||||
if (json_string) {
|
||||
printf("%s\n\n", json_string);
|
||||
free(json_string);
|
||||
}
|
||||
cJSON_Delete(events[i]);
|
||||
}
|
||||
free(events);
|
||||
|
||||
printf("🎉 WebSocket SSL Test PASSED - OpenSSL WebSocket working correctly!\n");
|
||||
} else {
|
||||
printf("\n❌ No events received or query failed\n");
|
||||
printf("❌ WebSocket SSL Test FAILED\n");
|
||||
|
||||
// Cleanup and return error
|
||||
cJSON_Delete(filter);
|
||||
nostr_cleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
cJSON_Delete(filter);
|
||||
nostr_cleanup();
|
||||
|
||||
printf("✅ WebSocket connection and TLS working with OpenSSL\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user