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 clap::{Parser, arg};
use rust_brainwallet_search::brainwallet_single_sha256_check_btc; use rust_brainwallet_search::brainwallet_sha256_check_btc;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
struct Opts { 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, 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, output_file: String,
#[arg(long, help = "BTC Bloom filter file with known addresses")] #[arg(long, help = "BTC Bloom filter file with known addresses")]
bloom_file: String, 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() { fn main() {
let opts: Opts = Opts::parse(); 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>, bloom: &Bloom<String>,
query: String, query: String,
writer: &Mutex<csv::Writer<std::fs::File>>, writer: &Mutex<csv::Writer<std::fs::File>>,
source_id: String, source_id: String,
source_id2: String, source_id2: String,
hashing_rounds: String,
compressed_status: String, compressed_status: String,
prng_index: String,
prng_round_offset: String,
bit_length: String, bit_length: String,
path: String, passphrase: String,
print_hit: bool, print_hit: bool,
) { ) {
if bloom.check(&query) { if bloom.check(&query) {
@ -67,11 +66,10 @@ pub fn check_bloom_and_record_hits(
wtr.write_record([ wtr.write_record([
source_id, source_id,
source_id2, source_id2,
hashing_rounds,
compressed_status, compressed_status,
bit_length, bit_length,
prng_index, passphrase,
prng_round_offset,
path,
query, query,
]) ])
.unwrap(); .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, input_file: &String,
output_file: &String, output_file: &String,
btc_bloom: &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 ..."); println!("Loading bloom filter dump ...");
let bloom = address_filter::bloom::load(std::path::Path::new(&btc_bloom.to_string())) let bloom = address_filter::bloom::load(std::path::Path::new(&btc_bloom.to_string()))
.expect("Couldn't load bloom filter dump"); .expect("Couldn't load bloom filter dump");
println!("... done."); println!("... done.");
let wtr = Mutex::new(csv::Writer::from_path(output_file).unwrap());
// log every 2^16 lines // log every 2^16 lines
let c = Counter::new(0b0000_1111_1111_1111_1111); let c = Counter::new(0b0000_1111_1111_1111_1111);
let secp = secp_engine(); let secp = secp_engine();
let bitcoin_mainnet_constant = bitcoin::network::Network::Bitcoin; 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 // silently drop any problematic lines
// replace with a more robust handling // TODO replace with a mode that flags problematic inputs and keeps statistics on them
let passphrases: Vec<String> = BufReader::new(file) let passphrases: Vec<String> = BufReader::with_capacity(BUF_READER_CAPACITY, file)
.lines() .lines()
.map(|l| l.expect("Could not parse line")) .filter_map(|l| l.ok())
.collect(); .collect();
let source_id = "brainwallet-single-sha256-direct"; let source_id = "brainwallet-sha256";
passphrases.par_iter().for_each(|passphrase| { passphrases.par_iter().for_each(|passphrase| {
// Count lines and print progress // Count lines and print progress
@ -120,9 +126,22 @@ pub fn brainwallet_single_sha256_check_btc(
// contains those. // contains those.
// //
// TODO consider a different data conversion approach // 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()); hasher.update(passphrase.clone());
}
// read hash digest and consume hasher // 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(); 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 = let address_from_compressed_pubkey =
Address::p2pkh(&pubkey_compressed, bitcoin_mainnet_constant).to_string(); Address::p2pkh(&pubkey_compressed, bitcoin_mainnet_constant).to_string();
check_bloom_and_record_hits( check_brainwallet_bloom_and_record_hits(
&bloom, &bloom,
address_from_compressed_pubkey.to_string(), address_from_compressed_pubkey.to_string(),
&wtr, &wtr,
source_id.to_string(), source_id.to_string(),
"direct".to_string(), "direct".to_string(),
hashing_rounds.to_string(),
"compressed".to_string(), "compressed".to_string(),
"".to_string(),
"".to_string(),
"256".to_string(), "256".to_string(),
passphrase.to_string(), passphrase.to_string(),
true, true,
@ -167,15 +185,14 @@ pub fn brainwallet_single_sha256_check_btc(
let address_from_uncompressed_pubkey = let address_from_uncompressed_pubkey =
Address::p2pkh(&pubkey_uncompressed, bitcoin_mainnet_constant).to_string(); Address::p2pkh(&pubkey_uncompressed, bitcoin_mainnet_constant).to_string();
check_bloom_and_record_hits( check_brainwallet_bloom_and_record_hits(
&bloom, &bloom,
address_from_uncompressed_pubkey.to_string(), address_from_uncompressed_pubkey.to_string(),
&wtr, &wtr,
source_id.to_string(), source_id.to_string(),
"direct".to_string(), "direct".to_string(),
hashing_rounds.to_string(),
"uncompressed".to_string(), "uncompressed".to_string(),
"".to_string(),
"".to_string(),
"256".to_string(), "256".to_string(),
passphrase.to_string(), passphrase.to_string(),
true, true,
@ -187,15 +204,14 @@ pub fn brainwallet_single_sha256_check_btc(
bitcoin::network::Network::Bitcoin, bitcoin::network::Network::Bitcoin,
) )
.to_string(); .to_string();
check_bloom_and_record_hits( check_brainwallet_bloom_and_record_hits(
&bloom, &bloom,
address.to_string(), address.to_string(),
&wtr, &wtr,
source_id.to_string(), source_id.to_string(),
"direct".to_string(), "direct".to_string(),
hashing_rounds.to_string(),
"compressed".to_string(), "compressed".to_string(),
"".to_string(),
"".to_string(),
"256".to_string(), "256".to_string(),
passphrase.to_string(), passphrase.to_string(),
true, true,
@ -207,15 +223,14 @@ pub fn brainwallet_single_sha256_check_btc(
bitcoin::network::Network::Bitcoin, bitcoin::network::Network::Bitcoin,
) )
.to_string(); .to_string();
check_bloom_and_record_hits( check_brainwallet_bloom_and_record_hits(
&bloom, &bloom,
address.to_string(), address.to_string(),
&wtr, &wtr,
source_id.to_string(), source_id.to_string(),
"direct".to_string(), "direct".to_string(),
hashing_rounds.to_string(),
"compressed".to_string(), "compressed".to_string(),
"".to_string(),
"".to_string(),
"256".to_string(), "256".to_string(),
passphrase.to_string(), passphrase.to_string(),
true, true,