1
0
Fork 0
mirror of https://github.com/TransparentLC/opencl_vanity_gpg.git synced 2025-10-20 15:24:08 +00:00
opencl_vanity_gpg/src/main.rs

296 lines
9 KiB
Rust

use anyhow::bail;
use log::*;
use ocl::{Buffer, Device, Platform, ProQue};
use pgp::types::PublicKeyTrait;
use rand::thread_rng;
use std::{
fs,
path::Path,
str::FromStr,
sync::{mpsc::*, LazyLock},
thread,
time::Instant,
};
use utils::ARGS;
use utils::*;
mod utils;
fn main() -> anyhow::Result<()> {
let bars = init_logger();
debug!("{:#?}", LazyLock::force(&ARGS));
let device_list = utils::DeviceList::new()?;
if ARGS.list_device {
info!("Available OpenCL devices: \n");
for (i, device) in device_list.iter().enumerate() {
println!("Device #{i} - {device:?}");
}
return Ok(());
}
let device = match ARGS.device {
Some(i) => device_list[i].device,
None => Device::first(Platform::default())?,
};
info!("Using device: {}", device.name()?);
let dimension = match ARGS.thread {
Some(v) => v,
None => match device.info(ocl::core::DeviceInfo::MaxWorkItemSizes)? {
ocl::core::DeviceInfoResult::MaxWorkItemSizes(wgs) => {
let dimension = usize::max(wgs[0] * wgs[1], 1048576);
info!("Auto set thread: {dimension}");
dimension
}
_ => unreachable!(),
},
};
let iteration = ARGS.iteration;
// Determine the starting timestamp
let start_timestamp = ARGS
.start_timestamp
.unwrap_or_else(|| chrono::Utc::now().timestamp());
// Use the user-specified time range instead of dimension * iteration
let max_search_offset = ARGS.max_time_range as i64;
if ARGS.future_timestamp {
info!(
"Starting search from {} and going forward in time (up to {} seconds)",
chrono::DateTime::from_timestamp(start_timestamp, 0)
.unwrap_or_else(chrono::Utc::now)
.to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
max_search_offset
);
} else {
info!(
"Starting search from {} and going backward in time (up to {} seconds)",
chrono::DateTime::from_timestamp(start_timestamp, 0)
.unwrap_or_else(chrono::Utc::now)
.to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
max_search_offset
);
}
if ARGS.output.is_none() {
if ARGS.no_secret_key_logging {
warn!("No output dir given and you disabled secret key logging. You have no chance to save generated vanity keys.");
} else {
warn!("No output dir given. Generated vanity keys will not be saved.");
}
}
let pattern = match &ARGS.pattern {
Some(pattern) => Some(HashPattern::from_str(pattern)?),
None => None,
};
let (filter, estimate) = match &ARGS.filter {
Some(filter) => (filter.clone(), None),
None => match &pattern {
Some(p) => (p.filter.clone(), Some(p.possibliity)),
None => bail!("No filter or pattern given"),
},
};
debug!("Filter: {filter}");
let mut rng = thread_rng();
match ARGS.cipher_suite {
CipherSuite::RSA2048 | CipherSuite::RSA3072 | CipherSuite::RSA4096 => {
warn!("Generating RSA vanity keys is not recommended. Too slow!")
}
_ => (),
};
let mut vanity_key = VanitySecretKey::new(ARGS.cipher_suite, ARGS.user_id.clone(), &mut rng);
// Set the initial timestamp for the key
// Ensure we use the full timestamp value, not truncated
let initial_timestamp = if start_timestamp > u32::MAX as i64 {
// If timestamp is too large for u32, we need to handle this carefully
// For now, use the current approach but ensure we're aware of the limitation
warn!(
"Timestamp {} is too large for u32, may cause unexpected behavior",
start_timestamp
);
start_timestamp as u32
} else {
start_timestamp as u32
};
info!(
"Using base timestamp: {} ({})",
initial_timestamp,
chrono::DateTime::from_timestamp(initial_timestamp as i64, 0)
.unwrap_or_else(chrono::Utc::now)
.to_rfc3339_opts(chrono::SecondsFormat::Millis, true)
);
vanity_key.edit_timestamp(initial_timestamp, &mut rng);
let mut hashdata = manually_prepare_sha1(vanity_key.hashdata());
let (tx_hashdata, rx_hashdata) = channel::<Vec<u32>>();
let (tx_result, rx_result) = channel::<Option<u32>>();
let mut hashed = 0;
let mut start = Instant::now();
let pro_que = ProQue::builder()
.src(
std::include_str!("shader.cl").replace(
"#define __INJECTS__",
&[
format!("#define FILTER(h) ({filter})"),
format!("#define CHUNK ({})", hashdata.len() / 16),
format!(
"#define FUTURE_MODE ({})",
if ARGS.future_timestamp { 1 } else { 0 }
),
]
.join("\n"),
),
)
.device(device)
.dims(dimension)
.build()?;
let buffer_result = Buffer::<u32>::builder()
.queue(pro_que.queue().clone())
.len(1)
.fill_val(0)
.build()?;
thread::spawn(move || opencl_thread(buffer_result, pro_que, rx_hashdata, tx_result));
let bench_size = (dimension * iteration) as u64;
let bar = bars.add(init_progress_bar(estimate));
loop {
debug!("Send key to OpenCL thread");
tx_hashdata.send(hashdata)?;
let mut vanity_key_next =
VanitySecretKey::new(ARGS.cipher_suite, ARGS.user_id.clone(), &mut rng);
// Set the same initial timestamp for consistency
vanity_key_next.edit_timestamp(initial_timestamp, &mut rng);
let hashdata_next = manually_prepare_sha1(vanity_key_next.hashdata());
debug!("Receive result from OpenCL thread");
let vanity_timestamp = rx_result.recv()?;
hashed += bench_size;
let elapsed = start.elapsed().as_secs_f64();
bar.inc(bench_size);
if let Some(vanity_timestamp) = vanity_timestamp {
vanity_key.edit_timestamp(vanity_timestamp, &mut rng);
if match &pattern {
Some(pattern) => vanity_key.check_pattern(pattern),
None => true,
} {
vanity_key.log_state();
match estimate {
Some(estimate) => info!(
"Hashed: {} ({:.02}x) Time: {:.02}s Speed: {} hash/s",
format_number(hashed as f64),
(hashed as f64) / estimate,
elapsed,
format_number((hashed as f64) / elapsed),
),
None => info!(
"Hashed: {} Time: {:.02}s Speed: {} hash/s",
format_number(hashed as f64),
elapsed,
format_number((hashed as f64) / elapsed),
),
}
if let Some(ref output_dir) = ARGS.output {
fs::write(
Path::new(output_dir).join(format!(
"{}-sec.asc",
hex::encode_upper(vanity_key.secret_key.fingerprint().as_bytes())
)),
vanity_key.to_armored_string()?,
)
.unwrap();
}
if ARGS.oneshot {
bar.finish();
bars.clear()?;
break;
}
hashed = 0;
bar.reset();
start = Instant::now();
}
}
if let Some(timeout) = ARGS.timeout {
if elapsed > timeout {
info!("Timeout!");
break;
}
}
vanity_key = vanity_key_next;
hashdata = hashdata_next;
}
Ok(())
}
fn opencl_thread(
buffer_result: Buffer<u32>,
pro_que: ProQue,
rx_hashdata: Receiver<Vec<u32>>,
tx_result: Sender<Option<u32>>,
) {
let mut vec = vec![0; buffer_result.len()];
debug!("OpenCL thread ready");
while let Ok(hashdata) = rx_hashdata.recv() {
buffer_result.cmd().fill(0, None).enq().unwrap();
let buffer_hashdata = Buffer::<u32>::builder()
.queue(pro_que.queue().clone())
.len(hashdata.len())
.copy_host_slice(&hashdata)
.build()
.unwrap();
let kernel = pro_que
.kernel_builder("vanity_sha1")
.arg(&buffer_hashdata)
.arg(&buffer_result)
.arg(ARGS.iteration as u64)
.arg(ARGS.max_time_range as u32)
.build()
.unwrap();
unsafe {
kernel.enq().unwrap();
}
buffer_result.read(&mut vec).enq().unwrap();
tx_result
.send(match vec[0] {
0 => None,
x => Some(x),
})
.unwrap();
}
debug!("OpenCL thread quit");
}