/* * NOSTR Core Library - NIP-006: Key Derivation from Mnemonic */ #include "nip006.h" #include "utils.h" #include #include #include #include #include #include "../nostr_core/nostr_common.h" int nostr_generate_keypair(unsigned char* private_key, unsigned char* public_key) { if (!private_key || !public_key) { return NOSTR_ERROR_INVALID_INPUT; } // Generate random entropy using /dev/urandom FILE* urandom = fopen("/dev/urandom", "rb"); if (!urandom) { return NOSTR_ERROR_IO_FAILED; } if (fread(private_key, 1, 32, urandom) != 32) { fclose(urandom); return NOSTR_ERROR_IO_FAILED; } fclose(urandom); // Validate private key if (nostr_ec_private_key_verify(private_key) != 0) { return NOSTR_ERROR_CRYPTO_FAILED; } // Generate public key from private key (already x-only for NOSTR) if (nostr_ec_public_key_from_private_key(private_key, public_key) != 0) { return NOSTR_ERROR_CRYPTO_FAILED; } return NOSTR_SUCCESS; } int nostr_generate_mnemonic_and_keys(char* mnemonic, size_t mnemonic_size, int account, unsigned char* private_key, unsigned char* public_key) { if (!mnemonic || mnemonic_size < 256 || !private_key || !public_key) { return NOSTR_ERROR_INVALID_INPUT; } // Generate entropy for 12-word mnemonic unsigned char entropy[16]; FILE* urandom = fopen("/dev/urandom", "rb"); if (!urandom) { return NOSTR_ERROR_IO_FAILED; } if (fread(entropy, 1, sizeof(entropy), urandom) != sizeof(entropy)) { fclose(urandom); return NOSTR_ERROR_IO_FAILED; } fclose(urandom); // Generate mnemonic from entropy if (nostr_bip39_mnemonic_from_bytes(entropy, sizeof(entropy), mnemonic) != 0) { return NOSTR_ERROR_CRYPTO_FAILED; } // Derive keys from the generated mnemonic return nostr_derive_keys_from_mnemonic(mnemonic, account, private_key, public_key); } int nostr_derive_keys_from_mnemonic(const char* mnemonic, int account, unsigned char* private_key, unsigned char* public_key) { if (!mnemonic || !private_key || !public_key) { return NOSTR_ERROR_INVALID_INPUT; } // Validate mnemonic if (nostr_bip39_mnemonic_validate(mnemonic) != 0) { return NOSTR_ERROR_INVALID_INPUT; } // Convert mnemonic to seed unsigned char seed[64]; if (nostr_bip39_mnemonic_to_seed(mnemonic, "", seed, sizeof(seed)) != 0) { return NOSTR_ERROR_CRYPTO_FAILED; } // Derive master key from seed nostr_hd_key_t master_key; if (nostr_bip32_key_from_seed(seed, sizeof(seed), &master_key) != 0) { return NOSTR_ERROR_CRYPTO_FAILED; } // NIP-06 path: m/44'/1237'/account'/0/0 nostr_hd_key_t derived_key; uint32_t path[] = { 0x80000000 + 44, // 44' (hardened) 0x80000000 + 1237, // 1237' (hardened) 0x80000000 + account, // account' (hardened) 0, // 0 (not hardened) 0 // 0 (not hardened) }; if (nostr_bip32_derive_path(&master_key, path, sizeof(path) / sizeof(path[0]), &derived_key) != 0) { return NOSTR_ERROR_CRYPTO_FAILED; } // Extract private key and public key memcpy(private_key, derived_key.private_key, 32); memcpy(public_key, derived_key.public_key + 1, 32); // Remove compression prefix for x-only return NOSTR_SUCCESS; } // Note: nostr_detect_input_type, nostr_decode_nsec, and nostr_decode_npub // are implemented in NIP-019 to avoid multiple definitions