Improve and generalize sha256 handling

This commit is contained in:
Christian Reitter 2025-08-17 19:46:22 +02:00
parent 5650e731c5
commit aa9ec80ece
2 changed files with 77 additions and 34 deletions

View File

@ -1,20 +1,48 @@
use clap::{Parser, arg};
use rust_brainwallet_search::brainwallet_single_sha256_check_btc;
use rust_brainwallet_search::brainwallet_sha256_check_btc;
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Opts {
#[arg(short, long, help = "Input file with newline-separated text snippet candidates")]
#[arg(
short,
long,
help = "Input file with newline-separated text snippet candidates"
)]
input_file: String,
#[arg(short, long, help = "Output file in CSV format with information on matches")]
#[arg(
short,
long,
help = "Output file in CSV format with information on matches"
)]
output_file: String,
#[arg(long, help = "BTC Bloom filter file with known addresses")]
bloom_file: String,
#[arg(
long,
help = "How many times to use the hashing function",
default_value_t = 1
)]
hashing_rounds: usize,
#[arg(
long,
help = "How many times to repeat the input during hashing",
default_value_t = 1
)]
hasher_input_repetition_count: usize,
}
fn main() {
let opts: Opts = Opts::parse();
brainwallet_single_sha256_check_btc(&opts.input_file, &opts.output_file, &opts.bloom_file);
brainwallet_sha256_check_btc(
&opts.input_file,
&opts.output_file,
&opts.bloom_file,
opts.hashing_rounds,
opts.hasher_input_repetition_count,
);
}

View File

@ -43,17 +43,16 @@ impl Counter {
}
}
pub fn check_bloom_and_record_hits(
pub fn check_brainwallet_bloom_and_record_hits(
bloom: &Bloom<String>,
query: String,
writer: &Mutex<csv::Writer<std::fs::File>>,
source_id: String,
source_id2: String,
hashing_rounds: String,
compressed_status: String,
prng_index: String,
prng_round_offset: String,
bit_length: String,
path: String,
passphrase: String,
print_hit: bool,
) {
if bloom.check(&query) {
@ -67,11 +66,10 @@ pub fn check_bloom_and_record_hits(
wtr.write_record([
source_id,
source_id2,
hashing_rounds,
compressed_status,
bit_length,
prng_index,
prng_round_offset,
path,
passphrase,
query,
])
.unwrap();
@ -79,33 +77,41 @@ pub fn check_bloom_and_record_hits(
}
}
pub fn brainwallet_single_sha256_check_btc(
pub fn brainwallet_sha256_check_btc(
input_file: &String,
output_file: &String,
btc_bloom: &String,
hashing_rounds: usize,
hasher_repetition_count: usize,
) {
if hashing_rounds == 0 {
panic!("Invalid number of hashing rounds");
}
let file = File::open(input_file).unwrap();
let wtr = Mutex::new(csv::Writer::from_path(output_file).unwrap());
println!("Loading bloom filter dump ...");
let bloom = address_filter::bloom::load(std::path::Path::new(&btc_bloom.to_string()))
.expect("Couldn't load bloom filter dump");
println!("... done.");
let wtr = Mutex::new(csv::Writer::from_path(output_file).unwrap());
// log every 2^16 lines
let c = Counter::new(0b0000_1111_1111_1111_1111);
let secp = secp_engine();
let bitcoin_mainnet_constant = bitcoin::network::Network::Bitcoin;
let file = File::open(input_file).unwrap();
// minor performance tuning: use larger buffer size then default 8KiB
const BUF_READER_CAPACITY: usize = 1_048_576; // 1MiB
// TODO this intentionally aborts hard on any problematic line
// replace with a more robust handling
let passphrases: Vec<String> = BufReader::new(file)
// silently drop any problematic lines
// TODO replace with a mode that flags problematic inputs and keeps statistics on them
let passphrases: Vec<String> = BufReader::with_capacity(BUF_READER_CAPACITY, file)
.lines()
.map(|l| l.expect("Could not parse line"))
.filter_map(|l| l.ok())
.collect();
let source_id = "brainwallet-single-sha256-direct";
let source_id = "brainwallet-sha256";
passphrases.par_iter().for_each(|passphrase| {
// Count lines and print progress
@ -120,9 +126,22 @@ pub fn brainwallet_single_sha256_check_btc(
// contains those.
//
// TODO consider a different data conversion approach
//
// Optionally hash the data multiple times, which concatenates the output N times
for _i in 0..hasher_repetition_count {
hasher.update(passphrase.clone());
}
// read hash digest and consume hasher
let entropy = hasher.finalize();
let mut intermediary = hasher.finalize();
// if we're asked to do more than one hashing round, repeat the hashing
// this is inefficient and designed for a low number of rounds
for _i in 1..hashing_rounds {
intermediary = Sha256::digest(intermediary);
}
// read hash digest and consume hasher
let entropy = intermediary;
let secret_key = bitcoin::secp256k1::SecretKey::from_slice(&entropy[..]).unwrap();
@ -149,15 +168,14 @@ pub fn brainwallet_single_sha256_check_btc(
let address_from_compressed_pubkey =
Address::p2pkh(&pubkey_compressed, bitcoin_mainnet_constant).to_string();
check_bloom_and_record_hits(
check_brainwallet_bloom_and_record_hits(
&bloom,
address_from_compressed_pubkey.to_string(),
&wtr,
source_id.to_string(),
"direct".to_string(),
hashing_rounds.to_string(),
"compressed".to_string(),
"".to_string(),
"".to_string(),
"256".to_string(),
passphrase.to_string(),
true,
@ -167,15 +185,14 @@ pub fn brainwallet_single_sha256_check_btc(
let address_from_uncompressed_pubkey =
Address::p2pkh(&pubkey_uncompressed, bitcoin_mainnet_constant).to_string();
check_bloom_and_record_hits(
check_brainwallet_bloom_and_record_hits(
&bloom,
address_from_uncompressed_pubkey.to_string(),
&wtr,
source_id.to_string(),
"direct".to_string(),
hashing_rounds.to_string(),
"uncompressed".to_string(),
"".to_string(),
"".to_string(),
"256".to_string(),
passphrase.to_string(),
true,
@ -187,15 +204,14 @@ pub fn brainwallet_single_sha256_check_btc(
bitcoin::network::Network::Bitcoin,
)
.to_string();
check_bloom_and_record_hits(
check_brainwallet_bloom_and_record_hits(
&bloom,
address.to_string(),
&wtr,
source_id.to_string(),
"direct".to_string(),
hashing_rounds.to_string(),
"compressed".to_string(),
"".to_string(),
"".to_string(),
"256".to_string(),
passphrase.to_string(),
true,
@ -207,15 +223,14 @@ pub fn brainwallet_single_sha256_check_btc(
bitcoin::network::Network::Bitcoin,
)
.to_string();
check_bloom_and_record_hits(
check_brainwallet_bloom_and_record_hits(
&bloom,
address.to_string(),
&wtr,
source_id.to_string(),
"direct".to_string(),
hashing_rounds.to_string(),
"compressed".to_string(),
"".to_string(),
"".to_string(),
"256".to_string(),
passphrase.to_string(),
true,