Browse Source

Merge pull request #2165 from chriseth/cleanupKeyManager

KeyManager refactoring to increase readability.
cl-refactor
Gav Wood 9 years ago
parent
commit
a133519ed4
  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. 169
      libethcore/KeyManager.cpp
  9. 68
      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);
// 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<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);
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;

2
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);

2
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)

7
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())

8
eth/main.cpp

@ -690,7 +690,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);
@ -1136,10 +1136,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;

8
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<std::string, std::string> 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<std::string, std::string> 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;

9
libdevcore/CommonData.h

@ -25,6 +25,7 @@
#include <vector>
#include <algorithm>
#include <unordered_set>
#include <type_traits>
#include <cstring>
#include <string>
@ -253,7 +254,7 @@ template <class T, class U> std::set<T>& operator+=(std::set<T>& _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 <class T, class U> std::unordered_set<T>& operator+=(std::unordered_set<T>& _a, U const& _b)
{
for (auto const& i: _b)
@ -275,6 +276,12 @@ template <class T, class U> std::set<T> operator+(std::set<T> _a, U const& _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
template <class T, class U> std::vector<T> operator+(std::vector<T> _a, U const& _b)
{

169
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<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);
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<string()> const& _pass, KDF _kdf)
bool KeyManager::recode(Address const& _address, SemanticPassword _newPass, function<string()> 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<std::string()> const& _pass) const
Secret KeyManager::secret(Address const& _address, function<string()> 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<std::string()> const
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); }));
}
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);
h256 ph;
@ -131,19 +137,19 @@ std::string KeyManager::getPassword(h128 const& _uuid, function<std::string()> c
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);
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<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;
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());
}

68
libethcore/KeyManager.h

@ -23,8 +23,9 @@
#include <functional>
#include <mutex>
#include <libdevcrypto/SecretStore.h>
#include <libdevcore/FileSystem.h>
#include <libdevcore/CommonData.h>
#include <libdevcrypto/SecretStore.h>
namespace dev
{
@ -35,14 +36,17 @@ 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 '{'.
/// 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 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 +57,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 +82,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<Address, std::pair<std::string, std::string>> 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<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;
bool recode(Address const& _address, SemanticPassword _newPass, std::function<std::string()> const& _pass = DontKnowThrow, KDF _kdf = KDF::Scrypt);
@ -110,6 +129,9 @@ private:
std::string defaultPassword(std::function<std::string()> 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 +140,15 @@ private:
void write(h128 const& _key, std::string const& _keysFile) const;
// Ethereum keys.
/// Mapping address -> key uuid.
std::unordered_map<Address, h128> m_addrLookup;
/// Mapping key uuid -> key info.
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;
// DEPRECATED.
@ -130,10 +156,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;
};

2
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)

1
libweb3jsonrpc/AccountHolder.h

@ -48,7 +48,6 @@ class AccountHolder
public:
explicit AccountHolder(std::function<Interface*()> 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

12
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;
}

Loading…
Cancel
Save