ui;
diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp
index 1336c3f05..fcca822fe 100644
--- a/alethzero/Transact.cpp
+++ b/alethzero/Transact.cpp
@@ -76,6 +76,7 @@ void Transact::setEnvironment(AddressHash const& _accounts, dev::eth::Client* _e
m_ethereum = _eth;
m_natSpecDB = _natSpecDB;
+ auto old = ui->from->currentIndex();
ui->from->clear();
for (auto const& i: m_accounts)
{
@@ -84,6 +85,10 @@ void Transact::setEnvironment(AddressHash const& _accounts, dev::eth::Client* _e
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);
}
+ if (old > -1 && old < ui->from->count())
+ ui->from->setCurrentIndex(old);
+ else if (ui->from->count())
+ ui->from->setCurrentIndex(0);
}
bool Transact::isCreation() const
@@ -301,6 +306,9 @@ void Transact::rejigData()
// Determine how much balance we have to play with...
//findSecret(value() + ethereum()->gasLimitRemaining() * gasPrice());
auto s = fromAccount();
+ if (!s)
+ return;
+
auto b = ethereum()->balanceAt(s, PendingBlock);
m_allGood = true;
@@ -344,7 +352,7 @@ void Transact::rejigData()
if (b < value() + baseGas * gasPrice())
{
// Not enough - bail.
- bail("ERROR No single account contains enough for paying even the basic amount of gas required.
");
+ bail("ERROR Account doesn't contain enough for paying even the basic amount of gas required.
");
return;
}
else
@@ -417,6 +425,8 @@ Secret Transact::findSecret(u256 _totalReq) const
Address Transact::fromAccount()
{
+ if (ui->from->currentIndex() < 0 || ui->from->currentIndex() >= (int)m_accounts.size())
+ return Address();
auto it = m_accounts.begin();
std::advance(it, ui->from->currentIndex());
return *it;
@@ -425,14 +435,19 @@ Address Transact::fromAccount()
void Transact::on_send_clicked()
{
// Secret s = findSecret(value() + fee());
- Secret s = m_context->retrieveSecret(fromAccount());
- auto b = ethereum()->balanceAt(KeyPair(s).address(), PendingBlock);
- if (!s || b < value() + fee())
+ auto a = fromAccount();
+ auto b = ethereum()->balanceAt(a, PendingBlock);
+
+ if (!a || b < value() + fee())
{
- QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: no single account contains at least the required amount.");
+ QMessageBox::critical(nullptr, "Transaction Failed", "Couldn't make transaction: account doesn't contain at least the required amount.", QMessageBox::Ok);
return;
}
+ Secret s = m_context->retrieveSecret(a);
+ if (!s)
+ return;
+
if (isCreation())
{
// If execution is a contract creation, add Natspec to
@@ -467,7 +482,7 @@ void Transact::on_debug_clicked()
auto b = ethereum()->balanceAt(from, PendingBlock);
if (!from || b < value() + fee())
{
- QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: no single account contains at least the required amount.");
+ QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: account doesn't contain at least the required amount.");
return;
}
diff --git a/alethzero/Transact.h b/alethzero/Transact.h
index cd62c0e20..8c079a2fe 100644
--- a/alethzero/Transact.h
+++ b/alethzero/Transact.h
@@ -44,6 +44,7 @@ public:
void setEnvironment(dev::AddressHash const& _accounts, dev::eth::Client* _eth, NatSpecFace* _natSpecDB);
private slots:
+ void on_from_currentIndexChanged(int) { rejigData(); }
void on_destination_currentTextChanged(QString);
void on_value_valueChanged(int) { updateFee(); }
void on_gas_valueChanged(int) { updateFee(); }
diff --git a/libdevcore/Common.h b/libdevcore/Common.h
index ac4d89103..ae98861c1 100644
--- a/libdevcore/Common.h
+++ b/libdevcore/Common.h
@@ -39,6 +39,7 @@
#include
#include
#include
+#include
#include
#include
#pragma warning(push)
@@ -63,6 +64,8 @@ namespace dev
extern char const* Version;
+static const std::string EmptyString;
+
// Binary data types.
using bytes = std::vector;
using bytesRef = vector_ref;
diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp
index 2a3561314..814f8309e 100644
--- a/libdevcrypto/Common.cpp
+++ b/libdevcrypto/Common.cpp
@@ -120,10 +120,11 @@ std::pair dev::encryptSymNoAuth(h128 const& _k, bytesConstRef _plai
return make_pair(encryptSymNoAuth(_k, iv, _plain), iv);
}
-bytes dev::encryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _plain)
+bytes dev::encryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _plain)
{
- const int c_aesKeyLen = 16;
- SecByteBlock key(_k.data(), c_aesKeyLen);
+ if (_k.size() != 16 && _k.size() != 24 && _k.size() != 32)
+ return bytes();
+ SecByteBlock key(_k.data(), _k.size());
try
{
CTR_Mode::Encryption e;
@@ -139,10 +140,11 @@ bytes dev::encryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _plai
}
}
-bytes dev::decryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _cipher)
+bytes dev::decryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _cipher)
{
- const size_t c_aesKeyLen = 16;
- SecByteBlock key(_k.data(), c_aesKeyLen);
+ if (_k.size() != 16 && _k.size() != 24 && _k.size() != 32)
+ return bytes();
+ SecByteBlock key(_k.data(), _k.size());
try
{
CTR_Mode::Decryption d;
diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h
index a0f894a25..10bcdd067 100644
--- a/libdevcrypto/Common.h
+++ b/libdevcrypto/Common.h
@@ -98,18 +98,26 @@ bool decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext);
/// Encrypt payload using ECIES standard with AES128-CTR.
void encryptECIES(Public const& _k, bytesConstRef _plain, bytes& o_cipher);
-
+
/// Decrypt payload using ECIES standard with AES128-CTR.
bool decryptECIES(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext);
-
+
/// Encrypts payload with random IV/ctr using AES128-CTR.
std::pair encryptSymNoAuth(h128 const& _k, bytesConstRef _plain);
/// Encrypts payload with specified IV/ctr using AES128-CTR.
-bytes encryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _plain);
+bytes encryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _plain);
+
+/// Decrypts payload with specified IV/ctr using AES128-CTR.
+bytes decryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _cipher);
+
+/// Encrypts payload with specified IV/ctr using AES128-CTR.
+inline bytes encryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _plain) { return encryptAES128CTR(_k.ref(), _iv, _plain); }
+inline bytes encryptSymNoAuth(h256 const& _k, h128 const& _iv, bytesConstRef _plain) { return encryptAES128CTR(_k.ref(), _iv, _plain); }
/// Decrypts payload with specified IV/ctr using AES128-CTR.
-bytes decryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _cipher);
+inline bytes decryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _cipher) { return decryptAES128CTR(_k.ref(), _iv, _cipher); }
+inline bytes decryptSymNoAuth(h256 const& _k, h128 const& _iv, bytesConstRef _cipher) { return decryptAES128CTR(_k.ref(), _iv, _cipher); }
/// Recovers Public key from signed message hash.
Public recover(Signature const& _sig, h256 const& _hash);
diff --git a/libdevcrypto/SecretStore.cpp b/libdevcrypto/SecretStore.cpp
index 6a975525b..982b7bae6 100644
--- a/libdevcrypto/SecretStore.cpp
+++ b/libdevcrypto/SecretStore.cpp
@@ -34,6 +34,56 @@ using namespace dev;
namespace js = json_spirit;
namespace fs = boost::filesystem;
+static const int c_keyFileVersion = 3;
+
+static js::mValue upgraded(std::string const& _s)
+{
+ js::mValue v;
+ js::read_string(_s, v);
+ if (v.type() != js::obj_type)
+ return js::mValue();
+ js::mObject ret = v.get_obj();
+ unsigned version = ret.count("Version") ? stoi(ret["Version"].get_str()) : ret.count("version") ? ret["version"].get_int() : 0;
+ if (version == 1)
+ {
+ // upgrade to version 2
+ js::mObject old;
+ swap(old, ret);
+
+ ret["id"] = old["Id"];
+ js::mObject c;
+ c["ciphertext"] = old["Crypto"].get_obj()["CipherText"];
+ c["cipher"] = "aes-128-cbc";
+ {
+ js::mObject cp;
+ cp["iv"] = old["Crypto"].get_obj()["IV"];
+ c["cipherparams"] = cp;
+ }
+ c["kdf"] = old["Crypto"].get_obj()["KeyHeader"].get_obj()["Kdf"];
+ {
+ js::mObject kp;
+ kp["salt"] = old["Crypto"].get_obj()["Salt"];
+ for (auto const& i: old["Crypto"].get_obj()["KeyHeader"].get_obj()["KdfParams"].get_obj())
+ if (i.first != "SaltLen")
+ kp[boost::to_lower_copy(i.first)] = i.second;
+ c["kdfparams"] = kp;
+ }
+ c["sillymac"] = old["Crypto"].get_obj()["MAC"];
+ c["sillymacjson"] = _s;
+ ret["crypto"] = c;
+ version = 2;
+ }
+ if (version == 2)
+ {
+ ret["crypto"].get_obj()["cipher"] = "aes-128-ctr";
+ ret["crypto"].get_obj()["compat"] = "2";
+ version = 3;
+ }
+ if (version == c_keyFileVersion)
+ return ret;
+ return js::mValue();
+}
+
SecretStore::SecretStore()
{
load();
@@ -45,6 +95,7 @@ SecretStore::~SecretStore()
bytes SecretStore::secret(h128 const& _uuid, function const& _pass, bool _useCache) const
{
+ (void)_pass;
auto rit = m_cached.find(_uuid);
if (_useCache && rit != m_cached.end())
return rit->second;
@@ -94,7 +145,7 @@ void SecretStore::save(std::string const& _keysPath)
js::read_string(k.second.first, crypto);
v["crypto"] = crypto;
v["id"] = uuid;
- v["version"] = 2;
+ v["version"] = c_keyFileVersion;
writeFile(filename, js::write_string(js::mValue(v), true));
if (!k.second.second.empty() && k.second.second != filename)
boost::filesystem::remove(k.second.second);
@@ -102,48 +153,6 @@ void SecretStore::save(std::string const& _keysPath)
}
}
-static js::mValue upgraded(std::string const& _s)
-{
- js::mValue v;
- js::read_string(_s, v);
- if (v.type() != js::obj_type)
- return js::mValue();
- js::mObject ret = v.get_obj();
- unsigned version = ret.count("Version") ? stoi(ret["Version"].get_str()) : ret.count("version") ? ret["version"].get_int() : 0;
- if (version == 1)
- {
- // upgrade to version 2
- js::mObject old;
- swap(old, ret);
-
- ret["id"] = old["Id"];
- js::mObject c;
- c["ciphertext"] = old["Crypto"].get_obj()["CipherText"];
- c["cipher"] = "aes-128-cbc";
- {
- js::mObject cp;
- cp["iv"] = old["Crypto"].get_obj()["IV"];
- c["cipherparams"] = cp;
- }
- c["kdf"] = old["Crypto"].get_obj()["KeyHeader"].get_obj()["Kdf"];
- {
- js::mObject kp;
- kp["salt"] = old["Crypto"].get_obj()["Salt"];
- for (auto const& i: old["Crypto"].get_obj()["KeyHeader"].get_obj()["KdfParams"].get_obj())
- if (i.first != "SaltLen")
- kp[boost::to_lower_copy(i.first)] = i.second;
- c["kdfparams"] = kp;
- }
- c["sillymac"] = old["Crypto"].get_obj()["MAC"];
- c["sillymacjson"] = _s;
- ret["crypto"] = c;
- version = 2;
- }
- if (version == 2)
- return ret;
- return js::mValue();
-}
-
void SecretStore::load(std::string const& _keysPath)
{
fs::path p(_keysPath);
@@ -169,10 +178,17 @@ h128 SecretStore::readKey(std::string const& _file, bool _deleteFile)
return h128();
}
-void SecretStore::recode(h128 const& _uuid, string const& _pass, KDF _kdf)
+bool SecretStore::recode(h128 const& _uuid, string const& _newPass, std::function const& _pass, KDF _kdf)
{
- m_keys[_uuid].first = encrypt(secret(_uuid, [&](){ return _pass; }), _pass, _kdf);
+ cdebug << "recode:" << toUUID(_uuid);
+ cdebug << "newPass:" << _newPass;
+ bytes s = secret(_uuid, _pass, true);
+ if (s.empty())
+ return false;
+ cdebug << "secret:" << toHex(s);
+ m_keys[_uuid].first = encrypt(s, _newPass, _kdf);
save();
+ return true;
}
std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass, KDF _kdf)
@@ -180,25 +196,26 @@ std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass, KDF
js::mObject ret;
// KDF info
- unsigned dklen = 16;
+ unsigned dklen = 32;
bytes salt = h256::random().asBytes();
bytes derivedKey;
if (_kdf == KDF::Scrypt)
{
unsigned iterations = 262144;
- unsigned p = 262144;
- unsigned r = 262144;
+ unsigned p = 1;
+ unsigned r = 8;
ret["kdf"] = "scrypt";
{
js::mObject params;
- params["n"] = (int)iterations;
- params["p"] = 1;
- params["r"] = 8;
+ params["n"] = (int64_t)iterations;
+ params["p"] = (int)p;
+ params["r"] = (int)r;
params["dklen"] = (int)dklen;
params["salt"] = toHex(salt);
ret["kdfparams"] = params;
}
derivedKey = scrypt(_pass, salt, iterations, p, r, dklen);
+ cdebug << "derivedKey" << toHex(derivedKey);
}
else
{
@@ -213,11 +230,13 @@ std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass, KDF
ret["kdfparams"] = params;
}
derivedKey = pbkdf2(_pass, salt, iterations, dklen);
+ cdebug << "derivedKey" << toHex(derivedKey);
}
// cipher info
- ret["cipher"] = "aes-128-cbc";
- h128 key(sha3(h128(derivedKey, h128::AlignRight)), h128::AlignRight);
+ ret["cipher"] = "aes-128-ctr";
+ h128 key(derivedKey, h128::AlignLeft);
+ cdebug << "cipherKey" << key.hex();
h128 iv = h128::random();
{
js::mObject params;
@@ -230,7 +249,9 @@ std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass, KDF
ret["ciphertext"] = toHex(cipherText);
// and mac.
- h256 mac = sha3(bytesConstRef(&derivedKey).cropped(derivedKey.size() - 16).toBytes() + cipherText);
+ h256 mac = sha3(ref(derivedKey).cropped(16, 16).toBytes() + cipherText);
+ cdebug << "macBody" << toHex(ref(derivedKey).cropped(16, 16).toBytes() + cipherText);
+ cdebug << "mac" << mac.hex();
ret["mac"] = toHex(mac.ref());
return js::write_string((js::mValue)ret, true);
@@ -270,13 +291,23 @@ bytes SecretStore::decrypt(std::string const& _v, std::string const& _pass)
return bytes();
}
+ if (derivedKey.size() < 32 && !(o.count("compat") && o["compat"].get_str() == "2"))
+ {
+ cwarn << "Derived key's length too short (<32 bytes)";
+ return bytes();
+ }
+
bytes cipherText = fromHex(o["ciphertext"].get_str());
// check MAC
if (o.count("mac"))
{
h256 mac(o["mac"].get_str());
- h256 macExp = sha3(bytesConstRef(&derivedKey).cropped(derivedKey.size() - 16).toBytes() + cipherText);
+ h256 macExp;
+ if (o.count("compat") && o["compat"].get_str() == "2")
+ macExp = sha3(bytesConstRef(&derivedKey).cropped(derivedKey.size() - 16).toBytes() + cipherText);
+ else
+ macExp = sha3(bytesConstRef(&derivedKey).cropped(16, 16).toBytes() + cipherText);
if (mac != macExp)
{
cwarn << "Invalid key - MAC mismatch; expected" << toString(macExp) << ", got" << toString(mac);
@@ -297,12 +328,17 @@ bytes SecretStore::decrypt(std::string const& _v, std::string const& _pass)
cwarn << "No MAC. Proceeding anyway.";
// decrypt
- if (o["cipher"].get_str() == "aes-128-cbc")
+ if (o["cipher"].get_str() == "aes-128-ctr")
{
auto params = o["cipherparams"].get_obj();
- h128 key(sha3(h128(derivedKey, h128::AlignRight)), h128::AlignRight);
h128 iv(params["iv"].get_str());
- return decryptSymNoAuth(key, iv, &cipherText);
+ if (o.count("compat") && o["compat"].get_str() == "2")
+ {
+ h128 key(sha3(h128(derivedKey, h128::AlignRight)), h128::AlignRight);
+ return decryptSymNoAuth(key, iv, &cipherText);
+ }
+ else
+ return decryptSymNoAuth(h128(derivedKey, h128::AlignLeft), iv, &cipherText);
}
else
{
diff --git a/libdevcrypto/SecretStore.h b/libdevcrypto/SecretStore.h
index 6e66ce967..6a62147b8 100644
--- a/libdevcrypto/SecretStore.h
+++ b/libdevcrypto/SecretStore.h
@@ -44,7 +44,7 @@ public:
bytes secret(h128 const& _uuid, std::function const& _pass, bool _useCache = true) const;
h128 importKey(std::string const& _file) { auto ret = readKey(_file, false); if (ret) save(); return ret; }
h128 importSecret(bytes const& _s, std::string const& _pass);
- void recode(h128 const& _uuid, std::string const& _pass, KDF _kdf = KDF::Scrypt);
+ bool recode(h128 const& _uuid, std::string const& _newPass, std::function const& _pass, KDF _kdf = KDF::Scrypt);
void kill(h128 const& _uuid);
// Clear any cached keys.
diff --git a/libethcore/ICAP.h b/libethcore/ICAP.h
index b4229e1f5..a2456bd40 100644
--- a/libethcore/ICAP.h
+++ b/libethcore/ICAP.h
@@ -38,8 +38,6 @@ namespace eth
struct InvalidICAP: virtual public dev::Exception {};
-static const std::string EmptyString;
-
/**
* @brief Encapsulation of an ICAP address.
* Can be encoded, decoded, looked-up and inspected.
diff --git a/libethereum/AccountDiff.h b/libethereum/AccountDiff.h
index 22107b958..dd494c0a5 100644
--- a/libethereum/AccountDiff.h
+++ b/libethereum/AccountDiff.h
@@ -62,7 +62,7 @@ struct AccountDiff
Diff exist; ///< The account's existance; was it created/deleted or not?
Diff balance; ///< The account's balance; did it alter?
Diff nonce; ///< The account's nonce; did it alter?
- std::unordered_map> storage; ///< The account's storage addresses; each has its own Diff.
+ std::map> storage; ///< The account's storage addresses; each has its own Diff.
Diff code; ///< The account's code; in general this should only have changed if exist also changed.
};
diff --git a/libethereum/KeyManager.cpp b/libethereum/KeyManager.cpp
index 687e13991..4a03d8435 100644
--- a/libethereum/KeyManager.cpp
+++ b/libethereum/KeyManager.cpp
@@ -49,10 +49,20 @@ void KeyManager::create(std::string const& _pass)
write(_pass, m_keysFile);
}
-void KeyManager::reencode(Address const& _address, std::function const& _pass, KDF _kdf)
+bool KeyManager::recode(Address const& _address, std::string const& _newPass, std::string const& _hint, std::function const& _pass, KDF _kdf)
+{
+ noteHint(_newPass, _hint);
+ return store().recode(uuid(_address), _newPass, _pass, _kdf);
+}
+
+bool KeyManager::recode(Address const& _address, SemanticPassword _newPass, std::function const& _pass, KDF _kdf)
{
h128 u = uuid(_address);
- store().recode(u, getPassword(u, _pass), _kdf);
+ if (_newPass == SemanticPassword::Existing)
+ return store().recode(u, getPassword(u, _pass), _pass, _kdf);
+ else if (_newPass == SemanticPassword::Master)
+ return store().recode(u, defaultPassword(), _pass, _kdf);
+ return false;
}
bool KeyManager::load(std::string const& _pass)
diff --git a/libethereum/KeyManager.h b/libethereum/KeyManager.h
index 2cacf7bfe..2fc47e9b5 100644
--- a/libethereum/KeyManager.h
+++ b/libethereum/KeyManager.h
@@ -42,6 +42,12 @@ struct KeyInfo
static const auto DontKnowThrow = [](){ throw UnknownPassword(); return std::string(); };
+enum class SemanticPassword
+{
+ Existing,
+ Master
+};
+
// TODO: This one is specifically for Ethereum, but we can make it generic in due course.
// TODO: hidden-partition style key-store.
/**
@@ -67,9 +73,12 @@ 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; }
+ bool haveHint(std::string const& _pass) const { auto h = hashPassword(_pass); return m_cachedPasswords.count(h) && !m_cachedPasswords.at(h).empty(); }
AddressHash accounts() const;
std::unordered_map> accountDetails() const;
+ std::string const& hint(Address const& _a) const { try { return m_passwordInfo.at(m_keyInfo.at(m_addrLookup.at(_a)).passHash); } catch (...) { return EmptyString; } }
h128 uuid(Address const& _a) const;
Address address(h128 const& _uuid) const;
@@ -84,7 +93,8 @@ public:
Secret secret(Address const& _address, std::function const& _pass = DontKnowThrow) const;
Secret secret(h128 const& _uuid, std::function const& _pass = DontKnowThrow) const;
- void reencode(Address const& _address, std::function const& _pass = DontKnowThrow, KDF _kdf = KDF::Scrypt);
+ bool recode(Address const& _address, SemanticPassword _newPass, std::function const& _pass = DontKnowThrow, KDF _kdf = KDF::Scrypt);
+ bool recode(Address const& _address, std::string const& _newPass, std::string const& _hint, std::function const& _pass = DontKnowThrow, KDF _kdf = KDF::Scrypt);
void kill(h128 const& _id) { kill(address(_id)); }
void kill(Address const& _a);