123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484 |
- /*
- This file is part of cpp-ethereum.
- cpp-ethereum is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- cpp-ethereum is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
- */
- /** @file KeyManager.cpp
- * @author Gav Wood <i@gavwood.com>
- * @date 2014
- */
- #include "KeyManager.h"
- #include <thread>
- #include <mutex>
- #include <boost/filesystem.hpp>
- #include <json_spirit/JsonSpiritHeaders.h>
- #include <libdevcore/Log.h>
- #include <libdevcore/Guards.h>
- #include <libdevcore/RLP.h>
- #include <libdevcore/SHA3.h>
- using namespace std;
- using namespace dev;
- using namespace eth;
- namespace js = json_spirit;
- namespace fs = boost::filesystem;
- KeyManager::KeyManager(string const& _keysFile, string const& _secretsPath):
- m_keysFile(_keysFile), m_store(_secretsPath)
- {
- for (auto const& uuid: m_store.keys())
- {
- auto addr = m_store.address(uuid);
- m_addrLookup[addr] = uuid;
- m_uuidLookup[uuid] = addr;
- }
- }
- KeyManager::~KeyManager()
- {}
- bool KeyManager::exists() const
- {
- return !contents(m_keysFile + ".salt").empty() && !contents(m_keysFile).empty();
- }
- void KeyManager::create(string const& _pass)
- {
- m_defaultPasswordDeprecated = asString(h256::random().asBytes());
- write(_pass, m_keysFile);
- }
- bool KeyManager::recode(Address const& _address, string const& _newPass, string const& _hint, function<string()> const& _pass, KDF _kdf)
- {
- noteHint(_newPass, _hint);
- h128 u = uuid(_address);
- if (!store().recode(u, _newPass, [&](){ return getPassword(u, _pass); }, _kdf))
- return false;
- m_keyInfo[_address].passHash = hashPassword(_newPass);
- write();
- return true;
- }
- bool KeyManager::recode(Address const& _address, SemanticPassword _newPass, function<string()> const& _pass, KDF _kdf)
- {
- h128 u = uuid(_address);
- string p;
- if (_newPass == SemanticPassword::Existing)
- p = getPassword(u, _pass);
- else if (_newPass == SemanticPassword::Master)
- p = defaultPassword();
- else
- return false;
- return recode(_address, p, string(), _pass, _kdf);
- }
- bool KeyManager::load(string const& _pass)
- {
- try
- {
- bytes salt = contents(m_keysFile + ".salt");
- bytes encKeys = contents(m_keysFile);
- if (encKeys.empty())
- return false;
- m_keysFileKey = SecureFixedHash<16>(pbkdf2(_pass, salt, 262144, 16));
- bytesSec bs = decryptSymNoAuth(m_keysFileKey, h128(), &encKeys);
- RLP s(bs.ref());
- unsigned version = unsigned(s[0]);
- if (version == 1)
- {
- bool saveRequired = false;
- for (auto const& i: s[1])
- {
- h128 uuid(i[1]);
- Address addr(i[0]);
- if (uuid)
- {
- if (m_store.contains(uuid))
- {
- m_addrLookup[addr] = uuid;
- m_uuidLookup[uuid] = addr;
- m_keyInfo[addr] = KeyInfo(h256(i[2]), string(i[3]), i.itemCount() > 4 ? string(i[4]) : "");
- if (m_store.noteAddress(uuid, addr))
- saveRequired = true;
- }
- else
- cwarn << "Missing key:" << uuid << addr;
- }
- else
- {
- // TODO: brain wallet.
- m_keyInfo[addr] = KeyInfo(h256(i[2]), string(i[3]), i.itemCount() > 4 ? string(i[4]) : "");
- }
- // cdebug << toString(addr) << toString(uuid) << toString((h256)i[2]) << (string)i[3];
- }
- if (saveRequired)
- m_store.save();
- for (auto const& i: s[2])
- m_passwordHint[h256(i[0])] = string(i[1]);
- m_defaultPasswordDeprecated = string(s[3]);
- }
- // cdebug << hashPassword(m_password) << toHex(m_password);
- cachePassword(m_defaultPasswordDeprecated);
- // cdebug << hashPassword(asString(m_key.ref())) << m_key.hex();
- cachePassword(asString(m_keysFileKey.ref()));
- // cdebug << hashPassword(_pass) << _pass;
- m_master = hashPassword(_pass);
- cachePassword(_pass);
- return true;
- }
- catch (...)
- {
- return false;
- }
- }
- Secret KeyManager::secret(Address const& _address, function<string()> const& _pass, bool _usePasswordCache) const
- {
- if (m_addrLookup.count(_address))
- return secret(m_addrLookup.at(_address), _pass, _usePasswordCache);
- else
- return brain(_pass());
- }
- Secret KeyManager::secret(h128 const& _uuid, function<string()> const& _pass, bool _usePasswordCache) const
- {
- if (_usePasswordCache)
- return Secret(m_store.secret(_uuid, [&](){ return getPassword(_uuid, _pass); }, _usePasswordCache));
- else
- return Secret(m_store.secret(_uuid, _pass, _usePasswordCache));
- }
- string KeyManager::getPassword(h128 const& _uuid, function<string()> const& _pass) const
- {
- h256 ph;
- auto ait = m_uuidLookup.find(_uuid);
- if (ait != m_uuidLookup.end())
- {
- auto kit = m_keyInfo.find(ait->second);
- if (kit != m_keyInfo.end())
- ph = kit->second.passHash;
- }
- return getPassword(ph, _pass);
- }
- string KeyManager::getPassword(h256 const& _passHash, function<string()> const& _pass) const
- {
- auto it = m_cachedPasswords.find(_passHash);
- if (it != m_cachedPasswords.end())
- return it->second;
- for (unsigned i = 0; i < 10; ++i)
- {
- string p = _pass();
- if (p.empty())
- break;
- if (_passHash == UnknownPassword || hashPassword(p) == _passHash)
- {
- cachePassword(p);
- return p;
- }
- }
- return string();
- }
- h128 KeyManager::uuid(Address const& _a) const
- {
- auto it = m_addrLookup.find(_a);
- if (it == m_addrLookup.end())
- return h128();
- return it->second;
- }
- Address KeyManager::address(h128 const& _uuid) const
- {
- auto it = m_uuidLookup.find(_uuid);
- if (it == m_uuidLookup.end())
- return Address();
- return it->second;
- }
- h128 KeyManager::import(Secret const& _s, string const& _accountName, string const& _pass, string const& _passwordHint)
- {
- Address addr = KeyPair(_s).address();
- auto passHash = hashPassword(_pass);
- cachePassword(_pass);
- m_passwordHint[passHash] = _passwordHint;
- auto uuid = m_store.importSecret(_s.asBytesSec(), _pass);
- m_keyInfo[addr] = KeyInfo{passHash, _accountName, ""};
- m_addrLookup[addr] = uuid;
- m_uuidLookup[uuid] = addr;
- write(m_keysFile);
- return uuid;
- }
- Secret KeyManager::brain(string const& _seed)
- {
- h256 r = sha3(_seed);
- for (auto i = 0; i < 16384; ++i)
- r = sha3(r);
- Secret ret(r);
- r.ref().cleanse();
- while (toAddress(ret)[0])
- ret = sha3(ret);
- return ret;
- }
- Secret KeyManager::subkey(Secret const& _s, unsigned _index)
- {
- RLPStream out(2);
- out << _s.ref();
- out << _index;
- bytesSec r;
- out.swapOut(r.writable());
- return sha3(r);
- }
- Address KeyManager::importBrain(string const& _seed, string const& _accountName, string const& _passwordHint)
- {
- Address addr = toAddress(brain(_seed));
- m_keyInfo[addr].accountName = _accountName;
- m_keyInfo[addr].passwordHint = _passwordHint;
- write();
- return addr;
- }
- void KeyManager::importExistingBrain(Address const& _a, string const& _accountName, string const& _passwordHint)
- {
- m_keyInfo[_a].accountName = _accountName;
- m_keyInfo[_a].passwordHint = _passwordHint;
- write();
- }
- void KeyManager::importExisting(h128 const& _uuid, string const& _info, string const& _pass, string const& _passwordHint)
- {
- bytesSec key = m_store.secret(_uuid, [&](){ return _pass; });
- if (key.empty())
- return;
- Address a = KeyPair(Secret(key)).address();
- auto passHash = hashPassword(_pass);
- if (!m_cachedPasswords.count(passHash))
- cachePassword(_pass);
- importExisting(_uuid, _info, a, passHash, _passwordHint);
- }
- void KeyManager::importExisting(h128 const& _uuid, string const& _accountName, Address const& _address, h256 const& _passHash, string const& _passwordHint)
- {
- if (!m_passwordHint.count(_passHash))
- m_passwordHint[_passHash] = _passwordHint;
- m_uuidLookup[_uuid] = _address;
- m_addrLookup[_address] = _uuid;
- m_keyInfo[_address].passHash = _passHash;
- m_keyInfo[_address].accountName = _accountName;
- write(m_keysFile);
- }
- void KeyManager::kill(Address const& _a)
- {
- auto id = m_addrLookup[_a];
- m_uuidLookup.erase(id);
- m_addrLookup.erase(_a);
- m_keyInfo.erase(_a);
- m_store.kill(id);
- write(m_keysFile);
- }
- KeyPair KeyManager::presaleSecret(std::string const& _json, function<string(bool)> const& _password)
- {
- js::mValue val;
- json_spirit::read_string(_json, val);
- auto obj = val.get_obj();
- string p = _password(true);
- if (obj["encseed"].type() == js::str_type)
- {
- auto encseed = fromHex(obj["encseed"].get_str());
- KeyPair k;
- for (bool gotit = false; !gotit;)
- {
- gotit = true;
- k = KeyPair::fromEncryptedSeed(&encseed, p);
- if (obj["ethaddr"].type() == js::str_type)
- {
- Address a(obj["ethaddr"].get_str());
- Address b = k.address();
- if (a != b)
- {
- if ((p = _password(false)).empty())
- BOOST_THROW_EXCEPTION(PasswordUnknown());
- else
- gotit = false;
- }
- }
- }
- return k;
- }
- else
- BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("encseed type is not js::str_type"));
- }
- Addresses KeyManager::accounts() const
- {
- set<Address> addresses;
- for (auto const& i: m_keyInfo)
- addresses.insert(i.first);
- for (auto const& key: m_store.keys())
- addresses.insert(m_store.address(key));
- // Remove the zero address if present
- return Addresses{addresses.upper_bound(Address()), addresses.end()};
- }
- bool KeyManager::hasAccount(Address const& _address) const
- {
- if (!_address)
- return false;
- if (m_keyInfo.count(_address))
- return true;
- for (auto const& key: m_store.keys())
- if (m_store.address(key) == _address)
- return true;
- return false;
- }
- string const& KeyManager::accountName(Address const& _address) const
- {
- try
- {
- return m_keyInfo.at(_address).accountName;
- }
- catch (...)
- {
- return EmptyString;
- }
- }
- void KeyManager::changeName(Address const& _address, std::string const& _name)
- {
- auto it = m_keyInfo.find(_address);
- if (it != m_keyInfo.end())
- {
- it->second.accountName = _name;
- write(m_keysFile);
- }
- }
- string const& KeyManager::passwordHint(Address const& _address) const
- {
- try
- {
- auto& info = m_keyInfo.at(_address);
- if (info.passwordHint.size())
- return info.passwordHint;
- return m_passwordHint.at(info.passHash);
- }
- catch (...)
- {
- return EmptyString;
- }
- }
- h256 KeyManager::hashPassword(string const& _pass) const
- {
- // TODO SECURITY: store this a bit more securely; Scrypt perhaps?
- return h256(pbkdf2(_pass, asBytes(m_defaultPasswordDeprecated), 262144, 32).makeInsecure());
- }
- void KeyManager::cachePassword(string const& _password) const
- {
- m_cachedPasswords[hashPassword(_password)] = _password;
- }
- bool KeyManager::write(string const& _keysFile) const
- {
- if (!m_keysFileKey)
- return false;
- write(m_keysFileKey, _keysFile);
- return true;
- }
- void KeyManager::write(string const& _pass, string const& _keysFile) const
- {
- bytes salt = h256::random().asBytes();
- writeFile(_keysFile + ".salt", salt, true);
- auto key = SecureFixedHash<16>(pbkdf2(_pass, salt, 262144, 16));
- cachePassword(_pass);
- m_master = hashPassword(_pass);
- write(key, _keysFile);
- }
- void KeyManager::write(SecureFixedHash<16> const& _key, string const& _keysFile) const
- {
- RLPStream s(4);
- s << 1; // version
- s.appendList(m_keyInfo.size());
- for (auto const& info: m_keyInfo)
- {
- h128 id = uuid(info.first);
- auto const& ki = info.second;
- s.appendList(5) << info.first << id << ki.passHash << ki.accountName << ki.passwordHint;
- }
- s.appendList(m_passwordHint.size());
- for (auto const& i: m_passwordHint)
- s.appendList(2) << i.first << i.second;
- s.append(m_defaultPasswordDeprecated);
- writeFile(_keysFile, encryptSymNoAuth(_key, h128(), &s.out()), true);
- m_keysFileKey = _key;
- cachePassword(defaultPassword());
- }
- KeyPair KeyManager::newKeyPair(KeyManager::NewKeyType _type)
- {
- KeyPair p;
- bool keepGoing = true;
- unsigned done = 0;
- function<void()> f = [&]() {
- KeyPair lp;
- while (keepGoing)
- {
- done++;
- if (done % 1000 == 0)
- cnote << "Tried" << done << "keys";
- lp = KeyPair::create();
- auto a = lp.address();
- if (_type == NewKeyType::NoVanity ||
- (_type == NewKeyType::DirectICAP && !a[0]) ||
- (_type == NewKeyType::FirstTwo && a[0] == a[1]) ||
- (_type == NewKeyType::FirstTwoNextTwo && a[0] == a[1] && a[2] == a[3]) ||
- (_type == NewKeyType::FirstThree && a[0] == a[1] && a[1] == a[2]) ||
- (_type == NewKeyType::FirstFour && a[0] == a[1] && a[1] == a[2] && a[2] == a[3])
- )
- break;
- }
- if (keepGoing)
- p = lp;
- keepGoing = false;
- };
- vector<std::thread*> ts;
- for (unsigned t = 0; t < std::thread::hardware_concurrency() - 1; ++t)
- ts.push_back(new std::thread(f));
- f();
- for (std::thread* t: ts)
- {
- t->join();
- delete t;
- }
- return p;
- }
|