Browse Source

KeyManager refactoring to increase readability.

cl-refactor
chriseth 10 years ago
parent
commit
2c37204a69
  1. 46
      alethzero/MainWin.cpp
  2. 2
      alethzero/MainWin.h
  3. 2
      alethzero/OurWebThreeStubServer.cpp
  4. 7
      alethzero/Transact.cpp
  5. 8
      eth/main.cpp
  6. 8
      ethkey/KeyAux.h
  7. 9
      libdevcore/CommonData.h
  8. 163
      libethcore/KeyManager.cpp
  9. 66
      libethcore/KeyManager.h
  10. 2
      libweb3jsonrpc/AccountHolder.cpp
  11. 1
      libweb3jsonrpc/AccountHolder.h
  12. 12
      libweb3jsonrpc/WebThreeStubServer.cpp

46
alethzero/MainWin.cpp

@ -235,7 +235,7 @@ Main::Main(QWidget *parent) :
// ui->webView->page()->settings()->setAttribute(QWebEngineSettings::DeveloperExtrasEnabled, true); // ui->webView->page()->settings()->setAttribute(QWebEngineSettings::DeveloperExtrasEnabled, true);
// QWebEngineInspector* inspector = new QWebEngineInspector(); // QWebEngineInspector* inspector = new QWebEngineInspector();
// inspector->setPage(page); // inspector->setPage(page);
setBeneficiary(*m_keyManager.accounts().begin()); setBeneficiary(m_keyManager.accounts().front());
ethereum()->setDefault(LatestBlock); ethereum()->setDefault(LatestBlock);
@ -430,9 +430,9 @@ void Main::installBalancesWatch()
// TODO: Update for new currencies reg. // TODO: Update for new currencies reg.
for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, PendingBlock); ++i) for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, PendingBlock); ++i)
altCoins.push_back(right160(ethereum()->stateAt(coinsAddr, i + 1))); 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) 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); uninstallWatch(m_balancesFilter);
m_balancesFilter = installWatch(tf, [=](LocalisedLogEntries const&){ onBalancesChange(); }); m_balancesFilter = installWatch(tf, [=](LocalisedLogEntries const&){ onBalancesChange(); });
@ -501,7 +501,7 @@ void Main::load(QString _s)
void Main::on_newTransaction_triggered() 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(); m_transact->show();
} }
@ -735,18 +735,17 @@ void Main::writeSettings()
s.setValue("windowState", saveState()); 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) while (true)
{ {
Secret s = m_keyManager.secret(_a, [&](){ Secret s = m_keyManager.secret(_address, [&](){
QDialog d; QDialog d;
Ui_GetPassword gp; Ui_GetPassword gp;
gp.setupUi(&d); gp.setupUi(&d);
d.setWindowTitle("Unlock Account"); 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.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(info.second)); gp.entry->setPlaceholderText("Hint: " + QString::fromStdString(m_keyManager.passwordHint(_address)));
return d.exec() == QDialog::Accepted ? gp.entry->text().toStdString() : string(); 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) 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) for (unsigned i = 0; i < b.size() / sizeof(Secret); ++i)
{ {
memcpy(&k, b.data() + i * sizeof(Secret), sizeof(Secret)); 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."); m_keyManager.import(k, "Imported (UNSAFE) key.");
} }
} }
@ -858,7 +857,7 @@ void Main::on_importKey_triggered()
if (b.size() == 32) if (b.size() == 32)
{ {
auto k = KeyPair(h256(b)); 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"); 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) 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(); 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()); ethereum()->submitTransaction(k.sec(), ethereum()->balanceAt(k.address()) - gasPrice() * c_txGas, m_beneficiary, {}, c_txGas, gasPrice());
else else
QMessageBox::warning(this, "Already Have Key", "Could not import the secret key: we already own this account."); 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()); // cdebug << n << addr << denom << sha3(h256(n).asBytes());
altCoins[addr] = make_tuple(fromRaw(n), 0, denom); altCoins[addr] = make_tuple(fromRaw(n), 0, denom);
}*/ }*/
for (pair<Address, std::pair<std::string, std::string>> const& i: m_keyManager.accountDetails()) for (auto const& address: m_keyManager.accounts())
{ {
u256 b = ethereum()->balanceAt(i.first); u256 b = ethereum()->balanceAt(address);
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); 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*)i.first.data(), Address::size)); li->setData(Qt::UserRole, QByteArray((char const*)address.data(), Address::size));
li->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); 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; totalBalance += b;
// for (auto& c: altCoins) // for (auto& c: altCoins)
@ -2094,9 +2093,8 @@ void Main::on_killAccount_triggered()
{ {
auto hba = ui->ourAccounts->currentItem()->data(Qt::UserRole).toByteArray(); auto hba = ui->ourAccounts->currentItem()->data(Qt::UserRole).toByteArray();
Address h((byte const*)hba.data(), Address::ConstructFromPointer); Address h((byte const*)hba.data(), Address::ConstructFromPointer);
auto k = m_keyManager.accountDetails()[h]; QString s = QInputDialog::getText(this, QString::fromStdString("Kill Account " + m_keyManager.accountName(h) + "?!"),
QString s = QInputDialog::getText(this, QString::fromStdString("Kill Account " + k.first + "?!"), 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"
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"
"Are you sure you want to continue? \r\n If so, type 'YES' to confirm."), "Are you sure you want to continue? \r\n If so, type 'YES' to confirm."),
QLineEdit::Normal, "NO"); QLineEdit::Normal, "NO");
if (s != "YES") if (s != "YES")
@ -2104,10 +2102,10 @@ void Main::on_killAccount_triggered()
m_keyManager.kill(h); m_keyManager.kill(h);
if (m_keyManager.accounts().empty()) if (m_keyManager.accounts().empty())
m_keyManager.import(Secret::random(), "Default account"); m_keyManager.import(Secret::random(), "Default account");
m_beneficiary = *m_keyManager.accounts().begin(); m_beneficiary = m_keyManager.accounts().front();
keysChanged(); keysChanged();
if (m_beneficiary == h) if (m_beneficiary == h)
setBeneficiary(*m_keyManager.accounts().begin()); setBeneficiary(m_keyManager.accounts().front());
} }
} }
@ -2128,7 +2126,7 @@ void Main::on_reencryptKey_triggered()
return; return;
try { try {
auto pw = [&](){ 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()) if (p.empty())
throw PasswordUnknown(); throw PasswordUnknown();
return p; return p;
@ -2151,7 +2149,7 @@ void Main::on_reencryptAll_triggered()
try { try {
for (Address const& a: m_keyManager.accounts()) for (Address const& a: m_keyManager.accounts())
while (!m_keyManager.recode(a, SemanticPassword::Existing, [&](){ 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()) if (p.empty())
throw PasswordUnknown(); throw PasswordUnknown();
return p; return p;

2
alethzero/MainWin.h

@ -96,7 +96,7 @@ public:
dev::eth::KeyManager& keyManager() override { return m_keyManager; } dev::eth::KeyManager& keyManager() override { return m_keyManager; }
bool doConfirm(); bool doConfirm();
dev::Secret retrieveSecret(dev::Address const& _a) const override; dev::Secret retrieveSecret(dev::Address const& _address) const override;
public slots: public slots:
void load(QString _file); void load(QString _file);

2
alethzero/OurWebThreeStubServer.cpp

@ -136,7 +136,7 @@ void OurAccountHolder::doValidations()
AddressHash OurAccountHolder::realAccounts() const AddressHash OurAccountHolder::realAccounts() const
{ {
return m_main->keyManager().accounts(); return m_main->keyManager().accountsHash();
} }
bool OurAccountHolder::validateTransaction(TransactionSkeleton const& _t, bool _toProxy) bool OurAccountHolder::validateTransaction(TransactionSkeleton const& _t, bool _toProxy)

7
alethzero/Transact.cpp

@ -77,11 +77,10 @@ void Transact::setEnvironment(AddressHash const& _accounts, dev::eth::Client* _e
auto old = ui->from->currentIndex(); auto old = ui->from->currentIndex();
ui->from->clear(); 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(address, PendingBlock);
u256 b = ethereum()->balanceAt(i, 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)));
QString s = QString("%4 %2: %1").arg(formatBalance(b).c_str()).arg(QString::fromStdString(m_context->render(i))).arg(QString::fromStdString(d.first));
ui->from->addItem(s); ui->from->addItem(s);
} }
if (old > -1 && old < ui->from->count()) if (old > -1 && old < ui->from->count())

8
eth/main.cpp

@ -678,7 +678,7 @@ int main(int argc, char** argv)
return ret; return ret;
}; };
auto getAccountPassword = [&](Address const& a){ 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); StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat, structuredLoggingURL);
@ -1122,10 +1122,10 @@ int main(int argc, char** argv)
{ {
cout << "Accounts:" << endl; cout << "Accounts:" << endl;
u256 total = 0; u256 total = 0;
for (auto const& i: keyManager.accountDetails()) for (auto const& address: keyManager.accounts())
{ {
auto b = c->balanceAt(i.first); auto b = c->balanceAt(address);
cout << ((i.first == signingKey) ? "SIGNING " : " ") << ((i.first == beneficiary) ? "COINBASE " : " ") << i.second.first << " (" << i.first << "): " << formatBalance(b) << " = " << b << " wei" << endl; cout << ((address == signingKey) ? "SIGNING " : " ") << ((address == beneficiary) ? "COINBASE " : " ") << keyManager.accountName(address) << " (" << address << "): " << formatBalance(b) << " = " << b << " wei" << endl;
total += b; total += b;
} }
cout << "Total: " << formatBalance(total) << " = " << total << " wei" << endl; cout << "Total: " << formatBalance(total) << " = " << total << " wei" << endl;

8
ethkey/KeyAux.h

@ -44,7 +44,7 @@ class BadArgument: public Exception {};
string getAccountPassword(KeyManager& keyManager, Address const& a) 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) string createPassword(std::string const& _prompt)
@ -359,20 +359,18 @@ public:
nonIcap.push_back(u); nonIcap.push_back(u);
else else
{ {
std::pair<std::string, std::string> info = wallet.accountDetails()[a];
cout << toUUID(u) << " " << a.abridged(); cout << toUUID(u) << " " << a.abridged();
cout << " " << ICAP(a).encoded(); cout << " " << ICAP(a).encoded();
cout << " " << info.first << endl; cout << " " << wallet.accountName(a) << endl;
} }
else else
bare.push_back(u); bare.push_back(u);
for (auto const& u: nonIcap) for (auto const& u: nonIcap)
if (Address a = wallet.address(u)) if (Address a = wallet.address(u))
{ {
std::pair<std::string, std::string> info = wallet.accountDetails()[a];
cout << toUUID(u) << " " << a.abridged(); cout << toUUID(u) << " " << a.abridged();
cout << " (Not ICAP) "; cout << " (Not ICAP) ";
cout << " " << info.first << endl; cout << " " << wallet.accountName(a) << endl;
} }
for (auto const& u: bare) for (auto const& u: bare)
cout << toUUID(u) << " (Bare)" << endl; cout << toUUID(u) << " (Bare)" << endl;

9
libdevcore/CommonData.h

@ -25,6 +25,7 @@
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
#include <unordered_set>
#include <type_traits> #include <type_traits>
#include <cstring> #include <cstring>
#include <string> #include <string>
@ -258,7 +259,7 @@ template <class T, class U> std::set<T>& operator+=(std::set<T>& _a, U const& _b
return _a; return _a;
} }
/// Insert the contents of a container into an unordered_st /// Insert the contents of a container into an unordered_set
template <class T, class U> std::unordered_set<T>& operator+=(std::unordered_set<T>& _a, U const& _b) template <class T, class U> std::unordered_set<T>& operator+=(std::unordered_set<T>& _a, U const& _b)
{ {
for (auto const& i: _b) for (auto const& i: _b)
@ -280,6 +281,12 @@ template <class T, class U> std::set<T> operator+(std::set<T> _a, U const& _b)
return _a += _b; return _a += _b;
} }
/// Insert the contents of a container into an unordered_set
template <class T, class U> std::unordered_set<T> operator+(std::unordered_set<T> _a, U const& _b)
{
return _a += _b;
}
/// Concatenate the contents of a container onto a vector /// Concatenate the contents of a container onto a vector
template <class T, class U> std::vector<T> operator+(std::vector<T> _a, U const& _b) template <class T, class U> std::vector<T> operator+(std::vector<T> _a, U const& _b)
{ {

163
libethcore/KeyManager.cpp

@ -31,7 +31,7 @@ using namespace dev;
using namespace eth; using namespace eth;
namespace fs = boost::filesystem; 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) m_keysFile(_keysFile), m_store(_secretsPath)
{} {}
@ -43,13 +43,13 @@ bool KeyManager::exists() const
return !contents(m_keysFile + ".salt").empty() && !contents(m_keysFile).empty(); 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); write(_pass, m_keysFile);
} }
bool KeyManager::recode(Address const& _address, std::string const& _newPass, std::string const& _hint, std::function<string()> const& _pass, KDF _kdf) bool KeyManager::recode(Address const& _address, string const& _newPass, string const& _hint, function<string()> const& _pass, KDF _kdf)
{ {
noteHint(_newPass, _hint); noteHint(_newPass, _hint);
h128 u = uuid(_address); h128 u = uuid(_address);
@ -61,10 +61,10 @@ bool KeyManager::recode(Address const& _address, std::string const& _newPass, st
return true; return true;
} }
bool KeyManager::recode(Address const& _address, SemanticPassword _newPass, std::function<string()> const& _pass, KDF _kdf) bool KeyManager::recode(Address const& _address, SemanticPassword _newPass, function<string()> const& _pass, KDF _kdf)
{ {
h128 u = uuid(_address); h128 u = uuid(_address);
std::string p; string p;
if (_newPass == SemanticPassword::Existing) if (_newPass == SemanticPassword::Existing)
p = getPassword(u, _pass); p = getPassword(u, _pass);
else if (_newPass == SemanticPassword::Master) 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); 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 salt = contents(m_keysFile + ".salt");
bytes encKeys = contents(m_keysFile); bytes encKeys = contents(m_keysFile);
m_key = h128(pbkdf2(_pass, salt, 262144, 16)); m_keysFileKey = h128(pbkdf2(_pass, salt, 262144, 16));
bytes bs = decryptSymNoAuth(m_key, h128(), &encKeys); bytes bs = decryptSymNoAuth(m_keysFileKey, h128(), &encKeys);
RLP s(bs); RLP s(bs);
unsigned version = (unsigned)s[0]; unsigned version = unsigned(s[0]);
if (version == 1) if (version == 1)
{ {
for (auto const& i: s[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]); h128 uuid(i[1]);
// cdebug << toString((Address)i[0]) << toString((h128)i[1]) << toString((h256)i[2]) << (std::string)i[3]; 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]) for (auto const& i: s[2])
m_passwordInfo[(h256)i[0]] = (std::string)i[1]; m_passwordHint[h256(i[0])] = string(i[1]);
m_password = (string)s[3]; m_defaultPasswordDeprecated = string(s[3]);
} }
// cdebug << hashPassword(m_password) << toHex(m_password); // 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(); // 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; // cdebug << hashPassword(_pass) << _pass;
m_cachedPasswords[m_master = hashPassword(_pass)] = _pass; m_master = hashPassword(_pass);
cachePassword(_pass);
return true; return true;
} }
catch (...) { catch (...)
{
return false; return false;
} }
} }
Secret KeyManager::secret(Address const& _address, function<std::string()> const& _pass) const Secret KeyManager::secret(Address const& _address, function<string()> const& _pass) const
{ {
auto it = m_addrLookup.find(_address); auto it = m_addrLookup.find(_address);
if (it == m_addrLookup.end()) if (it == m_addrLookup.end())
@ -117,12 +123,12 @@ Secret KeyManager::secret(Address const& _address, function<std::string()> const
return secret(it->second, _pass); return secret(it->second, _pass);
} }
Secret KeyManager::secret(h128 const& _uuid, function<std::string()> const& _pass) const Secret KeyManager::secret(h128 const& _uuid, function<string()> const& _pass) const
{ {
return Secret(m_store.secret(_uuid, [&](){ return getPassword(_uuid, _pass); })); return Secret(m_store.secret(_uuid, [&](){ return getPassword(_uuid, _pass); }));
} }
std::string KeyManager::getPassword(h128 const& _uuid, function<std::string()> const& _pass) const string KeyManager::getPassword(h128 const& _uuid, function<string()> const& _pass) const
{ {
auto kit = m_keyInfo.find(_uuid); auto kit = m_keyInfo.find(_uuid);
h256 ph; h256 ph;
@ -131,19 +137,19 @@ std::string KeyManager::getPassword(h128 const& _uuid, function<std::string()> c
return getPassword(ph, _pass); return getPassword(ph, _pass);
} }
std::string KeyManager::getPassword(h256 const& _passHash, function<std::string()> const& _pass) const string KeyManager::getPassword(h256 const& _passHash, function<string()> const& _pass) const
{ {
auto it = m_cachedPasswords.find(_passHash); auto it = m_cachedPasswords.find(_passHash);
if (it != m_cachedPasswords.end()) if (it != m_cachedPasswords.end())
return it->second; 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()) if (p.empty())
break; break;
if (hashPassword(p) == _passHash || _passHash == UnknownPassword) if (_passHash == UnknownPassword || hashPassword(p) == _passHash)
{ {
m_cachedPasswords[hashPassword(p)] = p; cachePassword(p);
return p; return p;
} }
} }
@ -166,20 +172,20 @@ Address KeyManager::address(h128 const& _uuid) const
return Address(); 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(); Address addr = KeyPair(_s).address();
auto passHash = hashPassword(_pass); auto passHash = hashPassword(_pass);
m_cachedPasswords[passHash] = _pass; cachePassword(_pass);
m_passwordInfo[passHash] = _passInfo; m_passwordHint[passHash] = _passwordHint;
auto uuid = m_store.importSecret(_s.asBytes(), _pass); auto uuid = m_store.importSecret(_s.asBytes(), _pass);
m_keyInfo[uuid] = KeyInfo{passHash, _info}; m_keyInfo[uuid] = KeyInfo{passHash, _accountName};
m_addrLookup[addr] = uuid; m_addrLookup[addr] = uuid;
write(m_keysFile); write(m_keysFile);
return uuid; 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; }); bytes key = m_store.secret(_uuid, [&](){ return _pass; });
if (key.empty()) if (key.empty())
@ -187,17 +193,17 @@ void KeyManager::importExisting(h128 const& _uuid, std::string const& _info, std
Address a = KeyPair(Secret(key)).address(); Address a = KeyPair(Secret(key)).address();
auto passHash = hashPassword(_pass); auto passHash = hashPassword(_pass);
if (!m_cachedPasswords.count(passHash)) if (!m_cachedPasswords.count(passHash))
m_cachedPasswords[passHash] = _pass; cachePassword(_pass);
importExisting(_uuid, _info, a, passHash, _passInfo); 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)) if (!m_passwordHint.count(_passHash))
m_passwordInfo[_passHash] = _passInfo; m_passwordHint[_passHash] = _passwordHint;
m_addrLookup[_address] = _uuid; m_addrLookup[_address] = _uuid;
m_keyInfo[_uuid].passHash = _passHash; m_keyInfo[_uuid].passHash = _passHash;
m_keyInfo[_uuid].info = _info; m_keyInfo[_uuid].accountName = _accountName;
write(m_keysFile); write(m_keysFile);
} }
@ -209,67 +215,92 @@ void KeyManager::kill(Address const& _a)
m_store.kill(id); 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) for (auto const& i: m_addrLookup)
if (m_keyInfo.count(i.second) > 0) if (m_keyInfo.count(i.second) > 0)
ret.insert(i.first); ret.push_back(i.first);
return ret; return ret;
} }
std::unordered_map<Address, std::pair<std::string, std::string>> KeyManager::accountDetails() const bool KeyManager::hasAccount(const Address& _address) const
{ {
std::unordered_map<Address, std::pair<std::string, std::string>> ret; return m_addrLookup.count(_address) && m_keyInfo.count(m_addrLookup.at(_address));
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) : ""); string const& KeyManager::accountName(Address const& _address) const
return ret; {
try
{
return m_keyInfo.at(m_addrLookup.at(_address)).accountName;
}
catch (...)
{
return EmptyString;
}
} }
h256 KeyManager::hashPassword(std::string const& _pass) const 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(string const& _pass) const
{ {
// TODO SECURITY: store this a bit more securely; Scrypt perhaps? // 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; return false;
write(m_key, _keysFile); write(m_keysFileKey, _keysFile);
return true; 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(); bytes salt = h256::random().asBytes();
writeFile(_keysFile + ".salt", salt); writeFile(_keysFile + ".salt", salt);
auto key = h128(pbkdf2(_pass, salt, 262144, 16)); auto key = h128(pbkdf2(_pass, salt, 262144, 16));
m_cachedPasswords[hashPassword(_pass)] = _pass; cachePassword(_pass);
m_master = hashPassword(_pass); m_master = hashPassword(_pass);
write(key, _keysFile); 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); RLPStream s(4);
s << 1; s << 1; // version
s.appendList(m_addrLookup.size()); s.appendList(accounts().size());
for (auto const& i: m_addrLookup) for (auto const& address: accounts())
if (m_keyInfo.count(i.second))
{ {
auto ki = m_keyInfo.at(i.second); h128 id = uuid(address);
s.appendList(4) << i.first << i.second << ki.passHash << ki.info; auto const& ki = m_keyInfo.at(id);
s.appendList(4) << address << id << ki.passHash << ki.accountName;
} }
s.appendList(m_passwordInfo.size()); s.appendList(m_passwordHint.size());
for (auto const& i: m_passwordInfo) for (auto const& i: m_passwordHint)
s.appendList(2) << i.first << i.second; s.appendList(2) << i.first << i.second;
s.append(m_password); s.append(m_defaultPasswordDeprecated);
writeFile(_keysFile, encryptSymNoAuth(_key, h128(), &s.out())); writeFile(_keysFile, encryptSymNoAuth(_key, h128(), &s.out()));
m_key = _key; m_keysFileKey = _key;
m_cachedPasswords[hashPassword(defaultPassword())] = defaultPassword(); cachePassword(defaultPassword());
} }

66
libethcore/KeyManager.h

@ -23,8 +23,9 @@
#include <functional> #include <functional>
#include <mutex> #include <mutex>
#include <libdevcrypto/SecretStore.h>
#include <libdevcore/FileSystem.h> #include <libdevcore/FileSystem.h>
#include <libdevcore/CommonData.h>
#include <libdevcrypto/SecretStore.h>
namespace dev namespace dev
{ {
@ -35,14 +36,15 @@ class PasswordUnknown: public Exception {};
struct KeyInfo struct KeyInfo
{ {
KeyInfo() = default; 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. h256 passHash; ///< Hash of the password or h256() / UnknownPassword if unknown.
std::string info; ///< Name of the key, or JSON key info if begins with '{'. std::string accountName; ///< Name of the key, or JSON key info if begins with '{'.
}; };
static const h256 UnknownPassword; static h256 const UnknownPassword;
static const auto DontKnowThrow = [](){ throw PasswordUnknown(); return std::string(); }; /// Password query function that never returns a password.
static auto const DontKnowThrow = [](){ throw PasswordUnknown(); return std::string(); };
enum class SemanticPassword 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: This one is specifically for Ethereum, but we can make it generic in due course.
// TODO: hidden-partition style key-store. // 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: * Usage:
* *
* Call exists() to check whether there is already a database. If so, get the master password from * 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 * 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. * 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 class KeyManager
{ {
@ -75,25 +80,37 @@ public:
void save(std::string const& _pass) const { write(_pass, m_keysFile); } void save(std::string const& _pass) const { write(_pass, m_keysFile); }
void notePassword(std::string const& _pass) { m_cachedPasswords[hashPassword(_pass)] = _pass; } 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(); } bool haveHint(std::string const& _pass) const { auto h = hashPassword(_pass); return m_cachedPasswords.count(h) && !m_cachedPasswords.at(h).empty(); }
AddressHash accounts() const; /// @returns the list of account addresses.
std::unordered_map<Address, std::pair<std::string, std::string>> accountDetails() const; Addresses accounts() 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 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; 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; 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& _accountName, std::string const& _pass, std::string const& _passwordHint);
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) { return import(_s, _accountName, defaultPassword(), std::string()); }
SecretStore& store() { return m_store; } 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& _accountName, std::string const& _pass, std::string const& _passwordHint);
void importExisting(h128 const& _uuid, std::string const& _info) { importExisting(_uuid, _info, defaultPassword(), std::string()); } void importExisting(h128 const& _uuid, std::string const& _accountName) { importExisting(_uuid, _accountName, 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, 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<std::string()> const& _pass = DontKnowThrow) const; Secret secret(Address const& _address, std::function<std::string()> 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<std::string()> const& _pass = DontKnowThrow) const; Secret secret(h128 const& _uuid, std::function<std::string()> const& _pass = DontKnowThrow) const;
bool recode(Address const& _address, SemanticPassword _newPass, std::function<std::string()> const& _pass = DontKnowThrow, KDF _kdf = KDF::Scrypt); bool recode(Address const& _address, SemanticPassword _newPass, std::function<std::string()> const& _pass = DontKnowThrow, KDF _kdf = KDF::Scrypt);
@ -110,6 +127,9 @@ private:
std::string defaultPassword(std::function<std::string()> const& _pass = DontKnowThrow) const { return getPassword(m_master, _pass); } std::string defaultPassword(std::function<std::string()> const& _pass = DontKnowThrow) const { return getPassword(m_master, _pass); }
h256 hashPassword(std::string const& _pass) const; 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. // Only use if previously loaded ok.
// @returns false if wasn't previously loaded ok. // @returns false if wasn't previously loaded ok.
bool write() const { return write(m_keysFile); } bool write() const { return write(m_keysFile); }
@ -118,11 +138,15 @@ private:
void write(h128 const& _key, std::string const& _keysFile) const; void write(h128 const& _key, std::string const& _keysFile) const;
// Ethereum keys. // Ethereum keys.
/// Mapping address -> key uuid.
std::unordered_map<Address, h128> m_addrLookup; std::unordered_map<Address, h128> m_addrLookup;
/// Mapping key uuid -> key info.
std::unordered_map<h128, KeyInfo> m_keyInfo; std::unordered_map<h128, KeyInfo> m_keyInfo;
std::unordered_map<h256, std::string> m_passwordInfo; /// Mapping password hash -> password hint.
std::unordered_map<h256, std::string> m_passwordHint;
// Passwords that we're storing. // Passwords that we're storing. Mapping password hash -> password.
mutable std::unordered_map<h256, std::string> m_cachedPasswords; mutable std::unordered_map<h256, std::string> m_cachedPasswords;
// DEPRECATED. // DEPRECATED.
@ -130,10 +154,10 @@ private:
// Now the default password is based off the key of the keys file directly, so this is redundant // 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 // except for the fact that people have existing keys stored with it. Leave for now until/unless
// we have an upgrade strategy. // we have an upgrade strategy.
std::string m_password; std::string m_defaultPasswordDeprecated;
mutable std::string m_keysFile; mutable std::string m_keysFile;
mutable h128 m_key; mutable h128 m_keysFileKey;
mutable h256 m_master; mutable h256 m_master;
SecretStore m_store; SecretStore m_store;
}; };

2
libweb3jsonrpc/AccountHolder.cpp

@ -103,7 +103,7 @@ void AccountHolder::clearQueue(int _id)
AddressHash SimpleAccountHolder::realAccounts() const AddressHash SimpleAccountHolder::realAccounts() const
{ {
return m_keyManager.accounts(); return m_keyManager.accountsHash();
} }
void SimpleAccountHolder::authenticate(dev::eth::TransactionSkeleton const& _t) void SimpleAccountHolder::authenticate(dev::eth::TransactionSkeleton const& _t)

1
libweb3jsonrpc/AccountHolder.h

@ -48,7 +48,6 @@ class AccountHolder
public: public:
explicit AccountHolder(std::function<Interface*()> const& _client): m_client(_client) {} explicit AccountHolder(std::function<Interface*()> const& _client): m_client(_client) {}
// easiest to return keyManager.addresses();
virtual AddressHash realAccounts() const = 0; virtual AddressHash realAccounts() const = 0;
// use m_web3's submitTransaction // use m_web3's submitTransaction
// or use AccountHolder::queueTransaction(_t) to accept // or use AccountHolder::queueTransaction(_t) to accept

12
libweb3jsonrpc/WebThreeStubServer.cpp

@ -157,19 +157,19 @@ Json::Value WebThreeStubServer::admin_eth_allAccounts(std::string const& _sessio
u256 total = 0; u256 total = 0;
u256 pendingtotal = 0; u256 pendingtotal = 0;
Address beneficiary; 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 pending = m_web3.ethereum()->balanceAt(address, PendingBlock);
auto latest = m_web3.ethereum()->balanceAt(i.first, LatestBlock); auto latest = m_web3.ethereum()->balanceAt(address, LatestBlock);
Json::Value a; Json::Value a;
if (i.first == beneficiary) if (address == beneficiary)
a["beneficiary"] = true; a["beneficiary"] = true;
a["address"] = toJS(i.first); a["address"] = toJS(address);
a["balance"] = toJS(latest); a["balance"] = toJS(latest);
a["nicebalance"] = formatBalance(latest); a["nicebalance"] = formatBalance(latest);
a["pending"] = toJS(pending); a["pending"] = toJS(pending);
a["nicepending"] = formatBalance(pending); a["nicepending"] = formatBalance(pending);
ret["accounts"][i.second.first] = a; ret["accounts"][m_keyMan.accountName(address)] = a;
total += latest; total += latest;
pendingtotal += pending; pendingtotal += pending;
} }

Loading…
Cancel
Save