refactor: move util::Xor to Obfuscation().Xor

This is meant to focus the usages to narrow the scope of the obfuscation optimization.

`Obfuscation::Xor` is mostly a move.

Co-authored-by: maflcko <6399679+maflcko@users.noreply.github.com>
This commit is contained in:
Lőrinc
2025-07-05 13:39:15 +02:00
parent fa5d296e3b
commit 377aab8e5a
5 changed files with 26 additions and 28 deletions

View File

@@ -4,7 +4,6 @@
#include <bench/bench.h>
#include <random.h>
#include <streams.h>
#include <util/obfuscation.h>
#include <cstddef>
@@ -18,7 +17,7 @@ static void ObfuscationBench(benchmark::Bench& bench)
size_t offset{0};
bench.batch(data.size()).unit("byte").run([&] {
util::Xor(data, key, offset++); // mutated differently each time
Obfuscation().Xor(data, key, offset++); // mutated differently each time
ankerl::nanobench::doNotOptimizeAway(data);
});
}

View File

@@ -6,6 +6,7 @@
#include <span.h>
#include <streams.h>
#include <util/fs_helpers.h>
#include <util/obfuscation.h>
#include <array>
@@ -24,7 +25,7 @@ std::size_t AutoFile::detail_fread(std::span<std::byte> dst)
size_t ret = std::fread(dst.data(), 1, dst.size(), m_file);
if (!m_obfuscation.empty()) {
if (!m_position.has_value()) throw std::ios_base::failure("AutoFile::read: position unknown");
util::Xor(dst.subspan(0, ret), m_obfuscation, *m_position);
Obfuscation().Xor(dst.subspan(0, ret), m_obfuscation, *m_position);
}
if (m_position.has_value()) *m_position += ret;
return ret;
@@ -103,7 +104,7 @@ void AutoFile::write_buffer(std::span<std::byte> src)
if (!m_file) throw std::ios_base::failure("AutoFile::write_buffer: file handle is nullptr");
if (m_obfuscation.size()) {
if (!m_position) throw std::ios_base::failure("AutoFile::write_buffer: obfuscation position unknown");
util::Xor(src, m_obfuscation, *m_position); // obfuscate in-place
Obfuscation().Xor(src, m_obfuscation, *m_position); // obfuscate in-place
}
if (std::fwrite(src.data(), 1, src.size(), m_file) != src.size()) {
throw std::ios_base::failure("AutoFile::write_buffer: write failed");

View File

@@ -11,6 +11,7 @@
#include <span.h>
#include <support/allocators/zeroafterfree.h>
#include <util/check.h>
#include <util/obfuscation.h>
#include <util/overflow.h>
#include <util/syserror.h>
@@ -27,27 +28,6 @@
#include <utility>
#include <vector>
namespace util {
inline void Xor(std::span<std::byte> write, std::span<const std::byte> key, size_t key_offset = 0)
{
if (key.size() == 0) {
return;
}
key_offset %= key.size();
for (size_t i = 0, j = key_offset; i != write.size(); i++) {
write[i] ^= key[j++];
// This potentially acts on very many bytes of data, so it's
// important that we calculate `j`, i.e. the `key` index in this
// way instead of doing a %, which would effectively be a division
// for each byte Xor'd -- much slower than need be.
if (j == key.size())
j = 0;
}
}
} // namespace util
/* Minimal stream for overwriting and/or appending to an existing byte vector
*
* The referenced vector will grow as necessary
@@ -279,7 +259,7 @@ public:
*/
void Xor(const std::vector<unsigned char>& key)
{
util::Xor(MakeWritableByteSpan(*this), MakeByteSpan(key));
Obfuscation().Xor(MakeWritableByteSpan(*this), MakeByteSpan(key));
}
/** Compute total memory usage of this object (own memory + any dynamic memory). */

View File

@@ -24,7 +24,7 @@ BOOST_AUTO_TEST_CASE(xor_roundtrip_random_chunks)
auto apply_random_xor_chunks{[&](std::span<std::byte> target, std::span<const std::byte, Obfuscation::KEY_SIZE> obfuscation) {
for (size_t offset{0}; offset < target.size();) {
const size_t chunk_size{1 + m_rng.randrange(target.size() - offset)};
util::Xor(target.subspan(offset, chunk_size), obfuscation, offset);
Obfuscation().Xor(target.subspan(offset, chunk_size), obfuscation, offset);
offset += chunk_size;
}
}};
@@ -67,7 +67,7 @@ BOOST_AUTO_TEST_CASE(xor_bytes_reference)
std::vector actual{expected};
expected_xor(std::span{expected}.subspan(write_offset), key_bytes, key_offset);
util::Xor(std::span{actual}.subspan(write_offset), key_bytes, key_offset);
Obfuscation().Xor(std::span{actual}.subspan(write_offset), key_bytes, key_offset);
BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(), actual.begin(), actual.end());
}

View File

@@ -6,11 +6,29 @@
#define BITCOIN_UTIL_OBFUSCATION_H
#include <cstdint>
#include <span.h>
class Obfuscation
{
public:
static constexpr size_t KEY_SIZE{sizeof(uint64_t)};
void Xor(std::span<std::byte> write, std::span<const std::byte> key, size_t key_offset = 0)
{
assert(key.size() == KEY_SIZE);
key_offset %= KEY_SIZE;
for (size_t i = 0, j = key_offset; i != write.size(); i++) {
write[i] ^= key[j++];
// This potentially acts on very many bytes of data, so it's
// important that we calculate `j`, i.e. the `key` index in this
// way instead of doing a %, which would effectively be a division
// for each byte Xor'd -- much slower than need be.
if (j == KEY_SIZE)
j = 0;
}
}
};
#endif // BITCOIN_UTIL_OBFUSCATION_H