Browse Source

Safe file writing and some tests.

cl-refactor
chriseth 10 years ago
parent
commit
af93ec370d
  1. 24
      libdevcore/CommonIO.cpp
  2. 8
      libdevcore/CommonIO.h
  3. 6
      libdevcrypto/SecretStore.cpp
  4. 132
      test/libdevcrypto/SecretStore.cpp

24
libdevcore/CommonIO.cpp

@ -23,13 +23,14 @@
#include <iostream>
#include <cstdlib>
#include <fstream>
#include "Exceptions.h"
#include <stdio.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <termios.h>
#endif
#include <boost/filesystem.hpp>
#include "Exceptions.h"
using namespace std;
using namespace dev;
@ -95,12 +96,23 @@ string dev::contentsString(string const& _file)
return contentsGeneric<string>(_file);
}
void dev::writeFile(std::string const& _file, bytesConstRef _data)
void dev::writeFile(std::string const& _file, bytesConstRef _data, bool _writeDeleteRename)
{
ofstream s(_file, ios::trunc | ios::binary);
s.write(reinterpret_cast<char const*>(_data.data()), _data.size());
if (!s)
BOOST_THROW_EXCEPTION(FileError());
if (_writeDeleteRename)
{
namespace fs = boost::filesystem;
fs::path tempPath = fs::unique_path(_file + "-%%%%%%");
writeFile(tempPath.string(), _data, false);
// will delete _file if it exists
fs::rename(tempPath, _file);
}
else
{
ofstream s(_file, ios::trunc | ios::binary);
s.write(reinterpret_cast<char const*>(_data.data()), _data.size());
if (!s)
BOOST_THROW_EXCEPTION(FileError());
}
}
std::string dev::getPassword(std::string const& _prompt)

8
libdevcore/CommonIO.h

@ -57,10 +57,12 @@ bytesRef contentsNew(std::string const& _file, bytesRef _dest = bytesRef());
/// Write the given binary data into the given file, replacing the file if it pre-exists.
/// Throws exception on error.
void writeFile(std::string const& _file, bytesConstRef _data);
/// @param _writeDeleteRename useful not to lose any data: If set, first writes to another file in
/// the same directory and then moves that file.
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.
inline void writeFile(std::string const& _file, bytes const& _data) { writeFile(_file, bytesConstRef(&_data)); }
inline void writeFile(std::string const& _file, std::string const& _data) { writeFile(_file, bytesConstRef(_data)); }
inline void writeFile(std::string const& _file, bytes const& _data, bool _writeDeleteRename = false) { writeFile(_file, bytesConstRef(&_data), _writeDeleteRename); }
inline void writeFile(std::string const& _file, std::string const& _data, bool _writeDeleteRename = false) { writeFile(_file, bytesConstRef(_data), _writeDeleteRename); }
/// Nicely renders the given bytes to a string, optionally as HTML.
/// @a _bytes: bytes array to be rendered as string. @a _width of a bytes line.

6
libdevcrypto/SecretStore.cpp

@ -88,8 +88,7 @@ static js::mValue upgraded(string const& _s)
SecretStore::SecretStore(string const& _path): m_path(_path)
{
if (!m_path.empty())
load();
load();
}
bytes SecretStore::secret(h128 const& _uuid, function<string()> const& _pass, bool _useCache) const
@ -136,9 +135,6 @@ void SecretStore::clearCache() const
void SecretStore::save(string const& _keysPath)
{
if (_keysPath.empty())
return;
fs::path p(_keysPath);
fs::create_directories(p);
for (auto& k: m_keys)

132
test/libdevcrypto/SecretStore.cpp

@ -36,6 +36,7 @@ using namespace dev;
using namespace dev::test;
namespace js = json_spirit;
namespace fs = boost::filesystem;
BOOST_GLOBAL_FIXTURE( MoveNonceToTempDir );
@ -56,7 +57,8 @@ BOOST_AUTO_TEST_CASE(basic_tests)
{
cnote << i.first;
js::mObject& o = i.second.get_obj();
SecretStore store("");
TransientDirectory tmpDir;
SecretStore store(tmpDir.path());
h128 u = store.readKeyContent(js::write_string(o["json"], false));
cdebug << "read uuid" << u;
bytes s = store.secret(u, [&](){ return o["password"].get_str(); });
@ -65,4 +67,132 @@ BOOST_AUTO_TEST_CASE(basic_tests)
}
}
BOOST_AUTO_TEST_CASE(import_key_from_file)
{
// Imports a key from an external file. Tests that the imported key is there
// and that the external file is not deleted.
TransientDirectory importDir;
string importFile = importDir.path() + "/import.json";
TransientDirectory storeDir;
string keyData = R"({
"version": 3,
"crypto": {
"ciphertext": "d69313b6470ac1942f75d72ebf8818a0d484ac78478a132ee081cd954d6bd7a9",
"cipherparams": { "iv": "ffffffffffffffffffffffffffffffff" },
"kdf": "pbkdf2",
"kdfparams": { "dklen": 32, "c": 262144, "prf": "hmac-sha256", "salt": "c82ef14476014cbf438081a42709e2ed" },
"mac": "cf6bfbcc77142a22c4a908784b4a16f1023a1d0e2aff404c20158fa4f1587177",
"cipher": "aes-128-ctr",
"version": 1
},
"id": "abb67040-8dbe-0dad-fc39-2b082ef0ee5f"
})";
string password = "bar";
string priv = "0202020202020202020202020202020202020202020202020202020202020202";
writeFile(importFile, keyData);
h128 uuid;
{
SecretStore store(storeDir.path());
BOOST_CHECK_EQUAL(store.keys().size(), 0);
uuid = store.importKey(importFile);
BOOST_CHECK(!!uuid);
BOOST_CHECK(contentsString(importFile) == keyData);
BOOST_CHECK_EQUAL(priv, toHex(store.secret(uuid, [&](){ return password; })));
BOOST_CHECK_EQUAL(store.keys().size(), 1);
}
fs::remove(importFile);
// now do it again to check whether SecretStore properly stored it on disk
{
SecretStore store(storeDir.path());
BOOST_CHECK_EQUAL(store.keys().size(), 1);
BOOST_CHECK_EQUAL(priv, toHex(store.secret(uuid, [&](){ return password; })));
}
}
BOOST_AUTO_TEST_CASE(import_secret)
{
for (string const& password: {"foobar", ""})
{
TransientDirectory storeDir;
string priv = "0202020202020202020202020202020202020202020202020202020202020202";
h128 uuid;
{
SecretStore store(storeDir.path());
BOOST_CHECK_EQUAL(store.keys().size(), 0);
uuid = store.importSecret(fromHex(priv), password);
BOOST_CHECK(!!uuid);
BOOST_CHECK_EQUAL(priv, toHex(store.secret(uuid, [&](){ return password; })));
BOOST_CHECK_EQUAL(store.keys().size(), 1);
}
{
SecretStore store(storeDir.path());
BOOST_CHECK_EQUAL(store.keys().size(), 1);
BOOST_CHECK_EQUAL(priv, toHex(store.secret(uuid, [&](){ return password; })));
}
}
}
BOOST_AUTO_TEST_CASE(wrong_password)
{
TransientDirectory storeDir;
SecretStore store(storeDir.path());
string password = "foobar";
string priv = "0202020202020202020202020202020202020202020202020202020202020202";
h128 uuid;
{
SecretStore store(storeDir.path());
BOOST_CHECK_EQUAL(store.keys().size(), 0);
uuid = store.importSecret(fromHex(priv), password);
BOOST_CHECK(!!uuid);
BOOST_CHECK_EQUAL(priv, toHex(store.secret(uuid, [&](){ return password; })));
BOOST_CHECK_EQUAL(store.keys().size(), 1);
// password will not be queried
BOOST_CHECK_EQUAL(priv, toHex(store.secret(uuid, [&](){ return "abcdefg"; })));
}
{
SecretStore store(storeDir.path());
BOOST_CHECK_EQUAL(store.keys().size(), 1);
BOOST_CHECK(store.secret(uuid, [&](){ return "abcdefg"; }).empty());
}
}
BOOST_AUTO_TEST_CASE(recode)
{
TransientDirectory storeDir;
SecretStore store(storeDir.path());
string password = "foobar";
string changedPassword = "abcdefg";
string priv = "0202020202020202020202020202020202020202020202020202020202020202";
h128 uuid;
{
SecretStore store(storeDir.path());
BOOST_CHECK_EQUAL(store.keys().size(), 0);
uuid = store.importSecret(fromHex(priv), password);
BOOST_CHECK(!!uuid);
BOOST_CHECK_EQUAL(priv, toHex(store.secret(uuid, [&](){ return password; })));
BOOST_CHECK_EQUAL(store.keys().size(), 1);
}
{
SecretStore store(storeDir.path());
BOOST_CHECK_EQUAL(store.keys().size(), 1);
BOOST_CHECK(store.secret(uuid, [&](){ return "abcdefg"; }).empty());
BOOST_CHECK(store.recode(uuid, changedPassword, [&](){ return password; }));
BOOST_CHECK_EQUAL(store.keys().size(), 1);
BOOST_CHECK_EQUAL(priv, toHex(store.secret(uuid, [&](){ return changedPassword; })));
store.clearCache();
BOOST_CHECK(store.secret(uuid, [&](){ return password; }).empty());
BOOST_CHECK_EQUAL(priv, toHex(store.secret(uuid, [&](){ return changedPassword; })));
}
{
SecretStore store(storeDir.path());
BOOST_CHECK_EQUAL(store.keys().size(), 1);
BOOST_CHECK(store.secret(uuid, [&](){ return password; }).empty());
BOOST_CHECK_EQUAL(priv, toHex(store.secret(uuid, [&](){ return changedPassword; })));
}
}
BOOST_AUTO_TEST_SUITE_END()

Loading…
Cancel
Save