From 21c16318257d729679d587e7e23d9556b9b31246 Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Wed, 13 Mar 2024 13:16:36 +0100 Subject: [PATCH 1/3] auth basic r and d --- contrib/epee/include/net/net_helper.h | 2 +- .../currency_protocol_handler.inl | 5 +++ src/gui/qt-daemon/layout | 2 +- src/wallet/wallet_rpc_server.cpp | 33 +++++++++++++++++++ src/wallet/wallet_rpc_server.h | 11 ++++--- tests/CMakeLists.txt | 6 ++-- tests/performance_tests/main.cpp | 26 +++++++++++++++ tests/performance_tests/single_tx_test_base.h | 3 +- 8 files changed, 78 insertions(+), 10 deletions(-) diff --git a/contrib/epee/include/net/net_helper.h b/contrib/epee/include/net/net_helper.h index 4a42e679..fb3be17c 100644 --- a/contrib/epee/include/net/net_helper.h +++ b/contrib/epee/include/net/net_helper.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, anonimal, + // Copyright (c) 2019, anonimal, // Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net // All rights reserved. // diff --git a/src/currency_protocol/currency_protocol_handler.inl b/src/currency_protocol/currency_protocol_handler.inl index a23e85b5..9f4c22de 100644 --- a/src/currency_protocol/currency_protocol_handler.inl +++ b/src/currency_protocol/currency_protocol_handler.inl @@ -171,6 +171,11 @@ namespace currency return true; } + if(hshd.top_id == currency::null_hash) + { + LOG_PRINT_L0("wtf"); + } + int64_t diff = static_cast(hshd.current_height) - static_cast(m_core.get_current_blockchain_size()); LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, (is_inital ? "Inital ":"Idle ") << "sync data returned unknown top block (" << hshd.top_id << "): " << m_core.get_top_block_height() << " -> " << hshd.current_height - 1 << " [" << std::abs(diff) << " blocks (" << diff / (24 * 60 * 60 / DIFFICULTY_TOTAL_TARGET ) << " days) " diff --git a/src/gui/qt-daemon/layout b/src/gui/qt-daemon/layout index e4a76ad1..f8e9556f 160000 --- a/src/gui/qt-daemon/layout +++ b/src/gui/qt-daemon/layout @@ -1 +1 @@ -Subproject commit e4a76ad1329244ec4fb9ec7825361518e95d1933 +Subproject commit f8e9556fbaccd49841ce91afc3c90c8e3142ac95 diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index a602ecba..35e47b57 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -17,6 +17,7 @@ using namespace epee; #include "wallet_rpc_server_error_codes.h" #include "wallet_helpers.h" #include "wrap_service.h" +#include #define GET_WALLET() wallet_rpc_locker w(m_pwallet_provider); @@ -184,11 +185,43 @@ namespace tools m_net_server.set_threads_prefix("RPC"); bool r = handle_command_line(vm); CHECK_AND_ASSERT_MES(r, false, "Failed to process command line in core_rpc_server"); + m_jwt_secrete = "secretesecrete"; + return epee::http_server_impl_base::init(m_port, m_bind_ip); } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::auth_http_request(const epee::net_utils::http::http_request_info& query_info, epee::net_utils::http::http_response_info& response, connection_context& m_conn_context) + { + + auto it = std::find_if(query_info.m_header_info.m_etc_fields.begin(), query_info.m_header_info.m_etc_fields.end(), [](const auto& element) + { return element.first == ZANO_ACCESS_TOKEN; }); + if(it == query_info.m_header_info.m_etc_fields.end()) + return false; + std::string token = it->second; //"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCIsInNhbXBsZSI6InRlc3QifQ.lQm3N2bVlqt2-1L-FsOjtR6uE-L4E9zJutMWKIe1v1M"; + auto decoded_token = jwt::decode(token); + + auto verifier = jwt::verify() + .with_issuer("auth0") + .with_claim("sample", jwt::claim(std::string("test"))) + .allow_algorithm(jwt::algorithm::hs256 { m_jwt_secrete }); + + verifier.verify(decoded_token); + return false; + + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::handle_http_request(const epee::net_utils::http::http_request_info& query_info, epee::net_utils::http::http_response_info& response, connection_context& m_conn_context) { + if (m_jwt_secrete.size()) + { + if (!auth_http_request(query_info, response, m_conn_context)) + { + response.m_response_code = 401; + response.m_response_comment = "Unauthorized"; + return true; + } + } + response.m_response_code = 200; response.m_response_comment = "Ok"; std::string reference_stub; diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index f95ae920..aa848276 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -12,11 +12,11 @@ #include "wallet_public_structs_defs.h" #include "wallet2.h" #include "common/command_line.h" + +#define ZANO_ACCESS_TOKEN "Zano-Access-Token" + namespace tools { - - - struct i_wallet_provider { virtual void lock() {}; @@ -143,8 +143,9 @@ namespace tools MAP_JON_RPC_WE("encrypt_data", on_encrypt_data, wallet_public::COMMAND_ENCRYPT_DATA) MAP_JON_RPC_WE("decrypt_data", on_decrypt_data, wallet_public::COMMAND_DECRYPT_DATA) END_JSON_RPC_MAP() - END_URI_MAP2() + END_URI_MAP2() + bool auth_http_request(const epee::net_utils::http::http_request_info& query_info, epee::net_utils::http::http_response_info& response, connection_context& m_conn_context); //json_rpc bool on_getbalance(const wallet_public::COMMAND_RPC_GET_BALANCE::request& req, wallet_public::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_getaddress(const wallet_public::COMMAND_RPC_GET_ADDRESS::request& req, wallet_public::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er, connection_context& cntx); @@ -212,6 +213,8 @@ namespace tools bool m_do_mint; bool m_deaf; uint64_t m_last_wallet_store_height; + std::string m_jwt_secrete; + }; } // namespace tools diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 71b544c1..323dbbd8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -29,11 +29,11 @@ add_executable(net_load_tests_srv net_load_tests/srv.cpp) add_dependencies(coretests version) target_link_libraries(coretests rpc wallet currency_core common crypto zlibstatic ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) -target_link_libraries(functional_tests rpc wallet currency_core crypto common zlibstatic ethash libminiupnpc-static ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) +target_link_libraries(functional_tests rpc wallet currency_core crypto common zlibstatic ethash libminiupnpc-static ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) target_link_libraries(hash-tests crypto ethash) target_link_libraries(hash-target-tests crypto currency_core ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) -target_link_libraries(performance_tests rpc wallet currency_core common crypto zlibstatic ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) -target_link_libraries(unit_tests wallet currency_core common crypto gtest_main zlibstatic ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) +target_link_libraries(performance_tests rpc wallet currency_core common crypto zlibstatic ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) +target_link_libraries(unit_tests wallet currency_core common crypto gtest_main zlibstatic ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) target_link_libraries(net_load_tests_clt currency_core common crypto gtest_main ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) target_link_libraries(net_load_tests_srv currency_core common crypto gtest_main ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index acd1f406..36e60807 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -26,9 +26,35 @@ #include "threads_pool_tests.h" #include "wallet/plain_wallet_api.h" #include "wallet/view_iface.h" +#include + void test_plain_wallet() { + + + std::string token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCIsInNhbXBsZSI6InRlc3QifQ.lQm3N2bVlqt2-1L-FsOjtR6uE-L4E9zJutMWKIe1v1M"; + auto decoded_token = jwt::decode(token); + + auto verifier = jwt::verify() + .with_issuer("auth0") + .with_claim("sample", jwt::claim(std::string("test"))) + .allow_algorithm(jwt::algorithm::hs256 { "secret" }); + + verifier.verify(decoded_token); + + auto token = jwt::create() + .set_type("JWS") + .set_issuer("auth0") + .set_payload_claim("sample", jwt::claim(std::string("test"))) + .sign(jwt::algorithm::hs256 { "secret" }); + + + return; + + + + std::string res = plain_wallet::init("195.201.107.230", "33336", "E:\\tmp\\", 0); uint64_t instance_id = 0; diff --git a/tests/performance_tests/single_tx_test_base.h b/tests/performance_tests/single_tx_test_base.h index 9bda3809..13c4d0ae 100644 --- a/tests/performance_tests/single_tx_test_base.h +++ b/tests/performance_tests/single_tx_test_base.h @@ -18,7 +18,8 @@ public: m_bob.generate(); uint64_t block_reward_without_fee = 0; - if (!construct_miner_tx(0, 0, 0, 2, 0, m_bob.get_keys().account_address, m_bob.get_keys().account_address, m_tx, block_reward_without_fee, TRANSACTION_VERSION_PRE_HF4, blobdata(), CURRENCY_MINER_TX_MAX_OUTS)) + uint64_t block_reward = 0; + if(!construct_miner_tx(0, 0, 0, 2, 0, m_bob.get_keys().account_address, m_bob.get_keys().account_address, m_tx, block_reward_without_fee, block_reward, TRANSACTION_VERSION_PRE_HF4, blobdata(), CURRENCY_MINER_TX_MAX_OUTS)) return false; m_tx_pub_key = get_tx_pub_key_from_extra(m_tx); From 7e676e74e91756e7d18149f06ad553f6340e4cbe Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Thu, 14 Mar 2024 21:55:22 +0100 Subject: [PATCH 2/3] implemented JWT support in simplewallet --- .gitmodules | 5 +- CMakeLists.txt | 4 +- contrib/CMakeLists.txt | 2 + contrib/epee/include/misc_language.h | 25 +++++++++ contrib/jwt-cpp | 1 + src/currency_core/currency_config.h | 5 -- src/simplewallet/simplewallet.cpp | 20 ++++--- src/wallet/wallet2.cpp | 53 ++++++++++++++++++ src/wallet/wallet2.h | 8 ++- src/wallet/wallet_rpc_server.cpp | 83 ++++++++++++++++++++++++---- src/wallet/wallet_rpc_server.h | 6 +- tests/performance_tests/main.cpp | 26 ++++++++- 12 files changed, 205 insertions(+), 33 deletions(-) create mode 160000 contrib/jwt-cpp diff --git a/.gitmodules b/.gitmodules index 191af28d..97a855bd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -8,4 +8,7 @@ [submodule "contrib/tor-connect"] path = contrib/tor-connect url = https://github.com/hyle-team/tor-connect.git - branch = main \ No newline at end of file + branch = main +[submodule "contrib/jwt-cpp"] + path = contrib/jwt-cpp + url = https://github.com/Thalhammer/jwt-cpp.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 31c80a7a..9baf00a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,7 +58,7 @@ else() endif() endif() message("Generated with config types: ${CMAKE_CONFIGURATION_TYPES}, and built type: ${CMAKE_BUILD_TYPE}") - + enable_testing() set(OPENSSL_USE_STATIC_LIBS TRUE) # link statically @@ -74,7 +74,7 @@ set(DISABLE_TOR FALSE CACHE BOOL "Disable TOR library(and related tor-connect su set(TESTNET FALSE CACHE BOOL "Compile for testnet") set(BUILD_GUI FALSE CACHE BOOL "Build qt-daemon") -include_directories(src contrib/eos_portable_archive contrib contrib/epee/include ${OPENSSL_INCLUDE_DIR} "${CMAKE_BINARY_DIR}/version" "${CMAKE_BINARY_DIR}/contrib/zlib") +include_directories(src contrib/eos_portable_archive contrib contrib/epee/include contrib/jwt-cpp/include ${OPENSSL_INCLUDE_DIR} "${CMAKE_BINARY_DIR}/version" "${CMAKE_BINARY_DIR}/contrib/zlib") add_definitions(-DSTATICLIB) diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index a3220829..abfc4885 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -14,9 +14,11 @@ if(CMAKE_SYSTEM_NAME STREQUAL "iOS" OR CMAKE_SYSTEM_NAME STREQUAL "Android") message("excluded upnp support for IOS build") return() endif() + add_subdirectory(miniupnp/miniupnpc) + set_property(TARGET libminiupnpc-static PROPERTY FOLDER "contrib") set_property(TARGET zlibstatic PROPERTY FOLDER "contrib") set_property(TARGET mdbx PROPERTY FOLDER "contrib") diff --git a/contrib/epee/include/misc_language.h b/contrib/epee/include/misc_language.h index 5855d977..6e2e09f2 100644 --- a/contrib/epee/include/misc_language.h +++ b/contrib/epee/include/misc_language.h @@ -532,6 +532,31 @@ namespace misc_utils }; + template + struct expirating_set + { + typedef std::set main_set; + main_set m_set; + std::multimap m_expirations; + + const main_set& get_set() + { + return m_set; + } + void add(const key& k, const expiration_type& e) + { + auto res = m_set.insert(k); + m_expirations.insert({ e, res.first }); + } + + void remove_if_expiration_less_than(const expiration_type& e) + { + while(m_expirations.size() && m_expirations.begin()->first < e) + { + m_expirations.erase(m_expirations.begin()); + } + } + }; } // namespace misc_utils diff --git a/contrib/jwt-cpp b/contrib/jwt-cpp new file mode 160000 index 00000000..364a5572 --- /dev/null +++ b/contrib/jwt-cpp @@ -0,0 +1 @@ +Subproject commit 364a5572f4b46bb9f4304cb1c92acec8ddb2c620 diff --git a/src/currency_core/currency_config.h b/src/currency_core/currency_config.h index 50ca76a8..aee33609 100644 --- a/src/currency_core/currency_config.h +++ b/src/currency_core/currency_config.h @@ -249,13 +249,8 @@ #define BC_OFFERS_CURRENT_OFFERS_SERVICE_ARCHIVE_VER CURRENCY_FORMATION_VERSION + BLOCKCHAIN_STORAGE_MAJOR_COMPATIBILITY_VERSION + 9 #define BC_OFFERS_CURRENCY_MARKET_FILENAME "market.bin" -#ifndef TESTNET #define WALLET_FILE_SERIALIZATION_VERSION 163 #define WALLET_FILE_LAST_SUPPORTED_VERSION 163 -#else -#define WALLET_FILE_LAST_SUPPORTED_VERSION (CURRENCY_FORMATION_VERSION+76) -#define WALLET_FILE_SERIALIZATION_VERSION (CURRENCY_FORMATION_VERSION+76) -#endif #define CURRENT_MEMPOOL_ARCHIVE_VER (CURRENCY_FORMATION_VERSION+31) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 1a1f1e8e..681d356f 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -287,7 +287,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("start_mining", boost::bind(&simple_wallet::start_mining, this, ph::_1), "start_mining - Start mining in daemon"); m_cmd_binder.set_handler("stop_mining", boost::bind(&simple_wallet::stop_mining, this, ph::_1), "Stop mining in daemon"); m_cmd_binder.set_handler("refresh", boost::bind(&simple_wallet::refresh, this, ph::_1), "Resynchronize transactions and balance"); - m_cmd_binder.set_handler("balance", boost::bind(&simple_wallet::show_balance, this, ph::_1), "Show current wallet balance"); + m_cmd_binder.set_handler("balance", boost::bind(&simple_wallet::show_balance, this, ph::_1), "[force_all] Show current wallet balance, with 'force_all' param it displays all assets without filtering against whitelists"); m_cmd_binder.set_handler("show_staking_history", boost::bind(&simple_wallet::show_staking_history, this, ph::_1), "show_staking_history [2] - Show staking transfers, if option provided - number of days for history to display"); m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, ph::_1), "incoming_transfers [available|unavailable] - Show incoming transfers - all of them or filter them by availability"); m_cmd_binder.set_handler("incoming_counts", boost::bind(&simple_wallet::show_incoming_transfers_counts, this, ph::_1), "incoming_transfers counts"); @@ -539,8 +539,7 @@ void simple_wallet::handle_command_line(const boost::program_options::variables_ m_restore_wallet = command_line::get_arg(vm, arg_restore_wallet); m_disable_tor = command_line::get_arg(vm, arg_disable_tor_relay); m_voting_config_file = command_line::get_arg(vm, arg_voting_config_file); - m_no_password_confirmations = command_line::get_arg(vm, arg_no_password_confirmations); - + m_no_password_confirmations = command_line::get_arg(vm, arg_no_password_confirmations); } //---------------------------------------------------------------------------------------------------- @@ -998,9 +997,16 @@ bool simple_wallet::refresh(const std::vector& args) return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::show_balance(const std::vector& args/* = std::vector()*/) +bool simple_wallet::show_balance(const std::vector& args /* = std::vector()*/) { - success_msg_writer() << m_wallet->get_balance_str(); + if (args.size() == 1 && args[0] == "raw") + { + success_msg_writer() << m_wallet->get_balance_str_raw(); + } + else + { + success_msg_writer() << m_wallet->get_balance_str(); + } return true; } //---------------------------------------------------------------------------------------------------- @@ -2675,9 +2681,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_params, arg_set_timeout); command_line::add_arg(desc_params, arg_voting_config_file); command_line::add_arg(desc_params, arg_no_password_confirmations); - - - + tools::wallet_rpc_server::init_options(desc_params); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 87c72e83..07fab031 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3752,6 +3752,59 @@ std::string wallet2::get_balance_str() const return ss.str(); } //---------------------------------------------------------------------------------------------------- +std::string wallet2::get_balance_str_raw() const +{ + // balance unlocked / [balance total] ticker asset id + // 1391306.970000000000 / 1391306.970000000000 ZANO d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a + // 1391306.97 ZANO d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a + // 106.971 / 206.4 ZANO d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a + + static const char* header = " balance unlocked / [balance total] asset id"; + std::stringstream ss; + ss << header << ENDL; + + uint64_t dummy = 0; + std::unordered_map balances_map; + this->balance(balances_map, dummy); + + for(const auto& entry : balances_map) + { + ss << " " << std::left << std::setw(20) << print_fixed_decimal_point_with_trailing_spaces(entry.second.unlocked, 12); + if(entry.second.total == entry.second.unlocked) + ss << " "; + else + ss << " / " << std::setw(20) << print_fixed_decimal_point_with_trailing_spaces(entry.second.total, 12); + ss << " " << std::setw(8) << std::left << entry.first << ENDL; + } + + //print whitelist + ss << "WHITELIST: " << ENDL; + + + for(const auto& entry : m_whitelisted_assets) + { + ss << " " << std::left << entry.first << " " << entry.second.ticker << ENDL; + } + + // print whitelist + ss << "CUSTOM LIST: " << ENDL; + + + for(const auto& entry : m_custom_assets) + { + ss << " " << std::left << entry.first << " " << entry.second.ticker << ENDL; + } + + ss << "OWN DESCRIPTORS LIST: " << ENDL; + + for(const auto& entry : m_own_asset_descriptors) + { + ss << " " << std::left << entry.first << " " << entry.second.asset_descriptor.ticker << ENDL; + } + + return ss.str(); +} +//---------------------------------------------------------------------------------------------------- void wallet2::get_payments(const std::string& payment_id, std::list& payments, uint64_t min_height) const { auto range = m_payments.equal_range(payment_id); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index d65f5fd6..f26f88d8 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -154,7 +154,9 @@ namespace tools std::atomic m_last_sync_percent = 0; mutable uint64_t m_current_wallet_file_size = 0; bool m_use_assets_whitelisting = true; - + + // variables that should be part of state data object but should not be stored during serialization + mutable std::atomic m_whitelist_updated = false; //=============================================================== template @@ -218,7 +220,7 @@ namespace tools a & m_rollback_events; a & m_whitelisted_assets; a & m_use_assets_whitelisting; - } + } }; @@ -536,6 +538,7 @@ namespace tools void get_transfers(transfer_container& incoming_transfers) const; std::string get_transfers_str(bool include_spent = true, bool include_unspent = true, bool show_only_unknown = false, const std::string& filter_asset_ticker = std::string{}) const; std::string get_balance_str() const; + std::string get_balance_str_raw() const; // Returns all payments by given id in unspecified order void get_payments(const std::string& payment_id, std::list& payments, uint64_t min_height = 0) const; @@ -886,7 +889,6 @@ private: uint64_t m_upper_transaction_size_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value std::atomic m_stop; - mutable std::atomic m_whitelist_updated = false; std::shared_ptr m_core_proxy; std::shared_ptr m_wcallback; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index a9f87356..166dd787 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -17,7 +17,16 @@ using namespace epee; #include "wallet_rpc_server_error_codes.h" #include "wallet_helpers.h" #include "wrap_service.h" -#include +#include "jwt-cpp/jwt.h" +#include "crypto/bitcoin/sha256_helper.h" + +#define JWT_TOKEN_EXPIRATION_MAXIMUM (60 * 60) +#define JWT_TOKEN_CLAIM_NAME_BODY_HASH "body_hash" +#define JWT_TOKEN_CLAIM_NAME_SALT "salt" +#define JWT_TOKEN_CLAIM_NAME_EXPIRATION "exp" +#define JWT_TOKEN_OVERWHELM_LIMIT 100000 // if there are more records in m_jwt_used_salts then we consider it as an attack + + #define GET_WALLET() wallet_rpc_locker w(m_pwallet_provider); @@ -61,6 +70,7 @@ namespace tools const command_line::arg_descriptor wallet_rpc_server::arg_rpc_bind_ip ("rpc-bind-ip", "Specify ip to bind rpc server", "127.0.0.1"); const command_line::arg_descriptor wallet_rpc_server::arg_miner_text_info ( "miner-text-info", "Wallet password"); const command_line::arg_descriptor wallet_rpc_server::arg_deaf_mode ( "deaf", "Put wallet into 'deaf' mode make it ignore any rpc commands(usable for safe PoS mining)"); + const command_line::arg_descriptor wallet_rpc_server::arg_jwt_secret("jwt-secret", "Enables JWT auth over secret string provided"); void wallet_rpc_server::init_options(boost::program_options::options_description& desc) { @@ -68,6 +78,7 @@ namespace tools command_line::add_arg(desc, arg_rpc_bind_port); command_line::add_arg(desc, arg_miner_text_info); command_line::add_arg(desc, arg_deaf_mode); + command_line::add_arg(desc, arg_jwt_secret); } //------------------------------------------------------------------------------------------------------------------------------ wallet_rpc_server::wallet_rpc_server(std::shared_ptr wptr): @@ -185,8 +196,12 @@ namespace tools m_net_server.set_threads_prefix("RPC"); bool r = handle_command_line(vm); CHECK_AND_ASSERT_MES(r, false, "Failed to process command line in core_rpc_server"); - m_jwt_secrete = "secretesecrete"; + + if(command_line::has_arg(vm, arg_jwt_secret)) + { + m_jwt_secret = command_line::get_arg(vm, arg_jwt_secret); + } return epee::http_server_impl_base::init(m_port, m_bind_ip); } //------------------------------------------------------------------------------------------------------------------------------ @@ -197,22 +212,65 @@ namespace tools { return element.first == ZANO_ACCESS_TOKEN; }); if(it == query_info.m_header_info.m_etc_fields.end()) return false; - std::string token = it->second; //"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCIsInNhbXBsZSI6InRlc3QifQ.lQm3N2bVlqt2-1L-FsOjtR6uE-L4E9zJutMWKIe1v1M"; - auto decoded_token = jwt::decode(token); + + try + { + if(m_jwt_used_salts.get_set().size() > JWT_TOKEN_OVERWHELM_LIMIT) + { + throw std::runtime_error("Salt is overwhelmed"); + } + + auto decoded = jwt::decode(it->second, [](const std::string& str) + { return jwt::base::decode(jwt::base::pad(str)); }); + + + auto verifier = jwt::verify().allow_algorithm(jwt::algorithm::hs256 { m_jwt_secret }); + + verifier.verify(decoded); + std::string body_hash = decoded.get_payload_claim(JWT_TOKEN_CLAIM_NAME_BODY_HASH).as_string(); + std::string salt = decoded.get_payload_claim(JWT_TOKEN_CLAIM_NAME_SALT).as_string(); + crypto::hash jwt_claim_sha256 = currency::null_hash; + epee::string_tools::hex_to_pod(body_hash, jwt_claim_sha256); + crypto::hash sha256 = crypto::sha256_hash(query_info.m_body.data(), query_info.m_body.size()); + if (sha256 != jwt_claim_sha256) + { + throw std::runtime_error("Body hash missmatch"); + } + if(m_jwt_used_salts.get_set().find(salt) != m_jwt_used_salts.get_set().end()) + { + throw std::runtime_error("Salt reused"); + } + + uint64_t ticks_now = epee::misc_utils::get_tick_count(); + m_jwt_used_salts.add(salt, ticks_now + JWT_TOKEN_EXPIRATION_MAXIMUM); + m_jwt_used_salts.remove_if_expiration_less_than(ticks_now); + + + //TODO: check for salt unique + + // std::cout << "Token is valid. Claims:" << std::endl; + //for(auto& e : decoded.get_payload_json()) + //{ + // std::cout << e.first << " = " << e.second << std::endl; + //} + + LOG_PRINT_L0("JWT token OK"); + return true; + } + catch(const std::exception& e) + { + LOG_ERROR("Invalid JWT token: " << e.what()); + return false; + } - auto verifier = jwt::verify() - .with_issuer("auth0") - .with_claim("sample", jwt::claim(std::string("test"))) - .allow_algorithm(jwt::algorithm::hs256 { m_jwt_secrete }); - verifier.verify(decoded_token); return false; } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::handle_http_request(const epee::net_utils::http::http_request_info& query_info, epee::net_utils::http::http_response_info& response, connection_context& m_conn_context) { - if (m_jwt_secrete.size()) + if (m_jwt_secret.size()) { if (!auth_http_request(query_info, response, m_conn_context)) { @@ -247,6 +305,11 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + void wallet_rpc_server::set_jwt_secret(const std::string& jwt) + { + m_jwt_secret = jwt; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_getbalance(const wallet_public::COMMAND_RPC_GET_BALANCE::request& req, wallet_public::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er, connection_context& cntx) { WALLET_RPC_BEGIN_TRY_ENTRY(); diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 14da11a4..fc0e60be 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -82,12 +82,14 @@ namespace tools const static command_line::arg_descriptor arg_rpc_bind_ip; const static command_line::arg_descriptor arg_miner_text_info; const static command_line::arg_descriptor arg_deaf_mode; + const static command_line::arg_descriptor arg_jwt_secret; static void init_options(boost::program_options::options_description& desc); bool init(const boost::program_options::variables_map& vm); bool run(bool do_mint, bool offline_mode, const currency::account_public_address& miner_address); bool handle_http_request(const epee::net_utils::http::http_request_info& query_info, epee::net_utils::http::http_response_info& response, connection_context& m_conn_context); + void set_jwt_secret(const std::string& jwt); BEGIN_URI_MAP2_VIRTUAL() BEGIN_JSON_RPC_MAP("/json_rpc") @@ -223,8 +225,8 @@ namespace tools bool m_do_mint; bool m_deaf; uint64_t m_last_wallet_store_height; - std::string m_jwt_secrete; - + std::string m_jwt_secret; + epee::misc_utils::expirating_set m_jwt_used_salts; }; } // namespace tools diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 36e60807..ed6d991e 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -33,9 +33,31 @@ void test_plain_wallet() { - std::string token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCIsInNhbXBsZSI6InRlc3QifQ.lQm3N2bVlqt2-1L-FsOjtR6uE-L4E9zJutMWKIe1v1M"; + std::string token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiemFub19leHRlbnNpb24iLCJzYWx0IjoiYTUyMTk5MzQyNmYxN2Y2MDQyMzkzYTI4YzJhMzk1NjFiYTgxYmVkZDkxODJlY2E5NTY3ZDBlNjQ3YjIwZTE2NSIsImV4cCI6MTcxMDM2MzA1MH0.CwqvPBtgE8ZUFZ4cYy1ZJLWdYCnhfEiCzEhqDYCK4CQ"; auto decoded_token = jwt::decode(token); + std::string sharedSecret = "DFDvfedceEDCECECecedcyhtyh"; + + try + { + auto decoded = jwt::decode(token); + + auto verifier = jwt::verify() + .allow_algorithm(jwt::algorithm::hs256 { sharedSecret }); + + verifier.verify(decoded); + + std::cout << "Token is valid. Claims:" << std::endl; + for(auto& e : decoded.get_payload_json()) + std::cout << e.first << " = " << e.second << std::endl; + } + catch(const std::exception& e) + { + std::cerr << "Invalid token: " << e.what() << std::endl; + } + + + /* auto verifier = jwt::verify() .with_issuer("auth0") .with_claim("sample", jwt::claim(std::string("test"))) @@ -49,7 +71,7 @@ void test_plain_wallet() .set_payload_claim("sample", jwt::claim(std::string("test"))) .sign(jwt::algorithm::hs256 { "secret" }); - +*/ return; From ddbc8e2484165abd2a065dcc9594ee65a596b008 Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Thu, 14 Mar 2024 22:04:36 +0100 Subject: [PATCH 3/3] removed comment --- src/wallet/wallet_rpc_server.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 166dd787..605ffed5 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -245,15 +245,6 @@ namespace tools m_jwt_used_salts.add(salt, ticks_now + JWT_TOKEN_EXPIRATION_MAXIMUM); m_jwt_used_salts.remove_if_expiration_less_than(ticks_now); - - //TODO: check for salt unique - - // std::cout << "Token is valid. Claims:" << std::endl; - //for(auto& e : decoded.get_payload_json()) - //{ - // std::cout << e.first << " = " << e.second << std::endl; - //} - LOG_PRINT_L0("JWT token OK"); return true; }