mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-12-05 10:12:48 +01:00
Merge bitcoin/bitcoin#28052: blockstorage: XOR blocksdir *.dat files
fa895c7283mingw: Document mode wbx workaround (MarcoFalke)fa359255feAdd -blocksxor boolean option (MarcoFalke)fa7f7ac040Return XOR AutoFile from BlockManager::Open*File() (MarcoFalke) Pull request description: Currently the *.dat files in the blocksdir store the data received from remote peers as-is. This may be problematic when a program other than Bitcoin Core tries to interpret them by accident. For example, an anti-virus program or other program may scan them and move them into quarantine, or delete them, or corrupt them. This may cause Bitcoin Core to fail a reorg, or fail to reply to block requests (via P2P, RPC, REST, ...). Fix this, similar to https://github.com/bitcoin/bitcoin/pull/6650, by rolling a random XOR pattern over the dat files when writing or reading them. Obviously this can only protect against programs that accidentally and unintentionally are trying to mess with the dat files. Any program that intentionally wants to mess with the dat files can still trivially do so. The XOR pattern is only applied when the blocksdir is freshly created, and there is an option to disable it (on creation), so that people can disable it, if needed. ACKs for top commit: achow101: ACKfa895c7283TheCharlatan: Re-ACKfa895c7283hodlinator: ACKfa895c7283Tree-SHA512: c92a6a717da83bc33a9b8671a779eeefde2c63b192362ba1d71e6535ee31d08e2802b74acc908345197de9daac6930e4771595ee25b09acd5a67f7ea34854720
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
namespace node {
|
||||
util::Result<void> ApplyArgsManOptions(const ArgsManager& args, BlockManager::Options& opts)
|
||||
{
|
||||
if (auto value{args.GetBoolArg("-blocksxor")}) opts.use_xor = *value;
|
||||
// block pruning; get the amount of disk space (in MiB) to allot for block & undo files
|
||||
int64_t nPruneArg{args.GetIntArg("-prune", opts.prune_target)};
|
||||
if (nPruneArg < 0) {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <pow.h>
|
||||
#include <primitives/block.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <random.h>
|
||||
#include <reverse_iterator.h>
|
||||
#include <serialize.h>
|
||||
#include <signet.h>
|
||||
@@ -818,13 +819,13 @@ void BlockManager::UnlinkPrunedFiles(const std::set<int>& setFilesToPrune) const
|
||||
|
||||
AutoFile BlockManager::OpenBlockFile(const FlatFilePos& pos, bool fReadOnly) const
|
||||
{
|
||||
return AutoFile{m_block_file_seq.Open(pos, fReadOnly)};
|
||||
return AutoFile{m_block_file_seq.Open(pos, fReadOnly), m_xor_key};
|
||||
}
|
||||
|
||||
/** Open an undo file (rev?????.dat) */
|
||||
AutoFile BlockManager::OpenUndoFile(const FlatFilePos& pos, bool fReadOnly) const
|
||||
{
|
||||
return AutoFile{m_undo_file_seq.Open(pos, fReadOnly)};
|
||||
return AutoFile{m_undo_file_seq.Open(pos, fReadOnly), m_xor_key};
|
||||
}
|
||||
|
||||
fs::path BlockManager::GetBlockPosFilename(const FlatFilePos& pos) const
|
||||
@@ -1144,6 +1145,54 @@ FlatFilePos BlockManager::SaveBlockToDisk(const CBlock& block, int nHeight)
|
||||
return blockPos;
|
||||
}
|
||||
|
||||
static auto InitBlocksdirXorKey(const BlockManager::Options& opts)
|
||||
{
|
||||
// Bytes are serialized without length indicator, so this is also the exact
|
||||
// size of the XOR-key file.
|
||||
std::array<std::byte, 8> xor_key{};
|
||||
|
||||
if (opts.use_xor && fs::is_empty(opts.blocks_dir)) {
|
||||
// Only use random fresh key when the boolean option is set and on the
|
||||
// very first start of the program.
|
||||
FastRandomContext{}.fillrand(xor_key);
|
||||
}
|
||||
|
||||
const fs::path xor_key_path{opts.blocks_dir / "xor.dat"};
|
||||
if (fs::exists(xor_key_path)) {
|
||||
// A pre-existing xor key file has priority.
|
||||
AutoFile xor_key_file{fsbridge::fopen(xor_key_path, "rb")};
|
||||
xor_key_file >> xor_key;
|
||||
} else {
|
||||
// Create initial or missing xor key file
|
||||
AutoFile xor_key_file{fsbridge::fopen(xor_key_path,
|
||||
#ifdef __MINGW64__
|
||||
"wb" // Temporary workaround for https://github.com/bitcoin/bitcoin/issues/30210
|
||||
#else
|
||||
"wbx"
|
||||
#endif
|
||||
)};
|
||||
xor_key_file << xor_key;
|
||||
}
|
||||
// If the user disabled the key, it must be zero.
|
||||
if (!opts.use_xor && xor_key != decltype(xor_key){}) {
|
||||
throw std::runtime_error{
|
||||
strprintf("The blocksdir XOR-key can not be disabled when a random key was already stored! "
|
||||
"Stored key: '%s', stored path: '%s'.",
|
||||
HexStr(xor_key), fs::PathToString(xor_key_path)),
|
||||
};
|
||||
}
|
||||
LogInfo("Using obfuscation key for blocksdir *.dat files (%s): '%s'\n", fs::PathToString(opts.blocks_dir), HexStr(xor_key));
|
||||
return std::vector<std::byte>{xor_key.begin(), xor_key.end()};
|
||||
}
|
||||
|
||||
BlockManager::BlockManager(const util::SignalInterrupt& interrupt, Options opts)
|
||||
: m_prune_mode{opts.prune_target > 0},
|
||||
m_xor_key{InitBlocksdirXorKey(opts)},
|
||||
m_opts{std::move(opts)},
|
||||
m_block_file_seq{FlatFileSeq{m_opts.blocks_dir, "blk", m_opts.fast_prune ? 0x4000 /* 16kB */ : BLOCKFILE_CHUNK_SIZE}},
|
||||
m_undo_file_seq{FlatFileSeq{m_opts.blocks_dir, "rev", UNDOFILE_CHUNK_SIZE}},
|
||||
m_interrupt{interrupt} {}
|
||||
|
||||
class ImportingNow
|
||||
{
|
||||
std::atomic<bool>& m_importing;
|
||||
|
||||
@@ -240,6 +240,8 @@ private:
|
||||
|
||||
const bool m_prune_mode;
|
||||
|
||||
const std::vector<std::byte> m_xor_key;
|
||||
|
||||
/** Dirty block index entries. */
|
||||
std::set<CBlockIndex*> m_dirty_blockindex;
|
||||
|
||||
@@ -264,12 +266,7 @@ private:
|
||||
public:
|
||||
using Options = kernel::BlockManagerOpts;
|
||||
|
||||
explicit BlockManager(const util::SignalInterrupt& interrupt, Options opts)
|
||||
: m_prune_mode{opts.prune_target > 0},
|
||||
m_opts{std::move(opts)},
|
||||
m_block_file_seq{FlatFileSeq{m_opts.blocks_dir, "blk", m_opts.fast_prune ? 0x4000 /* 16kB */ : BLOCKFILE_CHUNK_SIZE}},
|
||||
m_undo_file_seq{FlatFileSeq{m_opts.blocks_dir, "rev", UNDOFILE_CHUNK_SIZE}},
|
||||
m_interrupt{interrupt} {}
|
||||
explicit BlockManager(const util::SignalInterrupt& interrupt, Options opts);
|
||||
|
||||
const util::SignalInterrupt& m_interrupt;
|
||||
std::atomic<bool> m_importing{false};
|
||||
|
||||
Reference in New Issue
Block a user