mirror of
https://github.com/TransparentLC/opencl_vanity_gpg.git
synced 2025-10-20 07:14:09 +00:00
Refactor and clean up the code, update the workflow (#1)
This commit is contained in:
parent
f4e4050ef8
commit
0b6038d395
9 changed files with 789 additions and 443 deletions
20
.github/workflows/build.yml
vendored
20
.github/workflows/build.yml
vendored
|
@ -5,24 +5,29 @@ on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os:
|
os:
|
||||||
- windows-latest
|
- windows-latest
|
||||||
- ubuntu-latest
|
- ubuntu-latest
|
||||||
# - macos-latest
|
- macos-latest
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
toolchain: nightly
|
toolchain: stable
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- name: Prepare OpenCL SDK
|
- name: Prepare OpenCL SDK
|
||||||
shell: bash
|
shell: bash
|
||||||
|
id: prepare
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ github.token }}
|
GH_TOKEN: ${{ github.token }}
|
||||||
run: |
|
run: |
|
||||||
|
@ -35,10 +40,8 @@ jobs:
|
||||||
if [[ "$RUNNER_OS" == "Linux" ]]; then
|
if [[ "$RUNNER_OS" == "Linux" ]]; then
|
||||||
sudo apt install opencl-headers ocl-icd-opencl-dev
|
sudo apt install opencl-headers ocl-icd-opencl-dev
|
||||||
fi
|
fi
|
||||||
# FIXME
|
echo "rust_arch=$(rustc -vV | grep 'host: ' | cut -d ' ' -f 2)" >> $GITHUB_OUTPUT
|
||||||
# if [[ "$RUNNER_OS" == "macOS" ]]; then
|
|
||||||
#
|
|
||||||
# fi
|
|
||||||
- name: Build
|
- name: Build
|
||||||
id: build
|
id: build
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -54,8 +57,9 @@ jobs:
|
||||||
else
|
else
|
||||||
echo "executable=target/release/opencl_vanity_gpg" >> $GITHUB_OUTPUT
|
echo "executable=target/release/opencl_vanity_gpg" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Upload result
|
- name: Upload result
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: opencl_vanity_gpg-${{ runner.os }}
|
name: opencl_vanity_gpg-${{ steps.prepare.outputs.rust_arch }}
|
||||||
path: ${{ steps.build.outputs.executable }}
|
path: ${{ steps.build.outputs.executable }}
|
||||||
|
|
68
Cargo.lock
generated
68
Cargo.lock
generated
|
@ -116,6 +116,12 @@ dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.95"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "argon2"
|
name = "argon2"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
|
@ -364,6 +370,19 @@ version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "console"
|
||||||
|
version = "0.15.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b"
|
||||||
|
dependencies = [
|
||||||
|
"encode_unicode",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
"unicode-width",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-oid"
|
name = "const-oid"
|
||||||
version = "0.9.6"
|
version = "0.9.6"
|
||||||
|
@ -742,6 +761,12 @@ dependencies = [
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encode_unicode"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "enum_primitive"
|
name = "enum_primitive"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
@ -956,6 +981,19 @@ dependencies = [
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indicatif"
|
||||||
|
version = "0.17.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cbf675b85ed934d3c67b5c5469701eec7db22689d0a2139d856e0925fa28b281"
|
||||||
|
dependencies = [
|
||||||
|
"console",
|
||||||
|
"number_prefix",
|
||||||
|
"portable-atomic",
|
||||||
|
"unicode-width",
|
||||||
|
"web-time",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inout"
|
name = "inout"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
|
@ -1171,6 +1209,12 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "number_prefix"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ocb3"
|
name = "ocb3"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -1238,11 +1282,13 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
||||||
name = "opencl_vanity_gpg"
|
name = "opencl_vanity_gpg"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"hex",
|
"hex",
|
||||||
|
"indicatif",
|
||||||
"log",
|
"log",
|
||||||
"ocl",
|
"ocl",
|
||||||
"pgp",
|
"pgp",
|
||||||
|
@ -1408,6 +1454,12 @@ dependencies = [
|
||||||
"universal-hash",
|
"universal-hash",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "portable-atomic"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.20"
|
version = "0.2.20"
|
||||||
|
@ -1749,6 +1801,12 @@ version = "1.0.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
|
@ -1837,6 +1895,16 @@ version = "0.2.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
|
checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-time"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-core"
|
name = "windows-core"
|
||||||
version = "0.52.0"
|
version = "0.52.0"
|
||||||
|
|
29
Cargo.toml
29
Cargo.toml
|
@ -3,14 +3,23 @@ name = "opencl_vanity_gpg"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
lto = "thin"
|
||||||
|
opt-level = 3
|
||||||
|
panic = "abort"
|
||||||
|
strip = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.5.0"
|
byteorder = "1.5"
|
||||||
chrono = { version = "0.4.39", default-features = false, features = ["now"] }
|
chrono = { version = "0.4", default-features = false, features = ["now"] }
|
||||||
clap = { version = "4.5.23", features = ["derive"] }
|
clap = { version = "4.5", features = ["derive"] }
|
||||||
env_logger = { version = "0.11.6", default-features = false, features = ["auto-color", "humantime"] }
|
env_logger = { version = "0.11", default-features = false, features = ["auto-color", "humantime"] }
|
||||||
hex = "0.4.3"
|
hex = "0.4"
|
||||||
log = "0.4.22"
|
log = "0.4"
|
||||||
ocl = "0.19.7"
|
ocl = "0.19"
|
||||||
pgp = "0.14.2"
|
pgp = "0.14"
|
||||||
rand = "0.8.5"
|
rand = "0.8"
|
||||||
smallvec = "1.13.2"
|
smallvec = "1.13"
|
||||||
|
anyhow = "1.0"
|
||||||
|
indicatif = "0.17"
|
||||||
|
|
532
src/main.rs
532
src/main.rs
|
@ -1,265 +1,47 @@
|
||||||
mod vanity_gpg;
|
use anyhow::bail;
|
||||||
|
use indicatif::{ProgressBar, ProgressState, ProgressStyle};
|
||||||
use vanity_gpg::{CipherSuite, VanitySecretKey};
|
use log::{debug, info, warn};
|
||||||
use clap::Parser;
|
use ocl::{Buffer, Device, Platform, ProQue};
|
||||||
|
use pgp::types::PublicKeyTrait;
|
||||||
|
use rand::thread_rng;
|
||||||
use std::{
|
use std::{
|
||||||
|
fmt::Write,
|
||||||
fs,
|
fs,
|
||||||
io,
|
|
||||||
io::Write,
|
|
||||||
mem,
|
|
||||||
path::Path,
|
path::Path,
|
||||||
sync::mpsc::channel,
|
str::FromStr,
|
||||||
|
sync::{mpsc::*, LazyLock},
|
||||||
thread,
|
thread,
|
||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
use ocl::{Buffer, Device, Platform, ProQue};
|
use utils::ARGS;
|
||||||
use rand::thread_rng;
|
use utils::*;
|
||||||
use pgp::types::PublicKeyTrait;
|
|
||||||
use log::{warn, info, debug};
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
mod utils;
|
||||||
#[command(version, about, long_about = None)]
|
|
||||||
struct Args {
|
|
||||||
/// Cipher suite of the vanity key
|
|
||||||
/// ed25519, ecdsa-****, rsa**** => Primary key
|
|
||||||
/// cv25519, ecdh-**** => Subkey
|
|
||||||
/// Use gpg CLI for further editing of the key.
|
|
||||||
#[arg(short, long, default_value_t, value_enum, verbatim_doc_comment)]
|
|
||||||
cipher_suite: vanity_gpg::CipherSuite,
|
|
||||||
|
|
||||||
/// OpenPGP compatible user ID
|
fn main() -> anyhow::Result<()> {
|
||||||
#[arg(short, long, default_value_t = String::from("Dummy <dummy@example.com>"), verbatim_doc_comment)]
|
env_logger::Builder::from_env(
|
||||||
user_id: String,
|
env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"),
|
||||||
|
)
|
||||||
|
.format_indent(None)
|
||||||
|
.init();
|
||||||
|
|
||||||
/// A pattern less than 40 chars for matching fingerprints
|
debug!("{:#?}", LazyLock::force(&ARGS));
|
||||||
/// 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
|
|
||||||
#[arg(short, long, verbatim_doc_comment)]
|
|
||||||
pattern: Option<String>,
|
|
||||||
|
|
||||||
/// OpenCL kernel function for uint h[5] for matching fingerprints
|
let device_list = utils::DeviceList::new()?;
|
||||||
/// 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
|
|
||||||
#[arg(short, long, verbatim_doc_comment)]
|
|
||||||
filter: Option<String>,
|
|
||||||
|
|
||||||
/// The dir where the vanity keys are saved
|
if ARGS.list_device {
|
||||||
#[arg(short, long)]
|
info!("Available OpenCL devices: \n{:?}", device_list);
|
||||||
output: Option<String>,
|
|
||||||
|
|
||||||
/// Device ID to use
|
|
||||||
#[arg(short, long)]
|
|
||||||
device: Option<usize>,
|
|
||||||
|
|
||||||
/// Adjust it to maximum your device's usage
|
|
||||||
#[arg(short, long)]
|
|
||||||
thread: Option<usize>,
|
|
||||||
|
|
||||||
/// Adjust it to maximum your device's usage
|
|
||||||
#[arg(short, long, default_value_t = 1 << 9)]
|
|
||||||
iteration: usize,
|
|
||||||
|
|
||||||
/// Exit after a specified time in seconds
|
|
||||||
#[arg(long)]
|
|
||||||
timeout: Option<f64>,
|
|
||||||
|
|
||||||
/// Exit after getting a vanity key
|
|
||||||
#[arg(long, default_value_t = false)]
|
|
||||||
oneshot: bool,
|
|
||||||
|
|
||||||
/// Don't print progress
|
|
||||||
#[arg(long, default_value_t = false)]
|
|
||||||
no_progress: bool,
|
|
||||||
|
|
||||||
/// Don't print armored secret key
|
|
||||||
#[arg(long, default_value_t = false)]
|
|
||||||
no_secret_key_logging: bool,
|
|
||||||
|
|
||||||
/// Show available OpenCL devices then exit
|
|
||||||
#[arg(long, default_value_t = false)]
|
|
||||||
device_list: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 手动进行SHA-1的填充操作
|
|
||||||
/// SHA-1的一个数据块是512 bit,因此输出的Vec<u32>长度是16的倍数
|
|
||||||
fn manually_prepare_sha1(hashdata: Vec<u8>) -> Vec<u32> {
|
|
||||||
// 填充后的长度
|
|
||||||
// 用80 00 ...填充到448 mod 512 bit即56 mod 64 bytes,加上u64的8 bytes后长度是64的倍数
|
|
||||||
let padded_length = hashdata.len() + (64 - ((hashdata.len() + 8) % 64)) + 8;
|
|
||||||
let mut result_u8 = Vec::with_capacity(padded_length);
|
|
||||||
result_u8.extend_from_slice(&hashdata);
|
|
||||||
result_u8.push(0x80);
|
|
||||||
result_u8.resize(padded_length, 0);
|
|
||||||
// 需要把Vec<u8>直接转换成Vec<u32>
|
|
||||||
// https://stackoverflow.com/questions/49690459/converting-a-vecu32-to-vecu8-in-place-and-with-minimal-overhead
|
|
||||||
let mut result_u32 = unsafe {
|
|
||||||
let ptr = result_u8.as_mut_ptr() as *mut u32;
|
|
||||||
let length = result_u8.len() / 4;
|
|
||||||
let capacity = result_u8.capacity() / 4;
|
|
||||||
mem::forget(result_u8);
|
|
||||||
Vec::from_raw_parts(ptr, length, capacity)
|
|
||||||
};
|
|
||||||
// assert_eq!(result_u32.len() % 16, 0);
|
|
||||||
// SHA-1的word和length使用大端序
|
|
||||||
for i in 0..result_u32.len() {
|
|
||||||
result_u32[i] = result_u32[i].to_be();
|
|
||||||
}
|
|
||||||
let bit_length = hashdata.len() * 8;
|
|
||||||
result_u32[padded_length / 4 - 1] = (bit_length ) as u32;
|
|
||||||
result_u32[padded_length / 4 - 2] = (bit_length >> 32) as u32;
|
|
||||||
result_u32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_pattern(pattern: String) -> (String, f64) {
|
|
||||||
let pattern = match pattern.trim().replace(" ", "").to_ascii_uppercase() {
|
|
||||||
x if x.len() <= 40 => "*".repeat(40 - x.len()) + &x,
|
|
||||||
_ => panic!("Invalid pattern"),
|
|
||||||
};
|
|
||||||
let mut parts: Vec<String> = vec![];
|
|
||||||
// 处理0-9A-F
|
|
||||||
let mut fixed_pos_count: usize = 0;
|
|
||||||
for i in 0..=4 {
|
|
||||||
let mut mask = String::new();
|
|
||||||
let mut value = String::new();
|
|
||||||
let mut activated = false;
|
|
||||||
for j in 0..8 {
|
|
||||||
let char = *pattern.chars().nth(i * 8 + j).get_or_insert(' ');
|
|
||||||
if char.is_ascii_hexdigit() {
|
|
||||||
fixed_pos_count += 1;
|
|
||||||
mask += "F";
|
|
||||||
value += &String::from(char);
|
|
||||||
activated = true;
|
|
||||||
} else {
|
|
||||||
mask += "0";
|
|
||||||
value += "0";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if activated {
|
|
||||||
parts.push(format!("(h[{i}] & 0x{mask}) == 0x{value}"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 处理通配符G-Z
|
|
||||||
let mut wildcard_pos_all: [Vec<usize>; (b'Z' - b'G' + 1) as usize] = std::default::Default::default();
|
|
||||||
for (i, wildcard) in pattern.chars().enumerate() {
|
|
||||||
if ('G'..='Z').contains(&wildcard) {
|
|
||||||
wildcard_pos_all[((wildcard as u8) - b'G') as usize].push(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut wildcard_pos_count = 0;
|
|
||||||
for wildcard in 'G'..='Z' {
|
|
||||||
let wildcard_pos = &wildcard_pos_all[((wildcard as u8) - b'G') as usize];
|
|
||||||
if wildcard_pos.len() >= 2 {
|
|
||||||
for i in 1..wildcard_pos.len() {
|
|
||||||
let left_index = wildcard_pos[i - 1] / 8;
|
|
||||||
let right_index = wildcard_pos[i] / 8;
|
|
||||||
let left_digit = 7 - wildcard_pos[i - 1] % 8;
|
|
||||||
let right_digit = 7 - wildcard_pos[i] % 8;
|
|
||||||
parts.push(format!(
|
|
||||||
"(/* {}: h[{}][{}] == h[{}][{}] */ (h[{}] {} {}) & 0xF{}) == (h[{}] & 0xF{})",
|
|
||||||
wildcard,
|
|
||||||
left_index,
|
|
||||||
left_digit,
|
|
||||||
right_index,
|
|
||||||
right_digit,
|
|
||||||
left_index,
|
|
||||||
if right_digit > left_digit { "<<" } else { ">>" },
|
|
||||||
right_digit.abs_diff(left_digit) * 4,
|
|
||||||
"0".repeat(right_digit),
|
|
||||||
right_index,
|
|
||||||
"0".repeat(right_digit),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
wildcard_pos_count += wildcard_pos.len() - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let filter = if parts.len() != 0 {
|
|
||||||
parts.join(" && ")
|
|
||||||
} else {
|
|
||||||
String::from("true")
|
|
||||||
};
|
|
||||||
(filter, (16f64).powi((fixed_pos_count + wildcard_pos_count) as i32))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn format_number(v: impl Into<f64>) -> String {
|
|
||||||
match Into::<f64>::into(v) {
|
|
||||||
// v if v >= 1e9f64 => { return format!("{:.02}g", v / 1e9f64); },
|
|
||||||
v if v >= 1e6f64 => { return format!("{:.02}m", v / 1e6f64); },
|
|
||||||
v if v >= 1e3f64 => { return format!("{:.02}k", v / 1e3f64); },
|
|
||||||
v => { return format!("{v:.02}"); },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
env_logger::Builder::from_env(env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"))
|
|
||||||
.format_indent(None)
|
|
||||||
.init();
|
|
||||||
|
|
||||||
let args = if cfg!(debug_assertions) {
|
|
||||||
Args {
|
|
||||||
cipher_suite: CipherSuite::Ed25519,
|
|
||||||
user_id: String::from("Dummy <dummy@example.com>"),
|
|
||||||
pattern: Some(String::from("XXXYYYZZZWWW")),
|
|
||||||
filter: None,
|
|
||||||
output: None,
|
|
||||||
device: None,
|
|
||||||
thread: None,
|
|
||||||
iteration: 512,
|
|
||||||
timeout: None,
|
|
||||||
oneshot: true,
|
|
||||||
no_progress: true,
|
|
||||||
no_secret_key_logging: false,
|
|
||||||
device_list: false,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Args::parse()
|
|
||||||
};
|
|
||||||
debug!("{:?}", &args);
|
|
||||||
|
|
||||||
let device_list: Vec<(Platform, Device)> = Platform::list()
|
|
||||||
.iter()
|
|
||||||
.rfold(
|
|
||||||
Vec::new(),
|
|
||||||
|mut list, platform| {
|
|
||||||
match Device::list_all(platform) {
|
|
||||||
Ok(devices) => {
|
|
||||||
let mut devices = devices
|
|
||||||
.iter()
|
|
||||||
.map(|device| (*platform, *device))
|
|
||||||
.collect();
|
|
||||||
list.append(&mut devices);
|
|
||||||
},
|
|
||||||
Err(_) => {},
|
|
||||||
}
|
|
||||||
list
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if args.device_list {
|
|
||||||
for (index, (platform, device)) in device_list.iter().enumerate() {
|
|
||||||
info!("Device #{} - {}", index, format!(
|
|
||||||
"{} ({}, MaxWorkGroupSize={}, MaxWorkItemSizes={}, MaxWorkItemDimensions={})",
|
|
||||||
device.name()?,
|
|
||||||
platform.name()?,
|
|
||||||
device.info(ocl::core::DeviceInfo::MaxWorkGroupSize)?,
|
|
||||||
device.info(ocl::core::DeviceInfo::MaxWorkItemSizes)?,
|
|
||||||
device.info(ocl::core::DeviceInfo::MaxWorkItemDimensions)?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let device = match args.device {
|
let device = match ARGS.device {
|
||||||
Some(i) => device_list[i].1,
|
Some(i) => device_list[i].device,
|
||||||
None => Device::first(Platform::default())?,
|
None => Device::first(Platform::default())?,
|
||||||
};
|
};
|
||||||
|
|
||||||
info!("Using device: {}", device.name()?);
|
info!("Using device: {}", device.name()?);
|
||||||
let dimension: usize = match args.thread {
|
|
||||||
|
let dimension = match ARGS.thread {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => match device.info(ocl::core::DeviceInfo::MaxWorkItemSizes)? {
|
None => match device.info(ocl::core::DeviceInfo::MaxWorkItemSizes)? {
|
||||||
ocl::core::DeviceInfoResult::MaxWorkItemSizes(wgs) => {
|
ocl::core::DeviceInfoResult::MaxWorkItemSizes(wgs) => {
|
||||||
|
@ -270,51 +52,67 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let iteration: usize = args.iteration;
|
|
||||||
|
let iteration = ARGS.iteration;
|
||||||
info!(
|
info!(
|
||||||
"You will get vanity keys created after {}",
|
"You will get vanity keys created after {}",
|
||||||
chrono::Utc::now()
|
chrono::Utc::now()
|
||||||
.checked_sub_signed(chrono::TimeDelta::seconds((dimension * iteration) as i64)).unwrap()
|
.checked_sub_signed(chrono::TimeDelta::seconds((dimension * iteration) as i64))
|
||||||
|
.unwrap()
|
||||||
.to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
|
.to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
|
||||||
);
|
);
|
||||||
if let None = args.output {
|
|
||||||
if args.no_secret_key_logging {
|
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.");
|
warn!("No output dir given and you disabled secret key logging. You have no chance to save generated vanity keys.");
|
||||||
} else {
|
} else {
|
||||||
warn!("No output dir given. Generated vanity keys will not be saved.");
|
warn!("No output dir given. Generated vanity keys will not be saved.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (filter, estimate) = match args.filter {
|
let pattern = match &ARGS.pattern {
|
||||||
Some(filter) => (filter, None),
|
Some(pattern) => Some(HashPattern::from_str(pattern)?),
|
||||||
None => match args.pattern {
|
None => None,
|
||||||
Some(pattern) => {
|
};
|
||||||
let (filter, estimate) = parse_pattern(pattern);
|
|
||||||
(filter, Some(estimate))
|
let (filter, estimate) = match &ARGS.filter {
|
||||||
},
|
Some(filter) => (filter.clone(), None),
|
||||||
None => panic!("No filter or pattern given"),
|
None => match &pattern {
|
||||||
|
Some(p) => (p.filter.clone(), Some(p.possibliity)),
|
||||||
|
None => bail!("No filter or pattern given"),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
debug!("Filter: {filter}");
|
debug!("Filter: {filter}");
|
||||||
|
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
match args.cipher_suite {
|
match ARGS.cipher_suite {
|
||||||
CipherSuite::RSA2048 | CipherSuite::RSA3072 | CipherSuite::RSA4096 => warn!("Generating RSA vanity keys is not recommended. Too slow!"),
|
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);
|
let mut vanity_key = VanitySecretKey::new(ARGS.cipher_suite, ARGS.user_id.clone(), &mut rng);
|
||||||
let mut hashdata = manually_prepare_sha1(vanity_key.hashdata());
|
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()
|
let pro_que = ProQue::builder()
|
||||||
.src(std::include_str!("shader.cl").replace(
|
.src(
|
||||||
"#define __INJECTS__",
|
std::include_str!("shader.cl").replace(
|
||||||
&[
|
"#define __INJECTS__",
|
||||||
format!("#define FILTER(h) ({filter})"),
|
&[
|
||||||
format!("#define CHUNK ({})", hashdata.len() / 16),
|
format!("#define FILTER(h) ({filter})"),
|
||||||
].join("\n"),
|
format!("#define CHUNK ({})", hashdata.len() / 16),
|
||||||
))
|
]
|
||||||
|
.join("\n"),
|
||||||
|
),
|
||||||
|
)
|
||||||
.device(device)
|
.device(device)
|
||||||
.dims(dimension)
|
.dims(dimension)
|
||||||
.build()?;
|
.build()?;
|
||||||
|
@ -325,97 +123,33 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
.fill_val(0)
|
.fill_val(0)
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
let (tx_hashdata, rx_hashdata) = channel::<Vec<u32>>();
|
thread::spawn(move || opencl_thread(buffer_result, pro_que, rx_hashdata, tx_result));
|
||||||
let (tx_result, rx_result) = channel::<Option<u32>>();
|
|
||||||
|
|
||||||
let mut hashed: usize = 0;
|
|
||||||
let mut hashed_count: usize = 0;
|
|
||||||
let mut start = Instant::now();
|
|
||||||
|
|
||||||
thread::spawn(move || {
|
|
||||||
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(iteration as u64)
|
|
||||||
.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");
|
|
||||||
});
|
|
||||||
|
|
||||||
|
let bench_size = (dimension * iteration) as u64;
|
||||||
|
let bar = init_progress_bar(estimate);
|
||||||
loop {
|
loop {
|
||||||
debug!("Send key to OpenCL thread");
|
debug!("Send key to OpenCL thread");
|
||||||
tx_hashdata.send(hashdata)?;
|
tx_hashdata.send(hashdata)?;
|
||||||
let vanity_key_next = VanitySecretKey::new(args.cipher_suite, args.user_id.clone(), &mut rng);
|
let vanity_key_next =
|
||||||
|
VanitySecretKey::new(ARGS.cipher_suite, ARGS.user_id.clone(), &mut rng);
|
||||||
let hashdata_next = manually_prepare_sha1(vanity_key_next.hashdata());
|
let hashdata_next = manually_prepare_sha1(vanity_key_next.hashdata());
|
||||||
|
|
||||||
debug!("Receive result from OpenCL thread");
|
debug!("Receive result from OpenCL thread");
|
||||||
let vanity_timestamp = rx_result.recv()?;
|
let vanity_timestamp = rx_result.recv()?;
|
||||||
hashed += dimension * iteration;
|
hashed += bench_size;
|
||||||
hashed_count += 1;
|
|
||||||
let elapsed = start.elapsed().as_secs_f64();
|
let elapsed = start.elapsed().as_secs_f64();
|
||||||
if !args.no_progress {
|
bar.inc(bench_size);
|
||||||
match estimate {
|
|
||||||
Some(estimate) => print!(
|
if let Some(vanity_timestamp) = vanity_timestamp {
|
||||||
"[{}] {}/{} {:.02}x {:.02}s {} hash/s \r",
|
vanity_key.edit_timestamp(vanity_timestamp, &mut rng);
|
||||||
match hashed_count % 16 {
|
|
||||||
x if x < 8 => format!("{}>))'>{}", " ".repeat( x), " ".repeat(7 - x)),
|
if match &pattern {
|
||||||
x => format!("{}<'((<{}", " ".repeat(15 - x), " ".repeat(x - 8)),
|
Some(pattern) => vanity_key.check_pattern(pattern),
|
||||||
},
|
None => true,
|
||||||
format_number(hashed as f64),
|
} {
|
||||||
format_number(estimate),
|
vanity_key.log_state();
|
||||||
(hashed as f64) / estimate,
|
|
||||||
elapsed,
|
|
||||||
format_number((hashed as f64) / elapsed),
|
|
||||||
),
|
|
||||||
None => print!(
|
|
||||||
"[{}] {} {:.02}s {} hash/s \r",
|
|
||||||
match hashed_count % 16 {
|
|
||||||
x if x < 8 => format!("{}>))'>{}", " ".repeat( x), " ".repeat(7 - x)),
|
|
||||||
x => format!("{}<'((<{}", " ".repeat(15 - x), " ".repeat(x - 8)),
|
|
||||||
},
|
|
||||||
format_number(hashed as f64),
|
|
||||||
elapsed,
|
|
||||||
format_number((hashed as f64) / elapsed),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
io::stdout().flush()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
match vanity_timestamp {
|
|
||||||
Some(vanity_timestamp) => {
|
|
||||||
vanity_key.edit_timestamp(vanity_timestamp, &mut rng);
|
|
||||||
if args.no_secret_key_logging {
|
|
||||||
info!("Get a vanity key!");
|
|
||||||
} else {
|
|
||||||
info!("Get a vanity key: \n{}", vanity_key.to_armored_string()?);
|
|
||||||
}
|
|
||||||
info!(
|
|
||||||
"Created at: {} ({})",
|
|
||||||
vanity_key.secret_key.created_at().to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
|
|
||||||
vanity_key.secret_key.created_at().timestamp(),
|
|
||||||
);
|
|
||||||
info!("Fingerprint #0: {}", hex::encode_upper(vanity_key.secret_key.fingerprint().as_bytes()));
|
|
||||||
for (i, subkey) in vanity_key.secret_key.secret_subkeys.iter().enumerate() {
|
|
||||||
info!("Fingerprint #{}: {}", i + 1, hex::encode_upper(subkey.fingerprint().as_bytes()));
|
|
||||||
}
|
|
||||||
match estimate {
|
match estimate {
|
||||||
Some(estimate) => info!(
|
Some(estimate) => info!(
|
||||||
"Hashed: {} ({:.02}x) Time: {:.02}s Speed: {} hash/s",
|
"Hashed: {} ({:.02}x) Time: {:.02}s Speed: {} hash/s",
|
||||||
|
@ -431,21 +165,29 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
format_number((hashed as f64) / elapsed),
|
format_number((hashed as f64) / elapsed),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
if let Some(ref output_dir) = args.output {
|
|
||||||
|
if let Some(ref output_dir) = ARGS.output {
|
||||||
fs::write(
|
fs::write(
|
||||||
Path::new(output_dir).join(format!("{}-sec.asc", hex::encode_upper(vanity_key.secret_key.fingerprint().as_bytes()))),
|
Path::new(output_dir).join(format!(
|
||||||
|
"{}-sec.asc",
|
||||||
|
hex::encode_upper(vanity_key.secret_key.fingerprint().as_bytes())
|
||||||
|
)),
|
||||||
vanity_key.to_armored_string()?,
|
vanity_key.to_armored_string()?,
|
||||||
).unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
if args.oneshot {
|
|
||||||
|
if ARGS.oneshot {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
hashed = 0;
|
hashed = 0;
|
||||||
|
bar.reset();
|
||||||
start = Instant::now();
|
start = Instant::now();
|
||||||
},
|
}
|
||||||
None => {},
|
|
||||||
}
|
}
|
||||||
if let Some(timeout) = args.timeout {
|
|
||||||
|
if let Some(timeout) = ARGS.timeout {
|
||||||
if elapsed > timeout {
|
if elapsed > timeout {
|
||||||
info!("Timeout!");
|
info!("Timeout!");
|
||||||
break;
|
break;
|
||||||
|
@ -458,3 +200,81 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
||||||
Ok(())
|
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)
|
||||||
|
.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");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_progress_bar(estimate: Option<f64>) -> ProgressBar {
|
||||||
|
let bar = match estimate {
|
||||||
|
Some(estimate) => ProgressBar::new(estimate as u64),
|
||||||
|
None => ProgressBar::new_spinner(),
|
||||||
|
};
|
||||||
|
|
||||||
|
bar.set_style(
|
||||||
|
ProgressStyle::default_spinner()
|
||||||
|
.template("[{elapsed_precise}] {bar:40.cyan/blue} {progress} {rate}")
|
||||||
|
.unwrap()
|
||||||
|
.progress_chars("##-")
|
||||||
|
.with_key("progress", |state: &ProgressState, w: &mut dyn Write| {
|
||||||
|
write!(
|
||||||
|
w,
|
||||||
|
"{}/{}",
|
||||||
|
format_number(state.pos() as f64),
|
||||||
|
match state.len() {
|
||||||
|
None => "???".to_string(),
|
||||||
|
Some(x) => format_number(x as f64),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
})
|
||||||
|
.with_key("rate", |state: &ProgressState, w: &mut dyn Write| {
|
||||||
|
write!(
|
||||||
|
w,
|
||||||
|
"{} hash/s",
|
||||||
|
format_number((state.pos() as f64) / state.elapsed().as_secs_f64()),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
bar
|
||||||
|
}
|
||||||
|
|
120
src/utils/args.rs
Normal file
120
src/utils/args.rs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
use clap::{Parser, ValueEnum};
|
||||||
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
|
pub static ARGS: LazyLock<Args> = LazyLock::new(if cfg!(debug_assertions) {
|
||||||
|
Args::default
|
||||||
|
} else {
|
||||||
|
Args::parse
|
||||||
|
});
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(version, about, long_about = None)]
|
||||||
|
pub struct Args {
|
||||||
|
/// Cipher suite of the vanity key
|
||||||
|
/// ed25519, ecdsa-****, rsa**** => Primary key
|
||||||
|
/// cv25519, ecdh-**** => Subkey
|
||||||
|
/// Use gpg CLI for further editing of the key.
|
||||||
|
#[arg(short, long, default_value_t, value_enum, verbatim_doc_comment)]
|
||||||
|
pub cipher_suite: CipherSuite,
|
||||||
|
|
||||||
|
/// OpenPGP compatible user ID
|
||||||
|
#[arg(short, long, default_value_t = String::from("Dummy <dummy@example.com>"), verbatim_doc_comment)]
|
||||||
|
pub user_id: String,
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
#[arg(short, long, verbatim_doc_comment)]
|
||||||
|
pub pattern: Option<String>,
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
#[arg(short, long, verbatim_doc_comment)]
|
||||||
|
pub filter: Option<String>,
|
||||||
|
|
||||||
|
/// The dir where the vanity keys are saved
|
||||||
|
#[arg(short, long)]
|
||||||
|
pub output: Option<String>,
|
||||||
|
|
||||||
|
/// Device ID to use
|
||||||
|
#[arg(short, long)]
|
||||||
|
pub device: Option<usize>,
|
||||||
|
|
||||||
|
/// Adjust it to maximum your device's usage
|
||||||
|
#[arg(short, long)]
|
||||||
|
pub thread: Option<usize>,
|
||||||
|
|
||||||
|
/// Adjust it to maximum your device's usage
|
||||||
|
#[arg(short, long, default_value_t = 1 << 9)]
|
||||||
|
pub iteration: usize,
|
||||||
|
|
||||||
|
/// Exit after a specified time in seconds
|
||||||
|
#[arg(long)]
|
||||||
|
pub timeout: Option<f64>,
|
||||||
|
|
||||||
|
/// Exit after getting a vanity key
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
pub oneshot: bool,
|
||||||
|
|
||||||
|
/// Don't print progress
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
pub no_progress: bool,
|
||||||
|
|
||||||
|
/// Don't print armored secret key
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
pub no_secret_key_logging: bool,
|
||||||
|
|
||||||
|
/// Show available OpenCL devices then exit
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
pub list_device: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Args {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
cipher_suite: CipherSuite::Ed25519,
|
||||||
|
user_id: String::from("Dummy <dummy@example.com>"),
|
||||||
|
pattern: Some(String::from("XXXYYYZZZWWW")),
|
||||||
|
filter: None,
|
||||||
|
output: None,
|
||||||
|
device: None,
|
||||||
|
thread: None,
|
||||||
|
iteration: 512,
|
||||||
|
timeout: None,
|
||||||
|
oneshot: true,
|
||||||
|
no_progress: true,
|
||||||
|
no_secret_key_logging: false,
|
||||||
|
list_device: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cipher Suites
|
||||||
|
#[derive(ValueEnum, Default, Clone, Copy, Debug)]
|
||||||
|
#[clap(rename_all = "kebab_case")]
|
||||||
|
pub enum CipherSuite {
|
||||||
|
#[default]
|
||||||
|
Ed25519,
|
||||||
|
Cv25519,
|
||||||
|
RSA2048,
|
||||||
|
RSA3072,
|
||||||
|
RSA4096,
|
||||||
|
EcdhP256,
|
||||||
|
EcdhP384,
|
||||||
|
EcdhP521,
|
||||||
|
EcdsaP256,
|
||||||
|
EcdsaP384,
|
||||||
|
EcdsaP521,
|
||||||
|
}
|
87
src/utils/device.rs
Normal file
87
src/utils/device.rs
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
use ocl::core::{DeviceInfo as OclInfo, DeviceInfoResult};
|
||||||
|
use ocl::{Device, Platform};
|
||||||
|
|
||||||
|
pub struct DeviceInfo {
|
||||||
|
pub name: String,
|
||||||
|
pub platform_name: String,
|
||||||
|
pub max_work_group_size: usize,
|
||||||
|
pub max_work_item_sizes: Vec<usize>,
|
||||||
|
pub max_work_item_dimensions: u32,
|
||||||
|
|
||||||
|
// hold the actual device
|
||||||
|
pub device: Device,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DeviceList(pub Vec<DeviceInfo>);
|
||||||
|
|
||||||
|
impl DeviceInfo {
|
||||||
|
pub fn new(device: Device, platform: Platform) -> anyhow::Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
name: device.name()?,
|
||||||
|
platform_name: platform.name()?,
|
||||||
|
max_work_group_size: match device.info(OclInfo::MaxWorkGroupSize)? {
|
||||||
|
DeviceInfoResult::MaxWorkGroupSize(size) => size,
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
max_work_item_sizes: match device.info(OclInfo::MaxWorkItemSizes)? {
|
||||||
|
DeviceInfoResult::MaxWorkItemSizes(wgs) => wgs,
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
max_work_item_dimensions: match device.info(OclInfo::MaxWorkItemDimensions)? {
|
||||||
|
DeviceInfoResult::MaxWorkItemDimensions(dim) => dim,
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
device,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeviceList {
|
||||||
|
pub fn new() -> anyhow::Result<Self> {
|
||||||
|
let platforms = Platform::list();
|
||||||
|
let mut list = Vec::with_capacity(platforms.len());
|
||||||
|
for platform in platforms {
|
||||||
|
if let Ok(devices) = Device::list_all(platform) {
|
||||||
|
for device in devices {
|
||||||
|
list.push(DeviceInfo::new(device, platform)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Self(list))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for DeviceInfo {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{} ({}, MaxWorkGroupSize={}, MaxWorkItemSizes={}, MaxWorkItemDimensions={})",
|
||||||
|
self.name,
|
||||||
|
self.platform_name,
|
||||||
|
self.max_work_group_size,
|
||||||
|
self.max_work_item_sizes
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.to_string())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(", "),
|
||||||
|
self.max_work_item_dimensions
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for DeviceList {
|
||||||
|
type Target = Vec<DeviceInfo>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for DeviceList {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
for (index, device) in self.0.iter().enumerate() {
|
||||||
|
writeln!(f, "Device #{} - {:?}\n", index, device)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
115
src/utils/hash_pattern.rs
Normal file
115
src/utils/hash_pattern.rs
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use anyhow::bail;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct HashPattern {
|
||||||
|
pub pattern: String,
|
||||||
|
pub filter: String,
|
||||||
|
pub possibliity: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HashPattern {
|
||||||
|
pub fn is_match(&self, hash: &[u8]) -> bool {
|
||||||
|
if hash.len() != 20 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let hash_str = hex::encode_upper(hash);
|
||||||
|
|
||||||
|
let mut matched = true;
|
||||||
|
for (i, c) in self.pattern.chars().enumerate() {
|
||||||
|
if c.is_ascii_hexdigit() && c != hash_str.chars().nth(i).unwrap() {
|
||||||
|
matched = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matched
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for HashPattern {
|
||||||
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let pattern = match s.trim().replace(' ', "").to_ascii_uppercase() {
|
||||||
|
x if x.len() <= 40 => "*".repeat(40 - x.len()) + &x,
|
||||||
|
_ => bail!("Invalid pattern: {}", s),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut parts: Vec<String> = vec![];
|
||||||
|
|
||||||
|
// Handle fixed 0-9A-F
|
||||||
|
let mut fixed_pos_count: usize = 0;
|
||||||
|
for i in 0..=4 {
|
||||||
|
let mut mask = String::new();
|
||||||
|
let mut value = String::new();
|
||||||
|
let mut activated = false;
|
||||||
|
for j in 0..8 {
|
||||||
|
let char = *pattern.chars().nth(i * 8 + j).get_or_insert(' ');
|
||||||
|
if char.is_ascii_hexdigit() {
|
||||||
|
fixed_pos_count += 1;
|
||||||
|
mask += "F";
|
||||||
|
value += &String::from(char);
|
||||||
|
activated = true;
|
||||||
|
} else {
|
||||||
|
mask += "0";
|
||||||
|
value += "0";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if activated {
|
||||||
|
parts.push(format!("(h[{i}] & 0x{mask}) == 0x{value}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle wildcard G-Z
|
||||||
|
let mut wildcard_pos_all: [Vec<usize>; (b'Z' - b'G' + 1) as usize] =
|
||||||
|
std::default::Default::default();
|
||||||
|
for (i, wildcard) in pattern.chars().enumerate() {
|
||||||
|
if ('G'..='Z').contains(&wildcard) {
|
||||||
|
wildcard_pos_all[((wildcard as u8) - b'G') as usize].push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut wildcard_pos_count = 0;
|
||||||
|
|
||||||
|
for wildcard in 'G'..='Z' {
|
||||||
|
let wildcard_pos = &wildcard_pos_all[((wildcard as u8) - b'G') as usize];
|
||||||
|
if wildcard_pos.len() >= 2 {
|
||||||
|
for i in 1..wildcard_pos.len() {
|
||||||
|
let left_index = wildcard_pos[i - 1] / 8;
|
||||||
|
let right_index = wildcard_pos[i] / 8;
|
||||||
|
let left_digit = 7 - wildcard_pos[i - 1] % 8;
|
||||||
|
let right_digit = 7 - wildcard_pos[i] % 8;
|
||||||
|
parts.push(format!(
|
||||||
|
"(/* {}: h[{}][{}] == h[{}][{}] */ (h[{}] {} {}) & 0xF{}) == (h[{}] & 0xF{})",
|
||||||
|
wildcard,
|
||||||
|
left_index,
|
||||||
|
left_digit,
|
||||||
|
right_index,
|
||||||
|
right_digit,
|
||||||
|
left_index,
|
||||||
|
if right_digit > left_digit { "<<" } else { ">>" },
|
||||||
|
right_digit.abs_diff(left_digit) * 4,
|
||||||
|
"0".repeat(right_digit),
|
||||||
|
right_index,
|
||||||
|
"0".repeat(right_digit),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
wildcard_pos_count += wildcard_pos.len() - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let filter = if !parts.is_empty() {
|
||||||
|
parts.join(" && ")
|
||||||
|
} else {
|
||||||
|
String::from("true")
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(HashPattern {
|
||||||
|
pattern,
|
||||||
|
filter,
|
||||||
|
possibliity: (16f64).powi((fixed_pos_count + wildcard_pos_count) as i32),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
65
src/utils/mod.rs
Normal file
65
src/utils/mod.rs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
mod args;
|
||||||
|
mod device;
|
||||||
|
mod hash_pattern;
|
||||||
|
mod vanity_secret_key;
|
||||||
|
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
pub use args::*;
|
||||||
|
pub use device::DeviceList;
|
||||||
|
pub use hash_pattern::HashPattern;
|
||||||
|
pub use vanity_secret_key::VanitySecretKey;
|
||||||
|
|
||||||
|
/// Do SHA-1 padding manually
|
||||||
|
/// A SHA-1 block is 512 bit, so the output Vec<u32> length is a multiple of 16
|
||||||
|
pub fn manually_prepare_sha1(hashdata: Vec<u8>) -> Vec<u32> {
|
||||||
|
// Length after padding
|
||||||
|
// Fill with 0x80 0x00 ... to 448 mod 512 bit, which is 56 mod 64 bytes
|
||||||
|
// plus u64's 8 bytes, the length is a multiple of 64
|
||||||
|
let padded_length = hashdata.len() + (64 - ((hashdata.len() + 8) % 64)) + 8;
|
||||||
|
let mut result_u8 = Vec::with_capacity(padded_length);
|
||||||
|
result_u8.extend_from_slice(&hashdata);
|
||||||
|
result_u8.push(0x80);
|
||||||
|
result_u8.resize(padded_length, 0);
|
||||||
|
|
||||||
|
// convert Vec<u8> to Vec<u32>
|
||||||
|
// https://stackoverflow.com/questions/49690459/converting-a-vecu32-to-vecu8-in-place-and-with-minimal-overhead
|
||||||
|
let mut result_u32 = unsafe {
|
||||||
|
let ptr = result_u8.as_mut_ptr() as *mut u32;
|
||||||
|
let length = result_u8.len() / 4;
|
||||||
|
let capacity = result_u8.capacity() / 4;
|
||||||
|
mem::forget(result_u8);
|
||||||
|
Vec::from_raw_parts(ptr, length, capacity)
|
||||||
|
};
|
||||||
|
|
||||||
|
// assert_eq!(result_u32.len() % 16, 0);
|
||||||
|
// SHA-1 uses big-endian words and length
|
||||||
|
for pos in &mut result_u32 {
|
||||||
|
*pos = pos.to_be();
|
||||||
|
}
|
||||||
|
|
||||||
|
let bit_length = hashdata.len() * 8;
|
||||||
|
result_u32[padded_length / 4 - 1] = (bit_length) as u32;
|
||||||
|
result_u32[padded_length / 4 - 2] = (bit_length >> 32) as u32;
|
||||||
|
result_u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format_number(v: impl Into<f64>) -> String {
|
||||||
|
match Into::<f64>::into(v) {
|
||||||
|
v if v >= 1e12f64 => {
|
||||||
|
format!("{:.02}t", v / 1e12f64)
|
||||||
|
}
|
||||||
|
v if v >= 1e9f64 => {
|
||||||
|
format!("{:.02}b", v / 1e9f64)
|
||||||
|
}
|
||||||
|
v if v >= 1e6f64 => {
|
||||||
|
format!("{:.02}m", v / 1e6f64)
|
||||||
|
}
|
||||||
|
v if v >= 1e3f64 => {
|
||||||
|
format!("{:.02}k", v / 1e3f64)
|
||||||
|
}
|
||||||
|
v => {
|
||||||
|
format!("{v:.02}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,34 +1,28 @@
|
||||||
use clap::ValueEnum;
|
use anyhow::Result;
|
||||||
use byteorder::{ByteOrder, BigEndian};
|
use byteorder::{BigEndian, ByteOrder};
|
||||||
use rand::{Rng, CryptoRng};
|
use log::{debug, info};
|
||||||
use pgp::{
|
use pgp::{
|
||||||
composed::{key::SecretKeyParamsBuilder, KeyType},
|
composed::{key::SecretKeyParamsBuilder, KeyType},
|
||||||
crypto::{
|
crypto::{ecc_curve::ECCCurve, hash::HashAlgorithm, sym::SymmetricKeyAlgorithm},
|
||||||
hash::HashAlgorithm,
|
|
||||||
sym::SymmetricKeyAlgorithm,
|
|
||||||
ecc_curve::ECCCurve,
|
|
||||||
},
|
|
||||||
ser::Serialize,
|
|
||||||
packet::KeyFlags,
|
packet::KeyFlags,
|
||||||
types::{
|
ser::Serialize,
|
||||||
CompressionAlgorithm,
|
types::{CompressionAlgorithm, KeyVersion, PublicKeyTrait, SecretKeyTrait},
|
||||||
PublicKeyTrait,
|
Deserializable, SecretKey, SecretSubkey, SignedSecretKey, SubkeyParamsBuilder,
|
||||||
SecretKeyTrait,
|
|
||||||
KeyVersion,
|
|
||||||
},
|
|
||||||
Deserializable,
|
|
||||||
SecretKey,
|
|
||||||
SecretSubkey,
|
|
||||||
SignedSecretKey,
|
|
||||||
SubkeyParamsBuilder,
|
|
||||||
};
|
};
|
||||||
|
use rand::{CryptoRng, Rng};
|
||||||
use smallvec::smallvec;
|
use smallvec::smallvec;
|
||||||
use log::debug;
|
|
||||||
|
|
||||||
/// 获取用于计算一个私钥的指纹的数据
|
use crate::ARGS;
|
||||||
|
|
||||||
|
use super::{CipherSuite, HashPattern};
|
||||||
|
|
||||||
|
/// Get the data used to calculate the fingerprint of a private key
|
||||||
fn build_secret_key_hashdata(secret_key: impl SecretKeyTrait) -> Vec<u8> {
|
fn build_secret_key_hashdata(secret_key: impl SecretKeyTrait) -> Vec<u8> {
|
||||||
let mut hashdata = vec![0x99, 0, 0, 0x04, 0, 0, 0, 0];
|
let mut hashdata = vec![0x99, 0, 0, 0x04, 0, 0, 0, 0];
|
||||||
BigEndian::write_u32(&mut hashdata[4..8], secret_key.created_at().timestamp() as u32);
|
BigEndian::write_u32(
|
||||||
|
&mut hashdata[4..8],
|
||||||
|
secret_key.created_at().timestamp() as u32,
|
||||||
|
);
|
||||||
hashdata.push(secret_key.algorithm().into());
|
hashdata.push(secret_key.algorithm().into());
|
||||||
secret_key.public_params().to_writer(&mut hashdata).unwrap();
|
secret_key.public_params().to_writer(&mut hashdata).unwrap();
|
||||||
let packet_len = (hashdata.len() - 3) as u16;
|
let packet_len = (hashdata.len() - 3) as u16;
|
||||||
|
@ -36,23 +30,6 @@ fn build_secret_key_hashdata(secret_key: impl SecretKeyTrait) -> Vec<u8> {
|
||||||
hashdata
|
hashdata
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 需要被修改的密钥类型
|
|
||||||
#[derive(ValueEnum, Default, Clone, Copy, Debug)]
|
|
||||||
#[clap(rename_all = "kebab_case")]
|
|
||||||
pub enum CipherSuite {
|
|
||||||
#[default]
|
|
||||||
Ed25519,
|
|
||||||
Cv25519,
|
|
||||||
RSA2048,
|
|
||||||
RSA3072,
|
|
||||||
RSA4096,
|
|
||||||
EcdhP256,
|
|
||||||
EcdhP384,
|
|
||||||
EcdhP521,
|
|
||||||
EcdsaP256,
|
|
||||||
EcdsaP384,
|
|
||||||
EcdsaP521,
|
|
||||||
}
|
|
||||||
pub struct VanitySecretKey {
|
pub struct VanitySecretKey {
|
||||||
pub cipher_suite: CipherSuite,
|
pub cipher_suite: CipherSuite,
|
||||||
pub secret_key: SignedSecretKey,
|
pub secret_key: SignedSecretKey,
|
||||||
|
@ -92,10 +69,13 @@ impl VanitySecretKey {
|
||||||
secret_key_params_builder
|
secret_key_params_builder
|
||||||
.key_type(KeyType::EdDSALegacy)
|
.key_type(KeyType::EdDSALegacy)
|
||||||
.subkey(subkey_params_builder.build().unwrap());
|
.subkey(subkey_params_builder.build().unwrap());
|
||||||
},
|
}
|
||||||
CipherSuite::EcdhP256 | CipherSuite::EcdsaP256 |
|
CipherSuite::EcdhP256
|
||||||
CipherSuite::EcdhP384 | CipherSuite::EcdsaP384 |
|
| CipherSuite::EcdsaP256
|
||||||
CipherSuite::EcdhP521 | CipherSuite::EcdsaP521 => {
|
| CipherSuite::EcdhP384
|
||||||
|
| CipherSuite::EcdsaP384
|
||||||
|
| CipherSuite::EcdhP521
|
||||||
|
| CipherSuite::EcdsaP521 => {
|
||||||
let curve = match cipher_suite {
|
let curve = match cipher_suite {
|
||||||
CipherSuite::EcdhP256 | CipherSuite::EcdsaP256 => ECCCurve::P256,
|
CipherSuite::EcdhP256 | CipherSuite::EcdsaP256 => ECCCurve::P256,
|
||||||
CipherSuite::EcdhP384 | CipherSuite::EcdsaP384 => ECCCurve::P384,
|
CipherSuite::EcdhP384 | CipherSuite::EcdsaP384 => ECCCurve::P384,
|
||||||
|
@ -109,7 +89,7 @@ impl VanitySecretKey {
|
||||||
secret_key_params_builder
|
secret_key_params_builder
|
||||||
.key_type(KeyType::ECDSA(curve.clone()))
|
.key_type(KeyType::ECDSA(curve.clone()))
|
||||||
.subkey(subkey_params_builder.build().unwrap());
|
.subkey(subkey_params_builder.build().unwrap());
|
||||||
},
|
}
|
||||||
CipherSuite::RSA2048 | CipherSuite::RSA3072 | CipherSuite::RSA4096 => {
|
CipherSuite::RSA2048 | CipherSuite::RSA3072 | CipherSuite::RSA4096 => {
|
||||||
let bits = match cipher_suite {
|
let bits = match cipher_suite {
|
||||||
CipherSuite::RSA2048 => 2048,
|
CipherSuite::RSA2048 => 2048,
|
||||||
|
@ -120,13 +100,15 @@ impl VanitySecretKey {
|
||||||
secret_key_params_builder
|
secret_key_params_builder
|
||||||
.key_type(KeyType::Rsa(bits))
|
.key_type(KeyType::Rsa(bits))
|
||||||
.can_encrypt(true);
|
.can_encrypt(true);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let secret_key_params = secret_key_params_builder.build().unwrap();
|
let secret_key_params = secret_key_params_builder.build().unwrap();
|
||||||
let secret_key = secret_key_params
|
let secret_key = secret_key_params
|
||||||
.generate(&mut rng).unwrap()
|
.generate(&mut rng)
|
||||||
.sign(&mut rng, || String::new()).unwrap();
|
.unwrap()
|
||||||
|
.sign(&mut rng, String::new)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(secret_key.version(), KeyVersion::V4);
|
assert_eq!(secret_key.version(), KeyVersion::V4);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -139,7 +121,7 @@ impl VanitySecretKey {
|
||||||
// RFC 9580 - OpenPGP
|
// RFC 9580 - OpenPGP
|
||||||
// 4. Packet Syntax
|
// 4. Packet Syntax
|
||||||
// https://datatracker.ietf.org/doc/html/rfc9580#name-packet-syntax
|
// https://datatracker.ietf.org/doc/html/rfc9580#name-packet-syntax
|
||||||
let mut secret_key_bytes = Vec::from(self.secret_key.to_bytes().unwrap());
|
let mut secret_key_bytes = self.secret_key.to_bytes().unwrap();
|
||||||
let mut packet_read_pos: usize = 0;
|
let mut packet_read_pos: usize = 0;
|
||||||
while packet_read_pos < secret_key_bytes.len() {
|
while packet_read_pos < secret_key_bytes.len() {
|
||||||
let cipher_type_byte = secret_key_bytes[packet_read_pos];
|
let cipher_type_byte = secret_key_bytes[packet_read_pos];
|
||||||
|
@ -148,15 +130,33 @@ impl VanitySecretKey {
|
||||||
let (size, size_length) = match cipher_type_byte >> 6 {
|
let (size, size_length) = match cipher_type_byte >> 6 {
|
||||||
0b10 => match cipher_type_byte & 0b00000011 {
|
0b10 => match cipher_type_byte & 0b00000011 {
|
||||||
0 => (secret_key_bytes[packet_read_pos] as usize, 1),
|
0 => (secret_key_bytes[packet_read_pos] as usize, 1),
|
||||||
1 => (BigEndian::read_u16(&secret_key_bytes[packet_read_pos..packet_read_pos + 2]) as usize, 2),
|
1 => (
|
||||||
2 => (BigEndian::read_u32(&secret_key_bytes[packet_read_pos..packet_read_pos + 4]) as usize, 4),
|
BigEndian::read_u16(&secret_key_bytes[packet_read_pos..packet_read_pos + 2])
|
||||||
|
as usize,
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
2 => (
|
||||||
|
BigEndian::read_u32(&secret_key_bytes[packet_read_pos..packet_read_pos + 4])
|
||||||
|
as usize,
|
||||||
|
4,
|
||||||
|
),
|
||||||
3 => unimplemented!("Indeterminate length"),
|
3 => unimplemented!("Indeterminate length"),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
0b11 => match secret_key_bytes[packet_read_pos] {
|
0b11 => match secret_key_bytes[packet_read_pos] {
|
||||||
x if x < 192 => (secret_key_bytes[packet_read_pos] as usize, 1),
|
x if x < 192 => (secret_key_bytes[packet_read_pos] as usize, 1),
|
||||||
x if x < 224 => ((((secret_key_bytes[packet_read_pos] - 192) as usize) << 8) + (secret_key_bytes[packet_read_pos + 1] as usize) + 192, 2),
|
x if x < 224 => (
|
||||||
x if x == 255 => (BigEndian::read_u32(&secret_key_bytes[packet_read_pos + 1..packet_read_pos + 5]) as usize, 5),
|
(((secret_key_bytes[packet_read_pos] - 192) as usize) << 8)
|
||||||
|
+ (secret_key_bytes[packet_read_pos + 1] as usize)
|
||||||
|
+ 192,
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
255 => (
|
||||||
|
BigEndian::read_u32(
|
||||||
|
&secret_key_bytes[packet_read_pos + 1..packet_read_pos + 5],
|
||||||
|
) as usize,
|
||||||
|
5,
|
||||||
|
),
|
||||||
_ => unimplemented!("Partial body length"),
|
_ => unimplemented!("Partial body length"),
|
||||||
},
|
},
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
@ -172,45 +172,103 @@ impl VanitySecretKey {
|
||||||
// 0x05 => Secret-Key Packet
|
// 0x05 => Secret-Key Packet
|
||||||
// 0x07 => Secret-Subkey Packet
|
// 0x07 => Secret-Subkey Packet
|
||||||
if [0x05, 0x07].contains(&packet_type) {
|
if [0x05, 0x07].contains(&packet_type) {
|
||||||
BigEndian::write_u32(&mut secret_key_bytes[packet_read_pos + 1..packet_read_pos + 5], timestamp);
|
BigEndian::write_u32(
|
||||||
|
&mut secret_key_bytes[packet_read_pos + 1..packet_read_pos + 5],
|
||||||
|
timestamp,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
packet_read_pos += size;
|
packet_read_pos += size;
|
||||||
}
|
}
|
||||||
// 只修改了时间,因此这个key的签名是无效的
|
|
||||||
|
// The signature of this key is invalid because only the timestamp is modified
|
||||||
let edited_key = SignedSecretKey::from_bytes(&secret_key_bytes[..]).unwrap();
|
let edited_key = SignedSecretKey::from_bytes(&secret_key_bytes[..]).unwrap();
|
||||||
|
|
||||||
// 重新对key签名
|
// Re-sign the key
|
||||||
let mut subkey_flags = KeyFlags::default();
|
let mut subkey_flags = KeyFlags::default();
|
||||||
subkey_flags.set_encrypt_storage(true);
|
subkey_flags.set_encrypt_storage(true);
|
||||||
subkey_flags.set_encrypt_comms(true);
|
subkey_flags.set_encrypt_comms(true);
|
||||||
self.secret_key = SecretKey::new(
|
self.secret_key = SecretKey::new(
|
||||||
edited_key.primary_key,
|
edited_key.primary_key,
|
||||||
edited_key.details.as_unsigned(),
|
edited_key.details.as_unsigned(),
|
||||||
edited_key.public_subkeys
|
edited_key
|
||||||
|
.public_subkeys
|
||||||
.iter()
|
.iter()
|
||||||
.map(|e| e.as_unsigned())
|
.map(|e| e.as_unsigned())
|
||||||
.collect(),
|
.collect(),
|
||||||
edited_key.secret_subkeys
|
edited_key
|
||||||
|
.secret_subkeys
|
||||||
.iter()
|
.iter()
|
||||||
.map(|e| SecretSubkey::new(e.key.clone(), subkey_flags))
|
.map(|e| SecretSubkey::new(e.key.clone(), subkey_flags))
|
||||||
.collect(),
|
.collect(),
|
||||||
).sign(&mut rng, || String::new()).unwrap();
|
)
|
||||||
// self.secret_key.verify().unwrap();
|
.sign(&mut rng, String::new)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hashdata(&self) -> Vec<u8> {
|
pub fn hashdata(&self) -> Vec<u8> {
|
||||||
match self.cipher_suite {
|
match self.cipher_suite {
|
||||||
CipherSuite::Ed25519 |
|
CipherSuite::Ed25519
|
||||||
CipherSuite::EcdsaP256 | CipherSuite::EcdsaP384 | CipherSuite::EcdsaP521 |
|
| CipherSuite::EcdsaP256
|
||||||
CipherSuite::RSA2048 | CipherSuite::RSA3072 | CipherSuite::RSA4096
|
| CipherSuite::EcdsaP384
|
||||||
=> build_secret_key_hashdata(&self.secret_key),
|
| CipherSuite::EcdsaP521
|
||||||
CipherSuite::Cv25519 |
|
| CipherSuite::RSA2048
|
||||||
CipherSuite::EcdhP256 | CipherSuite::EcdhP384 | CipherSuite::EcdhP521
|
| CipherSuite::RSA3072
|
||||||
=> build_secret_key_hashdata(&self.secret_key.secret_subkeys[0]),
|
| CipherSuite::RSA4096 => build_secret_key_hashdata(&self.secret_key),
|
||||||
|
CipherSuite::Cv25519
|
||||||
|
| CipherSuite::EcdhP256
|
||||||
|
| CipherSuite::EcdhP384
|
||||||
|
| CipherSuite::EcdhP521 => {
|
||||||
|
build_secret_key_hashdata(&self.secret_key.secret_subkeys[0])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_armored_string(&self) -> Result<String, pgp::errors::Error> {
|
pub fn to_armored_string(&self) -> Result<String> {
|
||||||
self.secret_key.to_armored_string(pgp::ArmorOptions::default())
|
Ok(self
|
||||||
|
.secret_key
|
||||||
|
.to_armored_string(pgp::ArmorOptions::default())?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_pattern(&self, pattern: &HashPattern) -> bool {
|
||||||
|
if pattern.is_match(self.secret_key.fingerprint().as_bytes()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for subkey in &self.secret_key.secret_subkeys {
|
||||||
|
if pattern.is_match(subkey.fingerprint().as_bytes()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn log_state(&self) {
|
||||||
|
if ARGS.no_secret_key_logging {
|
||||||
|
info!("Get a vanity key!");
|
||||||
|
} else {
|
||||||
|
info!(
|
||||||
|
"Get a vanity key: \n{}",
|
||||||
|
self.to_armored_string().unwrap_or_default()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
info!(
|
||||||
|
"Created at: {} ({})",
|
||||||
|
self.secret_key
|
||||||
|
.created_at()
|
||||||
|
.to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
|
||||||
|
self.secret_key.created_at().timestamp(),
|
||||||
|
);
|
||||||
|
info!(
|
||||||
|
"Fingerprint #0: {}",
|
||||||
|
hex::encode_upper(self.secret_key.fingerprint().as_bytes())
|
||||||
|
);
|
||||||
|
for (i, subkey) in self.secret_key.secret_subkeys.iter().enumerate() {
|
||||||
|
info!(
|
||||||
|
"Fingerprint #{}: {}",
|
||||||
|
i + 1,
|
||||||
|
hex::encode_upper(subkey.fingerprint().as_bytes())
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue