nostr_core_lib/tests/crypto_test.c

479 lines
15 KiB
C

/*
* 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) {
printf(" %s:\n", test_name);
printf(" Expected: ");
for (size_t i = 0; i < len; i++) printf("%02x", expected[i]);
printf("\n Actual: ");
for (size_t i = 0; i < len; i++) printf("%02x", result[i]);
printf("\n");
if (memcmp(result, expected, len) == 0) {
printf(" ✓ PASSED\n\n");
return 1;
} else {
printf(" ❌ FAILED\n\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";
printf(" PBKDF2 BIP39 seed generation:\n");
printf(" Input: \"%s\"\n", mnemonic);
printf(" Salt: \"mnemonic\"\n");
printf(" Iterations: 2048\n");
int ret = nostr_pbkdf2_hmac_sha512((const unsigned char*)mnemonic, strlen(mnemonic),
(const unsigned char*)"mnemonic", 8,
2048, result, 64);
if (ret == 0) {
printf(" Result: ");
for (int i = 0; i < 64; i++) printf("%02x", result[i]);
printf("\n ✓ PASSED\n\n");
return 1;
} else {
printf(" Result: FAILED (return code: %d)\n", ret);
printf(" ❌ FAILED\n\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];
printf(" BIP39 entropy to mnemonic:\n");
printf(" Entropy: ");
for (int i = 0; i < 16; i++) printf("%02x", entropy[i]);
printf("\n");
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(" Result: %s\n", mnemonic);
printf(" ✓ PASSED\n\n");
return 1;
} else {
printf(" Result: FAILED (return code: %d)\n", ret);
printf(" ❌ FAILED\n\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;
}