Files
monero_c/patches/wownero/0009-polyseed.patch
T
cyan b4c3fbacab Wownero 0.11.3.0 (#105)
* update wownero to 0.11.3.0

* Updated wownero, cleaned up patches

* unshallow zano

* Fix ios builds

* Update checksum
2025-01-15 14:04:42 +01:00

1465 lines
56 KiB
Diff

From d146c6c1e23e1bf3762449e72a3aab847b3c0412 Mon Sep 17 00:00:00 2001
From: tobtoht <tob@featherwallet.org>
Date: Tue, 12 Mar 2024 09:42:37 +0100
Subject: [PATCH 09/15] polyseed
Co-authored-by: Czarek Nakamoto <cyjan@mrcyjanek.net>
---
.gitmodules | 6 +
CMakeLists.txt | 4 +-
contrib/depends/hosts/darwin.mk | 2 +
contrib/depends/hosts/linux.mk | 8 +-
contrib/depends/packages/packages.mk | 2 +-
contrib/depends/packages/polyseed.mk | 28 +++
contrib/depends/packages/sodium.mk | 2 +-
.../polyseed/0001-disable-soname.patch | 48 +++++
.../patches/polyseed/force-static-mingw.patch | 23 +++
contrib/epee/include/wipeable_string.h | 7 +
contrib/epee/src/wipeable_string.cpp | 10 +
external/CMakeLists.txt | 2 +
external/polyseed | 1 +
external/utf8proc | 1 +
src/CMakeLists.txt | 1 +
src/cryptonote_basic/CMakeLists.txt | 1 +
src/cryptonote_basic/account.cpp | 23 ++-
src/cryptonote_basic/account.h | 6 +
src/cryptonote_config.h | 2 +
src/polyseed/CMakeLists.txt | 25 +++
src/polyseed/pbkdf2.c | 85 ++++++++
src/polyseed/pbkdf2.h | 46 +++++
src/polyseed/polyseed.cpp | 182 ++++++++++++++++++
src/polyseed/polyseed.hpp | 167 ++++++++++++++++
src/wallet/api/wallet.cpp | 70 +++++++
src/wallet/api/wallet.h | 10 +
src/wallet/api/wallet2_api.h | 25 +++
src/wallet/api/wallet_manager.cpp | 9 +
src/wallet/api/wallet_manager.h | 10 +
src/wallet/wallet2.cpp | 99 ++++++++--
src/wallet/wallet2.h | 30 ++-
31 files changed, 912 insertions(+), 23 deletions(-)
create mode 100644 contrib/depends/packages/polyseed.mk
create mode 100644 contrib/depends/patches/polyseed/0001-disable-soname.patch
create mode 100644 contrib/depends/patches/polyseed/force-static-mingw.patch
create mode 160000 external/polyseed
create mode 160000 external/utf8proc
create mode 100644 src/polyseed/CMakeLists.txt
create mode 100644 src/polyseed/pbkdf2.c
create mode 100644 src/polyseed/pbkdf2.h
create mode 100644 src/polyseed/polyseed.cpp
create mode 100644 src/polyseed/polyseed.hpp
diff --git a/.gitmodules b/.gitmodules
index b24855d9b..589676649 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -20,3 +20,9 @@
path = external/bc-ur
url = https://github.com/MrCyjaneK/bc-ur
branch = misc
+[submodule "external/utf8proc"]
+ path = external/utf8proc
+ url = https://github.com/JuliaStrings/utf8proc.git
+[submodule "external/polyseed"]
+ path = external/polyseed
+ url = https://github.com/tevador/polyseed.git
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 86000f811..96056e324 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -372,6 +372,8 @@ if(NOT MANUAL_SUBMODULES)
#check_submodule(external/trezor-common)
check_submodule(external/randomwow)
check_submodule(external/supercop)
+ check_submodule(external/polyseed)
+ check_submodule(external/utf8proc)
endif()
endif()
@@ -461,7 +463,7 @@ endif()
# elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*")
# set(BSDI TRUE)
-include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include)
+include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include external/polyseed/include external/utf8proc)
if(APPLE)
cmake_policy(SET CMP0042 NEW)
diff --git a/contrib/depends/hosts/darwin.mk b/contrib/depends/hosts/darwin.mk
index 83d83036b..b14ee5c5b 100644
--- a/contrib/depends/hosts/darwin.mk
+++ b/contrib/depends/hosts/darwin.mk
@@ -8,6 +8,8 @@ endif
darwin_CC=clang -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(host_prefix)/native/SDK/ -mlinker-version=$(LD64_VERSION) -B$(host_prefix)/native/bin/$(host)-
darwin_CXX=clang++ -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(host_prefix)/native/SDK/ -mlinker-version=$(LD64_VERSION) -stdlib=libc++ -B$(host_prefix)/native/bin/$(host)-
+darwin_RANLIB=$(host_prefix)/native/bin/$(host)-ranlib
+
darwin_CFLAGS=-pipe
darwin_CXXFLAGS=$(darwin_CFLAGS)
darwin_ARFLAGS=cr
diff --git a/contrib/depends/hosts/linux.mk b/contrib/depends/hosts/linux.mk
index 912fdb03c..b79799f30 100644
--- a/contrib/depends/hosts/linux.mk
+++ b/contrib/depends/hosts/linux.mk
@@ -11,15 +11,15 @@ linux_debug_CXXFLAGS=$(linux_debug_CFLAGS)
linux_debug_CPPFLAGS=-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC
ifeq (86,$(findstring 86,$(build_arch)))
-i686_linux_CC=gcc -m32
-i686_linux_CXX=g++ -m32
+i686_linux_CC=i686-linux-gnu-gcc
+i686_linux_CXX=i686-linux-gnu-g++
i686_linux_AR=ar
i686_linux_RANLIB=ranlib
i686_linux_NM=nm
i686_linux_STRIP=strip
-x86_64_linux_CC=gcc -m64
-x86_64_linux_CXX=g++ -m64
+x86_64_linux_CC=x86_64-linux-gnu-gcc
+x86_64_linux_CXX=x86_64-linux-gnu-g++
x86_64_linux_AR=ar
x86_64_linux_RANLIB=ranlib
x86_64_linux_NM=nm
diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk
index d2d1eca85..8783d4955 100644
--- a/contrib/depends/packages/packages.mk
+++ b/contrib/depends/packages/packages.mk
@@ -1,4 +1,4 @@
-packages:=boost openssl zeromq libiconv expat unbound
+packages:=boost openssl zeromq libiconv expat unbound polyseed
# ccache is useless in gitian builds
ifneq ($(GITIAN),1)
diff --git a/contrib/depends/packages/polyseed.mk b/contrib/depends/packages/polyseed.mk
new file mode 100644
index 000000000..0071b20f3
--- /dev/null
+++ b/contrib/depends/packages/polyseed.mk
@@ -0,0 +1,28 @@
+package=polyseed
+$(package)_version=2.0.0
+$(package)_download_path=https://github.com/tevador/$(package)/archive/refs/tags/
+$(package)_download_file=v$($(package)_version).tar.gz
+$(package)_file_name=$(package)-$($(package)_version).tar.gz
+$(package)_sha256_hash=f36282fcbcd68d32461b8230c89e1a40661bd46b91109681cec637433004135a
+$(package)_patches=force-static-mingw.patch 0001-disable-soname.patch
+
+define $(package)_preprocess_cmds
+ patch -p1 < $($(package)_patch_dir)/force-static-mingw.patch &&\
+ patch -p1 < $($(package)_patch_dir)/0001-disable-soname.patch
+endef
+
+define $(package)_config_cmds
+ CC="$($(package)_cc)" cmake -DCMAKE_INSTALL_PREFIX="$(host_prefix)" .
+endef
+
+define $(package)_set_vars
+ $(package)_build_opts=CC="$($(package)_cc)"
+endef
+
+define $(package)_build_cmds
+ CC="$($(package)_cc)" $(MAKE)
+endef
+
+define $(package)_stage_cmds
+ $(MAKE) DESTDIR=$($(package)_staging_dir) install
+endef
diff --git a/contrib/depends/packages/sodium.mk b/contrib/depends/packages/sodium.mk
index 87b34599e..68a5b48ba 100644
--- a/contrib/depends/packages/sodium.mk
+++ b/contrib/depends/packages/sodium.mk
@@ -6,7 +6,7 @@ $(package)_sha256_hash=6f504490b342a4f8a4c4a02fc9b866cbef8622d5df4e5452b46be121e
$(package)_patches=disable-glibc-getrandom-getentropy.patch fix-whitespace.patch
define $(package)_set_vars
-$(package)_config_opts=--enable-static --disable-shared
+$(package)_config_opts=--enable-static --disable-shared --with-pic
$(package)_config_opts+=--prefix=$(host_prefix)
endef
diff --git a/contrib/depends/patches/polyseed/0001-disable-soname.patch b/contrib/depends/patches/polyseed/0001-disable-soname.patch
new file mode 100644
index 000000000..bd97dd394
--- /dev/null
+++ b/contrib/depends/patches/polyseed/0001-disable-soname.patch
@@ -0,0 +1,48 @@
+From aabafcfc0572651436d024a635483c49042fad7f Mon Sep 17 00:00:00 2001
+From: Czarek Nakamoto <cyjan@mrcyjanek.net>
+Date: Thu, 28 Mar 2024 00:32:51 +0100
+Subject: [PATCH] disable soname
+
+---
+ CMakeLists.txt | 16 +++++++++-------
+ 1 file changed, 9 insertions(+), 7 deletions(-)
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 8a8e7c2..5301353 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -36,6 +36,7 @@ include_directories(polyseed
+ target_compile_definitions(polyseed PRIVATE POLYSEED_SHARED)
+ set_target_properties(polyseed PROPERTIES VERSION 2.0.0
+ SOVERSION 2
++ NO_SONAME 1
+ C_STANDARD 11
+ C_STANDARD_REQUIRED ON)
+
+@@ -45,16 +46,17 @@ include_directories(polyseed_static
+ include/)
+ target_compile_definitions(polyseed_static PRIVATE POLYSEED_STATIC)
+ set_target_properties(polyseed_static PROPERTIES OUTPUT_NAME polyseed
++ NO_SONAME 1
+ C_STANDARD 11
+ C_STANDARD_REQUIRED ON)
+
+-add_executable(polyseed-tests
+- tests/tests.c)
+-include_directories(polyseed-tests
+- include/)
+-target_compile_definitions(polyseed-tests PRIVATE POLYSEED_STATIC)
+-target_link_libraries(polyseed-tests
+- PRIVATE polyseed_static)
++# add_executable(polyseed-tests
++# tests/tests.c)
++# include_directories(polyseed-tests
++# include/)
++# target_compile_definitions(polyseed-tests PRIVATE POLYSEED_STATIC)
++# target_link_libraries(polyseed-tests
++# PRIVATE polyseed_static)
+
+ include(GNUInstallDirs)
+ install(TARGETS polyseed polyseed_static
+--
+2.39.2
diff --git a/contrib/depends/patches/polyseed/force-static-mingw.patch b/contrib/depends/patches/polyseed/force-static-mingw.patch
new file mode 100644
index 000000000..f05cb2b6a
--- /dev/null
+++ b/contrib/depends/patches/polyseed/force-static-mingw.patch
@@ -0,0 +1,23 @@
+--- a/include/polyseed.h
++++ b/include/polyseed.h
+@@ -93,13 +93,13 @@ Shared/static library definitions
+ - define POLYSEED_STATIC when linking to the static library
+ */
+ #if defined(_WIN32) || defined(__CYGWIN__)
+- #ifdef POLYSEED_SHARED
+- #define POLYSEED_API __declspec(dllexport)
+- #elif !defined(POLYSEED_STATIC)
+- #define POLYSEED_API __declspec(dllimport)
+- #else
+- #define POLYSEED_API
+- #endif
++// #ifdef POLYSEED_SHARED
++// #define POLYSEED_API __declspec(dllexport)
++// #elif !defined(POLYSEED_STATIC)
++// #define POLYSEED_API __declspec(dllimport)
++// #else
++ #define POLYSEED_API
++// #endif
+ #define POLYSEED_PRIVATE
+ #else
+ #ifdef POLYSEED_SHARED
diff --git a/contrib/epee/include/wipeable_string.h b/contrib/epee/include/wipeable_string.h
index 65977cd97..594e15de4 100644
--- a/contrib/epee/include/wipeable_string.h
+++ b/contrib/epee/include/wipeable_string.h
@@ -34,6 +34,7 @@
#include <string>
#include "memwipe.h"
#include "fnv1.h"
+#include "serialization/keyvalue_serialization.h"
namespace epee
{
@@ -75,6 +76,12 @@ namespace epee
bool operator!=(const wipeable_string &other) const noexcept { return buffer != other.buffer; }
wipeable_string &operator=(wipeable_string &&other);
wipeable_string &operator=(const wipeable_string &other);
+ char& operator[](size_t idx);
+ const char& operator[](size_t idx) const;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_CONTAINER_POD_AS_BLOB(buffer)
+ END_KV_SERIALIZE_MAP()
private:
void grow(size_t sz, size_t reserved = 0);
diff --git a/contrib/epee/src/wipeable_string.cpp b/contrib/epee/src/wipeable_string.cpp
index b016f2f48..f2f365b1b 100644
--- a/contrib/epee/src/wipeable_string.cpp
+++ b/contrib/epee/src/wipeable_string.cpp
@@ -261,4 +261,14 @@ wipeable_string &wipeable_string::operator=(const wipeable_string &other)
return *this;
}
+char& wipeable_string::operator[](size_t idx) {
+ CHECK_AND_ASSERT_THROW_MES(idx < buffer.size(), "Index out of bounds");
+ return buffer[idx];
+}
+
+const char& wipeable_string::operator[](size_t idx) const {
+ CHECK_AND_ASSERT_THROW_MES(idx < buffer.size(), "Index out of bounds");
+ return buffer[idx];
+}
+
}
diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt
index dfdffe67c..fa98f61b5 100644
--- a/external/CMakeLists.txt
+++ b/external/CMakeLists.txt
@@ -70,4 +70,6 @@ add_subdirectory(db_drivers)
add_subdirectory(easylogging++)
add_subdirectory(qrcodegen)
add_subdirectory(bc-ur)
+add_subdirectory(polyseed EXCLUDE_FROM_ALL)
+add_subdirectory(utf8proc EXCLUDE_FROM_ALL)
add_subdirectory(randomwow EXCLUDE_FROM_ALL)
diff --git a/external/polyseed b/external/polyseed
new file mode 160000
index 000000000..bd79f5014
--- /dev/null
+++ b/external/polyseed
@@ -0,0 +1 @@
+Subproject commit bd79f5014c331273357277ed8a3d756fb61b9fa1
diff --git a/external/utf8proc b/external/utf8proc
new file mode 160000
index 000000000..3de4596fb
--- /dev/null
+++ b/external/utf8proc
@@ -0,0 +1 @@
+Subproject commit 3de4596fbe28956855df2ecb3c11c0bbc3535838
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9216bcaa5..c043ba150 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -95,6 +95,7 @@ add_subdirectory(net)
add_subdirectory(hardforks)
add_subdirectory(blockchain_db)
add_subdirectory(mnemonics)
+add_subdirectory(polyseed)
add_subdirectory(rpc)
if(NOT IOS)
add_subdirectory(serialization)
diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt
index 1414be1b2..414936a05 100644
--- a/src/cryptonote_basic/CMakeLists.txt
+++ b/src/cryptonote_basic/CMakeLists.txt
@@ -71,6 +71,7 @@ target_link_libraries(cryptonote_basic
checkpoints
cryptonote_format_utils_basic
device
+ polyseed_wrapper
${Boost_DATE_TIME_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_SERIALIZATION_LIBRARY}
diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp
index 4e87d4477..2d556f285 100644
--- a/src/cryptonote_basic/account.cpp
+++ b/src/cryptonote_basic/account.cpp
@@ -87,12 +87,16 @@ DISABLE_VS_WARNINGS(4244 4345)
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()));
+ epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (3 + m_multisig_keys.size()) + m_passphrase.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 (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
+ m_polyseed.data[i] ^= *ptr++;
+ for (size_t i = 0; i < m_passphrase.size(); ++i)
+ m_passphrase.data()[i] ^= *ptr++;
for (crypto::secret_key &k: m_multisig_keys)
{
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
@@ -150,6 +154,8 @@ DISABLE_VS_WARNINGS(4244 4345)
{
m_keys.m_spend_secret_key = crypto::secret_key();
m_keys.m_multisig_keys.clear();
+ m_keys.m_polyseed = crypto::secret_key();
+ m_keys.m_passphrase.wipe();
}
//-----------------------------------------------------------------
void account_base::set_spend_key(const crypto::secret_key& spend_secret_key)
@@ -255,6 +261,21 @@ DISABLE_VS_WARNINGS(4244 4345)
create_from_keys(address, fake, viewkey);
}
//-----------------------------------------------------------------
+ void account_base::create_from_polyseed(const polyseed::data& seed, const epee::wipeable_string &passphrase)
+ {
+ crypto::secret_key secret_key;
+ seed.keygen(&secret_key, sizeof(secret_key));
+
+ if (!passphrase.empty()) {
+ secret_key = cryptonote::decrypt_key(secret_key, passphrase);
+ }
+
+ generate(secret_key, true, false);
+
+ seed.save(m_keys.m_polyseed.data);
+ m_keys.m_passphrase = passphrase;
+ }
+ //-----------------------------------------------------------------
bool account_base::make_multisig(const crypto::secret_key &view_secret_key, const crypto::secret_key &spend_secret_key, const crypto::public_key &spend_public_key, const std::vector<crypto::secret_key> &multisig_keys)
{
m_keys.m_account_address.m_spend_public_key = spend_public_key;
diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h
index 93d1d28f0..1f76febce 100644
--- a/src/cryptonote_basic/account.h
+++ b/src/cryptonote_basic/account.h
@@ -33,6 +33,7 @@
#include "cryptonote_basic.h"
#include "crypto/crypto.h"
#include "serialization/keyvalue_serialization.h"
+#include "polyseed/polyseed.hpp"
namespace cryptonote
{
@@ -45,6 +46,8 @@ namespace cryptonote
std::vector<crypto::secret_key> m_multisig_keys;
hw::device *m_device = &hw::get_device("default");
crypto::chacha_iv m_encryption_iv;
+ crypto::secret_key m_polyseed;
+ epee::wipeable_string m_passphrase; // Only used with polyseed
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(m_account_address)
@@ -53,6 +56,8 @@ namespace cryptonote
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)
+ KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_polyseed)
+ KV_SERIALIZE(m_passphrase)
END_KV_SERIALIZE_MAP()
void encrypt(const crypto::chacha_key &key);
@@ -79,6 +84,7 @@ namespace cryptonote
void create_from_device(hw::device &hwdev);
void create_from_keys(const cryptonote::account_public_address& address, const crypto::secret_key& spendkey, const crypto::secret_key& viewkey);
void create_from_viewkey(const cryptonote::account_public_address& address, const crypto::secret_key& viewkey);
+ void create_from_polyseed(const polyseed::data &polyseed, const epee::wipeable_string &passphrase);
bool make_multisig(const crypto::secret_key &view_secret_key, const crypto::secret_key &spend_secret_key, const crypto::public_key &spend_public_key, const std::vector<crypto::secret_key> &multisig_keys);
const account_keys& get_keys() const;
std::string get_public_address_str(network_type nettype) const;
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index 8b5091a46..d9151e8d2 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -219,6 +219,8 @@
#define DNS_BLOCKLIST_LIFETIME (86400 * 8)
+#define POLYSEED_COIN POLYSEED_WOWNERO
+
//The limit is enough for the mandatory transaction content with 16 outputs (547 bytes),
//a custom tag (1 byte) and up to 32 bytes of custom data for each recipient.
// (1+32) + (1+1+16*32) + (1+16*32) = 1060
diff --git a/src/polyseed/CMakeLists.txt b/src/polyseed/CMakeLists.txt
new file mode 100644
index 000000000..cca4eb746
--- /dev/null
+++ b/src/polyseed/CMakeLists.txt
@@ -0,0 +1,25 @@
+set(polyseed_sources
+ pbkdf2.c
+ polyseed.cpp
+)
+
+monero_find_all_headers(polyseed_private_headers "${CMAKE_CURRENT_SOURCE_DIR}")
+
+monero_private_headers(polyseed_wrapper
+ ${polyseed_private_headers}
+)
+
+monero_add_library(polyseed_wrapper
+ ${polyseed_sources}
+ ${polyseed_headers}
+ ${polyseed_private_headers}
+)
+
+target_link_libraries(polyseed_wrapper
+PUBLIC
+ polyseed
+ utf8proc
+ ${SODIUM_LIBRARY}
+ PRIVATE
+ ${EXTRA_LIBRARIES}
+)
diff --git a/src/polyseed/pbkdf2.c b/src/polyseed/pbkdf2.c
new file mode 100644
index 000000000..1c45f4708
--- /dev/null
+++ b/src/polyseed/pbkdf2.c
@@ -0,0 +1,85 @@
+// Copyright (c) 2023, The Monero Project
+// Copyright (c) 2021, tevador <tevador@gmail.com>
+// Copyright (c) 2005,2007,2009 Colin Percival
+//
+// 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.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 <string.h>
+
+#include <sodium/crypto_auth_hmacsha256.h>
+#include <sodium/utils.h>
+
+static inline void
+store32_be(uint8_t dst[4], uint32_t w)
+{
+ dst[3] = (uint8_t) w; w >>= 8;
+ dst[2] = (uint8_t) w; w >>= 8;
+ dst[1] = (uint8_t) w; w >>= 8;
+ dst[0] = (uint8_t) w;
+}
+
+void
+crypto_pbkdf2_sha256(const uint8_t* passwd, size_t passwdlen,
+ const uint8_t* salt, size_t saltlen, uint64_t c,
+ uint8_t* buf, size_t dkLen)
+{
+ crypto_auth_hmacsha256_state Phctx, PShctx, hctx;
+ size_t i;
+ uint8_t ivec[4];
+ uint8_t U[32];
+ uint8_t T[32];
+ uint64_t j;
+ int k;
+ size_t clen;
+
+ crypto_auth_hmacsha256_init(&Phctx, passwd, passwdlen);
+ PShctx = Phctx;
+ crypto_auth_hmacsha256_update(&PShctx, salt, saltlen);
+
+ for (i = 0; i * 32 < dkLen; i++) {
+ store32_be(ivec, (uint32_t)(i + 1));
+ hctx = PShctx;
+ crypto_auth_hmacsha256_update(&hctx, ivec, 4);
+ crypto_auth_hmacsha256_final(&hctx, U);
+
+ memcpy(T, U, 32);
+ for (j = 2; j <= c; j++) {
+ hctx = Phctx;
+ crypto_auth_hmacsha256_update(&hctx, U, 32);
+ crypto_auth_hmacsha256_final(&hctx, U);
+
+ for (k = 0; k < 32; k++) {
+ T[k] ^= U[k];
+ }
+ }
+
+ clen = dkLen - i * 32;
+ if (clen > 32) {
+ clen = 32;
+ }
+ memcpy(&buf[i * 32], T, clen);
+ }
+ sodium_memzero((void*)&Phctx, sizeof Phctx);
+ sodium_memzero((void*)&PShctx, sizeof PShctx);
+}
\ No newline at end of file
diff --git a/src/polyseed/pbkdf2.h b/src/polyseed/pbkdf2.h
new file mode 100644
index 000000000..f6253b9d7
--- /dev/null
+++ b/src/polyseed/pbkdf2.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2023, The Monero Project
+// Copyright (c) 2021, tevador <tevador@gmail.com>
+//
+// 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.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+
+#ifndef PBKDF2_H
+#define PBKDF2_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+crypto_pbkdf2_sha256(const uint8_t* passwd, size_t passwdlen,
+ const uint8_t* salt, size_t saltlen, uint64_t c,
+ uint8_t* buf, size_t dkLen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
\ No newline at end of file
diff --git a/src/polyseed/polyseed.cpp b/src/polyseed/polyseed.cpp
new file mode 100644
index 000000000..231a48a94
--- /dev/null
+++ b/src/polyseed/polyseed.cpp
@@ -0,0 +1,182 @@
+// Copyright (c) 2023, The Monero Project
+// Copyright (c) 2021, tevador <tevador@gmail.com>
+//
+// 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.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 "polyseed.hpp"
+#include "pbkdf2.h"
+
+#include <sodium/core.h>
+#include <sodium/utils.h>
+#include <sodium/randombytes.h>
+#include <utf8proc.h>
+
+#include <cstring>
+#include <algorithm>
+#include <array>
+
+namespace polyseed {
+
+ inline size_t utf8_norm(const char* str, polyseed_str norm, utf8proc_option_t options) {
+ utf8proc_int32_t buffer[POLYSEED_STR_SIZE];
+ utf8proc_ssize_t result;
+
+ result = utf8proc_decompose(reinterpret_cast<const uint8_t*>(str), 0, buffer, POLYSEED_STR_SIZE, options);
+ if (result < 0 || result > (POLYSEED_STR_SIZE - 1)) {
+ throw std::runtime_error("Unicode normalization failed");
+ }
+
+ result = utf8proc_reencode(buffer, result, options);
+ if (result < 0 || result > POLYSEED_STR_SIZE) {
+ throw std::runtime_error("Unicode normalization failed");
+ }
+
+ strcpy(norm, reinterpret_cast<const char*>(buffer));
+ sodium_memzero(buffer, POLYSEED_STR_SIZE);
+ return result;
+ }
+
+ static size_t utf8_nfc(const char* str, polyseed_str norm) {
+ // Note: UTF8PROC_LUMP is used here to replace the ideographic space with a regular space for Japanese phrases
+ // to allow wallets to split on ' '.
+ return utf8_norm(str, norm, (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE | UTF8PROC_COMPOSE | UTF8PROC_STRIPNA));
+ }
+
+ static size_t utf8_nfkd(const char* str, polyseed_str norm) {
+ return utf8_norm(str, norm, (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE | UTF8PROC_DECOMPOSE | UTF8PROC_COMPAT | UTF8PROC_STRIPNA));
+ }
+
+ struct dependency {
+ dependency();
+ std::vector<language> languages;
+ };
+
+ static dependency deps;
+
+ dependency::dependency() {
+ if (sodium_init() == -1) {
+ throw std::runtime_error("sodium_init failed");
+ }
+
+ polyseed_dependency pd;
+ pd.randbytes = &randombytes_buf;
+ pd.pbkdf2_sha256 = &crypto_pbkdf2_sha256;
+ pd.memzero = &sodium_memzero;
+ pd.u8_nfc = &utf8_nfc;
+ pd.u8_nfkd = &utf8_nfkd;
+ pd.time = nullptr;
+ pd.alloc = nullptr;
+ pd.free = nullptr;
+
+ polyseed_inject(&pd);
+
+ for (int i = 0; i < polyseed_get_num_langs(); ++i) {
+ languages.push_back(language(polyseed_get_lang(i)));
+ }
+ }
+
+ static language invalid_lang;
+
+ const std::vector<language>& get_langs() {
+ return deps.languages;
+ }
+
+ const language& get_lang_by_name(const std::string& name) {
+ for (auto& lang : deps.languages) {
+ if (name == lang.name_en()) {
+ return lang;
+ }
+ if (name == lang.name()) {
+ return lang;
+ }
+ }
+ return invalid_lang;
+ }
+
+ inline void data::check_init() const {
+ if (valid()) {
+ throw std::runtime_error("already initialized");
+ }
+ }
+
+ static std::array<const char*, 8> error_desc = {
+ "Success",
+ "Wrong number of words in the phrase",
+ "Unknown language or unsupported words",
+ "Checksum mismatch",
+ "Unsupported seed features",
+ "Invalid seed format",
+ "Memory allocation failure",
+ "Unicode normalization failed"
+ };
+
+ static error get_error(polyseed_status status) {
+ if (status > 0 && status < sizeof(error_desc) / sizeof(const char*)) {
+ return error(error_desc[(int)status], status);
+ }
+ return error("Unknown error", status);
+ }
+
+ void data::create(feature_type features) {
+ check_init();
+ auto status = polyseed_create(features, &m_data);
+ if (status != POLYSEED_OK) {
+ throw get_error(status);
+ }
+ }
+
+ void data::split(const language& lang, polyseed_phrase& words) {
+ check_init();
+ if (!lang.valid()) {
+ throw std::runtime_error("invalid language");
+ }
+ }
+
+ void data::load(polyseed_storage storage) {
+ check_init();
+ auto status = polyseed_load(storage, &m_data);
+ if (status != POLYSEED_OK) {
+ throw get_error(status);
+ }
+ }
+
+ void data::load(const crypto::secret_key &key) {
+ polyseed_storage d;
+ memcpy(&d, &key.data, 32);
+ auto status = polyseed_load(d, &m_data);
+ if (status != POLYSEED_OK) {
+ throw get_error(status);
+ }
+ }
+
+ language data::decode(const char* phrase) {
+ check_init();
+ const polyseed_lang* lang;
+ auto status = polyseed_decode(phrase, m_coin, &lang, &m_data);
+ if (status != POLYSEED_OK) {
+ throw get_error(status);
+ }
+ return language(lang);
+ }
+}
diff --git a/src/polyseed/polyseed.hpp b/src/polyseed/polyseed.hpp
new file mode 100644
index 000000000..2c8c777a7
--- /dev/null
+++ b/src/polyseed/polyseed.hpp
@@ -0,0 +1,167 @@
+// Copyright (c) 2023, The Monero Project
+// Copyright (c) 2021, tevador <tevador@gmail.com>
+//
+// 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.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+
+#ifndef POLYSEED_HPP
+#define POLYSEED_HPP
+
+#include <polyseed/include/polyseed.h>
+#include <polyseed/src/lang.h>
+#include <vector>
+#include <stdexcept>
+#include <string>
+#include "crypto/crypto.h"
+
+namespace polyseed {
+
+ class data;
+
+ class language {
+ public:
+ language() : m_lang(nullptr) {}
+ language(const language&) = default;
+ language(const polyseed_lang* lang) : m_lang(lang) {}
+ const char* name() const {
+ return polyseed_get_lang_name(m_lang);
+ }
+ const char* name_en() const {
+ return polyseed_get_lang_name_en(m_lang);
+ }
+ const char* separator() const {
+ return m_lang->separator;
+ }
+ bool valid() const {
+ return m_lang != nullptr;
+ }
+
+ const polyseed_lang* m_lang;
+ private:
+
+ friend class data;
+ };
+
+ const std::vector<language>& get_langs();
+ const language& get_lang_by_name(const std::string& name);
+
+ class error : public std::runtime_error {
+ public:
+ error(const char* msg, polyseed_status status)
+ : std::runtime_error(msg), m_status(status)
+ {
+ }
+ polyseed_status status() const {
+ return m_status;
+ }
+ private:
+ polyseed_status m_status;
+ };
+
+ using feature_type = unsigned int;
+
+ inline int enable_features(feature_type features) {
+ return polyseed_enable_features(features);
+ }
+
+ class data {
+ public:
+ data(const data&) = delete;
+ data(polyseed_coin coin) : m_data(nullptr), m_coin(coin) {}
+ ~data() {
+ polyseed_free(m_data);
+ }
+
+ void create(feature_type features);
+
+ void load(polyseed_storage storage);
+
+ void load(const crypto::secret_key &key);
+
+ language decode(const char* phrase);
+
+ template<class str_type>
+ void encode(const language& lang, str_type& str) const {
+ check_valid();
+ if (!lang.valid()) {
+ throw std::runtime_error("invalid language");
+ }
+ str.resize(POLYSEED_STR_SIZE);
+ auto size = polyseed_encode(m_data, lang.m_lang, m_coin, &str[0]);
+ str.resize(size);
+ }
+
+ void split(const language& lang, polyseed_phrase& words);
+
+ void save(polyseed_storage storage) const {
+ check_valid();
+ polyseed_store(m_data, storage);
+ }
+
+ void save(void *storage) const {
+ check_valid();
+ polyseed_store(m_data, (uint8_t*)storage);
+ }
+
+ void crypt(const char* password) {
+ check_valid();
+ polyseed_crypt(m_data, password);
+ }
+
+ void keygen(void* ptr, size_t key_size) const {
+ check_valid();
+ polyseed_keygen(m_data, m_coin, key_size, (uint8_t*)ptr);
+ }
+
+ bool valid() const {
+ return m_data != nullptr;
+ }
+
+ bool encrypted() const {
+ check_valid();
+ return polyseed_is_encrypted(m_data);
+ }
+
+ uint64_t birthday() const {
+ check_valid();
+ return polyseed_get_birthday(m_data);
+ }
+
+ bool has_feature(feature_type feature) const {
+ check_valid();
+ return polyseed_get_feature(m_data, feature) != 0;
+ }
+ private:
+ void check_valid() const {
+ if (m_data == nullptr) {
+ throw std::runtime_error("invalid object");
+ }
+ }
+ void check_init() const;
+
+ polyseed_data* m_data;
+ polyseed_coin m_coin;
+ };
+}
+
+#endif //POLYSEED_HPP
\ No newline at end of file
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 556e2a8ce..704e5e148 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -728,6 +728,28 @@ bool WalletImpl::recoverFromDevice(const std::string &path, const std::string &p
return true;
}
+bool WalletImpl::createFromPolyseed(const std::string &path, const std::string &password, const std::string &seed,
+ const std::string &passphrase, bool newWallet, uint64_t restoreHeight)
+{
+ clearStatus();
+ m_recoveringFromSeed = !newWallet;
+ m_recoveringFromDevice = false;
+
+ polyseed::data polyseed(POLYSEED_COIN);
+
+ try {
+ auto lang = polyseed.decode(seed.data());
+ m_wallet->set_seed_language(lang.name());
+ m_wallet->generate(path, password, polyseed, passphrase, !newWallet);
+ }
+ catch (const std::exception &e) {
+ setStatusError(e.what());
+ return false;
+ }
+
+ return true;
+}
+
Wallet::Device WalletImpl::getDeviceType() const
{
return static_cast<Wallet::Device>(m_wallet->get_device_type());
@@ -845,6 +867,54 @@ std::string WalletImpl::seed(const std::string& seed_offset) const
}
}
+bool WalletImpl::getPolyseed(std::string &seed_words, std::string &passphrase) const
+{
+ epee::wipeable_string seed_words_epee(seed_words.c_str(), seed_words.size());
+ epee::wipeable_string passphrase_epee(passphrase.c_str(), passphrase.size());
+ clearStatus();
+
+ if (!m_wallet) {
+ return false;
+ }
+
+ bool result = m_wallet->get_polyseed(seed_words_epee, passphrase_epee);
+
+ seed_words.assign(seed_words_epee.data(), seed_words_epee.size());
+ passphrase.assign(passphrase_epee.data(), passphrase_epee.size());
+
+ return result;
+}
+
+std::vector<std::pair<std::string, std::string>> Wallet::getPolyseedLanguages()
+ {
+ std::vector<std::pair<std::string, std::string>> languages;
+
+ auto langs = polyseed::get_langs();
+ for (const auto &lang : langs) {
+ languages.emplace_back(std::pair<std::string, std::string>(lang.name_en(), lang.name()));
+ }
+
+ return languages;
+}
+
+bool Wallet::createPolyseed(std::string &seed_words, std::string &err, const std::string &language)
+{
+ epee::wipeable_string seed_words_epee(seed_words.c_str(), seed_words.size());
+
+ try {
+ polyseed::data polyseed(POLYSEED_COIN);
+ polyseed.create(0);
+ polyseed.encode(polyseed::get_lang_by_name(language), seed_words_epee);
+
+ seed_words.assign(seed_words_epee.data(), seed_words_epee.size());
+ }
+ catch (const std::exception &e) {
+ err = e.what();
+ return false;
+ }
+
+ return true;
+}
std::string WalletImpl::getSeedLanguage() const
{
return m_wallet->get_seed_language();
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 4e9c21ecb..32e12284b 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -79,9 +79,19 @@ public:
bool recoverFromDevice(const std::string &path,
const std::string &password,
const std::string &device_name);
+
+ bool createFromPolyseed(const std::string &path,
+ const std::string &password,
+ const std::string &seed,
+ const std::string &passphrase = "",
+ bool newWallet = true,
+ uint64_t restoreHeight = 0);
+
Device getDeviceType() const override;
bool close(bool store = true);
std::string seed(const std::string& seed_offset = "") const override;
+ bool getPolyseed(std::string &seed_words, std::string &passphrase) const override;
+
std::string getSeedLanguage() const override;
void setSeedLanguage(const std::string &arg) override;
// void setListener(Listener *) {}
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index 53ec4abfc..be1c3704e 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -709,6 +709,10 @@ struct Wallet
static void warning(const std::string &category, const std::string &str);
static void error(const std::string &category, const std::string &str);
+ virtual bool getPolyseed(std::string &seed, std::string &passphrase) const = 0;
+ static bool createPolyseed(std::string &seed_words, std::string &err, const std::string &language = "English");
+ static std::vector<std::pair<std::string, std::string>> getPolyseedLanguages();
+
/**
* @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds)
*/
@@ -1320,6 +1324,27 @@ struct WalletManager
uint64_t kdf_rounds = 1,
WalletListener * listener = nullptr) = 0;
+ /*!
+ * \brief creates a wallet from a polyseed mnemonic phrase
+ * \param path Name of the wallet file to be created
+ * \param password Password of wallet file
+ * \param nettype Network type
+ * \param mnemonic Polyseed mnemonic
+ * \param passphrase Optional seed offset passphrase
+ * \param newWallet Whether it is a new wallet
+ * \param restoreHeight Override the embedded restore height
+ * \param kdf_rounds Number of rounds for key derivation function
+ * @return
+ */
+ virtual Wallet * createWalletFromPolyseed(const std::string &path,
+ const std::string &password,
+ NetworkType nettype,
+ const std::string &mnemonic,
+ const std::string &passphrase = "",
+ bool newWallet = true,
+ uint64_t restore_height = 0,
+ uint64_t kdf_rounds = 1) = 0;
+
/*!
* \brief Closes wallet. In case operation succeeded, wallet object deleted. in case operation failed, wallet object not deleted
* \param wallet previously opened / created wallet instance
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index 277be6ac9..da2056d8a 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -156,6 +156,15 @@ Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path,
return wallet;
}
+Wallet *WalletManagerImpl::createWalletFromPolyseed(const std::string &path, const std::string &password, NetworkType nettype,
+ const std::string &mnemonic, const std::string &passphrase,
+ bool newWallet, uint64_t restoreHeight, uint64_t kdf_rounds)
+{
+ WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds);
+ wallet->createFromPolyseed(path, password, mnemonic, passphrase, newWallet, restoreHeight);
+ return wallet;
+}
+
bool WalletManagerImpl::closeWallet(Wallet *wallet, bool store)
{
WalletImpl * wallet_ = dynamic_cast<WalletImpl*>(wallet);
diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h
index a223e1df9..28fcd36c9 100644
--- a/src/wallet/api/wallet_manager.h
+++ b/src/wallet/api/wallet_manager.h
@@ -75,6 +75,16 @@ public:
const std::string &subaddressLookahead = "",
uint64_t kdf_rounds = 1,
WalletListener * listener = nullptr) override;
+
+ virtual Wallet * createWalletFromPolyseed(const std::string &path,
+ const std::string &password,
+ NetworkType nettype,
+ const std::string &mnemonic,
+ const std::string &passphrase,
+ bool newWallet = true,
+ uint64_t restore_height = 0,
+ uint64_t kdf_rounds = 1) override;
+
virtual bool closeWallet(Wallet *wallet, bool store = true) override;
bool walletExists(const std::string &path) override;
bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds = 1) const override;
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index e14d4d2fc..fd4094360 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -92,6 +92,7 @@ using namespace epee;
#include "device/device_cold.hpp"
#include "device_trezor/device_trezor.hpp"
#include "net/socks_connect.h"
+#include "polyseed/include/polyseed.h"
extern "C"
{
@@ -1278,7 +1279,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std
m_enable_multisig(false),
m_pool_info_query_time(0),
m_has_ever_refreshed_from_node(false),
- m_allow_mismatched_daemon_version(true)
+ m_allow_mismatched_daemon_version(true),
+ m_polyseed(false)
{
set_rpc_client_secret_key(rct::rct2sk(rct::skGen()));
}
@@ -1483,6 +1485,20 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab
return true;
}
//----------------------------------------------------------------------------------------------------
+
+bool wallet2::get_polyseed(epee::wipeable_string& polyseed, epee::wipeable_string& passphrase) const
+{
+ if (!m_polyseed) {
+ return false;
+ }
+
+ polyseed::data data(POLYSEED_COIN);
+ data.load(get_account().get_keys().m_polyseed);
+ data.encode(polyseed::get_lang_by_name(seed_language), polyseed);
+ passphrase = get_account().get_keys().m_passphrase;
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase) const
{
bool ready;
@@ -4801,6 +4817,9 @@ boost::optional<wallet2::keys_file_data> wallet2::get_keys_file_data(const crypt
value2.SetInt(m_enable_multisig ? 1 : 0);
json.AddMember("enable_multisig", value2, json.GetAllocator());
+ value2.SetInt(m_polyseed ? 1 : 0);
+ json.AddMember("polyseed", value2, json.GetAllocator());
+
if (m_background_sync_type == BackgroundSyncCustomPassword && !background_keys_file && m_custom_background_key)
{
value.SetString(reinterpret_cast<const char*>(m_custom_background_key.get().data()), m_custom_background_key.get().size());
@@ -5040,6 +5059,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
m_enable_multisig = false;
m_allow_mismatched_daemon_version = true;
m_custom_background_key = boost::none;
+ m_polyseed = false;
}
else if(json.IsObject())
{
@@ -5280,6 +5300,9 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, background_sync_type, BackgroundSyncType, Int, false, BackgroundSyncOff);
m_background_sync_type = field_background_sync_type;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, polyseed, int, Int, false, false);
+ m_polyseed = field_polyseed;
+
// Load encryption key used to encrypt background cache
crypto::chacha_key custom_background_key;
m_custom_background_key = boost::none;
@@ -5599,6 +5622,48 @@ void wallet2::init_type(hw::device::device_type device_type)
m_key_device_type = device_type;
}
+/*!
+ * \brief Generates a polyseed wallet or restores one.
+ * \param wallet_ Name of wallet file
+ * \param password Password of wallet file
+ * \param passphrase Seed offset passphrase
+ * \param recover Whether it is a restore
+ * \param seed_words If it is a restore, the polyseed
+ * \param create_address_file Whether to create an address file
+ * \return The secret key of the generated wallet
+ */
+void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
+ const polyseed::data &seed, const epee::wipeable_string& passphrase, bool recover, uint64_t restoreHeight, bool create_address_file)
+{
+ clear();
+ prepare_file_names(wallet_);
+
+ if (!wallet_.empty()) {
+ boost::system::error_code ignored_ec;
+ THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
+ THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
+ }
+
+ m_account.create_from_polyseed(seed, passphrase);
+
+ init_type(hw::device::device_type::SOFTWARE);
+ m_polyseed = true;
+ setup_keys(password);
+
+ if (recover) {
+ m_refresh_from_block_height = estimate_blockchain_height(restoreHeight > 0 ? restoreHeight : seed.birthday());
+ } else {
+ m_refresh_from_block_height = estimate_blockchain_height();
+ }
+
+ create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
+
+ setup_new_blockchain();
+
+ if (!wallet_.empty())
+ store();
+}
+
/*!
* \brief Generates a wallet or restores one. Assumes the multisig setup
* has already completed for the provided multisig info.
@@ -5726,7 +5791,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
return retval;
}
- uint64_t wallet2::estimate_blockchain_height()
+ uint64_t wallet2::estimate_blockchain_height(uint64_t time)
{
// -1 month for fluctuations in block time and machine date/time setup.
// avg seconds per block
@@ -5750,7 +5815,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
// the daemon is currently syncing.
// If we use the approximate height we subtract one month as
// a safety margin.
- height = get_approximate_blockchain_height();
+ height = get_approximate_blockchain_height(time);
uint64_t target_height = get_daemon_blockchain_target_height(err);
if (err.empty()) {
if (target_height < height)
@@ -13643,9 +13708,10 @@ uint64_t wallet2::get_daemon_blockchain_target_height(string &err)
return target_height;
}
-uint64_t wallet2::get_approximate_blockchain_height() const
+uint64_t wallet2::get_approximate_blockchain_height(uint64_t t) const
{
uint64_t approx_blockchain_height = m_nettype == TESTNET ? 0 : (time(NULL) - 1522624244)/307;
+ // uint64_t approx_blockchain_height = fork_block + ((t > 0 ? t : time(NULL)) - fork_time)/seconds_per_block;
LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height);
return approx_blockchain_height;
}
@@ -15780,15 +15846,6 @@ bool wallet2::parse_uri(const std::string &uri, std::string &address, std::strin
//----------------------------------------------------------------------------------------------------
uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day)
{
- uint32_t version;
- if (!check_connection(&version))
- {
- throw std::runtime_error("failed to connect to daemon: " + get_daemon_address());
- }
- if (version < MAKE_CORE_RPC_VERSION(1, 6))
- {
- throw std::runtime_error("this function requires RPC version 1.6 or higher");
- }
std::tm date = { 0, 0, 0, 0, 0, 0, 0, 0 };
date.tm_year = year - 1900;
date.tm_mon = month - 1;
@@ -15797,7 +15854,23 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui
{
throw std::runtime_error("month or day out of range");
}
+
uint64_t timestamp_target = std::mktime(&date);
+
+ return get_blockchain_height_by_timestamp(timestamp_target);
+}
+
+uint64_t wallet2::get_blockchain_height_by_timestamp(uint64_t timestamp_target) {
+ uint32_t version;
+ if (!check_connection(&version))
+ {
+ throw std::runtime_error("failed to connect to daemon: " + get_daemon_address());
+ }
+ if (version < MAKE_CORE_RPC_VERSION(1, 6))
+ {
+ throw std::runtime_error("this function requires RPC version 1.6 or higher");
+ }
+
std::string err;
uint64_t height_min = 0;
uint64_t height_max = get_daemon_blockchain_height(err) - 1;
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 746e2aeff..c165acb9d 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -72,6 +72,7 @@
#include "message_store.h"
#include "wallet_light_rpc.h"
#include "wallet_rpc_helpers.h"
+#include "polyseed/polyseed.hpp"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2"
@@ -921,6 +922,20 @@ private:
void generate(const std::string& wallet_, const epee::wipeable_string& password,
const epee::wipeable_string& multisig_data, bool create_address_file = false);
+ /*!
+ * \brief Generates a wallet from a polyseed.
+ * @param wallet_ Name of wallet file
+ * @param password Password of wallet file
+ * @param seed Polyseed data
+ * @param passphrase Optional seed offset passphrase
+ * @param recover Whether it is a restore
+ * @param restoreHeight Override the embedded restore height
+ * @param create_address_file Whether to create an address file
+ */
+ void generate(const std::string& wallet_, const epee::wipeable_string& password,
+ const polyseed::data &seed, const epee::wipeable_string& passphrase = "",
+ bool recover = false, uint64_t restoreHeight = 0, bool create_address_file = false);
+
/*!
* \brief Generates a wallet or restores one.
* \param wallet_ Name of wallet file
@@ -1095,6 +1110,15 @@ private:
bool is_deterministic() const;
bool get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const;
+ /*!
+ * \brief get_polyseed Gets the polyseed (if available) and passphrase (if set) needed to recover the wallet.
+ * @param seed Polyseed mnemonic phrase
+ * @param passphrase Seed offset passphrase that was used to restore the wallet
+ * @return Returns true if the wallet has a polyseed.
+ * Note: both the mnemonic phrase and the passphrase are needed to recover the wallet
+ */
+ bool get_polyseed(epee::wipeable_string& seed, epee::wipeable_string &passphrase) const;
+
/*!
* \brief Checks if light wallet. A light wallet sends view key to a server where the blockchain is scanned.
*/
@@ -1562,8 +1586,8 @@ private:
/*!
* \brief Calculates the approximate blockchain height from current date/time.
*/
- uint64_t get_approximate_blockchain_height() const;
- uint64_t estimate_blockchain_height();
+ uint64_t get_approximate_blockchain_height(uint64_t time = 0) const;
+ uint64_t estimate_blockchain_height(uint64_t time = 0);
std::vector<size_t> select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct);
std::vector<size_t> select_available_outputs(const std::function<bool(const transfer_details &td)> &f);
std::vector<size_t> select_available_unmixable_outputs();
@@ -1657,6 +1681,7 @@ private:
bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error);
uint64_t get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day); // 1<=month<=12, 1<=day<=31
+ uint64_t get_blockchain_height_by_timestamp(uint64_t timestamp);
bool is_synced();
@@ -2003,6 +2028,7 @@ private:
std::string seed_language; /*!< Language of the mnemonics (seed). */
bool is_old_file_format; /*!< Whether the wallet file is of an old file format */
bool m_watch_only; /*!< no spend key */
+ bool m_polyseed;
bool m_multisig; /*!< if > 1 spend secret key will not match spend public key */
uint32_t m_multisig_threshold;
std::vector<crypto::public_key> m_multisig_signers;
--
2.48.0