1 // Aleth: Ethereum C++ client, tools and libraries.
2 // Copyright 2015-2019 Aleth Authors.
3 // Licensed under the GNU General Public License, Version 3.
4 
5 
6 #pragma once
7 
8 #include <functional>
9 #include <mutex>
10 #include <libdevcore/FileSystem.h>
11 #include <libdevcore/CommonData.h>
12 #include <libdevcrypto/SecretStore.h>
13 
14 #include <boost/filesystem.hpp>
15 
16 namespace dev
17 {
18 namespace eth
19 {
20 class PasswordUnknown: public Exception {};
21 
22 struct KeyInfo
23 {
24 	KeyInfo() = default;
passHashKeyInfo25 	KeyInfo(h256 const& _passHash, std::string const& _accountName, std::string const& _passwordHint = std::string()): passHash(_passHash), accountName(_accountName), passwordHint(_passwordHint) {}
26 
27 	/// Hash of the password or h256() / UnknownPassword if unknown.
28 	h256 passHash;
29 	/// Name of the key, or JSON key info if begins with '{'.
30 	std::string accountName;
31 	/// Hint of the password. Alternative place for storage than the hash-based lookup.
32 	std::string passwordHint;
33 };
34 
35 static h256 const UnknownPassword;
36 /// Password query function that never returns a password.
37 static auto const DontKnowThrow = [](){ throw PasswordUnknown(); return std::string(); };
38 
39 enum class SemanticPassword
40 {
41 	Existing,
42 	Master
43 };
44 
45 // TODO: This one is specifically for Ethereum, but we can make it generic in due course.
46 // TODO: hidden-partition style key-store.
47 /**
48  * @brief High-level manager of password-encrypted keys for Ethereum.
49  * Usage:
50  *
51  * Call exists() to check whether there is already a database. If so, get the master password from
52  * the user and call load() with it. If not, get a new master password from the user (get them to type
53  * it twice and keep some hint around!) and call create() with it.
54  *
55  * Uses a "key file" (and a corresponding .salt file) that contains encrypted information about the keys and
56  * a directory called "secrets path" that contains a file for each key.
57  */
58 class KeyManager
59 {
60 public:
61 	enum class NewKeyType { DirectICAP = 0, NoVanity, FirstTwo, FirstTwoNextTwo, FirstThree, FirstFour };
62 
63 	KeyManager(boost::filesystem::path const& _keysFile = defaultPath(), boost::filesystem::path const& _secretsPath = SecretStore::defaultPath());
64 	~KeyManager();
65 
setKeysFile(boost::filesystem::path const & _keysFile)66 	void setKeysFile(boost::filesystem::path const& _keysFile) { m_keysFile = _keysFile; }
keysFile()67 	boost::filesystem::path const& keysFile() const { return m_keysFile; }
68 
69 	bool exists() const;
70 	void create(std::string const& _pass);
71 	bool load(std::string const& _pass);
save(std::string const & _pass)72 	void save(std::string const& _pass) const { write(_pass, m_keysFile); }
73 
notePassword(std::string const & _pass)74 	void notePassword(std::string const& _pass) { m_cachedPasswords[hashPassword(_pass)] = _pass; }
noteHint(std::string const & _pass,std::string const & _hint)75 	void noteHint(std::string const& _pass, std::string const& _hint) { if (!_hint.empty()) m_passwordHint[hashPassword(_pass)] = _hint; }
haveHint(std::string const & _pass)76 	bool haveHint(std::string const& _pass) const { auto h = hashPassword(_pass); return m_cachedPasswords.count(h) && !m_cachedPasswords.at(h).empty(); }
77 
78 	/// @returns the list of account addresses.
79 	Addresses accounts() const;
80 	/// @returns a hashset of all account addresses.
accountsHash()81 	AddressHash accountsHash() const { return AddressHash() + accounts(); }
82 	bool hasAccount(Address const& _address) const;
83 	/// @returns the human-readable name or json-encoded info of the account for the given address.
84 	std::string const& accountName(Address const& _address) const;
85 	/// @returns the password hint for the account for the given address;
86 	std::string const& passwordHint(Address const& _address) const;
87 
88 	/// @returns the uuid of the key for the address @a _a or the empty hash on error.
89 	h128 uuid(Address const& _a) const;
90 	/// @returns the address corresponding to the key with uuid @a _uuid or the zero address on error.
91 	Address address(h128 const& _uuid) const;
92 
93 	h128 import(Secret const& _s, std::string const& _accountName, std::string const& _pass, std::string const& _passwordHint);
import(Secret const & _s,std::string const & _accountName)94 	h128 import(Secret const& _s, std::string const& _accountName) { return import(_s, _accountName, defaultPassword(), std::string()); }
95 
store()96 	SecretStore& store() { return m_store; }
97 	void importExisting(h128 const& _uuid, std::string const& _accountName, std::string const& _pass, std::string const& _passwordHint);
98 	void importExisting(h128 const& _uuid, std::string const& _accountName, Address const& _addr, h256 const& _passHash = h256(), std::string const& _passwordHint = std::string());
99 
100 	/// @returns the secret key associated with an address provided the password query
101 	/// function @a _pass or the zero-secret key on error.
102 	Secret secret(Address const& _address, std::function<std::string()> const& _pass = DontKnowThrow, bool _usePasswordCache = true) const;
103 	/// @returns the secret key associated with the uuid of a key provided the password query
104 	/// function @a _pass or the zero-secret key on error.
105 	Secret secret(h128 const& _uuid, std::function<std::string()> const& _pass = DontKnowThrow, bool _usePasswordCache = true) const;
106 
107 	bool recode(Address const& _address, std::string const& _newPass, std::string const& _hint, std::function<std::string()> const& _pass = DontKnowThrow, KDF _kdf = KDF::Scrypt);
108 
kill(h128 const & _id)109 	void kill(h128 const& _id) { kill(address(_id)); }
110 	void kill(Address const& _a);
111 
defaultPath()112 	static boost::filesystem::path defaultPath() { return getDataDir("ethereum") / boost::filesystem::path("keys.info"); }
113 
114 	/// Extracts the secret key from the presale wallet.
115 	static KeyPair presaleSecret(std::string const& _json, std::function<std::string(bool)> const& _password);
116 
117 	/// @returns new random keypair with given vanity
118 	static  KeyPair newKeyPair(NewKeyType _type);
119 private:
120 	std::string getPassword(h128 const& _uuid, std::function<std::string()> const& _pass = DontKnowThrow) const;
121 	std::string getPassword(h256 const& _passHash, std::function<std::string()> const& _pass = DontKnowThrow) const;
122 	std::string defaultPassword(std::function<std::string()> const& _pass = DontKnowThrow) const { return getPassword(m_master, _pass); }
123 	h256 hashPassword(std::string const& _pass) const;
124 
125 	/// Stores the password by its hash in the password cache.
126 	void cachePassword(std::string const& _password) const;
127 
128 	// Only use if previously loaded ok.
129 	// @returns false if wasn't previously loaded ok.
write()130 	bool write() const { return write(m_keysFile); }
131 	bool write(boost::filesystem::path const& _keysFile) const;
132 	void write(std::string const& _pass, boost::filesystem::path const& _keysFile) const;	// TODO: all passwords should be a secure string.
133 	void write(SecureFixedHash<16> const& _key, boost::filesystem::path const& _keysFile) const;
134 
135 	// Ethereum keys.
136 
137 	/// Mapping key uuid -> address.
138 	std::unordered_map<h128, Address> m_uuidLookup;
139 	/// Mapping address -> key uuid.
140 	std::unordered_map<Address, h128> m_addrLookup;
141 	/// Mapping address -> key info.
142 	std::unordered_map<Address, KeyInfo> m_keyInfo;
143 	/// Mapping password hash -> password hint.
144 	std::unordered_map<h256, std::string> m_passwordHint;
145 
146 	// Passwords that we're storing. Mapping password hash -> password.
147 	mutable std::unordered_map<h256, std::string> m_cachedPasswords;
148 
149 	// DEPRECATED.
150 	// Used to be the default password for keys in the keystore, stored in the keys file.
151 	// Now the default password is based off the key of the keys file directly, so this is redundant
152 	// except for the fact that people have existing keys stored with it. Leave for now until/unless
153 	// we have an upgrade strategy.
154 	std::string m_defaultPasswordDeprecated;
155 
156 	mutable boost::filesystem::path m_keysFile;
157 	mutable SecureFixedHash<16> m_keysFileKey;
158 	mutable h256 m_master;
159 	SecretStore m_store;
160 };
161 
162 }
163 }
164