diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 5d7321e48..9c3dc2b32 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1254,7 +1254,7 @@ bool simple_wallet::export_multisig_main(const std::vector &args, b } else { - bool r = epee::file_io_utils::save_string_to_file(filename, ciphertext); + bool r = m_wallet->save_to_file(filename, ciphertext); if (!r) { fail_msg_writer() << tr("failed to save file ") << filename; @@ -1315,7 +1315,7 @@ bool simple_wallet::import_multisig_main(const std::vector &args, b { const std::string &filename = args[n]; std::string data; - bool r = epee::file_io_utils::load_file_to_string(filename, data); + bool r = m_wallet->load_from_file(filename, data); if (!r) { fail_msg_writer() << tr("failed to read file ") << filename; @@ -1626,7 +1626,7 @@ bool simple_wallet::export_raw_multisig(const std::vector &args) if (!filenames.empty()) filenames += ", "; filenames += filename; - if (!epee::file_io_utils::save_string_to_file(filename, cryptonote::tx_to_blob(ptx.tx))) + if (!m_wallet->save_to_file(filename, cryptonote::tx_to_blob(ptx.tx))) { fail_msg_writer() << tr("Failed to export multisig transaction to file ") << filename; return true; @@ -2712,6 +2712,35 @@ bool simple_wallet::set_device_name(const std::vector &args/* = std return true; } +bool simple_wallet::set_export_format(const std::vector &args/* = std::vectorset_export_format(tools::wallet2::ExportFormat::Ascii); + } + else if (boost::algorithm::iequals(args[1], "binary")) + { + m_wallet->set_export_format(tools::wallet2::ExportFormat::Binary); + } + else + { + fail_msg_writer() << tr("Export format not recognized."); + return true; + } + const auto pwd_container = get_and_verify_password(); + if (pwd_container) + { + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + } + return true; +} + bool simple_wallet::help(const std::vector &args/* = std::vector()*/) { if(args.empty()) @@ -2912,7 +2941,9 @@ simple_wallet::simple_wallet() "setup-background-mining <1|0>\n " " Whether to enable background mining. Set this to support the network and to get a chance to receive new monero.\n " "device-name \n " - " Device name for hardware wallet.")); + " Device name for hardware wallet.\n " + "export-format <\"binary\"|\"ascii\">\n " + " Save all exported files as binary (cannot be copied and pasted) or ascii (can be).\n ")); m_cmd_binder.set_handler("encrypted_seed", boost::bind(&simple_wallet::encrypted_seed, this, _1), tr("Display the encrypted Electrum-style mnemonic seed.")); @@ -3278,6 +3309,7 @@ bool simple_wallet::set_variable(const std::vector &args) success_msg_writer() << "track-uses = " << m_wallet->track_uses(); success_msg_writer() << "setup-background-mining = " << setup_background_mining_string; success_msg_writer() << "device-name = " << m_wallet->device_name(); + success_msg_writer() << "export-format = " << (m_wallet->export_format() == tools::wallet2::ExportFormat::Ascii ? "ascii" : "binary"); return true; } else @@ -3336,6 +3368,7 @@ bool simple_wallet::set_variable(const std::vector &args) CHECK_SIMPLE_VARIABLE("track-uses", set_track_uses, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("setup-background-mining", set_setup_background_mining, tr("1/yes or 0/no")); CHECK_SIMPLE_VARIABLE("device-name", set_device_name, tr("")); + CHECK_SIMPLE_VARIABLE("export-format", set_export_format, tr("\"binary\" or \"ascii\"")); } fail_msg_writer() << tr("set: unrecognized argument(s)"); return true; @@ -7312,7 +7345,7 @@ bool simple_wallet::get_tx_proof(const std::vector &args) { std::string sig_str = m_wallet->get_tx_proof(txid, info.address, info.is_subaddress, args.size() == 3 ? args[2] : ""); const std::string filename = "monero_tx_proof"; - if (epee::file_io_utils::save_string_to_file(filename, sig_str)) + if (m_wallet->save_to_file(filename, sig_str, true)) success_msg_writer() << tr("signature file saved to: ") << filename; else fail_msg_writer() << tr("failed to save signature file"); @@ -7440,7 +7473,7 @@ bool simple_wallet::check_tx_proof(const std::vector &args) // read signature file std::string sig_str; - if (!epee::file_io_utils::load_file_to_string(args[2], sig_str)) + if (!m_wallet->load_from_file(args[2], sig_str)) { fail_msg_writer() << tr("failed to load signature file"); return true; @@ -7524,7 +7557,7 @@ bool simple_wallet::get_spend_proof(const std::vector &args) { const std::string sig_str = m_wallet->get_spend_proof(txid, args.size() == 2 ? args[1] : ""); const std::string filename = "monero_spend_proof"; - if (epee::file_io_utils::save_string_to_file(filename, sig_str)) + if (m_wallet->save_to_file(filename, sig_str, true)) success_msg_writer() << tr("signature file saved to: ") << filename; else fail_msg_writer() << tr("failed to save signature file"); @@ -7554,7 +7587,7 @@ bool simple_wallet::check_spend_proof(const std::vector &args) return true; std::string sig_str; - if (!epee::file_io_utils::load_file_to_string(args[1], sig_str)) + if (!m_wallet->load_from_file(args[1], sig_str)) { fail_msg_writer() << tr("failed to load signature file"); return true; @@ -7613,7 +7646,7 @@ bool simple_wallet::get_reserve_proof(const std::vector &args) { const std::string sig_str = m_wallet->get_reserve_proof(account_minreserve, args.size() == 2 ? args[1] : ""); const std::string filename = "monero_reserve_proof"; - if (epee::file_io_utils::save_string_to_file(filename, sig_str)) + if (m_wallet->save_to_file(filename, sig_str, true)) success_msg_writer() << tr("signature file saved to: ") << filename; else fail_msg_writer() << tr("failed to save signature file"); @@ -7648,7 +7681,7 @@ bool simple_wallet::check_reserve_proof(const std::vector &args) } std::string sig_str; - if (!epee::file_io_utils::load_file_to_string(args[1], sig_str)) + if (!m_wallet->load_from_file(args[1], sig_str)) { fail_msg_writer() << tr("failed to load signature file"); return true; @@ -9011,7 +9044,7 @@ bool simple_wallet::sign(const std::vector &args) std::string filename = args[0]; std::string data; - bool r = epee::file_io_utils::load_file_to_string(filename, data); + bool r = m_wallet->load_from_file(filename, data); if (!r) { fail_msg_writer() << tr("failed to read file ") << filename; @@ -9037,7 +9070,7 @@ bool simple_wallet::verify(const std::vector &args) std::string signature= args[2]; std::string data; - bool r = epee::file_io_utils::load_file_to_string(filename, data); + bool r = m_wallet->load_from_file(filename, data); if (!r) { fail_msg_writer() << tr("failed to read file ") << filename; @@ -9255,7 +9288,7 @@ bool simple_wallet::export_outputs(const std::vector &args_) try { std::string data = m_wallet->export_outputs_to_str(all); - bool r = epee::file_io_utils::save_string_to_file(filename, data); + bool r = m_wallet->save_to_file(filename, data); if (!r) { fail_msg_writer() << tr("failed to save file ") << filename; @@ -9288,7 +9321,7 @@ bool simple_wallet::import_outputs(const std::vector &args) std::string filename = args[0]; std::string data; - bool r = epee::file_io_utils::load_file_to_string(filename, data); + bool r = m_wallet->load_from_file(filename, data); if (!r) { fail_msg_writer() << tr("failed to read file ") << filename; @@ -9489,7 +9522,7 @@ void simple_wallet::commit_or_save(std::vector& ptx_ tx_to_blob(ptx.tx, blob); const std::string blob_hex = epee::string_tools::buff_to_hex_nodelimer(blob); const std::string filename = "raw_monero_tx" + (ptx_vector.size() == 1 ? "" : ("_" + std::to_string(i++))); - if (epee::file_io_utils::save_string_to_file(filename, blob_hex)) + if (m_wallet->save_to_file(filename, blob_hex, true)) success_msg_writer(true) << tr("Transaction successfully saved to ") << filename << tr(", txid ") << txid; else fail_msg_writer() << tr("Failed to save transaction to ") << filename << tr(", txid ") << txid; @@ -10274,7 +10307,7 @@ void simple_wallet::mms_export(const std::vector &args) if (valid_id) { const std::string filename = "mms_message_content"; - if (epee::file_io_utils::save_string_to_file(filename, m.content)) + if (m_wallet->save_to_file(filename, m.content)) { success_msg_writer() << tr("Message content saved to: ") << filename; } diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 2de390666..cbc1cb6fa 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -146,6 +146,7 @@ namespace cryptonote bool set_track_uses(const std::vector &args = std::vector()); bool set_setup_background_mining(const std::vector &args = std::vector()); bool set_device_name(const std::vector &args = std::vector()); + bool set_export_format(const std::vector &args = std::vector()); bool help(const std::vector &args = std::vector()); bool start_mining(const std::vector &args); bool stop_mining(const std::vector &args); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 9782e4b1e..88ef53377 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include "include_base_utils.h" using namespace epee; @@ -138,6 +139,8 @@ using namespace cryptonote; static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1"; static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1"; +static const std::string ASCII_OUTPUT_MAGIC = "MoneroAsciiDataV1"; + namespace { std::string get_default_ringdb_path() @@ -1143,7 +1146,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_device_last_key_image_sync(0), m_use_dns(true), m_offline(false), - m_rpc_version(0) + m_rpc_version(0), + m_export_format(ExportFormat::Binary) { } @@ -3647,6 +3651,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable value2.SetInt(m_original_keys_available ? 1 : 0); json.AddMember("original_keys_available", value2, json.GetAllocator()); + value2.SetInt(m_export_format); + json.AddMember("export_format", value2, json.GetAllocator()); + value2.SetUint(1); json.AddMember("encrypted_secret_keys", value2, json.GetAllocator()); @@ -3684,7 +3691,7 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable std::string tmp_file_name = keys_file_name + ".new"; std::string buf; r = ::serialization::dump_binary(keys_file_data, buf); - r = r && epee::file_io_utils::save_string_to_file(tmp_file_name, buf); + r = r && save_to_file(tmp_file_name, buf); CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << tmp_file_name); unlock_keys_file(); @@ -3741,7 +3748,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ wallet2::keys_file_data keys_file_data; std::string buf; bool encrypted_secret_keys = false; - bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf); + bool r = load_from_file(keys_file_name, buf); THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, keys_file_name); // Decrypt the contents @@ -3754,7 +3761,6 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject()) crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); - // The contents should be JSON if the wallet follows the new format. if (json.Parse(account_data.c_str()).HasParseError()) { @@ -3793,6 +3799,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR; m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR; m_original_keys_available = false; + m_export_format = ExportFormat::Binary; m_device_name = ""; m_device_derivation_path = ""; m_key_device_type = hw::device::device_type::SOFTWARE; @@ -3954,6 +3961,9 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, encrypted_secret_keys, uint32_t, Uint, false, false); encrypted_secret_keys = field_encrypted_secret_keys; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, export_format, ExportFormat, Int, false, Binary); + m_export_format = field_export_format; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, device_name, std::string, String, false, std::string()); if (m_device_name.empty()) { @@ -4101,7 +4111,7 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip wallet2::keys_file_data keys_file_data; std::string buf; bool encrypted_secret_keys = false; - bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf); + bool r = load_from_file(keys_file_name, buf); THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, keys_file_name); // Decrypt the contents @@ -4186,7 +4196,7 @@ void wallet2::create_keys_file(const std::string &wallet_, bool watch_only, cons if (create_address_file) { - r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype)); + r = save_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype), true); if(!r) MERROR("String with address text not saved"); } } @@ -4208,7 +4218,7 @@ bool wallet2::query_device(hw::device::device_type& device_type, const std::stri rapidjson::Document json; wallet2::keys_file_data keys_file_data; std::string buf; - bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf); + bool r = load_from_file(keys_file_name, buf); THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, keys_file_name); // Decrypt the contents @@ -4757,7 +4767,7 @@ std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &passwor if (boost::filesystem::exists(m_wallet_file + ".address.txt")) { - r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype)); + r = save_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype), true); if(!r) MERROR("String with address text not saved"); } } @@ -5266,7 +5276,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass { wallet2::cache_file_data cache_file_data; std::string buf; - bool r = epee::file_io_utils::load_file_to_string(m_wallet_file, buf, std::numeric_limits::max()); + bool r = load_from_file(m_wallet_file, buf, std::numeric_limits::max()); THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, m_wallet_file); // try to read it as an encrypted cache @@ -5501,7 +5511,7 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas { // save address to the new file const std::string address_file = m_wallet_file + ".address.txt"; - r = file_io_utils::save_string_to_file(address_file, m_account.get_public_address_str(m_nettype)); + r = save_to_file(address_file, m_account.get_public_address_str(m_nettype), true); THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_wallet_file); } // remove old wallet file @@ -5536,7 +5546,7 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas binary_archive oar(oss); bool success = ::serialization::serialize(oar, cache_file_data); if (success) { - success = epee::file_io_utils::save_string_to_file(new_file, oss.str()); + success = save_to_file(new_file, oss.str()); } THROW_WALLET_EXCEPTION_IF(!success, error::file_save_error, new_file); #else @@ -6164,7 +6174,7 @@ bool wallet2::save_tx(const std::vector& ptx_vector, const std::stri std::string ciphertext = dump_tx_to_str(ptx_vector); if (ciphertext.empty()) return false; - return epee::file_io_utils::save_string_to_file(filename, ciphertext); + return save_to_file(filename, ciphertext); } //---------------------------------------------------------------------------------------------------- std::string wallet2::dump_tx_to_str(const std::vector &ptx_vector) const @@ -6206,7 +6216,7 @@ bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx LOG_PRINT_L0("File " << unsigned_filename << " does not exist: " << errcode); return false; } - if (!epee::file_io_utils::load_file_to_string(unsigned_filename.c_str(), s)) + if (!load_from_file(unsigned_filename.c_str(), s)) { LOG_PRINT_L0("Failed to load from " << unsigned_filename); return false; @@ -6427,7 +6437,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f return false; } - if (!epee::file_io_utils::save_string_to_file(signed_filename, ciphertext)) + if (!save_to_file(signed_filename, ciphertext)) { LOG_PRINT_L0("Failed to save file to " << signed_filename); return false; @@ -6439,7 +6449,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f { std::string tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(signed_txes.ptx[i].tx)); std::string raw_filename = signed_filename + "_raw" + (signed_txes.ptx.size() == 1 ? "" : ("_" + std::to_string(i))); - if (!epee::file_io_utils::save_string_to_file(raw_filename, tx_as_hex)) + if (!save_to_file(raw_filename, tx_as_hex)) { LOG_PRINT_L0("Failed to save file to " << raw_filename); return false; @@ -6487,7 +6497,7 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector& ptx_vector) const @@ -6646,7 +6656,7 @@ bool wallet2::save_multisig_tx(const std::vector& ptx_vector, const std::string ciphertext = save_multisig_tx(ptx_vector); if (ciphertext.empty()) return false; - return epee::file_io_utils::save_string_to_file(filename, ciphertext); + return save_to_file(filename, ciphertext); } //---------------------------------------------------------------------------------------------------- bool wallet2::parse_multisig_tx_from_str(std::string multisig_tx_st, multisig_tx_set &exported_txs) const @@ -6737,7 +6747,7 @@ bool wallet2::load_multisig_tx_from_file(const std::string &filename, multisig_t LOG_PRINT_L0("File " << filename << " does not exist: " << errcode); return false; } - if (!epee::file_io_utils::load_file_to_string(filename.c_str(), s)) + if (!load_from_file(filename.c_str(), s)) { LOG_PRINT_L0("Failed to load from " << filename); return false; @@ -11633,7 +11643,7 @@ bool wallet2::export_key_images(const std::string &filename, bool all) const // encrypt data, keep magic plaintext PERF_TIMER(export_key_images_encrypt); std::string ciphertext = encrypt_with_view_secret_key(data); - return epee::file_io_utils::save_string_to_file(filename, magic + ciphertext); + return save_to_file(filename, magic + ciphertext); } //---------------------------------------------------------------------------------------------------- @@ -11698,7 +11708,7 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent { PERF_TIMER(import_key_images_fsu); std::string data; - bool r = epee::file_io_utils::load_file_to_string(filename, data); + bool r = load_from_file(filename, data); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, std::string(tr("failed to read file ")) + filename); @@ -13102,6 +13112,83 @@ void wallet2::throw_on_rpc_response_error(const boost::optional &st THROW_WALLET_EXCEPTION_IF(*status != CORE_RPC_STATUS_OK, tools::error::wallet_generic_rpc_error, method, m_trusted_daemon ? *status : "daemon error"); } //---------------------------------------------------------------------------------------------------- + +bool wallet2::save_to_file(const std::string& path_to_file, const std::string& raw, bool is_printable) const +{ + if (is_printable || m_export_format == ExportFormat::Binary) + { + return epee::file_io_utils::save_string_to_file(path_to_file, raw); + } + + FILE *fp = fopen(path_to_file.c_str(), "w+"); + // Save the result b/c we need to close the fp before returning success/failure. + int write_result = PEM_write(fp, ASCII_OUTPUT_MAGIC.c_str(), "", (const unsigned char *) raw.c_str(), raw.length()); + fclose(fp); + + if (write_result == 0) + { + return false; + } + else + { + return true; + } +} +//---------------------------------------------------------------------------------------------------- + +bool wallet2::load_from_file(const std::string& path_to_file, std::string& target_str, + size_t max_size) +{ + std::string data; + bool r = epee::file_io_utils::load_file_to_string(path_to_file, data, max_size); + if (!r) + { + return false; + } + + if (!boost::algorithm::contains(boost::make_iterator_range(data.begin(), data.end()), ASCII_OUTPUT_MAGIC)) + { + // It's NOT our ascii dump. + target_str = std::move(data); + return true; + } + + // Creating a BIO and calling PEM_read_bio instead of simpler PEM_read + // to avoid reading the file from disk twice. + BIO* b = BIO_new_mem_buf((const void*) data.data(), data.length()); + + char *name = NULL; + char *header = NULL; + unsigned char *openssl_data = NULL; + long len = 0; + + // Save the result b/c we need to free the data before returning success/failure. + int success = PEM_read_bio(b, &name, &header, &openssl_data, &len); + + try + { + target_str = std::string((const char*) openssl_data, len); + } + catch (...) + { + success = 0; + } + + OPENSSL_free((void *) name); + OPENSSL_free((void *) header); + OPENSSL_free((void *) openssl_data); + BIO_free(b); + + if (success == 0) + { + return false; + } + else + { + return true; + } +} +//---------------------------------------------------------------------------------------------------- void wallet2::hash_m_transfer(const transfer_details & transfer, crypto::hash &hash) const { KECCAK_CTX state; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index c2e34dd76..e1aca1e89 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -227,6 +227,11 @@ private: BackgroundMiningNo = 2, }; + enum ExportFormat { + Binary = 0, + Ascii, + }; + static const char* tr(const char* str); static bool has_testnet_option(const boost::program_options::variables_map& vm); @@ -1050,6 +1055,8 @@ private: void device_name(const std::string & device_name) { m_device_name = device_name; } const std::string & device_derivation_path() const { return m_device_derivation_path; } void device_derivation_path(const std::string &device_derivation_path) { m_device_derivation_path = device_derivation_path; } + const ExportFormat & export_format() const { return m_export_format; } + inline void set_export_format(const ExportFormat& export_format) { m_export_format = export_format; } bool get_tx_key_cached(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector &additional_tx_keys) const; void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector &additional_tx_keys); @@ -1300,6 +1307,9 @@ private: bool frozen(const crypto::key_image &ki) const; bool frozen(const transfer_details &td) const; + bool save_to_file(const std::string& path_to_file, const std::string& binary, bool is_printable = false) const; + static bool load_from_file(const std::string& path_to_file, std::string& target_str, size_t max_size = 1000000000); + uint64_t get_bytes_sent() const; uint64_t get_bytes_received() const; @@ -1546,6 +1556,8 @@ private: std::shared_ptr m_tx_notify; std::unique_ptr m_device_callback; + + ExportFormat m_export_format; }; } BOOST_CLASS_VERSION(tools::wallet2, 28)