25 #include <boost/algorithm/string.hpp> 26 #include <boost/filesystem.hpp> 36 namespace fs = boost::filesystem;
38 static const int c_keyFileVersion = 3;
48 unsigned version = ret.count(
"Version") ? stoi(ret[
"Version"].
get_str()) : ret.
count(
"version") ? ret[
"version"].get_int() : 0;
55 ret[
"id"] = old[
"Id"];
57 c[
"ciphertext"] = old[
"Crypto"].get_obj()[
"CipherText"];
58 c[
"cipher"] =
"aes-128-cbc";
61 cp[
"iv"] = old[
"Crypto"].get_obj()[
"IV"];
62 c[
"cipherparams"] = cp;
64 c[
"kdf"] = old[
"Crypto"].get_obj()[
"KeyHeader"].get_obj()[
"Kdf"];
67 kp[
"salt"] = old[
"Crypto"].get_obj()[
"Salt"];
68 for (
auto const& i: old[
"Crypto"].get_obj()[
"KeyHeader"].get_obj()[
"KdfParams"].get_obj())
69 if (i.first !=
"SaltLen")
70 kp[boost::to_lower_copy(i.first)] = i.second;
73 c[
"sillymac"] = old[
"Crypto"].get_obj()[
"MAC"];
74 c[
"sillymacjson"] = _s;
78 if (ret.count(
"Crypto") && !ret.count(
"crypto"))
80 ret[
"crypto"] = ret[
"Crypto"];
85 ret[
"crypto"].get_obj()[
"cipher"] =
"aes-128-ctr";
86 ret[
"crypto"].get_obj()[
"compat"] =
"2";
89 if (version == c_keyFileVersion)
94 SecretStore::SecretStore(
string const& _path): m_path(_path)
107 auto rit = m_cached.find(_uuid);
108 if (_useCache && rit != m_cached.end())
110 auto it = m_keys.find(_uuid);
112 if (it != m_keys.end())
116 m_cached[_uuid] = key;
124 if (
auto k = key(_address))
149 m_keys[r] = move(key);
159 m_keys[r] = move(key);
166 m_cached.erase(_uuid);
167 if (m_keys.count(_uuid))
169 fs::remove(m_keys[_uuid].filename);
181 fs::path p(_keysPath);
182 fs::create_directories(p);
184 for (
auto& k: m_keys)
186 string uuid =
toUUID(k.first);
187 string filename = (p / uuid).
string() +
".json";
191 v[
"address"] = k.second.address.hex();
192 v[
"crypto"] = crypto;
194 v[
"version"] = c_keyFileVersion;
196 swap(k.second.filename, filename);
197 if (!filename.empty() && !fs::equivalent(filename, k.second.filename))
198 fs::remove(filename);
204 if (m_keys.find(_uuid) != m_keys.end() && m_keys[_uuid].address ==
ZeroAddress)
206 m_keys[_uuid].address = _address;
214 fs::path p(_keysPath);
217 for (fs::directory_iterator it(p); it != fs::directory_iterator(); ++it)
218 if (fs::is_regular_file(it->path()))
219 readKey(it->path().string(),
true);
226 ctrace <<
"Reading" << _file;
227 return readKeyContent(
contentsString(_file), _takeFileOwnership ? _file :
string());
240 if (o.find(
"address") != o.end() &&
isHex(o[
"address"].
get_str()))
243 cwarn <<
"Account address is either not defined or not in hex format" << _file;
248 cwarn <<
"Invalid JSON in key file" << _file;
259 if (
auto k = key(_address))
261 bytesSec s = secret(_address, _pass);
266 k->second.encryptedKey =
encrypt(s.
ref(), _newPass, _kdf);
276 for (
auto const& k: m_keys)
277 if (k.second.address == _address)
284 for (
auto& k: m_keys)
285 if (k.second.address == _address)
292 bytesSec s = secret(_uuid, _pass,
true);
295 m_cached.erase(_uuid);
296 m_keys[_uuid].encryptedKey =
encrypt(s.
ref(), _newPass, _kdf);
304 unsigned iterations = 1 << 18;
310 o_ret[
"kdf"] =
"scrypt";
313 params[
"n"] = int64_t(iterations);
314 params[
"r"] = int(r);
315 params[
"p"] = int(p);
316 params[
"dklen"] = int(dklen);
317 params[
"salt"] =
toHex(salt);
318 o_ret[
"kdfparams"] = params;
320 return scrypt(_pass, salt, iterations, r, p, dklen);
324 o_ret[
"kdf"] =
"pbkdf2";
327 params[
"prf"] =
"hmac-sha256";
328 params[
"c"] = int(iterations);
329 params[
"salt"] =
toHex(salt);
330 params[
"dklen"] = int(dklen);
331 o_ret[
"kdfparams"] = params;
333 return pbkdf2(_pass, salt, iterations, dklen);
341 bytesSec derivedKey = deriveNewKey(_pass, _kdf, ret);
342 if (derivedKey.
empty())
343 BOOST_THROW_EXCEPTION(crypto::CryptoException() <<
errinfo_comment(
"Key derivation failed."));
345 ret[
"cipher"] =
"aes-128-ctr";
351 ret[
"cipherparams"] = params;
356 if (cipherText.empty())
357 BOOST_THROW_EXCEPTION(crypto::CryptoException() <<
errinfo_comment(
"Key encryption failed."));
358 ret[
"ciphertext"] =
toHex(cipherText);
361 h256 mac =
sha3(derivedKey.
ref().cropped(16, 16).toBytes() + cipherText);
378 if (o[
"kdf"].
get_str() ==
"pbkdf2")
380 auto params = o[
"kdfparams"].get_obj();
381 if (params[
"prf"].
get_str() !=
"hmac-sha256")
383 cwarn <<
"Unknown PRF for PBKDF2" << params[
"prf"].get_str() <<
"not supported.";
386 unsigned iterations = params[
"c"].get_int();
388 derivedKey =
pbkdf2(_pass, salt, iterations, params[
"dklen"].get_int());
390 else if (o[
"kdf"].
get_str() ==
"scrypt")
392 auto p = o[
"kdfparams"].get_obj();
393 derivedKey =
scrypt(_pass,
fromHex(p[
"salt"].
get_str()), p[
"n"].get_int(), p[
"r"].get_int(), p[
"p"].get_int(), p[
"dklen"].get_int());
397 cwarn <<
"Unknown KDF" << o[
"kdf"].get_str() <<
"not supported.";
401 if (derivedKey.
size() < 32 && !(o.count(
"compat") && o[
"compat"].get_str() ==
"2"))
403 cwarn <<
"Derived key's length too short (<32 bytes)";
414 if (o.count(
"compat") && o[
"compat"].get_str() ==
"2")
415 macExp =
sha3(derivedKey.
ref().cropped(derivedKey.
size() - 16).toBytes() + cipherText);
417 macExp =
sha3(derivedKey.
ref().cropped(16, 16).toBytes() + cipherText);
424 else if (o.count(
"sillymac"))
435 cwarn <<
"No MAC. Proceeding anyway.";
438 if (o[
"cipher"].
get_str() ==
"aes-128-ctr")
440 auto params = o[
"cipherparams"].get_obj();
442 if (o.count(
"compat") && o[
"compat"].get_str() ==
"2")
452 cwarn <<
"Unknown cipher" << o[
"cipher"].get_str() <<
"not supported.";
const Object & get_obj() const
h128 fromUUID(std::string const &_uuid)
Adapted from code found on http://stackoverflow.com/questions/180947/base64-decode-snippet-in-c Origi...
std::string toHex(T const &_data, int _w=2, HexPrefix _prefix=HexPrefix::DontAdd)
h128 readKeyContent(std::string const &_content, std::string const &_file=std::string())
Import the key contained in the json-encoded _content, but do not store it in the managed directory...
void swap(dev::eth::Watch &_a, dev::eth::Watch &_b)
void save()
Store all keys in the managed directory.
void writeFile(std::string const &_file, bytesConstRef _data, bool _writeDeleteRename=false)
Write the given binary data into the given file, replacing the file if it pre-exists.
bytesSec decryptSymNoAuth(SecureFixedHash< 16 > const &_k, h128 const &_iv, bytesConstRef _cipher)
Decrypts payload with specified IV/ctr using AES128-CTR.
bytesSec pbkdf2(std::string const &_pass, bytes const &_salt, unsigned _iterations, unsigned _dkLen=32)
Derive key via PBKDF2.
Simple class that represents a "key pair".
secure_vector< byte > bytesSec
h160 Address
An Ethereum address: 20 bytes.
SecureFixedHash< 32 > sha3Secure(bytesConstRef _input)
std::pair< bytes, h128 > encryptSymNoAuth(SecureFixedHash< 16 > const &_k, bytesConstRef _plain)
Encrypts payload with random IV/ctr using AES128-CTR.
SecureFixedHash< 32 > Secret
std::hash for asio::adress
std::string contentsString(std::string const &_file)
Retrieve and returns the contents of the given file as a std::string.
std::string toString(string32 const &_s)
Make normal string from fixed-length string.
mConfig::Value_type mValue
bytes fromHex(std::string const &_s, WhenError _throw=WhenError::DontThrow)
bool read_string(const String_type &s, Value_type &value)
std::pair< h128 const, EncryptedKey > const * key(Address const &_address) const
bytesSec secret(h128 const &_uuid, std::function< std::string()> const &_pass, bool _useCache=true) const
bool isHex(std::string const &_s) noexcept
Address ZeroAddress
The zero address.
static bytesSec decrypt(std::string const &_v, std::string const &_pass)
Decrypts _v with a key derived from _pass or the empty byte array on error.
std::vector< byte > bytes
Fixed-size raw-byte array container type, with an API optimised for storing hashes.
bytes asBytes(std::string const &_b)
Converts a string to a byte array containing the string's (byte) data.
h128 importSecret(bytesSec const &_s, std::string const &_pass)
Imports the decrypted key given by _s and stores it, encrypted with (a key derived from) the password...
static std::string encrypt(bytesConstRef _v, std::string const &_pass, KDF _kdf=KDF::Scrypt)
Encrypts _v with a key derived from _pass or the empty string on error.
bool recode(h128 const &_uuid, std::string const &_newPass, std::function< std::string()> const &_pass, KDF _kdf=KDF::Scrypt)
Decrypts and re-encrypts the key identified by _uuid.
mConfig::Object_type mObject
Address const & address() const
Retrieve the associated address of the public key.
bytesSec scrypt(std::string const &_pass, bytes const &_salt, uint64_t _n, uint32_t _r, uint32_t _p, unsigned _dkLen)
Derive key via Scrypt.
static FixedHash random()
Value_type::String_type write_string(const Value_type &value, bool pretty)
h128 readKey(std::string const &_file, bool _takeFileOwnership)
Import the key from the file _file, but do not copy it to the managed directory yet.
void encrypt(Public const &_k, bytesConstRef _plain, bytes &o_cipher)
Encrypts plain text using Public key.
bool decrypt(Secret const &_k, bytesConstRef _cipher, bytes &o_plaintext)
Decrypts cipher using Secret key.
boost::error_info< struct tag_comment, std::string > errinfo_comment
bool sha3(bytesConstRef _input, bytesRef o_output)
Calculate SHA3-256 hash of the given input and load it into the given output.
void kill(h128 const &_uuid)
Removes the key specified by _uuid from both memory and disk.
void setPath(std::string const &_path)
Set a path for finding secrets.
struct evm_uint160be address(struct evm_env *env)
std::string get_str(std::string::const_iterator begin, std::string::const_iterator end)
void clearCache() const
Clears all cached decrypted keys.
#define DEV_IGNORE_EXCEPTIONS(X)
bool noteAddress(h128 const &_uuid, Address const &_address)
std::string toUUID(h128 const &_uuid)