From 2c37204a69d8d73c5f561d327f2781264fafe9ab Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 11 Jun 2015 17:35:50 +0200 Subject: [PATCH 1/2] KeyManager refactoring to increase readability. --- alethzero/MainWin.cpp | 46 ++++--- alethzero/MainWin.h | 2 +- alethzero/OurWebThreeStubServer.cpp | 2 +- alethzero/Transact.cpp | 7 +- eth/main.cpp | 8 +- ethkey/KeyAux.h | 8 +- libdevcore/CommonData.h | 9 +- libethcore/KeyManager.cpp | 169 +++++++++++++++----------- libethcore/KeyManager.h | 66 ++++++---- libweb3jsonrpc/AccountHolder.cpp | 2 +- libweb3jsonrpc/AccountHolder.h | 1 - libweb3jsonrpc/WebThreeStubServer.cpp | 12 +- 12 files changed, 194 insertions(+), 138 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 848020cbc..297ad0eda 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -235,7 +235,7 @@ Main::Main(QWidget *parent) : // ui->webView->page()->settings()->setAttribute(QWebEngineSettings::DeveloperExtrasEnabled, true); // QWebEngineInspector* inspector = new QWebEngineInspector(); // inspector->setPage(page); - setBeneficiary(*m_keyManager.accounts().begin()); + setBeneficiary(m_keyManager.accounts().front()); ethereum()->setDefault(LatestBlock); @@ -430,9 +430,9 @@ void Main::installBalancesWatch() // TODO: Update for new currencies reg. for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, PendingBlock); ++i) altCoins.push_back(right160(ethereum()->stateAt(coinsAddr, i + 1))); - for (auto const& i: m_keyManager.accounts()) + for (auto const& address: m_keyManager.accounts()) for (auto c: altCoins) - tf.address(c).topic(0, h256(i, h256::AlignRight)); + tf.address(c).topic(0, h256(address, h256::AlignRight)); uninstallWatch(m_balancesFilter); m_balancesFilter = installWatch(tf, [=](LocalisedLogEntries const&){ onBalancesChange(); }); @@ -501,7 +501,7 @@ void Main::load(QString _s) void Main::on_newTransaction_triggered() { - m_transact->setEnvironment(m_keyManager.accounts(), ethereum(), &m_natSpecDB); + m_transact->setEnvironment(m_keyManager.accountsHash(), ethereum(), &m_natSpecDB); m_transact->show(); } @@ -735,18 +735,17 @@ void Main::writeSettings() s.setValue("windowState", saveState()); } -Secret Main::retrieveSecret(Address const& _a) const +Secret Main::retrieveSecret(Address const& _address) const { - auto info = m_keyManager.accountDetails()[_a]; while (true) { - Secret s = m_keyManager.secret(_a, [&](){ + Secret s = m_keyManager.secret(_address, [&](){ QDialog d; Ui_GetPassword gp; gp.setupUi(&d); d.setWindowTitle("Unlock Account"); - gp.label->setText(QString("Enter the password for the account %2 (%1).").arg(QString::fromStdString(_a.abridged())).arg(QString::fromStdString(info.first))); - gp.entry->setPlaceholderText("Hint: " + QString::fromStdString(info.second)); + gp.label->setText(QString("Enter the password for the account %2 (%1).").arg(QString::fromStdString(_address.abridged())).arg(QString::fromStdString(m_keyManager.accountName(_address)))); + gp.entry->setPlaceholderText("Hint: " + QString::fromStdString(m_keyManager.passwordHint(_address))); return d.exec() == QDialog::Accepted ? gp.entry->text().toStdString() : string(); }); if (s || QMessageBox::warning(nullptr, "Unlock Account", "The password you gave is incorrect for this key.", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel) @@ -770,7 +769,7 @@ void Main::readSettings(bool _skipGeometry) for (unsigned i = 0; i < b.size() / sizeof(Secret); ++i) { memcpy(&k, b.data() + i * sizeof(Secret), sizeof(Secret)); - if (!m_keyManager.accounts().count(KeyPair(k).address())) + if (!m_keyManager.hasAccount(KeyPair(k).address())) m_keyManager.import(k, "Imported (UNSAFE) key."); } } @@ -858,7 +857,7 @@ void Main::on_importKey_triggered() if (b.size() == 32) { auto k = KeyPair(h256(b)); - if (!m_keyManager.accounts().count(k.address())) + if (!m_keyManager.hasAccount(k.address())) { QString s = QInputDialog::getText(this, "Import Account Key", "Enter this account's name"); if (QMessageBox::question(this, "Additional Security?", "Would you like to use additional security for this key? This lets you protect it with a different password to other keys, but also means you must re-enter the key's password every time you wish to use the account.", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) @@ -939,7 +938,7 @@ void Main::on_claimPresale_triggered() } cnote << k.address(); - if (!m_keyManager.accounts().count(k.address())) + if (!m_keyManager.hasAccount(k.address())) ethereum()->submitTransaction(k.sec(), ethereum()->balanceAt(k.address()) - gasPrice() * c_txGas, m_beneficiary, {}, c_txGas, gasPrice()); else QMessageBox::warning(this, "Already Have Key", "Could not import the secret key: we already own this account."); @@ -1110,13 +1109,13 @@ void Main::refreshBalances() // cdebug << n << addr << denom << sha3(h256(n).asBytes()); altCoins[addr] = make_tuple(fromRaw(n), 0, denom); }*/ - for (pair> const& i: m_keyManager.accountDetails()) + for (auto const& address: m_keyManager.accounts()) { - u256 b = ethereum()->balanceAt(i.first); - QListWidgetItem* li = new QListWidgetItem(QString("%4 %2: %1 [%3]").arg(formatBalance(b).c_str()).arg(QString::fromStdString(render(i.first))).arg((unsigned)ethereum()->countAt(i.first)).arg(QString::fromStdString(i.second.first)), ui->ourAccounts); - li->setData(Qt::UserRole, QByteArray((char const*)i.first.data(), Address::size)); + u256 b = ethereum()->balanceAt(address); + QListWidgetItem* li = new QListWidgetItem(QString("%4 %2: %1 [%3]").arg(formatBalance(b).c_str()).arg(QString::fromStdString(render(address))).arg((unsigned)ethereum()->countAt(address)).arg(QString::fromStdString(m_keyManager.accountName(address))), ui->ourAccounts); + li->setData(Qt::UserRole, QByteArray((char const*)address.data(), Address::size)); li->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); - li->setCheckState(m_beneficiary == i.first ? Qt::Checked : Qt::Unchecked); + li->setCheckState(m_beneficiary == address ? Qt::Checked : Qt::Unchecked); totalBalance += b; // for (auto& c: altCoins) @@ -2094,9 +2093,8 @@ void Main::on_killAccount_triggered() { auto hba = ui->ourAccounts->currentItem()->data(Qt::UserRole).toByteArray(); Address h((byte const*)hba.data(), Address::ConstructFromPointer); - auto k = m_keyManager.accountDetails()[h]; - QString s = QInputDialog::getText(this, QString::fromStdString("Kill Account " + k.first + "?!"), - QString::fromStdString("Account " + k.first + " (" + render(h) + ") has " + formatBalance(ethereum()->balanceAt(h)) + " in it.\r\nIt, and any contract that this account can access, will be lost forever if you continue. Do NOT continue unless you know what you are doing.\n" + QString s = QInputDialog::getText(this, QString::fromStdString("Kill Account " + m_keyManager.accountName(h) + "?!"), + QString::fromStdString("Account " + m_keyManager.accountName(h) + " (" + render(h) + ") has " + formatBalance(ethereum()->balanceAt(h)) + " in it.\r\nIt, and any contract that this account can access, will be lost forever if you continue. Do NOT continue unless you know what you are doing.\n" "Are you sure you want to continue? \r\n If so, type 'YES' to confirm."), QLineEdit::Normal, "NO"); if (s != "YES") @@ -2104,10 +2102,10 @@ void Main::on_killAccount_triggered() m_keyManager.kill(h); if (m_keyManager.accounts().empty()) m_keyManager.import(Secret::random(), "Default account"); - m_beneficiary = *m_keyManager.accounts().begin(); + m_beneficiary = m_keyManager.accounts().front(); keysChanged(); if (m_beneficiary == h) - setBeneficiary(*m_keyManager.accounts().begin()); + setBeneficiary(m_keyManager.accounts().front()); } } @@ -2128,7 +2126,7 @@ void Main::on_reencryptKey_triggered() return; try { auto pw = [&](){ - auto p = QInputDialog::getText(this, "Re-Encrypt Key", "Enter the original password for this key.\nHint: " + QString::fromStdString(m_keyManager.hint(a)), QLineEdit::Password, QString()).toStdString(); + auto p = QInputDialog::getText(this, "Re-Encrypt Key", "Enter the original password for this key.\nHint: " + QString::fromStdString(m_keyManager.passwordHint(a)), QLineEdit::Password, QString()).toStdString(); if (p.empty()) throw PasswordUnknown(); return p; @@ -2151,7 +2149,7 @@ void Main::on_reencryptAll_triggered() try { for (Address const& a: m_keyManager.accounts()) while (!m_keyManager.recode(a, SemanticPassword::Existing, [&](){ - auto p = QInputDialog::getText(nullptr, "Re-Encrypt Key", QString("Enter the original password for key %1.\nHint: %2").arg(QString::fromStdString(pretty(a))).arg(QString::fromStdString(m_keyManager.hint(a))), QLineEdit::Password, QString()).toStdString(); + auto p = QInputDialog::getText(nullptr, "Re-Encrypt Key", QString("Enter the original password for key %1.\nHint: %2").arg(QString::fromStdString(pretty(a))).arg(QString::fromStdString(m_keyManager.passwordHint(a))), QLineEdit::Password, QString()).toStdString(); if (p.empty()) throw PasswordUnknown(); return p; diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index efff89d2b..f8a6fa6c7 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -96,7 +96,7 @@ public: dev::eth::KeyManager& keyManager() override { return m_keyManager; } bool doConfirm(); - dev::Secret retrieveSecret(dev::Address const& _a) const override; + dev::Secret retrieveSecret(dev::Address const& _address) const override; public slots: void load(QString _file); diff --git a/alethzero/OurWebThreeStubServer.cpp b/alethzero/OurWebThreeStubServer.cpp index e18cb55d5..aaeffa16b 100644 --- a/alethzero/OurWebThreeStubServer.cpp +++ b/alethzero/OurWebThreeStubServer.cpp @@ -136,7 +136,7 @@ void OurAccountHolder::doValidations() AddressHash OurAccountHolder::realAccounts() const { - return m_main->keyManager().accounts(); + return m_main->keyManager().accountsHash(); } bool OurAccountHolder::validateTransaction(TransactionSkeleton const& _t, bool _toProxy) diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index fd466e475..7a26f56f2 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -77,11 +77,10 @@ void Transact::setEnvironment(AddressHash const& _accounts, dev::eth::Client* _e auto old = ui->from->currentIndex(); ui->from->clear(); - for (auto const& i: m_accounts) + for (auto const& address: m_accounts) { - auto d = m_context->keyManager().accountDetails()[i]; - u256 b = ethereum()->balanceAt(i, PendingBlock); - QString s = QString("%4 %2: %1").arg(formatBalance(b).c_str()).arg(QString::fromStdString(m_context->render(i))).arg(QString::fromStdString(d.first)); + u256 b = ethereum()->balanceAt(address, PendingBlock); + QString s = QString("%4 %2: %1").arg(formatBalance(b).c_str()).arg(QString::fromStdString(m_context->render(address))).arg(QString::fromStdString(m_context->keyManager().accountName(address))); ui->from->addItem(s); } if (old > -1 && old < ui->from->count()) diff --git a/eth/main.cpp b/eth/main.cpp index 382858ae7..a7d9d7c00 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -678,7 +678,7 @@ int main(int argc, char** argv) return ret; }; auto getAccountPassword = [&](Address const& a){ - return getPassword("Enter password for address " + keyManager.accountDetails()[a].first + " (" + a.abridged() + "; hint:" + keyManager.accountDetails()[a].second + "): "); + return getPassword("Enter password for address " + keyManager.accountName(a) + " (" + a.abridged() + "; hint:" + keyManager.passwordHint(a) + "): "); }; StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat, structuredLoggingURL); @@ -1122,10 +1122,10 @@ int main(int argc, char** argv) { cout << "Accounts:" << endl; u256 total = 0; - for (auto const& i: keyManager.accountDetails()) + for (auto const& address: keyManager.accounts()) { - auto b = c->balanceAt(i.first); - cout << ((i.first == signingKey) ? "SIGNING " : " ") << ((i.first == beneficiary) ? "COINBASE " : " ") << i.second.first << " (" << i.first << "): " << formatBalance(b) << " = " << b << " wei" << endl; + auto b = c->balanceAt(address); + cout << ((address == signingKey) ? "SIGNING " : " ") << ((address == beneficiary) ? "COINBASE " : " ") << keyManager.accountName(address) << " (" << address << "): " << formatBalance(b) << " = " << b << " wei" << endl; total += b; } cout << "Total: " << formatBalance(total) << " = " << total << " wei" << endl; diff --git a/ethkey/KeyAux.h b/ethkey/KeyAux.h index d2ec13b2a..b7b10ce21 100644 --- a/ethkey/KeyAux.h +++ b/ethkey/KeyAux.h @@ -44,7 +44,7 @@ class BadArgument: public Exception {}; string getAccountPassword(KeyManager& keyManager, Address const& a) { - return getPassword("Enter password for address " + keyManager.accountDetails()[a].first + " (" + a.abridged() + "; hint:" + keyManager.accountDetails()[a].second + "): "); + return getPassword("Enter password for address " + keyManager.accountName(a) + " (" + a.abridged() + "; hint:" + keyManager.passwordHint(a) + "): "); } string createPassword(std::string const& _prompt) @@ -359,20 +359,18 @@ public: nonIcap.push_back(u); else { - std::pair info = wallet.accountDetails()[a]; cout << toUUID(u) << " " << a.abridged(); cout << " " << ICAP(a).encoded(); - cout << " " << info.first << endl; + cout << " " << wallet.accountName(a) << endl; } else bare.push_back(u); for (auto const& u: nonIcap) if (Address a = wallet.address(u)) { - std::pair info = wallet.accountDetails()[a]; cout << toUUID(u) << " " << a.abridged(); cout << " (Not ICAP) "; - cout << " " << info.first << endl; + cout << " " << wallet.accountName(a) << endl; } for (auto const& u: bare) cout << toUUID(u) << " (Bare)" << endl; diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index ddc00e09f..780e8f6f5 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -258,7 +259,7 @@ template std::set& operator+=(std::set& _a, U const& _b return _a; } -/// Insert the contents of a container into an unordered_st +/// Insert the contents of a container into an unordered_set template std::unordered_set& operator+=(std::unordered_set& _a, U const& _b) { for (auto const& i: _b) @@ -280,6 +281,12 @@ template std::set operator+(std::set _a, U const& _b) return _a += _b; } +/// Insert the contents of a container into an unordered_set +template std::unordered_set operator+(std::unordered_set _a, U const& _b) +{ + return _a += _b; +} + /// Concatenate the contents of a container onto a vector template std::vector operator+(std::vector _a, U const& _b) { diff --git a/libethcore/KeyManager.cpp b/libethcore/KeyManager.cpp index 4430a588e..602c60b4a 100644 --- a/libethcore/KeyManager.cpp +++ b/libethcore/KeyManager.cpp @@ -31,7 +31,7 @@ using namespace dev; using namespace eth; namespace fs = boost::filesystem; -KeyManager::KeyManager(std::string const& _keysFile, std::string const& _secretsPath): +KeyManager::KeyManager(string const& _keysFile, string const& _secretsPath): m_keysFile(_keysFile), m_store(_secretsPath) {} @@ -43,13 +43,13 @@ bool KeyManager::exists() const return !contents(m_keysFile + ".salt").empty() && !contents(m_keysFile).empty(); } -void KeyManager::create(std::string const& _pass) +void KeyManager::create(string const& _pass) { - m_password = asString(h256::random().asBytes()); + m_defaultPasswordDeprecated = asString(h256::random().asBytes()); write(_pass, m_keysFile); } -bool KeyManager::recode(Address const& _address, std::string const& _newPass, std::string const& _hint, std::function const& _pass, KDF _kdf) +bool KeyManager::recode(Address const& _address, string const& _newPass, string const& _hint, function const& _pass, KDF _kdf) { noteHint(_newPass, _hint); h128 u = uuid(_address); @@ -61,10 +61,10 @@ bool KeyManager::recode(Address const& _address, std::string const& _newPass, st return true; } -bool KeyManager::recode(Address const& _address, SemanticPassword _newPass, std::function const& _pass, KDF _kdf) +bool KeyManager::recode(Address const& _address, SemanticPassword _newPass, function const& _pass, KDF _kdf) { h128 u = uuid(_address); - std::string p; + string p; if (_newPass == SemanticPassword::Existing) p = getPassword(u, _pass); else if (_newPass == SemanticPassword::Master) @@ -75,41 +75,47 @@ bool KeyManager::recode(Address const& _address, SemanticPassword _newPass, std: return recode(_address, p, string(), _pass, _kdf); } -bool KeyManager::load(std::string const& _pass) +bool KeyManager::load(string const& _pass) { - try { + try + { bytes salt = contents(m_keysFile + ".salt"); bytes encKeys = contents(m_keysFile); - m_key = h128(pbkdf2(_pass, salt, 262144, 16)); - bytes bs = decryptSymNoAuth(m_key, h128(), &encKeys); + m_keysFileKey = h128(pbkdf2(_pass, salt, 262144, 16)); + bytes bs = decryptSymNoAuth(m_keysFileKey, h128(), &encKeys); RLP s(bs); - unsigned version = (unsigned)s[0]; + unsigned version = unsigned(s[0]); if (version == 1) { for (auto const& i: s[1]) { - m_keyInfo[m_addrLookup[(Address)i[0]] = (h128)i[1]] = KeyInfo((h256)i[2], (std::string)i[3]); -// cdebug << toString((Address)i[0]) << toString((h128)i[1]) << toString((h256)i[2]) << (std::string)i[3]; + h128 uuid(i[1]); + Address addr(i[0]); + m_addrLookup[addr] = uuid; + m_keyInfo[uuid] = KeyInfo(h256(i[2]), string(i[3])); +// cdebug << toString(addr) << toString(uuid) << toString((h256)i[2]) << (string)i[3]; } for (auto const& i: s[2]) - m_passwordInfo[(h256)i[0]] = (std::string)i[1]; - m_password = (string)s[3]; + m_passwordHint[h256(i[0])] = string(i[1]); + m_defaultPasswordDeprecated = string(s[3]); } // cdebug << hashPassword(m_password) << toHex(m_password); - m_cachedPasswords[hashPassword(m_password)] = m_password; + cachePassword(m_defaultPasswordDeprecated); // cdebug << hashPassword(asString(m_key.ref())) << m_key.hex(); - m_cachedPasswords[hashPassword(asString(m_key.ref()))] = asString(m_key.ref()); + cachePassword(asString(m_keysFileKey.ref())); // cdebug << hashPassword(_pass) << _pass; - m_cachedPasswords[m_master = hashPassword(_pass)] = _pass; + m_master = hashPassword(_pass); + cachePassword(_pass); return true; } - catch (...) { + catch (...) + { return false; } } -Secret KeyManager::secret(Address const& _address, function const& _pass) const +Secret KeyManager::secret(Address const& _address, function const& _pass) const { auto it = m_addrLookup.find(_address); if (it == m_addrLookup.end()) @@ -117,12 +123,12 @@ Secret KeyManager::secret(Address const& _address, function const return secret(it->second, _pass); } -Secret KeyManager::secret(h128 const& _uuid, function const& _pass) const +Secret KeyManager::secret(h128 const& _uuid, function const& _pass) const { return Secret(m_store.secret(_uuid, [&](){ return getPassword(_uuid, _pass); })); } -std::string KeyManager::getPassword(h128 const& _uuid, function const& _pass) const +string KeyManager::getPassword(h128 const& _uuid, function const& _pass) const { auto kit = m_keyInfo.find(_uuid); h256 ph; @@ -131,19 +137,19 @@ std::string KeyManager::getPassword(h128 const& _uuid, function c return getPassword(ph, _pass); } -std::string KeyManager::getPassword(h256 const& _passHash, function const& _pass) const +string KeyManager::getPassword(h256 const& _passHash, function const& _pass) const { auto it = m_cachedPasswords.find(_passHash); if (it != m_cachedPasswords.end()) return it->second; - for (unsigned i = 0; i< 10; ++i) + for (unsigned i = 0; i < 10; ++i) { - std::string p = _pass(); + string p = _pass(); if (p.empty()) break; - if (hashPassword(p) == _passHash || _passHash == UnknownPassword) + if (_passHash == UnknownPassword || hashPassword(p) == _passHash) { - m_cachedPasswords[hashPassword(p)] = p; + cachePassword(p); return p; } } @@ -166,20 +172,20 @@ Address KeyManager::address(h128 const& _uuid) const return Address(); } -h128 KeyManager::import(Secret const& _s, string const& _info, std::string const& _pass, string const& _passInfo) +h128 KeyManager::import(Secret const& _s, string const& _accountName, string const& _pass, string const& _passwordHint) { Address addr = KeyPair(_s).address(); auto passHash = hashPassword(_pass); - m_cachedPasswords[passHash] = _pass; - m_passwordInfo[passHash] = _passInfo; + cachePassword(_pass); + m_passwordHint[passHash] = _passwordHint; auto uuid = m_store.importSecret(_s.asBytes(), _pass); - m_keyInfo[uuid] = KeyInfo{passHash, _info}; + m_keyInfo[uuid] = KeyInfo{passHash, _accountName}; m_addrLookup[addr] = uuid; write(m_keysFile); return uuid; } -void KeyManager::importExisting(h128 const& _uuid, std::string const& _info, std::string const& _pass, std::string const& _passInfo) +void KeyManager::importExisting(h128 const& _uuid, string const& _info, string const& _pass, string const& _passwordHint) { bytes key = m_store.secret(_uuid, [&](){ return _pass; }); if (key.empty()) @@ -187,17 +193,17 @@ void KeyManager::importExisting(h128 const& _uuid, std::string const& _info, std Address a = KeyPair(Secret(key)).address(); auto passHash = hashPassword(_pass); if (!m_cachedPasswords.count(passHash)) - m_cachedPasswords[passHash] = _pass; - importExisting(_uuid, _info, a, passHash, _passInfo); + cachePassword(_pass); + importExisting(_uuid, _info, a, passHash, _passwordHint); } -void KeyManager::importExisting(h128 const& _uuid, std::string const& _info, Address const& _address, h256 const& _passHash, std::string const& _passInfo) +void KeyManager::importExisting(h128 const& _uuid, string const& _accountName, Address const& _address, h256 const& _passHash, string const& _passwordHint) { - if (!m_passwordInfo.count(_passHash)) - m_passwordInfo[_passHash] = _passInfo; + if (!m_passwordHint.count(_passHash)) + m_passwordHint[_passHash] = _passwordHint; m_addrLookup[_address] = _uuid; m_keyInfo[_uuid].passHash = _passHash; - m_keyInfo[_uuid].info = _info; + m_keyInfo[_uuid].accountName = _accountName; write(m_keysFile); } @@ -209,67 +215,92 @@ void KeyManager::kill(Address const& _a) m_store.kill(id); } -AddressHash KeyManager::accounts() const +Addresses KeyManager::accounts() const { - AddressHash ret; + Addresses ret; + ret.reserve(m_addrLookup.size()); for (auto const& i: m_addrLookup) if (m_keyInfo.count(i.second) > 0) - ret.insert(i.first); + ret.push_back(i.first); return ret; } -std::unordered_map> KeyManager::accountDetails() const +bool KeyManager::hasAccount(const Address& _address) const { - std::unordered_map> ret; - for (auto const& i: m_addrLookup) - if (m_keyInfo.count(i.second) > 0) - ret[i.first] = make_pair(m_keyInfo.count(i.second) ? m_keyInfo.at(i.second).info : "", m_keyInfo.count(i.second) && m_passwordInfo.count(m_keyInfo.at(i.second).passHash) ? m_passwordInfo.at(m_keyInfo.at(i.second).passHash) : ""); - return ret; + return m_addrLookup.count(_address) && m_keyInfo.count(m_addrLookup.at(_address)); +} + +string const& KeyManager::accountName(Address const& _address) const +{ + try + { + return m_keyInfo.at(m_addrLookup.at(_address)).accountName; + } + catch (...) + { + return EmptyString; + } +} + +string const& KeyManager::passwordHint(Address const& _address) const +{ + try + { + return m_passwordHint.at(m_keyInfo.at(m_addrLookup.at(_address)).passHash); + } + catch (...) + { + return EmptyString; + } } -h256 KeyManager::hashPassword(std::string const& _pass) const +h256 KeyManager::hashPassword(string const& _pass) const { // TODO SECURITY: store this a bit more securely; Scrypt perhaps? - return h256(pbkdf2(_pass, asBytes(m_password), 262144, 32)); + return h256(pbkdf2(_pass, asBytes(m_defaultPasswordDeprecated), 262144, 32)); +} + +void KeyManager::cachePassword(string const& _password) const +{ + m_cachedPasswords[hashPassword(_password)] = _password; } -bool KeyManager::write(std::string const& _keysFile) const +bool KeyManager::write(string const& _keysFile) const { - if (!m_key) + if (!m_keysFileKey) return false; - write(m_key, _keysFile); + write(m_keysFileKey, _keysFile); return true; } -void KeyManager::write(std::string const& _pass, std::string const& _keysFile) const +void KeyManager::write(string const& _pass, string const& _keysFile) const { bytes salt = h256::random().asBytes(); writeFile(_keysFile + ".salt", salt); auto key = h128(pbkdf2(_pass, salt, 262144, 16)); - m_cachedPasswords[hashPassword(_pass)] = _pass; + cachePassword(_pass); m_master = hashPassword(_pass); write(key, _keysFile); } -void KeyManager::write(h128 const& _key, std::string const& _keysFile) const +void KeyManager::write(h128 const& _key, string const& _keysFile) const { RLPStream s(4); - s << 1; - s.appendList(m_addrLookup.size()); - for (auto const& i: m_addrLookup) - if (m_keyInfo.count(i.second)) - { - auto ki = m_keyInfo.at(i.second); - s.appendList(4) << i.first << i.second << ki.passHash << ki.info; - } - s.appendList(m_passwordInfo.size()); - for (auto const& i: m_passwordInfo) + s << 1; // version + s.appendList(accounts().size()); + for (auto const& address: accounts()) + { + h128 id = uuid(address); + auto const& ki = m_keyInfo.at(id); + s.appendList(4) << address << id << ki.passHash << ki.accountName; + } + s.appendList(m_passwordHint.size()); + for (auto const& i: m_passwordHint) s.appendList(2) << i.first << i.second; - s.append(m_password); + s.append(m_defaultPasswordDeprecated); writeFile(_keysFile, encryptSymNoAuth(_key, h128(), &s.out())); - m_key = _key; - m_cachedPasswords[hashPassword(defaultPassword())] = defaultPassword(); - + m_keysFileKey = _key; + cachePassword(defaultPassword()); } diff --git a/libethcore/KeyManager.h b/libethcore/KeyManager.h index 62263c3c5..d9bb6457c 100644 --- a/libethcore/KeyManager.h +++ b/libethcore/KeyManager.h @@ -23,8 +23,9 @@ #include #include -#include #include +#include +#include namespace dev { @@ -35,14 +36,15 @@ class PasswordUnknown: public Exception {}; struct KeyInfo { KeyInfo() = default; - KeyInfo(h256 const& _passHash, std::string const& _info): passHash(_passHash), info(_info) {} + KeyInfo(h256 const& _passHash, std::string const& _accountName): passHash(_passHash), accountName(_accountName) {} - h256 passHash; ///< Hash of the password or h256() if unknown. - std::string info; ///< Name of the key, or JSON key info if begins with '{'. + h256 passHash; ///< Hash of the password or h256() / UnknownPassword if unknown. + std::string accountName; ///< Name of the key, or JSON key info if begins with '{'. }; -static const h256 UnknownPassword; -static const auto DontKnowThrow = [](){ throw PasswordUnknown(); return std::string(); }; +static h256 const UnknownPassword; +/// Password query function that never returns a password. +static auto const DontKnowThrow = [](){ throw PasswordUnknown(); return std::string(); }; enum class SemanticPassword { @@ -53,12 +55,15 @@ enum class SemanticPassword // TODO: This one is specifically for Ethereum, but we can make it generic in due course. // TODO: hidden-partition style key-store. /** - * @brief High-level manager of keys for Ethereum. + * @brief High-level manager of password-encrypted keys for Ethereum. * Usage: * * Call exists() to check whether there is already a database. If so, get the master password from * the user and call load() with it. If not, get a new master password from the user (get them to type * it twice and keep some hint around!) and call create() with it. + * + * Uses a "key file" (and a corresponding .salt file) that contains encrypted information about the keys and + * a directory called "secrets path" that contains a file for each key. */ class KeyManager { @@ -75,25 +80,37 @@ public: void save(std::string const& _pass) const { write(_pass, m_keysFile); } void notePassword(std::string const& _pass) { m_cachedPasswords[hashPassword(_pass)] = _pass; } - void noteHint(std::string const& _pass, std::string const& _hint) { if (!_hint.empty()) m_passwordInfo[hashPassword(_pass)] = _hint; } + void noteHint(std::string const& _pass, std::string const& _hint) { if (!_hint.empty()) m_passwordHint[hashPassword(_pass)] = _hint; } bool haveHint(std::string const& _pass) const { auto h = hashPassword(_pass); return m_cachedPasswords.count(h) && !m_cachedPasswords.at(h).empty(); } - AddressHash accounts() const; - std::unordered_map> accountDetails() const; - std::string const& hint(Address const& _a) const { try { return m_passwordInfo.at(m_keyInfo.at(m_addrLookup.at(_a)).passHash); } catch (...) { return EmptyString; } } - + /// @returns the list of account addresses. + Addresses accounts() const; + /// @returns a hashset of all account addresses. + AddressHash accountsHash() const { return AddressHash() + accounts(); } + bool hasAccount(Address const& _address) const; + /// @returns the human-readable name or json-encoded info of the account for the given address. + std::string const& accountName(Address const& _address) const; + /// @returns the password hint for the account for the given address; + std::string const& passwordHint(Address const& _address) const; + + /// @returns the uuid of the key for the address @a _a or the empty hash on error. h128 uuid(Address const& _a) const; + /// @returns the address corresponding to the key with uuid @a _uuid or the zero address on error. Address address(h128 const& _uuid) const; - h128 import(Secret const& _s, std::string const& _info, std::string const& _pass, std::string const& _passInfo); - h128 import(Secret const& _s, std::string const& _info) { return import(_s, _info, defaultPassword(), std::string()); } + h128 import(Secret const& _s, std::string const& _accountName, std::string const& _pass, std::string const& _passwordHint); + h128 import(Secret const& _s, std::string const& _accountName) { return import(_s, _accountName, defaultPassword(), std::string()); } SecretStore& store() { return m_store; } - void importExisting(h128 const& _uuid, std::string const& _info, std::string const& _pass, std::string const& _passInfo); - void importExisting(h128 const& _uuid, std::string const& _info) { importExisting(_uuid, _info, defaultPassword(), std::string()); } - void importExisting(h128 const& _uuid, std::string const& _info, Address const& _addr, h256 const& _passHash = h256(), std::string const& _passInfo = std::string()); + void importExisting(h128 const& _uuid, std::string const& _accountName, std::string const& _pass, std::string const& _passwordHint); + void importExisting(h128 const& _uuid, std::string const& _accountName) { importExisting(_uuid, _accountName, defaultPassword(), std::string()); } + void importExisting(h128 const& _uuid, std::string const& _accountName, Address const& _addr, h256 const& _passHash = h256(), std::string const& _passwordHint = std::string()); + /// @returns the secret key associated with an address provided the password query + /// function @a _pass or the zero-secret key on error. Secret secret(Address const& _address, std::function const& _pass = DontKnowThrow) const; + /// @returns the secret key associated with the uuid of a key provided the password query + /// function @a _pass or the zero-secret key on error. Secret secret(h128 const& _uuid, std::function const& _pass = DontKnowThrow) const; bool recode(Address const& _address, SemanticPassword _newPass, std::function const& _pass = DontKnowThrow, KDF _kdf = KDF::Scrypt); @@ -110,6 +127,9 @@ private: std::string defaultPassword(std::function const& _pass = DontKnowThrow) const { return getPassword(m_master, _pass); } h256 hashPassword(std::string const& _pass) const; + /// Stores the password by its hash in the password cache. + void cachePassword(std::string const& _password) const; + // Only use if previously loaded ok. // @returns false if wasn't previously loaded ok. bool write() const { return write(m_keysFile); } @@ -118,11 +138,15 @@ private: void write(h128 const& _key, std::string const& _keysFile) const; // Ethereum keys. + + /// Mapping address -> key uuid. std::unordered_map m_addrLookup; + /// Mapping key uuid -> key info. std::unordered_map m_keyInfo; - std::unordered_map m_passwordInfo; + /// Mapping password hash -> password hint. + std::unordered_map m_passwordHint; - // Passwords that we're storing. + // Passwords that we're storing. Mapping password hash -> password. mutable std::unordered_map m_cachedPasswords; // DEPRECATED. @@ -130,10 +154,10 @@ private: // Now the default password is based off the key of the keys file directly, so this is redundant // except for the fact that people have existing keys stored with it. Leave for now until/unless // we have an upgrade strategy. - std::string m_password; + std::string m_defaultPasswordDeprecated; mutable std::string m_keysFile; - mutable h128 m_key; + mutable h128 m_keysFileKey; mutable h256 m_master; SecretStore m_store; }; diff --git a/libweb3jsonrpc/AccountHolder.cpp b/libweb3jsonrpc/AccountHolder.cpp index abd0a1adf..3250eae6b 100644 --- a/libweb3jsonrpc/AccountHolder.cpp +++ b/libweb3jsonrpc/AccountHolder.cpp @@ -103,7 +103,7 @@ void AccountHolder::clearQueue(int _id) AddressHash SimpleAccountHolder::realAccounts() const { - return m_keyManager.accounts(); + return m_keyManager.accountsHash(); } void SimpleAccountHolder::authenticate(dev::eth::TransactionSkeleton const& _t) diff --git a/libweb3jsonrpc/AccountHolder.h b/libweb3jsonrpc/AccountHolder.h index 10b036226..559f8509a 100644 --- a/libweb3jsonrpc/AccountHolder.h +++ b/libweb3jsonrpc/AccountHolder.h @@ -48,7 +48,6 @@ class AccountHolder public: explicit AccountHolder(std::function const& _client): m_client(_client) {} - // easiest to return keyManager.addresses(); virtual AddressHash realAccounts() const = 0; // use m_web3's submitTransaction // or use AccountHolder::queueTransaction(_t) to accept diff --git a/libweb3jsonrpc/WebThreeStubServer.cpp b/libweb3jsonrpc/WebThreeStubServer.cpp index adef51033..f0e532c5c 100644 --- a/libweb3jsonrpc/WebThreeStubServer.cpp +++ b/libweb3jsonrpc/WebThreeStubServer.cpp @@ -157,19 +157,19 @@ Json::Value WebThreeStubServer::admin_eth_allAccounts(std::string const& _sessio u256 total = 0; u256 pendingtotal = 0; Address beneficiary; - for (auto const& i: m_keyMan.accountDetails()) + for (auto const& address: m_keyMan.accounts()) { - auto pending = m_web3.ethereum()->balanceAt(i.first, PendingBlock); - auto latest = m_web3.ethereum()->balanceAt(i.first, LatestBlock); + auto pending = m_web3.ethereum()->balanceAt(address, PendingBlock); + auto latest = m_web3.ethereum()->balanceAt(address, LatestBlock); Json::Value a; - if (i.first == beneficiary) + if (address == beneficiary) a["beneficiary"] = true; - a["address"] = toJS(i.first); + a["address"] = toJS(address); a["balance"] = toJS(latest); a["nicebalance"] = formatBalance(latest); a["pending"] = toJS(pending); a["nicepending"] = formatBalance(pending); - ret["accounts"][i.second.first] = a; + ret["accounts"][m_keyMan.accountName(address)] = a; total += latest; pendingtotal += pending; } From 3264250b5de65b0db8a041826bbd8e38a11bb1a9 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 17 Jun 2015 14:35:55 +0200 Subject: [PATCH 2/2] Removed spaces between code and comment. --- libethcore/KeyManager.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libethcore/KeyManager.h b/libethcore/KeyManager.h index d9bb6457c..a2b5a4e07 100644 --- a/libethcore/KeyManager.h +++ b/libethcore/KeyManager.h @@ -38,8 +38,10 @@ struct KeyInfo KeyInfo() = default; KeyInfo(h256 const& _passHash, std::string const& _accountName): passHash(_passHash), accountName(_accountName) {} - h256 passHash; ///< Hash of the password or h256() / UnknownPassword if unknown. - std::string accountName; ///< Name of the key, or JSON key info if begins with '{'. + /// Hash of the password or h256() / UnknownPassword if unknown. + h256 passHash; + /// Name of the key, or JSON key info if begins with '{'. + std::string accountName; }; static h256 const UnknownPassword;