diff --git a/src/seraphis_wallet/jamtis_keys.cpp b/src/seraphis_wallet/jamtis_keys.cpp index 5c101e8cebf..9c74d4111ee 100644 --- a/src/seraphis_wallet/jamtis_keys.cpp +++ b/src/seraphis_wallet/jamtis_keys.cpp @@ -1,46 +1,48 @@ // Copyright (c) 2024, The Monero Project -// +// // All rights reserved. -// +// // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: -// +// // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. -// +// // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other // materials provided with the distribution. -// +// // 3. Neither the name of the copyright holder nor the names of its contributors may be // used to endorse or promote products derived from this software without specific // prior written permission. -// +// // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -//paired header +// NOT FOR PRODUCTION + +// paired header #include "jamtis_keys.h" -//local headers +// local headers #include "crypto/chacha.h" #include "crypto/crypto.h" #include "crypto/x25519.h" #include "ringct/rctOps.h" +#include "ringct/rctTypes.h" #include "seraphis_core/jamtis_core_utils.h" #include "seraphis_core/sp_core_enote_utils.h" -//third party headers - -//standard headers +// third party headers +// standard headers #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "seraphis_wallet" @@ -52,8 +54,10 @@ namespace jamtis //------------------------------------------------------------------------------------------------------------------- void make_jamtis_keys(JamtisKeys &keys_out) { - keys_out.k_m = rct::rct2sk(rct::skGen()); - keys_out.k_vb = rct::rct2sk(rct::skGen()); + keys_out.k_s_legacy = rct::rct2sk(rct::zero()); + keys_out.k_v_legacy = rct::rct2sk(rct::zero()); + keys_out.k_m = rct::rct2sk(rct::skGen()); + keys_out.k_vb = rct::rct2sk(rct::skGen()); make_jamtis_unlockamounts_key(keys_out.k_vb, keys_out.xk_ua); make_jamtis_findreceived_key(keys_out.k_vb, keys_out.xk_fr); make_jamtis_generateaddress_secret(keys_out.k_vb, keys_out.s_ga); @@ -63,33 +67,27 @@ void make_jamtis_keys(JamtisKeys &keys_out) make_jamtis_findreceived_pubkey(keys_out.xk_fr, keys_out.xK_ua, keys_out.xK_fr); } //------------------------------------------------------------------------------------------------------------------- -void make_address_random(const JamtisKeys &user_keys, JamtisDestinationV1 &user_address_out) +void make_destination_random(const JamtisKeys &user_keys, JamtisDestinationV1 &user_destination_out) { address_index_t address_index; address_index = gen_address_index(); - make_jamtis_destination_v1(user_keys.K_1_base, - user_keys.xK_ua, - user_keys.xK_fr, - user_keys.s_ga, - address_index, - user_address_out); + make_jamtis_destination_v1( + user_keys.K_1_base, user_keys.xK_ua, user_keys.xK_fr, user_keys.s_ga, address_index, user_destination_out); } //------------------------------------------------------------------------------------------------------------------- -void make_address_zero(const JamtisKeys &user_keys, JamtisDestinationV1 &user_address_out) +void make_destination_zero(const JamtisKeys &user_keys, JamtisDestinationV1 &user_destination_out) { address_index_t address_index{}; - make_jamtis_destination_v1(user_keys.K_1_base, - user_keys.xK_ua, - user_keys.xK_fr, - user_keys.s_ga, - address_index, - user_address_out); + make_jamtis_destination_v1( + user_keys.K_1_base, user_keys.xK_ua, user_keys.xK_fr, user_keys.s_ga, address_index, user_destination_out); } //------------------------------------------------------------------------------------------------------------------- void JamtisKeys::encrypt(const crypto::chacha_key &key, const crypto::chacha_iv &iv) { + crypto::chacha20(k_s_legacy.data, sizeof(k_s_legacy), key, iv, k_s_legacy.data); + crypto::chacha20(k_v_legacy.data, sizeof(k_v_legacy), key, iv, k_v_legacy.data); crypto::chacha20(k_m.data, sizeof(k_m), key, iv, k_m.data); crypto::chacha20(k_vb.data, sizeof(k_vb), key, iv, k_vb.data); crypto::chacha20(xk_ua.data, sizeof(xk_ua), key, iv, (char *)xk_ua.data); @@ -98,10 +96,7 @@ void JamtisKeys::encrypt(const crypto::chacha_key &key, const crypto::chacha_iv crypto::chacha20(s_ct.data, sizeof(s_ct), key, iv, s_ct.data); } //------------------------------------------------------------------------------------------------------------------- -void JamtisKeys::decrypt(const crypto::chacha_key &key, const crypto::chacha_iv &iv) -{ - encrypt(key, iv); -} +void JamtisKeys::decrypt(const crypto::chacha_key &key, const crypto::chacha_iv &iv) { encrypt(key, iv); } //------------------------------------------------------------------------------------------------------------------- -} //namespace jamtis -} //namespace sp +} // namespace jamtis +} // namespace sp diff --git a/src/seraphis_wallet/jamtis_keys.h b/src/seraphis_wallet/jamtis_keys.h index 9f35bfe0d25..68def4499d4 100644 --- a/src/seraphis_wallet/jamtis_keys.h +++ b/src/seraphis_wallet/jamtis_keys.h @@ -50,8 +50,13 @@ namespace sp namespace jamtis { +//// +// Set of jamtis keys +/// struct JamtisKeys { + crypto::secret_key k_s_legacy; //legacy spend-key + crypto::secret_key k_v_legacy; //legacy view-key crypto::secret_key k_m; //master crypto::secret_key k_vb; //view-balance crypto::x25519_secret_key xk_ua; //unlock-amounts @@ -64,7 +69,9 @@ struct JamtisKeys bool operator==(const JamtisKeys &other) const { // use hash? - return other.k_m == k_m && + return other.k_s_legacy == k_s_legacy && + other.k_v_legacy == k_v_legacy && + other.k_m == k_m && other.k_vb == k_vb && other.xk_ua == xk_ua && other.xk_fr == xk_fr && @@ -79,10 +86,20 @@ struct JamtisKeys void decrypt(const crypto::chacha_key &key, const crypto::chacha_iv &iv); }; +/// Legacy keys +struct LegacyKeys +{ + crypto::secret_key k_s; //spend privkey + crypto::secret_key k_v; //view privkey + rct::key Ks; //main spend pubkey: Ks = k_s G + rct::key Kv; //main view pubkey: Kv = k_v G +}; + +/// make a set of jamtis keys void make_jamtis_keys(JamtisKeys &keys_out); /// make a random jamtis address for the given privkeys -void make_address_random(const JamtisKeys &user_keys, JamtisDestinationV1 &user_address_out); -void make_address_zero(const JamtisKeys &user_keys, JamtisDestinationV1 &user_address_out); +void make_destination_random(const JamtisKeys &user_keys, JamtisDestinationV1 &user_destination_out); +void make_destination_zero(const JamtisKeys &user_keys, JamtisDestinationV1 &user_destination_out); } //namespace jamtis } //namespace sp diff --git a/src/seraphis_wallet/key_container.cpp b/src/seraphis_wallet/key_container.cpp index e03bcbb29c8..fe3bbd2e9b3 100644 --- a/src/seraphis_wallet/key_container.cpp +++ b/src/seraphis_wallet/key_container.cpp @@ -31,12 +31,16 @@ // local headers #include "crypto/chacha.h" -#include "jamtis_keys.h" +#include "crypto/crypto.h" +#include "crypto/x25519.h" +#include "cryptonote_basic/account.h" #include "ringct/rctOps.h" #include "seraphis_core/jamtis_core_utils.h" #include "seraphis_core/jamtis_destination.h" #include "seraphis_core/sp_core_enote_utils.h" +#include "seraphis_wallet/address_utils.h" #include "seraphis_wallet/encrypted_file.h" +#include "seraphis_wallet/jamtis_keys.h" // standard headers @@ -46,51 +50,81 @@ namespace seraphis_wallet { //------------------------------------------------------------------------------------------------------------------- -KeyContainer::KeyContainer(JamtisKeys &&keys, const crypto::chacha_key &key) : - m_keys{keys}, +KeyContainer::KeyContainer(JamtisKeys &&sp_keys, LegacyKeys &&legacy_keys, const crypto::chacha_key &key) : + m_sp_keys{sp_keys}, + m_legacy_keys{legacy_keys}, m_encrypted{false}, m_encryption_iv{} { encrypt(key); } //------------------------------------------------------------------------------------------------------------------- -KeyContainer::KeyContainer(JamtisKeys &&keys, bool encrypted, const crypto::chacha_iv encryption_iv) : - m_keys{keys}, +KeyContainer::KeyContainer(JamtisKeys &&sp_keys, + LegacyKeys &&legacy_keys, + bool encrypted, + const crypto::chacha_iv encryption_iv) : + m_sp_keys{sp_keys}, + m_legacy_keys{legacy_keys}, m_encrypted{encrypted}, m_encryption_iv{encryption_iv} { } //------------------------------------------------------------------------------------------------------------------- -bool KeyContainer::load_from_keys_file(const std::string &path, const crypto::chacha_key &chacha_key) +bool KeyContainer::load_from_keys_file(const std::string &path, const crypto::chacha_key &chacha_key, bool check) { // 1. define serializable ser_JamtisKeys ser_keys; // 2. get the keys in the encrypted file into the serializable - CHECK_AND_ASSERT_THROW_MES( - read_encrypted_file(path, chacha_key, ser_keys), "load_from_keys_file: failed reading encrypted file."); + if (!read_encrypted_file(path, chacha_key, ser_keys)) + { + // write in log: "load_from_keys_file: failed reading encrypted file."; + return false; + } - // 3. recover jamtis keys + // 3. recover jamtis keys JamtisKeys recovered_keys{}; recover_jamtis_keys(ser_keys, recovered_keys); - // 4. check if keys are valid and move to m_keys if so - CHECK_AND_ASSERT_THROW_MES(jamtis_keys_valid(recovered_keys, chacha_key), "load_from_keys_file: failed validating jamtis keys."); - m_keys = std::move(recovered_keys); + // 4. check if keys are valid and move to m_keys if not verifying password + if (!jamtis_keys_valid(recovered_keys, chacha_key)) + { + // write in log: "load_from_keys_file: failed validating jamtis keys."; + return false; + } + + if (check) + return true; - // 5. encrypt keys in memory - encrypt(chacha_key); + // 5. store keys in m_sp_keys + m_sp_keys = std::move(recovered_keys); return true; } //------------------------------------------------------------------------------------------------------------------- +bool KeyContainer::verify_password(const crypto::chacha_key &chacha_key) +{ + // 1. decrypt keys if they are encrypted in memory + if (m_encrypted) + decrypt(chacha_key); + + // 2. test if keys are valid + bool r = jamtis_keys_valid(m_sp_keys, chacha_key); + + // 3. encrypt keys if they are decrypted + if (!m_encrypted) + encrypt(chacha_key); + + return r; +} +//------------------------------------------------------------------------------------------------------------------- bool KeyContainer::jamtis_keys_valid(const JamtisKeys &keys, const crypto::chacha_key &chacha_key) { // 1. make test_keys = keys JamtisKeys test_keys{keys}; // 2. derive keys - switch (get_wallet_type()) + switch (get_wallet_type(keys)) { case WalletType::Master: { @@ -103,20 +137,36 @@ bool KeyContainer::jamtis_keys_valid(const JamtisKeys &keys, const crypto::chach sp::jamtis::make_jamtis_findreceived_pubkey(test_keys.xk_fr, test_keys.xK_ua, test_keys.xK_fr); break; } - case WalletType::ViewOnly: + case WalletType::ViewAll: { + sp::jamtis::make_jamtis_unlockamounts_key(test_keys.k_vb, test_keys.xk_ua); + sp::jamtis::make_jamtis_findreceived_key(test_keys.k_vb, test_keys.xk_fr); + sp::jamtis::make_jamtis_generateaddress_secret(test_keys.k_vb, test_keys.s_ga); + sp::jamtis::make_jamtis_ciphertag_secret(test_keys.s_ga, test_keys.s_ct); + sp::jamtis::make_jamtis_unlockamounts_pubkey(test_keys.xk_ua, test_keys.xK_ua); sp::jamtis::make_jamtis_findreceived_pubkey(test_keys.xk_fr, test_keys.xK_ua, test_keys.xK_fr); break; } - case WalletType::ViewBalance: + case WalletType::ViewReceived: { - sp::jamtis::make_jamtis_findreceived_key(test_keys.k_vb, test_keys.xk_fr); + sp::jamtis::make_jamtis_ciphertag_secret(test_keys.s_ga, test_keys.s_ct); + sp::jamtis::make_jamtis_unlockamounts_pubkey(test_keys.xk_ua, test_keys.xK_ua); + sp::jamtis::make_jamtis_findreceived_pubkey(test_keys.xk_fr, test_keys.xK_ua, test_keys.xK_fr); + break; + } + case WalletType::FindReceived: + { + sp::jamtis::make_jamtis_findreceived_pubkey(test_keys.xk_fr, test_keys.xK_ua, test_keys.xK_fr); + break; + } + case WalletType::AddrGen: + { + sp::jamtis::make_jamtis_ciphertag_secret(test_keys.s_ga, test_keys.s_ct); break; } default: { return false; - break; } } @@ -134,7 +184,7 @@ bool KeyContainer::encrypt(const crypto::chacha_key &chacha_key) m_encryption_iv = crypto::rand(); // 3. encrypt keys with chacha_key and iv - m_keys.encrypt(chacha_key, m_encryption_iv); + m_sp_keys.encrypt(chacha_key, m_encryption_iv); // 4. set encrypted flag true m_encrypted = true; @@ -149,7 +199,7 @@ bool KeyContainer::decrypt(const crypto::chacha_key &chacha_key) return false; // 2. decrypt keys with chacha_key and iv - m_keys.decrypt(chacha_key, m_encryption_iv); + m_sp_keys.decrypt(chacha_key, m_encryption_iv); // 3. set encrypted flag false m_encrypted = false; @@ -157,17 +207,38 @@ bool KeyContainer::decrypt(const crypto::chacha_key &chacha_key) return true; } //------------------------------------------------------------------------------------------------------------------- -void KeyContainer::generate_keys(const crypto::chacha_key &chacha_key) +void KeyContainer::convert_legacy_keys(const cryptonote::account_base &legacy_keys) +{ + m_sp_keys.k_s_legacy = legacy_keys.get_keys().m_spend_secret_key; + m_sp_keys.k_v_legacy = legacy_keys.get_keys().m_view_secret_key; + m_legacy_keys.k_s = legacy_keys.get_keys().m_spend_secret_key; + m_legacy_keys.k_v = legacy_keys.get_keys().m_view_secret_key; + m_legacy_keys.Ks = rct::pk2rct(legacy_keys.get_keys().m_account_address.m_spend_public_key); + m_legacy_keys.Kv = rct::pk2rct(legacy_keys.get_keys().m_account_address.m_view_public_key); +} +//------------------------------------------------------------------------------------------------------------------- +void KeyContainer::derive_seraphis_keys_from_legacy() +{ + // Spec: https://gist.github.com/tevador/50160d160d24cfc6c52ae02eb3d17024#33-elliptic-curves + + m_sp_keys.k_m = m_legacy_keys.k_s; + make_jamtis_viewbalance_key(m_sp_keys.k_m, m_sp_keys.k_vb); + make_jamtis_unlockamounts_key(m_sp_keys.k_vb, m_sp_keys.xk_ua); + make_jamtis_findreceived_key(m_sp_keys.k_vb, m_sp_keys.xk_fr); + make_jamtis_generateaddress_secret(m_sp_keys.k_vb, m_sp_keys.s_ga); + make_jamtis_ciphertag_secret(m_sp_keys.s_ga, m_sp_keys.s_ct); + make_seraphis_spendkey(m_sp_keys.k_vb, m_sp_keys.k_m, m_sp_keys.K_1_base); + make_jamtis_unlockamounts_pubkey(m_sp_keys.xk_ua, m_sp_keys.xK_ua); + make_jamtis_findreceived_pubkey(m_sp_keys.xk_fr, m_sp_keys.xK_ua, m_sp_keys.xK_fr); +} +//------------------------------------------------------------------------------------------------------------------- +void KeyContainer::generate_keys() { // 1. generate new keys and store to m_keys - make_jamtis_keys(m_keys); - - // 2. encrypt keys if they are decrypted - if (!m_encrypted) - encrypt(chacha_key); + make_jamtis_keys(m_sp_keys); } //------------------------------------------------------------------------------------------------------------------- -bool KeyContainer::write_all(const std::string &path, const crypto::chacha_key &chacha_key) +bool KeyContainer::write_master(const std::string &path, const crypto::chacha_key &chacha_key) { // 1. decrypt keys if they are encrypted in memory if (m_encrypted) @@ -177,26 +248,24 @@ bool KeyContainer::write_all(const std::string &path, const crypto::chacha_key & // (the serializable with the decrypted private keys will // remain in memory only during the scope of the function) ser_JamtisKeys ser_keys = { - .k_m = m_keys.k_m, - .k_vb = m_keys.k_vb, - .xk_ua = m_keys.xk_ua, - .xk_fr = m_keys.xk_fr, - .s_ga = m_keys.s_ga, - .s_ct = m_keys.s_ct, - .K_1_base = m_keys.K_1_base, - .xK_ua = m_keys.xK_ua, - .xK_fr = m_keys.xK_fr, + .k_s_legacy = m_sp_keys.k_s_legacy, + .k_v_legacy = m_sp_keys.k_v_legacy, + .k_m = m_sp_keys.k_m, + .k_vb = m_sp_keys.k_vb, + .xk_ua = m_sp_keys.xk_ua, + .xk_fr = m_sp_keys.xk_fr, + .s_ga = m_sp_keys.s_ga, + .s_ct = m_sp_keys.s_ct, + .K_1_base = m_sp_keys.K_1_base, + .xK_ua = m_sp_keys.xK_ua, + .xK_fr = m_sp_keys.xK_fr, }; - // 3. encrypt keys if they are decrypted - if (!m_encrypted) - encrypt(chacha_key); - // 4. write serializable to file return write_encrypted_file(path, chacha_key, ser_keys); } //------------------------------------------------------------------------------------------------------------------- -bool KeyContainer::write_view_only(const std::string &path, const crypto::chacha_key &chacha_key) +bool KeyContainer::write_view_all(const std::string &path, const crypto::chacha_key &chacha_key) { // 1. decrypt keys if they are encrypted in memory if (m_encrypted) @@ -205,27 +274,52 @@ bool KeyContainer::write_view_only(const std::string &path, const crypto::chacha // 2. copy keys to serializable // (the serializable with the decrypted private keys will // remain in memory only during the scope of the function) - ser_JamtisKeys view_only_keys = { + ser_JamtisKeys view_all{ + .k_s_legacy = {}, + .k_v_legacy = m_sp_keys.k_v_legacy, .k_m = {}, - .k_vb = {}, - .xk_ua = {}, - .xk_fr = m_keys.xk_fr, - .s_ga = {}, - .s_ct = {}, - .K_1_base = {}, - .xK_ua = m_keys.xK_ua, - .xK_fr = m_keys.xK_fr, + .k_vb = m_sp_keys.k_vb, + .xk_ua = m_sp_keys.xk_ua, + .xk_fr = m_sp_keys.xk_fr, + .s_ga = m_sp_keys.s_ga, + .s_ct = m_sp_keys.s_ct, + .K_1_base = m_sp_keys.K_1_base, + .xK_ua = m_sp_keys.xK_ua, + .xK_fr = m_sp_keys.xK_fr, }; - // 3. encrypt keys if they are decrypted - if (!m_encrypted) - encrypt(chacha_key); + // 3. write serializable to file + return write_encrypted_file(path, chacha_key, view_all); +} +//------------------------------------------------------------------------------------------------------------------- +bool KeyContainer::write_view_received(const std::string &path, const crypto::chacha_key &chacha_key) +{ + // 1. decrypt keys if they are encrypted in memory + if (m_encrypted) + decrypt(chacha_key); - // 4. write serializable to file - return write_encrypted_file(path, chacha_key, view_only_keys); + // 2. copy keys to serializable + // (the serializable with the decrypted private keys will + // remain in memory only during the scope of the function) + ser_JamtisKeys view_received{ + .k_s_legacy = {}, + .k_v_legacy = m_sp_keys.k_v_legacy, + .k_m = {}, + .k_vb = {}, + .xk_ua = m_sp_keys.xk_ua, + .xk_fr = m_sp_keys.xk_fr, + .s_ga = m_sp_keys.s_ga, + .s_ct = m_sp_keys.s_ct, + .K_1_base = m_sp_keys.K_1_base, + .xK_ua = m_sp_keys.xK_ua, + .xK_fr = m_sp_keys.xK_fr, + }; + + // 3. write serializable to file + return write_encrypted_file(path, chacha_key, view_received); } //------------------------------------------------------------------------------------------------------------------- -bool KeyContainer::write_view_balance(const std::string &path, const crypto::chacha_key &chacha_key) +bool KeyContainer::write_find_received(const std::string &path, const crypto::chacha_key &chacha_key) { // 1. decrypt keys if they are encrypted in memory if (m_encrypted) @@ -234,83 +328,150 @@ bool KeyContainer::write_view_balance(const std::string &path, const crypto::cha // 2. copy keys to serializable // (the serializable with the decrypted private keys will // remain in memory only during the scope of the function) - ser_JamtisKeys view_balance{ + ser_JamtisKeys find_received{ + .k_s_legacy = {}, + .k_v_legacy = m_sp_keys.k_v_legacy, .k_m = {}, - .k_vb = m_keys.k_vb, + .k_vb = {}, .xk_ua = {}, - .xk_fr = m_keys.xk_fr, + .xk_fr = m_sp_keys.xk_fr, .s_ga = {}, .s_ct = {}, - .K_1_base = {}, - .xK_ua = m_keys.xK_ua, - .xK_fr = m_keys.xK_fr, + .K_1_base = m_sp_keys.K_1_base, + .xK_ua = m_sp_keys.xK_ua, + .xK_fr = m_sp_keys.xK_fr, }; - // 3. encrypt keys if they are decrypted - if (!m_encrypted) - encrypt(chacha_key); + // 3. write serializable to file + return write_encrypted_file(path, chacha_key, find_received); +} +//------------------------------------------------------------------------------------------------------------------- +bool KeyContainer::write_address_generator(const std::string &path, const crypto::chacha_key &chacha_key) +{ + // 1. decrypt keys if they are encrypted in memory + if (m_encrypted) + decrypt(chacha_key); - // 4. write serializable to file - return write_encrypted_file(path, chacha_key, view_balance); + // 2. copy keys to serializable + // (the serializable with the decrypted private keys will + // remain in memory only during the scope of the function) + ser_JamtisKeys address_generator{ + .k_s_legacy = {}, + .k_v_legacy = m_sp_keys.k_v_legacy, + .k_m = {}, + .k_vb = {}, + .xk_ua = {}, + .xk_fr = {}, + .s_ga = m_sp_keys.s_ga, + .s_ct = m_sp_keys.s_ct, + .K_1_base = m_sp_keys.K_1_base, + .xK_ua = m_sp_keys.xK_ua, + .xK_fr = m_sp_keys.xK_fr, + }; + + // 3. write serializable to file + return write_encrypted_file(path, chacha_key, address_generator); } //------------------------------------------------------------------------------------------------------------------- WalletType KeyContainer::get_wallet_type() +{ + return get_wallet_type(m_sp_keys); +} +//------------------------------------------------------------------------------------------------------------------- +WalletType KeyContainer::get_wallet_type(const JamtisKeys sp_keys) { // 1. check which keys are present - if (m_keys.k_m == rct::rct2sk(rct::zero())) + if (sp_keys.k_m == rct::rct2sk(rct::zero())) { - if (m_keys.k_vb == rct::rct2sk(rct::zero())) - return WalletType::ViewOnly; + if (sp_keys.k_vb == rct::rct2sk(rct::zero())) + { + if (sp_keys.xk_ua == crypto::x25519_secret_key{}) + { + if (sp_keys.xk_fr == crypto::x25519_secret_key{}) + return WalletType::AddrGen; + else + return WalletType::FindReceived; + } + return WalletType::ViewReceived; + } else - return WalletType::ViewBalance; + return WalletType::ViewAll; } return WalletType::Master; } //------------------------------------------------------------------------------------------------------------------- +std::string KeyContainer::get_address_random(const JamtisAddressVersion address_version, + const JamtisAddressNetwork address_network) +{ + // 1. get a random destination address + std::string str_address; + JamtisDestinationV1 dest_address; + make_destination_random(m_sp_keys, dest_address); + get_str_from_destination(dest_address, address_version, address_network, str_address); + + // 2. return address + return str_address; +} +//------------------------------------------------------------------------------------------------------------------- +void KeyContainer::get_random_destination(JamtisDestinationV1 &dest_out) +{ + make_destination_random(m_sp_keys, dest_out); +} +//------------------------------------------------------------------------------------------------------------------- +std::string KeyContainer::get_address_zero(const JamtisAddressVersion address_version, + const JamtisAddressNetwork address_network) +{ + + // 1. get destination address corresponding to index zero + std::string str_address; + JamtisDestinationV1 dest_address; + make_destination_zero(m_sp_keys, dest_address); + get_str_from_destination(dest_address, address_version, address_network, str_address); + + // 2. return address + return str_address; +} +//------------------------------------------------------------------------------------------------------------------- void KeyContainer::make_serializable_jamtis_keys(ser_JamtisKeys &serializable_keys) { - serializable_keys.k_m = m_keys.k_m; - serializable_keys.k_vb = m_keys.k_vb; - serializable_keys.xk_ua = m_keys.xk_ua; - serializable_keys.xk_fr = m_keys.xk_fr; - serializable_keys.s_ga = m_keys.s_ga; - serializable_keys.s_ct = m_keys.s_ct; - serializable_keys.K_1_base = m_keys.K_1_base; - serializable_keys.xK_ua = m_keys.xK_ua; - serializable_keys.xK_fr = m_keys.xK_fr; + serializable_keys.k_s_legacy = m_sp_keys.k_s_legacy; + serializable_keys.k_v_legacy = m_sp_keys.k_v_legacy; + serializable_keys.k_m = m_sp_keys.k_m; + serializable_keys.k_vb = m_sp_keys.k_vb; + serializable_keys.xk_ua = m_sp_keys.xk_ua; + serializable_keys.xk_fr = m_sp_keys.xk_fr; + serializable_keys.s_ga = m_sp_keys.s_ga; + serializable_keys.s_ct = m_sp_keys.s_ct; + serializable_keys.K_1_base = m_sp_keys.K_1_base; + serializable_keys.xK_ua = m_sp_keys.xK_ua; + serializable_keys.xK_fr = m_sp_keys.xK_fr; } //------------------------------------------------------------------------------------------------------------------- void KeyContainer::recover_jamtis_keys(const ser_JamtisKeys &ser_keys, JamtisKeys &keys_out) { - keys_out.k_m = ser_keys.k_m; - keys_out.k_vb = ser_keys.k_vb; - keys_out.xk_ua = ser_keys.xk_ua; - keys_out.xk_fr = ser_keys.xk_fr; - keys_out.s_ga = ser_keys.s_ga; - keys_out.s_ct = ser_keys.s_ct; - keys_out.K_1_base = ser_keys.K_1_base; - keys_out.xK_ua = ser_keys.xK_ua; - keys_out.xK_fr = ser_keys.xK_fr; + keys_out.k_s_legacy = ser_keys.k_s_legacy; + keys_out.k_v_legacy = ser_keys.k_v_legacy; + keys_out.k_m = ser_keys.k_m; + keys_out.k_vb = ser_keys.k_vb; + keys_out.xk_ua = ser_keys.xk_ua; + keys_out.xk_fr = ser_keys.xk_fr; + keys_out.s_ga = ser_keys.s_ga; + keys_out.s_ct = ser_keys.s_ct; + keys_out.K_1_base = ser_keys.K_1_base; + keys_out.xK_ua = ser_keys.xK_ua; + keys_out.xK_fr = ser_keys.xK_fr; } //------------------------------------------------------------------------------------------------------------------- bool KeyContainer::compare_keys(KeyContainer &other, const crypto::chacha_key &chacha_key) { - // 1. decrypt keys if they are encrypted in memory - other.decrypt(chacha_key); - - // 2. decrypt if encrypted in memory - decrypt(chacha_key); - - bool r = other.m_keys.k_m == m_keys.k_m && other.m_keys.k_vb == m_keys.k_vb && other.m_keys.xk_ua == m_keys.xk_ua && - other.m_keys.xk_fr == m_keys.xk_fr && other.m_keys.s_ga == m_keys.s_ga && - other.m_keys.s_ct == m_keys.s_ct && other.m_keys.K_1_base == m_keys.K_1_base && - other.m_keys.xK_ua == m_keys.xK_ua && other.m_keys.xK_fr == m_keys.xK_fr; - - // 3. encrypt in memory - other.encrypt(chacha_key); - // 4. encrypt in memory - encrypt(chacha_key); + bool r = other.m_sp_keys.k_s_legacy == m_sp_keys.k_s_legacy && + other.m_sp_keys.k_v_legacy == m_sp_keys.k_v_legacy && + other.m_sp_keys.k_m == m_sp_keys.k_m && other.m_sp_keys.k_vb == m_sp_keys.k_vb && + other.m_sp_keys.xk_ua == m_sp_keys.xk_ua && other.m_sp_keys.xk_fr == m_sp_keys.xk_fr && + other.m_sp_keys.s_ga == m_sp_keys.s_ga && other.m_sp_keys.s_ct == m_sp_keys.s_ct && + other.m_sp_keys.K_1_base == m_sp_keys.K_1_base && other.m_sp_keys.xK_ua == m_sp_keys.xK_ua && + other.m_sp_keys.xK_fr == m_sp_keys.xK_fr; // 5. return result of comparison return r; diff --git a/src/seraphis_wallet/key_container.h b/src/seraphis_wallet/key_container.h index 50896044d12..1fe70325c1a 100644 --- a/src/seraphis_wallet/key_container.h +++ b/src/seraphis_wallet/key_container.h @@ -31,7 +31,10 @@ // local headers #include "crypto/chacha.h" #include "crypto/crypto.h" -#include "jamtis_keys.h" +#include "cryptonote_basic/account.h" +#include "seraphis_core/jamtis_destination.h" +#include "seraphis_wallet/address_utils.h" +#include "seraphis_wallet/jamtis_keys.h" #include "serialization/serialization.h" // third party headers @@ -46,6 +49,8 @@ using namespace sp::jamtis; // NOTE: I don't think this is a good idea. struct ser_JamtisKeys { + crypto::secret_key k_s_legacy; //legacy spend-key + crypto::secret_key k_v_legacy; //legacy view-key crypto::secret_key k_m; // master crypto::secret_key k_vb; // view-balance crypto::x25519_secret_key xk_ua; // unlock-amounts @@ -57,6 +62,8 @@ struct ser_JamtisKeys crypto::x25519_pubkey xK_fr; // find-received pubkey = xk_fr xk_ua xG BEGIN_SERIALIZE() + FIELD(k_s_legacy) + FIELD(k_v_legacy) FIELD(k_m) FIELD(k_vb) FIELD(xk_ua) @@ -90,9 +97,11 @@ namespace seraphis_wallet enum class WalletType { - ViewOnly, - ViewBalance, Master, + ViewAll, + ViewReceived, + FindReceived, + AddrGen, }; /// KeyContainer @@ -101,45 +110,61 @@ enum class WalletType class KeyContainer { public: - KeyContainer(JamtisKeys &&keys, const crypto::chacha_key &key); + KeyContainer(JamtisKeys &&sp_keys, LegacyKeys &&legacy_keys, const crypto::chacha_key &key); - KeyContainer() : m_keys{}, m_encryption_iv{}, m_encrypted{false} {} + KeyContainer() : m_sp_keys{}, m_legacy_keys{}, m_encryption_iv{}, m_encrypted{false} {} - KeyContainer(JamtisKeys &&keys, + KeyContainer(JamtisKeys &&sp_keys, + LegacyKeys &&legacy_keys, bool encrypted, const crypto::chacha_iv encryption_iv); // member functions /// verify if is encrypted - bool is_encrypted() { return m_encrypted; } + bool is_encrypted() const {return m_encrypted; } /// load keys from a file and ensure their validity - bool load_from_keys_file(const std::string &path, const crypto::chacha_key &chacha_key); + bool load_from_keys_file(const std::string &path, const crypto::chacha_key &chacha_key, bool check); + + /// verify if password is valid + bool verify_password(const crypto::chacha_key &chacha_key); /// check if keys are valid bool jamtis_keys_valid(const JamtisKeys &keys, const crypto::chacha_key &chacha_key); - /// encrypt the keys in-memory + /// encrypt keys in-memory bool encrypt(const crypto::chacha_key &chacha_key); - /// decrypt the keys in-memory + /// decrypt keys in-memory bool decrypt(const crypto::chacha_key &chacha_key); - /// generate new keys - void generate_keys(const crypto::chacha_key &chacha_key); + /// convert legacy keys format into new legacy keys struct + void convert_legacy_keys(const cryptonote::account_base &legacy_keys); - /// write all private keys to file - bool write_all(const std::string &path, crypto::chacha_key const &chacha_key); + /// derive seraphis_keys from legacy + void derive_seraphis_keys_from_legacy(); - /// write view-only keys to file - bool write_view_only(const std::string &path, const crypto::chacha_key &chacha_key); + /// generate new keys + void generate_keys(); - /// write view-balance keys to file - bool write_view_balance(const std::string &path, const crypto::chacha_key &chacha_key); + /// write wallet tiers + bool write_master(const std::string &path, crypto::chacha_key const &chacha_key); + bool write_view_all(const std::string &path, const crypto::chacha_key &chacha_key); + bool write_view_received(const std::string &path, const crypto::chacha_key &chacha_key); + bool write_find_received(const std::string &path, const crypto::chacha_key &chacha_key); + bool write_address_generator(const std::string &path, const crypto::chacha_key &chacha_key); /// get the wallet type of the loaded keys WalletType get_wallet_type(); + WalletType get_wallet_type(const JamtisKeys sp_keys); + + /// get a random address for loaded wallet + std::string get_address_random(const JamtisAddressVersion address_version, const JamtisAddressNetwork address_network); + void get_random_destination(JamtisDestinationV1 &dest_out); + + /// get the zero address for loaded wallet + std::string get_address_zero(const JamtisAddressVersion address_version, const JamtisAddressNetwork address_network); /// make jamtis_keys serializable void make_serializable_jamtis_keys(ser_JamtisKeys &serializable_keys); @@ -150,12 +175,20 @@ class KeyContainer /// compare the keys of two containers that have the same chacha_key bool compare_keys(KeyContainer &other, const crypto::chacha_key &chacha_key); + /// return keys + const JamtisKeys& get_sp_keys() const {return m_sp_keys;} + const LegacyKeys& get_legacy_keys() const {return m_legacy_keys;} + + private: /// initialization vector crypto::chacha_iv m_encryption_iv; - /// struct that contains the private keys - epee::mlocked m_keys; + /// struct that contains the seraphis private keys + epee::mlocked m_sp_keys; + + /// struct that contains the legacy private keys + epee::mlocked m_legacy_keys; /// true if keys are encrypted in memory bool m_encrypted; diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 34ccf6ff02d..06ae877ab50 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -31,6 +31,7 @@ set(unit_tests_sources apply_permutation.cpp address_from_url.cpp async.cpp + base32.cpp base58.cpp blockchain_db.cpp block_queue.cpp @@ -62,7 +63,6 @@ set(unit_tests_sources hmac_keccak.cpp http.cpp keccak.cpp - key_container.cpp levin.cpp logging.cpp long_term_block_weight.cpp @@ -98,6 +98,7 @@ set(unit_tests_sources seraphis_tx.cpp sha256.cpp slow_memmem.cpp + spw_key_container.cpp subaddress.cpp tasking_system_demo.cpp test_tx_utils.cpp @@ -114,6 +115,7 @@ set(unit_tests_sources output_selection.cpp vercmp.cpp ringdb.cpp + wallet_storage.cpp wipeable_string.cpp is_hdd.cpp aligned.cpp @@ -136,6 +138,7 @@ target_link_libraries(unit_tests cryptonote_core daemon_messages daemon_rpc_server + device_trezor blockchain_db lmdb_lib mx25519_static @@ -149,6 +152,7 @@ target_link_libraries(unit_tests seraphis_mocks seraphis_wallet wallet + wallet2_basic p2p version ${Boost_CHRONO_LIBRARY} diff --git a/tests/unit_tests/key_container.cpp b/tests/unit_tests/key_container.cpp deleted file mode 100644 index 905ce99114f..00000000000 --- a/tests/unit_tests/key_container.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include -#include - -#include "crypto/chacha.h" -#include "seraphis_wallet/key_container.h" -#include "unit_tests_utils.h" - -using namespace seraphis_wallet; - -TEST(seraphis_wallet, store_and_load_key_container) -{ - // 1. create variables, set password and path - KeyContainer kc_all{},kc_all_recovered{},kc_vo{},kc_vb{}; - crypto::chacha_key chacha_key; - const uint64_t kdf_rounds = 1; - const epee::wipeable_string password = "password"; - const boost::filesystem::path wallet_file_all = unit_test::data_dir / "wallet3.spkeys"; - const boost::filesystem::path wallet_file_vo = unit_test::data_dir / "wallet3_vo.spkeys"; - - // 2. generate chacha_key and keys of container - crypto::generate_chacha_key(password.data(),password.length(),chacha_key,kdf_rounds); - kc_all.generate_keys(chacha_key); - - // 3. save keys to file - ASSERT_TRUE(kc_all.write_all(wallet_file_all.string(), chacha_key)); - ASSERT_TRUE(kc_all.write_view_only(wallet_file_vo.string(), chacha_key)); - - // 4. load keys from file - ASSERT_TRUE(kc_all_recovered.load_from_keys_file(wallet_file_all.string(), chacha_key)); - ASSERT_TRUE(kc_vo.load_from_keys_file(wallet_file_vo.string(), chacha_key)); - - // 5. verify if stored and loaded keys are the same - ASSERT_TRUE(kc_all.compare_keys(kc_all_recovered, chacha_key)); - ASSERT_FALSE(kc_all.compare_keys(kc_vo, chacha_key)); -} diff --git a/tests/unit_tests/spw_key_container.cpp b/tests/unit_tests/spw_key_container.cpp new file mode 100644 index 00000000000..98562e4f78c --- /dev/null +++ b/tests/unit_tests/spw_key_container.cpp @@ -0,0 +1,69 @@ +// Copyright (c) 2014-2024, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include + +#include "crypto/chacha.h" +#include "seraphis_wallet/key_container.h" +#include "unit_tests_utils.h" + +using namespace seraphis_wallet; + +TEST(seraphis_wallet, key_container) { + KeyContainer container; + crypto::chacha_key key; +} + +TEST(seraphis_wallet, store_and_load_key_container) +{ + // 1. create variables, set password and path + KeyContainer kc_all{},kc_all_recovered{},kc_vo{},kc_vb{}; + crypto::chacha_key chacha_key; + const uint64_t kdf_rounds = 1; + const epee::wipeable_string password = "password"; + const boost::filesystem::path wallet_file_all = unit_test::data_dir / "wallet3.spkeys"; + const boost::filesystem::path wallet_file_vo = unit_test::data_dir / "wallet3_vo.spkeys"; + + // 2. generate chacha_key and keys of container + crypto::generate_chacha_key(password.data(),password.length(),chacha_key,kdf_rounds); + kc_all.generate_keys(); + + // 3. save keys to file + ASSERT_TRUE(kc_all.write_master(wallet_file_all.string(), chacha_key)); + ASSERT_TRUE(kc_all.write_view_all(wallet_file_vo.string(), chacha_key)); + + // 4. load keys from file + ASSERT_TRUE(kc_all_recovered.load_from_keys_file(wallet_file_all.string(), chacha_key, false)); + ASSERT_TRUE(kc_vo.load_from_keys_file(wallet_file_vo.string(), chacha_key, false)); + + // 5. verify if stored and loaded keys are the same + ASSERT_TRUE(kc_all.compare_keys(kc_all_recovered, chacha_key)); + ASSERT_FALSE(kc_all.compare_keys(kc_vo, chacha_key)); +}