diff --git a/rust-brainwallet-search/src/bin/brainwallet_single_sha256.rs b/rust-brainwallet-search/src/bin/brainwallet_single_sha256.rs index 97697fe..6acae1d 100644 --- a/rust-brainwallet-search/src/bin/brainwallet_single_sha256.rs +++ b/rust-brainwallet-search/src/bin/brainwallet_single_sha256.rs @@ -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, + ); } diff --git a/rust-brainwallet-search/src/lib.rs b/rust-brainwallet-search/src/lib.rs index cb52d96..d902082 100644 --- a/rust-brainwallet-search/src/lib.rs +++ b/rust-brainwallet-search/src/lib.rs @@ -43,17 +43,16 @@ impl Counter { } } -pub fn check_bloom_and_record_hits( +pub fn check_brainwallet_bloom_and_record_hits( bloom: &Bloom, query: String, writer: &Mutex>, 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 = 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 = 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 - hasher.update(passphrase.clone()); + // + // 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,