Gav Wood
10 years ago
15 changed files with 528 additions and 17 deletions
@ -0,0 +1,33 @@ |
|||
cmake_policy(SET CMP0015 NEW) |
|||
set(CMAKE_AUTOMOC OFF) |
|||
|
|||
aux_source_directory(. SRC_LIST) |
|||
|
|||
include_directories(BEFORE ..) |
|||
include_directories(${Boost_INCLUDE_DIRS}) |
|||
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) |
|||
|
|||
if (JSCONSOLE) |
|||
include_directories(${V8_INCLUDE_DIRS}) |
|||
endif() |
|||
|
|||
set(EXECUTABLE ethkey) |
|||
|
|||
file(GLOB HEADERS "*.h") |
|||
|
|||
add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) |
|||
|
|||
add_dependencies(${EXECUTABLE} BuildInfo.h) |
|||
|
|||
target_link_libraries(${EXECUTABLE} devcrypto) |
|||
target_link_libraries(${EXECUTABLE} ethcore) |
|||
|
|||
if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW) |
|||
eth_copy_dlls("${EXECUTABLE}" MHD_DLLS) |
|||
endif() |
|||
|
|||
if (APPLE) |
|||
install(TARGETS ${EXECUTABLE} DESTINATION bin) |
|||
else() |
|||
eth_install_executable(${EXECUTABLE}) |
|||
endif() |
@ -0,0 +1,351 @@ |
|||
#pragma once |
|||
|
|||
/*
|
|||
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 <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file KeyAux.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
* CLI module for key management. |
|||
*/ |
|||
|
|||
#include <thread> |
|||
#include <chrono> |
|||
#include <fstream> |
|||
#include <iostream> |
|||
#include <boost/algorithm/string.hpp> |
|||
#include <boost/algorithm/string/trim_all.hpp> |
|||
#include <libdevcore/SHA3.h> |
|||
#include <libdevcore/FileSystem.h> |
|||
#include <libethcore/KeyManager.h> |
|||
#include <libethcore/ICAP.h> |
|||
#include "BuildInfo.h" |
|||
using namespace std; |
|||
using namespace dev; |
|||
using namespace dev::eth; |
|||
using namespace boost::algorithm; |
|||
|
|||
#undef RETURN |
|||
|
|||
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 + "): "); |
|||
} |
|||
|
|||
string createPassword(std::string const& _prompt) |
|||
{ |
|||
string ret; |
|||
while (true) |
|||
{ |
|||
ret = getPassword(_prompt); |
|||
string confirm = getPassword("Please confirm the password by entering it again: "); |
|||
if (ret == confirm) |
|||
break; |
|||
cout << "Passwords were different. Try again." << endl; |
|||
} |
|||
return ret; |
|||
// cout << "Enter a hint to help you remember this password: " << flush;
|
|||
// cin >> hint;
|
|||
// return make_pair(ret, hint);
|
|||
} |
|||
|
|||
pair<string, string> createPassword(KeyManager& _keyManager, std::string const& _prompt) |
|||
{ |
|||
string pass; |
|||
while (true) |
|||
{ |
|||
pass = getPassword(_prompt); |
|||
string confirm = getPassword("Please confirm the password by entering it again: "); |
|||
if (pass == confirm) |
|||
break; |
|||
cout << "Passwords were different. Try again." << endl; |
|||
} |
|||
string hint; |
|||
if (!_keyManager.haveHint(pass)) |
|||
{ |
|||
cout << "Enter a hint to help you remember this password: " << flush; |
|||
cin >> hint; |
|||
} |
|||
return make_pair(pass, hint); |
|||
} |
|||
|
|||
class KeyCLI |
|||
{ |
|||
public: |
|||
enum class OperationMode |
|||
{ |
|||
None, |
|||
ListBare, |
|||
NewBare, |
|||
ImportBare, |
|||
ExportBare, |
|||
RecodeBare, |
|||
FirstWallet, |
|||
CreateWallet, |
|||
List = FirstWallet, |
|||
New, |
|||
Import, |
|||
Export, |
|||
Recode, |
|||
Kill |
|||
}; |
|||
|
|||
KeyCLI(OperationMode _mode = OperationMode::None): m_mode(_mode) {} |
|||
|
|||
bool interpretOption(int& i, int argc, char** argv) |
|||
{ |
|||
string arg = argv[i]; |
|||
if (arg == "-n" || arg == "--new") |
|||
m_mode = OperationMode::New; |
|||
else if (arg == "--wallet-path" && i + 1 < argc) |
|||
m_walletPath = argv[++i]; |
|||
else if (arg == "--secrets-path" && i + 1 < argc) |
|||
m_secretsPath = argv[++i]; |
|||
else if ((arg == "-m" || arg == "--master") && i + 1 < argc) |
|||
m_masterPassword = argv[++i]; |
|||
else if (arg == "--unlock" && i + 1 < argc) |
|||
m_unlocks.push_back(argv[++i]); |
|||
else if (arg == "--lock" && i + 1 < argc) |
|||
m_lock = argv[++i]; |
|||
else if (arg == "--kdf" && i + 1 < argc) |
|||
m_kdf = argv[++i]; |
|||
else if (arg == "--kdf-param" && i + 2 < argc) |
|||
{ |
|||
auto n = argv[++i]; |
|||
auto v = argv[++i]; |
|||
m_kdfParams[n] = v; |
|||
} |
|||
else if (arg == "--new-bare") |
|||
m_mode = OperationMode::NewBare; |
|||
else if (arg == "--import-bare") |
|||
m_mode = OperationMode::ImportBare; |
|||
else if (arg == "--list-bare") |
|||
m_mode = OperationMode::ListBare; |
|||
else if (arg == "--export-bare") |
|||
m_mode = OperationMode::ExportBare; |
|||
else if (arg == "--recode-bare") |
|||
m_mode = OperationMode::RecodeBare; |
|||
else if (arg == "--create-wallet") |
|||
m_mode = OperationMode::CreateWallet; |
|||
else if (arg == "--list") |
|||
m_mode = OperationMode::List; |
|||
else if ((arg == "-n" || arg == "--new") && i + 1 < argc) |
|||
{ |
|||
m_mode = OperationMode::New; |
|||
m_name = argv[++i]; |
|||
} |
|||
else if ((arg == "-i" || arg == "--import") && i + 2 < argc) |
|||
{ |
|||
m_mode = OperationMode::Import; |
|||
m_inputs = strings(1, argv[++i]); |
|||
m_name = argv[++i]; |
|||
} |
|||
else if (arg == "--export") |
|||
m_mode = OperationMode::Export; |
|||
else if (arg == "--recode") |
|||
m_mode = OperationMode::Recode; |
|||
else if (arg == "--no-icap") |
|||
m_icap = false; |
|||
else if (m_mode == OperationMode::ImportBare || m_mode == OperationMode::Recode || m_mode == OperationMode::Export || m_mode == OperationMode::RecodeBare || m_mode == OperationMode::ExportBare) |
|||
m_inputs.push_back(arg); |
|||
else |
|||
return false; |
|||
return true; |
|||
} |
|||
|
|||
KeyPair makeKey() const |
|||
{ |
|||
KeyPair k(Secret::random()); |
|||
while (m_icap && k.address()[0]) |
|||
k = KeyPair(sha3(k.secret())); |
|||
return k; |
|||
} |
|||
|
|||
void execute() |
|||
{ |
|||
if (m_mode == OperationMode::CreateWallet) |
|||
{ |
|||
KeyManager wallet(m_walletPath, m_secretsPath); |
|||
if (m_masterPassword.empty()) |
|||
m_masterPassword = createPassword("Please enter a MASTER password to protect your key store (make it strong!): "); |
|||
if (m_masterPassword.empty()) |
|||
cerr << "Aborted (empty password not allowed)." << endl; |
|||
else |
|||
wallet.create(m_masterPassword); |
|||
} |
|||
else if (m_mode < OperationMode::FirstWallet) |
|||
{ |
|||
SecretStore store(m_secretsPath); |
|||
switch (m_mode) |
|||
{ |
|||
case OperationMode::ListBare: |
|||
for (h128 const& u: std::set<h128>() + store.keys()) |
|||
cout << toUUID(u) << endl; |
|||
break; |
|||
case OperationMode::NewBare: |
|||
{ |
|||
if (m_lock.empty()) |
|||
m_lock = createPassword("Enter a password with which to secure this account: "); |
|||
auto k = makeKey(); |
|||
store.importSecret(k.secret().asBytes(), m_lock); |
|||
cout << "Created key " << k.address().abridged() << endl; |
|||
cout << "Address: " << k.address().hex() << endl; |
|||
cout << "ICAP: " << ICAP(k.address()).encoded() << endl; |
|||
break; |
|||
} |
|||
case OperationMode::ImportBare: |
|||
for (string const& i: m_inputs) |
|||
{ |
|||
h128 u; |
|||
bytes b; |
|||
b = fromHex(i); |
|||
if (b.size() != 32) |
|||
{ |
|||
std::string s = contentsString(i); |
|||
b = fromHex(s); |
|||
if (b.size() != 32) |
|||
u = store.importKey(i); |
|||
} |
|||
if (!u && b.size() == 32) |
|||
u = store.importSecret(b, lockPassword(toAddress(Secret(b)).abridged())); |
|||
else |
|||
{ |
|||
cerr << "Cannot import " << i << " not a file or secret." << endl; |
|||
continue; |
|||
} |
|||
cout << "Successfully imported " << i << " as " << toUUID(u); |
|||
} |
|||
break; |
|||
case OperationMode::ExportBare: break; |
|||
case OperationMode::RecodeBare: |
|||
for (auto const& i: m_inputs) |
|||
{ |
|||
h128 u = fromUUID(i); |
|||
if (u) |
|||
if (store.recode(u, lockPassword(toUUID(u)), [&](){ return getPassword("Enter password for key " + toUUID(u) + ": "); }, kdf())) |
|||
cerr << "Re-encoded " << toUUID(u) << endl; |
|||
else |
|||
cerr << "Couldn't re-encode " << toUUID(u) << "; key corrupt or incorrect password supplied." << endl; |
|||
else |
|||
cerr << "Couldn't re-encode " << toUUID(u) << "; not found." << endl; |
|||
} |
|||
default: break; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
KeyManager wallet(m_walletPath, m_secretsPath); |
|||
if (wallet.exists()) |
|||
while (true) |
|||
{ |
|||
if (wallet.load(m_masterPassword)) |
|||
break; |
|||
if (!m_masterPassword.empty()) |
|||
{ |
|||
cout << "Password invalid. Try again." << endl; |
|||
m_masterPassword.clear(); |
|||
} |
|||
m_masterPassword = getPassword("Please enter your MASTER password: "); |
|||
} |
|||
else |
|||
{ |
|||
cerr << "Couldn't open wallet. Does it exist?" << endl; |
|||
exit(-1); |
|||
} |
|||
} |
|||
} |
|||
|
|||
std::string lockPassword(std::string const& _accountName) |
|||
{ |
|||
return m_lock.empty() ? createPassword("Enter a password with which to secure account " + _accountName + ": ") : m_lock; |
|||
} |
|||
|
|||
static void streamHelp(ostream& _out) |
|||
{ |
|||
_out |
|||
<< "Secret-store (\"bare\") operation modes:" << endl |
|||
<< " --list-bare List all secret available in secret-store." << endl |
|||
<< " --new-bare Generate and output a key without interacting with wallet and dump the JSON." << endl |
|||
<< " --import-bare [ <file>|<secret-hex> , ... ] Import keys from given sources." << endl |
|||
<< " --recode-bare [ <uuid>|<file> , ... ] Decrypt and re-encrypt given keys." << endl |
|||
// << " --export-bare [ <uuid> , ... ] Export given keys." << endl
|
|||
<< "Secret-store configuration:" << endl |
|||
<< " --secrets-path <path> Specify Web3 secret-store path (default: " << SecretStore::defaultPath() << ")" << endl |
|||
<< endl |
|||
<< "Wallet operating modes:" << endl |
|||
<< " -l,--list List all keys available in wallet." << endl |
|||
<< " -n,--new <name> Create a new key with given name and add it in the wallet." << endl |
|||
<< " -i,--import [<uuid>|<file>|<secret-hex>] <name> Import keys from given source and place in wallet." << endl |
|||
<< " -e,--export [ <address>|<uuid> , ... ] Export given keys." << endl |
|||
<< " -r,--recode [ <address>|<uuid>|<file> , ... ] Decrypt and re-encrypt given keys." << endl |
|||
<< "Wallet configuration:" << endl |
|||
<< " --create-wallet Create an Ethereum master wallet." << endl |
|||
<< " --wallet-path <path> Specify Ethereum wallet path (default: " << KeyManager::defaultPath() << ")" << endl |
|||
<< " -m, --master <password> Specify wallet (master) password." << endl |
|||
<< endl |
|||
<< "Encryption configuration:" << endl |
|||
<< " --kdf <kdfname> Specify KDF to use when encrypting (default: sc rypt)" << endl |
|||
<< " --kdf-param <name> <value> Specify a parameter for the KDF." << endl |
|||
// << " --cipher <ciphername> Specify cipher to use when encrypting (default: aes-128-ctr)" << endl
|
|||
// << " --cipher-param <name> <value> Specify a parameter for the cipher." << endl
|
|||
<< " --lock <password> <hint> Specify password for when encrypting a (the) key." << endl |
|||
<< endl |
|||
<< "Decryption configuration:" << endl |
|||
<< " --unlock <password> Specify password for a (the) key." << endl |
|||
<< "Key generation configuration:" << endl |
|||
<< " --no-icap Don't bother to make a direct-ICAP capable key." << endl |
|||
; |
|||
} |
|||
|
|||
static bool isTrue(std::string const& _m) |
|||
{ |
|||
return _m == "on" || _m == "yes" || _m == "true" || _m == "1"; |
|||
} |
|||
|
|||
static bool isFalse(std::string const& _m) |
|||
{ |
|||
return _m == "off" || _m == "no" || _m == "false" || _m == "0"; |
|||
} |
|||
|
|||
private: |
|||
KDF kdf() const { return m_kdf == "pbkdf2" ? KDF::PBKDF2_SHA256 : KDF::Scrypt; } |
|||
|
|||
/// Operating mode.
|
|||
OperationMode m_mode; |
|||
|
|||
/// Wallet stuff
|
|||
string m_secretsPath = SecretStore::defaultPath(); |
|||
string m_walletPath = KeyManager::defaultPath(); |
|||
|
|||
/// Wallet password stuff
|
|||
string m_masterPassword; |
|||
strings m_unlocks; |
|||
string m_lock; |
|||
bool m_icap = true; |
|||
|
|||
/// Creating
|
|||
string m_name; |
|||
|
|||
/// Importing
|
|||
strings m_inputs; |
|||
|
|||
string m_kdf = "scrypt"; |
|||
map<string, string> m_kdfParams; |
|||
// string m_cipher;
|
|||
// map<string, string> m_cipherParams;
|
|||
}; |
@ -0,0 +1,84 @@ |
|||
/*
|
|||
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 <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file main.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
* Ethereum client. |
|||
*/ |
|||
|
|||
#include <thread> |
|||
#include <chrono> |
|||
#include <fstream> |
|||
#include <iostream> |
|||
#include <libdevcore/FileSystem.h> |
|||
#include <libdevcore/Log.h> |
|||
#include <libethcore/KeyManager.h> |
|||
#include "BuildInfo.h" |
|||
#include "KeyAux.h" |
|||
using namespace std; |
|||
using namespace dev; |
|||
using namespace dev::eth; |
|||
|
|||
void help() |
|||
{ |
|||
cout |
|||
<< "Usage ethkey [OPTIONS]" << endl |
|||
<< "Options:" << endl << endl; |
|||
KeyCLI::streamHelp(cout); |
|||
cout |
|||
<< "General Options:" << endl |
|||
<< " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (default: 8)." << endl |
|||
<< " -V,--version Show the version and exit." << endl |
|||
<< " -h,--help Show this help message and exit." << endl |
|||
; |
|||
exit(0); |
|||
} |
|||
|
|||
void version() |
|||
{ |
|||
cout << "ethkey version " << dev::Version << endl; |
|||
cout << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl; |
|||
exit(0); |
|||
} |
|||
|
|||
int main(int argc, char** argv) |
|||
{ |
|||
KeyCLI m(KeyCLI::OperationMode::ListBare); |
|||
g_logVerbosity = 0; |
|||
|
|||
for (int i = 1; i < argc; ++i) |
|||
{ |
|||
string arg = argv[i]; |
|||
if (m.interpretOption(i, argc, argv)) {} |
|||
else if ((arg == "-v" || arg == "--verbosity") && i + 1 < argc) |
|||
g_logVerbosity = atoi(argv[++i]); |
|||
else if (arg == "-h" || arg == "--help") |
|||
help(); |
|||
else if (arg == "-V" || arg == "--version") |
|||
version(); |
|||
else |
|||
{ |
|||
cerr << "Invalid argument: " << arg << endl; |
|||
exit(-1); |
|||
} |
|||
} |
|||
|
|||
m.execute(); |
|||
|
|||
return 0; |
|||
} |
|||
|
Loading…
Reference in new issue