Skip to content

Commit cafe8b3

Browse files
committed
Merge branch 'develop' of https://github.com/ethereum/cpp-ethereum into core_perf
2 parents f52f868 + e0e21fd commit cafe8b3

28 files changed

Lines changed: 1195 additions & 466 deletions

exp/main.cpp

Lines changed: 198 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -65,40 +65,54 @@ namespace fs = boost::filesystem;
6565
#if 1
6666

6767
inline h128 fromUUID(std::string const& _uuid) { return h128(boost::replace_all_copy(_uuid, "-", "")); }
68+
inline std::string toUUID(h128 const& _uuid) { std::string ret = toHex(_uuid.ref()); for (unsigned i: {20, 16, 12, 8}) ret.insert(ret.begin() + i, '-'); return ret; }
6869

69-
class KeyManager: public Worker
70+
class KeyStore
7071
{
7172
public:
72-
KeyManager() { readKeys(); }
73-
~KeyManager() {}
73+
KeyStore() { readKeys(); }
74+
~KeyStore() {}
7475

75-
Secret secret(h128 const& _uuid, function<std::string()> const& _pass)
76+
bytes key(h128 const& _uuid, function<std::string()> const& _pass)
7677
{
77-
auto rit = m_ready.find(_uuid);
78-
if (rit != m_ready.end())
78+
auto rit = m_cached.find(_uuid);
79+
if (rit != m_cached.end())
7980
return rit->second;
8081
auto it = m_keys.find(_uuid);
8182
if (it == m_keys.end())
82-
return Secret();
83-
Secret ret(decrypt(it->second, _pass()));
84-
if (ret)
85-
m_ready[_uuid] = ret;
86-
return ret;
83+
return bytes();
84+
bytes key = decrypt(it->second, _pass());
85+
if (!key.empty())
86+
m_cached[_uuid] = key;
87+
return key;
8788
}
8889

89-
h128 create(std::string const& _pass)
90+
h128 import(bytes const& _s, std::string const& _pass)
9091
{
91-
auto s = Secret::random();
92-
h128 r(sha3(s));
93-
m_ready[r] = s;
94-
m_keys[r] = encrypt(s.asBytes(), _pass);
92+
h128 r = h128::random();
93+
m_cached[r] = _s;
94+
m_keys[r] = encrypt(_s, _pass);
95+
writeKeys();
9596
return r;
9697
}
9798

99+
// Clear any cached keys.
100+
void clearCache() const { m_cached.clear(); }
101+
98102
private:
99103
void writeKeys(std::string const& _keysPath = getDataDir("web3") + "/keys")
100104
{
101-
(void)_keysPath;
105+
fs::path p(_keysPath);
106+
boost::filesystem::create_directories(p);
107+
for (auto const& k: m_keys)
108+
{
109+
std::string uuid = toUUID(k.first);
110+
js::mObject v;
111+
v["crypto"] = k.second;
112+
v["id"] = uuid;
113+
v["version"] = 2;
114+
writeFile((p / uuid).string() + ".json", js::write_string(js::mValue(v), true));
115+
}
102116
}
103117

104118
void readKeys(std::string const& _keysPath = getDataDir("web3") + "/keys")
@@ -119,16 +133,49 @@ class KeyManager: public Worker
119133
else
120134
cwarn << "Cannot read key version" << version;
121135
}
122-
else
123-
cwarn << "Invalid JSON in key file" << it->path().string();
136+
// else
137+
// cwarn << "Invalid JSON in key file" << it->path().string();
124138
}
125139
}
126140

127141
static js::mValue encrypt(bytes const& _v, std::string const& _pass)
128142
{
129-
(void)_v;
130-
(void)_pass;
131-
return js::mValue();
143+
js::mObject ret;
144+
145+
// KDF info
146+
unsigned dklen = 16;
147+
unsigned iterations = 262144;
148+
bytes salt = h256::random().asBytes();
149+
ret["kdf"] = "pbkdf2";
150+
{
151+
js::mObject params;
152+
params["prf"] = "hmac-sha256";
153+
params["c"] = (int)iterations;
154+
params["salt"] = toHex(salt);
155+
params["dklen"] = (int)dklen;
156+
ret["kdfparams"] = params;
157+
}
158+
bytes derivedKey = pbkdf2(_pass, salt, iterations, dklen);
159+
160+
// cipher info
161+
ret["cipher"] = "aes-128-cbc";
162+
h128 key(sha3(h128(derivedKey, h128::AlignRight)), h128::AlignRight);
163+
h128 iv = h128::random();
164+
{
165+
js::mObject params;
166+
params["iv"] = toHex(iv.ref());
167+
ret["cipherparams"] = params;
168+
}
169+
170+
// cipher text
171+
bytes cipherText = encryptSymNoAuth(key, iv, &_v);
172+
ret["ciphertext"] = toHex(cipherText);
173+
174+
// and mac.
175+
h256 mac = sha3(bytesConstRef(&derivedKey).cropped(derivedKey.size() - 16).toBytes() + cipherText);
176+
ret["mac"] = toHex(mac.ref());
177+
178+
return ret;
132179
}
133180

134181
static bytes decrypt(js::mValue const& _v, std::string const& _pass)
@@ -167,32 +214,154 @@ class KeyManager: public Worker
167214
}
168215

169216
// decrypt
170-
bytes ret;
171217
if (o["cipher"].get_str() == "aes-128-cbc")
172218
{
173219
auto params = o["cipherparams"].get_obj();
174220
h128 key(sha3(h128(derivedKey, h128::AlignRight)), h128::AlignRight);
175221
h128 iv(params["iv"].get_str());
176-
decryptSymNoAuth(key, iv, &cipherText, ret);
222+
return decryptSymNoAuth(key, iv, &cipherText);
177223
}
178224
else
179225
{
180226
cwarn << "Unknown cipher" << o["cipher"].get_str() << "not supported.";
181227
return bytes();
182228
}
183-
184-
return ret;
185229
}
186230

187-
mutable std::map<h128, Secret> m_ready;
231+
mutable std::map<h128, bytes> m_cached;
188232
std::map<h128, js::mValue> m_keys;
189233
};
190234

235+
class UnknownPassword: public Exception {};
236+
237+
struct KeyInfo
238+
{
239+
h256 passHash;
240+
std::string name;
241+
};
242+
243+
static const auto DontKnowThrow = [](){ BOOST_THROW_EXCEPTION(UnknownPassword()); return std::string(); };
244+
245+
// This one is specifically for Ethereum, but we can make it generic in due course.
246+
// TODO: hidden-partition style key-store.
247+
class KeyManager
248+
{
249+
public:
250+
KeyManager() { m_cachedPasswords[sha3(m_password)] = m_password; }
251+
~KeyManager() {}
252+
253+
void load(std::string const& _pass, std::string const& _keysFile = getDataDir("ethereum") + "/keys.info")
254+
{
255+
try {
256+
bytes salt = contents(_keysFile + ".salt");
257+
bytes encKeys = contents(_keysFile);
258+
m_key = h128(pbkdf2(_pass, salt, 262144, 16));
259+
bytes bs = decryptSymNoAuth(m_key, h128(), &encKeys);
260+
RLP s(bs);
261+
unsigned version = (unsigned)s[0];
262+
if (version == 1)
263+
{
264+
for (auto const& i: s[1])
265+
m_keyInfo[m_addrLookup[(Address)i[0]] = (h128)i[1]] = KeyInfo{(h256)i[2], (std::string)i[3]};
266+
for (auto const& i: s[2])
267+
m_passwordInfo[(h256)i[0]] = (std::string)i[1];
268+
m_password = (string)s[3];
269+
}
270+
}
271+
catch (...) {}
272+
m_cachedPasswords[sha3(m_password)] = m_password;
273+
}
274+
275+
// Only use if previously loaded ok.
276+
// @returns false if wasn't previously loaded ok.
277+
bool save(std::string const& _keysFile = getDataDir("ethereum") + "/keys.info") { if (!m_key) return false; save(m_key, _keysFile); return true; }
278+
279+
void save(std::string const& _pass, std::string const& _keysFile = getDataDir("ethereum") + "/keys.info")
280+
{
281+
bytes salt = h256::random().asBytes();
282+
writeFile(_keysFile + ".salt", salt);
283+
auto key = h128(pbkdf2(_pass, salt, 262144, 16));
284+
save(key, _keysFile);
285+
}
286+
287+
void save(h128 const& _key, std::string const& _keysFile = getDataDir("ethereum") + "/keys.info")
288+
{
289+
RLPStream s(4);
290+
s << 1;
291+
s.appendList(m_addrLookup.size());
292+
for (auto const& i: m_addrLookup)
293+
s.appendList(4) << i.first << i.second << m_keyInfo[i.second].passHash << m_keyInfo[i.second].name;
294+
s.appendList(m_passwordInfo.size());
295+
for (auto const& i: m_passwordInfo)
296+
s.appendList(2) << i.first << i.second;
297+
s.append(m_password);
298+
299+
writeFile(_keysFile, encryptSymNoAuth(_key, h128(), &s.out()));
300+
m_key = _key;
301+
}
302+
303+
Secret secret(Address const& _address, function<std::string()> const& _pass = DontKnowThrow)
304+
{
305+
auto it = m_addrLookup.find(_address);
306+
if (it == m_addrLookup.end())
307+
return Secret();
308+
return secret(it->second, _pass);
309+
}
310+
311+
Secret secret(h128 const& _uuid, function<std::string()> const& _pass = DontKnowThrow)
312+
{
313+
return Secret(m_store.key(_uuid, [&](){
314+
auto it = m_cachedPasswords.find(m_keyInfo[_uuid].passHash);
315+
if (it == m_cachedPasswords.end())
316+
{
317+
std::string p = _pass();
318+
m_cachedPasswords[sha3(p)] = p;
319+
return p;
320+
}
321+
else
322+
return it->second;
323+
}));
324+
}
325+
326+
h128 import(Secret const& _s, std::string const& _pass, string const& _info = std::string(), string const& _passInfo = std::string())
327+
{
328+
Address addr = KeyPair(_s).address();
329+
auto passHash = sha3(_pass);
330+
m_cachedPasswords[passHash] = _pass;
331+
m_passwordInfo[passHash] = _passInfo;
332+
auto uuid = m_store.import(_s.asBytes(), _pass);
333+
m_keyInfo[uuid] = KeyInfo{passHash, _info};
334+
m_addrLookup[addr] = uuid;
335+
return uuid;
336+
}
337+
338+
h128 import(Secret const& _s, std::string const& _info = std::string())
339+
{
340+
// cache password, remember the key, remember the address
341+
return import(_s, m_password, _info, std::string());
342+
}
343+
344+
private:
345+
// Ethereum keys.
346+
std::map<Address, h128> m_addrLookup;
347+
std::map<h128, KeyInfo> m_keyInfo;
348+
std::map<h256, std::string> m_passwordInfo;
349+
350+
// Passwords that we're storing.
351+
std::map<h256, std::string> m_cachedPasswords;
352+
353+
// The default password for keys in the keystore - protected by the master password.
354+
std::string m_password = asString(h256::random().asBytes());
355+
356+
KeyStore m_store;
357+
h128 m_key;
358+
};
359+
191360
int main()
192361
{
193-
cdebug << toHex(pbkdf2("password", asBytes("salt"), 1, 20));
194362
KeyManager keyman;
195-
cdebug << "Secret key for 0498f19a-59db-4d54-ac95-33901b4f1870 is " << keyman.secret(fromUUID("0498f19a-59db-4d54-ac95-33901b4f1870"), [](){ return "foo"; });
363+
auto id = fromUUID("441193ae-a767-f1c3-48ba-dd6610db5ed0");
364+
cdebug << "Secret key for " << toUUID(id) << "is" << keyman.secret(id, [](){ return "bar"; });
196365
}
197366

198367
#elif 0

libdevcore/Common.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ using namespace dev;
2828
namespace dev
2929
{
3030

31-
char const* Version = "0.9.15o";
31+
char const* Version = "0.9.16";
3232

3333
void HasInvariants::checkInvariants() const
3434
{

libdevcore/FixedHash.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ class FixedHash
113113
/// @returns an abridged version of the hash as a user-readable hex string.
114114
std::string abridged() const { return toHex(ref().cropped(0, 4)) + "\342\200\246"; }
115115

116+
/// @returns an abridged version of the hash as a user-readable hex string.
117+
std::string hex() const { return toHex(ref()); }
118+
116119
/// @returns a mutable byte vector_ref to the object's data.
117120
bytesRef ref() { return bytesRef(m_data.data(), N); }
118121

libdevcrypto/Common.cpp

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -112,51 +112,47 @@ bool dev::decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plain)
112112
return decrypt(_k, _cipher, o_plain);
113113
}
114114

115-
h128 dev::encryptSymNoAuth(h128 const& _k, bytesConstRef _plain, bytes& o_cipher)
115+
std::pair<bytes, h128> dev::encryptSymNoAuth(h128 const& _k, bytesConstRef _plain)
116116
{
117117
h128 iv(Nonce::get());
118-
return encryptSymNoAuth(_k, _plain, o_cipher, iv);
118+
return make_pair(encryptSymNoAuth(_k, iv, _plain), iv);
119119
}
120120

121-
h128 dev::encryptSymNoAuth(h128 const& _k, bytesConstRef _plain, bytes& o_cipher, h128 const& _iv)
121+
bytes dev::encryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _plain)
122122
{
123-
o_cipher.resize(_plain.size());
124-
125123
const int c_aesKeyLen = 16;
126124
SecByteBlock key(_k.data(), c_aesKeyLen);
127125
try
128126
{
129127
CTR_Mode<AES>::Encryption e;
130128
e.SetKeyWithIV(key, key.size(), _iv.data());
131-
e.ProcessData(o_cipher.data(), _plain.data(), _plain.size());
132-
return _iv;
129+
bytes ret(_plain.size());
130+
e.ProcessData(ret.data(), _plain.data(), _plain.size());
131+
return ret;
133132
}
134133
catch (CryptoPP::Exception& _e)
135134
{
136135
cerr << _e.what() << endl;
137-
o_cipher.resize(0);
138-
return h128();
136+
return bytes();
139137
}
140138
}
141139

142-
bool dev::decryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _cipher, bytes& o_plaintext)
140+
bytes dev::decryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _cipher)
143141
{
144-
o_plaintext.resize(_cipher.size());
145-
146142
const size_t c_aesKeyLen = 16;
147143
SecByteBlock key(_k.data(), c_aesKeyLen);
148144
try
149145
{
150146
CTR_Mode<AES>::Decryption d;
151147
d.SetKeyWithIV(key, key.size(), _iv.data());
152-
d.ProcessData(o_plaintext.data(), _cipher.data(), _cipher.size());
153-
return true;
148+
bytes ret(_cipher.size());
149+
d.ProcessData(ret.data(), _cipher.data(), _cipher.size());
150+
return ret;
154151
}
155152
catch (CryptoPP::Exception& _e)
156153
{
157154
cerr << _e.what() << endl;
158-
o_plaintext.resize(0);
159-
return false;
155+
return bytes();
160156
}
161157
}
162158

libdevcrypto/Common.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,13 @@ void encryptECIES(Public const& _k, bytesConstRef _plain, bytes& o_cipher);
103103
bool decryptECIES(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext);
104104

105105
/// Encrypts payload with random IV/ctr using AES128-CTR.
106-
h128 encryptSymNoAuth(h128 const& _k, bytesConstRef _plain, bytes& o_cipher);
106+
std::pair<bytes, h128> encryptSymNoAuth(h128 const& _k, bytesConstRef _plain);
107107

108108
/// Encrypts payload with specified IV/ctr using AES128-CTR.
109-
h128 encryptSymNoAuth(h128 const& _k, bytesConstRef _plain, bytes& o_cipher, h128 const& _iv);
110-
109+
bytes encryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _plain);
110+
111111
/// Decrypts payload with specified IV/ctr using AES128-CTR.
112-
bool decryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _cipher, bytes& o_plaintext);
112+
bytes decryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _cipher);
113113

114114
/// Recovers Public key from signed message hash.
115115
Public recover(Signature const& _sig, h256 const& _hash);

0 commit comments

Comments
 (0)