diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 60dc8e18..f6eb4a1b 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -20,6 +20,10 @@ pub struct Opts { /// Turn off human checker #[clap(short, long)] disable_human_checker: bool, + + /// Maximum number of decodings to perform on a string + #[clap(short, long, default_value = "10000")] + max_depth: u32, } /// Parse CLI Arguments turns a Clap Opts struct, seen above @@ -54,6 +58,7 @@ fn cli_args_into_config_struct(opts: Opts) -> (String, Config) { // default is false, we want default to be true human_checker_on: !opts.disable_human_checker, offline_mode: true, + max_depth: Some(opts.max_depth), }, ) } diff --git a/src/config/mod.rs b/src/config/mod.rs index e75293af..4f91afa8 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -25,6 +25,9 @@ pub struct Config { pub human_checker_on: bool, /// Is the program being run in offline mode? pub offline_mode: bool, + /// Number of times decoding shall be perfomed + /// Setting it to [`None`] means we will go infinitely in depth + pub max_depth: Option, } /// Cell for storing global Config @@ -58,6 +61,7 @@ impl Default for Config { lemmeknow_config: LEMMEKNOW_DEFAULT_CONFIG, human_checker_on: false, offline_mode: false, + max_depth: Some(10_000), // TODO: Set this to some better value } } } diff --git a/src/lib.rs b/src/lib.rs index e27497d4..3c97dc8b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,8 +46,9 @@ pub fn perform_cracking(text: &str, config: Config) -> Option { // let search_tree = searchers::Tree::new(text.to_string()); // Perform the search algorithm // It will either return a failure or success. + let max_depth = config.max_depth; config::set_global_config(config); - searchers::search_for_plaintext(text) + searchers::search_for_plaintext(text, max_depth) } #[cfg(test)] diff --git a/src/searchers/bfs.rs b/src/searchers/bfs.rs index f1b1d4f4..d9f4bda4 100644 --- a/src/searchers/bfs.rs +++ b/src/searchers/bfs.rs @@ -5,16 +5,19 @@ use crate::{decoders::crack_results::CrackResult, filtration_system::MyResults}; /// Breadth first search is our search algorithm /// https://en.wikipedia.org/wiki/Breadth-first_search -pub fn bfs(input: &str) -> Option { +pub fn bfs(input: &str, max_depth: Option) -> Option { let mut seen_strings = HashSet::new(); // all strings to search through let mut current_strings = vec![input.to_string()]; let mut exit_result: Option = None; + let mut curr_depth: u32 = 1; // as we have input string, so we start from 1 + // loop through all of the strings in the vec - while !current_strings.is_empty() { + while !current_strings.is_empty() && max_depth.map_or(true, |x| curr_depth <= x) { trace!("Number of potential decodings: {}", current_strings.len()); + trace!("Current depth is {:?}; [ {:?} max ]", curr_depth, max_depth); let mut new_strings: Vec = vec![]; @@ -51,6 +54,7 @@ pub fn bfs(input: &str) -> Option { } current_strings = new_strings; + curr_depth += 1; trace!("Refreshed the vector, {:?}", current_strings); } @@ -68,7 +72,7 @@ mod tests { // let result = bfs("Q0FOQVJZOiBoZWxsbw=="); // assert!(result.is_some()); // assert!(result.unwrap() == "CANARY: hello"); - let result = bfs("b2xsZWg="); + let result = bfs("b2xsZWg=", None); assert!(result.is_some()); assert!(result.unwrap() == "hello"); } @@ -84,9 +88,20 @@ mod tests { fn non_deterministic_like_behaviour_regression_test() { // text was too long, so we put \ to escape the \n // and put the rest of string on next line. - let result = bfs("UFRCRVRWVkNiRlZMTVVkYVVFWjZVbFZPU0\ - dGMU1WVlpZV2d4VkRVNWJWWnJjRzFVUlhCc1pYSlNWbHBPY0VaV1ZXeHJWRWd4TUZWdlZsWlg="); + let result = bfs( + "UFRCRVRWVkNiRlZMTVVkYVVFWjZVbFZPU0\ + dGMU1WVlpZV2d4VkRVNWJWWnJjRzFVUlhCc1pYSlNWbHBPY0VaV1ZXeHJWRWd4TUZWdlZsWlg=", + None, + ); assert!(result.is_some()); assert_eq!(result.unwrap(), "https://www.google.com"); } + + #[test] + fn max_depth_test() { + // text is encoded with base64 5 times + let result = bfs("VjFaV2ExWXlUWGxUYTJoUVVrUkJPUT09", Some(4)); + // It goes only upto depth 4, so it can't find the plaintext + assert!(result.is_none()); + } } diff --git a/src/searchers/mod.rs b/src/searchers/mod.rs index 3fdafe4d..89b5acba 100644 --- a/src/searchers/mod.rs +++ b/src/searchers/mod.rs @@ -27,9 +27,9 @@ mod bfs; /// We can return an Option? An Enum? And then match on that /// So if we return CrackSuccess we return /// Else if we return an array, we add it to the children and go again. -pub fn search_for_plaintext(input: &str) -> Option { +pub fn search_for_plaintext(input: &str, max_depth: Option) -> Option { // Change this to select which search algorithm we want to use. - bfs::bfs(input) + bfs::bfs(input, max_depth) } /// Performs the decodings by getting all of the decoders