Merge bitcoin/bitcoin#28052: blockstorage: XOR blocksdir *.dat files

fa895c7283 mingw: Document mode wbx workaround (MarcoFalke)
fa359255fe Add -blocksxor boolean option (MarcoFalke)
fa7f7ac040 Return 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:
    ACK fa895c7283
  TheCharlatan:
    Re-ACK fa895c7283
  hodlinator:
    ACK fa895c7283

Tree-SHA512: c92a6a717da83bc33a9b8671a779eeefde2c63b192362ba1d71e6535ee31d08e2802b74acc908345197de9daac6930e4771595ee25b09acd5a67f7ea34854720
This commit is contained in:
Ava Chow
2024-08-05 17:52:42 -04:00
10 changed files with 97 additions and 16 deletions

View File

@@ -26,6 +26,10 @@ class LoadblockTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 2
self.supports_cli = False
self.extra_args = [
["-blocksxor=0"], # TODO: The linearize scripts should be adjusted to apply any XOR
[],
]
def run_test(self):
self.nodes[1].setnetworkactive(state=False)

View File

@@ -39,9 +39,19 @@ class ReindexTest(BitcoinTestFramework):
# we're generating them rather than getting them from peers), so to
# test out-of-order handling, swap blocks 1 and 2 on disk.
blk0 = self.nodes[0].blocks_path / "blk00000.dat"
with open(self.nodes[0].blocks_path / "xor.dat", "rb") as xor_f:
NUM_XOR_BYTES = 8 # From InitBlocksdirXorKey::xor_key.size()
xor_dat = xor_f.read(NUM_XOR_BYTES)
def util_xor(data, key, *, offset):
data = bytearray(data)
for i in range(len(data)):
data[i] ^= key[(i + offset) % len(key)]
return bytes(data)
with open(blk0, 'r+b') as bf:
# Read at least the first few blocks (including genesis)
b = bf.read(2000)
b = util_xor(bf.read(2000), xor_dat, offset=0)
# Find the offsets of blocks 2, 3, and 4 (the first 3 blocks beyond genesis)
# by searching for the regtest marker bytes (see pchMessageStart).
@@ -55,12 +65,12 @@ class ReindexTest(BitcoinTestFramework):
b4_start = find_block(b, b3_start)
# Blocks 2 and 3 should be the same size.
assert_equal(b3_start-b2_start, b4_start-b3_start)
assert_equal(b3_start - b2_start, b4_start - b3_start)
# Swap the second and third blocks (don't disturb the genesis block).
bf.seek(b2_start)
bf.write(b[b3_start:b4_start])
bf.write(b[b2_start:b3_start])
bf.write(util_xor(b[b3_start:b4_start], xor_dat, offset=b2_start))
bf.write(util_xor(b[b2_start:b3_start], xor_dat, offset=b3_start))
# The reindexing code should detect and accommodate out of order blocks.
with self.nodes[0].assert_debug_log([