├── .github └── workflows │ └── main.yml ├── .gitignore ├── Cargo.toml ├── LICENSE.md ├── README.md ├── bruteforce-macros ├── Cargo.toml ├── README.md └── src │ └── lib.rs ├── bruteforce ├── Cargo.toml ├── README.md ├── benches │ └── basic.rs └── src │ ├── charset.rs │ └── lib.rs └── docs └── CODE_OF_CONDUCT.md /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | lint: 7 | name: Lint 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout the source code 11 | uses: actions/checkout@master 12 | 13 | - name: Install Rust nightly 14 | run: | 15 | rustup install 1.80.0 16 | rustup default nightly 17 | rustup component add rustfmt 18 | - name: Run rustfmt 19 | run: cargo fmt -- --check 20 | test: 21 | name: Test 22 | runs-on: ${{ matrix.os }} 23 | strategy: 24 | matrix: 25 | os: [ubuntu-latest, windows-latest] 26 | cargo_flags: 27 | - "" 28 | include: 29 | # Integration tests are disabled on Windows as they take *way* too 30 | # long to pull the Docker image 31 | - os: windows-latest 32 | test_flags: --skip buildtest --skip integration 33 | steps: 34 | - name: Checkout the source code 35 | uses: actions/checkout@master 36 | 37 | - name: Install Rust nightly 38 | run: | 39 | rustup install 1.80.0 40 | rustup default nightly 41 | - name: Build bruteforce 42 | run: cargo build --all ${{ matrix.cargo_flags }} 43 | 44 | - name: Test bruteforce 45 | run: cargo test --all ${{ matrix.cargo_flags }} -- ${{ matrix.test_flags }} 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # .gitignore file of bruteforce-rs 2 | 3 | target 4 | Cargo.lock 5 | .vscode -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "bruteforce", 5 | "bruteforce-macros" 6 | ] -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2020 Robin Lindner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bruteforce 2 | 3 | [![Crates.io](https://img.shields.io/crates/v/bruteforce?style=flat-square)](https://crates.io/crates/bruteforce/) 4 | [![Crates.io](https://img.shields.io/crates/l/bruteforce?style=flat-square)](LICENSE.md) 5 | [![Codacy grade](https://img.shields.io/codacy/grade/6d381bdf373e4205bfd0d23876acb07d?style=flat-square)](https://app.codacy.com/manual/DeepRobin/bruteforce-rs/dashboard) 6 | 7 | [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/DeepRobin/bruteforce-rs/CI?style=flat-square)](https://github.com/deeprobin/bruteforce-rs/actions) 8 | [![GitHub issues](https://img.shields.io/github/issues/DeepRobin/bruteforce-rs?style=flat-square)](https://github.com/deeprobin/bruteforce-rs/issues) 9 | [![Discord](https://img.shields.io/discord/137221870452867072?style=flat-square)](https://discord.gg/mFHDMVe) 10 | 11 | This is the fastest string generation library for brute-forcing or similar. (Supports no-std) 12 | 13 | * [Docs - docs.rs](https://docs.rs/bruteforce/) 14 | * [Crate information - crates.io](https://crates.io/crates/bruteforce/) 15 | 16 | ## Add to your dependencies 17 | 18 | ```toml 19 | 20 | [dependencies] 21 | bruteforce = "0.3.0" 22 | 23 | ``` 24 | 25 | ## Example 26 | 27 | ```rust 28 | use bruteforce::BruteForce; 29 | let mut brute_forcer = BruteForce::new(charset!("ABCDEFGHIJKLMNOPQRSTUVWXYZ")); 30 | 31 | const password: &'static str = "PASS"; 32 | for s in brute_forcer { 33 | if s == password.to_string() { 34 | println!("Password cracked"); 35 | break; 36 | } 37 | } 38 | ``` 39 | 40 | ## Contribution 41 | 42 | If you want you can contribute. We need people, who write better documentation, optimize algorithms, implement more algorithms, finding bugs or submitting ideas. 43 | -------------------------------------------------------------------------------- /bruteforce-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bruteforce-macros" 3 | edition = "2021" 4 | version = "0.3.0" 5 | authors = ["Robin Lindner "] 6 | license = "MIT" 7 | homepage = "https://deeprobin.de/?reference=bruteforce" 8 | documentation = "https://docs.rs/bruteforce/" 9 | repository = "https://github.com/DeepRobin/bruteforce-rs.git" 10 | description = "Macro repository for bruteforce-rs" 11 | keywords = ["security", "iterator", "password", "algorithm", "no_std"] 12 | 13 | [lib] 14 | proc-macro = true 15 | 16 | [features] 17 | default = ["std"] 18 | std = ["no-std-compat/std", "no-std-compat/unstable"] 19 | 20 | [dependencies] 21 | no-std-compat = { version = "0.4.1", features = ["alloc"] } 22 | quote = "1.0.31" 23 | 24 | [package.metadata.docs.rs] 25 | all-features = true 26 | -------------------------------------------------------------------------------- /bruteforce-macros/README.md: -------------------------------------------------------------------------------- 1 | # bruteforce-macros 2 | 3 | This is the procedural macro crate of bruteforce-rs. 4 | -------------------------------------------------------------------------------- /bruteforce-macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | 3 | extern crate proc_macro; 4 | 5 | #[cfg(not(feature = "std"))] 6 | extern crate no_std_compat as std; 7 | 8 | use proc_macro::TokenStream; 9 | 10 | use std::collections::HashSet; 11 | use std::prelude::v1::*; // needed for std-compat 12 | 13 | use quote::quote; 14 | 15 | /// Converts an string into a slice 16 | #[doc(hidden)] 17 | #[proc_macro] 18 | pub fn charset_string(item: TokenStream) -> TokenStream { 19 | assert!(!item.is_empty(), "Cannot parse empty string as charset"); 20 | let chars: HashSet = item.to_string().chars().collect(); // hashset, because duplicates are not permitted 21 | let iter = chars.into_iter(); 22 | TokenStream::from(quote! { 23 | let slice = &[#(#iter),*]; 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /bruteforce/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bruteforce" 3 | description = "This is a no_std-compatible brute force/string generation rust-nightly library" 4 | version = "0.3.0" 5 | authors = ["Robin Lindner "] 6 | edition = "2021" 7 | license = "MIT" 8 | homepage = "https://deeprobin.de/?reference=bruteforce" 9 | documentation = "https://docs.rs/bruteforce/" 10 | repository = "https://github.com/DeepRobin/bruteforce-rs.git" 11 | readme = "../README.md" 12 | keywords = ["security", "iterator", "password", "algorithm", "no_std"] 13 | 14 | [badges] 15 | maintenance = { status = "passively-maintained" } 16 | 17 | [dependencies] 18 | no-std-compat = { version = "0.4.1", features = ["alloc"] } 19 | bruteforce-macros = { version = "0.3.0", path = "../bruteforce-macros", optional = true } 20 | 21 | [dev-dependencies] 22 | criterion = "0.5.1" 23 | 24 | [features] 25 | default = ["std", "constants", "bruteforce-macros"] 26 | constants = [] 27 | std = ["no-std-compat/std", "no-std-compat/unstable"] 28 | generators = [] 29 | 30 | [[bench]] 31 | name = "basic" 32 | harness = false 33 | -------------------------------------------------------------------------------- /bruteforce/README.md: -------------------------------------------------------------------------------- 1 | # bruteforce 2 | 3 | This is the main crate of bruteforce-rs. 4 | 5 | To read more about bruteforce-rs, visit [README.md](../README.md). 6 | -------------------------------------------------------------------------------- /bruteforce/benches/basic.rs: -------------------------------------------------------------------------------- 1 | use bruteforce::charset::Charset; 2 | use bruteforce::BruteForce; 3 | use criterion::{black_box, criterion_group, criterion_main, Criterion}; 4 | 5 | const BENCH_CHARS: Charset = Charset::new(&[ 6 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 7 | 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 8 | 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', 9 | '5', '6', '7', '8', '9', '!', '\"', '\'', '?', '\\', '#', '$', '§', '%', '&', '/', '(', ')', 10 | '=', '[', ']', '{', '}', '´', '`', '<', '>', '€', ',', '.', '-', '_', 11 | ]); 12 | 13 | fn bench_raw_next(c: &mut Criterion) { 14 | c.bench_function("bench_raw_next", |b| { 15 | let mut brute_forcer = BruteForce::new(black_box(BENCH_CHARS)); 16 | b.iter(|| { 17 | brute_forcer.raw_next(); 18 | }); 19 | }); 20 | } 21 | 22 | fn bench_next(c: &mut Criterion) { 23 | c.bench_function("bench_next", |b| { 24 | let mut brute_forcer = BruteForce::new(black_box(BENCH_CHARS)); 25 | b.iter(|| brute_forcer.next()); 26 | }); 27 | } 28 | 29 | fn bench_new(c: &mut Criterion) { 30 | c.bench_function("bench_new", |b| { 31 | b.iter(|| BruteForce::new(black_box(BENCH_CHARS))); 32 | }); 33 | } 34 | 35 | fn bench_charset_new(c: &mut Criterion) { 36 | c.bench_function("bench_charset_new", |b| { 37 | b.iter(|| { 38 | Charset::new(black_box(&[ 39 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 40 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 41 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 42 | 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '\"', 43 | '\'', '?', '\\', '#', '$', '§', '%', '&', '/', '(', ')', '=', '[', ']', '{', '}', 44 | '´', '`', '<', '>', '€', ',', '.', '-', '_', 45 | ])); 46 | }) 47 | }); 48 | } 49 | 50 | fn bench_charset_new_by_str(c: &mut Criterion) { 51 | c.bench_function("bench_charset_new_by_str", |b| { 52 | b.iter(|| Charset::new_by_str( 53 | black_box( 54 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!\"\'?\\$§%&/()=[]{}´`<>€,.-_" 55 | ) 56 | )); 57 | }); 58 | } 59 | 60 | fn bench_charset_concat(c: &mut Criterion) { 61 | c.bench_function("bench_charset_concat", |b| { 62 | let c1 = Charset::new_by_str(black_box("ABCDEFGHIJKLMNOPQRSTUVWXYZ")); 63 | let c2 = Charset::new_by_str(black_box("abcdefghijklmnopqrstuvwxyz0123456789")); 64 | 65 | b.iter(|| c1.concat(&c2)); 66 | }); 67 | } 68 | 69 | fn bench_charset_by_range(c: &mut Criterion) { 70 | c.bench_function("bench_charset_by_range", |b| { 71 | b.iter(|| Charset::by_char_range(black_box('a'..='z'))); 72 | }); 73 | } 74 | 75 | fn bench_charset_to_string(c: &mut Criterion) { 76 | c.bench_function("bench_charset_to_string", |b| { 77 | b.iter(|| black_box(BENCH_CHARS).to_string()); 78 | }); 79 | } 80 | 81 | criterion_group!( 82 | all, 83 | bench_raw_next, 84 | bench_next, 85 | bench_new, 86 | bench_charset_new, 87 | bench_charset_new_by_str, 88 | bench_charset_concat, 89 | bench_charset_by_range, 90 | bench_charset_to_string, 91 | ); 92 | 93 | criterion_main!(all); 94 | -------------------------------------------------------------------------------- /bruteforce/src/charset.rs: -------------------------------------------------------------------------------- 1 | use std::prelude::v1::*; // needed for std-compat 2 | 3 | use std::borrow::Cow; 4 | use std::ops::Index; 5 | use std::ops::RangeInclusive; 6 | use std::slice::Iter; 7 | 8 | #[cfg(feature = "bruteforce-macros")] 9 | #[macro_export] 10 | /// The charset macro generates a `Charset` by a string literal 11 | macro_rules! charset { 12 | ($string:expr) => {{ 13 | charset_string!($string); 14 | Charset::new(slice) 15 | }}; 16 | } 17 | 18 | /// The charset representation for bruteforce 19 | /// 20 | /// # Example 21 | /// 22 | /// ```rust 23 | /// use bruteforce::charset::Charset; 24 | /// 25 | /// let uppercase_alphabeth = Charset::from("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); 26 | /// ``` 27 | #[derive(Debug, Clone)] 28 | pub struct Charset<'a> { 29 | chars: Cow<'a, [char]>, 30 | } 31 | 32 | impl<'a> Charset<'a> { 33 | /// This creates a new charset by a defined slice of chars in compile-time 34 | /// 35 | /// # Arguments 36 | /// 37 | /// * `charset` - A char slice which contains the chars 38 | /// 39 | /// # Examples 40 | /// 41 | /// ```rust 42 | /// use bruteforce::charset::Charset; 43 | /// 44 | /// Charset::new(&['A', 'B', 'C', 'D', 'E']); 45 | /// ``` 46 | pub const fn new(charset: &[char]) -> Charset { 47 | assert!( 48 | !charset.is_empty(), 49 | "The &[char] must contain at least one character" 50 | ); 51 | Charset { 52 | chars: Cow::Borrowed(charset), 53 | } 54 | } 55 | 56 | /// This function creates a new charset by a defined char range 57 | /// 58 | /// # Arguments 59 | /// 60 | /// * `range` - A char range 61 | /// 62 | /// # Examples 63 | /// 64 | /// ```rust 65 | /// use bruteforce::charset::Charset; 66 | /// 67 | /// Charset::by_char_range('a'..='z'); // = abcdefghijklmnopqrstuvwxyz 68 | /// ``` 69 | pub fn by_char_range(range: RangeInclusive) -> Charset<'a> { 70 | let range_as_u32 = (*range.start() as u32)..=(*range.end() as u32); 71 | let vec = range_as_u32.filter_map(std::char::from_u32).collect(); 72 | Charset { 73 | chars: Cow::Owned(vec), 74 | } 75 | } 76 | 77 | /// This function creates a new charset by a defined char range ignoring utf-validity 78 | /// 79 | /// # Arguments 80 | /// 81 | /// * `range` - A unchecked char range 82 | /// 83 | /// # Safety 84 | /// 85 | /// This function is unsafe, as it may construct invalid `char` values. 86 | /// 87 | /// For a safe version of this function, see the [`by_char_range`] function. 88 | /// 89 | /// [`by_char_range`]: fn.by_char_range.html 90 | /// 91 | /// # Examples 92 | /// 93 | /// ```rust 94 | /// use bruteforce::charset::Charset; 95 | /// 96 | /// let charset = unsafe { Charset::by_char_range_unchecked('a'..='z') }; 97 | /// ``` 98 | pub unsafe fn by_char_range_unchecked(range: RangeInclusive) -> Charset<'a> { 99 | let range_as_u32 = (*range.start() as u32)..=(*range.end() as u32); 100 | let vec = range_as_u32 101 | .map(|c| std::char::from_u32_unchecked(c)) 102 | .collect(); 103 | Charset { 104 | chars: Cow::Owned(vec), 105 | } 106 | } 107 | 108 | /// This function creates a charset by &str 109 | /// 110 | /// # Arguments 111 | /// 112 | /// * `s` - The char slice from `new` but easier to write 113 | /// 114 | /// # Examples 115 | /// 116 | /// ```rust 117 | /// use bruteforce::charset::Charset; 118 | /// 119 | /// Charset::new_by_str("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); 120 | /// 121 | /// // But it is recommended to write: 122 | /// Charset::from("ABCDEFGHIJKLMNOPRSTUVWXYZ"); 123 | /// ``` 124 | pub fn new_by_str(s: &str) -> Charset<'a> { 125 | assert!( 126 | !s.is_empty(), 127 | "The string must contain at least one character" 128 | ); 129 | let vec = s.chars().collect::>(); 130 | Charset { 131 | chars: Cow::Owned(vec), 132 | } 133 | } 134 | 135 | /// This function concat's 2 charsets 136 | /// 137 | /// # Arguments 138 | /// 139 | /// `self` 140 | /// 141 | /// `other` - Pointer to other charset 142 | /// 143 | /// # Example 144 | /// 145 | /// ```rust 146 | /// use bruteforce::charset::Charset; 147 | /// 148 | /// let foo = Charset::from("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); 149 | /// let bar = Charset::from("abcdefghijklmnopqrstuvwxyz"); 150 | /// let result = foo.concat(&bar); 151 | /// ``` 152 | pub fn concat(&self, other: &Charset) -> Charset<'a> { 153 | let mut s = self.clone(); 154 | for &ch in other.iter() { 155 | if !s.chars.contains(&ch) { 156 | s.chars.to_mut().push(ch); 157 | } 158 | } 159 | s 160 | } 161 | 162 | /// This function returns the length of the internal char slice 163 | /// 164 | /// # Example 165 | /// ```rust 166 | /// use bruteforce::charset::Charset; 167 | /// 168 | /// let charset = Charset::from("ABCDEF"); 169 | /// charset.len(); // = 6 170 | /// ``` 171 | #[inline] 172 | pub fn len(&self) -> usize { 173 | self.chars.len() 174 | } 175 | 176 | /// If the length of the internal char slice is zero, this will return true 177 | /// 178 | /// This function returns in all cases true, because the minimum size of the internal slice is 1 179 | #[inline] 180 | #[cold] 181 | pub fn is_empty(&self) -> bool { 182 | self.chars.is_empty() 183 | } 184 | 185 | /// This function returns the immutable iterator of the internal char slice 186 | #[inline] 187 | pub fn iter(&self) -> Iter<'_, char> { 188 | self.chars.iter() 189 | } 190 | } 191 | 192 | impl Index for Charset<'_> { 193 | type Output = char; 194 | 195 | #[inline] 196 | fn index(&self, index: usize) -> &Self::Output { 197 | &self.chars[index] 198 | } 199 | } 200 | 201 | impl<'a> From<&'a str> for Charset<'_> { 202 | fn from(input: &'a str) -> Self { 203 | Self::new_by_str(input) 204 | } 205 | } 206 | 207 | impl From for Charset<'_> { 208 | fn from(s: String) -> Self { 209 | s.as_str().into() 210 | } 211 | } 212 | 213 | impl std::fmt::Display for Charset<'_> { 214 | fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { 215 | self.chars.iter().try_for_each(|&c| write!(fmt, "{}", c)) 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /bruteforce/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This is the documentation for the no-std compatible `bruteforce` crate 2 | 3 | #![crate_name = "bruteforce"] 4 | #![feature(test, coroutines, proc_macro_hygiene)] 5 | #![cfg_attr(not(feature = "std"), no_std)] 6 | 7 | #[cfg(not(feature = "std"))] 8 | extern crate no_std_compat as std; 9 | 10 | #[cfg(feature = "bruteforce-macros")] 11 | extern crate bruteforce_macros; 12 | 13 | pub mod charset; 14 | 15 | #[cfg(feature = "generators")] 16 | use std::ops::{Generator, GeneratorState}; 17 | #[cfg(feature = "generators")] 18 | use std::pin::Pin; 19 | use std::prelude::v1::*; 20 | 21 | use charset::Charset; 22 | 23 | /// Represents a brute-forcing instance 24 | #[derive(Debug, Clone)] 25 | pub struct BruteForce<'a> { 26 | /// Represents the charset of the brute-forcer 27 | pub chars: Charset<'a>, 28 | 29 | /// This is the current string 30 | pub current: String, 31 | 32 | /// Reversed representation of current where each element is an index of charset 33 | raw_current: Vec, 34 | } 35 | 36 | impl<'a> BruteForce<'a> { 37 | /// Returns a brute forcer with default settings 38 | /// 39 | /// # Arguments 40 | /// 41 | /// * `charset` - A char array that contains all chars to be tried 42 | /// 43 | /// # Example 44 | /// 45 | /// ```rust 46 | /// use bruteforce::BruteForce; 47 | /// use bruteforce::charset::Charset; 48 | /// const CHARSET: Charset = Charset::new(&['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']); 49 | /// let mut brute_forcer = BruteForce::new(CHARSET); 50 | /// 51 | /// const password: &'static str = "PASS"; 52 | /// for s in brute_forcer { 53 | /// if s == password.to_string() { 54 | /// println!("Password cracked"); 55 | /// break; 56 | /// } 57 | /// } 58 | /// ``` 59 | pub fn new(charset: Charset) -> BruteForce { 60 | BruteForce { 61 | chars: charset, 62 | current: String::default(), 63 | // Maybe the answer is an empty string? 64 | raw_current: vec![], 65 | } 66 | } 67 | 68 | /// Returns a brute forcer skipping some letters 69 | /// 70 | /// # Arguments 71 | /// 72 | /// * `charset` - A char array that contains all chars to be tried 73 | /// * `start` - E.g. the known password length 74 | /// 75 | /// # Example 76 | /// 77 | /// ```rust 78 | /// // This example will take less time, because we know the password length 79 | /// use bruteforce::BruteForce; 80 | /// use bruteforce::charset::Charset; 81 | /// const CHARSET: Charset = Charset::new(&['A', 'B', 'C', 'P', 'S']); // all possible characters 82 | /// let mut brute_forcer = BruteForce::new_at(CHARSET, 4); 83 | /// 84 | /// const password: &'static str = "PASS"; 85 | /// for s in brute_forcer { 86 | /// if s == password.to_string() { 87 | /// println!("Password cracked"); 88 | /// break; 89 | /// } 90 | /// } 91 | /// ``` 92 | pub fn new_at(charset: Charset, start: usize) -> BruteForce { 93 | BruteForce { 94 | chars: charset, 95 | current: String::default(), 96 | raw_current: (0..start).map(|_| 0).collect::>(), 97 | } 98 | } 99 | 100 | /// Returns a brute forcer skipping some text 101 | /// 102 | /// # Arguments 103 | /// 104 | /// * `charset` - A char array that contains all chars to be tried 105 | /// * `start_string` - A string 106 | /// 107 | /// # Example 108 | /// 109 | /// ```rust 110 | /// // This could be useful if we want to save our brute force progress and resume it later 111 | /// use bruteforce::BruteForce; 112 | /// use bruteforce::charset::Charset; 113 | /// const CHARSET: Charset = Charset::new(&['A', 'B', 'C', 'P', 'S']); // all possible characters 114 | /// let mut brute_forcer = BruteForce::new_by_start_string(CHARSET, "CCCC".to_string()); 115 | /// 116 | /// const password: &'static str = "PASS"; 117 | /// for s in brute_forcer { 118 | /// if s == password.to_string() { 119 | /// println!("Password cracked"); 120 | /// break; 121 | /// } 122 | /// } 123 | /// ``` 124 | pub fn new_by_start_string(charset: Charset, start_string: String) -> BruteForce { 125 | BruteForce { 126 | current: String::default(), 127 | raw_current: start_string 128 | .chars() 129 | .rev() 130 | .map(|c1| charset.iter().position(|&c2| c1 == c2)) 131 | .collect::>>() 132 | .expect("characters in start_string must exist in charset"), 133 | // assigning charset to chars must happen after it is used by .map() 134 | chars: charset, 135 | } 136 | } 137 | 138 | /// This returns the next element without unnecessary boxing in a Option 139 | pub fn raw_next(&mut self) -> &str { 140 | let cur = &mut self.current; 141 | let chars = &self.chars; 142 | 143 | cur.clear(); 144 | cur.extend(self.raw_current.iter().rev().map(|&i| { 145 | assert!(i < chars.len(), "Bug: Invalid character index"); 146 | chars[i] 147 | })); 148 | 149 | // "Add" 1 to self.raw_current 150 | let mut carryover = true; 151 | let chars_len_m = self.chars.len() - 1; 152 | for i in self.raw_current.iter_mut() { 153 | if *i == chars_len_m { 154 | *i = 0; 155 | } else { 156 | *i += 1; 157 | carryover = false; 158 | break; 159 | } 160 | } 161 | if carryover { 162 | self.raw_current.push(0); 163 | } 164 | 165 | &self.current 166 | } 167 | } 168 | 169 | impl<'a> Iterator for BruteForce<'a> { 170 | type Item = String; 171 | 172 | fn next(&mut self) -> Option { 173 | Some(self.raw_next().to_string()) 174 | } 175 | } 176 | 177 | #[cfg(feature = "generators")] 178 | impl Generator for Pin<&mut BruteForce<'_>> { 179 | type Yield = String; 180 | type Return = (); 181 | 182 | fn resume(self: Pin<&mut Self>) -> GeneratorState { 183 | GeneratorState::Yielded(self.get_mut().raw_next().to_string()) 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /docs/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at robin@deeprobin.de. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | --------------------------------------------------------------------------------