119 lines
3.7 KiB
C
119 lines
3.7 KiB
C
/*
|
|
* NOSTR Core Library - NIP-006: Key Derivation from Mnemonic
|
|
*/
|
|
|
|
#include "nip006.h"
|
|
#include "utils.h"
|
|
#include "nostr_crypto.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <time.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
|