Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
wallet_manager.cpp
Go to the documentation of this file.
6#include <boost/algorithm/string.hpp>
7namespace sysio {
8namespace wallet {
9
10constexpr auto file_ext = ".wallet";
11constexpr auto password_prefix = "PW";
12
13std::string gen_password() {
14 auto key = private_key_type::generate();
15 return password_prefix + key.to_string();
16
17}
18
19bool valid_filename(const string& name) {
20 if (name.empty()) return false;
21 if (std::find_if(name.begin(), name.end(), !boost::algorithm::is_alnum() && !boost::algorithm::is_any_of("._-")) != name.end()) return false;
22 return boost::filesystem::path(name).filename().string() == name;
23}
24
26#ifdef __APPLE__
27 try {
28 wallets.emplace("SecureEnclave", std::make_unique<se_wallet>());
29 } catch(const std::exception& ) {}
30#endif
31}
32
34 //not really required, but may spook users
35 if(wallet_dir_lock)
36 boost::filesystem::remove(lock_path);
37}
38
39void wallet_manager::set_timeout(const std::chrono::seconds& t) {
40 timeout = t;
41 auto now = std::chrono::system_clock::now();
42 timeout_time = now + timeout;
43 SYS_ASSERT(timeout_time >= now && timeout_time.time_since_epoch().count() > 0, invalid_lock_timeout_exception, "Overflow on timeout_time, specified ${t}, now ${now}, timeout_time ${timeout_time}",
44 ("t", t.count())("now", now.time_since_epoch().count())("timeout_time", timeout_time.time_since_epoch().count()));
45}
46
47void wallet_manager::check_timeout() {
48 if (timeout_time != timepoint_t::max()) {
49 const auto& now = std::chrono::system_clock::now();
50 if (now >= timeout_time) {
51 lock_all();
52 }
53 timeout_time = now + timeout;
54 }
55}
56
57std::string wallet_manager::create(const std::string& name) {
58 check_timeout();
59
60 SYS_ASSERT(valid_filename(name), wallet_exception, "Invalid filename, path not allowed in wallet name ${n}", ("n", name));
61
62 auto wallet_filename = dir / (name + file_ext);
63
64 if (fc::exists(wallet_filename)) {
65 SYS_THROW(chain::wallet_exist_exception, "Wallet with name: '${n}' already exists at ${path}", ("n", name)("path",fc::path(wallet_filename)));
66 }
67
68 std::string password = gen_password();
70 auto wallet = make_unique<soft_wallet>(d);
71 wallet->set_password(password);
72 wallet->set_wallet_filename(wallet_filename.string());
73 wallet->unlock(password);
74 wallet->lock();
75 wallet->unlock(password);
76
77 // Explicitly save the wallet file here, to ensure it now exists.
78 wallet->save_wallet_file();
79
80 // If we have name in our map then remove it since we want the emplace below to replace.
81 // This can happen if the wallet file is removed while eos-walletd is running.
82 auto it = wallets.find(name);
83 if (it != wallets.end()) {
84 wallets.erase(it);
85 }
86 wallets.emplace(name, std::move(wallet));
87
88 return password;
89}
90
91void wallet_manager::open(const std::string& name) {
92 check_timeout();
93
94 SYS_ASSERT(valid_filename(name), wallet_exception, "Invalid filename, path not allowed in wallet name ${n}", ("n", name));
95
97 auto wallet = std::make_unique<soft_wallet>(d);
98 auto wallet_filename = dir / (name + file_ext);
99 wallet->set_wallet_filename(wallet_filename.string());
100 if (!wallet->load_wallet_file()) {
101 SYS_THROW(chain::wallet_nonexistent_exception, "Unable to open file: ${f}", ("f", wallet_filename.string()));
102 }
103
104 // If we have name in our map then remove it since we want the emplace below to replace.
105 // This can happen if the wallet file is added while eos-walletd is running.
106 auto it = wallets.find(name);
107 if (it != wallets.end()) {
108 wallets.erase(it);
109 }
110 wallets.emplace(name, std::move(wallet));
111}
112
113std::vector<std::string> wallet_manager::list_wallets() {
114 check_timeout();
115 std::vector<std::string> result;
116 for (const auto& i : wallets) {
117 if (i.second->is_locked()) {
118 result.emplace_back(i.first);
119 } else {
120 result.emplace_back(i.first + " *");
121 }
122 }
123 return result;
124}
125
126map<public_key_type,private_key_type> wallet_manager::list_keys(const string& name, const string& pw) {
127 check_timeout();
128
129 if (wallets.count(name) == 0)
130 SYS_THROW(chain::wallet_nonexistent_exception, "Wallet not found: ${w}", ("w", name));
131 auto& w = wallets.at(name);
132 if (w->is_locked())
133 SYS_THROW(chain::wallet_locked_exception, "Wallet is locked: ${w}", ("w", name));
134 w->check_password(pw); //throws if bad password
135 return w->list_keys();
136}
137
138flat_set<public_key_type> wallet_manager::get_public_keys() {
139 check_timeout();
140 SYS_ASSERT(!wallets.empty(), wallet_not_available_exception, "You don't have any wallet!");
141 flat_set<public_key_type> result;
142 bool is_all_wallet_locked = true;
143 for (const auto& i : wallets) {
144 if (!i.second->is_locked()) {
145 result.merge(i.second->list_public_keys());
146 }
147 is_all_wallet_locked &= i.second->is_locked();
148 }
149 SYS_ASSERT(!is_all_wallet_locked, wallet_locked_exception, "You don't have any unlocked wallet!");
150 return result;
151}
152
153
155 // no call to check_timeout since we are locking all anyway
156 for (auto& i : wallets) {
157 if (!i.second->is_locked()) {
158 i.second->lock();
159 }
160 }
161}
162
163void wallet_manager::lock(const std::string& name) {
164 check_timeout();
165 if (wallets.count(name) == 0) {
166 SYS_THROW(chain::wallet_nonexistent_exception, "Wallet not found: ${w}", ("w", name));
167 }
168 auto& w = wallets.at(name);
169 if (w->is_locked()) {
170 return;
171 }
172 w->lock();
173}
174
175void wallet_manager::unlock(const std::string& name, const std::string& password) {
176 check_timeout();
177 if (wallets.count(name) == 0) {
178 open( name );
179 }
180 auto& w = wallets.at(name);
181 if (!w->is_locked()) {
182 SYS_THROW(chain::wallet_unlocked_exception, "Wallet is already unlocked: ${w}", ("w", name));
183 return;
184 }
185 w->unlock(password);
186}
187
188void wallet_manager::import_key(const std::string& name, const std::string& wif_key) {
189 check_timeout();
190 if (wallets.count(name) == 0) {
191 SYS_THROW(chain::wallet_nonexistent_exception, "Wallet not found: ${w}", ("w", name));
192 }
193 auto& w = wallets.at(name);
194 if (w->is_locked()) {
195 SYS_THROW(chain::wallet_locked_exception, "Wallet is locked: ${w}", ("w", name));
196 }
197 w->import_key(wif_key);
198}
199
200void wallet_manager::remove_key(const std::string& name, const std::string& password, const std::string& key) {
201 check_timeout();
202 if (wallets.count(name) == 0) {
203 SYS_THROW(chain::wallet_nonexistent_exception, "Wallet not found: ${w}", ("w", name));
204 }
205 auto& w = wallets.at(name);
206 if (w->is_locked()) {
207 SYS_THROW(chain::wallet_locked_exception, "Wallet is locked: ${w}", ("w", name));
208 }
209 w->check_password(password); //throws if bad password
210 w->remove_key(key);
211}
212
213string wallet_manager::create_key(const std::string& name, const std::string& key_type) {
214 check_timeout();
215 if (wallets.count(name) == 0) {
216 SYS_THROW(chain::wallet_nonexistent_exception, "Wallet not found: ${w}", ("w", name));
217 }
218 auto& w = wallets.at(name);
219 if (w->is_locked()) {
220 SYS_THROW(chain::wallet_locked_exception, "Wallet is locked: ${w}", ("w", name));
221 }
222
223 string upper_key_type = boost::to_upper_copy<std::string>(key_type);
224 return w->create_key(upper_key_type);
225}
226
228wallet_manager::sign_transaction(const chain::signed_transaction& txn, const flat_set<public_key_type>& keys, const chain::chain_id_type& id) {
229 check_timeout();
231
232 for (const auto& pk : keys) {
233 bool found = false;
234 for (const auto& i : wallets) {
235 if (!i.second->is_locked()) {
236 std::optional<signature_type> sig = i.second->try_sign_digest(stxn.sig_digest(id, stxn.context_free_data), pk);
237 if (sig) {
238 stxn.signatures.push_back(*sig);
239 found = true;
240 break; // inner for
241 }
242 }
243 }
244 if (!found) {
245 SYS_THROW(chain::wallet_missing_pub_key_exception, "Public key not found in unlocked wallets ${k}", ("k", pk));
246 }
247 }
248
249 return stxn;
250}
251
254 check_timeout();
255
256 try {
257 for (const auto& i : wallets) {
258 if (!i.second->is_locked()) {
259 std::optional<signature_type> sig = i.second->try_sign_digest(digest, key);
260 if (sig)
261 return *sig;
262 }
263 }
265
266 SYS_THROW(chain::wallet_missing_pub_key_exception, "Public key not found in unlocked wallets ${k}", ("k", key));
267}
268
269void wallet_manager::own_and_use_wallet(const string& name, std::unique_ptr<wallet_api>&& wallet) {
270 if(wallets.find(name) != wallets.end())
271 SYS_THROW(wallet_exception, "Tried to use wallet name that already exists.");
272 wallets.emplace(name, std::move(wallet));
273}
274
275void wallet_manager::start_lock_watch(std::shared_ptr<boost::asio::deadline_timer> t)
276{
277 t->async_wait([t, this](const boost::system::error_code& /*ec*/)
278 {
279 namespace bfs = boost::filesystem;
280 boost::system::error_code ec;
281 auto rc = bfs::status(lock_path, ec);
282 if(ec != boost::system::error_code()) {
283 if(rc.type() == bfs::file_not_found) {
284 appbase::app().quit();
285 SYS_THROW(wallet_exception, "Lock file removed while kiod still running. Terminating.");
286 }
287 }
288 t->expires_from_now(boost::posix_time::seconds(1));
289 start_lock_watch(t);
290 });
291}
292
293void wallet_manager::initialize_lock() {
294 //This is technically somewhat racy in here -- if multiple kiod are in this function at once.
295 //I've considered that an acceptable tradeoff to maintain cross-platform boost constructs here
296 lock_path = dir / "wallet.lock";
297 {
298 std::ofstream x(lock_path.string());
299 SYS_ASSERT(!x.fail(), wallet_exception, "Failed to open wallet lock file at ${f}", ("f", lock_path.string()));
300 }
301 wallet_dir_lock = std::make_unique<boost::interprocess::file_lock>(lock_path.string().c_str());
302 if(!wallet_dir_lock->try_lock()) {
303 wallet_dir_lock.reset();
304 SYS_THROW(wallet_exception, "Failed to lock access to wallet directory; is another kiod running?");
305 }
306 auto timer = std::make_shared<boost::asio::deadline_timer>(appbase::app().get_io_service(), boost::posix_time::seconds(1));
307 start_lock_watch(timer);
308}
309
310} // namespace wallet
311} // namespace sysio
const uint8_t password[]
Definition attest.c:39
std::string name
#define SYS_THROW(exc_type, FORMAT,...)
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
Definition exceptions.hpp:7
static private_key generate()
wraps boost::filesystem::path to provide platform independent path manipulation.
void unlock(const std::string &name, const std::string &password)
void own_and_use_wallet(const string &name, std::unique_ptr< wallet_api > &&wallet)
Takes ownership of a wallet to use.
map< public_key_type, private_key_type > list_keys(const string &name, const string &pw)
void set_timeout(const std::chrono::seconds &t)
void lock_all()
Locks all the unlocked wallets.
chain::signed_transaction sign_transaction(const chain::signed_transaction &txn, const flat_set< public_key_type > &keys, const chain::chain_id_type &id)
flat_set< public_key_type > get_public_keys()
void import_key(const std::string &name, const std::string &wif_key)
std::string create(const std::string &name)
void open(const std::string &name)
void remove_key(const std::string &name, const std::string &password, const std::string &key)
string create_key(const std::string &name, const std::string &key_type)
chain::signature_type sign_digest(const chain::digest_type &digest, const public_key_type &key)
std::vector< std::string > list_wallets()
void lock(const std::string &name)
#define FC_LOG_AND_RETHROW()
application & app()
fc::sha256 digest(const T &value)
Definition digest.hpp:9
bool exists(const path &p)
std::string gen_password()
constexpr auto password_prefix
bool valid_filename(const string &name)
constexpr auto file_ext
Immutable except for fc::from_variant.
Definition name.hpp:43
constexpr bool empty() const
Definition name.hpp:53
vector< bytes > context_free_data
for each context-free action, there is an entry here
vector< signature_type > signatures
digest_type sig_digest(const chain_id_type &chain_id, const vector< bytes > &cfd=vector< bytes >()) const
CK_ULONG d
yh_rc rc
struct @108 key_type