changed passing of mnemonic_string by value to by ref and taking copy for normalization to simplify interface; returning detected mnemonic language from wallet_with rather than requiring it; added are_equal_mnemonics function

pull/29/head
Paul Shapiro 6 years ago
parent d599d30456
commit 8c17ae4766

@ -35,6 +35,7 @@
#include "cryptonote_basic.h"
#include "device/device.hpp"
#include "cryptonote_basic/account.h"
#include "wallet_errors.h" // not crazy about including this but it's not that bad
#include "keccak.h"
//
#include "string_tools.h"
@ -105,7 +106,7 @@ bool monero_wallet_utils::new_wallet(
std::string address_string = account.get_public_address_str(nettype); // getting the string here instead of leaving it to the consumer b/c get_public_address_str could potentially change in implementation (see TODO) so it's not right to duplicate that here
//
std::string mnemonic_string;
bool r = crypto::ElectrumWords::bytes_to_words(nonLegacy32B_sec_seed, mnemonic_string, std::move(mnemonic_language));
bool r = crypto::ElectrumWords::bytes_to_words(nonLegacy32B_sec_seed, mnemonic_string, mnemonic_language);
// ^-- it's OK to directly call ElectrumWords w/ crypto::secret_key as we are generating new wallet, not reading
if (!r) {
retVals.did_error = true;
@ -124,28 +125,44 @@ bool monero_wallet_utils::new_wallet(
keys.m_account_address.m_spend_public_key,
keys.m_account_address.m_view_public_key,
//
mnemonic_string
mnemonic_string,
mnemonic_language
};
return true;
}
//
bool monero_wallet_utils::are_equal_mnemonics(const string &words_a, const string &words_b)
{
bool r;
//
MnemonicDecodedSeed_RetVals retVals__a;
r = decoded_seed(std::move(words_a), retVals__a);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Can't check equality of invalid mnemonic (a)");
//
MnemonicDecodedSeed_RetVals retVals__b;
r = decoded_seed(std::move(words_b), retVals__b);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Can't check equality of invalid mnemonic (b)");
//
return *(retVals__a.optl__sec_seed) == *(retVals__b.optl__sec_seed);
}
//
const uint32_t stable_32B_seed_mnemonic_word_count = 25;
const uint32_t legacy_16B_seed_mnemonic_word_count = 13;
bool monero_wallet_utils::decoded_seed(
string mnemonic_string,
string mnemonic_language,
const string &mnemonic_string__ref,
MnemonicDecodedSeed_RetVals &retVals
) {
retVals = {};
//
// sanitize inputs
if (mnemonic_string.empty()) {
if (mnemonic_string__ref.empty()) {
retVals.did_error = true;
retVals.err_string = "Please enter a valid seed";
//
return false;
}
string mnemonic_string = mnemonic_string__ref; // just going to take a copy rather than require people to pass mutable string in.
boost::algorithm::to_lower(mnemonic_string); // critical
// TODO: strip wrapping whitespace? anything else?
//
@ -153,8 +170,9 @@ bool monero_wallet_utils::decoded_seed(
unsigned long word_count = std::distance(std::istream_iterator<std::string>(stream), std::istream_iterator<std::string>());
// unsigned long word_count = boost::range::distance(boost::algorithm::make_split_iterator(mnemonic_string, boost::algorithm::is_space())); // TODO: get this workin
//
crypto::secret_key sec_seed;
std::string sec_seed_string; // TODO/FIXME: needed this for shared ref outside of if branch below… not intending extra default constructor call but not sure how to get around it yet
secret_key sec_seed;
string sec_seed_string; // TODO/FIXME: needed this for shared ref outside of if branch below… not intending extra default constructor call but not sure how to get around it yet
string mnemonic_language;
bool from_legacy16B_lw_seed = false;
if (word_count == stable_32B_seed_mnemonic_word_count) {
from_legacy16B_lw_seed = false; // to be clear
@ -184,6 +202,7 @@ bool monero_wallet_utils::decoded_seed(
//
return false;
}
retVals.mnemonic_language = mnemonic_language;
retVals.optl__sec_seed = sec_seed;
retVals.optl__sec_seed_string = sec_seed_string;
retVals.optl__mnemonic_string = mnemonic_string;
@ -236,14 +255,13 @@ SeedDecodedMnemonic_RetVals monero_wallet_utils::mnemonic_string_from_seed_hex_s
//
bool monero_wallet_utils::wallet_with(
const string &mnemonic_string,
const string &mnemonic_language,
WalletDescriptionRetVals &retVals,
cryptonote::network_type nettype
) {
retVals = {};
//
MnemonicDecodedSeed_RetVals decodedSeed_retVals;
bool r = decoded_seed(std::move(mnemonic_string), std::move(mnemonic_language), decodedSeed_retVals);
bool r = decoded_seed(mnemonic_string, decodedSeed_retVals);
if (!r) {
retVals.did_error = true;
retVals.err_string = *decodedSeed_retVals.err_string; // TODO: assert?
@ -268,6 +286,7 @@ bool monero_wallet_utils::wallet_with(
keys.m_account_address.m_view_public_key,
//
*decodedSeed_retVals.optl__mnemonic_string, // assumed non nil if r; copied for return
*decodedSeed_retVals.mnemonic_language
};
return true;
}

@ -50,6 +50,8 @@ namespace monero_wallet_utils
using namespace std;
using namespace boost;
using namespace tools;
using namespace crypto;
using namespace cryptonote;
//
// 16B keys
POD_CLASS ec_nonscalar_16Byte {
@ -59,31 +61,33 @@ namespace monero_wallet_utils
using legacy16B_secret_key = tools::scrubbed<ec_nonscalar_16Byte>;
void coerce_valid_sec_key_from(
const legacy16B_secret_key &legacy16B_mymonero_sec_seed,
crypto::secret_key &dst__sec_seed
secret_key &dst__sec_seed
);
static_assert(sizeof(legacy16B_secret_key) == 16, "Invalid structure size");
inline std::ostream &operator <<(std::ostream &o, const legacy16B_secret_key &v) {
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
}
const static legacy16B_secret_key null_legacy16B_skey = boost::value_initialized<legacy16B_secret_key>();
const static unsigned long sec_seed_hex_string_length = sizeof(crypto::secret_key) * 2;
const static unsigned long sec_seed_hex_string_length = sizeof(secret_key) * 2;
const static unsigned long legacy16B__sec_seed_hex_string_length = sizeof(legacy16B_secret_key) * 2;
//
bool words_to_bytes(std::string words, legacy16B_secret_key& dst, std::string &language_name);
bool bytes_to_words(const legacy16B_secret_key& src, std::string& words, const std::string &language_name);
//
bool are_equal_mnemonics(const string &words_a, const string &words_b);
//
//
// Accounts
struct MnemonicDecodedSeed_RetVals: RetVals_base
{
optional<crypto::secret_key> optl__sec_seed = none;
optional<secret_key> optl__sec_seed = none;
optional<string> optl__sec_seed_string = none;
optional<string> optl__mnemonic_string = none;
optional<string> mnemonic_language = none;
bool from_legacy16B_lw_seed = false;
};
bool decoded_seed(
string mnemonic_string,
string mnemonic_language,
const string &mnemonic_string,
//
MnemonicDecodedSeed_RetVals &retVals
);
@ -104,27 +108,27 @@ namespace monero_wallet_utils
//
string address_string;
//
crypto::secret_key sec_spendKey;
crypto::secret_key sec_viewKey;
crypto::public_key pub_spendKey;
crypto::public_key pub_viewKey;
secret_key sec_spendKey;
secret_key sec_viewKey;
public_key pub_spendKey;
public_key pub_viewKey;
//
std::string mnemonic_string; // mnemonic_language is not returned because it must be provided to all functions which can return a WalletDescription
string mnemonic_string;
string mnemonic_language; // this might be redundant if the function returning this WalletDescription itself required the language, such as new_wallet
};
struct WalletDescriptionRetVals: RetVals_base
{
boost::optional<WalletDescription> optl__desc = boost::none;
optional<WalletDescription> optl__desc = boost::none;
};
bool new_wallet(
const string &mnemonic_language,
WalletDescriptionRetVals &retVals,
cryptonote::network_type nettype = cryptonote::MAINNET
network_type nettype = MAINNET
);
bool wallet_with(
const string &mnemonic_string,
const string &mnemonic_language,
WalletDescriptionRetVals &retVals,
cryptonote::network_type nettype = cryptonote::MAINNET
network_type nettype = MAINNET
);
//
struct WalletComponentsValidationResults: RetVals_base
@ -139,7 +143,7 @@ namespace monero_wallet_utils
const string &sec_viewKey_string,
optional<string> sec_spendKey_string,
optional<string> sec_seed_string,
cryptonote::network_type nettype,
network_type nettype,
WalletComponentsValidationResults &retVals
);
}

@ -255,6 +255,27 @@ string serial_bridge::newly_created_wallet(const string &args_string)
//
return ret_json_from_root(root);
}
string serial_bridge::are_equal_mnemonics(const string &args_string)
{
boost::property_tree::ptree json_root;
if (!parsed_json_root(args_string, json_root)) {
// it will already have thrown an exception
return error_ret_json_from_message("Invalid JSON");
}
bool equal;
try {
equal = monero_wallet_utils::are_equal_mnemonics(
json_root.get<string>("a"),
json_root.get<string>("b")
);
} catch (std::exception const& e) {
return error_ret_json_from_message(e.what());
}
boost::property_tree::ptree root;
root.put(ret_json_key__generic_retVal(), equal);
//
return ret_json_from_root(root);
}
string serial_bridge::mnemonic_from_seed(const string &args_string)
{
boost::property_tree::ptree json_root;
@ -268,7 +289,7 @@ string serial_bridge::mnemonic_from_seed(const string &args_string)
);
boost::property_tree::ptree root;
if (retVals.err_string != none) {
error_ret_json_from_message(*(retVals.err_string));
return error_ret_json_from_message(*(retVals.err_string));
}
root.put(ret_json_key__generic_retVal(), *(retVals.mnemonic_string));
//
@ -284,7 +305,6 @@ string serial_bridge::seed_and_keys_from_mnemonic(const string &args_string)
monero_wallet_utils::WalletDescriptionRetVals retVals;
bool r = monero_wallet_utils::wallet_with(
json_root.get<string>("mnemonic_string"),
json_root.get<string>("wordset_name"),
retVals
);
bool did_error = retVals.did_error;
@ -297,6 +317,7 @@ string serial_bridge::seed_and_keys_from_mnemonic(const string &args_string)
//
boost::property_tree::ptree root;
root.put(ret_json_key__sec_seed_string(), (*(retVals.optl__desc)).sec_seed_string);
root.put(ret_json_key__mnemonic_language(), (*(retVals.optl__desc)).mnemonic_language);
root.put(ret_json_key__address_string(), (*(retVals.optl__desc)).address_string);
root.put(ret_json_key__pub_viewKey_string(), epee::string_tools::pod_to_hex((*(retVals.optl__desc)).pub_viewKey));
root.put(ret_json_key__sec_viewKey_string(), epee::string_tools::pod_to_hex((*(retVals.optl__desc)).sec_viewKey));

@ -53,6 +53,7 @@ namespace serial_bridge
string new_payment_id(const string &args_string);
//
string newly_created_wallet(const string &args_string); // TODO: maybe expose random scalar as arg
string are_equal_mnemonics(const string &args_string);
string mnemonic_from_seed(const string &args_string);
string seed_and_keys_from_mnemonic(const string &args_string);
string validate_components_for_login(const string &args_string);
@ -80,6 +81,7 @@ namespace serial_bridge
static inline string ret_json_key__paymentID_string() { return "paymentID_string"; } // optional
static inline string ret_json_key__isSubaddress() { return "isSubaddress"; }
static inline string ret_json_key__mnemonic_string() { return "mnemonic_string"; }
static inline string ret_json_key__mnemonic_language() { return "mnemonic_language"; }
static inline string ret_json_key__sec_seed_string() { return "sec_seed_string"; }
static inline string ret_json_key__address_string() { return "address_string"; }
static inline string ret_json_key__pub_viewKey_string() { return "pub_viewKey_string"; }

@ -656,6 +656,29 @@ BOOST_AUTO_TEST_CASE(bridged__new_wallet)
cout << "bridged__new_wallet: sec_spendKey_string: " << *sec_spendKey_string << endl;
}
//
BOOST_AUTO_TEST_CASE(bridged__are_equal_mnemonics)
{
using namespace serial_bridge;
//
boost::property_tree::ptree root;
root.put("a", "foxe selfish hum nexus juven dodeg pepp ember biscuti elap jazz vibrate biscui");
root.put("b", "fox sel hum nex juv dod pep emb bis ela jaz vib bis");
//
auto ret_string = serial_bridge::are_equal_mnemonics(args_string_from_root(root));
stringstream ret_stream;
ret_stream << ret_string;
boost::property_tree::ptree ret_tree;
boost::property_tree::read_json(ret_stream, ret_tree);
optional<string> err_string = ret_tree.get_optional<string>(ret_json_key__any__err_msg());
if (err_string != none) {
BOOST_REQUIRE_MESSAGE(false, *err_string);
}
optional<bool> value = ret_tree.get_optional<bool>(ret_json_key__generic_retVal());
BOOST_REQUIRE(value != none);
BOOST_REQUIRE(*value != false);
cout << "bridged__are_equal_mnemonics: " << *value << endl;
}
//
BOOST_AUTO_TEST_CASE(bridged__mnemonic_from_seed)
{
using namespace serial_bridge;
@ -685,7 +708,6 @@ BOOST_AUTO_TEST_CASE(bridged__seed_and_keys_from_mnemonic)
//
boost::property_tree::ptree root;
root.put("mnemonic_string", "foxe selfish hum nexus juven dodeg pepp ember biscuti elap jazz vibrate biscui");
root.put("wordset_name", "English");
//
auto ret_string = serial_bridge::seed_and_keys_from_mnemonic(args_string_from_root(root));
stringstream ret_stream;
@ -696,6 +718,10 @@ BOOST_AUTO_TEST_CASE(bridged__seed_and_keys_from_mnemonic)
if (err_string != none) {
BOOST_REQUIRE_MESSAGE(false, *err_string);
}
optional<string> mnemonic_language = ret_tree.get_optional<string>(ret_json_key__mnemonic_language());
BOOST_REQUIRE(mnemonic_language != none);
BOOST_REQUIRE((*mnemonic_language).size() > 0);
cout << "bridged__seed_and_keys_from_mnemonic: mnemonic_language: " << *mnemonic_language << endl;
optional<string> sec_seed_string = ret_tree.get_optional<string>(ret_json_key__sec_seed_string());
BOOST_REQUIRE(sec_seed_string != none);
BOOST_REQUIRE((*sec_seed_string).size() > 0);

Loading…
Cancel
Save