Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
wallet.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cctype>
5#include <iomanip>
6#include <iostream>
7#include <iterator>
8#include <sstream>
9#include <string>
10#include <list>
11
14#include <fc/git_revision.hpp>
15#include <fc/io/json.hpp>
16#include <fc/crypto/aes.hpp>
17#include <fc/crypto/hex.hpp>
18
19#include <boost/range/adaptor/map.hpp>
20#include <boost/range/algorithm/copy.hpp>
21
22#ifndef WIN32
23# include <sys/types.h>
24# include <sys/stat.h>
26
27#endif
28
29namespace sysio { namespace wallet {
30
31namespace detail {
32
33private_key_type derive_private_key( const std::string& prefix_string,
34 int sequence_number )
35{
36 std::string sequence_string = std::to_string(sequence_number);
37 fc::sha512 h = fc::sha512::hash(prefix_string + " " + sequence_string);
39}
40
42{
43 private:
44 void enable_umask_protection() {
45#ifdef __unix__
46 _old_umask = umask( S_IRWXG | S_IRWXO );
47#endif
48 }
49
50 void disable_umask_protection() {
51#ifdef __unix__
52 umask( _old_umask );
53#endif
54 }
55
56public:
58 soft_wallet_impl( soft_wallet& s, const wallet_data& initial_data )
59 : self( s )
60 {
61 }
62
64 {}
65
67 {
68 if( !is_locked() )
69 {
70 plain_keys data;
71 data.keys = _keys;
72 data.checksum = _checksum;
73 auto plain_txt = fc::raw::pack(data);
74 _wallet.cipher_keys = fc::aes_encrypt( data.checksum, plain_txt );
75 }
76 }
77
78 bool copy_wallet_file( string destination_filename )
79 {
80 fc::path src_path = get_wallet_filename();
81 if( !fc::exists( src_path ) )
82 return false;
83 fc::path dest_path = destination_filename + _wallet_filename_extension;
84 int suffix = 0;
85 while( fc::exists(dest_path) )
86 {
87 ++suffix;
88 dest_path = destination_filename + "-" + std::to_string( suffix ) + _wallet_filename_extension;
89 }
90 wlog( "backing up wallet ${src} to ${dest}",
91 ("src", src_path)
92 ("dest", dest_path) );
93
94 fc::path dest_parent = fc::absolute(dest_path).parent_path();
95 try
96 {
97 enable_umask_protection();
98 if( !fc::exists( dest_parent ) )
99 fc::create_directories( dest_parent );
100 fc::copy( src_path, dest_path );
101 disable_umask_protection();
102 }
103 catch(...)
104 {
105 disable_umask_protection();
106 throw;
107 }
108 return true;
109 }
110
111 bool is_locked()const
112 {
113 return _checksum == fc::sha512();
114 }
115
116 string get_wallet_filename() const { return _wallet_filename; }
117
118 std::optional<private_key_type> try_get_private_key(const public_key_type& id)const
119 {
120 auto it = _keys.find(id);
121 if( it != _keys.end() )
122 return it->second;
123 return std::optional<private_key_type>();
124 }
125
126 std::optional<signature_type> try_sign_digest( const digest_type digest, const public_key_type public_key ) {
127 auto it = _keys.find(public_key);
128 if( it == _keys.end() )
129 return std::optional<signature_type>();
130 return it->second.sign(digest);
131 }
132
134 {
135 auto has_key = try_get_private_key( id );
136 SYS_ASSERT( has_key, chain::key_nonexistent_exception, "Key doesn't exist!" );
137 return *has_key;
138 }
139
140
141 // imports the private key into the wallet, and associate it in some way (?) with the
142 // given account name.
143 // @returns true if the key matches a current active/owner/memo key for the named
144 // account, false otherwise (but it is stored either way)
145 bool import_key(string wif_key)
146 {
147 private_key_type priv(wif_key);
149
150 auto itr = _keys.find(wif_pub_key);
151 if( itr == _keys.end() ) {
152 _keys[wif_pub_key] = priv;
153 return true;
154 }
155 SYS_THROW( chain::key_exist_exception, "Key already in wallet" );
156 }
157
158 // Removes a key from the wallet
159 // @returns true if the key matches a current active/owner/memo key for the named
160 // account, false otherwise (but it is removed either way)
161 bool remove_key(string key)
162 {
163 public_key_type pub(key);
164 auto itr = _keys.find(pub);
165 if( itr != _keys.end() ) {
166 _keys.erase(pub);
167 return true;
168 }
169 SYS_THROW( chain::key_nonexistent_exception, "Key not in wallet" );
170 }
171
172 string create_key(string key_type)
173 {
174 if(key_type.empty())
176
177 private_key_type priv_key;
178 if(key_type == "K1")
180 else if(key_type == "R1")
182 else
183 SYS_THROW(chain::unsupported_key_type_exception, "Key type \"${kt}\" not supported by software wallet", ("kt", key_type));
184
185 import_key(priv_key.to_string());
186 return priv_key.get_public_key().to_string();
187 }
188
189 bool load_wallet_file(string wallet_filename = "")
190 {
191 // TODO: Merge imported wallet with existing wallet,
192 // instead of replacing it
193 if( wallet_filename == "" )
194 wallet_filename = _wallet_filename;
195
196 if( ! fc::exists( wallet_filename ) )
197 return false;
198
199 _wallet = fc::json::from_file( wallet_filename ).as< wallet_data >();
200
201 return true;
202 }
203
204 void save_wallet_file(string wallet_filename = "")
205 {
206 //
207 // Serialize in memory, then save to disk
208 //
209 // This approach lessens the risk of a partially written wallet
210 // if exceptions are thrown in serialization
211 //
212
213 encrypt_keys();
214
215 if( wallet_filename == "" )
216 wallet_filename = _wallet_filename;
217
218 wlog( "saving wallet to file ${fn}", ("fn", wallet_filename) );
219
220 string data = fc::json::to_pretty_string( _wallet );
221 try
222 {
223 enable_umask_protection();
224 //
225 // Parentheses on the following declaration fails to compile,
226 // due to the Most Vexing Parse. Thanks, C++
227 //
228 // http://en.wikipedia.org/wiki/Most_vexing_parse
229 //
230 ofstream outfile{ wallet_filename };
231 if (!outfile) {
232 elog("Unable to open file: ${fn}", ("fn", wallet_filename));
233 SYS_THROW(wallet_exception, "Unable to open file: ${fn}", ("fn", wallet_filename));
234 }
235 outfile.write( data.c_str(), data.length() );
236 outfile.flush();
237 outfile.close();
238 disable_umask_protection();
239 }
240 catch(...)
241 {
242 disable_umask_protection();
243 throw;
244 }
245 }
246
249
250 map<public_key_type,private_key_type> _keys;
252
253#ifdef __unix__
254 mode_t _old_umask;
255#endif
256 const string _wallet_filename_extension = ".wallet";
257 const string _default_key_type = "K1";
258};
259
260} } } // sysio::wallet::detail
261
262
263
264namespace sysio { namespace wallet {
265
267 : my(new detail::soft_wallet_impl(*this, initial_data))
268{}
269
271
272bool soft_wallet::copy_wallet_file(string destination_filename)
273{
274 return my->copy_wallet_file(destination_filename);
275}
276
278{
279 return my->get_wallet_filename();
280}
281
282bool soft_wallet::import_key(string wif_key)
283{
284 SYS_ASSERT(!is_locked(), wallet_locked_exception, "Unable to import key on a locked wallet");
285
286 if( my->import_key(wif_key) )
287 {
289 return true;
290 }
291 return false;
292}
293
295{
296 SYS_ASSERT(!is_locked(), wallet_locked_exception, "Unable to remove key from a locked wallet");
297
298 if( my->remove_key(key) )
299 {
301 return true;
302 }
303 return false;
304}
305
307{
308 SYS_ASSERT(!is_locked(), wallet_locked_exception, "Unable to create key on a locked wallet");
309
310 string ret = my->create_key(key_type);
312 return ret;
313}
314
315bool soft_wallet::load_wallet_file( string wallet_filename )
316{
317 return my->load_wallet_file( wallet_filename );
318}
319
320void soft_wallet::save_wallet_file( string wallet_filename )
321{
322 my->save_wallet_file( wallet_filename );
323}
324
326{
327 return my->is_locked();
328}
329
331{
332 return my->_wallet.cipher_keys.size() == 0;
333}
334
336{
337 my->encrypt_keys();
338}
339
341{ try {
342 SYS_ASSERT( !is_locked(), wallet_locked_exception, "Unable to lock a locked wallet" );
343 encrypt_keys();
344 for( auto key : my->_keys )
345 key.second = private_key_type();
346
347 my->_keys.clear();
348 my->_checksum = fc::sha512();
350
351void soft_wallet::unlock(string password)
352{ try {
353 FC_ASSERT(password.size() > 0);
354 auto pw = fc::sha512::hash(password.c_str(), password.size());
355 vector<char> decrypted = fc::aes_decrypt(pw, my->_wallet.cipher_keys);
356 auto pk = fc::raw::unpack<plain_keys>(decrypted);
357 FC_ASSERT(pk.checksum == pw);
358 my->_keys = std::move(pk.keys);
359 my->_checksum = pk.checksum;
360} SYS_RETHROW_EXCEPTIONS(chain::wallet_invalid_password_exception,
361 "Invalid password for wallet: \"${wallet_name}\"", ("wallet_name", get_wallet_filename())) }
362
363void soft_wallet::check_password(string password)
364{ try {
365 FC_ASSERT(password.size() > 0);
366 auto pw = fc::sha512::hash(password.c_str(), password.size());
367 vector<char> decrypted = fc::aes_decrypt(pw, my->_wallet.cipher_keys);
368 auto pk = fc::raw::unpack<plain_keys>(decrypted);
369 FC_ASSERT(pk.checksum == pw);
370} SYS_RETHROW_EXCEPTIONS(chain::wallet_invalid_password_exception,
371 "Invalid password for wallet: \"${wallet_name}\"", ("wallet_name", get_wallet_filename())) }
372
373void soft_wallet::set_password( string password )
374{
375 if( !is_new() )
376 SYS_ASSERT( !is_locked(), wallet_locked_exception, "The wallet must be unlocked before the password can be set" );
377 my->_checksum = fc::sha512::hash( password.c_str(), password.size() );
378 lock();
379}
380
381map<public_key_type, private_key_type> soft_wallet::list_keys()
382{
383 SYS_ASSERT(!is_locked(), wallet_locked_exception, "Unable to list public keys of a locked wallet");
384 return my->_keys;
385}
386
387flat_set<public_key_type> soft_wallet::list_public_keys() {
388 SYS_ASSERT(!is_locked(), wallet_locked_exception, "Unable to list private keys of a locked wallet");
389 flat_set<public_key_type> keys;
390 boost::copy(my->_keys | boost::adaptors::map_keys, std::inserter(keys, keys.end()));
391 return keys;
392}
393
395{
396 return my->get_private_key( pubkey );
397}
398
399std::optional<signature_type> soft_wallet::try_sign_digest( const digest_type digest, const public_key_type public_key ) {
400 return my->try_sign_digest(digest, public_key);
401}
402
403pair<public_key_type,private_key_type> soft_wallet::get_private_key_from_password( string account, string role, string password )const {
404 auto seed = account + role + password;
405 SYS_ASSERT( seed.size(), wallet_exception, "seed should not be empty" );
406 auto secret = fc::sha256::hash( seed.c_str(), seed.size() );
408 return std::make_pair( priv.get_public_key(), priv );
409}
410
411void soft_wallet::set_wallet_filename(string wallet_filename)
412{
413 my->_wallet_filename = wallet_filename;
414}
415
416} } // sysio::wallet
417
const uint8_t password[]
Definition attest.c:39
#define SYS_THROW(exc_type, FORMAT,...)
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
Definition exceptions.hpp:7
#define SYS_RETHROW_EXCEPTIONS(exception_type, FORMAT,...)
std::string to_string(const fc::yield_function_t &yield=fc::yield_function_t()) const
static private_key generate()
public_key get_public_key() const
static private_key regenerate(const typename KeyType::data_type &data)
std::string to_string(const fc::yield_function_t &yield=fc::yield_function_t()) const
contains only the public point of an elliptic curve key.
static variant from_file(const fc::path &p, const parse_type ptype=parse_type::legacy_parser, const uint32_t max_depth=DEFAULT_MAX_RECURSION_DEPTH)
Definition json.cpp:797
static string to_pretty_string(const variant &v, const yield_function_t &yield, const output_formatting format=output_formatting::stringify_large_ints_and_doubles)
Definition json.cpp:775
wraps boost::filesystem::path to provide platform independent path manipulation.
fc::path parent_path() const
static sha256 hash(const char *d, uint32_t dlen)
Definition sha256.cpp:44
static sha512 hash(const char *d, uint32_t dlen)
Definition sha512.cpp:37
T as() const
Definition variant.hpp:327
void save_wallet_file(string wallet_filename="")
Definition wallet.cpp:204
soft_wallet_impl(soft_wallet &s, const wallet_data &initial_data)
Definition wallet.cpp:58
map< public_key_type, private_key_type > _keys
Definition wallet.cpp:250
private_key_type get_private_key(const public_key_type &id) const
Definition wallet.cpp:133
bool copy_wallet_file(string destination_filename)
Definition wallet.cpp:78
bool load_wallet_file(string wallet_filename="")
Definition wallet.cpp:189
string create_key(string key_type)
Definition wallet.cpp:172
std::optional< private_key_type > try_get_private_key(const public_key_type &id) const
Definition wallet.cpp:118
std::optional< signature_type > try_sign_digest(const digest_type digest, const public_key_type public_key)
Definition wallet.cpp:126
void set_wallet_filename(string wallet_filename)
Definition wallet.cpp:411
void unlock(string password) override
Definition wallet.cpp:351
bool is_locked() const override
Definition wallet.cpp:325
map< public_key_type, private_key_type > list_keys() override
Definition wallet.cpp:381
private_key_type get_private_key(public_key_type pubkey) const override
Definition wallet.cpp:394
std::shared_ptr< detail::soft_wallet_impl > my
Definition wallet.hpp:182
string get_wallet_filename() const
Definition wallet.cpp:277
bool load_wallet_file(string wallet_filename="")
Definition wallet.cpp:315
soft_wallet(const wallet_data &initial_data)
Definition wallet.cpp:266
void check_password(string password) override
Definition wallet.cpp:363
string create_key(string key_type) override
Definition wallet.cpp:306
pair< public_key_type, private_key_type > get_private_key_from_password(string account, string role, string password) const
Definition wallet.cpp:403
void lock() override
Definition wallet.cpp:340
void set_password(string password) override
Definition wallet.cpp:373
flat_set< public_key_type > list_public_keys() override
Definition wallet.cpp:387
bool import_key(string wif_key) override
Definition wallet.cpp:282
bool copy_wallet_file(string destination_filename)
Definition wallet.cpp:272
std::optional< signature_type > try_sign_digest(const digest_type digest, const public_key_type public_key) override
Definition wallet.cpp:399
void save_wallet_file(string wallet_filename="")
Definition wallet.cpp:320
bool remove_key(string key) override
Definition wallet.cpp:294
#define FC_CAPTURE_AND_RETHROW(...)
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
#define wlog(FORMAT,...)
Definition logger.hpp:124
#define elog(FORMAT,...)
Definition logger.hpp:130
void unpack(Stream &s, std::deque< T > &value)
Definition raw.hpp:540
void pack(Stream &s, const std::deque< T > &value)
Definition raw.hpp:531
fc::sha256 digest(const T &value)
Definition digest.hpp:9
unsigned aes_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, unsigned char *iv, unsigned char *plaintext)
Definition aes.cpp:208
bool exists(const path &p)
unsigned aes_encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key, unsigned char *iv, unsigned char *ciphertext)
Definition aes.cpp:159
void create_directories(const path &p)
void copy(const path &from, const path &to)
path absolute(const path &p)
fc::crypto::private_key private_key_type
Definition types.hpp:77
private_key_type derive_private_key(const std::string &prefix_string, int sequence_number)
Definition wallet.cpp:33
map< public_key_type, private_key_type > keys
Definition wallet.hpp:188
vector< char > cipher_keys
Definition wallet.hpp:18
bool pub
CK_RV ret
char * s
CK_BYTE_PTR pubkey
struct @108 key_type