Skip to content

Commit

Permalink
Merge pull request UkoeHB#14 from ghostway0/ghostway/encrypted_file
Browse files Browse the repository at this point in the history
[seraphis_wallet]: encrypted file
  • Loading branch information
rbrunner7 authored Dec 10, 2023
2 parents 94864e8 + 5378c45 commit e2ca1aa
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 1 deletion.
119 changes: 119 additions & 0 deletions src/seraphis_wallet/encrypted_file.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#pragma once

#include "crypto/chacha.h"
#include "crypto/crypto.h"
#include "file_io_utils.h"
#include "serialization/binary_archive.h"
#include "serialization/containers.h"
#include "serialization/crypto.h"
#include "serialization/serialization.h"
#include "serialization/string.h"
#include "storages/portable_storage_template_helper.h"
#include "string_coding.h"

#include <type_traits>

struct EncryptedFile
{
std::string encrypted_data;
crypto::chacha_iv iv;

BEGIN_SERIALIZE_OBJECT()
VERSION_FIELD(0)
FIELD(encrypted_data)
FIELD(iv)
END_SERIALIZE()
};

template <class T> bool read_encrypted_file(std::string path, const crypto::chacha_key &key, T &struct_out)
{
std::string buf;
if (!epee::file_io_utils::load_file_to_string(path, buf))
return false;

EncryptedFile file;

binary_archive<false> file_ar{epee::strspan<std::uint8_t>(buf)};
if (!::serialization::serialize(file_ar, file))
return false;

std::string decrypted_data;
decrypted_data.resize(file.encrypted_data.size());
crypto::chacha20(file.encrypted_data.data(), file.encrypted_data.size(), key, file.iv, &decrypted_data[0]);

binary_archive<false> ar{epee::strspan<std::uint8_t>(decrypted_data)};

if (!::serialization::serialize(ar, struct_out))
return false;

return true;
}

// NOTE: if this were c++20, Concepts and `require` could be used to make this one function
template <class T> bool read_encrypted_file_json(std::string path, const crypto::chacha_key &key, T &struct_out)
{
std::string buf;
if (!epee::file_io_utils::load_file_to_string(path, buf))
return false;

EncryptedFile file;

binary_archive<false> file_ar{epee::strspan<std::uint8_t>(buf)};
if (!::serialization::serialize(file_ar, file))
return false;

std::string decrypted_data;
decrypted_data.resize(file.encrypted_data.size());
crypto::chacha20(file.encrypted_data.data(), file.encrypted_data.size(), key, file.iv, &decrypted_data[0]);

return epee::serialization::load_t_from_json(struct_out, decrypted_data);
}

template <class T> bool write_encrypted_file(std::string path, const crypto::chacha_key &key, T &struct_in)
{
std::stringstream data_oss;
binary_archive<true> data_ar(data_oss);
if (!::serialization::serialize(data_ar, struct_in))
return false;

std::string buf = data_oss.str();

EncryptedFile file = {};
file.iv = crypto::rand<crypto::chacha_iv>();

std::string encrypted_data;
encrypted_data.resize(buf.size());

crypto::chacha20(std::move(buf.data()), buf.size(), key, file.iv, &encrypted_data[0]);

file.encrypted_data = encrypted_data;

std::stringstream file_oss;
binary_archive<true> file_ar(file_oss);
if (!::serialization::serialize(file_ar, file))
return false;

return epee::file_io_utils::save_string_to_file(path, file_oss.str());
}

template <class T> bool write_encrypted_file_json(std::string path, const crypto::chacha_key &key, T &struct_in)
{
std::string struct_json = epee::serialization::store_t_to_json(struct_in);

EncryptedFile file = {};
file.iv = crypto::rand<crypto::chacha_iv>();

std::string encrypted_data;
encrypted_data.resize(struct_json.size());

crypto::chacha20(std::move(struct_json.data()), struct_json.size(), key, file.iv, &encrypted_data[0]);

file.encrypted_data = encrypted_data;

std::stringstream file_oss;
binary_archive<true> file_ar(file_oss);
if (!::serialization::serialize(file_ar, file))
return false;

return epee::file_io_utils::save_string_to_file(path, file_oss.str());
}
3 changes: 2 additions & 1 deletion tests/unit_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ set(unit_tests_sources
aligned.cpp
rpc_version_str.cpp
x25519.cpp
zmq_rpc.cpp)
zmq_rpc.cpp
encrypted_file.cpp)

set(unit_tests_headers
unit_tests_utils.h)
Expand Down
55 changes: 55 additions & 0 deletions tests/unit_tests/encrypted_file.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include <boost/filesystem.hpp>
#include <boost/filesystem/operations.hpp>
#include <gtest/gtest.h>

#include "crypto/chacha.h"
#include "seraphis_wallet/encrypted_file.h"
#include "serialization/serialization.h"
#include "serialization/keyvalue_serialization.h"

struct test_s
{
std::string data;

BEGIN_SERIALIZE()
FIELD(data)
END_SERIALIZE()

BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(data)
END_KV_SERIALIZE_MAP()
};

TEST(EncryptedFile, ReadWriteBlob)
{
boost::filesystem::path temp_file = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
const std::string tmp_path = temp_file.native();

test_s test{.data = "monero is awesome"};

crypto::chacha_key key;
crypto::generate_chacha_key("monero is double awesome", key, 1);

ASSERT_TRUE(write_encrypted_file(tmp_path, key, test));

ASSERT_TRUE(read_encrypted_file(tmp_path, key, test));

ASSERT_TRUE(test.data == "monero is awesome");
}

TEST(EncryptedFile, ReadWriteJson)
{
boost::filesystem::path temp_file = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
const std::string tmp_path = temp_file.native();

test_s test{.data = "monero is awesome!"};

crypto::chacha_key key;
crypto::generate_chacha_key("monero is double awesome", key, 1);

ASSERT_TRUE(write_encrypted_file_json(tmp_path, key, test));

ASSERT_TRUE(read_encrypted_file_json(tmp_path, key, test));

ASSERT_TRUE(test.data == "monero is awesome!");
}

0 comments on commit e2ca1aa

Please sign in to comment.