diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index c9d687ef3..e51e3f6e0 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -136,11 +136,12 @@ void Transact::updateDestination() // TODO: should be a Qt model. ui->destination->clear(); ui->destination->addItem("(Create Contract)"); + QMultiMap in; for (Address const& a: m_main->allKnownAddresses()) - { - cdebug << "Adding" << a << m_main->toName(a) << " -> " << (m_main->toName(a) + " (" + ICAP(a).encoded() + ")"); - ui->destination->addItem(QString::fromStdString(m_main->toName(a) + " (" + ICAP(a).encoded() + ")"), QString::fromStdString(a.hex())); - } + in.insert(QString::fromStdString(m_main->toName(a) + " (" + ICAP(a).encoded() + ")"), QString::fromStdString(a.hex())); + for (auto i = in.begin(); i != in.end(); ++i) + ui->destination->addItem(i.key(), i.value()); + } void Transact::updateFee() @@ -361,9 +362,8 @@ void Transact::timerEvent(QTimerEvent*) } updateBounds(); - if (m_lowerBound == m_upperBound) - finaliseBounds(); } + finaliseBounds(); } void Transact::updateBounds() @@ -380,6 +380,8 @@ void Transact::updateBounds() void Transact::finaliseBounds() { + killTimer(m_gasCalcTimer); + quint64 baseGas = (quint64)Transaction::gasRequired(m_data, 0); ui->progressGas->setVisible(false); @@ -420,7 +422,6 @@ void Transact::finaliseBounds() updateFee(); ui->code->setHtml(htmlInfo + m_dataInfo); ui->send->setEnabled(true); - killTimer(m_gasCalcTimer); } GasRequirements Transact::determineGasRequirements() diff --git a/alethzero/plugins/keys/ImportKey.cpp b/alethzero/plugins/keys/ImportKey.cpp new file mode 100644 index 000000000..6f47505d4 --- /dev/null +++ b/alethzero/plugins/keys/ImportKey.cpp @@ -0,0 +1,222 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file ImportKey.cpp + * @author Gav Wood + * @date 2015 + */ + +#include "ImportKey.h" +#include +#include +#include +#include +#include +#include +#include +#include "ui_ImportKey.h" +using namespace std; +using namespace dev; +using namespace az; +using namespace eth; + +DEV_AZ_NOTE_PLUGIN(ImportKey); + +ImportKey::ImportKey(MainFace* _m): + Plugin(_m, "ImportKey") +{ + connect(addMenuItem("Import Key...", "menuTools", true), SIGNAL(triggered()), SLOT(import())); +} + +ImportKey::~ImportKey() +{ +} + +void ImportKey::import() +{ + QDialog d; + Ui_ImportKey u; + u.setupUi(&d); + d.setWindowTitle("Import Key"); + + string lastKey; + Secret lastSecret; + string lastPassword; + Address lastAddress; + + auto updateAction = [&](){ + if (!u.import_2->isEnabled()) + u.action->clear(); + else if (lastKey.empty() && !lastSecret) + u.action->setText("Import brainwallet with given address and hint"); + else if (!lastKey.empty() && !lastSecret) + { + h256 ph; + DEV_IGNORE_EXCEPTIONS(ph = h256(u.passwordHash->text().toStdString())); + if (ph) + u.action->setText("Import untouched key with given address and hint"); + else + u.action->setText("Import untouched key with given address, password hash and hint"); + } + else + { + bool mp = u.noPassword->isChecked(); + if (mp) + u.action->setText("Import recast key using master password and given hint"); + else + u.action->setText("Import recast key with given password and hint"); + } + }; + + auto updateImport = [&](){ + u.import_2->setDisabled(u.addressOut->text().isEmpty() || u.name->text().isEmpty() || !(u.oldPassword->isChecked() || u.newPassword->isChecked() || u.noPassword->isChecked())); + updateAction(); + }; + + auto updateAddress = [&](){ + lastAddress.clear(); + string as = u.address->text().toStdString(); + try + { + lastAddress = eth::toAddress(as); + u.addressOut->setText(QString::fromStdString(main()->render(lastAddress))); + } + catch (...) + { + u.addressOut->setText(""); + } + updateImport(); + }; + + auto updatePassword = [&](){ + u.passwordHash->setText(QString::fromStdString(sha3(u.password->text().toStdString()).hex())); + updateAction(); + }; + + function updateKey = [&](){ + // update according to key. + if (lastKey == u.key->text().toStdString()) + return; + lastKey = u.key->text().toStdString(); + lastSecret.clear(); + u.address->clear(); + u.oldPassword->setEnabled(false); + u.oldPassword->setChecked(false); + bytes b; + DEV_IGNORE_EXCEPTIONS(b = fromHex(lastKey, WhenError::Throw)); + if (b.size() == 32) + { + lastSecret = Secret(b); + bytesRef(&b).cleanse(); + } + while (!lastKey.empty() && !lastSecret) + { + bool ok; + lastPassword = QInputDialog::getText(&d, "Open Key File", "Enter the password protecting this key file. Cancel if you do not want to provide te password.", QLineEdit::Password, QString(), &ok).toStdString(); + if (!ok) + { + lastSecret.clear(); + break; + } + // Try to open as a file. + lastSecret = KeyManager::presaleSecret(contentsString(lastKey), [&](bool first){ return first ? lastPassword : string(); }).secret(); + if (!lastSecret) + lastSecret = Secret(SecretStore::secret(contentsString(lastKey), lastPassword)); + if (!lastSecret && QMessageBox::warning(&d, "Invalid Password or Key File", "The given password could not be used to decrypt the key file given. Are you sure it is a valid key file and that the password is correct?", QMessageBox::Abort, QMessageBox::Retry) == QMessageBox::Abort) + { + u.key->clear(); + updateKey(); + return; + } + } + u.oldPassword->setEnabled(!!lastSecret); + u.newPassword->setEnabled(!!lastSecret); + u.noPassword->setEnabled(!!lastSecret); + u.masterLabel->setEnabled(!!lastSecret); + u.oldLabel->setEnabled(!!lastSecret); + u.showPassword->setEnabled(!!lastSecret); + u.password->setEnabled(!!lastSecret); + u.passwordHash->setReadOnly(!!lastSecret); + u.address->setReadOnly(!!lastSecret); + if (lastSecret) + { + u.oldPassword->setEnabled(!lastPassword.empty()); + if (lastPassword.empty()) + u.oldPassword->setChecked(false); + u.address->setText(QString::fromStdString(ICAP(toAddress(lastSecret)).encoded())); + updateAddress(); + } + else + u.address->clear(); + updateImport(); + }; + + connect(u.noPassword, &QRadioButton::clicked, [&](){ + u.passwordHash->clear(); + u.hint->setText("No additional password (same as master password)."); + updateAction(); + }); + connect(u.oldPassword, &QRadioButton::clicked, [&](){ + u.passwordHash->setText(QString::fromStdString(sha3(lastPassword).hex())); + u.hint->setText("Same as original password for file " + QString::fromStdString(lastKey)); + updateAction(); + }); + connect(u.newPassword, &QRadioButton::clicked, [&](){ + u.hint->setText(""); + updatePassword(); + }); + connect(u.password, &QLineEdit::textChanged, [&](){ updatePassword(); }); + connect(u.address, &QLineEdit::textChanged, [&](){ updateAddress(); }); + connect(u.key, &QLineEdit::textEdited, [&](){ updateKey(); }); + connect(u.name, &QLineEdit::textEdited, [&](){ updateImport(); }); + connect(u.showPassword, &QCheckBox::toggled, [&](bool show){ u.password->setEchoMode(show ? QLineEdit::Normal : QLineEdit::Password); }); + connect(u.openKey, &QToolButton::clicked, [&](){ + QString fn = QFileDialog::getOpenFileName(main(), "Open Key File", QDir::homePath(), "JSON Files (*.json);;All Files (*)"); + if (!fn.isEmpty()) + { + u.key->setText(fn); + updateKey(); + } + }); + + if (d.exec() == QDialog::Accepted) + { + Address a = lastAddress; + string n = u.name->text().toStdString(); + string h = u.hint->text().toStdString(); + + // check for a brain wallet import + if (lastKey.empty() && !lastSecret) + main()->keyManager().importExistingBrain(a, n, h); + else if (!lastKey.empty() && !lastSecret) + { + h256 ph; + DEV_IGNORE_EXCEPTIONS(ph = h256(u.passwordHash->text().toStdString())); + main()->keyManager().importExisting(main()->keyManager().store().importKey(lastKey), n, a, ph, h); + } + else + { + bool mp = u.noPassword->isChecked(); + string p = mp ? string() : u.oldPassword ? lastPassword : u.password->text().toStdString(); + if (mp) + main()->keyManager().import(lastSecret, n); + else + main()->keyManager().import(lastSecret, n, p, h); + } + + main()->noteKeysChanged(); + } +} diff --git a/alethzero/plugins/keys/ImportKey.h b/alethzero/plugins/keys/ImportKey.h new file mode 100644 index 000000000..ed233664e --- /dev/null +++ b/alethzero/plugins/keys/ImportKey.h @@ -0,0 +1,44 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file ImportKey.h + * @author Gav Wood + * @date 2015 + */ + +#pragma once + +#include "MainFace.h" + +namespace dev +{ +namespace az +{ + +class ImportKey: public QObject, public Plugin +{ + Q_OBJECT + +public: + ImportKey(MainFace* _m); + ~ImportKey(); + +private slots: + void import(); +}; + +} +} diff --git a/alethzero/plugins/keys/ImportKey.ui b/alethzero/plugins/keys/ImportKey.ui new file mode 100644 index 000000000..03f62e152 --- /dev/null +++ b/alethzero/plugins/keys/ImportKey.ui @@ -0,0 +1,336 @@ + + + ImportKey + + + + 0 + 0 + 530 + 389 + + + + Dialog + + + + + + + + true + + + + + + + false + + + Show + + + + + + + &Address: + + + address + + + + + + + ... + + + + + + + false + + + New &Password: + + + buttonGroup + + + + + + + Place the address of the key here + + + + + + + &Name: + + + name + + + + + + + false + + + &Master password + + + true + + + buttonGroup + + + + + + + + + + false + + + &Old password + + + buttonGroup + + + + + + + Enter the password you wish to use for the key here + + + + + + + Enter this key's name here + + + + + + + Password &Hash: + + + passwordHash + + + + + + + Password Hin&t: + + + hint + + + + + + + false + + + Use same password for the key as for the master. + + + false + + + + + + + Brain wallet (no key file) + + + true + + + + + + + &Key: + + + key + + + + + + + false + + + Use the same password as in the key file. + + + + + + + true + + + Unknown Address + + + + + + + + + Qt::Vertical + + + + 20 + 5 + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Cancel + + + Esc + + + + + + + false + + + &Import + + + true + + + + + + + + + name + key + openKey + address + addressOut + noPassword + oldPassword + newPassword + password + showPassword + passwordHash + hint + cancel + import_2 + + + + + import_2 + clicked() + ImportKey + accept() + + + 519 + 378 + + + 449 + 388 + + + + + cancel + clicked() + ImportKey + reject() + + + 433 + 378 + + + 351 + 388 + + + + + newPassword + pressed() + password + setFocus() + + + 80 + 211 + + + 185 + 215 + + + + + + + + diff --git a/libdevcore/vector_ref.h b/libdevcore/vector_ref.h index c7ba3a437..b5d0453a6 100644 --- a/libdevcore/vector_ref.h +++ b/libdevcore/vector_ref.h @@ -86,7 +86,7 @@ public: void cleanse() { uint8_t* p = (uint8_t*)begin(); - size_t len = (uint8_t*)end() - p; + size_t const len = (uint8_t*)end() - p; size_t loop = len; size_t count = s_cleanseCounter; while (loop--) @@ -98,6 +98,7 @@ public: if (p) count += (63 + (size_t)p); s_cleanseCounter = (uint8_t)count; + memset((uint8_t*)begin(), 0, len); } _T* begin() { return m_data; } diff --git a/libdevcrypto/SecretStore.cpp b/libdevcrypto/SecretStore.cpp index 39f421f3e..1b1e67e9b 100644 --- a/libdevcrypto/SecretStore.cpp +++ b/libdevcrypto/SecretStore.cpp @@ -107,6 +107,14 @@ bytesSec SecretStore::secret(h128 const& _uuid, function const& _pass, return key; } +bytesSec SecretStore::secret(string const& _content, string const& _pass) +{ + js::mValue u = upgraded(_content); + if (u.type() != js::obj_type) + return bytesSec(); + return decrypt(js::write_string(u.get_obj()["crypto"], false), _pass); +} + h128 SecretStore::importSecret(bytesSec const& _s, string const& _pass) { h128 r; @@ -169,11 +177,13 @@ void SecretStore::save(string const& _keysPath) void SecretStore::load(string const& _keysPath) { fs::path p(_keysPath); - fs::create_directories(p); - DEV_IGNORE_EXCEPTIONS(fs::permissions(p, fs::owner_all)); - for (fs::directory_iterator it(p); it != fs::directory_iterator(); ++it) - if (fs::is_regular_file(it->path())) - readKey(it->path().string(), true); + try + { + for (fs::directory_iterator it(p); it != fs::directory_iterator(); ++it) + if (fs::is_regular_file(it->path())) + readKey(it->path().string(), true); + } + catch (...) {} } h128 SecretStore::readKey(string const& _file, bool _takeFileOwnership) diff --git a/libdevcrypto/SecretStore.h b/libdevcrypto/SecretStore.h index 0fcb18734..9117788cb 100644 --- a/libdevcrypto/SecretStore.h +++ b/libdevcrypto/SecretStore.h @@ -53,6 +53,9 @@ public: /// @param _pass function that returns the password for the key. /// @param _useCache if true, allow previously decrypted keys to be returned directly. bytesSec secret(h128 const& _uuid, std::function const& _pass, bool _useCache = true) const; + /// @returns the secret key stored by the given @a _uuid. + /// @param _pass function that returns the password for the key. + static bytesSec secret(std::string const& _content, std::string const& _pass); /// Imports the (encrypted) key stored in the file @a _file and copies it to the managed directory. h128 importKey(std::string const& _file) { auto ret = readKey(_file, false); if (ret) save(); return ret; } /// Imports the (encrypted) key contained in the json formatted @a _content and stores it in diff --git a/libethcore/KeyManager.cpp b/libethcore/KeyManager.cpp index 6f3733d74..e00cb68d7 100644 --- a/libethcore/KeyManager.cpp +++ b/libethcore/KeyManager.cpp @@ -243,6 +243,13 @@ Address KeyManager::importBrain(string const& _seed, string const& _accountName, return addr; } +void KeyManager::importExistingBrain(Address const& _a, string const& _accountName, string const& _passwordHint) +{ + m_keyInfo[_a].accountName = _accountName; + m_keyInfo[_a].passwordHint = _passwordHint; + write(); +} + void KeyManager::importExisting(h128 const& _uuid, string const& _info, string const& _pass, string const& _passwordHint) { bytesSec key = m_store.secret(_uuid, [&](){ return _pass; }); diff --git a/libethcore/KeyManager.h b/libethcore/KeyManager.h index 8830f0044..f1081116a 100644 --- a/libethcore/KeyManager.h +++ b/libethcore/KeyManager.h @@ -108,6 +108,7 @@ public: 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()); } Address importBrain(std::string const& _seed, std::string const& _accountName, std::string const& _seedHint); + void importExistingBrain(Address const& _a, std::string const& _accountName, std::string const& _seedHint); SecretStore& store() { return m_store; } void importExisting(h128 const& _uuid, std::string const& _accountName, std::string const& _pass, std::string const& _passwordHint); @@ -130,7 +131,7 @@ public: static std::string defaultPath() { return getDataDir("ethereum") + "/keys.info"; } /// Extracts the secret key from the presale wallet. - KeyPair presaleSecret(std::string const& _json, std::function const& _password); + static KeyPair presaleSecret(std::string const& _json, std::function const& _password); /// @returns the brainwallet secret for the given seed. static Secret brain(std::string const& _seed);