mirror of
https://codeberg.org/anoncontributorxmr/monero.git
synced 2024-11-30 03:03:17 +00:00
Merge pull request #4131
ea37614
wallet: wipe seed from memory where appropriate (moneromooo-monero)e9ffa91
store secret keys encrypted where possible (moneromooo-monero)70271fa
common: add a class to safely wrap mlock/munlock (moneromooo-monero)ab74dc2
crypto: make secret_key automatically mlock (moneromooo-monero)
This commit is contained in:
commit
13a34faeb0
36 changed files with 1788 additions and 313 deletions
45
contrib/epee/include/fnv1.h
Normal file
45
contrib/epee/include/fnv1.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) 2018, 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace epee
|
||||
{
|
||||
|
||||
namespace fnv
|
||||
{
|
||||
inline uint64_t FNV1a(const char *ptr, size_t sz)
|
||||
{
|
||||
uint64_t h = 0xcbf29ce484222325;
|
||||
for (size_t i = 0; i < sz; ++i)
|
||||
h = (h ^ *(const uint8_t*)ptr++) * 0x100000001b3;
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -33,6 +33,7 @@
|
|||
#include <iosfwd>
|
||||
#include <string>
|
||||
|
||||
#include "wipeable_string.h"
|
||||
#include "span.h"
|
||||
|
||||
namespace epee
|
||||
|
@ -41,6 +42,8 @@ namespace epee
|
|||
{
|
||||
//! \return A std::string containing hex of `src`.
|
||||
static std::string string(const span<const std::uint8_t> src);
|
||||
//! \return A epee::wipeable_string containing hex of `src`.
|
||||
static epee::wipeable_string wipeable_string(const span<const std::uint8_t> src);
|
||||
|
||||
//! \return An array containing hex of `src`.
|
||||
template<std::size_t N>
|
||||
|
@ -59,6 +62,8 @@ namespace epee
|
|||
static void formatted(std::ostream& out, const span<const std::uint8_t> src);
|
||||
|
||||
private:
|
||||
template<typename T> T static convert(const span<const std::uint8_t> src);
|
||||
|
||||
//! Write `src` bytes as hex to `out`. `out` must be twice the length
|
||||
static void buffer_unchecked(char* out, const span<const std::uint8_t> src) noexcept;
|
||||
};
|
||||
|
|
87
contrib/epee/include/mlocker.h
Normal file
87
contrib/epee/include/mlocker.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
// Copyright (c) 2018, 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
namespace epee
|
||||
{
|
||||
class mlocker
|
||||
{
|
||||
public:
|
||||
mlocker(void *ptr, size_t len);
|
||||
~mlocker();
|
||||
|
||||
static size_t get_page_size();
|
||||
static size_t get_num_locked_pages();
|
||||
static size_t get_num_locked_objects();
|
||||
|
||||
static void lock(void *ptr, size_t len);
|
||||
static void unlock(void *ptr, size_t len);
|
||||
|
||||
private:
|
||||
static size_t page_size;
|
||||
static size_t num_locked_objects;
|
||||
|
||||
static boost::mutex &mutex();
|
||||
static std::map<size_t, unsigned int> &map();
|
||||
static void lock_page(size_t page);
|
||||
static void unlock_page(size_t page);
|
||||
|
||||
void *ptr;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
/// Locks memory while in scope
|
||||
///
|
||||
/// Primarily useful for making sure that private keys don't get swapped out
|
||||
// to disk
|
||||
template <class T>
|
||||
struct mlocked : public T {
|
||||
using type = T;
|
||||
|
||||
mlocked(): T() { mlocker::lock(this, sizeof(T)); }
|
||||
mlocked(const T &t): T(t) { mlocker::lock(this, sizeof(T)); }
|
||||
mlocked(const mlocked<T> &mt): T(mt) { mlocker::lock(this, sizeof(T)); }
|
||||
mlocked(const T &&t): T(t) { mlocker::lock(this, sizeof(T)); }
|
||||
mlocked(const mlocked<T> &&mt): T(mt) { mlocker::lock(this, sizeof(T)); }
|
||||
mlocked<T> &operator=(const mlocked<T> &mt) { T::operator=(mt); return *this; }
|
||||
~mlocked() { mlocker::unlock(this, sizeof(T)); }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
T& unwrap(mlocked<T>& src) { return src; }
|
||||
|
||||
template<typename T>
|
||||
const T& unwrap(mlocked<T> const& src) { return src; }
|
||||
|
||||
template <class T, size_t N>
|
||||
using mlocked_arr = mlocked<std::array<T, N>>;
|
||||
}
|
|
@ -85,6 +85,14 @@ public: \
|
|||
static_assert(std::is_pod<decltype(this_ref.varialble)>::value, "t_type must be a POD type."); \
|
||||
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name)
|
||||
|
||||
#define KV_SERIALIZE_VAL_POD_AS_BLOB_OPT_N(varialble, val_name, default_value) \
|
||||
do { \
|
||||
static_assert(std::is_pod<decltype(this_ref.varialble)>::value, "t_type must be a POD type."); \
|
||||
bool ret = KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name); \
|
||||
if (!ret) \
|
||||
epee::serialize_default(this_ref.varialble, default_value); \
|
||||
} while(0);
|
||||
|
||||
#define KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, val_name) \
|
||||
epee::serialization::selector<is_store>::serialize_stl_container_pod_val_as_blob(this_ref.varialble, stg, hparent_section, val_name);
|
||||
|
||||
|
@ -92,6 +100,7 @@ public: \
|
|||
|
||||
#define KV_SERIALIZE(varialble) KV_SERIALIZE_N(varialble, #varialble)
|
||||
#define KV_SERIALIZE_VAL_POD_AS_BLOB(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_N(varialble, #varialble)
|
||||
#define KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(varialble, def) KV_SERIALIZE_VAL_POD_AS_BLOB_OPT_N(varialble, #varialble, def)
|
||||
#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, #varialble) //skip is_pod compile time check
|
||||
#define KV_SERIALIZE_CONTAINER_POD_AS_BLOB(varialble) KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, #varialble)
|
||||
#define KV_SERIALIZE_OPT(variable,default_value) KV_SERIALIZE_OPT_N(variable, #variable, default_value)
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include "hex.h"
|
||||
#include "memwipe.h"
|
||||
#include "mlocker.h"
|
||||
#include "span.h"
|
||||
#include "warnings.h"
|
||||
|
||||
|
@ -358,6 +359,12 @@ POP_WARNINGS
|
|||
return hex_to_pod(hex_str, unwrap(s));
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
template<class t_pod_type>
|
||||
bool hex_to_pod(const std::string& hex_str, epee::mlocked<t_pod_type>& s)
|
||||
{
|
||||
return hex_to_pod(hex_str, unwrap(s));
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
bool validate_hex(uint64_t length, const std::string& str);
|
||||
//----------------------------------------------------------------------------
|
||||
inline std::string get_extension(const std::string& str)
|
||||
|
|
|
@ -28,28 +28,43 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <boost/optional/optional_fwd.hpp>
|
||||
#include <stddef.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "fnv1.h"
|
||||
|
||||
namespace epee
|
||||
{
|
||||
class wipeable_string
|
||||
{
|
||||
public:
|
||||
typedef char value_type;
|
||||
|
||||
wipeable_string() {}
|
||||
wipeable_string(const wipeable_string &other);
|
||||
wipeable_string(wipeable_string &&other);
|
||||
wipeable_string(const std::string &other);
|
||||
wipeable_string(std::string &&other);
|
||||
wipeable_string(const char *s);
|
||||
wipeable_string(const char *s, size_t len);
|
||||
~wipeable_string();
|
||||
void wipe();
|
||||
void push_back(char c);
|
||||
void pop_back();
|
||||
void operator+=(char c);
|
||||
void operator+=(const std::string &s);
|
||||
void operator+=(const epee::wipeable_string &s);
|
||||
void operator+=(const char *s);
|
||||
void append(const char *ptr, size_t len);
|
||||
char pop_back();
|
||||
const char *data() const noexcept { return buffer.data(); }
|
||||
char *data() noexcept { return buffer.data(); }
|
||||
size_t size() const noexcept { return buffer.size(); }
|
||||
size_t length() const noexcept { return buffer.size(); }
|
||||
bool empty() const noexcept { return buffer.empty(); }
|
||||
void trim();
|
||||
void split(std::vector<wipeable_string> &fields) const;
|
||||
boost::optional<wipeable_string> parse_hexstr() const;
|
||||
void resize(size_t sz);
|
||||
void reserve(size_t sz);
|
||||
void clear();
|
||||
|
@ -65,3 +80,14 @@ namespace epee
|
|||
std::vector<char> buffer;
|
||||
};
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<> struct hash<epee::wipeable_string>
|
||||
{
|
||||
size_t operator()(const epee::wipeable_string &s) const
|
||||
{
|
||||
return epee::fnv::FNV1a(s.data(), s.size());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp net_utils_base.cpp string_tools.cpp wipeable_string.cpp memwipe.c
|
||||
connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp)
|
||||
connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp mlocker.cpp)
|
||||
if (USE_READLINE AND GNU_READLINE_FOUND)
|
||||
add_library(epee_readline STATIC readline_buffer.cpp)
|
||||
endif()
|
||||
|
|
|
@ -52,17 +52,21 @@ namespace epee
|
|||
}
|
||||
}
|
||||
|
||||
std::string to_hex::string(const span<const std::uint8_t> src)
|
||||
template<typename T>
|
||||
T to_hex::convert(const span<const std::uint8_t> src)
|
||||
{
|
||||
if (std::numeric_limits<std::size_t>::max() / 2 < src.size())
|
||||
throw std::range_error("hex_view::to_string exceeded maximum size");
|
||||
|
||||
std::string out{};
|
||||
T out{};
|
||||
out.resize(src.size() * 2);
|
||||
buffer_unchecked(std::addressof(out[0]), src);
|
||||
to_hex::buffer_unchecked((char*)out.data(), src); // can't see the non const version in wipeable_string??
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string to_hex::string(const span<const std::uint8_t> src) { return convert<std::string>(src); }
|
||||
epee::wipeable_string to_hex::wipeable_string(const span<const std::uint8_t> src) { return convert<epee::wipeable_string>(src); }
|
||||
|
||||
void to_hex::buffer(std::ostream& out, const span<const std::uint8_t> src)
|
||||
{
|
||||
write_hex(std::ostreambuf_iterator<char>{out}, src);
|
||||
|
|
182
contrib/epee/src/mlocker.cpp
Normal file
182
contrib/epee/src/mlocker.cpp
Normal file
|
@ -0,0 +1,182 @@
|
|||
// Copyright (c) 2018, 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.
|
||||
|
||||
#if defined __GNUC__ && !defined _WIN32
|
||||
#define HAVE_MLOCK 1
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#if defined HAVE_MLOCK
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
#include "misc_log_ex.h"
|
||||
#include "syncobj.h"
|
||||
#include "mlocker.h"
|
||||
|
||||
static size_t query_page_size()
|
||||
{
|
||||
#if defined HAVE_MLOCK
|
||||
long ret = sysconf(_SC_PAGESIZE);
|
||||
if (ret <= 0)
|
||||
{
|
||||
MERROR("Failed to determine page size");
|
||||
return 0;
|
||||
}
|
||||
MINFO("Page size: " << ret);
|
||||
return ret;
|
||||
#else
|
||||
#warning Missing query_page_size implementation
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void do_lock(void *ptr, size_t len)
|
||||
{
|
||||
#if defined HAVE_MLOCK
|
||||
int ret = mlock(ptr, len);
|
||||
if (ret < 0)
|
||||
MERROR("Error locking page at " << ptr << ": " << strerror(errno));
|
||||
#else
|
||||
#warning Missing do_lock implementation
|
||||
#endif
|
||||
}
|
||||
|
||||
static void do_unlock(void *ptr, size_t len)
|
||||
{
|
||||
#if defined HAVE_MLOCK
|
||||
int ret = munlock(ptr, len);
|
||||
if (ret < 0)
|
||||
MERROR("Error unlocking page at " << ptr << ": " << strerror(errno));
|
||||
#else
|
||||
#warning Missing implementation of page size detection
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace epee
|
||||
{
|
||||
size_t mlocker::page_size = 0;
|
||||
size_t mlocker::num_locked_objects = 0;
|
||||
|
||||
boost::mutex &mlocker::mutex()
|
||||
{
|
||||
static boost::mutex vmutex;
|
||||
return vmutex;
|
||||
}
|
||||
std::map<size_t, unsigned int> &mlocker::map()
|
||||
{
|
||||
static std::map<size_t, unsigned int> vmap;
|
||||
return vmap;
|
||||
}
|
||||
|
||||
size_t mlocker::get_page_size()
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(mutex());
|
||||
if (page_size == 0)
|
||||
page_size = query_page_size();
|
||||
return page_size;
|
||||
}
|
||||
|
||||
mlocker::mlocker(void *ptr, size_t len): ptr(ptr), len(len)
|
||||
{
|
||||
lock(ptr, len);
|
||||
}
|
||||
|
||||
mlocker::~mlocker()
|
||||
{
|
||||
unlock(ptr, len);
|
||||
}
|
||||
|
||||
void mlocker::lock(void *ptr, size_t len)
|
||||
{
|
||||
size_t page_size = get_page_size();
|
||||
if (page_size == 0)
|
||||
return;
|
||||
|
||||
CRITICAL_REGION_LOCAL(mutex());
|
||||
const size_t first = ((uintptr_t)ptr) / page_size;
|
||||
const size_t last = (((uintptr_t)ptr) + len - 1) / page_size;
|
||||
for (size_t page = first; page <= last; ++page)
|
||||
lock_page(page);
|
||||
++num_locked_objects;
|
||||
}
|
||||
|
||||
void mlocker::unlock(void *ptr, size_t len)
|
||||
{
|
||||
size_t page_size = get_page_size();
|
||||
if (page_size == 0)
|
||||
return;
|
||||
CRITICAL_REGION_LOCAL(mutex());
|
||||
const size_t first = ((uintptr_t)ptr) / page_size;
|
||||
const size_t last = (((uintptr_t)ptr) + len - 1) / page_size;
|
||||
for (size_t page = first; page <= last; ++page)
|
||||
unlock_page(page);
|
||||
--num_locked_objects;
|
||||
}
|
||||
|
||||
size_t mlocker::get_num_locked_pages()
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(mutex());
|
||||
return map().size();
|
||||
}
|
||||
|
||||
size_t mlocker::get_num_locked_objects()
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(mutex());
|
||||
return num_locked_objects;
|
||||
}
|
||||
|
||||
void mlocker::lock_page(size_t page)
|
||||
{
|
||||
std::pair<std::map<size_t, unsigned int>::iterator, bool> p = map().insert(std::make_pair(page, 1));
|
||||
if (p.second)
|
||||
{
|
||||
do_lock((void*)(page * page_size), page_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
++p.first->second;
|
||||
}
|
||||
}
|
||||
|
||||
void mlocker::unlock_page(size_t page)
|
||||
{
|
||||
std::map<size_t, unsigned int>::iterator i = map().find(page);
|
||||
if (i == map().end())
|
||||
{
|
||||
MERROR("Attempt to unlock unlocked page at " << (void*)(page * page_size));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!--i->second)
|
||||
{
|
||||
map().erase(i);
|
||||
do_unlock((void*)(page * page_size), page_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,11 +26,22 @@
|
|||
// 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 <boost/optional/optional.hpp>
|
||||
#include <string.h>
|
||||
#include "memwipe.h"
|
||||
#include "misc_log_ex.h"
|
||||
#include "wipeable_string.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
int atolower(int c)
|
||||
{
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
c |= 32;
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
namespace epee
|
||||
{
|
||||
|
||||
|
@ -69,6 +80,12 @@ wipeable_string::wipeable_string(const char *s)
|
|||
memcpy(buffer.data(), s, size());
|
||||
}
|
||||
|
||||
wipeable_string::wipeable_string(const char *s, size_t len)
|
||||
{
|
||||
grow(len);
|
||||
memcpy(buffer.data(), s, len);
|
||||
}
|
||||
|
||||
wipeable_string::~wipeable_string()
|
||||
{
|
||||
wipe();
|
||||
|
@ -109,9 +126,100 @@ void wipeable_string::push_back(char c)
|
|||
buffer.back() = c;
|
||||
}
|
||||
|
||||
void wipeable_string::pop_back()
|
||||
void wipeable_string::operator+=(char c)
|
||||
{
|
||||
resize(size() - 1);
|
||||
push_back(c);
|
||||
}
|
||||
|
||||
void wipeable_string::append(const char *ptr, size_t len)
|
||||
{
|
||||
const size_t orgsz = size();
|
||||
CHECK_AND_ASSERT_THROW_MES(orgsz < std::numeric_limits<size_t>::max() - len, "Appended data too large");
|
||||
grow(orgsz + len);
|
||||
if (len > 0)
|
||||
memcpy(data() + orgsz, ptr, len);
|
||||
}
|
||||
|
||||
void wipeable_string::operator+=(const char *s)
|
||||
{
|
||||
append(s, strlen(s));
|
||||
}
|
||||
|
||||
void wipeable_string::operator+=(const epee::wipeable_string &s)
|
||||
{
|
||||
append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void wipeable_string::operator+=(const std::string &s)
|
||||
{
|
||||
append(s.c_str(), s.size());
|
||||
}
|
||||
|
||||
void wipeable_string::trim()
|
||||
{
|
||||
size_t prefix = 0;
|
||||
while (prefix < size() && data()[prefix] == ' ')
|
||||
++prefix;
|
||||
if (prefix > 0)
|
||||
memmove(buffer.data(), buffer.data() + prefix, size() - prefix);
|
||||
|
||||
size_t suffix = 0;
|
||||
while (suffix < size()-prefix && data()[size() - 1 - prefix - suffix] == ' ')
|
||||
++suffix;
|
||||
|
||||
resize(size() - prefix - suffix);
|
||||
}
|
||||
|
||||
void wipeable_string::split(std::vector<wipeable_string> &fields) const
|
||||
{
|
||||
fields.clear();
|
||||
size_t len = size();
|
||||
const char *ptr = data();
|
||||
bool space = true;
|
||||
while (len--)
|
||||
{
|
||||
const char c = *ptr++;
|
||||
if (c != ' ')
|
||||
{
|
||||
if (space)
|
||||
fields.push_back({});
|
||||
fields.back().push_back(c);
|
||||
}
|
||||
space = c == ' ';
|
||||
}
|
||||
}
|
||||
|
||||
boost::optional<epee::wipeable_string> wipeable_string::parse_hexstr() const
|
||||
{
|
||||
if (size() % 2 != 0)
|
||||
return boost::none;
|
||||
boost::optional<epee::wipeable_string> res = epee::wipeable_string("");
|
||||
const size_t len = size();
|
||||
const char *d = data();
|
||||
res->grow(0, len / 2);
|
||||
static constexpr const char hex[] = u8"0123456789abcdef";
|
||||
for (size_t i = 0; i < len; i += 2)
|
||||
{
|
||||
char c = atolower(d[i]);
|
||||
const char *ptr0 = strchr(hex, c);
|
||||
if (!ptr0)
|
||||
return boost::none;
|
||||
c = atolower(d[i+1]);
|
||||
const char *ptr1 = strchr(hex, c);
|
||||
if (!ptr1)
|
||||
return boost::none;
|
||||
res->push_back(((ptr0-hex)<<4) | (ptr1-hex));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
char wipeable_string::pop_back()
|
||||
{
|
||||
const size_t sz = size();
|
||||
CHECK_AND_ASSERT_THROW_MES(sz > 0, "Popping from an empty string");
|
||||
const char c = buffer.back();
|
||||
resize(sz - 1);
|
||||
return c;
|
||||
}
|
||||
|
||||
void wipeable_string::resize(size_t sz)
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace
|
|||
return 0 != _isatty(_fileno(stdin));
|
||||
}
|
||||
|
||||
bool read_from_tty(epee::wipeable_string& pass)
|
||||
bool read_from_tty(epee::wipeable_string& pass, bool hide_input)
|
||||
{
|
||||
static constexpr const char BACKSPACE = 8;
|
||||
|
||||
|
@ -62,7 +62,7 @@ namespace
|
|||
|
||||
DWORD mode_old;
|
||||
::GetConsoleMode(h_cin, &mode_old);
|
||||
DWORD mode_new = mode_old & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT);
|
||||
DWORD mode_new = mode_old & ~((hide_input ? ENABLE_ECHO_INPUT : 0) | ENABLE_LINE_INPUT);
|
||||
::SetConsoleMode(h_cin, mode_new);
|
||||
|
||||
bool r = true;
|
||||
|
@ -107,14 +107,14 @@ namespace
|
|||
return 0 != isatty(fileno(stdin));
|
||||
}
|
||||
|
||||
int getch() noexcept
|
||||
int getch(bool hide_input) noexcept
|
||||
{
|
||||
struct termios tty_old;
|
||||
tcgetattr(STDIN_FILENO, &tty_old);
|
||||
|
||||
struct termios tty_new;
|
||||
tty_new = tty_old;
|
||||
tty_new.c_lflag &= ~(ICANON | ECHO);
|
||||
tty_new.c_lflag &= ~(ICANON | (hide_input ? ECHO : 0));
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &tty_new);
|
||||
|
||||
int ch = getchar();
|
||||
|
@ -124,14 +124,14 @@ namespace
|
|||
return ch;
|
||||
}
|
||||
|
||||
bool read_from_tty(epee::wipeable_string& aPass)
|
||||
bool read_from_tty(epee::wipeable_string& aPass, bool hide_input)
|
||||
{
|
||||
static constexpr const char BACKSPACE = 127;
|
||||
|
||||
aPass.reserve(tools::password_container::max_password_size);
|
||||
while (aPass.size() < tools::password_container::max_password_size)
|
||||
{
|
||||
int ch = getch();
|
||||
int ch = getch(hide_input);
|
||||
if (EOF == ch || ch == EOT)
|
||||
{
|
||||
return false;
|
||||
|
@ -159,18 +159,18 @@ namespace
|
|||
|
||||
#endif // end !WIN32
|
||||
|
||||
bool read_from_tty(const bool verify, const char *message, epee::wipeable_string& pass1, epee::wipeable_string& pass2)
|
||||
bool read_from_tty(const bool verify, const char *message, bool hide_input, epee::wipeable_string& pass1, epee::wipeable_string& pass2)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (message)
|
||||
std::cout << message <<": " << std::flush;
|
||||
if (!read_from_tty(pass1))
|
||||
if (!read_from_tty(pass1, hide_input))
|
||||
return false;
|
||||
if (verify)
|
||||
{
|
||||
std::cout << "Confirm password: ";
|
||||
if (!read_from_tty(pass2))
|
||||
if (!read_from_tty(pass2, hide_input))
|
||||
return false;
|
||||
if(pass1!=pass2)
|
||||
{
|
||||
|
@ -229,12 +229,12 @@ namespace tools
|
|||
|
||||
std::atomic<bool> password_container::is_prompting(false);
|
||||
|
||||
boost::optional<password_container> password_container::prompt(const bool verify, const char *message)
|
||||
boost::optional<password_container> password_container::prompt(const bool verify, const char *message, bool hide_input)
|
||||
{
|
||||
is_prompting = true;
|
||||
password_container pass1{};
|
||||
password_container pass2{};
|
||||
if (is_cin_tty() ? read_from_tty(verify, message, pass1.m_password, pass2.m_password) : read_from_file(pass1.m_password))
|
||||
if (is_cin_tty() ? read_from_tty(verify, message, hide_input, pass1.m_password, pass2.m_password) : read_from_file(pass1.m_password))
|
||||
{
|
||||
is_prompting = false;
|
||||
return {std::move(pass1)};
|
||||
|
|
|
@ -49,7 +49,7 @@ namespace tools
|
|||
password_container(std::string&& password) noexcept;
|
||||
|
||||
//! \return A password from stdin TTY prompt or `std::cin` pipe.
|
||||
static boost::optional<password_container> prompt(bool verify, const char *mesage = "Password");
|
||||
static boost::optional<password_container> prompt(bool verify, const char *mesage = "Password", bool hide_input = true);
|
||||
static std::atomic<bool> is_prompting;
|
||||
|
||||
password_container(const password_container&) = delete;
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include <memory.h>
|
||||
|
||||
#include "memwipe.h"
|
||||
#include "mlocker.h"
|
||||
#include "hash.h"
|
||||
|
||||
namespace crypto {
|
||||
|
@ -50,7 +51,7 @@ namespace crypto {
|
|||
#if defined(__cplusplus)
|
||||
}
|
||||
|
||||
using chacha_key = tools::scrubbed_arr<uint8_t, CHACHA_KEY_SIZE>;
|
||||
using chacha_key = epee::mlocked<tools::scrubbed_arr<uint8_t, CHACHA_KEY_SIZE>>;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
// MS VC 2012 doesn't interpret `class chacha_iv` as POD in spite of [9.0.10], so it is a struct
|
||||
|
@ -71,20 +72,20 @@ namespace crypto {
|
|||
|
||||
inline void generate_chacha_key(const void *data, size_t size, chacha_key& key, uint64_t kdf_rounds) {
|
||||
static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key");
|
||||
tools::scrubbed_arr<char, HASH_SIZE> pwd_hash;
|
||||
epee::mlocked<tools::scrubbed_arr<char, HASH_SIZE>> pwd_hash;
|
||||
crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 0/*prehashed*/);
|
||||
for (uint64_t n = 1; n < kdf_rounds; ++n)
|
||||
crypto::cn_slow_hash(pwd_hash.data(), pwd_hash.size(), pwd_hash.data(), 0/*variant*/, 0/*prehashed*/);
|
||||
memcpy(&unwrap(key), pwd_hash.data(), sizeof(key));
|
||||
memcpy(&unwrap(unwrap(key)), pwd_hash.data(), sizeof(key));
|
||||
}
|
||||
|
||||
inline void generate_chacha_key_prehashed(const void *data, size_t size, chacha_key& key, uint64_t kdf_rounds) {
|
||||
static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key");
|
||||
tools::scrubbed_arr<char, HASH_SIZE> pwd_hash;
|
||||
epee::mlocked<tools::scrubbed_arr<char, HASH_SIZE>> pwd_hash;
|
||||
crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 1/*prehashed*/);
|
||||
for (uint64_t n = 1; n < kdf_rounds; ++n)
|
||||
crypto::cn_slow_hash(pwd_hash.data(), pwd_hash.size(), pwd_hash.data(), 0/*variant*/, 0/*prehashed*/);
|
||||
memcpy(&unwrap(key), pwd_hash.data(), sizeof(key));
|
||||
memcpy(&unwrap(unwrap(key)), pwd_hash.data(), sizeof(key));
|
||||
}
|
||||
|
||||
inline void generate_chacha_key(std::string password, chacha_key& key, uint64_t kdf_rounds) {
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "common/pod-class.h"
|
||||
#include "common/util.h"
|
||||
#include "memwipe.h"
|
||||
#include "mlocker.h"
|
||||
#include "generic-ops.h"
|
||||
#include "hex.h"
|
||||
#include "span.h"
|
||||
|
@ -65,7 +66,7 @@ namespace crypto {
|
|||
friend class crypto_ops;
|
||||
};
|
||||
|
||||
using secret_key = tools::scrubbed<ec_scalar>;
|
||||
using secret_key = epee::mlocked<tools::scrubbed<ec_scalar>>;
|
||||
|
||||
POD_CLASS public_keyV {
|
||||
std::vector<public_key> keys;
|
||||
|
|
|
@ -44,6 +44,9 @@ extern "C"
|
|||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "account"
|
||||
|
||||
#define KEYS_ENCRYPTION_SALT 'k'
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
DISABLE_VS_WARNINGS(4244 4345)
|
||||
|
@ -60,7 +63,70 @@ DISABLE_VS_WARNINGS(4244 4345)
|
|||
m_device = &hwdev;
|
||||
MCDEBUG("device", "account_keys::set_device device type: "<<typeid(hwdev).name());
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
static void derive_key(const crypto::chacha_key &base_key, crypto::chacha_key &key)
|
||||
{
|
||||
static_assert(sizeof(base_key) == sizeof(crypto::hash), "chacha key and hash should be the same size");
|
||||
epee::mlocked<tools::scrubbed_arr<char, sizeof(base_key)+1>> data;
|
||||
memcpy(data.data(), &base_key, sizeof(base_key));
|
||||
data[sizeof(base_key)] = KEYS_ENCRYPTION_SALT;
|
||||
crypto::generate_chacha_key(data.data(), sizeof(data), key, 1);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
static epee::wipeable_string get_key_stream(const crypto::chacha_key &base_key, const crypto::chacha_iv &iv, size_t bytes)
|
||||
{
|
||||
// derive a new key
|
||||
crypto::chacha_key key;
|
||||
derive_key(base_key, key);
|
||||
|
||||
// chacha
|
||||
epee::wipeable_string buffer0(std::string(bytes, '\0'));
|
||||
epee::wipeable_string buffer1 = buffer0;
|
||||
crypto::chacha20(buffer0.data(), buffer0.size(), key, iv, buffer1.data());
|
||||
return buffer1;
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
void account_keys::xor_with_key_stream(const crypto::chacha_key &key)
|
||||
{
|
||||
// encrypt a large enough byte stream with chacha20
|
||||
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (2 + m_multisig_keys.size()));
|
||||
const char *ptr = key_stream.data();
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
m_spend_secret_key.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
m_view_secret_key.data[i] ^= *ptr++;
|
||||
for (crypto::secret_key &k: m_multisig_keys)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
k.data[i] ^= *ptr++;
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
void account_keys::encrypt(const crypto::chacha_key &key)
|
||||
{
|
||||
m_encryption_iv = crypto::rand<crypto::chacha_iv>();
|
||||
xor_with_key_stream(key);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
void account_keys::decrypt(const crypto::chacha_key &key)
|
||||
{
|
||||
xor_with_key_stream(key);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
void account_keys::encrypt_viewkey(const crypto::chacha_key &key)
|
||||
{
|
||||
// encrypt a large enough byte stream with chacha20
|
||||
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * 2);
|
||||
const char *ptr = key_stream.data();
|
||||
ptr += sizeof(crypto::secret_key);
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
m_view_secret_key.data[i] ^= *ptr++;
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
void account_keys::decrypt_viewkey(const crypto::chacha_key &key)
|
||||
{
|
||||
encrypt_viewkey(key);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
account_base::account_base()
|
||||
{
|
||||
|
@ -157,7 +223,7 @@ DISABLE_VS_WARNINGS(4244 4345)
|
|||
void account_base::create_from_viewkey(const cryptonote::account_public_address& address, const crypto::secret_key& viewkey)
|
||||
{
|
||||
crypto::secret_key fake;
|
||||
memset(&unwrap(fake), 0, sizeof(fake));
|
||||
memset(&unwrap(unwrap(fake)), 0, sizeof(fake));
|
||||
create_from_keys(address, fake, viewkey);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
|
|
|
@ -44,18 +44,29 @@ namespace cryptonote
|
|||
crypto::secret_key m_view_secret_key;
|
||||
std::vector<crypto::secret_key> m_multisig_keys;
|
||||
hw::device *m_device = &hw::get_device("default");
|
||||
crypto::chacha_iv m_encryption_iv;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(m_account_address)
|
||||
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_spend_secret_key)
|
||||
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_view_secret_key)
|
||||
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_multisig_keys)
|
||||
const crypto::chacha_iv default_iv{{0, 0, 0, 0, 0, 0, 0, 0}};
|
||||
KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(m_encryption_iv, default_iv)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
|
||||
account_keys& operator=(account_keys const&) = default;
|
||||
|
||||
void encrypt(const crypto::chacha_key &key);
|
||||
void decrypt(const crypto::chacha_key &key);
|
||||
void encrypt_viewkey(const crypto::chacha_key &key);
|
||||
void decrypt_viewkey(const crypto::chacha_key &key);
|
||||
|
||||
hw::device& get_device() const ;
|
||||
void set_device( hw::device &hwdev) ;
|
||||
|
||||
private:
|
||||
void xor_with_key_stream(const crypto::chacha_key &key);
|
||||
};
|
||||
|
||||
/************************************************************************/
|
||||
|
@ -87,6 +98,11 @@ namespace cryptonote
|
|||
void forget_spend_key();
|
||||
const std::vector<crypto::secret_key> &get_multisig_keys() const { return m_keys.m_multisig_keys; }
|
||||
|
||||
void encrypt_keys(const crypto::chacha_key &key) { m_keys.encrypt(key); }
|
||||
void decrypt_keys(const crypto::chacha_key &key) { m_keys.decrypt(key); }
|
||||
void encrypt_viewkey(const crypto::chacha_key &key) { m_keys.encrypt_viewkey(key); }
|
||||
void decrypt_viewkey(const crypto::chacha_key &key) { m_keys.decrypt_viewkey(key); }
|
||||
|
||||
template <class t_archive>
|
||||
inline void serialize(t_archive &a, const unsigned int /*ver*/)
|
||||
{
|
||||
|
|
|
@ -103,7 +103,7 @@ namespace hw {
|
|||
bool device_default::generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds) {
|
||||
const crypto::secret_key &view_key = keys.m_view_secret_key;
|
||||
const crypto::secret_key &spend_key = keys.m_spend_secret_key;
|
||||
tools::scrubbed_arr<char, sizeof(view_key) + sizeof(spend_key) + 1> data;
|
||||
epee::mlocked<tools::scrubbed_arr<char, sizeof(view_key) + sizeof(spend_key) + 1>> data;
|
||||
memcpy(data.data(), &view_key, sizeof(view_key));
|
||||
memcpy(data.data() + sizeof(view_key), &spend_key, sizeof(spend_key));
|
||||
data[sizeof(data) - 1] = CHACHA8_KEY_TAIL;
|
||||
|
|
|
@ -92,7 +92,7 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str
|
|||
{
|
||||
std::string name = basename + "-" + std::to_string(n + 1);
|
||||
wallets[n].reset(new tools::wallet2(nettype));
|
||||
wallets[n]->init("");
|
||||
wallets[n]->init(false, "");
|
||||
wallets[n]->generate(name, pwd_container->password(), rct::rct2sk(rct::skGen()), false, false, create_address_file);
|
||||
}
|
||||
|
||||
|
@ -101,11 +101,13 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str
|
|||
std::vector<crypto::public_key> pk(total);
|
||||
for (size_t n = 0; n < total; ++n)
|
||||
{
|
||||
wallets[n]->decrypt_keys(pwd_container->password());
|
||||
if (!tools::wallet2::verify_multisig_info(wallets[n]->get_multisig_info(), sk[n], pk[n]))
|
||||
{
|
||||
tools::fail_msg_writer() << tr("Failed to verify multisig info");
|
||||
return false;
|
||||
}
|
||||
wallets[n]->encrypt_keys(pwd_container->password());
|
||||
}
|
||||
|
||||
// make the wallets multisig
|
||||
|
|
|
@ -43,6 +43,8 @@
|
|||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include "wipeable_string.h"
|
||||
#include "misc_language.h"
|
||||
#include "crypto/crypto.h" // for declaration of crypto::secret_key
|
||||
#include <fstream>
|
||||
#include "mnemonics/electrum-words.h"
|
||||
|
@ -80,9 +82,9 @@ namespace crypto
|
|||
|
||||
namespace
|
||||
{
|
||||
uint32_t create_checksum_index(const std::vector<std::string> &word_list,
|
||||
uint32_t create_checksum_index(const std::vector<epee::wipeable_string> &word_list,
|
||||
uint32_t unique_prefix_length);
|
||||
bool checksum_test(std::vector<std::string> seed, uint32_t unique_prefix_length);
|
||||
bool checksum_test(std::vector<epee::wipeable_string> seed, uint32_t unique_prefix_length);
|
||||
|
||||
/*!
|
||||
* \brief Finds the word list that contains the seed words and puts the indices
|
||||
|
@ -93,7 +95,7 @@ namespace
|
|||
* \param language Language instance pointer to write to after it is found.
|
||||
* \return true if all the words were present in some language false if not.
|
||||
*/
|
||||
bool find_seed_language(const std::vector<std::string> &seed,
|
||||
bool find_seed_language(const std::vector<epee::wipeable_string> &seed,
|
||||
bool has_checksum, std::vector<uint32_t> &matched_indices, Language::Base **language)
|
||||
{
|
||||
// If there's a new language added, add an instance of it here.
|
||||
|
@ -114,17 +116,19 @@ namespace
|
|||
});
|
||||
Language::Base *fallback = NULL;
|
||||
|
||||
std::vector<epee::wipeable_string>::const_iterator it2;
|
||||
matched_indices.reserve(seed.size());
|
||||
|
||||
// Iterate through all the languages and find a match
|
||||
for (std::vector<Language::Base*>::iterator it1 = language_instances.begin();
|
||||
it1 != language_instances.end(); it1++)
|
||||
{
|
||||
const std::unordered_map<std::string, uint32_t> &word_map = (*it1)->get_word_map();
|
||||
const std::unordered_map<std::string, uint32_t> &trimmed_word_map = (*it1)->get_trimmed_word_map();
|
||||
const std::unordered_map<epee::wipeable_string, uint32_t> &word_map = (*it1)->get_word_map();
|
||||
const std::unordered_map<epee::wipeable_string, uint32_t> &trimmed_word_map = (*it1)->get_trimmed_word_map();
|
||||
// To iterate through seed words
|
||||
std::vector<std::string>::const_iterator it2;
|
||||
bool full_match = true;
|
||||
|
||||
std::string trimmed_word;
|
||||
epee::wipeable_string trimmed_word;
|
||||
// Iterate through all the words and see if they're all present
|
||||
for (it2 = seed.begin(); it2 != seed.end(); it2++)
|
||||
{
|
||||
|
@ -167,6 +171,7 @@ namespace
|
|||
return true;
|
||||
}
|
||||
// Some didn't match. Clear the index array.
|
||||
memwipe(matched_indices.data(), matched_indices.size() * sizeof(matched_indices[0]));
|
||||
matched_indices.clear();
|
||||
}
|
||||
|
||||
|
@ -181,6 +186,7 @@ namespace
|
|||
}
|
||||
|
||||
MINFO("No match found");
|
||||
memwipe(matched_indices.data(), matched_indices.size() * sizeof(matched_indices[0]));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -190,12 +196,12 @@ namespace
|
|||
* \param unique_prefix_length the prefix length of each word to use for checksum
|
||||
* \return Checksum index
|
||||
*/
|
||||
uint32_t create_checksum_index(const std::vector<std::string> &word_list,
|
||||
uint32_t create_checksum_index(const std::vector<epee::wipeable_string> &word_list,
|
||||
uint32_t unique_prefix_length)
|
||||
{
|
||||
std::string trimmed_words = "";
|
||||
epee::wipeable_string trimmed_words = "";
|
||||
|
||||
for (std::vector<std::string>::const_iterator it = word_list.begin(); it != word_list.end(); it++)
|
||||
for (std::vector<epee::wipeable_string>::const_iterator it = word_list.begin(); it != word_list.end(); it++)
|
||||
{
|
||||
if (it->length() > unique_prefix_length)
|
||||
{
|
||||
|
@ -217,22 +223,22 @@ namespace
|
|||
* \param unique_prefix_length the prefix length of each word to use for checksum
|
||||
* \return True if the test passed false if not.
|
||||
*/
|
||||
bool checksum_test(std::vector<std::string> seed, uint32_t unique_prefix_length)
|
||||
bool checksum_test(std::vector<epee::wipeable_string> seed, uint32_t unique_prefix_length)
|
||||
{
|
||||
if (seed.empty())
|
||||
return false;
|
||||
// The last word is the checksum.
|
||||
std::string last_word = seed.back();
|
||||
epee::wipeable_string last_word = seed.back();
|
||||
seed.pop_back();
|
||||
|
||||
std::string checksum = seed[create_checksum_index(seed, unique_prefix_length)];
|
||||
epee::wipeable_string checksum = seed[create_checksum_index(seed, unique_prefix_length)];
|
||||
|
||||
std::string trimmed_checksum = checksum.length() > unique_prefix_length ? Language::utf8prefix(checksum, unique_prefix_length) :
|
||||
epee::wipeable_string trimmed_checksum = checksum.length() > unique_prefix_length ? Language::utf8prefix(checksum, unique_prefix_length) :
|
||||
checksum;
|
||||
std::string trimmed_last_word = last_word.length() > unique_prefix_length ? Language::utf8prefix(last_word, unique_prefix_length) :
|
||||
epee::wipeable_string trimmed_last_word = last_word.length() > unique_prefix_length ? Language::utf8prefix(last_word, unique_prefix_length) :
|
||||
last_word;
|
||||
bool ret = trimmed_checksum == trimmed_last_word;
|
||||
MINFO("Checksum is %s" << (ret ? "valid" : "invalid"));
|
||||
MINFO("Checksum is " << (ret ? "valid" : "invalid"));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -260,13 +266,12 @@ namespace crypto
|
|||
* \param language_name Language of the seed as found gets written here.
|
||||
* \return false if not a multiple of 3 words, or if word is not in the words list
|
||||
*/
|
||||
bool words_to_bytes(std::string words, std::string& dst, size_t len, bool duplicate,
|
||||
bool words_to_bytes(const epee::wipeable_string &words, epee::wipeable_string& dst, size_t len, bool duplicate,
|
||||
std::string &language_name)
|
||||
{
|
||||
std::vector<std::string> seed;
|
||||
std::vector<epee::wipeable_string> seed;
|
||||
|
||||
boost::algorithm::trim(words);
|
||||
boost::split(seed, words, boost::is_any_of(" "), boost::token_compress_on);
|
||||
words.split(seed);
|
||||
|
||||
if (len % 4)
|
||||
{
|
||||
|
@ -291,6 +296,7 @@ namespace crypto
|
|||
}
|
||||
|
||||
std::vector<uint32_t> matched_indices;
|
||||
auto wiper = epee::misc_utils::create_scope_leave_handler([&](){memwipe(matched_indices.data(), matched_indices.size() * sizeof(matched_indices[0]));});
|
||||
Language::Base *language;
|
||||
if (!find_seed_language(seed, has_checksum, matched_indices, &language))
|
||||
{
|
||||
|
@ -313,33 +319,33 @@ namespace crypto
|
|||
|
||||
for (unsigned int i=0; i < seed.size() / 3; i++)
|
||||
{
|
||||
uint32_t val;
|
||||
uint32_t w1, w2, w3;
|
||||
w1 = matched_indices[i*3];
|
||||
w2 = matched_indices[i*3 + 1];
|
||||
w3 = matched_indices[i*3 + 2];
|
||||
uint32_t w[4];
|
||||
w[1] = matched_indices[i*3];
|
||||
w[2] = matched_indices[i*3 + 1];
|
||||
w[3] = matched_indices[i*3 + 2];
|
||||
|
||||
val = w1 + word_list_length * (((word_list_length - w1) + w2) % word_list_length) +
|
||||
word_list_length * word_list_length * (((word_list_length - w2) + w3) % word_list_length);
|
||||
w[0]= w[1] + word_list_length * (((word_list_length - w[1]) + w[2]) % word_list_length) +
|
||||
word_list_length * word_list_length * (((word_list_length - w[2]) + w[3]) % word_list_length);
|
||||
|
||||
if (!(val % word_list_length == w1))
|
||||
if (!(w[0]% word_list_length == w[1]))
|
||||
{
|
||||
memwipe(w, sizeof(w));
|
||||
MERROR("Invalid seed: mumble mumble");
|
||||
return false;
|
||||
}
|
||||
|
||||
dst.append((const char*)&val, 4); // copy 4 bytes to position
|
||||
dst.append((const char*)&w[0], 4); // copy 4 bytes to position
|
||||
memwipe(w, sizeof(w));
|
||||
}
|
||||
|
||||
if (len > 0 && duplicate)
|
||||
{
|
||||
const size_t expected = len * 3 / 32;
|
||||
std::string wlist_copy = words;
|
||||
if (seed.size() == expected/2)
|
||||
{
|
||||
dst.append(dst); // if electrum 12-word seed, duplicate
|
||||
wlist_copy += ' ';
|
||||
wlist_copy += words;
|
||||
dst += ' '; // if electrum 12-word seed, duplicate
|
||||
dst += dst; // if electrum 12-word seed, duplicate
|
||||
dst.pop_back(); // trailing space
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -353,10 +359,10 @@ namespace crypto
|
|||
* \param language_name Language of the seed as found gets written here.
|
||||
* \return false if not a multiple of 3 words, or if word is not in the words list
|
||||
*/
|
||||
bool words_to_bytes(std::string words, crypto::secret_key& dst,
|
||||
bool words_to_bytes(const epee::wipeable_string &words, crypto::secret_key& dst,
|
||||
std::string &language_name)
|
||||
{
|
||||
std::string s;
|
||||
epee::wipeable_string s;
|
||||
if (!words_to_bytes(words, s, sizeof(dst), true, language_name))
|
||||
{
|
||||
MERROR("Invalid seed: failed to convert words to bytes");
|
||||
|
@ -378,7 +384,7 @@ namespace crypto
|
|||
* \param language_name Seed language name
|
||||
* \return true if successful false if not. Unsuccessful if wrong key size.
|
||||
*/
|
||||
bool bytes_to_words(const char *src, size_t len, std::string& words,
|
||||
bool bytes_to_words(const char *src, size_t len, epee::wipeable_string& words,
|
||||
const std::string &language_name)
|
||||
{
|
||||
|
||||
|
@ -397,39 +403,38 @@ namespace crypto
|
|||
}
|
||||
const std::vector<std::string> &word_list = language->get_word_list();
|
||||
// To store the words for random access to add the checksum word later.
|
||||
std::vector<std::string> words_store;
|
||||
std::vector<epee::wipeable_string> words_store;
|
||||
|
||||
uint32_t word_list_length = word_list.size();
|
||||
// 4 bytes -> 3 words. 8 digits base 16 -> 3 digits base 1626
|
||||
for (unsigned int i=0; i < len/4; i++, words += ' ')
|
||||
for (unsigned int i=0; i < len/4; i++, words.push_back(' '))
|
||||
{
|
||||
uint32_t w1, w2, w3;
|
||||
uint32_t w[4];
|
||||
|
||||
uint32_t val;
|
||||
memcpy(&w[0], src + (i * 4), 4);
|
||||
|
||||
memcpy(&val, src + (i * 4), 4);
|
||||
w[1] = w[0] % word_list_length;
|
||||
w[2] = ((w[0] / word_list_length) + w[1]) % word_list_length;
|
||||
w[3] = (((w[0] / word_list_length) / word_list_length) + w[2]) % word_list_length;
|
||||
|
||||
w1 = val % word_list_length;
|
||||
w2 = ((val / word_list_length) + w1) % word_list_length;
|
||||
w3 = (((val / word_list_length) / word_list_length) + w2) % word_list_length;
|
||||
|
||||
words += word_list[w1];
|
||||
words += word_list[w[1]];
|
||||
words += ' ';
|
||||
words += word_list[w2];
|
||||
words += word_list[w[2]];
|
||||
words += ' ';
|
||||
words += word_list[w3];
|
||||
words += word_list[w[3]];
|
||||
|
||||
words_store.push_back(word_list[w1]);
|
||||
words_store.push_back(word_list[w2]);
|
||||
words_store.push_back(word_list[w3]);
|
||||
words_store.push_back(word_list[w[1]]);
|
||||
words_store.push_back(word_list[w[2]]);
|
||||
words_store.push_back(word_list[w[3]]);
|
||||
|
||||
memwipe(w, sizeof(w));
|
||||
}
|
||||
|
||||
words.pop_back();
|
||||
words += (' ' + words_store[create_checksum_index(words_store, language->get_unique_prefix_length())]);
|
||||
words += words_store[create_checksum_index(words_store, language->get_unique_prefix_length())];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bytes_to_words(const crypto::secret_key& src, std::string& words,
|
||||
bool bytes_to_words(const crypto::secret_key& src, epee::wipeable_string& words,
|
||||
const std::string &language_name)
|
||||
{
|
||||
return bytes_to_words(src.data, sizeof(src), words, language_name);
|
||||
|
@ -473,11 +478,10 @@ namespace crypto
|
|||
* \param seed The seed to check (a space delimited concatenated word list)
|
||||
* \return true if the seed passed is a old style seed false if not.
|
||||
*/
|
||||
bool get_is_old_style_seed(std::string seed)
|
||||
bool get_is_old_style_seed(const epee::wipeable_string &seed)
|
||||
{
|
||||
std::vector<std::string> word_list;
|
||||
boost::algorithm::trim(seed);
|
||||
boost::split(word_list, seed, boost::is_any_of(" "), boost::token_compress_on);
|
||||
std::vector<epee::wipeable_string> word_list;
|
||||
seed.split(word_list);
|
||||
return word_list.size() != (seed_length + 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,8 @@
|
|||
#include <map>
|
||||
#include "crypto/crypto.h" // for declaration of crypto::secret_key
|
||||
|
||||
namespace epee { class wipeable_string; }
|
||||
|
||||
/*!
|
||||
* \namespace crypto
|
||||
*
|
||||
|
@ -70,7 +72,7 @@ namespace crypto
|
|||
* \param language_name Language of the seed as found gets written here.
|
||||
* \return false if not a multiple of 3 words, or if word is not in the words list
|
||||
*/
|
||||
bool words_to_bytes(std::string words, std::string& dst, size_t len, bool duplicate,
|
||||
bool words_to_bytes(const epee::wipeable_string &words, epee::wipeable_string& dst, size_t len, bool duplicate,
|
||||
std::string &language_name);
|
||||
/*!
|
||||
* \brief Converts seed words to bytes (secret key).
|
||||
|
@ -79,7 +81,7 @@ namespace crypto
|
|||
* \param language_name Language of the seed as found gets written here.
|
||||
* \return false if not a multiple of 3 words, or if word is not in the words list
|
||||
*/
|
||||
bool words_to_bytes(std::string words, crypto::secret_key& dst,
|
||||
bool words_to_bytes(const epee::wipeable_string &words, crypto::secret_key& dst,
|
||||
std::string &language_name);
|
||||
|
||||
/*!
|
||||
|
@ -90,7 +92,7 @@ namespace crypto
|
|||
* \param language_name Seed language name
|
||||
* \return true if successful false if not. Unsuccessful if wrong key size.
|
||||
*/
|
||||
bool bytes_to_words(const char *src, size_t len, std::string& words,
|
||||
bool bytes_to_words(const char *src, size_t len, epee::wipeable_string& words,
|
||||
const std::string &language_name);
|
||||
|
||||
/*!
|
||||
|
@ -100,7 +102,7 @@ namespace crypto
|
|||
* \param language_name Seed language name
|
||||
* \return true if successful false if not. Unsuccessful if wrong key size.
|
||||
*/
|
||||
bool bytes_to_words(const crypto::secret_key& src, std::string& words,
|
||||
bool bytes_to_words(const crypto::secret_key& src, epee::wipeable_string& words,
|
||||
const std::string &language_name);
|
||||
|
||||
/*!
|
||||
|
@ -115,7 +117,7 @@ namespace crypto
|
|||
* \param seed The seed to check (a space delimited concatenated word list)
|
||||
* \return true if the seed passed is a old style seed false if not.
|
||||
*/
|
||||
bool get_is_old_style_seed(std::string seed);
|
||||
bool get_is_old_style_seed(const epee::wipeable_string &seed);
|
||||
|
||||
/*!
|
||||
* \brief Returns the name of a language in English
|
||||
|
|
|
@ -53,15 +53,20 @@ namespace Language
|
|||
* \param count How many characters to return.
|
||||
* \return A string consisting of the first count characters in s.
|
||||
*/
|
||||
inline std::string utf8prefix(const std::string &s, size_t count)
|
||||
template<typename T>
|
||||
inline T utf8prefix(const T &s, size_t count)
|
||||
{
|
||||
std::string prefix = "";
|
||||
const char *ptr = s.c_str();
|
||||
while (count-- && *ptr)
|
||||
T prefix = "";
|
||||
size_t avail = s.size();
|
||||
const char *ptr = s.data();
|
||||
while (count-- && avail--)
|
||||
{
|
||||
prefix += *ptr++;
|
||||
while (((*ptr) & 0xc0) == 0x80)
|
||||
while (avail && ((*ptr) & 0xc0) == 0x80)
|
||||
{
|
||||
prefix += *ptr++;
|
||||
--avail;
|
||||
}
|
||||
}
|
||||
return prefix;
|
||||
}
|
||||
|
@ -79,8 +84,8 @@ namespace Language
|
|||
ALLOW_DUPLICATE_PREFIXES = 1<<1,
|
||||
};
|
||||
const std::vector<std::string> word_list; /*!< A pointer to the array of words */
|
||||
std::unordered_map<std::string, uint32_t> word_map; /*!< hash table to find word's index */
|
||||
std::unordered_map<std::string, uint32_t> trimmed_word_map; /*!< hash table to find word's trimmed index */
|
||||
std::unordered_map<epee::wipeable_string, uint32_t> word_map; /*!< hash table to find word's index */
|
||||
std::unordered_map<epee::wipeable_string, uint32_t> trimmed_word_map; /*!< hash table to find word's trimmed index */
|
||||
std::string language_name; /*!< Name of language */
|
||||
std::string english_language_name; /*!< Name of language */
|
||||
uint32_t unique_prefix_length; /*!< Number of unique starting characters to trim the wordlist to when matching */
|
||||
|
@ -103,7 +108,7 @@ namespace Language
|
|||
else
|
||||
throw std::runtime_error("Too short word in " + language_name + " word list: " + *it);
|
||||
}
|
||||
std::string trimmed;
|
||||
epee::wipeable_string trimmed;
|
||||
if (it->length() > unique_prefix_length)
|
||||
{
|
||||
trimmed = utf8prefix(*it, unique_prefix_length);
|
||||
|
@ -115,9 +120,9 @@ namespace Language
|
|||
if (trimmed_word_map.find(trimmed) != trimmed_word_map.end())
|
||||
{
|
||||
if (flags & ALLOW_DUPLICATE_PREFIXES)
|
||||
MWARNING("Duplicate prefix in " << language_name << " word list: " << trimmed);
|
||||
MWARNING("Duplicate prefix in " << language_name << " word list: " << std::string(trimmed.data(), trimmed.size()));
|
||||
else
|
||||
throw std::runtime_error("Duplicate prefix in " + language_name + " word list: " + trimmed);
|
||||
throw std::runtime_error("Duplicate prefix in " + language_name + " word list: " + std::string(trimmed.data(), trimmed.size()));
|
||||
}
|
||||
trimmed_word_map[trimmed] = ii;
|
||||
}
|
||||
|
@ -145,7 +150,7 @@ namespace Language
|
|||
* \brief Returns a pointer to the word map.
|
||||
* \return A pointer to the word map.
|
||||
*/
|
||||
const std::unordered_map<std::string, uint32_t>& get_word_map() const
|
||||
const std::unordered_map<epee::wipeable_string, uint32_t>& get_word_map() const
|
||||
{
|
||||
return word_map;
|
||||
}
|
||||
|
@ -153,7 +158,7 @@ namespace Language
|
|||
* \brief Returns a pointer to the trimmed word map.
|
||||
* \return A pointer to the trimmed word map.
|
||||
*/
|
||||
const std::unordered_map<std::string, uint32_t>& get_trimmed_word_map() const
|
||||
const std::unordered_map<epee::wipeable_string, uint32_t>& get_trimmed_word_map() const
|
||||
{
|
||||
return trimmed_word_map;
|
||||
}
|
||||
|
|
|
@ -106,6 +106,12 @@ typedef cryptonote::simple_wallet sw;
|
|||
m_auto_refresh_enabled.store(auto_refresh_enabled, std::memory_order_relaxed); \
|
||||
})
|
||||
|
||||
#define SCOPED_WALLET_UNLOCK() \
|
||||
LOCK_IDLE_SCOPE(); \
|
||||
boost::optional<tools::password_container> pwd_container = boost::none; \
|
||||
if (m_wallet->ask_password() && !m_wallet->watch_only() && !(pwd_container = get_and_verify_password())) { return true; } \
|
||||
tools::wallet_keys_unlocker unlocker(*m_wallet, pwd_container);
|
||||
|
||||
enum TransferType {
|
||||
TransferOriginal,
|
||||
TransferNew,
|
||||
|
@ -152,12 +158,31 @@ namespace
|
|||
//
|
||||
// Note also that input for passwords is NOT translated, to remain compatible with any
|
||||
// passwords containing special characters that predate this switch to UTF-8 support.
|
||||
static std::string cp850_to_utf8(const std::string &cp850_str)
|
||||
template<typename T>
|
||||
static T cp850_to_utf8(const T &cp850_str)
|
||||
{
|
||||
boost::locale::generator gen;
|
||||
gen.locale_cache_enabled(true);
|
||||
std::locale loc = gen("en_US.CP850");
|
||||
return boost::locale::conv::to_utf<char>(cp850_str, loc);
|
||||
const boost::locale::conv::method_type how = boost::locale::conv::default_method;
|
||||
T result;
|
||||
const char *begin = cp850_str.data();
|
||||
const char *end = begin + cp850_str.size();
|
||||
result.reserve(end-begin);
|
||||
typedef std::back_insert_iterator<T> inserter_type;
|
||||
inserter_type inserter(result);
|
||||
boost::locale::utf::code_point c;
|
||||
while(begin!=end) {
|
||||
c=boost::locale::utf::utf_traits<char>::template decode<char const *>(begin,end);
|
||||
if(c==boost::locale::utf::illegal || c==boost::locale::utf::incomplete) {
|
||||
if(how==boost::locale::conv::stop)
|
||||
throw boost::locale::conv::conversion_error();
|
||||
}
|
||||
else {
|
||||
boost::locale::utf::utf_traits<char>::template encode<inserter_type>(c,inserter);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -177,6 +202,28 @@ namespace
|
|||
return epee::string_tools::trim(buf);
|
||||
}
|
||||
|
||||
epee::wipeable_string input_secure_line(const std::string& prompt)
|
||||
{
|
||||
#ifdef HAVE_READLINE
|
||||
rdln::suspend_readline pause_readline;
|
||||
#endif
|
||||
auto pwd_container = tools::password_container::prompt(false, prompt.c_str(), false);
|
||||
if (!pwd_container)
|
||||
{
|
||||
MERROR("Failed to read secure line");
|
||||
return "";
|
||||
}
|
||||
|
||||
epee::wipeable_string buf = pwd_container->password();
|
||||
|
||||
#ifdef WIN32
|
||||
buf = cp850_to_utf8(buf);
|
||||
#endif
|
||||
|
||||
buf.trim();
|
||||
return buf;
|
||||
}
|
||||
|
||||
boost::optional<tools::password_container> password_prompter(const char *prompt, bool verify)
|
||||
{
|
||||
#ifdef HAVE_READLINE
|
||||
|
@ -512,6 +559,18 @@ namespace
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void print_secret_key(const crypto::secret_key &k)
|
||||
{
|
||||
static constexpr const char hex[] = u8"0123456789abcdef";
|
||||
const uint8_t *ptr = (const uint8_t*)k.data;
|
||||
for (size_t i = 0, sz = sizeof(k); i < sz; ++i)
|
||||
{
|
||||
putchar(hex[*ptr >> 4]);
|
||||
putchar(hex[*ptr & 15]);
|
||||
++ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool parse_priority(const std::string& arg, uint32_t& priority)
|
||||
|
@ -561,12 +620,15 @@ std::string simple_wallet::get_command_usage(const std::vector<std::string> &arg
|
|||
|
||||
bool simple_wallet::viewkey(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
|
||||
{
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
// don't log
|
||||
PAUSE_READLINE();
|
||||
if (m_wallet->key_on_device()) {
|
||||
std::cout << "secret: On device. Not available" << std::endl;
|
||||
} else {
|
||||
std::cout << "secret: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key) << std::endl;
|
||||
printf("secret: ");
|
||||
print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key);
|
||||
putchar('\n');
|
||||
}
|
||||
std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key) << std::endl;
|
||||
|
||||
|
@ -580,12 +642,15 @@ bool simple_wallet::spendkey(const std::vector<std::string> &args/* = std::vecto
|
|||
fail_msg_writer() << tr("wallet is watch-only and has no spend key");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
// don't log
|
||||
PAUSE_READLINE();
|
||||
if (m_wallet->key_on_device()) {
|
||||
std::cout << "secret: On device. Not available" << std::endl;
|
||||
} else {
|
||||
std::cout << "secret: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_spend_secret_key) << std::endl;
|
||||
printf("secret: ");
|
||||
print_secret_key(m_wallet->get_account().get_keys().m_spend_secret_key);
|
||||
putchar('\n');
|
||||
}
|
||||
std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key) << std::endl;
|
||||
|
||||
|
@ -595,7 +660,7 @@ bool simple_wallet::spendkey(const std::vector<std::string> &args/* = std::vecto
|
|||
bool simple_wallet::print_seed(bool encrypted)
|
||||
{
|
||||
bool success = false;
|
||||
std::string seed;
|
||||
epee::wipeable_string seed;
|
||||
bool ready, multisig;
|
||||
|
||||
if (m_wallet->key_on_device())
|
||||
|
@ -608,7 +673,8 @@ bool simple_wallet::print_seed(bool encrypted)
|
|||
fail_msg_writer() << tr("wallet is watch-only and has no seed");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
multisig = m_wallet->multisig(&ready);
|
||||
if (multisig)
|
||||
|
@ -677,22 +743,36 @@ bool simple_wallet::seed_set_language(const std::vector<std::string> &args/* = s
|
|||
fail_msg_writer() << tr("wallet is watch-only and has no seed");
|
||||
return true;
|
||||
}
|
||||
if (!m_wallet->is_deterministic())
|
||||
{
|
||||
fail_msg_writer() << tr("wallet is non-deterministic and has no seed");
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto pwd_container = get_and_verify_password();
|
||||
if (pwd_container)
|
||||
epee::wipeable_string password;
|
||||
{
|
||||
std::string mnemonic_language = get_mnemonic_language();
|
||||
if (mnemonic_language.empty())
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
if (!m_wallet->is_deterministic())
|
||||
{
|
||||
fail_msg_writer() << tr("wallet is non-deterministic and has no seed");
|
||||
return true;
|
||||
}
|
||||
|
||||
m_wallet->set_seed_language(std::move(mnemonic_language));
|
||||
m_wallet->rewrite(m_wallet_file, pwd_container->password());
|
||||
// we need the password, even if ask-password is unset
|
||||
if (!pwd_container)
|
||||
{
|
||||
pwd_container = get_and_verify_password();
|
||||
if (pwd_container == boost::none)
|
||||
{
|
||||
fail_msg_writer() << tr("Incorrect password");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
password = pwd_container->password();
|
||||
}
|
||||
|
||||
std::string mnemonic_language = get_mnemonic_language();
|
||||
if (mnemonic_language.empty())
|
||||
return true;
|
||||
|
||||
m_wallet->set_seed_language(std::move(mnemonic_language));
|
||||
m_wallet->rewrite(m_wallet_file, password);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -713,8 +793,7 @@ bool simple_wallet::change_password(const std::vector<std::string> &args)
|
|||
|
||||
try
|
||||
{
|
||||
m_wallet->rewrite(m_wallet_file, pwd_container->password());
|
||||
m_wallet->store();
|
||||
m_wallet->change_password(m_wallet_file, orig_pwd_container->password(), pwd_container->password());
|
||||
}
|
||||
catch (const tools::error::wallet_logic_error& e)
|
||||
{
|
||||
|
@ -817,12 +896,7 @@ bool simple_wallet::prepare_multisig(const std::vector<std::string> &args)
|
|||
return true;
|
||||
}
|
||||
|
||||
const auto orig_pwd_container = get_and_verify_password();
|
||||
if(orig_pwd_container == boost::none)
|
||||
{
|
||||
fail_msg_writer() << tr("Your password is incorrect.");
|
||||
return true;
|
||||
}
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
std::string multisig_info = m_wallet->get_multisig_info();
|
||||
success_msg_writer() << multisig_info;
|
||||
|
@ -855,13 +929,6 @@ bool simple_wallet::make_multisig(const std::vector<std::string> &args)
|
|||
return true;
|
||||
}
|
||||
|
||||
const auto orig_pwd_container = get_and_verify_password();
|
||||
if(orig_pwd_container == boost::none)
|
||||
{
|
||||
fail_msg_writer() << tr("Your original password was incorrect.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.size() < 2)
|
||||
{
|
||||
fail_msg_writer() << tr("usage: make_multisig <threshold> <multisiginfo1> [<multisiginfo2>...]");
|
||||
|
@ -876,6 +943,13 @@ bool simple_wallet::make_multisig(const std::vector<std::string> &args)
|
|||
return true;
|
||||
}
|
||||
|
||||
const auto orig_pwd_container = get_and_verify_password();
|
||||
if(orig_pwd_container == boost::none)
|
||||
{
|
||||
fail_msg_writer() << tr("Your original password was incorrect.");
|
||||
return true;
|
||||
}
|
||||
|
||||
LOCK_IDLE_SCOPE();
|
||||
|
||||
try
|
||||
|
@ -917,6 +991,14 @@ bool simple_wallet::finalize_multisig(const std::vector<std::string> &args)
|
|||
fail_msg_writer() << tr("command not supported by HW wallet");
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto pwd_container = get_and_verify_password();
|
||||
if(pwd_container == boost::none)
|
||||
{
|
||||
fail_msg_writer() << tr("Your original password was incorrect.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!m_wallet->multisig(&ready))
|
||||
{
|
||||
fail_msg_writer() << tr("This wallet is not multisig");
|
||||
|
@ -928,12 +1010,7 @@ bool simple_wallet::finalize_multisig(const std::vector<std::string> &args)
|
|||
return true;
|
||||
}
|
||||
|
||||
const auto orig_pwd_container = get_and_verify_password();
|
||||
if(orig_pwd_container == boost::none)
|
||||
{
|
||||
fail_msg_writer() << tr("Your original password was incorrect.");
|
||||
return true;
|
||||
}
|
||||
LOCK_IDLE_SCOPE();
|
||||
|
||||
if (args.size() < 2)
|
||||
{
|
||||
|
@ -943,7 +1020,7 @@ bool simple_wallet::finalize_multisig(const std::vector<std::string> &args)
|
|||
|
||||
try
|
||||
{
|
||||
if (!m_wallet->finalize_multisig(orig_pwd_container->password(), args))
|
||||
if (!m_wallet->finalize_multisig(pwd_container->password(), args))
|
||||
{
|
||||
fail_msg_writer() << tr("Failed to finalize multisig");
|
||||
return true;
|
||||
|
@ -981,8 +1058,8 @@ bool simple_wallet::export_multisig(const std::vector<std::string> &args)
|
|||
fail_msg_writer() << tr("usage: export_multisig_info <filename>");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password())
|
||||
return true;
|
||||
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
const std::string filename = args[0];
|
||||
if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
|
||||
|
@ -1033,8 +1110,8 @@ bool simple_wallet::import_multisig(const std::vector<std::string> &args)
|
|||
fail_msg_writer() << tr("usage: import_multisig_info <filename1> [<filename2>...] - one for each other participant");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password())
|
||||
return true;
|
||||
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
std::vector<cryptonote::blobdata> info;
|
||||
for (size_t n = 0; n < args.size(); ++n)
|
||||
|
@ -1050,11 +1127,11 @@ bool simple_wallet::import_multisig(const std::vector<std::string> &args)
|
|||
info.push_back(std::move(data));
|
||||
}
|
||||
|
||||
LOCK_IDLE_SCOPE();
|
||||
|
||||
// all read and parsed, actually import
|
||||
try
|
||||
{
|
||||
m_in_manual_refresh.store(true, std::memory_order_relaxed);
|
||||
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
|
||||
size_t n_outputs = m_wallet->import_multisig(info);
|
||||
// Clear line "Height xxx of xxx"
|
||||
std::cout << "\r \r";
|
||||
|
@ -1112,7 +1189,8 @@ bool simple_wallet::sign_multisig(const std::vector<std::string> &args)
|
|||
fail_msg_writer() << tr("usage: sign_multisig <filename>");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
std::string filename = args[0];
|
||||
std::vector<crypto::hash> txids;
|
||||
|
@ -1185,7 +1263,8 @@ bool simple_wallet::submit_multisig(const std::vector<std::string> &args)
|
|||
fail_msg_writer() << tr("usage: submit_multisig <filename>");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
if (!try_connect_to_daemon())
|
||||
return true;
|
||||
|
@ -1252,7 +1331,8 @@ bool simple_wallet::export_raw_multisig(const std::vector<std::string> &args)
|
|||
fail_msg_writer() << tr("usage: export_raw_multisig <filename>");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
std::string filename = args[0];
|
||||
if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
|
||||
|
@ -1874,6 +1954,14 @@ bool simple_wallet::set_ask_password(const std::vector<std::string> &args/* = st
|
|||
if (pwd_container)
|
||||
{
|
||||
parse_bool_and_use(args[1], [&](bool r) {
|
||||
const bool cur_r = m_wallet->ask_password();
|
||||
if (!m_wallet->watch_only())
|
||||
{
|
||||
if (cur_r && !r)
|
||||
m_wallet->decrypt_keys(pwd_container->password());
|
||||
else if (!cur_r && r)
|
||||
m_wallet->encrypt_keys(pwd_container->password());
|
||||
}
|
||||
m_wallet->ask_password(r);
|
||||
m_wallet->rewrite(m_wallet_file, pwd_container->password());
|
||||
});
|
||||
|
@ -2679,28 +2767,45 @@ bool simple_wallet::ask_wallet_create_if_needed()
|
|||
* \brief Prints the seed with a nice message
|
||||
* \param seed seed to print
|
||||
*/
|
||||
void simple_wallet::print_seed(std::string seed)
|
||||
void simple_wallet::print_seed(const epee::wipeable_string &seed)
|
||||
{
|
||||
success_msg_writer(true) << "\n" << tr("NOTE: the following 25 words can be used to recover access to your wallet. "
|
||||
"Write them down and store them somewhere safe and secure. Please do not store them in "
|
||||
"your email or on file storage services outside of your immediate control.\n");
|
||||
boost::replace_nth(seed, " ", 15, "\n");
|
||||
boost::replace_nth(seed, " ", 7, "\n");
|
||||
// don't log
|
||||
std::cout << seed << std::endl;
|
||||
int space_index = 0;
|
||||
size_t len = seed.size();
|
||||
for (const char *ptr = seed.data(); len--; ++ptr)
|
||||
{
|
||||
if (*ptr == ' ')
|
||||
{
|
||||
if (space_index == 15 || space_index == 7)
|
||||
putchar('\n');
|
||||
else
|
||||
putchar(*ptr);
|
||||
++space_index;
|
||||
}
|
||||
else
|
||||
putchar(*ptr);
|
||||
}
|
||||
putchar('\n');
|
||||
fflush(stdout);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
static bool might_be_partial_seed(std::string words)
|
||||
static bool might_be_partial_seed(const epee::wipeable_string &words)
|
||||
{
|
||||
std::vector<std::string> seed;
|
||||
std::vector<epee::wipeable_string> seed;
|
||||
|
||||
boost::algorithm::trim(words);
|
||||
boost::split(seed, words, boost::is_any_of(" "), boost::token_compress_on);
|
||||
words.split(seed);
|
||||
return seed.size() < 24;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::init(const boost::program_options::variables_map& vm)
|
||||
{
|
||||
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){
|
||||
m_electrum_seed.wipe();
|
||||
});
|
||||
|
||||
const bool testnet = tools::wallet2::has_testnet_option(vm);
|
||||
const bool stagenet = tools::wallet2::has_stagenet_option(vm);
|
||||
if (testnet && stagenet)
|
||||
|
@ -2710,7 +2815,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
|
|||
}
|
||||
const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET;
|
||||
|
||||
std::string multisig_keys;
|
||||
epee::wipeable_string multisig_keys;
|
||||
|
||||
if (!handle_command_line(vm))
|
||||
return false;
|
||||
|
@ -2752,8 +2857,8 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
|
|||
{
|
||||
if (m_restore_multisig_wallet)
|
||||
{
|
||||
const char *prompt = "Specify multisig seed: ";
|
||||
m_electrum_seed = input_line(prompt);
|
||||
const char *prompt = "Specify multisig seed";
|
||||
m_electrum_seed = input_secure_line(prompt);
|
||||
if (std::cin.eof())
|
||||
return false;
|
||||
if (m_electrum_seed.empty())
|
||||
|
@ -2767,8 +2872,8 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
|
|||
m_electrum_seed = "";
|
||||
do
|
||||
{
|
||||
const char *prompt = m_electrum_seed.empty() ? "Specify Electrum seed: " : "Electrum seed continued: ";
|
||||
std::string electrum_seed = input_line(prompt);
|
||||
const char *prompt = m_electrum_seed.empty() ? "Specify Electrum seed" : "Electrum seed continued";
|
||||
epee::wipeable_string electrum_seed = input_secure_line(prompt);
|
||||
if (std::cin.eof())
|
||||
return false;
|
||||
if (electrum_seed.empty())
|
||||
|
@ -2776,18 +2881,21 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
|
|||
fail_msg_writer() << tr("specify a recovery parameter with the --electrum-seed=\"words list here\"");
|
||||
return false;
|
||||
}
|
||||
m_electrum_seed += electrum_seed + " ";
|
||||
m_electrum_seed += electrum_seed;
|
||||
m_electrum_seed += ' ';
|
||||
} while (might_be_partial_seed(m_electrum_seed));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_restore_multisig_wallet)
|
||||
{
|
||||
if (!epee::string_tools::parse_hexstr_to_binbuff(m_electrum_seed, multisig_keys))
|
||||
const boost::optional<epee::wipeable_string> parsed = m_electrum_seed.parse_hexstr();
|
||||
if (!parsed)
|
||||
{
|
||||
fail_msg_writer() << tr("Multisig seed failed verification");
|
||||
return false;
|
||||
}
|
||||
multisig_keys = *parsed;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2809,7 +2917,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
|
|||
crypto::secret_key key;
|
||||
crypto::cn_slow_hash(seed_pass.data(), seed_pass.size(), (crypto::hash&)key);
|
||||
sc_reduce32((unsigned char*)key.data);
|
||||
multisig_keys = m_wallet->decrypt(multisig_keys, key, true);
|
||||
multisig_keys = m_wallet->decrypt<epee::wipeable_string>(std::string(multisig_keys.data(), multisig_keys.size()), key, true);
|
||||
}
|
||||
else
|
||||
m_recovery_key = cryptonote::decrypt_key(m_recovery_key, seed_pass);
|
||||
|
@ -3112,7 +3220,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
|
|||
m_wallet_file = m_generate_from_json;
|
||||
try
|
||||
{
|
||||
m_wallet = tools::wallet2::make_from_json(vm, m_wallet_file, password_prompter);
|
||||
m_wallet = tools::wallet2::make_from_json(vm, false, m_wallet_file, password_prompter);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
|
@ -3414,7 +3522,7 @@ boost::optional<tools::password_container> simple_wallet::get_and_verify_passwor
|
|||
boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
|
||||
const crypto::secret_key& recovery_key, bool recover, bool two_random, const std::string &old_language)
|
||||
{
|
||||
auto rc = tools::wallet2::make_new(vm, password_prompter);
|
||||
auto rc = tools::wallet2::make_new(vm, false, password_prompter);
|
||||
m_wallet = std::move(rc.first);
|
||||
if (!m_wallet)
|
||||
{
|
||||
|
@ -3469,7 +3577,10 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
|
|||
recovery_val = m_wallet->generate(m_wallet_file, std::move(rc.second).password(), recovery_key, recover, two_random, create_address_file);
|
||||
message_writer(console_color_white, true) << tr("Generated new wallet: ")
|
||||
<< m_wallet->get_account().get_public_address_str(m_wallet->nettype());
|
||||
std::cout << tr("View key: ") << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key) << ENDL;
|
||||
PAUSE_READLINE();
|
||||
std::cout << tr("View key: ");
|
||||
print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key);
|
||||
putchar('\n');
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
|
@ -3478,7 +3589,7 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
|
|||
}
|
||||
|
||||
// convert rng value to electrum-style word list
|
||||
std::string electrum_words;
|
||||
epee::wipeable_string electrum_words;
|
||||
|
||||
crypto::ElectrumWords::bytes_to_words(recovery_val, electrum_words, mnemonic_language);
|
||||
|
||||
|
@ -3506,7 +3617,7 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
|
|||
const cryptonote::account_public_address& address, const boost::optional<crypto::secret_key>& spendkey,
|
||||
const crypto::secret_key& viewkey)
|
||||
{
|
||||
auto rc = tools::wallet2::make_new(vm, password_prompter);
|
||||
auto rc = tools::wallet2::make_new(vm, false, password_prompter);
|
||||
m_wallet = std::move(rc.first);
|
||||
if (!m_wallet)
|
||||
{
|
||||
|
@ -3552,7 +3663,7 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
|
|||
//----------------------------------------------------------------------------------------------------
|
||||
boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
|
||||
const std::string &device_name) {
|
||||
auto rc = tools::wallet2::make_new(vm, password_prompter);
|
||||
auto rc = tools::wallet2::make_new(vm, false, password_prompter);
|
||||
m_wallet = std::move(rc.first);
|
||||
if (!m_wallet)
|
||||
{
|
||||
|
@ -3586,9 +3697,9 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
|
|||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
|
||||
const std::string &multisig_keys, const std::string &old_language)
|
||||
const epee::wipeable_string &multisig_keys, const std::string &old_language)
|
||||
{
|
||||
auto rc = tools::wallet2::make_new(vm, password_prompter);
|
||||
auto rc = tools::wallet2::make_new(vm, false, password_prompter);
|
||||
m_wallet = std::move(rc.first);
|
||||
if (!m_wallet)
|
||||
{
|
||||
|
@ -3659,7 +3770,7 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
|
|||
epee::wipeable_string password;
|
||||
try
|
||||
{
|
||||
auto rc = tools::wallet2::make_from_file(vm, m_wallet_file, password_prompter);
|
||||
auto rc = tools::wallet2::make_from_file(vm, false, m_wallet_file, password_prompter);
|
||||
m_wallet = std::move(rc.first);
|
||||
password = std::move(std::move(rc.second).password());
|
||||
if (!m_wallet)
|
||||
|
@ -3686,7 +3797,12 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
|
|||
// NOTE: this is_deprecated() refers to the wallet file format before becoming JSON. It does not refer to the "old english" seed words form of "deprecated" used elsewhere.
|
||||
if (m_wallet->is_deprecated())
|
||||
{
|
||||
if (m_wallet->is_deterministic())
|
||||
bool is_deterministic;
|
||||
{
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
is_deterministic = m_wallet->is_deterministic();
|
||||
}
|
||||
if (is_deterministic)
|
||||
{
|
||||
message_writer(console_color_green, false) << "\n" << tr("You had been using "
|
||||
"a deprecated version of the wallet. Please proceed to upgrade your wallet.\n");
|
||||
|
@ -3697,7 +3813,7 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
|
|||
m_wallet->rewrite(m_wallet_file, password);
|
||||
|
||||
// Display the seed
|
||||
std::string seed;
|
||||
epee::wipeable_string seed;
|
||||
m_wallet->get_seed(seed);
|
||||
print_seed(seed);
|
||||
}
|
||||
|
@ -3924,7 +4040,7 @@ bool simple_wallet::set_daemon(const std::vector<std::string>& args)
|
|||
daemon_url = args[0];
|
||||
}
|
||||
LOCK_IDLE_SCOPE();
|
||||
m_wallet->init(daemon_url);
|
||||
m_wallet->init(false, daemon_url);
|
||||
|
||||
if (args.size() == 2)
|
||||
{
|
||||
|
@ -4038,6 +4154,32 @@ void simple_wallet::on_skip_transaction(uint64_t height, const crypto::hash &txi
|
|||
{
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
boost::optional<epee::wipeable_string> simple_wallet::on_get_password(const char *reason)
|
||||
{
|
||||
// can't ask for password from a background thread
|
||||
if (!m_in_manual_refresh.load(std::memory_order_relaxed))
|
||||
{
|
||||
message_writer(console_color_red, false) << tr("Password needed - use the refresh command");
|
||||
m_cmd_binder.print_prompt();
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
#ifdef HAVE_READLINE
|
||||
rdln::suspend_readline pause_readline;
|
||||
#endif
|
||||
std::string msg = tr("Enter password");
|
||||
if (reason && *reason)
|
||||
msg += std::string(" (") + reason + ")";
|
||||
auto pwd_container = tools::password_container::prompt(false, msg.c_str());
|
||||
if (!pwd_container)
|
||||
{
|
||||
MERROR("Failed to read password");
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
return pwd_container->password();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::refresh_main(uint64_t start_height, bool reset, bool is_init)
|
||||
{
|
||||
if (!try_connect_to_daemon(is_init))
|
||||
|
@ -4513,11 +4655,10 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
|
|||
bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::string> &args_)
|
||||
{
|
||||
// "transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>]"
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
if (!try_connect_to_daemon())
|
||||
return true;
|
||||
|
||||
LOCK_IDLE_SCOPE();
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
std::vector<std::string> local_args = args_;
|
||||
|
||||
|
@ -4920,11 +5061,11 @@ bool simple_wallet::locked_sweep_all(const std::vector<std::string> &args_)
|
|||
|
||||
bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
|
||||
{
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
if (!try_connect_to_daemon())
|
||||
return true;
|
||||
|
||||
LOCK_IDLE_SCOPE();
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
try
|
||||
{
|
||||
// figure out what tx will be necessary
|
||||
|
@ -5037,7 +5178,6 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
|
|||
return true;
|
||||
}
|
||||
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
if (!try_connect_to_daemon())
|
||||
return true;
|
||||
|
||||
|
@ -5201,7 +5341,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
|
|||
}
|
||||
}
|
||||
|
||||
LOCK_IDLE_SCOPE();
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -5303,7 +5443,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
|
|||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
|
||||
{
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
if (!try_connect_to_daemon())
|
||||
return true;
|
||||
|
||||
|
@ -5729,7 +5869,8 @@ bool simple_wallet::sign_transfer(const std::vector<std::string> &args_)
|
|||
fail_msg_writer() << tr("usage: sign_transfer [export_raw]");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
const bool export_raw = args_.size() == 1;
|
||||
|
||||
std::vector<tools::wallet2::pending_tx> ptx;
|
||||
|
@ -5818,7 +5959,6 @@ bool simple_wallet::get_tx_key(const std::vector<std::string> &args_)
|
|||
fail_msg_writer() << tr("usage: get_tx_key <txid>");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
|
||||
crypto::hash txid;
|
||||
if (!epee::string_tools::hex_to_pod(local_args[0], txid))
|
||||
|
@ -5827,7 +5967,7 @@ bool simple_wallet::get_tx_key(const std::vector<std::string> &args_)
|
|||
return true;
|
||||
}
|
||||
|
||||
LOCK_IDLE_SCOPE();
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
crypto::secret_key tx_key;
|
||||
std::vector<crypto::secret_key> additional_tx_keys;
|
||||
|
@ -5932,7 +6072,7 @@ bool simple_wallet::get_tx_proof(const std::vector<std::string> &args)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -6147,7 +6287,7 @@ bool simple_wallet::get_spend_proof(const std::vector<std::string> &args)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -6242,9 +6382,7 @@ bool simple_wallet::get_reserve_proof(const std::vector<std::string> &args)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
|
||||
LOCK_IDLE_SCOPE();
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -6497,6 +6635,9 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
|
|||
if (pool) {
|
||||
try
|
||||
{
|
||||
m_in_manual_refresh.store(true, std::memory_order_relaxed);
|
||||
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
|
||||
|
||||
m_wallet->update_pool_state();
|
||||
std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments;
|
||||
m_wallet->get_unconfirmed_payments(payments, m_current_subaddress_account, subaddr_indices);
|
||||
|
@ -7345,7 +7486,8 @@ bool simple_wallet::sign(const std::vector<std::string> &args)
|
|||
fail_msg_writer() << tr("This wallet is multisig and cannot sign");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
std::string filename = args[0];
|
||||
std::string data;
|
||||
bool r = epee::file_io_utils::load_file_to_string(filename, data);
|
||||
|
@ -7414,14 +7556,14 @@ bool simple_wallet::export_key_images(const std::vector<std::string> &args)
|
|||
fail_msg_writer() << tr("wallet is watch-only and cannot export key images");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
std::string filename = args[0];
|
||||
if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
LOCK_IDLE_SCOPE();
|
||||
if (!m_wallet->export_key_images(filename))
|
||||
{
|
||||
fail_msg_writer() << tr("failed to save file ") << filename;
|
||||
|
@ -7493,12 +7635,12 @@ bool simple_wallet::export_outputs(const std::vector<std::string> &args)
|
|||
fail_msg_writer() << tr("usage: export_outputs <filename>");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
std::string filename = args[0];
|
||||
if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
|
||||
return true;
|
||||
|
||||
LOCK_IDLE_SCOPE();
|
||||
try
|
||||
{
|
||||
std::string data = m_wallet->export_outputs_to_str();
|
||||
|
@ -7544,7 +7686,7 @@ bool simple_wallet::import_outputs(const std::vector<std::string> &args)
|
|||
|
||||
try
|
||||
{
|
||||
LOCK_IDLE_SCOPE();
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
size_t n_outputs = m_wallet->import_outputs_from_str(data);
|
||||
success_msg_writer() << boost::lexical_cast<std::string>(n_outputs) << " outputs imported";
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
||||
#include "wallet/wallet2.h"
|
||||
#include "console_handler.h"
|
||||
#include "wipeable_string.h"
|
||||
#include "common/i18n.h"
|
||||
#include "common/password.h"
|
||||
#include "crypto/crypto.h" // for definition of crypto::secret_key
|
||||
|
@ -96,7 +97,7 @@ namespace cryptonote
|
|||
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm, const cryptonote::account_public_address& address,
|
||||
const boost::optional<crypto::secret_key>& spendkey, const crypto::secret_key& viewkey);
|
||||
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm,
|
||||
const std::string &multisig_keys, const std::string &old_language);
|
||||
const epee::wipeable_string &multisig_keys, const std::string &old_language);
|
||||
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm, const std::string& device_name);
|
||||
bool open_wallet(const boost::program_options::variables_map& vm);
|
||||
bool close_wallet();
|
||||
|
@ -238,7 +239,7 @@ namespace cryptonote
|
|||
* \brief Prints the seed with a nice message
|
||||
* \param seed seed to print
|
||||
*/
|
||||
void print_seed(std::string seed);
|
||||
void print_seed(const epee::wipeable_string &seed);
|
||||
|
||||
/*!
|
||||
* \brief Gets the word seed language from the user.
|
||||
|
@ -261,6 +262,7 @@ namespace cryptonote
|
|||
virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index);
|
||||
virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index);
|
||||
virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx);
|
||||
virtual boost::optional<epee::wipeable_string> on_get_password(const char *reason);
|
||||
//----------------------------------------------------------
|
||||
|
||||
friend class refresh_progress_reporter_t;
|
||||
|
@ -329,7 +331,7 @@ namespace cryptonote
|
|||
std::string m_import_path;
|
||||
std::string m_subaddress_lookahead;
|
||||
|
||||
std::string m_electrum_seed; // electrum-style seed parameter
|
||||
epee::wipeable_string m_electrum_seed; // electrum-style seed parameter
|
||||
|
||||
crypto::secret_key m_recovery_key; // recovery key (used as random for wallet gen)
|
||||
bool m_restore_deterministic_wallet; // recover flag
|
||||
|
|
|
@ -733,10 +733,10 @@ bool WalletImpl::close(bool store)
|
|||
|
||||
std::string WalletImpl::seed() const
|
||||
{
|
||||
std::string seed;
|
||||
epee::wipeable_string seed;
|
||||
if (m_wallet)
|
||||
m_wallet->get_seed(seed);
|
||||
return seed;
|
||||
return std::string(seed.data(), seed.size()); // TODO
|
||||
}
|
||||
|
||||
std::string WalletImpl::getSeedLanguage() const
|
||||
|
@ -2032,7 +2032,8 @@ bool WalletImpl::isNewWallet() const
|
|||
|
||||
bool WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit, bool ssl)
|
||||
{
|
||||
if (!m_wallet->init(daemon_address, m_daemon_login, upper_transaction_size_limit, ssl))
|
||||
// claim RPC so there's no in-memory encryption for now
|
||||
if (!m_wallet->init(true, daemon_address, m_daemon_login, upper_transaction_size_limit, ssl))
|
||||
return false;
|
||||
|
||||
// in case new wallet, this will force fast-refresh (pulling hashes instead of blocks)
|
||||
|
|
|
@ -89,6 +89,7 @@ using namespace cryptonote;
|
|||
|
||||
// arbitrary, used to generate different hashes from the same input
|
||||
#define CHACHA8_KEY_TAIL 0x8c
|
||||
#define CACHE_KEY_TAIL 0x8d
|
||||
|
||||
#define UNSIGNED_TX_PREFIX "Monero unsigned tx set\004"
|
||||
#define SIGNED_TX_PREFIX "Monero signed tx set\004"
|
||||
|
@ -121,8 +122,6 @@ using namespace cryptonote;
|
|||
|
||||
static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1";
|
||||
|
||||
std::atomic<unsigned int> tools::wallet2::key_ref::refs(0);
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string get_default_ringdb_path()
|
||||
|
@ -197,7 +196,7 @@ std::string get_size_string(const cryptonote::blobdata &tx)
|
|||
return get_size_string(tx.size());
|
||||
}
|
||||
|
||||
std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, bool rpc, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
{
|
||||
const bool testnet = command_line::get_arg(vm, opts.testnet);
|
||||
const bool stagenet = command_line::get_arg(vm, opts.stagenet);
|
||||
|
@ -238,7 +237,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
|
|||
daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
|
||||
|
||||
std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, kdf_rounds));
|
||||
wallet->init(std::move(daemon_address), std::move(login));
|
||||
wallet->init(rpc, std::move(daemon_address), std::move(login));
|
||||
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
|
||||
wallet->set_ring_database(ringdb_path.string());
|
||||
return wallet;
|
||||
|
@ -273,7 +272,7 @@ boost::optional<tools::password_container> get_password(const boost::program_opt
|
|||
return password_prompter(verify ? tr("Enter a new password for the wallet") : tr("Wallet password"), verify);
|
||||
}
|
||||
|
||||
std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, bool rpc, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
{
|
||||
const bool testnet = command_line::get_arg(vm, opts.testnet);
|
||||
const bool stagenet = command_line::get_arg(vm, opts.stagenet);
|
||||
|
@ -411,7 +410,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
|
|||
THROW_WALLET_EXCEPTION_IF(deprecated_wallet, tools::error::wallet_internal_error,
|
||||
tools::wallet2::tr("Cannot generate deprecated wallets from JSON"));
|
||||
|
||||
wallet.reset(make_basic(vm, opts, password_prompter).release());
|
||||
wallet.reset(make_basic(vm, rpc, opts, password_prompter).release());
|
||||
wallet->set_refresh_from_block_height(field_scan_from_height);
|
||||
wallet->explicit_refresh_from_block_height(field_scan_from_height_found);
|
||||
|
||||
|
@ -648,6 +647,34 @@ const size_t MAX_SPLIT_ATTEMPTS = 30;
|
|||
constexpr const std::chrono::seconds wallet2::rpc_timeout;
|
||||
const char* wallet2::tr(const char* str) { return i18n_translate(str, "tools::wallet2"); }
|
||||
|
||||
wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional<tools::password_container> &password):
|
||||
w(w),
|
||||
locked(password != boost::none)
|
||||
{
|
||||
if (!locked || w.is_rpc())
|
||||
return;
|
||||
const epee::wipeable_string pass = password->password();
|
||||
w.generate_chacha_key_from_password(pass, key);
|
||||
w.decrypt_keys(key);
|
||||
}
|
||||
|
||||
wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, bool locked, const epee::wipeable_string &password):
|
||||
w(w),
|
||||
locked(locked)
|
||||
{
|
||||
if (!locked)
|
||||
return;
|
||||
w.generate_chacha_key_from_password(password, key);
|
||||
w.decrypt_keys(key);
|
||||
}
|
||||
|
||||
wallet_keys_unlocker::~wallet_keys_unlocker()
|
||||
{
|
||||
if (!locked)
|
||||
return;
|
||||
w.encrypt_keys(key);
|
||||
}
|
||||
|
||||
wallet2::wallet2(network_type nettype, uint64_t kdf_rounds):
|
||||
m_multisig_rescan_info(NULL),
|
||||
m_multisig_rescan_k(NULL),
|
||||
|
@ -693,7 +720,9 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds):
|
|||
m_key_on_device(false),
|
||||
m_ring_history_saved(false),
|
||||
m_ringdb(),
|
||||
m_last_block_reward(0)
|
||||
m_last_block_reward(0),
|
||||
m_encrypt_keys_after_refresh(boost::none),
|
||||
m_rpc(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -726,14 +755,14 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
|
|||
command_line::add_arg(desc_params, opts.kdf_rounds);
|
||||
}
|
||||
|
||||
std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, bool rpc, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
{
|
||||
const options opts{};
|
||||
return generate_from_json(json_file, vm, opts, password_prompter);
|
||||
return generate_from_json(json_file, vm, rpc, opts, password_prompter);
|
||||
}
|
||||
|
||||
std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
|
||||
const boost::program_options::variables_map& vm, const std::string& wallet_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
const boost::program_options::variables_map& vm, bool rpc, const std::string& wallet_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
{
|
||||
const options opts{};
|
||||
auto pwd = get_password(vm, opts, password_prompter, false);
|
||||
|
@ -741,7 +770,7 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
|
|||
{
|
||||
return {nullptr, password_container{}};
|
||||
}
|
||||
auto wallet = make_basic(vm, opts, password_prompter);
|
||||
auto wallet = make_basic(vm, rpc, opts, password_prompter);
|
||||
if (wallet)
|
||||
{
|
||||
wallet->load(wallet_file, pwd->password());
|
||||
|
@ -749,7 +778,7 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
|
|||
return {std::move(wallet), std::move(*pwd)};
|
||||
}
|
||||
|
||||
std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const boost::program_options::variables_map& vm, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter)
|
||||
std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const boost::program_options::variables_map& vm, bool rpc, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter)
|
||||
{
|
||||
const options opts{};
|
||||
auto pwd = get_password(vm, opts, password_prompter, true);
|
||||
|
@ -757,18 +786,19 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const
|
|||
{
|
||||
return {nullptr, password_container{}};
|
||||
}
|
||||
return {make_basic(vm, opts, password_prompter), std::move(*pwd)};
|
||||
return {make_basic(vm, rpc, opts, password_prompter), std::move(*pwd)};
|
||||
}
|
||||
|
||||
std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::variables_map& vm, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::variables_map& vm, bool rpc, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
{
|
||||
const options opts{};
|
||||
return make_basic(vm, opts, password_prompter);
|
||||
return make_basic(vm, rpc, opts, password_prompter);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_size_limit, bool ssl)
|
||||
bool wallet2::init(bool rpc, std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_size_limit, bool ssl)
|
||||
{
|
||||
m_rpc = rpc;
|
||||
m_checkpoints.init_default_checkpoints(m_nettype);
|
||||
if(m_http_client.is_connected())
|
||||
m_http_client.disconnect();
|
||||
|
@ -785,11 +815,10 @@ bool wallet2::is_deterministic() const
|
|||
crypto::secret_key second;
|
||||
keccak((uint8_t *)&get_account().get_keys().m_spend_secret_key, sizeof(crypto::secret_key), (uint8_t *)&second, sizeof(crypto::secret_key));
|
||||
sc_reduce32((uint8_t *)&second);
|
||||
bool keys_deterministic = memcmp(second.data,get_account().get_keys().m_view_secret_key.data, sizeof(crypto::secret_key)) == 0;
|
||||
return keys_deterministic;
|
||||
return memcmp(second.data,get_account().get_keys().m_view_secret_key.data, sizeof(crypto::secret_key)) == 0;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::get_seed(std::string& electrum_words, const epee::wipeable_string &passphrase) const
|
||||
bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase) const
|
||||
{
|
||||
bool keys_deterministic = is_deterministic();
|
||||
if (!keys_deterministic)
|
||||
|
@ -815,7 +844,7 @@ bool wallet2::get_seed(std::string& electrum_words, const epee::wipeable_string
|
|||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::get_multisig_seed(std::string& seed, const epee::wipeable_string &passphrase, bool raw) const
|
||||
bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase, bool raw) const
|
||||
{
|
||||
bool ready;
|
||||
uint32_t threshold, total;
|
||||
|
@ -838,7 +867,7 @@ bool wallet2::get_multisig_seed(std::string& seed, const epee::wipeable_string &
|
|||
crypto::secret_key skey;
|
||||
crypto::public_key pkey;
|
||||
const account_keys &keys = get_account().get_keys();
|
||||
std::string data;
|
||||
epee::wipeable_string data;
|
||||
data.append((const char*)&threshold, sizeof(uint32_t));
|
||||
data.append((const char*)&total, sizeof(uint32_t));
|
||||
skey = keys.m_spend_secret_key;
|
||||
|
@ -864,7 +893,7 @@ bool wallet2::get_multisig_seed(std::string& seed, const epee::wipeable_string &
|
|||
|
||||
if (raw)
|
||||
{
|
||||
seed = epee::string_tools::buff_to_hex_nodelimer(data);
|
||||
seed = epee::to_hex::wipeable_string({(const unsigned char*)data.data(), data.size()});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1103,9 +1132,25 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &
|
|||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs) const
|
||||
void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(i >= tx.vout.size(), error::wallet_internal_error, "Invalid vout index");
|
||||
|
||||
// if keys are encrypted, ask for password
|
||||
if (m_ask_password && !m_rpc && !m_watch_only && !m_multisig_rescan_k)
|
||||
{
|
||||
static critical_section password_lock;
|
||||
CRITICAL_REGION_LOCAL(password_lock);
|
||||
if (!m_encrypt_keys_after_refresh)
|
||||
{
|
||||
boost::optional<epee::wipeable_string> pwd = m_callback->on_get_password("output received");
|
||||
THROW_WALLET_EXCEPTION_IF(!pwd, error::password_needed, tr("Password is needed to compute key image for incoming monero"));
|
||||
THROW_WALLET_EXCEPTION_IF(!verify_password(*pwd), error::password_needed, tr("Invalid password: password is needed to compute key image for incoming monero"));
|
||||
decrypt_keys(*pwd);
|
||||
m_encrypt_keys_after_refresh = *pwd;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_multisig)
|
||||
{
|
||||
tx_scan_info.in_ephemeral.pub = boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key;
|
||||
|
@ -2063,6 +2108,14 @@ void wallet2::update_pool_state(bool refreshed)
|
|||
{
|
||||
MDEBUG("update_pool_state start");
|
||||
|
||||
auto keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]() {
|
||||
if (m_encrypt_keys_after_refresh)
|
||||
{
|
||||
encrypt_keys(*m_encrypt_keys_after_refresh);
|
||||
m_encrypt_keys_after_refresh = boost::none;
|
||||
}
|
||||
});
|
||||
|
||||
// get the pool state
|
||||
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::request req;
|
||||
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::response res;
|
||||
|
@ -2369,8 +2422,6 @@ bool wallet2::delete_address_book_row(std::size_t row_id) {
|
|||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_money)
|
||||
{
|
||||
key_ref kref(*this);
|
||||
|
||||
if(m_light_wallet) {
|
||||
|
||||
// MyMonero get_address_info needs to be called occasionally to trigger wallet sync.
|
||||
|
@ -2438,6 +2489,14 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
|
|||
// subsequent pulls in this refresh.
|
||||
start_height = 0;
|
||||
|
||||
auto keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]() {
|
||||
if (m_encrypt_keys_after_refresh)
|
||||
{
|
||||
encrypt_keys(*m_encrypt_keys_after_refresh);
|
||||
m_encrypt_keys_after_refresh = boost::none;
|
||||
}
|
||||
});
|
||||
|
||||
bool first = true;
|
||||
while(m_run.load(std::memory_order_relaxed))
|
||||
{
|
||||
|
@ -2507,6 +2566,12 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
|
|||
throw std::runtime_error("proxy exception in refresh thread");
|
||||
}
|
||||
}
|
||||
catch (const tools::error::password_needed&)
|
||||
{
|
||||
blocks_fetched += added_blocks;
|
||||
waiter.wait(&tpool);
|
||||
throw;
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
blocks_fetched += added_blocks;
|
||||
|
@ -2728,8 +2793,20 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
|
|||
std::string multisig_signers;
|
||||
cryptonote::account_base account = m_account;
|
||||
|
||||
crypto::chacha_key key;
|
||||
crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
|
||||
|
||||
if (m_ask_password && !m_rpc && !m_watch_only)
|
||||
{
|
||||
account.encrypt_viewkey(key);
|
||||
account.decrypt_keys(key);
|
||||
}
|
||||
|
||||
if (watch_only)
|
||||
account.forget_spend_key();
|
||||
|
||||
account.encrypt_keys(key);
|
||||
|
||||
bool r = epee::serialization::store_t_to_binary(account, account_data);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet keys");
|
||||
wallet2::keys_file_data keys_file_data = boost::value_initialized<wallet2::keys_file_data>();
|
||||
|
@ -2846,6 +2923,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
|
|||
value2.SetUint(m_subaddress_lookahead_minor);
|
||||
json.AddMember("subaddress_lookahead_minor", value2, json.GetAllocator());
|
||||
|
||||
value2.SetUint(1);
|
||||
json.AddMember("encrypted_secret_keys", value2, json.GetAllocator());
|
||||
|
||||
// Serialize the JSON object
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
|
@ -2853,7 +2933,6 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
|
|||
account_data = buffer.GetString();
|
||||
|
||||
// Encrypt the entire JSON object.
|
||||
crypto::chacha_key key;
|
||||
crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
|
||||
std::string cipher;
|
||||
cipher.resize(account_data.size());
|
||||
|
@ -2871,6 +2950,35 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
|
|||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::setup_keys(const epee::wipeable_string &password)
|
||||
{
|
||||
crypto::chacha_key key;
|
||||
crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
|
||||
|
||||
// re-encrypt, but keep viewkey unencrypted
|
||||
if (m_ask_password && !m_rpc && !m_watch_only)
|
||||
{
|
||||
m_account.encrypt_keys(key);
|
||||
m_account.decrypt_viewkey(key);
|
||||
}
|
||||
|
||||
static_assert(HASH_SIZE == sizeof(crypto::chacha_key), "Mismatched sizes of hash and chacha key");
|
||||
epee::mlocked<tools::scrubbed_arr<char, HASH_SIZE+1>> cache_key_data;
|
||||
memcpy(cache_key_data.data(), &key, HASH_SIZE);
|
||||
cache_key_data[HASH_SIZE] = CACHE_KEY_TAIL;
|
||||
cn_fast_hash(cache_key_data.data(), HASH_SIZE+1, (crypto::hash&)m_cache_key);
|
||||
get_ringdb_key();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password)
|
||||
{
|
||||
if (m_ask_password && !m_rpc && !m_watch_only)
|
||||
decrypt_keys(original_password);
|
||||
setup_keys(new_password);
|
||||
rewrite(filename, new_password);
|
||||
store();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
/*!
|
||||
* \brief Load wallet information from wallet file.
|
||||
* \param keys_file_name Name of wallet file
|
||||
|
@ -2881,6 +2989,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
|
|||
rapidjson::Document json;
|
||||
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);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, keys_file_name);
|
||||
|
||||
|
@ -2926,6 +3035,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_key_on_device = false;
|
||||
encrypted_secret_keys = false;
|
||||
}
|
||||
else if(json.IsObject())
|
||||
{
|
||||
|
@ -3055,6 +3165,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
|
|||
m_subaddress_lookahead_major = field_subaddress_lookahead_major;
|
||||
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR);
|
||||
m_subaddress_lookahead_minor = field_subaddress_lookahead_minor;
|
||||
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, encrypted_secret_keys, uint32_t, Uint, false, false);
|
||||
encrypted_secret_keys = field_encrypted_secret_keys;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3071,12 +3183,39 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
|
|||
m_account.set_device(hwdev);
|
||||
LOG_PRINT_L0("Device inited...");
|
||||
}
|
||||
|
||||
if (r)
|
||||
{
|
||||
if (encrypted_secret_keys)
|
||||
{
|
||||
m_account.decrypt_keys(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
// rewrite with encrypted keys, ignore errors
|
||||
if (m_ask_password && !m_rpc && !m_watch_only)
|
||||
encrypt_keys(key);
|
||||
bool saved_ret = store_keys(keys_file_name, password, m_watch_only);
|
||||
if (!saved_ret)
|
||||
{
|
||||
// just moan a bit, but not fatal
|
||||
MERROR("Error saving keys file with encrypted keys, not fatal");
|
||||
}
|
||||
if (m_ask_password && !m_rpc && !m_watch_only)
|
||||
decrypt_keys(key);
|
||||
m_keys_file_locker.reset();
|
||||
}
|
||||
}
|
||||
const cryptonote::account_keys& keys = m_account.get_keys();
|
||||
hw::device &hwdev = m_account.get_device();
|
||||
r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key);
|
||||
if(!m_watch_only && !m_multisig)
|
||||
r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password);
|
||||
|
||||
if (r)
|
||||
setup_keys(password);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3117,6 +3256,7 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip
|
|||
rapidjson::Document json;
|
||||
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);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, keys_file_name);
|
||||
|
||||
|
@ -3140,19 +3280,50 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip
|
|||
{
|
||||
account_data = std::string(json["key_data"].GetString(), json["key_data"].GetString() +
|
||||
json["key_data"].GetStringLength());
|
||||
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, encrypted_secret_keys, uint32_t, Uint, false, false);
|
||||
encrypted_secret_keys = field_encrypted_secret_keys;
|
||||
}
|
||||
|
||||
cryptonote::account_base account_data_check;
|
||||
|
||||
r = epee::serialization::load_t_from_binary(account_data_check, account_data);
|
||||
const cryptonote::account_keys& keys = account_data_check.get_keys();
|
||||
|
||||
if (encrypted_secret_keys)
|
||||
account_data_check.decrypt_keys(key);
|
||||
|
||||
const cryptonote::account_keys& keys = account_data_check.get_keys();
|
||||
r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key);
|
||||
if(!no_spend_key)
|
||||
r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key);
|
||||
return r;
|
||||
}
|
||||
|
||||
void wallet2::encrypt_keys(const crypto::chacha_key &key)
|
||||
{
|
||||
m_account.encrypt_keys(key);
|
||||
m_account.decrypt_viewkey(key);
|
||||
}
|
||||
|
||||
void wallet2::decrypt_keys(const crypto::chacha_key &key)
|
||||
{
|
||||
m_account.encrypt_viewkey(key);
|
||||
m_account.decrypt_keys(key);
|
||||
}
|
||||
|
||||
void wallet2::encrypt_keys(const epee::wipeable_string &password)
|
||||
{
|
||||
crypto::chacha_key key;
|
||||
crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
|
||||
encrypt_keys(key);
|
||||
}
|
||||
|
||||
void wallet2::decrypt_keys(const epee::wipeable_string &password)
|
||||
{
|
||||
crypto::chacha_key key;
|
||||
crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
|
||||
decrypt_keys(key);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Generates a wallet or restores one.
|
||||
* \param wallet_ Name of wallet file
|
||||
|
@ -3161,7 +3332,7 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip
|
|||
* \param create_address_file Whether to create an address file
|
||||
*/
|
||||
void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
|
||||
const std::string& multisig_data, bool create_address_file)
|
||||
const epee::wipeable_string& multisig_data, bool create_address_file)
|
||||
{
|
||||
clear();
|
||||
prepare_file_names(wallet_);
|
||||
|
@ -3227,6 +3398,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
|
|||
m_multisig_threshold = threshold;
|
||||
m_multisig_signers = multisig_signers;
|
||||
m_key_on_device = false;
|
||||
setup_keys(password);
|
||||
|
||||
if (!wallet_.empty())
|
||||
{
|
||||
|
@ -3281,6 +3453,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
|
|||
m_multisig_threshold = 0;
|
||||
m_multisig_signers.clear();
|
||||
m_key_on_device = false;
|
||||
setup_keys(password);
|
||||
|
||||
// calculate a starting refresh height
|
||||
if(m_refresh_from_block_height == 0 && !recover){
|
||||
|
@ -3382,6 +3555,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
|
|||
m_multisig_threshold = 0;
|
||||
m_multisig_signers.clear();
|
||||
m_key_on_device = false;
|
||||
setup_keys(password);
|
||||
|
||||
if (!wallet_.empty())
|
||||
{
|
||||
|
@ -3435,6 +3609,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
|
|||
m_multisig_threshold = 0;
|
||||
m_multisig_signers.clear();
|
||||
m_key_on_device = false;
|
||||
setup_keys(password);
|
||||
|
||||
if (!wallet_.empty())
|
||||
{
|
||||
|
@ -3481,6 +3656,7 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
|
|||
m_multisig = false;
|
||||
m_multisig_threshold = 0;
|
||||
m_multisig_signers.clear();
|
||||
setup_keys(password);
|
||||
|
||||
if (!wallet_.empty()) {
|
||||
bool r = store_keys(m_keys_file, password, false);
|
||||
|
@ -3520,6 +3696,17 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
|
|||
|
||||
clear();
|
||||
|
||||
// decrypt keys
|
||||
epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
|
||||
if (m_ask_password && !m_rpc && !m_watch_only)
|
||||
{
|
||||
crypto::chacha_key chacha_key;
|
||||
crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
|
||||
m_account.encrypt_viewkey(chacha_key);
|
||||
m_account.decrypt_keys(chacha_key);
|
||||
keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); });
|
||||
}
|
||||
|
||||
MINFO("Creating spend key...");
|
||||
std::vector<crypto::secret_key> multisig_keys;
|
||||
rct::key spend_pkey, spend_skey;
|
||||
|
@ -3580,6 +3767,9 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
|
|||
m_multisig_signers = std::vector<crypto::public_key>(spend_keys.size() + 1, crypto::null_pkey);
|
||||
}
|
||||
|
||||
// re-encrypt keys
|
||||
keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
|
||||
|
||||
if (!m_wallet_file.empty())
|
||||
{
|
||||
bool r = store_keys(m_keys_file, password, false);
|
||||
|
@ -3662,6 +3852,17 @@ bool wallet2::finalize_multisig(const epee::wipeable_string &password, std::unor
|
|||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(!pkeys.empty(), "empty pkeys");
|
||||
|
||||
// keys are decrypted
|
||||
epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
|
||||
if (m_ask_password && !m_rpc && !m_watch_only)
|
||||
{
|
||||
crypto::chacha_key chacha_key;
|
||||
crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
|
||||
m_account.encrypt_viewkey(chacha_key);
|
||||
m_account.decrypt_keys(chacha_key);
|
||||
keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); });
|
||||
}
|
||||
|
||||
// add ours if not included
|
||||
crypto::public_key local_signer;
|
||||
CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(get_account().get_keys().m_spend_secret_key, local_signer),
|
||||
|
@ -3684,6 +3885,9 @@ bool wallet2::finalize_multisig(const epee::wipeable_string &password, std::unor
|
|||
m_multisig_signers = signers;
|
||||
std::sort(m_multisig_signers.begin(), m_multisig_signers.end(), [](const crypto::public_key &e0, const crypto::public_key &e1){ return memcmp(&e0, &e1, sizeof(e0)); });
|
||||
|
||||
// keys are encrypted again
|
||||
keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
|
||||
|
||||
if (!m_wallet_file.empty())
|
||||
{
|
||||
bool r = store_keys(m_keys_file, password, false);
|
||||
|
@ -3995,6 +4199,11 @@ bool wallet2::generate_chacha_key_from_secret_keys(crypto::chacha_key &key) cons
|
|||
return hwdev.generate_chacha_key(m_account.get_keys(), key, m_kdf_rounds);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::generate_chacha_key_from_password(const epee::wipeable_string &pass, crypto::chacha_key &key) const
|
||||
{
|
||||
crypto::generate_chacha_key(pass.data(), pass.size(), key, m_kdf_rounds);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::load(const std::string& wallet_, const epee::wipeable_string& password)
|
||||
{
|
||||
clear();
|
||||
|
@ -4015,6 +4224,8 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
|
|||
LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype));
|
||||
lock_keys_file();
|
||||
|
||||
wallet_keys_unlocker unlocker(*this, m_ask_password && !m_rpc && !m_watch_only, password);
|
||||
|
||||
//keys loaded ok!
|
||||
//try to load wallet file. but even if we failed, it is not big problem
|
||||
if(!boost::filesystem::exists(m_wallet_file, e) || e)
|
||||
|
@ -4036,11 +4247,9 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
|
|||
|
||||
r = ::serialization::parse_binary(buf, cache_file_data);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + m_wallet_file + '\"');
|
||||
crypto::chacha_key key;
|
||||
generate_chacha_key_from_secret_keys(key);
|
||||
std::string cache_data;
|
||||
cache_data.resize(cache_file_data.cache_data.size());
|
||||
crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]);
|
||||
crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), m_cache_key, cache_file_data.iv, &cache_data[0]);
|
||||
|
||||
try {
|
||||
std::stringstream iss;
|
||||
|
@ -4048,11 +4257,13 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
|
|||
boost::archive::portable_binary_iarchive ar(iss);
|
||||
ar >> *this;
|
||||
}
|
||||
catch (...)
|
||||
catch(...)
|
||||
{
|
||||
crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]);
|
||||
try
|
||||
{
|
||||
// try with previous scheme: direct from keys
|
||||
crypto::chacha_key key;
|
||||
generate_chacha_key_from_secret_keys(key);
|
||||
crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]);
|
||||
try {
|
||||
std::stringstream iss;
|
||||
iss << cache_data;
|
||||
boost::archive::portable_binary_iarchive ar(iss);
|
||||
|
@ -4060,13 +4271,24 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
|
|||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_PRINT_L0("Failed to open portable binary, trying unportable");
|
||||
boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists);
|
||||
std::stringstream iss;
|
||||
iss.str("");
|
||||
iss << cache_data;
|
||||
boost::archive::binary_iarchive ar(iss);
|
||||
ar >> *this;
|
||||
crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]);
|
||||
try
|
||||
{
|
||||
std::stringstream iss;
|
||||
iss << cache_data;
|
||||
boost::archive::portable_binary_iarchive ar(iss);
|
||||
ar >> *this;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_PRINT_L0("Failed to open portable binary, trying unportable");
|
||||
boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists);
|
||||
std::stringstream iss;
|
||||
iss.str("");
|
||||
iss << cache_data;
|
||||
boost::archive::binary_iarchive ar(iss);
|
||||
ar >> *this;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4218,12 +4440,10 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
|
|||
|
||||
wallet2::cache_file_data cache_file_data = boost::value_initialized<wallet2::cache_file_data>();
|
||||
cache_file_data.cache_data = oss.str();
|
||||
crypto::chacha_key key;
|
||||
generate_chacha_key_from_secret_keys(key);
|
||||
std::string cipher;
|
||||
cipher.resize(cache_file_data.cache_data.size());
|
||||
cache_file_data.iv = crypto::rand<crypto::chacha_iv>();
|
||||
crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cipher[0]);
|
||||
crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), m_cache_key, cache_file_data.iv, &cipher[0]);
|
||||
cache_file_data.cache_data = cipher;
|
||||
|
||||
const std::string new_file = same_file ? m_wallet_file + ".new" : path;
|
||||
|
@ -5861,12 +6081,6 @@ crypto::chacha_key wallet2::get_ringdb_key()
|
|||
return *m_ringdb_key;
|
||||
}
|
||||
|
||||
void wallet2::clear_ringdb_key()
|
||||
{
|
||||
MINFO("clearing ringdb key");
|
||||
m_ringdb_key = boost::none;
|
||||
}
|
||||
|
||||
bool wallet2::add_rings(const crypto::chacha_key &key, const cryptonote::transaction_prefix &tx)
|
||||
{
|
||||
if (!m_ringdb)
|
||||
|
@ -5877,7 +6091,6 @@ bool wallet2::add_rings(const crypto::chacha_key &key, const cryptonote::transac
|
|||
|
||||
bool wallet2::add_rings(const cryptonote::transaction_prefix &tx)
|
||||
{
|
||||
key_ref kref(*this);
|
||||
try { return add_rings(get_ringdb_key(), tx); }
|
||||
catch (const std::exception &e) { return false; }
|
||||
}
|
||||
|
@ -5886,7 +6099,6 @@ bool wallet2::remove_rings(const cryptonote::transaction_prefix &tx)
|
|||
{
|
||||
if (!m_ringdb)
|
||||
return false;
|
||||
key_ref kref(*this);
|
||||
try { return m_ringdb->remove_rings(get_ringdb_key(), tx); }
|
||||
catch (const std::exception &e) { return false; }
|
||||
}
|
||||
|
@ -5924,7 +6136,6 @@ bool wallet2::get_rings(const crypto::hash &txid, std::vector<std::pair<crypto::
|
|||
|
||||
bool wallet2::get_ring(const crypto::key_image &key_image, std::vector<uint64_t> &outs)
|
||||
{
|
||||
key_ref kref(*this);
|
||||
try { return get_ring(get_ringdb_key(), key_image, outs); }
|
||||
catch (const std::exception &e) { return false; }
|
||||
}
|
||||
|
@ -5934,7 +6145,6 @@ bool wallet2::set_ring(const crypto::key_image &key_image, const std::vector<uin
|
|||
if (!m_ringdb)
|
||||
return false;
|
||||
|
||||
key_ref kref(*this);
|
||||
try { return m_ringdb->set_ring(get_ringdb_key(), key_image, outs, relative); }
|
||||
catch (const std::exception &e) { return false; }
|
||||
}
|
||||
|
@ -5946,7 +6156,6 @@ bool wallet2::find_and_save_rings(bool force)
|
|||
if (!m_ringdb)
|
||||
return false;
|
||||
|
||||
key_ref kref(*this);
|
||||
COMMAND_RPC_GET_TRANSACTIONS::request req = AUTO_VAL_INIT(req);
|
||||
COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res);
|
||||
|
||||
|
@ -10662,6 +10871,7 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs)
|
|||
m_multisig_rescan_info = &info;
|
||||
try
|
||||
{
|
||||
|
||||
refresh(false);
|
||||
}
|
||||
catch (...) {}
|
||||
|
@ -10671,14 +10881,14 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs)
|
|||
return n_outputs;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::string wallet2::encrypt(const std::string &plaintext, const crypto::secret_key &skey, bool authenticated) const
|
||||
std::string wallet2::encrypt(const char *plaintext, size_t len, const crypto::secret_key &skey, bool authenticated) const
|
||||
{
|
||||
crypto::chacha_key key;
|
||||
crypto::generate_chacha_key(&skey, sizeof(skey), key, m_kdf_rounds);
|
||||
std::string ciphertext;
|
||||
crypto::chacha_iv iv = crypto::rand<crypto::chacha_iv>();
|
||||
ciphertext.resize(plaintext.size() + sizeof(iv) + (authenticated ? sizeof(crypto::signature) : 0));
|
||||
crypto::chacha20(plaintext.data(), plaintext.size(), key, iv, &ciphertext[sizeof(iv)]);
|
||||
ciphertext.resize(len + sizeof(iv) + (authenticated ? sizeof(crypto::signature) : 0));
|
||||
crypto::chacha20(plaintext, len, key, iv, &ciphertext[sizeof(iv)]);
|
||||
memcpy(&ciphertext[0], &iv, sizeof(iv));
|
||||
if (authenticated)
|
||||
{
|
||||
|
@ -10692,12 +10902,28 @@ std::string wallet2::encrypt(const std::string &plaintext, const crypto::secret_
|
|||
return ciphertext;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::string wallet2::encrypt(const epee::span<char> &plaintext, const crypto::secret_key &skey, bool authenticated) const
|
||||
{
|
||||
return encrypt(plaintext.data(), plaintext.size(), skey, authenticated);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::string wallet2::encrypt(const std::string &plaintext, const crypto::secret_key &skey, bool authenticated) const
|
||||
{
|
||||
return encrypt(plaintext.data(), plaintext.size(), skey, authenticated);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::string wallet2::encrypt(const epee::wipeable_string &plaintext, const crypto::secret_key &skey, bool authenticated) const
|
||||
{
|
||||
return encrypt(plaintext.data(), plaintext.size(), skey, authenticated);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::string wallet2::encrypt_with_view_secret_key(const std::string &plaintext, bool authenticated) const
|
||||
{
|
||||
return encrypt(plaintext, get_account().get_keys().m_view_secret_key, authenticated);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated) const
|
||||
template<typename T>
|
||||
T wallet2::decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated) const
|
||||
{
|
||||
const size_t prefix_size = sizeof(chacha_iv) + (authenticated ? sizeof(crypto::signature) : 0);
|
||||
THROW_WALLET_EXCEPTION_IF(ciphertext.size() < prefix_size,
|
||||
|
@ -10706,8 +10932,6 @@ std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret
|
|||
crypto::chacha_key key;
|
||||
crypto::generate_chacha_key(&skey, sizeof(skey), key, m_kdf_rounds);
|
||||
const crypto::chacha_iv &iv = *(const crypto::chacha_iv*)&ciphertext[0];
|
||||
std::string plaintext;
|
||||
plaintext.resize(ciphertext.size() - prefix_size);
|
||||
if (authenticated)
|
||||
{
|
||||
crypto::hash hash;
|
||||
|
@ -10718,10 +10942,14 @@ std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret
|
|||
THROW_WALLET_EXCEPTION_IF(!crypto::check_signature(hash, pkey, signature),
|
||||
error::wallet_internal_error, "Failed to authenticate ciphertext");
|
||||
}
|
||||
crypto::chacha20(ciphertext.data() + sizeof(iv), ciphertext.size() - prefix_size, key, iv, &plaintext[0]);
|
||||
return plaintext;
|
||||
std::unique_ptr<char[]> buffer{new char[ciphertext.size() - prefix_size]};
|
||||
auto wiper = epee::misc_utils::create_scope_leave_handler([&]() { memwipe(buffer.get(), ciphertext.size() - prefix_size); });
|
||||
crypto::chacha20(ciphertext.data() + sizeof(iv), ciphertext.size() - prefix_size, key, iv, buffer.get());
|
||||
return T(buffer.get(), ciphertext.size() - prefix_size);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
template epee::wipeable_string wallet2::decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated) const;
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::string wallet2::decrypt_with_view_secret_key(const std::string &ciphertext, bool authenticated) const
|
||||
{
|
||||
return decrypt(ciphertext, get_account().get_keys().m_view_secret_key, authenticated);
|
||||
|
|
|
@ -67,6 +67,19 @@ class Serialization_portability_wallet_Test;
|
|||
namespace tools
|
||||
{
|
||||
class ringdb;
|
||||
class wallet2;
|
||||
|
||||
class wallet_keys_unlocker
|
||||
{
|
||||
public:
|
||||
wallet_keys_unlocker(wallet2 &w, const boost::optional<tools::password_container> &password);
|
||||
wallet_keys_unlocker(wallet2 &w, bool locked, const epee::wipeable_string &password);
|
||||
~wallet_keys_unlocker();
|
||||
private:
|
||||
wallet2 &w;
|
||||
bool locked;
|
||||
crypto::chacha_key key;
|
||||
};
|
||||
|
||||
class i_wallet2_callback
|
||||
{
|
||||
|
@ -77,6 +90,7 @@ namespace tools
|
|||
virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index) {}
|
||||
virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index) {}
|
||||
virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) {}
|
||||
virtual boost::optional<epee::wipeable_string> on_get_password(const char *reason) { return boost::none; }
|
||||
// Light wallet callbacks
|
||||
virtual void on_lw_new_block(uint64_t height) {}
|
||||
virtual void on_lw_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {}
|
||||
|
@ -133,9 +147,11 @@ namespace tools
|
|||
std::deque<crypto::hash> m_blockchain;
|
||||
};
|
||||
|
||||
class wallet_keys_unlocker;
|
||||
class wallet2
|
||||
{
|
||||
friend class ::Serialization_portability_wallet_Test;
|
||||
friend class wallet_keys_unlocker;
|
||||
public:
|
||||
static constexpr const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30);
|
||||
|
||||
|
@ -153,17 +169,17 @@ namespace tools
|
|||
static void init_options(boost::program_options::options_description& desc_params);
|
||||
|
||||
//! Uses stdin and stdout. Returns a wallet2 if no errors.
|
||||
static std::unique_ptr<wallet2> make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
|
||||
static std::unique_ptr<wallet2> make_from_json(const boost::program_options::variables_map& vm, bool rpc, const std::string& json_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
|
||||
|
||||
//! Uses stdin and stdout. Returns a wallet2 and password for `wallet_file` if no errors.
|
||||
static std::pair<std::unique_ptr<wallet2>, password_container>
|
||||
make_from_file(const boost::program_options::variables_map& vm, const std::string& wallet_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
|
||||
make_from_file(const boost::program_options::variables_map& vm, bool rpc, const std::string& wallet_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
|
||||
|
||||
//! Uses stdin and stdout. Returns a wallet2 and password for wallet with no file if no errors.
|
||||
static std::pair<std::unique_ptr<wallet2>, password_container> make_new(const boost::program_options::variables_map& vm, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
|
||||
static std::pair<std::unique_ptr<wallet2>, password_container> make_new(const boost::program_options::variables_map& vm, bool rpc, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
|
||||
|
||||
//! Just parses variables.
|
||||
static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
|
||||
static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm, bool rpc, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
|
||||
|
||||
static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds);
|
||||
|
||||
|
@ -477,16 +493,6 @@ namespace tools
|
|||
std::vector<is_out_data> additional;
|
||||
};
|
||||
|
||||
struct key_ref
|
||||
{
|
||||
key_ref(tools::wallet2 &w): wallet(w) { ++refs; }
|
||||
~key_ref() { if (!--refs) wallet.clear_ringdb_key(); }
|
||||
|
||||
private:
|
||||
tools::wallet2 &wallet;
|
||||
static std::atomic<unsigned int> refs;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Generates a wallet or restores one.
|
||||
* \param wallet_ Name of wallet file
|
||||
|
@ -495,7 +501,7 @@ namespace tools
|
|||
* \param create_address_file Whether to create an address file
|
||||
*/
|
||||
void generate(const std::string& wallet_, const epee::wipeable_string& password,
|
||||
const std::string& multisig_data, bool create_address_file = false);
|
||||
const epee::wipeable_string& multisig_data, bool create_address_file = false);
|
||||
|
||||
/*!
|
||||
* \brief Generates a wallet or restores one.
|
||||
|
@ -613,6 +619,11 @@ namespace tools
|
|||
cryptonote::account_base& get_account(){return m_account;}
|
||||
const cryptonote::account_base& get_account()const{return m_account;}
|
||||
|
||||
void encrypt_keys(const crypto::chacha_key &key);
|
||||
void encrypt_keys(const epee::wipeable_string &password);
|
||||
void decrypt_keys(const crypto::chacha_key &key);
|
||||
void decrypt_keys(const epee::wipeable_string &password);
|
||||
|
||||
void set_refresh_from_block_height(uint64_t height) {m_refresh_from_block_height = height;}
|
||||
uint64_t get_refresh_from_block_height() const {return m_refresh_from_block_height;}
|
||||
|
||||
|
@ -625,7 +636,7 @@ namespace tools
|
|||
// into account the current median block size rather than
|
||||
// the minimum block size.
|
||||
bool deinit();
|
||||
bool init(std::string daemon_address = "http://localhost:8080",
|
||||
bool init(bool rpc, std::string daemon_address = "http://localhost:8080",
|
||||
boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_size_limit = 0, bool ssl = false);
|
||||
|
||||
void stop() { m_run.store(false, std::memory_order_relaxed); }
|
||||
|
@ -637,7 +648,7 @@ namespace tools
|
|||
* \brief Checks if deterministic wallet
|
||||
*/
|
||||
bool is_deterministic() const;
|
||||
bool get_seed(std::string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const;
|
||||
bool get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const;
|
||||
|
||||
/*!
|
||||
* \brief Checks if light wallet. A light wallet sends view key to a server where the blockchain is scanned.
|
||||
|
@ -691,7 +702,7 @@ namespace tools
|
|||
bool multisig(bool *ready = NULL, uint32_t *threshold = NULL, uint32_t *total = NULL) const;
|
||||
bool has_multisig_partial_key_images() const;
|
||||
bool has_unknown_key_images() const;
|
||||
bool get_multisig_seed(std::string& seed, const epee::wipeable_string &passphrase = std::string(), bool raw = true) const;
|
||||
bool get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase = std::string(), bool raw = true) const;
|
||||
bool key_on_device() const { return m_key_on_device; }
|
||||
|
||||
// locked & unlocked balance of given or current subaddress account
|
||||
|
@ -1054,9 +1065,12 @@ namespace tools
|
|||
void update_pool_state(bool refreshed = false);
|
||||
void remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashes);
|
||||
|
||||
std::string encrypt(const char *plaintext, size_t len, const crypto::secret_key &skey, bool authenticated = true) const;
|
||||
std::string encrypt(const epee::span<char> &span, const crypto::secret_key &skey, bool authenticated = true) const;
|
||||
std::string encrypt(const std::string &plaintext, const crypto::secret_key &skey, bool authenticated = true) const;
|
||||
std::string encrypt(const epee::wipeable_string &plaintext, const crypto::secret_key &skey, bool authenticated = true) const;
|
||||
std::string encrypt_with_view_secret_key(const std::string &plaintext, bool authenticated = true) const;
|
||||
std::string decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated = true) const;
|
||||
template<typename T=std::string> T decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated = true) const;
|
||||
std::string decrypt_with_view_secret_key(const std::string &ciphertext, bool authenticated = true) const;
|
||||
|
||||
std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const;
|
||||
|
@ -1074,6 +1088,8 @@ namespace tools
|
|||
uint64_t adjust_mixin(uint64_t mixin) const;
|
||||
uint32_t adjust_priority(uint32_t priority);
|
||||
|
||||
bool is_rpc() const { return m_rpc; }
|
||||
|
||||
// Light wallet specific functions
|
||||
// fetch unspent outs from lw node and store in m_transfers
|
||||
void light_wallet_get_unspent_outs();
|
||||
|
@ -1150,6 +1166,9 @@ namespace tools
|
|||
bool lock_keys_file();
|
||||
bool unlock_keys_file();
|
||||
bool is_keys_file_locked() const;
|
||||
|
||||
void change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password);
|
||||
|
||||
private:
|
||||
/*!
|
||||
* \brief Stores wallet information to wallet file.
|
||||
|
@ -1184,6 +1203,7 @@ namespace tools
|
|||
void generate_genesis(cryptonote::block& b) const;
|
||||
void check_genesis(const crypto::hash& genesis_hash) const; //throws
|
||||
bool generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const;
|
||||
void generate_chacha_key_from_password(const epee::wipeable_string &pass, crypto::chacha_key &key) const;
|
||||
crypto::hash get_payment_id(const pending_tx &ptx) const;
|
||||
void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, tx_scan_info_t &tx_scan_info) const;
|
||||
void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info) const;
|
||||
|
@ -1201,7 +1221,7 @@ namespace tools
|
|||
crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const;
|
||||
bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const;
|
||||
std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const;
|
||||
void scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs) const;
|
||||
void scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs);
|
||||
void trim_hashchain();
|
||||
crypto::key_image get_multisig_composite_key_image(size_t n) const;
|
||||
rct::multisig_kLRki get_multisig_composite_kLRki(size_t n, const crypto::public_key &ignore, std::unordered_set<rct::key> &used_L, std::unordered_set<rct::key> &new_used_L) const;
|
||||
|
@ -1213,8 +1233,7 @@ namespace tools
|
|||
bool remove_rings(const cryptonote::transaction_prefix &tx);
|
||||
bool get_ring(const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector<uint64_t> &outs);
|
||||
crypto::chacha_key get_ringdb_key();
|
||||
void cache_ringdb_key();
|
||||
void clear_ringdb_key();
|
||||
void setup_keys(const epee::wipeable_string &password);
|
||||
|
||||
bool get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution);
|
||||
|
||||
|
@ -1317,6 +1336,11 @@ namespace tools
|
|||
|
||||
uint64_t m_last_block_reward;
|
||||
std::unique_ptr<tools::file_locker> m_keys_file_locker;
|
||||
|
||||
crypto::chacha_key m_cache_key;
|
||||
boost::optional<epee::wipeable_string> m_encrypt_keys_after_refresh;
|
||||
|
||||
bool m_rpc;
|
||||
};
|
||||
}
|
||||
BOOST_CLASS_VERSION(tools::wallet2, 25)
|
||||
|
|
|
@ -53,6 +53,7 @@ namespace tools
|
|||
// wallet_not_initialized
|
||||
// multisig_export_needed
|
||||
// multisig_import_needed
|
||||
// password_needed
|
||||
// std::logic_error
|
||||
// wallet_logic_error *
|
||||
// file_exists
|
||||
|
@ -209,6 +210,14 @@ namespace tools
|
|||
}
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
struct password_needed : public wallet_runtime_error
|
||||
{
|
||||
explicit password_needed(std::string&& loc, const std::string &msg = "Password needed")
|
||||
: wallet_runtime_error(std::move(loc), msg)
|
||||
{
|
||||
}
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
const char* const file_error_messages[] = {
|
||||
"file already exists",
|
||||
"file not found",
|
||||
|
|
|
@ -163,7 +163,7 @@ namespace tools
|
|||
walvars = m_wallet;
|
||||
else
|
||||
{
|
||||
tmpwal = tools::wallet2::make_dummy(*m_vm, password_prompter);
|
||||
tmpwal = tools::wallet2::make_dummy(*m_vm, true, password_prompter);
|
||||
walvars = tmpwal.get();
|
||||
}
|
||||
boost::optional<epee::net_utils::http::login> http_login{};
|
||||
|
@ -1574,11 +1574,13 @@ namespace tools
|
|||
|
||||
if (req.key_type.compare("mnemonic") == 0)
|
||||
{
|
||||
if (!m_wallet->get_seed(res.key))
|
||||
epee::wipeable_string seed;
|
||||
if (!m_wallet->get_seed(seed))
|
||||
{
|
||||
er.message = "The wallet is non-deterministic. Cannot display seed.";
|
||||
return false;
|
||||
}
|
||||
res.key = std::string(seed.data(), seed.size()); // send to the network, then wipe RAM :D
|
||||
}
|
||||
else if(req.key_type.compare("view_key") == 0)
|
||||
{
|
||||
|
@ -2636,7 +2638,7 @@ namespace tools
|
|||
command_line::add_arg(desc, arg_password);
|
||||
po::store(po::parse_command_line(argc, argv, desc), vm2);
|
||||
}
|
||||
std::unique_ptr<tools::wallet2> wal = tools::wallet2::make_new(vm2, nullptr).first;
|
||||
std::unique_ptr<tools::wallet2> wal = tools::wallet2::make_new(vm2, true, nullptr).first;
|
||||
if (!wal)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
|
@ -2710,7 +2712,7 @@ namespace tools
|
|||
}
|
||||
std::unique_ptr<tools::wallet2> wal = nullptr;
|
||||
try {
|
||||
wal = tools::wallet2::make_from_file(vm2, wallet_file, nullptr).first;
|
||||
wal = tools::wallet2::make_from_file(vm2, true, wallet_file, nullptr).first;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
|
@ -3259,13 +3261,13 @@ int main(int argc, char** argv) {
|
|||
LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet..."));
|
||||
if(!wallet_file.empty())
|
||||
{
|
||||
wal = tools::wallet2::make_from_file(*vm, wallet_file, password_prompt).first;
|
||||
wal = tools::wallet2::make_from_file(*vm, true, wallet_file, password_prompt).first;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
wal = tools::wallet2::make_from_json(*vm, from_json, password_prompt);
|
||||
wal = tools::wallet2::make_from_json(*vm, true, from_json, password_prompt);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
|
|
|
@ -138,7 +138,7 @@ bool transactions_flow_test(std::string& working_folder,
|
|||
return false;
|
||||
}
|
||||
|
||||
w1.init(daemon_addr_a);
|
||||
w1.init(true, daemon_addr_a);
|
||||
|
||||
uint64_t blocks_fetched = 0;
|
||||
bool received_money;
|
||||
|
@ -149,7 +149,7 @@ bool transactions_flow_test(std::string& working_folder,
|
|||
return false;
|
||||
}
|
||||
|
||||
w2.init(daemon_addr_b);
|
||||
w2.init(true, daemon_addr_b);
|
||||
|
||||
MGINFO_GREEN("Using wallets: " << ENDL
|
||||
<< "Source: " << w1.get_account().get_public_address_str(MAINNET) << ENDL << "Path: " << working_folder + "/" + path_source_wallet << ENDL
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
set(unit_tests_sources
|
||||
account.cpp
|
||||
apply_permutation.cpp
|
||||
address_from_url.cpp
|
||||
ban.cpp
|
||||
|
@ -52,6 +53,7 @@ set(unit_tests_sources
|
|||
http.cpp
|
||||
main.cpp
|
||||
memwipe.cpp
|
||||
mlocker.cpp
|
||||
mnemonics.cpp
|
||||
mul_div.cpp
|
||||
multisig.cpp
|
||||
|
@ -72,7 +74,8 @@ set(unit_tests_sources
|
|||
ringct.cpp
|
||||
output_selection.cpp
|
||||
vercmp.cpp
|
||||
ringdb.cpp)
|
||||
ringdb.cpp
|
||||
wipeable_string.cpp)
|
||||
|
||||
set(unit_tests_headers
|
||||
unit_tests_utils.h)
|
||||
|
|
71
tests/unit_tests/account.cpp
Normal file
71
tests/unit_tests/account.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
// Copyright (c) 2014-2018, 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 "gtest/gtest.h"
|
||||
|
||||
#include "cryptonote_basic/account.h"
|
||||
|
||||
TEST(account, encrypt_keys)
|
||||
{
|
||||
cryptonote::keypair recovery_key = cryptonote::keypair::generate(hw::get_device("default"));
|
||||
cryptonote::account_base account;
|
||||
crypto::secret_key key = account.generate(recovery_key.sec);
|
||||
const cryptonote::account_keys keys = account.get_keys();
|
||||
|
||||
ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address);
|
||||
ASSERT_EQ(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key);
|
||||
ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key);
|
||||
ASSERT_EQ(account.get_keys().m_multisig_keys, keys.m_multisig_keys);
|
||||
|
||||
crypto::chacha_key chacha_key;
|
||||
crypto::generate_chacha_key(&recovery_key, sizeof(recovery_key), chacha_key, 1);
|
||||
|
||||
account.encrypt_keys(chacha_key);
|
||||
|
||||
ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address);
|
||||
ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key);
|
||||
ASSERT_NE(account.get_keys().m_view_secret_key, keys.m_view_secret_key);
|
||||
|
||||
account.decrypt_viewkey(chacha_key);
|
||||
|
||||
ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address);
|
||||
ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key);
|
||||
ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key);
|
||||
|
||||
account.encrypt_viewkey(chacha_key);
|
||||
|
||||
ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address);
|
||||
ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key);
|
||||
ASSERT_NE(account.get_keys().m_view_secret_key, keys.m_view_secret_key);
|
||||
|
||||
account.decrypt_keys(chacha_key);
|
||||
|
||||
ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address);
|
||||
ASSERT_EQ(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key);
|
||||
ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key);
|
||||
}
|
186
tests/unit_tests/mlocker.cpp
Normal file
186
tests/unit_tests/mlocker.cpp
Normal file
|
@ -0,0 +1,186 @@
|
|||
// Copyright (c) 2018, 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 "gtest/gtest.h"
|
||||
|
||||
#include "misc_log_ex.h"
|
||||
#include "mlocker.h"
|
||||
|
||||
#define BASE(data) (char*)(((uintptr_t)(data.get() + page_size - 1)) / page_size * page_size)
|
||||
|
||||
TEST(mlocker, distinct_1)
|
||||
{
|
||||
const size_t page_size = epee::mlocker::get_page_size();
|
||||
ASSERT_TRUE(page_size > 0);
|
||||
const size_t base_pages = epee::mlocker::get_num_locked_pages();
|
||||
const size_t base_objects = epee::mlocker::get_num_locked_objects();
|
||||
std::unique_ptr<char[]> data{new char[8 * page_size]};
|
||||
epee::mlocker *m0 = new epee::mlocker(BASE(data), 1);
|
||||
epee::mlocker *m1 = new epee::mlocker(BASE(data) + 2 * page_size, 1);
|
||||
epee::mlocker *m2 = new epee::mlocker(BASE(data) + 3 * page_size, 1);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 3);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3);
|
||||
delete m0;
|
||||
delete m1;
|
||||
delete m2;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
|
||||
}
|
||||
|
||||
TEST(mlocker, distinct_full_page)
|
||||
{
|
||||
const size_t page_size = epee::mlocker::get_page_size();
|
||||
ASSERT_TRUE(page_size > 0);
|
||||
const size_t base_pages = epee::mlocker::get_num_locked_pages();
|
||||
const size_t base_objects = epee::mlocker::get_num_locked_objects();
|
||||
std::unique_ptr<char[]> data{new char[8 * page_size]};
|
||||
epee::mlocker *m0 = new epee::mlocker(BASE(data), page_size);
|
||||
epee::mlocker *m1 = new epee::mlocker(BASE(data) + 2 * page_size, page_size);
|
||||
epee::mlocker *m2 = new epee::mlocker(BASE(data) + 3 * page_size, page_size);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 3);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3);
|
||||
delete m0;
|
||||
delete m1;
|
||||
delete m2;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
|
||||
}
|
||||
|
||||
TEST(mlocker, identical)
|
||||
{
|
||||
const size_t page_size = epee::mlocker::get_page_size();
|
||||
ASSERT_TRUE(page_size >= 32);
|
||||
const size_t base_pages = epee::mlocker::get_num_locked_pages();
|
||||
const size_t base_objects = epee::mlocker::get_num_locked_objects();
|
||||
std::unique_ptr<char[]> data{new char[8 * page_size]};
|
||||
epee::mlocker *m0 = new epee::mlocker(BASE(data) + page_size, 32);
|
||||
epee::mlocker *m1 = new epee::mlocker(BASE(data) + page_size, 32);
|
||||
epee::mlocker *m2 = new epee::mlocker(BASE(data) + page_size, 32);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3);
|
||||
delete m1;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2);
|
||||
delete m0;
|
||||
delete m2;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
|
||||
}
|
||||
|
||||
TEST(mlocker, overlapping_small)
|
||||
{
|
||||
const size_t page_size = epee::mlocker::get_page_size();
|
||||
ASSERT_TRUE(page_size >= 64);
|
||||
const size_t base_pages = epee::mlocker::get_num_locked_pages();
|
||||
const size_t base_objects = epee::mlocker::get_num_locked_objects();
|
||||
std::unique_ptr<char[]> data{new char[8 * page_size]};
|
||||
epee::mlocker *m0 = new epee::mlocker(BASE(data), 32);
|
||||
epee::mlocker *m1 = new epee::mlocker(BASE(data) + 16, 32);
|
||||
epee::mlocker *m2 = new epee::mlocker(BASE(data) + 8, 32);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3);
|
||||
delete m1;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2);
|
||||
delete m2;
|
||||
delete m0;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
|
||||
}
|
||||
|
||||
TEST(mlocker, multi_page)
|
||||
{
|
||||
const size_t page_size = epee::mlocker::get_page_size();
|
||||
ASSERT_TRUE(page_size > 0);
|
||||
const size_t base_pages = epee::mlocker::get_num_locked_pages();
|
||||
const size_t base_objects = epee::mlocker::get_num_locked_objects();
|
||||
std::unique_ptr<char[]> data{new char[8 * page_size]};
|
||||
epee::mlocker *m0 = new epee::mlocker(BASE(data) + page_size, page_size * 3);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 3);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1);
|
||||
epee::mlocker *m1 = new epee::mlocker(BASE(data) + page_size * 7, page_size);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 4);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2);
|
||||
delete m0;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1);
|
||||
delete m1;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
|
||||
}
|
||||
|
||||
TEST(mlocker, cross_page)
|
||||
{
|
||||
const size_t page_size = epee::mlocker::get_page_size();
|
||||
ASSERT_TRUE(page_size > 32);
|
||||
const size_t base_pages = epee::mlocker::get_num_locked_pages();
|
||||
const size_t base_objects = epee::mlocker::get_num_locked_objects();
|
||||
std::unique_ptr<char[]> data{new char[2 * page_size]};
|
||||
epee::mlocker *m0 = new epee::mlocker(BASE(data) + page_size - 1, 2);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 2);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1);
|
||||
delete m0;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
|
||||
}
|
||||
|
||||
TEST(mlocker, redundant)
|
||||
{
|
||||
const size_t page_size = epee::mlocker::get_page_size();
|
||||
const size_t base_pages = epee::mlocker::get_num_locked_pages();
|
||||
const size_t base_objects = epee::mlocker::get_num_locked_objects();
|
||||
std::unique_ptr<char[]> data{new char[2 * page_size]};
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
|
||||
epee::mlocker *m0 = new epee::mlocker(BASE(data), 32);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1);
|
||||
epee::mlocker *m1 = new epee::mlocker(BASE(data), 32);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2);
|
||||
delete m1;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1);
|
||||
delete m0;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
|
||||
}
|
||||
|
||||
TEST(mlocker, mlocked)
|
||||
{
|
||||
const size_t base_pages = epee::mlocker::get_num_locked_pages();
|
||||
const size_t base_objects = epee::mlocker::get_num_locked_objects();
|
||||
{
|
||||
struct Foo { uint64_t u; };
|
||||
epee::mlocked<Foo> l;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1);
|
||||
}
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
|
||||
}
|
|
@ -27,6 +27,8 @@
|
|||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "wipeable_string.h"
|
||||
#include "mnemonics/language_base.h"
|
||||
#include "mnemonics/electrum-words.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include <stdlib.h>
|
||||
|
@ -74,14 +76,16 @@ namespace
|
|||
void test_language(const Language::Base &language)
|
||||
{
|
||||
const std::vector<std::string> &word_list = language.get_word_list();
|
||||
std::string seed = "", return_seed = "";
|
||||
epee::wipeable_string w_seed = "", w_return_seed = "";
|
||||
std::string seed, return_seed;
|
||||
// Generate a random seed without checksum
|
||||
crypto::secret_key randkey;
|
||||
for (size_t ii = 0; ii < sizeof(randkey); ++ii)
|
||||
{
|
||||
randkey.data[ii] = rand();
|
||||
}
|
||||
crypto::ElectrumWords::bytes_to_words(randkey, seed, language.get_language_name());
|
||||
crypto::ElectrumWords::bytes_to_words(randkey, w_seed, language.get_language_name());
|
||||
seed = std::string(w_seed.data(), w_seed.size());
|
||||
// remove the checksum word
|
||||
const char *space = strrchr(seed.c_str(), ' ');
|
||||
ASSERT_TRUE(space != NULL);
|
||||
|
@ -103,7 +107,8 @@ namespace
|
|||
ASSERT_STREQ(language.get_language_name().c_str(), language_name.c_str());
|
||||
|
||||
// Convert the secret key back to seed
|
||||
crypto::ElectrumWords::bytes_to_words(key, return_seed, language.get_language_name());
|
||||
crypto::ElectrumWords::bytes_to_words(key, w_return_seed, language.get_language_name());
|
||||
return_seed = std::string(w_return_seed.data(), w_return_seed.size());
|
||||
ASSERT_EQ(true, res);
|
||||
std::cout << "Returned seed:\n";
|
||||
std::cout << return_seed << std::endl;
|
||||
|
@ -126,8 +131,9 @@ namespace
|
|||
std::cout << "Detected language: " << language_name << std::endl;
|
||||
ASSERT_STREQ(language.get_language_name().c_str(), language_name.c_str());
|
||||
|
||||
return_seed = "";
|
||||
crypto::ElectrumWords::bytes_to_words(key, return_seed, language.get_language_name());
|
||||
w_return_seed = "";
|
||||
crypto::ElectrumWords::bytes_to_words(key, w_return_seed, language.get_language_name());
|
||||
return_seed = std::string(w_return_seed.data(), w_return_seed.size());
|
||||
ASSERT_EQ(true, res);
|
||||
std::cout << "Returned seed:\n";
|
||||
std::cout << return_seed << std::endl;
|
||||
|
@ -202,3 +208,17 @@ TEST(mnemonics, language_detection_with_bad_checksum)
|
|||
ASSERT_EQ(true, res);
|
||||
ASSERT_STREQ(language_name.c_str(), "Português");
|
||||
}
|
||||
|
||||
TEST(mnemonics, utf8prefix)
|
||||
{
|
||||
ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 0) == "");
|
||||
ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 1) == "f");
|
||||
ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 2) == "fo");
|
||||
ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 3) == "foo");
|
||||
ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 4) == "foo");
|
||||
ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 0) == "");
|
||||
ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 1) == "æ");
|
||||
ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 2) == "æo");
|
||||
ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 3) == "æon");
|
||||
ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 4) == "æon");
|
||||
}
|
||||
|
|
|
@ -61,10 +61,13 @@ static void make_wallet(unsigned int idx, tools::wallet2 &wallet)
|
|||
|
||||
try
|
||||
{
|
||||
wallet.init("");
|
||||
wallet.init(false, "");
|
||||
wallet.set_subaddress_lookahead(1, 1);
|
||||
wallet.generate("", "", spendkey, true, false);
|
||||
ASSERT_TRUE(test_addresses[idx].address == wallet.get_account().get_public_address_str(cryptonote::TESTNET));
|
||||
wallet.decrypt_keys("");
|
||||
ASSERT_TRUE(test_addresses[idx].spendkey == epee::string_tools::pod_to_hex(wallet.get_account().get_keys().m_spend_secret_key));
|
||||
wallet.encrypt_keys("");
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
|
@ -83,8 +86,12 @@ static void make_M_2_wallet(tools::wallet2 &wallet0, tools::wallet2 &wallet1, un
|
|||
std::vector<crypto::secret_key> sk0(1), sk1(1);
|
||||
std::vector<crypto::public_key> pk0(1), pk1(1);
|
||||
|
||||
wallet0.decrypt_keys("");
|
||||
std::string mi0 = wallet0.get_multisig_info();
|
||||
wallet0.encrypt_keys("");
|
||||
wallet1.decrypt_keys("");
|
||||
std::string mi1 = wallet1.get_multisig_info();
|
||||
wallet1.encrypt_keys("");
|
||||
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi1, sk0[0], pk0[0]));
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi0, sk1[0], pk1[0]));
|
||||
|
@ -118,9 +125,15 @@ static void make_M_3_wallet(tools::wallet2 &wallet0, tools::wallet2 &wallet1, to
|
|||
std::vector<crypto::secret_key> sk0(2), sk1(2), sk2(2);
|
||||
std::vector<crypto::public_key> pk0(2), pk1(2), pk2(2);
|
||||
|
||||
wallet0.decrypt_keys("");
|
||||
std::string mi0 = wallet0.get_multisig_info();
|
||||
wallet0.encrypt_keys("");
|
||||
wallet1.decrypt_keys("");
|
||||
std::string mi1 = wallet1.get_multisig_info();
|
||||
wallet1.encrypt_keys("");
|
||||
wallet2.decrypt_keys("");
|
||||
std::string mi2 = wallet2.get_multisig_info();
|
||||
wallet2.encrypt_keys("");
|
||||
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi1, sk0[0], pk0[0]));
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi2, sk0[1], pk0[1]));
|
||||
|
|
|
@ -47,7 +47,7 @@ static crypto::chacha_key generate_chacha_key()
|
|||
{
|
||||
crypto::chacha_key chacha_key;
|
||||
uint64_t password = crypto::rand<uint64_t>();
|
||||
crypto::generate_chacha_key(std::string((const char*)&password, sizeof(password)), chacha_key);
|
||||
crypto::generate_chacha_key(std::string((const char*)&password, sizeof(password)), chacha_key, 1);
|
||||
return chacha_key;
|
||||
}
|
||||
|
||||
|
|
204
tests/unit_tests/wipeable_string.cpp
Normal file
204
tests/unit_tests/wipeable_string.cpp
Normal file
|
@ -0,0 +1,204 @@
|
|||
// Copyright (c) 2018, 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 <boost/optional/optional.hpp>
|
||||
#include <string.h>
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "misc_log_ex.h"
|
||||
#include "wipeable_string.h"
|
||||
|
||||
TEST(wipeable_string, ctor)
|
||||
{
|
||||
epee::wipeable_string s0;
|
||||
ASSERT_EQ(s0.size(), 0);
|
||||
|
||||
epee::wipeable_string s1(std::string("foo"));
|
||||
ASSERT_EQ(s1.size(), 3);
|
||||
ASSERT_TRUE(!memcmp(s1.data(), "foo", s1.size()));
|
||||
|
||||
epee::wipeable_string s2(std::string("bar"));
|
||||
ASSERT_EQ(s2.size(), 3);
|
||||
ASSERT_TRUE(!memcmp(s2.data(), "bar", s2.size()));
|
||||
|
||||
epee::wipeable_string s3(std::string("quux"));
|
||||
ASSERT_EQ(s3.size(), 4);
|
||||
ASSERT_TRUE(!memcmp(s3.data(), "quux", s3.size()));
|
||||
}
|
||||
|
||||
TEST(wipeable_string, wipe)
|
||||
{
|
||||
epee::wipeable_string s0(std::string("foo"));
|
||||
ASSERT_EQ(s0.size(), 3);
|
||||
s0.wipe();
|
||||
ASSERT_EQ(s0.size(), 3);
|
||||
ASSERT_TRUE(!memcmp(s0.data(), "\0\0\0", 3));
|
||||
}
|
||||
|
||||
TEST(wipeable_string, clear)
|
||||
{
|
||||
epee::wipeable_string s0(std::string("foo"));
|
||||
ASSERT_EQ(s0.size(), 3);
|
||||
s0.clear();
|
||||
ASSERT_EQ(s0.size(), 0);
|
||||
}
|
||||
|
||||
TEST(wipeable_string, push_back)
|
||||
{
|
||||
epee::wipeable_string s0(std::string("fo"));
|
||||
ASSERT_EQ(s0.size(), 2);
|
||||
s0.push_back('o');
|
||||
ASSERT_EQ(s0.size(), 3);
|
||||
ASSERT_TRUE(!memcmp(s0.data(), "foo", s0.size()));
|
||||
}
|
||||
|
||||
TEST(wipeable_string, append_char)
|
||||
{
|
||||
epee::wipeable_string s0(std::string("fo"));
|
||||
ASSERT_EQ(s0.size(), 2);
|
||||
s0 += 'o';
|
||||
ASSERT_EQ(s0.size(), 3);
|
||||
ASSERT_TRUE(!memcmp(s0.data(), "foo", s0.size()));
|
||||
}
|
||||
|
||||
TEST(wipeable_string, append_string)
|
||||
{
|
||||
epee::wipeable_string s0(std::string("foo"));
|
||||
ASSERT_EQ(s0.size(), 3);
|
||||
s0 += "bar";
|
||||
ASSERT_EQ(s0.size(), 6);
|
||||
ASSERT_TRUE(!memcmp(s0.data(), "foobar", s0.size()));
|
||||
}
|
||||
|
||||
TEST(wipeable_string, empty)
|
||||
{
|
||||
epee::wipeable_string s0;
|
||||
ASSERT_TRUE(s0.empty());
|
||||
s0.push_back(' ');
|
||||
ASSERT_FALSE(s0.empty());
|
||||
ASSERT_EQ(s0.pop_back(), ' ');
|
||||
ASSERT_TRUE(s0.empty());
|
||||
}
|
||||
|
||||
TEST(wipeable_string, pop_back)
|
||||
{
|
||||
epee::wipeable_string s = "test";
|
||||
ASSERT_EQ(s.size(), 4);
|
||||
ASSERT_EQ(s.pop_back(), 't');
|
||||
ASSERT_EQ(s.size(), 3);
|
||||
ASSERT_TRUE(!memcmp(s.data(), "tes", s.size()));
|
||||
}
|
||||
|
||||
TEST(wipeable_string, equal)
|
||||
{
|
||||
epee::wipeable_string s0 = "foo";
|
||||
epee::wipeable_string s1 = "bar";
|
||||
epee::wipeable_string s0_2 = "foo";
|
||||
ASSERT_TRUE(s0 == s0);
|
||||
ASSERT_TRUE(s0 == s0_2);
|
||||
ASSERT_TRUE(s1 == s1);
|
||||
ASSERT_FALSE(s1 == s0);
|
||||
ASSERT_FALSE(s1 == s0_2);
|
||||
}
|
||||
|
||||
TEST(wipeable_string, not_equal)
|
||||
{
|
||||
epee::wipeable_string s0 = "foo";
|
||||
epee::wipeable_string s1 = "bar";
|
||||
epee::wipeable_string s0_2 = "foo";
|
||||
ASSERT_FALSE(s0 != s0);
|
||||
ASSERT_FALSE(s0 != s0_2);
|
||||
ASSERT_FALSE(s1 != s1);
|
||||
ASSERT_TRUE(s1 != s0);
|
||||
ASSERT_TRUE(s1 != s0_2);
|
||||
}
|
||||
|
||||
static epee::wipeable_string trimmed(const char *s)
|
||||
{
|
||||
epee::wipeable_string str(s);
|
||||
str.trim();
|
||||
return str;
|
||||
}
|
||||
|
||||
TEST(wipeable_string, trim)
|
||||
{
|
||||
ASSERT_TRUE(trimmed("") == "");
|
||||
ASSERT_TRUE(trimmed(" ") == "");
|
||||
ASSERT_TRUE(trimmed(" ") == "");
|
||||
ASSERT_TRUE(trimmed("a") == "a");
|
||||
ASSERT_TRUE(trimmed(" a") == "a");
|
||||
ASSERT_TRUE(trimmed(" a") == "a");
|
||||
ASSERT_TRUE(trimmed("a ") == "a");
|
||||
ASSERT_TRUE(trimmed("a ") == "a");
|
||||
ASSERT_TRUE(trimmed(" a ") == "a");
|
||||
ASSERT_TRUE(trimmed(" a ") == "a");
|
||||
ASSERT_TRUE(trimmed(" ab ") == "ab");
|
||||
ASSERT_TRUE(trimmed(" a b ") == "a b");
|
||||
ASSERT_TRUE(trimmed(" a b ") == "a b");
|
||||
}
|
||||
|
||||
static bool check_split(const char *s, const std::vector<epee::wipeable_string> &v)
|
||||
{
|
||||
epee::wipeable_string str(s);
|
||||
std::vector<epee::wipeable_string> fields;
|
||||
str.split(fields);
|
||||
return v == fields;
|
||||
}
|
||||
|
||||
TEST(wipeable_string, split)
|
||||
{
|
||||
ASSERT_TRUE(check_split("", {}));
|
||||
ASSERT_TRUE(check_split("foo", {"foo"}));
|
||||
ASSERT_TRUE(check_split(" foo ", {"foo"}));
|
||||
ASSERT_TRUE(check_split("foo bar", {"foo", "bar"}));
|
||||
ASSERT_TRUE(check_split("foo bar", {"foo", "bar"}));
|
||||
ASSERT_TRUE(check_split("foo bar baz", {"foo", "bar", "baz"}));
|
||||
ASSERT_TRUE(check_split(" foo bar baz ", {"foo", "bar", "baz"}));
|
||||
ASSERT_TRUE(check_split(" foo bar baz", {"foo", "bar", "baz"}));
|
||||
ASSERT_TRUE(check_split("foo bar baz ", {"foo", "bar", "baz"}));
|
||||
}
|
||||
|
||||
TEST(wipeable_string, parse_hexstr)
|
||||
{
|
||||
boost::optional<epee::wipeable_string> s;
|
||||
|
||||
ASSERT_EQ(boost::none, epee::wipeable_string("x").parse_hexstr());
|
||||
ASSERT_EQ(boost::none, epee::wipeable_string("x0000000000000000").parse_hexstr());
|
||||
ASSERT_EQ(boost::none, epee::wipeable_string("0000000000000000x").parse_hexstr());
|
||||
ASSERT_EQ(boost::none, epee::wipeable_string("0").parse_hexstr());
|
||||
ASSERT_EQ(boost::none, epee::wipeable_string("000").parse_hexstr());
|
||||
|
||||
ASSERT_TRUE((s = epee::wipeable_string("").parse_hexstr()));
|
||||
ASSERT_EQ(*s, "");
|
||||
ASSERT_TRUE((s = epee::wipeable_string("00").parse_hexstr()));
|
||||
ASSERT_EQ(*s, epee::wipeable_string("", 1));
|
||||
ASSERT_TRUE((s = epee::wipeable_string("41").parse_hexstr()));
|
||||
ASSERT_EQ(*s, epee::wipeable_string("A"));
|
||||
ASSERT_TRUE((s = epee::wipeable_string("414243").parse_hexstr()));
|
||||
ASSERT_EQ(*s, epee::wipeable_string("ABC"));
|
||||
}
|
Loading…
Reference in a new issue