From a3cc9cc10bffb3dcbdd06a86d7636993b00c7ff6 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 20 May 2015 22:06:21 +0200 Subject: [PATCH] Ability to import version 1 keys. Possibly. --- alethzero/Main.ui | 8 ++- alethzero/MainWin.cpp | 27 +++++++++ alethzero/MainWin.h | 1 + libdevcrypto/SecretStore.cpp | 105 +++++++++++++++++++++++++++-------- libdevcrypto/SecretStore.h | 4 +- 5 files changed, 120 insertions(+), 25 deletions(-) diff --git a/alethzero/Main.ui b/alethzero/Main.ui index b5e22409d..f37f532a2 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -157,6 +157,7 @@ + @@ -1656,7 +1657,7 @@ font-size: 14pt &Enable Local Addresses - + true @@ -1745,6 +1746,11 @@ font-size: 14pt Co&nfirm Transactions + + + Import &Secret Key File... + + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 07f48c0bc..4f1da1c1a 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -813,6 +813,33 @@ void Main::on_importKey_triggered() } void Main::on_importKeyFile_triggered() +{ + QString s = QFileDialog::getOpenFileName(this, "Claim Account Contents", QDir::homePath(), "JSON Files (*.json);;All Files (*)"); + h128 uuid = m_keyManager.store().importKey(s.toStdString()); + if (!uuid) + { + QMessageBox::warning(this, "Key File Invalid", "Could not find secret key definition. This is probably not an Web3 key file."); + return; + } + + QString info = QInputDialog::getText(this, "Import Key File", "Enter a description of this key to help you recognise it in the future."); + + QString pass; + for (Secret s; !s;) + { + s = Secret(m_keyManager.store().secret(uuid, [&](){ + pass = QInputDialog::getText(this, "Import Key File", "Enter the password for the key to complete the import.", QLineEdit::Password); + return pass.toStdString(); + }, false)); + if (!s && QMessageBox::question(this, "Import Key File", "The password you provided is incorrect. Would you like to try again?", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel) + return; + } + + QString hint = QInputDialog::getText(this, "Import Key File", "Enter a hint for this password to help you remember it."); + m_keyManager.importExisting(uuid, info.toStdString(), pass.toStdString(), hint.toStdString()); +} + +void Main::on_claimPresale_triggered() { QString s = QFileDialog::getOpenFileName(this, "Claim Account Contents", QDir::homePath(), "JSON Files (*.json);;All Files (*)"); try diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 9f2082dd0..fdf7a9a72 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -138,6 +138,7 @@ private slots: void on_killAccount_triggered(); void on_importKey_triggered(); void on_importKeyFile_triggered(); + void on_claimPresale_triggered(); void on_exportKey_triggered(); // Account pane diff --git a/libdevcrypto/SecretStore.cpp b/libdevcrypto/SecretStore.cpp index 7bc870bcf..e422c32f8 100644 --- a/libdevcrypto/SecretStore.cpp +++ b/libdevcrypto/SecretStore.cpp @@ -22,6 +22,7 @@ #include "SecretStore.h" #include #include +#include #include #include #include @@ -42,10 +43,10 @@ SecretStore::~SecretStore() { } -bytes SecretStore::secret(h128 const& _uuid, function const& _pass) const +bytes SecretStore::secret(h128 const& _uuid, function const& _pass, bool _useCache) const { auto rit = m_cached.find(_uuid); - if (rit != m_cached.end()) + if (_useCache && rit != m_cached.end()) return rit->second; auto it = m_keys.find(_uuid); if (it == m_keys.end()) @@ -101,28 +102,71 @@ 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); boost::filesystem::create_directories(p); - js::mValue v; for (fs::directory_iterator it(p); it != fs::directory_iterator(); ++it) if (is_regular_file(it->path())) - { - cdebug << "Reading" << it->path(); - js::read_string(contentsString(it->path().string()), v); - if (v.type() == js::obj_type) - { - js::mObject o = v.get_obj(); - int version = o.count("Version") ? stoi(o["Version"].get_str()) : o.count("version") ? o["version"].get_int() : 0; - if (version == 2) - m_keys[fromUUID(o["id"].get_str())] = make_pair(js::write_string(o["crypto"], false), it->path().string()); - else - cwarn << "Cannot read key version" << version; - } -// else -// cwarn << "Invalid JSON in key file" << it->path().string(); - } + readKey(it->path().string(), true); +} + +h128 SecretStore::readKey(std::string const& _file, bool _deleteFile) +{ + cdebug << "Reading" << _file; + js::mValue u = upgraded(contentsString(_file)); + if (u.type() == js::obj_type) + { + js::mObject& o = u.get_obj(); + auto uuid = fromUUID(o["id"].get_str()); + m_keys[uuid] = make_pair(js::write_string(o["crypto"], false), _deleteFile ? _file : string()); + return uuid; + } + else + cwarn << "Invalid JSON in key file" << _file; + return h128(); } std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass) @@ -202,13 +246,28 @@ bytes SecretStore::decrypt(std::string const& _v, std::string const& _pass) bytes cipherText = fromHex(o["ciphertext"].get_str()); // check MAC - h256 mac(o["mac"].get_str()); - h256 macExp = sha3(bytesConstRef(&derivedKey).cropped(derivedKey.size() - 16).toBytes() + cipherText); - if (mac != macExp) + if (o.count("mac")) { - cwarn << "Invalid key - MAC mismatch; expected" << toString(macExp) << ", got" << toString(mac); - return bytes(); + h256 mac(o["mac"].get_str()); + h256 macExp = sha3(bytesConstRef(&derivedKey).cropped(derivedKey.size() - 16).toBytes() + cipherText); + if (mac != macExp) + { + cwarn << "Invalid key - MAC mismatch; expected" << toString(macExp) << ", got" << toString(mac); + return bytes(); + } } + else if (o.count("sillymac")) + { + h256 mac(o["sillymac"].get_str()); + h256 macExp = sha3(asBytes(o["sillymacjson"].get_str()) + bytesConstRef(&derivedKey).cropped(derivedKey.size() - 16).toBytes() + cipherText); + if (mac != macExp) + { + cwarn << "Invalid key - MAC mismatch; expected" << toString(macExp) << ", got" << toString(mac); + return bytes(); + } + } + else + cwarn << "No MAC. Proceeding anyway."; // decrypt if (o["cipher"].get_str() == "aes-128-cbc") diff --git a/libdevcrypto/SecretStore.h b/libdevcrypto/SecretStore.h index 18c290c1f..171603053 100644 --- a/libdevcrypto/SecretStore.h +++ b/libdevcrypto/SecretStore.h @@ -36,7 +36,8 @@ public: SecretStore(); ~SecretStore(); - bytes secret(h128 const& _uuid, std::function const& _pass) const; + 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 kill(h128 const& _uuid); @@ -48,6 +49,7 @@ private: void load(std::string const& _keysPath = getDataDir("web3") + "/keys"); static std::string encrypt(bytes const& _v, std::string const& _pass); static bytes decrypt(std::string const& _v, std::string const& _pass); + h128 readKey(std::string const& _file, bool _deleteFile); mutable std::unordered_map m_cached; std::unordered_map> m_keys;