468 lines
15 KiB
C
468 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/nostr_crypto.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_rfc6070_test1() {
|
|
unsigned char result[20];
|
|
unsigned char expected[20];
|
|
|
|
// RFC 6070 Test Case 1
|
|
// P = "password", S = "salt", c = 1, dkLen = 20
|
|
// DK = 0c60c80f961f0e71f3a9b524af6012062fe037a6
|
|
|
|
hex_to_bytes("0c60c80f961f0e71f3a9b524af6012062fe037a6", expected, 20);
|
|
|
|
nostr_pbkdf2_hmac_sha512((const unsigned char*)"password", 8,
|
|
(const unsigned char*)"salt", 4,
|
|
1, result, 20);
|
|
|
|
return test_bytes_equal("PBKDF2 RFC6070 Test 1", result, expected, 20);
|
|
}
|
|
|
|
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, sizeof(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;
|
|
}
|