From ef3853bb22c26ecc51c9624b492b33bcbf5a46bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=9F=E5=8F=A4=E4=B8=80?= Date: Sat, 26 Jul 2025 09:06:17 +0800 Subject: [PATCH] feat: add future timestamp support and enhance OpenCL kernel for time-based searches --- README.md | 86 +++++++++++++++++++++++++++++++---------------- src/main.rs | 70 +++++++++++++++++++++++++++++++++----- src/shader.cl | 26 ++++++++++++-- src/utils/args.rs | 22 ++++++++++++ src/utils/mod.rs | 3 +- 5 files changed, 164 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 43d04d5..f1c4c5b 100644 --- a/README.md +++ b/README.md @@ -28,35 +28,63 @@ $ opencl_vanity_gpg -h Usage: opencl_vanity_gpg [OPTIONS] Options: - -c, --cipher-suite Cipher suite of the vanity key - ed25519, ecdsa-****, rsa**** => Primary key - cv25519, ecdh-**** => Subkey - Use gpg CLI for further editing of the key. [default: ed25519] [possible values: ed25519, cv25519, rsa2048, rsa3072, rsa4096, ecdh-p256, ecdh-p384, ecdh-p521, ecdsa-p256, ecdsa-p384, ecdsa-p521] - -u, --user-id OpenPGP compatible user ID [default: "Dummy "] - -p, --pattern A pattern less than 40 chars for matching fingerprints - > Format: - * 0-9A-F are fixed, G-Z are wildcards - * Other chars will be ignored - * Case insensitive - > Example: - * 11XXXX** may output a fingerprint ends with 11222234 or 11AAAABF - * 11XXYYZZ may output a fingerprint ends with 11223344 or 11AABBCC - -f, --filter OpenCL kernel function for uint h[5] for matching fingerprints - Ignore the pattern and no estimate is given if this has been set - > Example: - * (h[4] & 0xFFFF) == 0x1234 outputs a fingerprint ends with 1234 - * (h[0] & 0xFFFF0000) == 0xABCD0000 outputs a fingerprint starts with ABCD - -o, --output The dir where the vanity keys are saved - -d, --device Device ID to use - -t, --thread Adjust it to maximum your device's usage - -i, --iteration Adjust it to maximum your device's usage [default: 512] - --timeout Exit after a specified time in seconds - --oneshot Exit after getting a vanity key - --no-progress Don't print progress - --no-secret-key-logging Don't print armored secret key - --list-device Show available OpenCL devices then exit - -h, --help Print help - -V, --version Print version + -c, --cipher-suite + Cipher suite of the vanity key + ed25519, ecdsa-****, rsa**** => Primary key + cv25519, ecdh-**** => Subkey + Use gpg CLI for further editing of the key. [default: ed25519] [possible values: ed25519, cv25519, rsa2048, rsa3072, rsa4096, ecdh-p256, ecdh-p384, ecdh-p521, ecdsa-p256, ecdsa-p384, ecdsa-p521] + -u, --user-id + OpenPGP compatible user ID [default: "Dummy "] + -p, --pattern + A pattern less than 40 chars for matching fingerprints + > Format: + * 0-9A-F are fixed, G-Z are wildcards + * Other chars will be ignored + * Case insensitive + > Example: + * 11XXXX** may output a fingerprint ends with 11222234 or 11AAAABF + * 11XXYYZZ may output a fingerprint ends with 11223344 or 11AABBCC + -f, --filter + OpenCL kernel function for uint h[5] for matching fingerprints + Ignore the pattern and no estimate is given if this has been set + > Example: + * (h[4] & 0xFFFF) == 0x1234 outputs a fingerprint ends with 1234 + * (h[0] & 0xFFFF0000) == 0xABCD0000 outputs a fingerprint starts with ABCD + -o, --output + The dir where the vanity keys are saved + -d, --device + Device ID to use + -t, --thread + Adjust it to maximum your device's usage + -i, --iteration + Adjust it to maximum your device's usage [default: 512] + --timeout + Exit after a specified time in seconds + --oneshot + Exit after getting a vanity key + --no-progress + Don't print progress + --no-secret-key-logging + Don't print armored secret key + --list-device + Show available OpenCL devices then exit + --future-timestamp + Generate keys with future timestamps instead of past timestamps + When true: search from start_timestamp forward in time (start_timestamp + 0 to max_time_range) + When false: search from start_timestamp backward in time (start_timestamp - max_time_range to start_timestamp - 0) + --start-timestamp + Custom timestamp to start searching from (Unix timestamp) + This is the base time point from which the search begins + If not specified, uses current time as the starting point + Example: 1640995200 (Jan 1, 2022 00:00:00 UTC) + --max-time-range + Maximum time range to search in seconds (default: 86400000 = 1000 days) + future_timestamp=true: search from start_timestamp to (start_timestamp + max_time_range) + future_timestamp=false: search from (start_timestamp - max_time_range) to start_timestamp [default: 86400000] + -h, --help + Print help + -V, --version + Print version $ opencl_vanity_gpg -p 11XXYYZZ --oneshot [2025-01-08T19:00:25Z INFO opencl_vanity_gpg] Using device: Apple M1 Pro diff --git a/src/main.rs b/src/main.rs index f2825ed..c6960ac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,7 +26,7 @@ fn main() -> anyhow::Result<()> { if ARGS.list_device { info!("Available OpenCL devices: \n"); for (i, device) in device_list.iter().enumerate() { - println!("Device #{} - {:?}", i, device); + println!("Device #{i} - {device:?}"); } return Ok(()); } @@ -51,13 +51,32 @@ fn main() -> anyhow::Result<()> { }; let iteration = ARGS.iteration; - info!( - "You will get vanity keys created after {}", - chrono::Utc::now() - .checked_sub_signed(chrono::TimeDelta::seconds((dimension * iteration) as i64)) - .unwrap() - .to_rfc3339_opts(chrono::SecondsFormat::Millis, true), - ); + + // 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 { @@ -91,6 +110,31 @@ fn main() -> anyhow::Result<()> { }; 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::>(); @@ -106,6 +150,10 @@ fn main() -> anyhow::Result<()> { &[ format!("#define FILTER(h) ({filter})"), format!("#define CHUNK ({})", hashdata.len() / 16), + format!( + "#define FUTURE_MODE ({})", + if ARGS.future_timestamp { 1 } else { 0 } + ), ] .join("\n"), ), @@ -128,8 +176,11 @@ fn main() -> anyhow::Result<()> { loop { debug!("Send key to OpenCL thread"); tx_hashdata.send(hashdata)?; - let vanity_key_next = + 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"); @@ -224,6 +275,7 @@ fn opencl_thread( .arg(&buffer_hashdata) .arg(&buffer_result) .arg(ARGS.iteration as u64) + .arg(ARGS.max_time_range as u32) .build() .unwrap(); diff --git a/src/shader.cl b/src/shader.cl index 50aeaaa..fd9abde 100644 --- a/src/shader.cl +++ b/src/shader.cl @@ -4,15 +4,35 @@ #ifdef __INJECTS__ #define CHUNK (0) #define FILTER(h) (false) +#define FUTURE_MODE (0) #endif -__kernel void vanity_sha1(__constant uint *hashdata, __global uint *result, const ulong iter) { +__kernel void vanity_sha1(__constant uint *hashdata, __global uint *result, const ulong iter, const uint max_time_range) { uint data[CHUNK * 16]; for (uint i = 0; i < CHUNK * 16; i++) data[i] = hashdata[i]; uint nonce = data[1]; + + uint thread_id = get_global_id(0); + for (uint i = 0; i < iter; i++) { - data[1] = nonce - get_global_id(0) * iter - i; - if (data[1] > nonce) break; + // Use a simple sequential approach that searches close to base time first + // Each thread gets a small sequential offset + uint offset = thread_id + i * get_global_size(0); + + // Wrap around within max_time_range to avoid going too far + offset = offset % max_time_range; + + if (FUTURE_MODE) { + // For future mode: increment timestamp within range + data[1] = nonce + offset; + // Check for overflow + if (data[1] < nonce) break; + } else { + // For past mode: decrement timestamp within range + data[1] = nonce - offset; + // Check for underflow + if (data[1] > nonce) break; + } uint h[] = {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0}; diff --git a/src/utils/args.rs b/src/utils/args.rs index c4c413c..2c42a25 100644 --- a/src/utils/args.rs +++ b/src/utils/args.rs @@ -75,6 +75,25 @@ pub struct Args { /// Show available OpenCL devices then exit #[arg(long, default_value_t = false)] pub list_device: bool, + + /// Generate keys with future timestamps instead of past timestamps + /// When true: search from start_timestamp forward in time (start_timestamp + 0 to max_time_range) + /// When false: search from start_timestamp backward in time (start_timestamp - max_time_range to start_timestamp - 0) + #[arg(long, default_value_t = false, verbatim_doc_comment)] + pub future_timestamp: bool, + + /// Custom timestamp to start searching from (Unix timestamp) + /// This is the base time point from which the search begins + /// If not specified, uses current time as the starting point + /// Example: 1640995200 (Jan 1, 2022 00:00:00 UTC) + #[arg(long, verbatim_doc_comment)] + pub start_timestamp: Option, + + /// Maximum time range to search in seconds (default: 86400000 = 1000 days) + /// future_timestamp=true: search from start_timestamp to (start_timestamp + max_time_range) + /// future_timestamp=false: search from (start_timestamp - max_time_range) to start_timestamp + #[arg(long, default_value_t = 86400000, verbatim_doc_comment)] + pub max_time_range: u64, } impl Default for Args { @@ -93,6 +112,9 @@ impl Default for Args { no_progress: true, no_secret_key_logging: false, list_device: false, + future_timestamp: false, + start_timestamp: None, + max_time_range: 86400000, } } } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 8372a35..e4b5e19 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -7,9 +7,9 @@ use std::{fmt::Write, mem}; pub use args::*; pub use device::DeviceList; -pub use pattern::HashPattern; use indicatif::*; use indicatif_log_bridge::LogWrapper; +pub use pattern::HashPattern; pub use vanity_key::VanitySecretKey; /// Do SHA-1 padding manually @@ -98,7 +98,6 @@ pub fn init_progress_bar(estimate: Option) -> ProgressBar { bar } - pub fn format_number(v: impl Into) -> String { match Into::::into(v) { v if v >= 1e12f64 => {