├── src
├── utils
│ ├── mod.rs
│ └── utils.rs
├── services
│ ├── block_ranges
│ │ └── blockbuilder.rs
│ ├── mod.rs
│ └── key_search
│ │ ├── mod.rs
│ │ ├── math.rs
│ │ ├── bsgs.rs
│ │ ├── pollards_rho.rs
│ │ └── keyripper.rs
├── data
│ ├── mod.rs
│ └── addresses.json
├── config
│ └── mod.rs
└── main.rs
├── .gitattributes
├── .idea
├── .gitignore
├── vcs.xml
├── discord.xml
├── modules.xml
└── keyripper.iml
├── .github
└── workflows
│ └── rust.yml
├── .gitignore
├── Cargo.toml
├── LICENSE
└── README.md
/src/utils/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod utils;
--------------------------------------------------------------------------------
/src/services/block_ranges/blockbuilder.rs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/services/mod.rs:
--------------------------------------------------------------------------------
1 | pub(crate) mod key_search;
2 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/src/services/key_search/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod keyripper;
2 | pub mod bsgs;
3 | pub mod math;
4 | mod pollards_rho;
5 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/discord.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.github/workflows/rust.yml:
--------------------------------------------------------------------------------
1 | name: Rust
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 | pull_request:
7 | branches: [ "main" ]
8 |
9 | env:
10 | CARGO_TERM_COLOR: always
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v4
19 | - name: Build
20 | run: cargo build --verbose
21 | - name: Run tests
22 | run: cargo test --verbose
23 |
--------------------------------------------------------------------------------
/.idea/keyripper.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 |
17 | # Added by cargo
18 |
19 | /target
20 | .env
21 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "keyripper"
3 | version = "0.3.0"
4 | edition = "2021"
5 |
6 | [build]
7 | rustflags = ["-Clinker=rust-lld"]
8 |
9 | [dependencies]
10 | dotenv = "0.15"
11 | env_logger = "0.11.5"
12 | log = "0.4.22"
13 | tokio = { version = "1.39.3", features = ["rt", "rt-multi-thread", "macros"] }
14 | sys-info = "0.9"
15 | num_cpus = "1.13"
16 | serde_json = "1.0"
17 | rand = "0.8.5"
18 | hex = "0.4.3"
19 | bitcoin = "0.32.2"
20 | secp256k1 = "0.29.0"
21 | k256 = "0.14.0-pre.0"
22 | libsecp256k1 = "0.7.1"
23 | num-bigint = { version = "0.4.6", features = ["rand"] }
24 | num-traits = "0.2.19"
25 | serde = { version = "1.0.208", features = ["derive"] }
26 | reqwest = { version = "0.12.7", features = ["json"] }
--------------------------------------------------------------------------------
/src/data/mod.rs:
--------------------------------------------------------------------------------
1 | use serde::Deserialize;
2 |
3 | #[derive(Deserialize, Debug)]
4 | pub(crate) struct Address {
5 | #[serde(rename = "Address")]
6 | pub(crate) address: u8,
7 |
8 | #[serde(rename = "BitRange")]
9 | pub(crate) bit_range: String,
10 |
11 | #[serde(rename = "PrivateKeyRange")]
12 | pub(crate) private_key_range: String,
13 |
14 | #[serde(rename = "PrivateKeyRangeStart")]
15 | pub(crate) private_key_range_start: String,
16 |
17 | #[serde(rename = "PrivateKeyRangeEnd")]
18 | pub(crate) private_key_range_end: String,
19 |
20 | #[serde(rename = "PrivateKey(HEX)")]
21 | pub(crate) private_key_hex: String,
22 |
23 | #[serde(rename = "PublicKey(HEX)")]
24 | pub(crate) public_key_hex: String,
25 |
26 | #[serde(rename = "BitcoinAddress")]
27 | pub(crate) bitcoin_address: String,
28 |
29 | #[serde(rename = "PercentOfRange")]
30 | pub(crate) percent_of_range: f32,
31 |
32 | #[serde(rename = "ResolutionDate")]
33 | pub(crate) resolution_date: String,
34 |
35 | #[serde(rename = "Solver")]
36 | pub(crate) solver: String,
37 |
38 | #[serde(rename = "Solved")]
39 | pub(crate) solved: bool,
40 | }
41 |
--------------------------------------------------------------------------------
/src/services/key_search/math.rs:
--------------------------------------------------------------------------------
1 | use num_bigint::BigUint;
2 | use num_traits::One;
3 | use k256::{AffinePoint, EncodedPoint, ProjectivePoint, Scalar};
4 | use k256::elliptic_curve::group::GroupEncoding;
5 |
6 | pub(crate) fn sqrt_mod_prime(y_square: &BigUint, p: &BigUint) -> Option {
7 | let exponent = (p + BigUint::one()) >> 2;
8 | let result = y_square.modpow(&exponent, p);
9 |
10 | if (&result * &result) % p == *y_square {
11 | Some(result)
12 | } else {
13 | None
14 | }
15 | }
16 |
17 | pub fn affine_coordinates(
18 | encoded_point: &EncodedPoint, target_public_key_point: ProjectivePoint, public_key_y: BigUint
19 | ) -> (BigUint, BigUint) {
20 | // println!("Encoded point {:?}", encoded_point);
21 |
22 | let affine_point = AffinePoint::from(target_public_key_point);
23 |
24 | let point_bytes = affine_point.to_bytes().as_slice();
25 |
26 | let affine_point = AffinePoint::from(target_public_key_point);
27 |
28 | // affine point to bytes
29 | let binding = affine_point.to_bytes();
30 | let point_bytes = binding.as_slice();
31 |
32 | // first byte is a prefix, the next 32 are x
33 | let x_bytes = &point_bytes[1..];
34 |
35 | let x_decimal = BigUint::from_bytes_be(x_bytes).to_str_radix(10);
36 |
37 | /// y
38 |
39 | let y_bytes = public_key_y.to_bytes_be();
40 | let y_decimal = BigUint::from_bytes_be(&y_bytes).to_str_radix(10);
41 |
42 | (x_decimal.parse().unwrap(), y_decimal.parse().unwrap())
43 | }
44 |
--------------------------------------------------------------------------------
/src/config/mod.rs:
--------------------------------------------------------------------------------
1 | use dotenv::dotenv;
2 | use std::env;
3 |
4 | pub struct Config {
5 | pub process: String,
6 | pub num_cores: usize,
7 | pub num_threads: usize,
8 | pub subrange_size: u64,
9 | pub server_url: String,
10 | pub api_auth_token: String
11 | }
12 |
13 | impl Config {
14 | pub fn load() -> Config {
15 | dotenv().ok();
16 |
17 | let process = env::var("PROCESS").unwrap_or_else(|_| "".to_string());
18 |
19 | let num_cores = env::var("NUM_CORES")
20 | .ok()
21 | .and_then(|v| v.parse::().ok())
22 | .unwrap_or(0);
23 |
24 | let num_threads = env::var("NUM_THREADS")
25 | .ok()
26 | .and_then(|v| v.parse::().ok())
27 | .unwrap_or(0);
28 |
29 | let subrange_size = env::var("SUBRANGE_SIZE")
30 | .ok()
31 | .and_then(|v| v.parse::().ok())
32 | .unwrap_or(0);
33 |
34 | let server_url = env::var("SERVER_URL").unwrap_or_else(|_| "".to_string());
35 |
36 | let api_auth_token = env::var("API_AUTH_TOKEN")
37 | .unwrap_or_else(|_| "".to_string());
38 |
39 | if !process.is_empty() {
40 | println!("[+] Mode: {:?}", process);
41 | }
42 |
43 | if num_cores != 0 {
44 | println!("[+] Logical Cores: {:?}", num_cores);
45 | }
46 |
47 | if num_threads != 0 {
48 | println!("[+] Threads: {:?}", num_threads);
49 | }
50 |
51 | if !process.is_empty() {
52 | println!("[+] Server URL: {:?}", server_url);
53 | }
54 |
55 | Config {
56 | process,
57 | num_threads,
58 | num_cores,
59 | subrange_size,
60 | server_url,
61 | api_auth_token
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/main.rs:
--------------------------------------------------------------------------------
1 | #![deny(clippy::all)]
2 | #![allow(unused)]
3 |
4 | mod config;
5 | mod services;
6 | mod utils;
7 | mod data;
8 |
9 | use crate::utils::utils::{machine_info, import_addresses, show_hardware_info, HardwareInfo};
10 | use utils::utils::introduction;
11 |
12 | use services::key_search::keyripper::{KeySearch, EllipticCurve};
13 | use crate::config::Config;
14 | use crate::data::Address;
15 |
16 | fn main() -> Result<(), Box> {
17 | env_logger::init();
18 |
19 | introduction();
20 |
21 | let hardware_info = match machine_info() {
22 | Ok(hardware) => {
23 | show_hardware_info(&hardware);
24 | hardware
25 | }
26 | Err(e) => {
27 | eprintln!("{}", e);
28 | return Ok(());
29 | }
30 | };
31 |
32 | println!("[+] Preconfigured Processes");
33 |
34 | let config = Config::load();
35 |
36 | let addresses = import_addresses("./src/data/addresses.json")?;
37 |
38 | match config.process.as_str() {
39 | "SEARCH_PRIV_KEY_BY_ADDR" => search_private_key_by_address(&addresses),
40 | "SEARCH_PUB_KEY" => search_public_key_by_private_key(&addresses),
41 | _ => search_private_key_by_public_key(&hardware_info, config, &addresses),
42 | }
43 |
44 | Ok(())
45 | }
46 |
47 | /// Executes the process of searching for a private key by a public key.
48 | ///
49 | /// This process uses the `KeySearch` class to find the private key
50 | /// corresponding to a given public key.
51 | fn search_private_key_by_public_key(
52 | hardware_info: &HardwareInfo,
53 | config: Config,
54 | addresses: &Vec
55 | ) {
56 | for i in (5..=addresses.len()).step_by(5) {
57 | if let Some(address) = addresses.get(i - 1) {
58 | if !address.solved {
59 | println!("\n[+] Activating Private Key from Public Key search");
60 | println!(
61 | "[+] Address: {:?}: {}\n\n",
62 | address.address, address.bit_range
63 | );
64 |
65 | let key_search = KeySearch::new();
66 |
67 | key_search.private_key_by_public_key(&hardware_info, &config, &address);
68 |
69 | // break;
70 | }
71 | }
72 | }
73 | }
74 |
75 | /// Executes the process of searching for a public key by a private key.
76 | ///
77 | /// This process uses the `KeySearch` class to find the public key
78 | /// corresponding to a given private key. If the public key is not
79 | /// found, an error message will be displayed.
80 | fn search_public_key_by_private_key(addresses: &Vec) {
81 | for i in 1..=addresses.len() {
82 | if let Some(address) = addresses.get(i) {
83 | if !address.solved {
84 | println!("\n[+] {:?}: {}", address.address, address.bit_range);
85 |
86 | let key_search = KeySearch::new();
87 |
88 | if let Some(public_key) = key_search.compressed_public_key_by_private_key_hex(
89 | address.private_key_hex.as_str()
90 | ) {
91 | println!("{}", public_key);
92 | } else {
93 | println!("Public key not found!");
94 | }
95 | }
96 | }
97 | }
98 | }
99 |
100 | fn search_private_key_by_address(addresses: &Vec) {
101 | let status_output_timer = 10u64;
102 | println!("\n[+] Status output every {} seconds", status_output_timer);
103 | }
--------------------------------------------------------------------------------
/src/utils/utils.rs:
--------------------------------------------------------------------------------
1 | #![allow(unused)]
2 |
3 | use sys_info;
4 | use std::fs::File;
5 | use std::io::BufReader;
6 | use std::path::Path;
7 | use crate::data::Address;
8 | use bitcoin::secp256k1::Secp256k1;
9 | use num_cpus;
10 |
11 | pub fn introduction() {
12 | println!("\x1b[38;2;173;216;230m ╔═════════════════════════════════════════════════╗");
13 | println!("\x1b[38;2;173;216;230m║\x1b[0m\x1b[1m\x1b[38;2;173;216;230m keyrypper v0.3.0 - Satoshi Quest \x1b[0m\x1b[38;2;173;216;230m║");
14 | println!("\x1b[38;2;173;216;230m║\x1b[0m\x1b[1m\x1b[38;2;173;216;230m by Denzy Legacy \x1b[0m\x1b[38;2;173;216;230m║");
15 | println!("\x1b[38;2;173;216;230m ╚═════════════════════════════════════════════════╝\x1b[0m");
16 | }
17 |
18 |
19 | pub fn import_addresses(file_path: &str) -> Result, Box> {
20 | let file = File::open(Path::new(file_path))?;
21 | let reader = BufReader::new(file);
22 |
23 | let addresses: Vec = serde_json::from_reader(reader)?;
24 |
25 | Ok(addresses)
26 | }
27 |
28 | #[derive(Debug)]
29 | pub struct HardwareInfo {
30 | pub(crate) hostname: String,
31 | pub(crate) logical_cores: usize,
32 | pub(crate) current_processes: u64,
33 | pub(crate) cpu_speed_mhz: u64,
34 | pub(crate) cpu_speed_ghz: f64,
35 | pub(crate) total_ram_gb: f64,
36 | pub(crate) free_ram_gb: f64,
37 | pub(crate) os_type: String,
38 | pub(crate) os_release: String,
39 | pub(crate) total_disk_gb: f64,
40 | pub(crate) free_disk_gb: f64,
41 | }
42 |
43 | /// Gather host hardware information and return it as a `MachineInfo` object.
44 | pub fn machine_info() -> Result {
45 |
46 | let hostname = sys_info::hostname().map_err(
47 | |e| format!("Error retrieving host information: {}", e)
48 | )?;
49 |
50 | let logical_cores = num_cpus::get();
51 |
52 | let current_processes = sys_info::proc_total().map_err(
53 | |e| format!("Error retrieving processes information: {}", e)
54 | )?;
55 |
56 | let cpu_speed_mhz = sys_info::cpu_speed().map_err(
57 | |e| format!("Error retrieving CPU speed: {}", e)
58 | )?;
59 | let cpu_speed_ghz = cpu_speed_mhz as f64 / 1000.0;
60 |
61 | let mem_info = sys_info::mem_info().map_err(
62 | |e| format!("Error retrieving RAM information: {}", e)
63 | )?;
64 | let total_ram_gb = mem_info.total as f64 / (1024.0 * 1024.0);
65 | let free_ram_gb = mem_info.free as f64 / (1024.0 * 1024.0);
66 |
67 | let os_type = sys_info::os_type().map_err(
68 | |e| format!("Error retrieving operating system information: {}", e)
69 | )?;
70 | let os_release = sys_info::os_release().map_err(
71 | |e| format!("Error retrieving system version: {}", e)
72 | )?;
73 |
74 | let disk_info = sys_info::disk_info().map_err(
75 | |e| format!("Error retrieving disk information: {}", e)
76 | )?;
77 | let total_disk_gb = disk_info.total as f64 / (1024.0 * 1024.0);
78 | let free_disk_gb = disk_info.free as f64 / (1024.0 * 1024.0);
79 |
80 | Ok(HardwareInfo {
81 | hostname,
82 | logical_cores,
83 | current_processes,
84 | cpu_speed_mhz,
85 | cpu_speed_ghz,
86 | total_ram_gb,
87 | free_ram_gb,
88 | os_type,
89 | os_release,
90 | total_disk_gb,
91 | free_disk_gb,
92 | })
93 | }
94 |
95 | pub fn show_hardware_info(hardware: &HardwareInfo) {
96 | println!("[+] Hostname: {}", hardware.hostname);
97 | println!("[+] Logical Cores: {}", hardware.logical_cores);
98 | println!("[+] Current processes: {}", hardware.current_processes);
99 | println!(
100 | "[+] CPU Speed: {} MHz ({:.2} GHz)",
101 | hardware.cpu_speed_mhz, hardware.cpu_speed_ghz
102 | );
103 | println!(
104 | "[+] Total RAM: {:.2} GB ({:.2} GB free)",
105 | hardware.total_ram_gb, hardware.free_ram_gb
106 | );
107 | println!("[+] OS: {} v{}", hardware.os_type, hardware.os_release);
108 | println!(
109 | "[+] Total Disk Space: {:.2} GB ({:.2} GB free)\n",
110 | hardware.total_disk_gb, hardware.free_disk_gb
111 | );
112 | }
113 |
--------------------------------------------------------------------------------
/src/services/key_search/bsgs.rs:
--------------------------------------------------------------------------------
1 | use k256::{ProjectivePoint, AffinePoint};
2 | use num_bigint::BigUint;
3 | use std::collections::HashMap;
4 | use k256::elliptic_curve::group::GroupEncoding;
5 | use num_traits::{Zero, One};
6 |
7 | /// Scalar multiplication using the double-and-add algorithm.
8 | /// Computes `result = scalar * point`, where `point` is a point on the elliptic curve
9 | /// and `scalar` is a large integer (`k`).
10 | /// Formula: result = k * P, where `P` is the elliptic curve point.
11 | fn scalar_mul(point: &ProjectivePoint, scalar: &BigUint) -> ProjectivePoint {
12 | let mut result = ProjectivePoint::IDENTITY; // Identity element of the elliptic curve group
13 | let mut addend = *point;
14 | let mut k = scalar.clone(); // `k` is the scalar being multiplied
15 |
16 | // Double-and-add method to compute scalar multiplication
17 | while k > BigUint::zero() {
18 | // Add if the least significant bit of `k` is 1
19 | if &k & BigUint::one() == BigUint::one() {
20 | result += addend;
21 | }
22 | // Double the point (add point to itself)
23 | addend = addend.double();
24 | // Shift `k` one bit to the right (equivalent to dividing by 2)
25 | k >>= 1;
26 | }
27 | result
28 | }
29 |
30 | /// Baby-step Giant-step (BSGS) algorithm for solving discrete logarithm problem on elliptic curves.
31 | /// This algorithm finds the scalar `k` such that `target_point = k * G`, where `G` is a generator point.
32 | ///
33 | /// - `target_point` is the point we are trying to match.
34 | /// - `g` is the generator point (usually `G`).
35 | /// - `start` is the starting scalar value (offset).
36 | /// - `max_steps` defines the range of search, i.e., the maximum number of steps for both baby and giant steps.
37 | pub fn bsgs(
38 | target_point: &ProjectivePoint,
39 | g: &ProjectivePoint,
40 | start: &BigUint,
41 | max_steps: &BigUint,
42 | ) -> Option {
43 | println!("start: {:?}, max_steps: {:?}", start, max_steps);
44 |
45 | let mut baby_steps = HashMap::new();
46 |
47 | // Calculate `current = g * start`, where `g` is the generator point and `start` is the scalar offset.
48 | let mut current = scalar_mul(g, start);
49 |
50 | // Baby-step phase: Compute all `g^i` for i in the range [0, max_steps], store in a hash map.
51 | let mut i = BigUint::zero();
52 | while &i < max_steps {
53 | let affine_current = current.to_affine();
54 | let (x_decimal, y_decimal) = to_biguint_from_affine_point(&affine_current);
55 | baby_steps.insert((x_decimal, y_decimal), i.clone());
56 | current += g; // Move to the next point in the sequence by adding `g`
57 | i += BigUint::one(); // Increment scalar
58 | }
59 |
60 | // Giant-step phase: Compute `target_point - j * g^m` for j in the range [0, max_steps], and check if it matches a baby step.
61 | let giant_stride = scalar_mul(g, max_steps); // Compute `g^m`, where `m = max_steps`
62 | let mut current = *target_point;
63 |
64 | let mut j = BigUint::zero();
65 | while &j < max_steps {
66 | let affine_current = current.to_affine();
67 | let (x_decimal, y_decimal) = to_biguint_from_affine_point(&affine_current);
68 |
69 | // Check if the current giant step matches any of the stored baby steps
70 | if let Some(i) = baby_steps.get(&(x_decimal, y_decimal)) {
71 | // If match found, the result is `k = j * max_steps + i + start`
72 | let result = &j * max_steps + i + start;
73 | return Some(result);
74 | }
75 | current -= &giant_stride; // Move to the next giant step by subtracting `g^m`
76 | j += BigUint::one(); // Increment `j`
77 | }
78 |
79 | None // Return `None` if no match is found
80 | }
81 |
82 | /// Convert an elliptic curve point from affine coordinates to BigUint (x, y) coordinates.
83 | /// This is necessary because elliptic curve points are typically stored in compressed form.
84 | fn to_biguint_from_affine_point(point: &AffinePoint) -> (BigUint, BigUint) {
85 | let binding = point.to_bytes();
86 | let point_bytes = binding.as_slice();
87 |
88 | // The first byte is the prefix indicating the point format, the next 32 bytes are the x-coordinate,
89 | // and the remaining bytes represent the y-coordinate.
90 | let x_bytes = &point_bytes[1..33];
91 | let y_bytes = &point_bytes[33..];
92 |
93 | let x_decimal = BigUint::from_bytes_be(x_bytes); // Convert x-coordinate to BigUint
94 | let y_decimal = BigUint::from_bytes_be(y_bytes); // Convert y-coordinate to BigUint
95 |
96 | (x_decimal, y_decimal)
97 | }
--------------------------------------------------------------------------------
/src/services/key_search/pollards_rho.rs:
--------------------------------------------------------------------------------
1 | /*use k256::ProjectivePoint;
2 | use num_bigint::BigUint;
3 | use rand::{thread_rng, Rng};
4 | use std::sync::{Arc, Mutex};
5 | use std::thread;
6 | use num_traits::{Zero, One, ToPrimitive};
7 | use std::ops::{Add, Sub, Mul, Rem};
8 | use rand_bigint::RandBigInt;
9 |
10 | fn get_group_order() -> BigUint {
11 | // The group order of the secp256k1 curve used in k256
12 | let group_order_hex = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141";
13 | BigUint::parse_bytes(group_order_hex.as_bytes(), 16).unwrap()
14 | }
15 |
16 | fn point_mul(point: &ProjectivePoint, scalar: &BigUint) -> ProjectivePoint {
17 | let scalar_bytes = scalar.to_bytes_be();
18 |
19 | // Ensure scalar_bytes is 32 bytes
20 | let mut scalar_bytes_padded = [0u8; 32];
21 | let scalar_bytes_len = scalar_bytes.len();
22 | if scalar_bytes_len > 32 {
23 | panic!("Scalar too large");
24 | }
25 | scalar_bytes_padded[32 - scalar_bytes_len..].copy_from_slice(&scalar_bytes);
26 |
27 | // Use the multiply method that accepts a byte array
28 | point.multiply(&scalar_bytes_padded)
29 | }
30 |
31 | fn modinv(a: &BigUint, modulus: &BigUint) -> Option {
32 | // Extended Euclidean Algorithm
33 | let mut mn = (modulus.clone(), a.clone());
34 | let mut xy = (BigUint::zero(), BigUint::one());
35 |
36 | while mn.1 != BigUint::zero() {
37 | let q = &mn.0 / &mn.1;
38 | mn = (mn.1.clone(), &mn.0 - &q * &mn.1);
39 | xy = (xy.1.clone(), &xy.0 - &q * &xy.1);
40 | }
41 |
42 | if mn.0 == BigUint::one() {
43 | Some((xy.0 + modulus) % modulus)
44 | } else {
45 | None
46 | }
47 | }
48 |
49 | fn sub_mod(a: &BigUint, b: &BigUint, modulus: &BigUint) -> BigUint {
50 | if a >= b {
51 | (a - b) % modulus
52 | } else {
53 | (a + modulus - b) % modulus
54 | }
55 | }
56 |
57 | pub fn pollards_rho(
58 | target_point: &ProjectivePoint,
59 | g: &ProjectivePoint,
60 | num_threads: usize,
61 | ) -> Option {
62 | let modulus = get_group_order();
63 |
64 | let found_key = Arc::new(Mutex::new(None));
65 | let threads: Vec<_> = (0..num_threads)
66 | .map(|_| {
67 | let target_point = *target_point;
68 | let g = *g;
69 | let found_key = Arc::clone(&found_key);
70 |
71 | thread::spawn(move || {
72 | let mut rng = thread_rng();
73 |
74 | // Generate random a, b in [0, n-1]
75 | let a = rng.gen_biguint_below(&modulus);
76 | let b = rng.gen_biguint_below(&modulus);
77 |
78 | let mut x = point_mul(&g, &a) + point_mul(&target_point, &b);
79 | let mut a1 = a.clone();
80 | let mut b1 = b.clone();
81 |
82 | let mut x2 = x;
83 | let mut a2 = a1.clone();
84 | let mut b2 = b1.clone();
85 |
86 | loop {
87 | // Check if the key has been found by another thread
88 | if found_key.lock().unwrap().is_some() {
89 | break;
90 | }
91 |
92 | // Single step
93 | let (new_x, new_a1, new_b1) =
94 | update_point(&x, a1.clone(), b1.clone(), &g, &target_point, &modulus);
95 | x = new_x;
96 | a1 = new_a1;
97 | b1 = new_b1;
98 |
99 | // Double step
100 | for _ in 0..2 {
101 | let (new_x2, new_a2, new_b2) =
102 | update_point(&x2, a2.clone(), b2.clone(), &g, &target_point, &modulus);
103 | x2 = new_x2;
104 | a2 = new_a2;
105 | b2 = new_b2;
106 | }
107 |
108 | if x == x2 {
109 | let numerator = sub_mod(&a1, &a2, &modulus);
110 | let denominator = sub_mod(&b2, &b1, &modulus);
111 |
112 | if denominator.is_zero() {
113 | // Denominator is zero; restart with new random values
114 | break;
115 | }
116 |
117 | if let Some(inv_denominator) = modinv(&denominator, &modulus) {
118 | let k = (&numerator * &inv_denominator) % &modulus;
119 |
120 | // Verify the solution
121 | if point_mul(&g, &k) == *target_point {
122 | let mut found = found_key.lock().unwrap();
123 | *found = Some(k);
124 | break;
125 | }
126 | } else {
127 | // Inverse doesn't exist; restart with new random values
128 | break;
129 | }
130 | }
131 | }
132 | })
133 | })
134 | .collect();
135 |
136 | for t in threads {
137 | t.join().unwrap();
138 | }
139 |
140 | Arc::try_unwrap(found_key).ok().unwrap().lock().unwrap().clone()
141 | }
142 |
143 | fn update_point(
144 | point: &ProjectivePoint,
145 | a: BigUint,
146 | b: BigUint,
147 | g: &ProjectivePoint,
148 | target_point: &ProjectivePoint,
149 | modulus: &BigUint,
150 | ) -> (ProjectivePoint, BigUint, BigUint) {
151 | let x_coord = point.to_affine().x().unwrap().to_bytes();
152 | let x_int = BigUint::from_bytes_be(&x_coord);
153 |
154 | match (x_int.clone() % BigUint::from(3u8)).to_u8().unwrap() {
155 | 0 => {
156 | // Point = 2 * Point
157 | let new_point = point.double();
158 | let new_a = (a.clone() + a) % modulus;
159 | let new_b = (b.clone() + b) % modulus;
160 | (new_point, new_a, new_b)
161 | }
162 | 1 => {
163 | // Point = Point + g
164 | let new_point = point + g;
165 | let new_a = (a + BigUint::one()) % modulus;
166 | let new_b = b;
167 | (new_point, new_a, new_b)
168 | }
169 | 2 => {
170 | // Point = Point + target_point
171 | let new_point = point + target_point;
172 | let new_a = a;
173 | let new_b = (b + BigUint::one()) % modulus;
174 | (new_point, new_a, new_b)
175 | }
176 | _ => unreachable!(),
177 | }
178 | }
179 | */
--------------------------------------------------------------------------------
/src/services/key_search/keyripper.rs:
--------------------------------------------------------------------------------
1 | use log::{error, info, warn};
2 | use k256::elliptic_curve::FieldBytes;
3 | use hex::FromHex;
4 | use num_bigint::{BigUint, RandBigInt};
5 | use num_traits::{Num, One, ToPrimitive, Zero};
6 | extern crate secp256k1;
7 | use secp256k1::constants;
8 | use bitcoin::{Address, Network, PrivateKey, PublicKey};
9 | use bitcoin::secp256k1::{All, Secp256k1, SecretKey};
10 | use k256::{AffinePoint, EncodedPoint, ProjectivePoint, Scalar};
11 | use k256::ecdsa::{SigningKey, VerifyingKey};
12 | use std::collections::HashMap;
13 | use std::error::Error;
14 | use hex;
15 | use k256::elliptic_curve::group::GroupEncoding;
16 | use k256::elliptic_curve::point::AffineCoordinates;
17 | use k256::elliptic_curve::sec1::{FromEncodedPoint};
18 | use libsecp256k1::curve::Field;
19 | use num_traits::real::Real;
20 | use crate::utils::utils::{HardwareInfo};
21 | use crate::config::Config;
22 | use crate::data::Address as TargetAddress;
23 | use crate::services::key_search::math;
24 | use crate::services::key_search::bsgs::bsgs;
25 |
26 | use std::sync::{Arc, Mutex};
27 | use std::sync::mpsc;
28 | use std::thread;
29 | use rand::Rng;
30 | use reqwest::Client;
31 | use serde::Serialize;
32 | use tokio::runtime::Runtime;
33 |
34 |
35 | pub struct KeySearch {
36 | secp: Secp256k1,
37 | curve: EllipticCurve,
38 | }
39 |
40 | #[derive(Debug)]
41 | pub struct EllipticCurve {
42 | pub g: ProjectivePoint,
43 | pub order: [u8; 32], // BigUint
44 | }
45 |
46 | #[derive(Serialize)]
47 | pub struct Payload {
48 | pub _bit_range: String,
49 | pub _private_key_hex: String,
50 | pub _wif: String,
51 | pub _public_address: String,
52 | }
53 |
54 | impl KeySearch {
55 |
56 | pub fn new() -> Self {
57 | let curve = k256::Secp256k1::default();
58 | let g = ProjectivePoint::GENERATOR;
59 | let order = constants::CURVE_ORDER;
60 |
61 | let curve = EllipticCurve {
62 | g,
63 | order,
64 | };
65 |
66 | KeySearch {
67 | secp: Secp256k1::new(),
68 | curve,
69 | }
70 | }
71 |
72 | pub fn private_key_by_public_key(
73 | &self,
74 | hardware_info: &HardwareInfo,
75 | config: &Config,
76 | address: &TargetAddress,
77 | ) {
78 | let start_time = std::time::Instant::now();
79 |
80 | // Elliptic Curve Configuration SECP256k1
81 | let a = BigUint::from(0u32);
82 | let b = BigUint::from(7u32);
83 | let p = BigUint::from_str_radix(
84 | "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16
85 | ).unwrap();
86 |
87 | // Public key recovery
88 | let public_key_x = BigUint::from_str_radix(
89 | &address.public_key_hex.as_str()[2..], 16
90 | ).expect("Error converting public_key_x to whole number!");
91 |
92 | let mut y_square = (
93 | &public_key_x * &public_key_x * &public_key_x + &a * &public_key_x + &b
94 | ) % &p;
95 |
96 | let mut public_key_y = math::sqrt_mod_prime(&y_square, &p)
97 | .expect("Couldn't find a valid modular square root!");
98 |
99 | // Public Key Prefix Verification
100 | if (address.public_key_hex.as_str().starts_with("02") &&
101 | &public_key_y % 2u8 != BigUint::from(0u32)) ||
102 | (address.public_key_hex.as_str().starts_with("03") &&
103 | &public_key_y % 2u8 == BigUint::from(0u32)) {
104 | public_key_y = &p - &public_key_y;
105 | }
106 |
107 | // Creating the public key point on the curve
108 | let x_bytes = public_key_x.to_bytes_be();
109 | let y_bytes = public_key_y.to_bytes_be();
110 |
111 | let mut encoded_point = Vec::with_capacity(65);
112 | encoded_point.push(0x04); // Uncompressed Prefix
113 | encoded_point.extend_from_slice(&x_bytes);
114 | encoded_point.extend_from_slice(&y_bytes);
115 |
116 | let encoded_point = EncodedPoint::from_bytes(&encoded_point)
117 | .expect("Failed to create EncodedPoint");
118 | let target_public_key_point = ProjectivePoint::from_encoded_point(&encoded_point)
119 | .expect("Failed to create public key point");
120 |
121 | // Converting the hexadecimal range to decimal
122 | let start_range = BigUint::from_str_radix(
123 | address.private_key_range_start.as_str(), 16
124 | ).expect("Invalid Start Range");
125 |
126 | let end_range = BigUint::from_str_radix(
127 | address.private_key_range_end.as_str(), 16
128 | ).expect("Invalid End Range");
129 |
130 | let total_range = &end_range - &start_range + BigUint::one();
131 |
132 | // Subrange Size
133 | let subrange_size = BigUint::from(100_000_000_000u64);
134 |
135 | let current_position = Arc::new(Mutex::new(start_range.clone()));
136 | let target_public_key_point = Arc::new(target_public_key_point);
137 | let total_steps_tried = Arc::new(Mutex::new(BigUint::zero()));
138 | let private_key_integer = Arc::new(Mutex::new(None));
139 |
140 | let (tx, rx) = mpsc::channel();
141 | let mut threads = vec![];
142 |
143 | for _ in 0..config.num_threads {
144 | let tx = tx.clone();
145 | let current_position = Arc::clone(¤t_position);
146 | let end_range = end_range.clone();
147 | let subrange_size = subrange_size.clone();
148 | let target_public_key_point = Arc::clone(&target_public_key_point);
149 | let total_steps_tried = Arc::clone(&total_steps_tried);
150 | let private_key_integer = Arc::clone(&private_key_integer);
151 |
152 | let thread = thread::spawn(move || {
153 | loop {
154 | {
155 | if private_key_integer.lock().unwrap().is_some() {
156 | break;
157 | }
158 | }
159 |
160 | let (current_start, current_end) = {
161 | let mut pos = current_position.lock().unwrap();
162 | if *pos > end_range {
163 | break;
164 | }
165 |
166 | let current_start = pos.clone();
167 | let potential_end = ¤t_start + &subrange_size - BigUint::one();
168 |
169 | let current_end = if potential_end > end_range {
170 | end_range.clone()
171 | } else {
172 | potential_end
173 | };
174 |
175 | *pos = ¤t_end + BigUint::one();
176 |
177 | (current_start, current_end)
178 | };
179 |
180 | let interval_size = ¤t_end - ¤t_start + BigUint::one();
181 | let max_steps = interval_size.sqrt() + BigUint::one();
182 |
183 |
184 | println!(
185 | "[+] Thread {:?} searching: {} - {}",
186 | thread::current().id(), current_start, current_end
187 | );
188 |
189 | let key = bsgs(
190 | &target_public_key_point,
191 | &ProjectivePoint::GENERATOR,
192 | ¤t_start,
193 | &max_steps,
194 | );
195 |
196 | {
197 | let mut steps = total_steps_tried.lock().unwrap();
198 | *steps += &max_steps;
199 | }
200 |
201 | if let Some(found_key) = key {
202 | {
203 | let mut private_key = private_key_integer.lock().unwrap();
204 | *private_key = Some(found_key.clone());
205 | }
206 | tx.send(found_key.clone()).unwrap();
207 | break;
208 | }
209 | }
210 | });
211 | threads.push(thread);
212 | }
213 |
214 | drop(tx);
215 |
216 | if let Ok(key) = rx.recv() {
217 | let private_key_hex = format!("{:064x}", key);
218 |
219 | println!("\nPrivate Key Found! <{}>", private_key_hex);
220 |
221 | let payload = Payload {
222 | _bit_range: (&address.bit_range.as_str()).parse().unwrap(),
223 | _private_key_hex: private_key_hex.clone(),
224 | _wif: KeySearch::wif_by_private_key_hex(&private_key_hex),
225 | _public_address: self.compressed_public_key_by_private_key_hex(
226 | &private_key_hex).unwrap().to_string(),
227 | };
228 |
229 | if let Err(e) = self.server_bridge(
230 | &config.server_url, &config.api_auth_token, &payload) {
231 | eprintln!("Failed to send the data: {}", e);
232 | } else {
233 | println!("Data successfully sent to the server.");
234 | }
235 |
236 | } else {
237 | println!("Private key not found within the given range.");
238 | }
239 |
240 | for thread in threads {
241 | thread.join().unwrap();
242 | }
243 |
244 | println!("Elapsed time: {:?}", start_time.elapsed());
245 | println!("Total steps attempted: {}", *total_steps_tried.lock().unwrap());
246 | }
247 |
248 | pub fn public_key_address_by_private_key_hex(
249 | secp: Secp256k1,
250 | private_key_hex: &str,
251 | ) -> String {
252 | let private_key: PrivateKey =
253 | PrivateKey::from_slice(&hex::decode(private_key_hex).unwrap(), Network::Bitcoin).unwrap();
254 | let public_key: PublicKey = PublicKey::from_private_key(&secp, &private_key);
255 | let address: Address = Address::p2pkh(&public_key, Network::Bitcoin);
256 | address.to_string()
257 | }
258 |
259 | pub fn wif_by_private_key_hex(private_key_hex: &str) -> String {
260 | let private_key: PrivateKey =
261 | PrivateKey::from_slice(&hex::decode(private_key_hex).unwrap(), Network::Bitcoin).unwrap();
262 | private_key.to_wif()
263 | }
264 |
265 | pub fn compressed_public_key_by_private_key_hex(&self, private_key_hex: &str) -> Option {
266 | if private_key_hex.is_empty() {
267 | error!("No private key hexadecimal was provided!");
268 | return None;
269 | }
270 |
271 | let private_key_bytes = Vec::from_hex(private_key_hex).ok()?;
272 |
273 | let private_key_field_bytes = FieldBytes::::try_from(
274 | private_key_bytes.as_slice()
275 | ).ok()?;
276 |
277 | let signing_key = SigningKey::from_bytes(&private_key_field_bytes).ok()?;
278 |
279 | let verifying_key = VerifyingKey::from(&signing_key);
280 |
281 | let public_key_bytes = verifying_key.to_encoded_point(true).as_bytes().to_vec();
282 | let compressed_public_key_hex = hex::encode(public_key_bytes);
283 |
284 | Some(compressed_public_key_hex)
285 | }
286 |
287 | pub fn server_bridge(
288 | &self,
289 | url: &str,
290 | token: &str,
291 | payload: &Payload,
292 | ) -> Result> {
293 | let client = Client::new();
294 | let rt = Runtime::new()?;
295 |
296 | let response = rt.block_on(async {
297 | client.post(url)
298 | .json(&payload)
299 | .header("Authorization", format!("Bearer {}", token))
300 | .send()
301 | .await
302 | })?;
303 |
304 | Ok(response)
305 | }
306 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
keyripper
2 |
3 | **keyripper** is a powerful tool developed in Rust to assist in the recovery of Bitcoin private keys by leveraging the Baby-Step Giant-Step (BSGS) algorithm to solve the discrete logarithm problem on the secp256k1 elliptic curve. This project underscores the importance of robust cryptographic practices and provides insights into the mathematical foundations that secure blockchain technologies.
4 |
5 | ---
6 |
7 | **Mathematical Background Summary:**
8 |
9 | - **Elliptic Curves**: Key structures in cryptography, defined by the equation y² = x³ + ax + b with conditions to avoid singularities.
10 |
11 | - **secp256k1**: A specific elliptic curve used in Bitcoin, defined by y² = x³ + 7 over a 256-bit prime field.
12 |
13 | - **Key Points**:
14 | - **Generator Point (G)**: Starting point for key generation.
15 | - **Order (n)**: Number of distinct points generated from G, a large prime.
16 |
17 | - **Discrete Logarithm Problem (DLP)**: Challenge of finding the integer k such that Q = k * G. Security of systems like Bitcoin relies on the difficulty of solving DLP.
18 |
19 | - **Baby-Step Giant-Step (BSGS) Algorithm**: Efficient method for solving DLP, reducing complexity from O(n) to O(sqrt(n)).
20 |
21 | - **Baby Steps**: Compute and store points G, 2G, 3G, ..., mG.
22 | - **Giant Steps**: Compute Q - j * mG and check against the baby steps table for matches.
23 |
24 | - **Steps**:
25 | - **Initialization**: Choose m = ceiling(n) and create a hash table.
26 | - **Baby Steps Phase**: Store points in the hash table.
27 | - **Giant Steps Phase**: Check for matches and calculate k.
28 |
29 | - **Advantages of BSGS**: Efficient and deterministic.
30 |
31 | - **Limitations**: High memory usage and scalability issues for large n.
32 |
33 | - **Optimization Strategies**: Use hash tables, parallelization, and subrange splitting to improve performance.
34 |
35 | ---
36 |
37 | ## Features
38 |
39 | - **Efficient DLP Solver:** Implements the Baby-Step Giant-Step algorithm optimized for the secp256k1 curve.
40 | - **Multithreading Support:** Leverages multiple CPU cores to accelerate the search process.
41 | - **User-Friendly Configuration:** Allows customization of search ranges and subrange sizes.
42 | - **Integration with Google Colab:** Facilitates running the tool in cloud environments.
43 | - **Secure Key Handling:** Ensures safe management of sensitive cryptographic materials.
44 |
45 | ---
46 |
47 | ## Installation
48 |
49 | ### Prerequisites
50 |
51 | - **Rust Programming Language:** Ensure Rust is installed on your system. If not, follow the [Installing Rust](#installing-rust) section.
52 | - **Git:** Required for cloning the repository.
53 | - **Internet Connection:** Necessary for downloading dependencies.
54 |
55 | ### Installing Rust
56 |
57 | Rust is the primary language used for developing `keyripper`. Follow these steps to install Rust on your system:
58 |
59 | #### On Windows
60 |
61 | 1. **Download Rust Installer:**
62 |
63 | Visit [rustup.rs](https://rustup.rs/) and click on the Windows button to download the installer.
64 |
65 | 2. **Run the Installer:**
66 |
67 | Execute the downloaded `.exe` file and follow the on-screen instructions.
68 |
69 | 3. **Configure Your Current Shell:**
70 |
71 | After installation, open a new Command Prompt or PowerShell window to ensure that the Rust binaries are in your `PATH`.
72 |
73 | 4. **Verify the Installation:**
74 |
75 | ```bash
76 | rustc --version
77 | ```
78 |
79 | You should see output similar to:
80 |
81 | ```
82 | rustc 1.XX.X (commit hash)
83 | ```
84 |
85 | #### On Linux
86 |
87 | 1. **Install Rust via `rustup`:**
88 |
89 | Open your terminal and execute:
90 |
91 | ```bash
92 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
93 | ```
94 |
95 | 2. **Follow the On-Screen Instructions:**
96 |
97 | The script will guide you through the installation process. By default, it installs the latest stable version of Rust.
98 |
99 | 3. **Configure Your Current Shell:**
100 |
101 | After installation, configure your shell environment by running:
102 |
103 | ```bash
104 | source $HOME/.cargo/env
105 | ```
106 |
107 | 4. **Verify the Installation:**
108 |
109 | ```bash
110 | rustc --version
111 | ```
112 |
113 | You should see output similar to:
114 |
115 | ```
116 | rustc 1.XX.X (commit hash)
117 | ```
118 |
119 | ### Cloning the Repository
120 |
121 | Clone the `keyripper` repository from GitHub:
122 |
123 | ```bash
124 | git clone https://github.com/yourusername/keyripper.git
125 | ```
126 |
127 | Navigate to the project directory:
128 |
129 | ```bash
130 | cd keyripper
131 | ```
132 |
133 | ### Setting Up Configuration
134 |
135 | `keyripper` uses a `.env` file to configure essential variables. Create a `.env` file in the root directory of the project with the following content:
136 |
137 | ```env
138 | NUM_THREADS=4
139 | SUBRANGE_SIZE=1000000000
140 | SERVER_URL=https://yourserver.com/api
141 | API_AUTH_TOKEN=your_auth_token
142 | ```
143 |
144 | - `NUM_THREADS`: Number of threads to utilize for the search process.
145 | - `SUBRANGE_SIZE`: Size of each subrange for the search.
146 | - `SERVER_URL`: URL of the server to send the found key.
147 | - `API_AUTH_TOKEN`: Authentication token for the server.
148 |
149 | ### Building and Running the Project
150 |
151 | Build the project using Cargo, Rust's package manager and build system:
152 |
153 | #### On Windows and Linux
154 |
155 | ```bash
156 | cargo build --release
157 | cargo run
158 | ```
159 |
160 | This command compiles the project in release mode, optimizing for performance. The compiled binary will be located in the `target/release/` directory.
161 |
162 | ---
163 |
164 | **Example Output:**
165 |
166 | ```
167 | Start scalar: 780778204189001025463454792048743977763, Maximum steps: 10001
168 | [+] ThreadId(15) is processing the range: 780778204189001025463454792048743977763 - 780778204189001025463454792048743987763
169 | [-] ThreadId(18) is processing the range: 680564733841876926926749214863536422911 - 680564733841876926926749214863536422911
170 | ...
171 | Private Key Found! <1a2b3c4d5e6f...>
172 | Data successfully sent to the server.
173 | Elapsed time: 2m 35s
174 | Total steps attempted: 20002
175 | ```
176 |
177 | ---
178 |
179 | ## Running in Google Colab
180 |
181 | Google Colab provides a cloud-based environment to run `keyripper` without local setup. Follow these steps to execute `keyripper` in Google Colab:
182 |
183 | 1. **Open the Code in Google Colab**
184 |
185 | Click the badge below to open the project in Google Colab:
186 |
187 |