From 5db1a5ecc93549fdedf4ab57e305778d6ea7e6b8 Mon Sep 17 00:00:00 2001 From: edtubbs Date: Fri, 12 Jan 2024 14:48:45 -0600 Subject: [PATCH] wallet: added prompt to dogecoin_wallet_load headersdb: added prompt to headerdb load headersdb_file: added prompt to dogecoin_headers_db_load headersdb_file: removed check from dogecoin_headers_db_free seal: moved memory allocations for TEST_PASSWD spv: added prompt to client load spv: moved fcntl to dogecoin_spv_client_discover_peers spvnode: added prompt to client load calls spvnode: added handle_sigint to reset stdin to blocking spvnode: added dogecoin_spv_client_free and dogecoin_ecc_stop on failure wallet: added prompt to dogecoin_wallet_init wallet: added prompt to dogecoin_wallet_load wallet: added no prompt to wallet API load calls wallet: added encryption of random seed with software wallet: added dogecoin_wallet_free to dogecoin_wallet_init on failures such: added seed_to_master_key command doc: added seed_to_master_key to tools.md doc: added no_prompt to tools.md test: added no prompt to spv_tests test: added no prompt to wallet_tests --- doc/tools.md | 17 +++++-- include/dogecoin/headersdb.h | 2 +- include/dogecoin/headersdb_file.h | 4 +- include/dogecoin/spv.h | 2 +- include/dogecoin/wallet.h | 4 +- src/cli/spvnode.c | 33 +++++++++++--- src/cli/such.c | 46 +++++++++++++++++++ src/headersdb_file.c | 33 +++++++++++--- src/seal.c | 27 +++++++---- src/spv.c | 17 +++---- src/wallet.c | 75 +++++++++++++++++++++++++++---- test/spv_tests.c | 4 +- test/wallet_tests.c | 8 ++-- 13 files changed, 218 insertions(+), 54 deletions(-) diff --git a/doc/tools.md b/doc/tools.md index a8f461f59..0610e98c7 100644 --- a/doc/tools.md +++ b/doc/tools.md @@ -20,6 +20,7 @@ The `such` tool can be used by simply running the command `./such` in the top le - list_encryption_keys_in_tpm - decrypt_master_key - decrypt_mnemonic +- seed_to_master_key - mnemonic_to_key - mnemonic_to_addresses - print_keys @@ -47,10 +48,10 @@ Most of these commands require a flag following them to denote things like exist | -o, --account_int | account_int | yes | mnemonic_to_key or mnemonic_to_addresses -n -o | | -g, --change_level | change_level | yes | mnemonic_to_key or mnemonic_to_addresses -n -g | | -i, --address_index | address_index | yes | mnemonic_to_key or mnemonic_to_addresses -n -i | -| -y, --encrypted_file | file_num | yes | generate_mnemonic, bip32_extended_master_key, decrypt_master_key, decrypt_mnemonic, mnemonic_to_key or mnemonic_to_addresses -y +| -y, --encrypted_file | file_num | yes | generate_mnemonic, bip32_extended_master_key, decrypt_master_key, decrypt_mnemonic, seed_to_master_key, mnemonic_to_key or mnemonic_to_addresses -y | -w, --overwrite | overwrite | no | generate_mnemonic or bip32_extended_master_key -w | | -b, --silent | silent | no | generate_mnemonic or bip32_extended_master_key -b | -| -j, --use_tpm | use_tpm | no | generate_mnemonic, bip32_extended_master_key, decrypt_master_key, decrypt_mnemonic, mnemonic_to_key or mnemonic_to_addresses -j | +| -j, --use_tpm | use_tpm | no | generate_mnemonic, bip32_extended_master_key, decrypt_master_key, decrypt_mnemonic, seed_to_master_key, mnemonic_to_key or mnemonic_to_addresses -j | | -t, --testnet | designate_testnet | no | generate_private_key -t | | -s | script_hex | yes | comp2der -s | | -x | transaction_hex | yes | sign -x -s -i -h | @@ -71,6 +72,7 @@ Below is a list of all the commands and the flags that they require. As a remind | list_encryption_keys_in_tpm | None | None | List the encryption keys in the TPM. | | decrypt_master_key | -y | -j | Decrypt the master key with the TPM or SW. | | decrypt_mnemonic | -y | -j | Decrypt the mnemonic with the TPM or SW. | +| seed_to_master_key | -y | -j, -t | Generates an extended master private key from a seed for either mainnet or testnet. | | mnemonic_to_key | -n | -a, -y, -o, g, -i, -t | Generates a private key from a seed phrase with a default path or specified account, change level and index for either mainnet or testnet. | | mnemonic_to_addresses | -n | -a, -y, -o, g, -i, -t | Generates an address from a seed phrase with a default path or specified account, change level and index for either mainnet or testnet. | | print_keys | -p | -t | Print all keys associated with the provided private key. @@ -172,9 +174,9 @@ Below are some examples on how to use the `such` tool in practice. ./such -c verifymessage -x bleh -s ICrbftD0KamyaB68IoXbeke3w4CpcIvv+Q4pncBNpMk8fF5+xsR9H9gqmfM0JrjlfzZZA3E8AJ0Nug1KWeoVw3g= -k D8mQ2sKYpLbFCQLhGeHCPBmkLJRi6kRoSg Message is verified! -## Encrypted Mnemonics and Key Backups +## Encrypted Mnemonics, Key and Seed Backups -The `such` tool provides functionality to securely manage your encrypted mnemonics and key backups. With the ability to generate mnemonics and encrypt them for safe storage, and to decrypt them when needed, managing your cryptographic assets is made easier. To use encrypted files with `spvnode`, you must first use the `such` tool to generate and encrypt your mnemonic or master key. You can then use the `spvnode` tool to import the encrypted file and use it to connect to the network. +The `such` tool provides functionality to securely manage your encrypted mnemonics, key and seed backups. With the ability to generate mnemonics and encrypt them for safe storage, and to decrypt them when needed, managing your cryptographic assets is made easier. To use encrypted files with `spvnode`, you must first use the `such` tool to generate and encrypt your mnemonic or master key. You can then use the `spvnode` tool to import the encrypted file and use it to connect to the network. ### Generating and Encrypting Mnemonics @@ -218,6 +220,12 @@ And to decrypt it back when required: Always ensure to replace `` with the actual number of the encrypted file. +### Handling Seed Backups + +You can also decrypt your seed backups using the `seed_to_master_key` command. This command will decrypt the seed and generate a master key from it. If the seed was encrypted using TPM (Trusted Platform Module), you can use the `-j` flag as shown: + + ./such -c seed_to_master_key -y -j + ### Overwriting Encrypted Files If you want to overwrite an existing encrypted file, you can use the `-w` flag as shown: @@ -361,6 +369,7 @@ To utilize checkpoints for faster initial sync, apply the -p flag: | `-p`, `--checkpoint` | Checkpoint | No | Enable checkpoint sync: `./spvnode -p scan` | | `-w`, `--wallet_file` | Wallet File | Yes | Specify wallet file: `./spvnode -w "./wallet.db" scan` | | `-h`, `--headers_file` | Headers File | Yes | Specify headers DB file: `./spvnode -h "./headers.db" scan` | +| `-l`, `--no_prompt` | No Prompt | No | Load wallet and headers without prompt: `./spvnode -l scan` | | `-y`, `--encrypted_file` | Encrypted File | Yes | Use encrypted file: `./spvnode -y 0 scan` | | `-j`, `--use_tpm` | Use TPM | No | Utilize TPM for decryption: `./spvnode -j scan` | | `-k`, `--master_key` | Master Key | No | Use master key decryption: `./spvnode -k scan` | diff --git a/include/dogecoin/headersdb.h b/include/dogecoin/headersdb.h index 7b3eab28f..a98258b25 100644 --- a/include/dogecoin/headersdb.h +++ b/include/dogecoin/headersdb.h @@ -46,7 +46,7 @@ typedef struct dogecoin_headers_db_interface_ { void* (*init)(const dogecoin_chainparams* chainparams, dogecoin_bool inmem_only); void (*free)(void *db); - dogecoin_bool (*load)(void *db, const char *filename); + dogecoin_bool (*load)(void *db, const char *filename, dogecoin_bool prompt); void (*fill_blocklocator_tip)(void* db, vector *blocklocators); dogecoin_blockindex *(*connect_hdr)(void* db, struct const_buffer *buf, dogecoin_bool load_process, dogecoin_bool *connected); dogecoin_blockindex* (*getchaintip)(void *db); diff --git a/include/dogecoin/headersdb_file.h b/include/dogecoin/headersdb_file.h index c437a23c4..7e6c0328f 100644 --- a/include/dogecoin/headersdb_file.h +++ b/include/dogecoin/headersdb_file.h @@ -56,7 +56,7 @@ typedef struct dogecoin_headers_db_ dogecoin_headers_db *dogecoin_headers_db_new(const dogecoin_chainparams* chainparams, dogecoin_bool inmem_only); void dogecoin_headers_db_free(dogecoin_headers_db *db); -dogecoin_bool dogecoin_headers_db_load(dogecoin_headers_db* db, const char *filename); +dogecoin_bool dogecoin_headers_db_load(dogecoin_headers_db* db, const char *filename, dogecoin_bool prompt); dogecoin_blockindex * dogecoin_headers_db_connect_hdr(dogecoin_headers_db* db, struct const_buffer *buf, dogecoin_bool load_process, dogecoin_bool *connected); void dogecoin_headers_db_fill_block_locator(dogecoin_headers_db* db, vector *blocklocators); dogecoin_blockindex * dogecoin_headersdb_find(dogecoin_headers_db* db, uint256 hash); @@ -68,7 +68,7 @@ void dogecoin_headersdb_set_checkpoint_start(dogecoin_headers_db* db, uint256 ha static const dogecoin_headers_db_interface dogecoin_headers_db_interface_file = { (void* (*)(const dogecoin_chainparams*, dogecoin_bool))dogecoin_headers_db_new, (void (*)(void *))dogecoin_headers_db_free, - (dogecoin_bool (*)(void *, const char *))dogecoin_headers_db_load, + (dogecoin_bool (*)(void *, const char *, dogecoin_bool))dogecoin_headers_db_load, (void (*)(void* , vector *))dogecoin_headers_db_fill_block_locator, (dogecoin_blockindex *(*)(void* , struct const_buffer *, dogecoin_bool , dogecoin_bool *))dogecoin_headers_db_connect_hdr, (dogecoin_blockindex* (*)(void *))dogecoin_headersdb_getchaintip, diff --git a/include/dogecoin/spv.h b/include/dogecoin/spv.h index b453aa656..ae462d94d 100644 --- a/include/dogecoin/spv.h +++ b/include/dogecoin/spv.h @@ -66,7 +66,7 @@ typedef struct dogecoin_spv_client_ LIBDOGECOIN_API dogecoin_spv_client* dogecoin_spv_client_new(const dogecoin_chainparams *params, dogecoin_bool debug, dogecoin_bool headers_memonly, dogecoin_bool use_checkpoints, dogecoin_bool full_sync); LIBDOGECOIN_API void dogecoin_spv_client_free(dogecoin_spv_client *client); -LIBDOGECOIN_API dogecoin_bool dogecoin_spv_client_load(dogecoin_spv_client *client, const char *file_path); +LIBDOGECOIN_API dogecoin_bool dogecoin_spv_client_load(dogecoin_spv_client *client, const char *file_path, dogecoin_bool prompt); LIBDOGECOIN_API void dogecoin_spv_client_discover_peers(dogecoin_spv_client *client, const char *ips); LIBDOGECOIN_API void dogecoin_spv_client_runloop(dogecoin_spv_client *client); LIBDOGECOIN_API dogecoin_bool dogecoin_net_spv_request_headers(dogecoin_spv_client *client); diff --git a/include/dogecoin/wallet.h b/include/dogecoin/wallet.h index 8eb89c02d..cb4cc752d 100644 --- a/include/dogecoin/wallet.h +++ b/include/dogecoin/wallet.h @@ -129,12 +129,12 @@ LIBDOGECOIN_API void dogecoin_wallet_output_free(dogecoin_output* output); /** ------------------------------------ */ LIBDOGECOIN_API dogecoin_wallet* dogecoin_wallet_new(const dogecoin_chainparams *params); -LIBDOGECOIN_API dogecoin_wallet* dogecoin_wallet_init(const dogecoin_chainparams* chain, const char* address, const char* name, const char* mnemonic_in, const char* pass, const dogecoin_bool encrypted, const dogecoin_bool tpm, const int file_num, const dogecoin_bool master_key); +LIBDOGECOIN_API dogecoin_wallet* dogecoin_wallet_init(const dogecoin_chainparams* chain, const char* address, const char* name, const char* mnemonic_in, const char* pass, const dogecoin_bool encrypted, const dogecoin_bool tpm, const int file_num, const dogecoin_bool master_key, const dogecoin_bool prompt); LIBDOGECOIN_API void print_utxos(dogecoin_wallet* wallet); LIBDOGECOIN_API void dogecoin_wallet_free(dogecoin_wallet* wallet); /** load the wallet, sets masterkey, sets next_childindex */ -LIBDOGECOIN_API dogecoin_bool dogecoin_wallet_load(dogecoin_wallet* wallet, const char* file_path, int *error, dogecoin_bool *created); +LIBDOGECOIN_API dogecoin_bool dogecoin_wallet_load(dogecoin_wallet* wallet, const char* file_path, int *error, dogecoin_bool *created, dogecoin_bool prompt); /** load the wallet and replace a record */ LIBDOGECOIN_API dogecoin_bool dogecoin_wallet_replace(dogecoin_wallet* wallet, const char* file_path, cstring* record, uint8_t record_type, int *error); diff --git a/src/cli/spvnode.c b/src/cli/spvnode.c index d1c1c88ac..cd4065ec5 100644 --- a/src/cli/spvnode.c +++ b/src/cli/spvnode.c @@ -41,6 +41,7 @@ #endif #include +#include #include #include #include @@ -179,6 +180,7 @@ static struct option long_options[] = { {"checkpoint", no_argument, NULL, 'p'}, {"wallet_file", required_argument, NULL, 'w'}, {"headers_file", required_argument, NULL, 'h'}, + {"no_prompt", no_argument, NULL, 'l'}, {"encrypted_file", required_argument, NULL, 'y'}, {"use_tpm", no_argument, NULL, 'j'}, {"master_key", no_argument, NULL, 'k'}, @@ -199,7 +201,7 @@ static void print_usage() { print_version(); printf("Usage: spvnode (-c|continuous) (-i|-ips ) (-m[--maxpeers] ) (-f ) \ (-a[--address]
) (-n|-mnemonic ) (-s|-pass_phrase) (-y|-encrypted_file ) \ -(-w|-wallet_file ) (-h|-headers_file ) (-b[--full_sync]) (-p[--checkpoint]) (-k[--master_key] (-j[--use_tpm]) \ +(-w|-wallet_file ) (-h|-headers_file ) (-l|[--no_prompt]) (-b[--full_sync]) (-p[--checkpoint]) (-k[--master_key] (-j[--use_tpm]) \ (-t[--testnet]) (-r[--regtest]) (-d[--debug]) \n"); printf("Supported commands:\n"); printf(" scan (scan blocks up to the tip, creates header.db file)\n"); @@ -273,6 +275,16 @@ void spv_sync_completed(dogecoin_spv_client* client) { } } +// Signal handler for SIGINT +void handle_sigint() { + // Reset standard input back to blocking mode +#ifndef _WIN32 + int stdin_flags = fcntl(STDIN_FILENO, F_GETFL); + fcntl(STDIN_FILENO, F_SETFL, stdin_flags & ~O_NONBLOCK); +#endif + exit(0); +} + int main(int argc, char* argv[]) { int ret = 0; int long_index = 0; @@ -290,6 +302,7 @@ int main(int argc, char* argv[]) { char* headers_name = 0; dogecoin_bool full_sync = false; dogecoin_bool have_decl_daemon = false; + dogecoin_bool prompt = true; dogecoin_bool encrypted = false; dogecoin_bool master_key = false; dogecoin_bool tpm = false; @@ -303,7 +316,7 @@ int main(int argc, char* argv[]) { data = argv[argc - 1]; /* get arguments */ - while ((opt = getopt_long_only(argc, argv, "i:ctrdsm:n:f:y:w:h:a:bpzkj:", long_options, &long_index)) != -1) { + while ((opt = getopt_long_only(argc, argv, "i:ctrdsm:n:f:y:w:h:a:lbpzkj:", long_options, &long_index)) != -1) { switch (opt) { case 'c': quit_when_synced = false; @@ -341,6 +354,9 @@ int main(int argc, char* argv[]) { case 'h': headers_name = optarg; break; + case 'l': + prompt = false; + break; case 'y': encrypted = true; file_num = (int)strtol(optarg, (char**)NULL, 10); @@ -372,9 +388,10 @@ int main(int argc, char* argv[]) { dogecoin_spv_client* client = dogecoin_spv_client_new(chain, debug, (dbfile && (dbfile[0] == '0' || (strlen(dbfile) > 1 && dbfile[0] == 'n' && dbfile[0] == 'o'))) ? true : false, use_checkpoint, full_sync); client->header_message_processed = spv_header_message_processed; client->sync_completed = spv_sync_completed; + signal(SIGINT, handle_sigint); #if WITH_WALLET - dogecoin_wallet* wallet = dogecoin_wallet_init(chain, address, name, mnemonic_in, pass, encrypted, tpm, file_num, master_key); + dogecoin_wallet* wallet = dogecoin_wallet_init(chain, address, name, mnemonic_in, pass, encrypted, tpm, file_num, master_key, prompt); if (!wallet) { printf("Could not initialize wallet...\n"); // clear and free the passphrase @@ -382,6 +399,8 @@ int main(int argc, char* argv[]) { dogecoin_mem_zero (pass, strlen(pass)); dogecoin_free(pass); } + dogecoin_spv_client_free(client); + dogecoin_ecc_stop(); return EXIT_FAILURE; } // clear and free the passphrase @@ -405,19 +424,19 @@ int main(int argc, char* argv[]) { dogecoin_free(header_type_prefix); if (headers_name) { // Load headers file name with headers name: - response = dogecoin_spv_client_load(client, (dbfile ? dbfile : headers_name)); + response = dogecoin_spv_client_load(client, (dbfile ? dbfile : headers_name), prompt); } else { // Otherwise, use default headers file name: - response = dogecoin_spv_client_load(client, (dbfile ? dbfile : headersfile)); + response = dogecoin_spv_client_load(client, (dbfile ? dbfile : headersfile), prompt); } } else if (headers_name) { // Load headers file name with headers name: - response = dogecoin_spv_client_load(client, (dbfile ? dbfile : headers_name)); + response = dogecoin_spv_client_load(client, (dbfile ? dbfile : headers_name), prompt); } else { // Otherwise, use default headers file name: headersfile = concat(header_prefix, header_suffix); - response = dogecoin_spv_client_load(client, (dbfile ? dbfile : headersfile)); + response = dogecoin_spv_client_load(client, (dbfile ? dbfile : headersfile), prompt); } dogecoin_free(headersfile); diff --git a/src/cli/such.c b/src/cli/such.c index f8af0139d..4fe65c44a 100644 --- a/src/cli/such.c +++ b/src/cli/such.c @@ -651,6 +651,7 @@ static void print_usage() printf("list_encryption_keys_in_tpm,\n"); printf("decrypt_master_key (requires -y , -j (use_tpm) optional),\n"); printf("decrypt_mnemonic (requires -y , -j (use_tpm) optional),\n"); + printf("seed_to_master_key (-y , -j (use_tpm) optional),\n"); printf("mnemonic_to_key (requires -n or -y , -j (use_tpm), -o , -g , -i and -a, all optional),\n"); printf("mnemonic_to_addresses (requires -n or -y , -j (use_tpm), -o , -g , -i and -a, all optional),\n"); printf("print_keys (requires -p ),\n"); @@ -688,6 +689,7 @@ int main(int argc, char* argv[]) char* pass = 0; char* entropy = 0; MNEMONIC mnemonic = {0}; + SEED seed = {0}; dogecoin_bool tpm = false; dogecoin_bool encrypted = false; dogecoin_bool overwrite = false; @@ -1292,6 +1294,50 @@ int main(int argc, char* argv[]) return showError("decrypt_mnemonic (requires -y , -j (use_tpm) optional\n"); } } + else if (strcmp(cmd, "seed_to_master_key") == 0) { /* Creating a bip32 master key from a seed. */ + + /* if tpm is enabled, get seed from tpm */ + if (encrypted) { + printf("Decrypt seed for master key? Y/N\n"); + + char buffer[MAX_LEN]; + /* get user input */ + if (fgets(buffer, sizeof(buffer), stdin) != NULL) { + if (buffer[0] != 'Y' && buffer[0] != 'y') { + + /* if not confirmed, abort */ + printf("aborted\n"); + dogecoin_ecc_stop(); + return 1; + } + } + + if (tpm) { + /* get seed from tpm */ + if (!dogecoin_decrypt_seed_with_tpm (seed, file_num)) { + printf("seed_to_master_key (requires -y , -j (use_tpm) optional),\n"); + return showError("failed to decrypt seed with tpm\n"); + } + } + + else { + /* get seed from software */ + if (dogecoin_decrypt_seed_with_sw (seed, file_num, NULL) == false) { + printf("seed_to_master_key (requires -y , -j (use_tpm) optional),\n"); + return showError("failed to decrypt seed with software\n"); + } + } + } + + /* print master key from seed */ + dogecoin_hdnode node; + char masterkey[HDKEYLEN]; + dogecoin_hdnode_from_seed(seed, sizeof(seed), &node); + dogecoin_hdnode_serialize_private(&node, chain, masterkey, sizeof(masterkey)); + printf("bip32 extended master key: %s\n", masterkey); + dogecoin_mem_zero(masterkey, strlen(masterkey)); + dogecoin_mem_zero(seed, sizeof(seed)); + } else if (strcmp(cmd, "mnemonic_to_key") == 0) { /* Creating a bip32 master key from a mnemonic. */ /* if tpm is enabled, get mnemonic from tpm */ diff --git a/src/headersdb_file.c b/src/headersdb_file.c index 70059a142..7b2d1b6a9 100644 --- a/src/headersdb_file.c +++ b/src/headersdb_file.c @@ -129,8 +129,8 @@ void dogecoin_headers_db_free(dogecoin_headers_db* db) { dogecoin_free(scan_tip); scan_tip = prev; } - // If scan_tip is chainbottom, free it - if (scan_tip == db->chainbottom && scan_tip != &db->genesis) { + // If scan_tip is not the genesis block, free it + if (scan_tip != &db->genesis) { dogecoin_free(scan_tip); } } @@ -146,11 +146,12 @@ void dogecoin_headers_db_free(dogecoin_headers_db* db) { * * @param db the headers database object * @param file_path The path to the headers database file. If NULL, the default path is used. + * @param prompt If true, the user will be prompted to confirm loading the database. * * @return The return value is a boolean value that indicates whether the database was successfully * opened. */ -dogecoin_bool dogecoin_headers_db_load(dogecoin_headers_db* db, const char *file_path) { +dogecoin_bool dogecoin_headers_db_load(dogecoin_headers_db* db, const char *file_path, dogecoin_bool prompt) { if (!db->read_write_file) { return 1; @@ -169,8 +170,30 @@ dogecoin_bool dogecoin_headers_db_load(dogecoin_headers_db* db, const char *file struct stat buffer; dogecoin_bool create = true; - if (stat(file_path_local, &buffer) == 0) - create = false; + if (stat(file_path_local, &buffer) == 0) { + create = false; // Set create to false as file already exists + + if (prompt) { + printf("\nLoad %s? ", file_path_local); + char response[MAX_LEN]; + if (!fgets(response, MAX_LEN, stdin)) { + printf("Error reading input.\n"); + return false; + } + if (response[0] == 'o' || response[0] == 'O') { + printf("Are you sure? (y/n): \n"); + char confirm[MAX_LEN]; + if (!fgets(confirm, MAX_LEN, stdin)) { + printf("Error reading input.\n"); + return false; + } + if (confirm[0] == 'y' || confirm[0] == 'Y') { + remove(file_path_local); // remove the existing file + create = true; // Set create to true as a new file will be created + } + } + } + } db->headers_tree_file = fopen(file_path_local, create ? "a+b" : "r+b"); cstr_free(path_ret, true); diff --git a/src/seal.c b/src/seal.c index 6961e1627..34e869542 100644 --- a/src/seal.c +++ b/src/seal.c @@ -509,10 +509,11 @@ LIBDOGECOIN_API dogecoin_bool dogecoin_encrypt_seed_with_sw(const SEED seed, con } // Prompt for the password - char* password = malloc(PASS_MAX_LEN); + char* password = NULL; #ifdef TEST_PASSWD if (test_password) { + password = malloc(PASS_MAX_LEN); strcpy(password, test_password); } else @@ -533,10 +534,11 @@ LIBDOGECOIN_API dogecoin_bool dogecoin_encrypt_seed_with_sw(const SEED seed, con } // Confirm the password - char* confirm_password = malloc(PASS_MAX_LEN); + char* confirm_password = NULL; #ifdef TEST_PASSWD if (test_password) { + confirm_password = malloc(PASS_MAX_LEN); strcpy(confirm_password, test_password); } else @@ -656,10 +658,11 @@ LIBDOGECOIN_API dogecoin_bool dogecoin_decrypt_seed_with_sw(SEED seed, const int } // Prompt for the password - char* password = malloc(PASS_MAX_LEN); + char* password = NULL; #ifdef TEST_PASSWD if (test_password) { + password = malloc(PASS_MAX_LEN); strcpy(password, test_password); } else @@ -1220,10 +1223,11 @@ dogecoin_bool dogecoin_generate_hdnode_encrypt_with_sw(dogecoin_hdnode* out, con } // Prompt for the password - char* password = malloc(PASS_MAX_LEN); + char* password = NULL; #ifdef TEST_PASSWD if (test_password) { + password = malloc(PASS_MAX_LEN); strcpy(password, test_password); } else @@ -1245,10 +1249,11 @@ dogecoin_bool dogecoin_generate_hdnode_encrypt_with_sw(dogecoin_hdnode* out, con // Confirm the password - char* confirm_password = malloc(PASS_MAX_LEN); + char* confirm_password = NULL; #ifdef TEST_PASSWD if (test_password) { + confirm_password = malloc(PASS_MAX_LEN); strcpy(confirm_password, test_password); } else @@ -1376,10 +1381,11 @@ dogecoin_bool dogecoin_decrypt_hdnode_with_sw(dogecoin_hdnode* out, const int fi } // Prompt for the password - char* password = malloc(PASS_MAX_LEN); + char* password = NULL; #ifdef TEST_PASSWD if (test_password) { + password = malloc(PASS_MAX_LEN); strcpy(password, test_password); } else @@ -1951,10 +1957,11 @@ LIBDOGECOIN_API dogecoin_bool dogecoin_generate_mnemonic_encrypt_with_sw(MNEMONI } // Prompt for the password - char* password = malloc(PASS_MAX_LEN); + char* password = NULL; #ifdef TEST_PASSWD if (test_password) { + password = malloc(PASS_MAX_LEN); strcpy(password, test_password); } else @@ -1975,10 +1982,11 @@ LIBDOGECOIN_API dogecoin_bool dogecoin_generate_mnemonic_encrypt_with_sw(MNEMONI } // Confirm the password - char* confirm_password = malloc(PASS_MAX_LEN); + char* confirm_password = NULL; #ifdef TEST_PASSWD if (test_password) { + confirm_password = malloc(PASS_MAX_LEN); strcpy(confirm_password, test_password); } else @@ -2106,10 +2114,11 @@ LIBDOGECOIN_API dogecoin_bool dogecoin_decrypt_mnemonic_with_sw(MNEMONIC mnemoni } // Prompt for the password - char* password = malloc(PASS_MAX_LEN); + char* password = NULL; #ifdef TEST_PASSWD if (test_password) { + password = malloc(PASS_MAX_LEN); strcpy(password, test_password); } else diff --git a/src/spv.c b/src/spv.c index 7bc4f69b9..21a32b0f6 100644 --- a/src/spv.c +++ b/src/spv.c @@ -136,6 +136,12 @@ dogecoin_spv_client* dogecoin_spv_client_new(const dogecoin_chainparams *params, */ void dogecoin_spv_client_discover_peers(dogecoin_spv_client* client, const char *ips) { +#ifndef _WIN32 + // set stdin to non-blocking for quit command + int stdin_flags = fcntl(STDIN_FILENO, F_GETFL); + fcntl(STDIN_FILENO, F_SETFL, stdin_flags | O_NONBLOCK); +#endif + dogecoin_node_group_add_peers_by_ip_or_seed(client->nodegroup, ips); } @@ -186,10 +192,11 @@ void dogecoin_spv_client_free(dogecoin_spv_client *client) * * @param client the client object * @param file_path The path to the headers database file. + * @param prompt If true, the user will be prompted to confirm loading the database. * * @return A boolean value. */ -dogecoin_bool dogecoin_spv_client_load(dogecoin_spv_client *client, const char *file_path) +dogecoin_bool dogecoin_spv_client_load(dogecoin_spv_client *client, const char *file_path, dogecoin_bool prompt) { if (!client) return false; @@ -197,13 +204,7 @@ dogecoin_bool dogecoin_spv_client_load(dogecoin_spv_client *client, const char * if (!client->headers_db) return false; -#ifndef _WIN32 - // set stdin to non-blocking for quit command - int stdin_flags = fcntl(STDIN_FILENO, F_GETFL); - fcntl(STDIN_FILENO, F_SETFL, stdin_flags | O_NONBLOCK); -#endif - - return client->headers_db->load(client->headers_db_ctx, file_path); + return client->headers_db->load(client->headers_db_ctx, file_path, prompt); } diff --git a/src/wallet.c b/src/wallet.c index 968d40680..ebe3ed817 100644 --- a/src/wallet.c +++ b/src/wallet.c @@ -321,7 +321,7 @@ dogecoin_wallet* dogecoin_wallet_new(const dogecoin_chainparams *params) return wallet; } -dogecoin_wallet* dogecoin_wallet_init(const dogecoin_chainparams* chain, const char* address, const char* name, const char* mnemonic_in, const char* pass, const dogecoin_bool encrypted, const dogecoin_bool tpm, const int file_num, const dogecoin_bool master_key) { +dogecoin_wallet* dogecoin_wallet_init(const dogecoin_chainparams* chain, const char* address, const char* name, const char* mnemonic_in, const char* pass, const dogecoin_bool encrypted, const dogecoin_bool tpm, const int file_num, const dogecoin_bool master_key, const dogecoin_bool prompt) { dogecoin_wallet* wallet = dogecoin_wallet_new(chain); int error; dogecoin_bool created; @@ -336,24 +336,25 @@ dogecoin_wallet* dogecoin_wallet_init(const dogecoin_chainparams* chain, const c dogecoin_free(wallet_type_prefix); if (name) { // Override wallet file name with name: - res = dogecoin_wallet_load(wallet, name, &error, &created); + res = dogecoin_wallet_load(wallet, name, &error, &created, prompt); } else { - res = dogecoin_wallet_load(wallet, walletfile, &error, &created); + res = dogecoin_wallet_load(wallet, walletfile, &error, &created, prompt); } } // else if name is set, use name for wallet file name: else if (name) { - res = dogecoin_wallet_load(wallet, name, &error, &created); + res = dogecoin_wallet_load(wallet, name, &error, &created, prompt); } else { // prefix chain to wallet file name: walletfile = concat(wallet_prefix, wallet_suffix); - res = dogecoin_wallet_load(wallet, walletfile, &error, &created); + res = dogecoin_wallet_load(wallet, walletfile, &error, &created, prompt); } dogecoin_free(walletfile); if (!res) { showError("Loading wallet failed\n"); + dogecoin_wallet_free(wallet); return NULL; } if (created) { @@ -365,6 +366,7 @@ dogecoin_wallet* dogecoin_wallet_init(const dogecoin_chainparams* chain, const c // generate seed from mnemonic if (dogecoin_seed_from_mnemonic(mnemonic_in, pass, seed) == -1) { showError("Invalid mnemonic\n"); + dogecoin_wallet_free(wallet); return NULL; } } else if (encrypted && !master_key) { @@ -384,6 +386,7 @@ dogecoin_wallet* dogecoin_wallet_init(const dogecoin_chainparams* chain, const c if (!tpmSuccess) { if (!dogecoin_decrypt_mnemonic_with_sw(mnemonic, file_num, NULL)) { showError("Decrypting mnemonic from software failed\n"); + dogecoin_wallet_free(wallet); return NULL; } } @@ -391,6 +394,7 @@ dogecoin_wallet* dogecoin_wallet_init(const dogecoin_chainparams* chain, const c // generate seed from mnemonic if (dogecoin_seed_from_mnemonic(mnemonic, pass, seed) == -1) { showError("Invalid mnemonic\n"); + dogecoin_wallet_free(wallet); return NULL; } } else if (encrypted && master_key) { @@ -409,6 +413,7 @@ dogecoin_wallet* dogecoin_wallet_init(const dogecoin_chainparams* chain, const c if (!tpmSuccess) { if (!dogecoin_decrypt_hdnode_with_sw(&node, file_num, NULL)) { showError("Decrypting master key from software failed\n"); + dogecoin_wallet_free(wallet); return NULL; } } @@ -417,8 +422,35 @@ dogecoin_wallet* dogecoin_wallet_init(const dogecoin_chainparams* chain, const c res = dogecoin_random_bytes(seed, sizeof(seed), true); if (!res) { showError("Generating random bytes failed\n"); + dogecoin_wallet_free(wallet); return NULL; } + if (prompt) { + printf("Store seed in encrypted file? (Y/n): "); + + char buffer[MAX_LEN]; + int file_id = 0; // Variable to store file ID, defaulting to 0 + + // read user input + if (fgets(buffer, sizeof(buffer), stdin) != NULL) { + if (buffer[0] == 'Y' || buffer[0] == 'y') { + printf("Enter file ID (0-999): "); + if (fgets(buffer, sizeof(buffer), stdin) != NULL) { + file_id = atoi(buffer); // Update file_id with user input + } + + printf("Overwrite file if found? (Y/n): "); + if (fgets(buffer, sizeof(buffer), stdin) != NULL) { + bool overwrite = (buffer[0] == 'Y' || buffer[0] == 'y'); + // encrypt seed for storage with software + if (dogecoin_encrypt_seed_with_sw(seed, sizeof(seed), file_id, overwrite, NULL) == false) { + dogecoin_wallet_free(wallet); + return NULL; + } + } + } + } + } } if (!master_key) { // generate hdnode from mnemonic or random seed @@ -429,6 +461,7 @@ dogecoin_wallet* dogecoin_wallet_init(const dogecoin_chainparams* chain, const c // ensure we have a key/address if (wallet->masterkey == NULL && address == NULL) { showError("No master key or address in wallet.\n"); + dogecoin_wallet_free(wallet); exit(EXIT_FAILURE); } } @@ -826,14 +859,38 @@ dogecoin_bool dogecoin_wallet_load_transaction(dogecoin_wallet* wallet, uint32_t // return true; // } -dogecoin_bool dogecoin_wallet_load(dogecoin_wallet* wallet, const char* file_path, int *error, dogecoin_bool *created) +dogecoin_bool dogecoin_wallet_load(dogecoin_wallet* wallet, const char* file_path, int *error, dogecoin_bool *created, dogecoin_bool prompt) { (void)(error); if (!wallet) { return false; } struct stat buffer; *created = true; - if (stat(file_path, &buffer) == 0) *created = false; + + if (stat(file_path, &buffer) == 0) { + *created = false; // Set created to false as file already exists + + if (prompt) { + printf("Load %s? (Enter) or (o)verwrite:\n", file_path); + char response[MAX_LEN]; + if (!fgets(response, MAX_LEN, stdin)) { + printf("Error reading input.\n"); + return false; + } + if (response[0] == 'o' || response[0] == 'O') { + printf("Are you sure? (y/n): \n"); + char confirm[MAX_LEN]; + if (!fgets(confirm, MAX_LEN, stdin)) { + printf("Error reading input.\n"); + return false; + } + if (confirm[0] == 'y' || confirm[0] == 'Y') { + remove(file_path); // remove the existing file + *created = true; // Set created to true as a new file will be created + } + } + } + } wallet->dbfile = fopen(file_path, *created ? "a+b" : "r+b"); @@ -1415,7 +1472,7 @@ dogecoin_wallet* dogecoin_wallet_read(char* address) { char* wallet_suffix = "_wallet.db"; char* wallet_prefix = (char*)chain->chainname; char* walletfile = concat(wallet_prefix, wallet_suffix); - dogecoin_wallet* wallet = dogecoin_wallet_init(chain, address, walletfile, 0, 0, false, false, -1, false); + dogecoin_wallet* wallet = dogecoin_wallet_init(chain, address, walletfile, 0, 0, false, false, -1, false, false); wallet->filename = concat(wallet_prefix, wallet_suffix); dogecoin_free(walletfile); return wallet; @@ -1469,7 +1526,7 @@ int dogecoin_unregister_watch_address_with_node(char* address) { char* oldname = concat(path, concat(unix_delim, "temp.bin")); char* newname = concat(path, concat(unix_delim, (char*)wallet->filename)); #endif - dogecoin_wallet_load(wallet_new, oldname, &error, &created); + dogecoin_wallet_load(wallet_new, oldname, &error, &created, false); wallet_new->filename = oldname; dogecoin_wallet_addr* waddr_check = dogecoin_wallet_addr_new(); // convert address to 20 byte script hash: diff --git a/test/spv_tests.c b/test/spv_tests.c index e2491f99f..de733dd3f 100644 --- a/test/spv_tests.c +++ b/test/spv_tests.c @@ -73,7 +73,7 @@ void test_spv() dogecoin_spv_client* client = dogecoin_spv_client_new(chain, false, true, true, false); client->header_message_processed = test_spv_header_message_processed; client->sync_completed = test_spv_sync_completed; - dogecoin_spv_client_load(client, headersfile); + dogecoin_spv_client_load(client, headersfile, false); dogecoin_free(headersfile); printf("Discover peers..."); @@ -100,7 +100,7 @@ void test_reorg() { dogecoin_spv_client* client = dogecoin_spv_client_new(chain, false, true, false, false); client->header_message_processed = test_spv_header_message_processed; client->sync_completed = test_spv_sync_completed; - dogecoin_spv_client_load(client, headersfile); + dogecoin_spv_client_load(client, headersfile, false); // Create headers for the main chain and new chain dogecoin_block_header* header1 = dogecoin_block_header_new(); diff --git a/test/wallet_tests.c b/test/wallet_tests.c index 3f9185f0d..382bd2e8e 100644 --- a/test/wallet_tests.c +++ b/test/wallet_tests.c @@ -156,7 +156,7 @@ void test_wallet() dogecoin_wallet *wallet = dogecoin_wallet_new(&dogecoin_chainparams_main); int error; dogecoin_bool created; - u_assert_int_eq(dogecoin_wallet_load(wallet, wallettmpfile, &error, &created), true); + u_assert_int_eq(dogecoin_wallet_load(wallet, wallettmpfile, &error, &created, false), true); // inject a key dogecoin_wallet_addr *waddr = dogecoin_wallet_addr_new(); @@ -196,7 +196,7 @@ void test_wallet_basics() dogecoin_wallet *wallet = dogecoin_wallet_new(&dogecoin_chainparams_main); int error; dogecoin_bool created; - u_assert_int_eq(dogecoin_wallet_load(wallet, wallettmpfile, &error, &created), true); + u_assert_int_eq(dogecoin_wallet_load(wallet, wallettmpfile, &error, &created, false), true); char *xpub = "dgub8kXBZ7ymNWy2T7WH3WgpGDv6htHqBEPU8bymfvJeHNJaBT65E2EjemjSx6ggYmaMDfnSrtJWbafCJu2b1voNTARsyhCULtT8d8MH2MQwCqV"; @@ -210,7 +210,7 @@ void test_wallet_basics() dogecoin_wallet_free(wallet); wallet = dogecoin_wallet_new(&dogecoin_chainparams_main); - u_assert_int_eq(dogecoin_wallet_load(wallet, wallettmpfile, &error, &created), true); + u_assert_int_eq(dogecoin_wallet_load(wallet, wallettmpfile, &error, &created, false), true); dogecoin_wallet_addr *wallet_addr2 = dogecoin_wallet_next_addr(wallet); u_assert_int_eq(wallet_addr2->childindex, 1); @@ -240,7 +240,7 @@ void test_wallet_basics() dogecoin_wallet_free(wallet); wallet = dogecoin_wallet_new(&dogecoin_chainparams_main); - u_assert_int_eq(dogecoin_wallet_load(wallet, wallettmpfile, &error, &created), true); + u_assert_int_eq(dogecoin_wallet_load(wallet, wallettmpfile, &error, &created, false), true); addrs = vector_new(1, free); dogecoin_wallet_get_addresses(wallet, addrs); u_assert_int_eq(addrs->len, 3);