forked from moonpower/azahar-UWP
Encrypt downloaded apps with a console unique key
This commit is contained in:
@@ -187,7 +187,7 @@ endif()
|
||||
create_target_directory_groups(citra_common)
|
||||
|
||||
target_link_libraries(citra_common PUBLIC fmt library-headers microprofile Boost::boost Boost::serialization Boost::iostreams)
|
||||
target_link_libraries(citra_common PRIVATE zstd)
|
||||
target_link_libraries(citra_common PRIVATE cryptopp zstd)
|
||||
|
||||
if ("x86_64" IN_LIST ARCHITECTURE)
|
||||
target_link_libraries(citra_common PRIVATE xbyak)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright Citra Emulator Project / Lime3DS Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -13,7 +13,10 @@
|
||||
#include <unordered_map>
|
||||
#include <boost/iostreams/device/file_descriptor.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
#include <cryptopp/aes.h>
|
||||
#include <cryptopp/modes.h>
|
||||
#include <fmt/format.h>
|
||||
#include "common/archives.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_paths.h"
|
||||
@@ -1136,7 +1139,7 @@ u64 IOFile::GetSize() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool IOFile::Seek(s64 off, int origin) {
|
||||
bool IOFile::SeekImpl(s64 off, int origin) {
|
||||
if (!IsOpen() || 0 != fseeko(m_file, off, origin))
|
||||
m_good = false;
|
||||
|
||||
@@ -1240,6 +1243,107 @@ bool IOFile::Resize(u64 size) {
|
||||
return m_good;
|
||||
}
|
||||
|
||||
struct CryptoIOFileImpl {
|
||||
|
||||
std::vector<u8> key;
|
||||
std::vector<u8> iv;
|
||||
|
||||
CryptoPP::CTR_Mode<CryptoPP::AES>::Decryption d;
|
||||
CryptoPP::CTR_Mode<CryptoPP::AES>::Encryption e;
|
||||
|
||||
std::vector<u8> write_buffer;
|
||||
|
||||
std::size_t ReadImpl(CryptoIOFile& f, void* data, std::size_t length, std::size_t data_size) {
|
||||
std::size_t res = f.IOFile::ReadImpl(data, length, data_size);
|
||||
if (res != std::numeric_limits<std::size_t>::max() && res != 0) {
|
||||
d.ProcessData(reinterpret_cast<CryptoPP::byte*>(data),
|
||||
reinterpret_cast<CryptoPP::byte*>(data), length * data_size);
|
||||
e.Seek(f.IOFile::Tell());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::size_t ReadAtImpl(CryptoIOFile& f, void* data, std::size_t length, std::size_t data_size,
|
||||
std::size_t offset) {
|
||||
std::size_t res = f.IOFile::ReadAtImpl(data, length, data_size, offset);
|
||||
if (res != std::numeric_limits<std::size_t>::max() && res != 0) {
|
||||
d.Seek(offset);
|
||||
d.ProcessData(reinterpret_cast<CryptoPP::byte*>(data),
|
||||
reinterpret_cast<CryptoPP::byte*>(data), length * data_size);
|
||||
e.Seek(f.IOFile::Tell());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::size_t WriteImpl(CryptoIOFile& f, const void* data, std::size_t length,
|
||||
std::size_t data_size) {
|
||||
if (write_buffer.size() < length * data_size) {
|
||||
write_buffer.resize(length * data_size);
|
||||
}
|
||||
e.ProcessData(write_buffer.data(), reinterpret_cast<const CryptoPP::byte*>(data),
|
||||
length * data_size);
|
||||
std::size_t res = f.IOFile::WriteImpl(write_buffer.data(), length, data_size);
|
||||
if (res != std::numeric_limits<std::size_t>::max() && res != 0) {
|
||||
d.Seek(f.IOFile::Tell());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool SeekImpl(CryptoIOFile& f, s64 off, int origin) {
|
||||
bool res = f.IOFile::SeekImpl(off, origin);
|
||||
if (res) {
|
||||
u64 pos = f.IOFile::Tell();
|
||||
d.Seek(pos);
|
||||
e.Seek(pos);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
CryptoIOFile::CryptoIOFile() : IOFile() {
|
||||
impl = std::make_unique<CryptoIOFileImpl>();
|
||||
}
|
||||
|
||||
CryptoIOFile::CryptoIOFile(const std::string& filename, const char openmode[],
|
||||
const std::vector<u8>& aes_key, const std::vector<u8>& aes_iv, int flags)
|
||||
: IOFile(filename, openmode, flags) {
|
||||
impl = std::make_unique<CryptoIOFileImpl>();
|
||||
impl->key = aes_key;
|
||||
impl->iv = aes_iv;
|
||||
impl->d.SetKeyWithIV(aes_key.data(), aes_key.size(), aes_iv.data());
|
||||
impl->e.SetKeyWithIV(aes_key.data(), aes_key.size(), aes_iv.data());
|
||||
}
|
||||
|
||||
CryptoIOFile::~CryptoIOFile() {}
|
||||
|
||||
std::size_t CryptoIOFile::ReadImpl(void* data, std::size_t length, std::size_t data_size) {
|
||||
return impl->ReadImpl(*this, data, length, data_size);
|
||||
}
|
||||
|
||||
std::size_t CryptoIOFile::ReadAtImpl(void* data, std::size_t length, std::size_t data_size,
|
||||
std::size_t offset) {
|
||||
return impl->ReadAtImpl(*this, data, length, data_size, offset);
|
||||
}
|
||||
|
||||
std::size_t CryptoIOFile::WriteImpl(const void* data, std::size_t length, std::size_t data_size) {
|
||||
return impl->WriteImpl(*this, data, length, data_size);
|
||||
}
|
||||
|
||||
bool CryptoIOFile::SeekImpl(s64 off, int origin) {
|
||||
return impl->SeekImpl(*this, off, origin);
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void CryptoIOFile::serialize(Archive& ar, const unsigned int) {
|
||||
ar & impl->key;
|
||||
ar & impl->iv;
|
||||
if (Archive::is_loading::value) {
|
||||
impl->e.SetKeyWithIV(impl->key.data(), impl->key.size(), impl->iv.data());
|
||||
impl->d.SetKeyWithIV(impl->key.data(), impl->key.size(), impl->iv.data());
|
||||
}
|
||||
ar& boost::serialization::base_object<IOFile>(*this);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using boost_iostreams = boost::iostreams::stream<T>;
|
||||
|
||||
@@ -1271,3 +1375,6 @@ void OpenFStream<std::ios_base::out>(
|
||||
fstream.open(file_descriptor_sink);
|
||||
}
|
||||
} // namespace FileUtil
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileUtil::IOFile)
|
||||
SERIALIZE_EXPORT_IMPL(FileUtil::CryptoIOFile)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright Citra Emulator Project / Lime3DS Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -12,14 +12,18 @@
|
||||
#include <functional>
|
||||
#include <ios>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <boost/serialization/base_object.hpp>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/split_member.hpp>
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include <boost/serialization/wrapper.hpp>
|
||||
#include "common/common_types.h"
|
||||
#ifdef _MSC_VER
|
||||
@@ -267,6 +271,8 @@ enum class DirectorySeparator {
|
||||
std::string_view path,
|
||||
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
|
||||
|
||||
struct CryptoIOFileImpl;
|
||||
|
||||
// simple wrapper for cstdlib file functions to
|
||||
// hopefully will make error checking easier
|
||||
// and make forgetting an fclose() harder
|
||||
@@ -279,7 +285,7 @@ public:
|
||||
// isn't considered "locked" while citra is open and people can open the log file and view it
|
||||
IOFile(const std::string& filename, const char openmode[], int flags = 0);
|
||||
|
||||
~IOFile();
|
||||
virtual ~IOFile();
|
||||
|
||||
IOFile(IOFile&& other) noexcept;
|
||||
IOFile& operator=(IOFile&& other) noexcept;
|
||||
@@ -369,14 +375,10 @@ public:
|
||||
* @returns Count of T data successfully read.
|
||||
*/
|
||||
template <typename T>
|
||||
[[nodiscard]] size_t ReadSpan(std::span<T> data) const {
|
||||
[[nodiscard]] size_t ReadSpan(std::span<T> data) {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
|
||||
if (!IsOpen()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return std::fread(data.data(), sizeof(T), data.size(), m_file);
|
||||
return ReadImpl(data.data(), data.size(), sizeof(T));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -395,14 +397,10 @@ public:
|
||||
* @returns Count of T data successfully written.
|
||||
*/
|
||||
template <typename T>
|
||||
[[nodiscard]] size_t WriteSpan(std::span<const T> data) const {
|
||||
[[nodiscard]] size_t WriteSpan(std::span<const T> data) {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
|
||||
if (!IsOpen()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return std::fwrite(data.data(), sizeof(T), data.size(), m_file);
|
||||
return WriteImpl(data.data(), data.size(), sizeof(T));
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsOpen() const {
|
||||
@@ -426,7 +424,9 @@ public:
|
||||
return IsGood();
|
||||
}
|
||||
|
||||
bool Seek(s64 off, int origin);
|
||||
bool Seek(s64 off, int origin) {
|
||||
return SeekImpl(off, origin);
|
||||
}
|
||||
[[nodiscard]] u64 Tell() const;
|
||||
[[nodiscard]] u64 GetSize() const;
|
||||
bool Resize(u64 size);
|
||||
@@ -438,12 +438,24 @@ public:
|
||||
std::clearerr(m_file);
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size);
|
||||
std::size_t ReadAtImpl(void* data, std::size_t length, std::size_t data_size,
|
||||
std::size_t offset);
|
||||
std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size);
|
||||
virtual bool IsCrypto() {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string& Filename() const {
|
||||
return filename;
|
||||
}
|
||||
|
||||
protected:
|
||||
friend struct CryptoIOFileImpl;
|
||||
virtual std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size);
|
||||
virtual std::size_t ReadAtImpl(void* data, std::size_t length, std::size_t data_size,
|
||||
std::size_t offset);
|
||||
virtual std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size);
|
||||
|
||||
virtual bool SeekImpl(s64 off, int origin);
|
||||
|
||||
private:
|
||||
bool Open();
|
||||
|
||||
std::FILE* m_file = nullptr;
|
||||
@@ -472,6 +484,37 @@ private:
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
class CryptoIOFile : public IOFile {
|
||||
public:
|
||||
CryptoIOFile();
|
||||
|
||||
// flags is used for windows specific file open mode flags, which
|
||||
// allows citra to open the logs in shared write mode, so that the file
|
||||
// isn't considered "locked" while citra is open and people can open the log file and view it
|
||||
CryptoIOFile(const std::string& filename, const char openmode[], const std::vector<u8>& aes_key,
|
||||
const std::vector<u8>& aes_iv, int flags = 0);
|
||||
|
||||
bool IsCrypto() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
~CryptoIOFile() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<CryptoIOFileImpl> impl;
|
||||
|
||||
std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size) override;
|
||||
std::size_t ReadAtImpl(void* data, std::size_t length, std::size_t data_size,
|
||||
std::size_t offset) override;
|
||||
std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size) override;
|
||||
|
||||
bool SeekImpl(s64 off, int origin) override;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int);
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
template <std::ios_base::openmode o, typename T>
|
||||
void OpenFStream(T& fstream, const std::string& filename);
|
||||
} // namespace FileUtil
|
||||
@@ -485,3 +528,6 @@ void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmod
|
||||
fstream.open(filename, openmode);
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(FileUtil::IOFile)
|
||||
BOOST_CLASS_EXPORT_KEY(FileUtil::CryptoIOFile)
|
||||
Reference in New Issue
Block a user