25 #include <boost/algorithm/string.hpp>
26 #include <boost/filesystem.hpp>
35 namespace js = json_spirit;
36 namespace fs = boost::filesystem;
38 static const int c_keyFileVersion = 3;
41 static js::mValue upgraded(
string const& _s)
44 js::read_string(_s, v);
45 if (v.type() != js::obj_type)
47 js::mObject ret = v.get_obj();
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(fs::path
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())
117 m_cached[_uuid] = key;
128 if (
auto k = key(_address))
137 js::mValue u = upgraded(_content);
138 if (u.type() != js::obj_type)
140 return decrypt(js::write_string(u.get_obj()[
"crypto"],
false), _pass);
153 m_keys[r] = move(key);
163 m_keys[r] = move(key);
170 m_cached.erase(_uuid);
171 if (m_keys.count(_uuid))
173 fs::remove(m_keys[_uuid].filename);
185 fs::create_directories(_keysPath);
187 for (
auto& k: m_keys)
189 string uuid =
toUUID(k.first);
190 fs::path filename = (_keysPath / uuid).
string() +
".json";
193 js::read_string(k.second.encryptedKey, crypto);
194 v[
"address"] = k.second.address.hex();
195 v[
"crypto"] = crypto;
197 v[
"version"] = c_keyFileVersion;
198 writeFile(filename, js::write_string(js::mValue(v),
true));
199 swap(k.second.filename, filename);
200 if (!filename.empty() && !fs::equivalent(filename, k.second.filename))
201 fs::remove(filename);
207 auto it = m_keys.find(_uuid);
208 if (it != m_keys.end() && it->second.address ==
ZeroAddress)
210 it->second.address = _address;
216 void SecretStore::load(fs::path
const& _keysPath)
220 for (fs::directory_iterator it(_keysPath); it != fs::directory_iterator(); ++it)
221 if (fs::is_regular_file(it->path()))
222 readKey(it->path().string(),
true);
229 ctrace <<
"Reading" << _file.string();
237 js::mValue u = upgraded(_content);
238 if (u.type() == js::obj_type)
240 js::mObject& o = u.get_obj();
241 auto uuid =
fromUUID(o[
"id"].get_str());
243 if (o.find(
"address") != o.end() &&
isHex(o[
"address"].get_str()))
246 cwarn <<
"Account address is either not defined or not in hex format" << _file.string();
251 cwarn <<
"Invalid JSON in key file" << _file.string();
262 if (
auto k = key(_address))
269 k->second.encryptedKey =
encrypt(s.
ref(), _newPass, _kdf);
277 pair<h128 const, SecretStore::EncryptedKey>
const* SecretStore::key(
Address const& _address)
const
279 for (
auto const& k: m_keys)
280 if (k.second.address == _address)
285 pair<h128 const, SecretStore::EncryptedKey>* SecretStore::key(
Address const& _address)
287 for (
auto& k: m_keys)
288 if (k.second.address == _address)
298 m_cached.erase(_uuid);
299 m_keys[_uuid].encryptedKey =
encrypt(s.
ref(), _newPass, _kdf);
304 static bytesSec deriveNewKey(
string const& _pass,
KDF _kdf, js::mObject& o_ret)
307 unsigned iterations = 1 << 18;
313 o_ret[
"kdf"] =
"scrypt";
316 params[
"n"] = int64_t(iterations);
317 params[
"r"] = int(r);
318 params[
"p"] = int(p);
319 params[
"dklen"] = int(dklen);
320 params[
"salt"] =
toHex(salt);
321 o_ret[
"kdfparams"] = params;
323 return scrypt(_pass, salt, iterations, r, p, dklen);
327 o_ret[
"kdf"] =
"pbkdf2";
330 params[
"prf"] =
"hmac-sha256";
331 params[
"c"] = int(iterations);
332 params[
"salt"] =
toHex(salt);
333 params[
"dklen"] = int(dklen);
334 o_ret[
"kdfparams"] = params;
336 return pbkdf2(_pass, salt, iterations, dklen);
344 bytesSec derivedKey = deriveNewKey(_pass, _kdf, ret);
345 if (derivedKey.
empty())
346 BOOST_THROW_EXCEPTION(crypto::CryptoException() <<
errinfo_comment(
"Key derivation failed."));
348 ret[
"cipher"] =
"aes-128-ctr";
354 ret[
"cipherparams"] = params;
359 if (cipherText.empty())
360 BOOST_THROW_EXCEPTION(crypto::CryptoException() <<
errinfo_comment(
"Key encryption failed."));
361 ret[
"ciphertext"] =
toHex(cipherText);
364 h256 mac =
sha3(derivedKey.
ref().cropped(16, 16).toBytes() + cipherText);
367 return js::write_string(js::mValue(ret),
true);
375 js::read_string(_v, ov);
381 if (o[
"kdf"].get_str() ==
"pbkdf2")
383 auto params = o[
"kdfparams"].get_obj();
384 if (params[
"prf"].get_str() !=
"hmac-sha256")
386 cwarn <<
"Unknown PRF for PBKDF2" << params[
"prf"].get_str() <<
"not supported.";
389 unsigned iterations = params[
"c"].get_int();
391 derivedKey =
pbkdf2(_pass, salt, iterations, params[
"dklen"].get_int());
393 else if (o[
"kdf"].get_str() ==
"scrypt")
395 auto p = o[
"kdfparams"].get_obj();
396 derivedKey =
scrypt(_pass,
fromHex(p[
"salt"].get_str()), p[
"n"].get_int(), p[
"r"].get_int(), p[
"p"].get_int(), p[
"dklen"].get_int());
400 cwarn <<
"Unknown KDF" << o[
"kdf"].get_str() <<
"not supported.";
404 if (derivedKey.
size() < 32 && !(o.count(
"compat") && o[
"compat"].get_str() ==
"2"))
406 cwarn <<
"Derived key's length too short (<32 bytes)";
415 h256 mac(o[
"mac"].get_str());
417 if (o.count(
"compat") && o[
"compat"].get_str() ==
"2")
418 macExp =
sha3(derivedKey.
ref().cropped(derivedKey.
size() - 16).toBytes() + cipherText);
420 macExp =
sha3(derivedKey.
ref().cropped(16, 16).toBytes() + cipherText);
427 else if (o.count(
"sillymac"))
429 h256 mac(o[
"sillymac"].get_str());
430 h256 macExp =
sha3(
asBytes(o[
"sillymacjson"].get_str()) + derivedKey.
ref().cropped(derivedKey.
size() - 16).toBytes() + cipherText);
438 cwarn <<
"No MAC. Proceeding anyway.";
441 if (o[
"cipher"].get_str() ==
"aes-128-ctr")
443 auto params = o[
"cipherparams"].get_obj();
444 h128 iv(params[
"iv"].get_str());
445 if (o.count(
"compat") && o[
"compat"].get_str() ==
"2")
455 cwarn <<
"Unknown cipher" << o[
"cipher"].get_str() <<
"not supported.";