Skip to content

Commit

Permalink
Return path with decoded text (#80)
Browse files Browse the repository at this point in the history
* feat: decoder paths

* feat: updated tests

* feat: refactor bfs

* feat: reverse order of path and print it

* feat: ,

Co-authored-by: SkeletalDemise <29117662+SkeletalDemise@users.noreply.github.com>
  • Loading branch information
swanandx and SkeletalDemise authored Nov 16, 2022
1 parent 59ecbef commit f367dcb
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 27 deletions.
2 changes: 1 addition & 1 deletion src/decoders/crack_results.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub struct CrackResult {
/// Unencrypted text is what it looks like after.
/// if decoder failed, this will be None
pub unencrypted_text: Option<String>,
/// Deocder is the function we used to decode the text
/// Decoder is the function we used to decode the text
pub decoder: &'static str,
/// Checker which identified the text
pub checker_name: &'static str,
Expand Down
15 changes: 12 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use crate::config::Config;
/// perform_cracking("VGhlIG1haW4gZnVuY3Rpb24gdG8gY2FsbCB3aGljaCBwZXJmb3JtcyB0aGUgY3JhY2tpbmcu", Config::default());
/// assert!(true)
/// ```
pub fn perform_cracking(text: &str, config: Config) -> Option<String> {
pub fn perform_cracking(text: &str, config: Config) -> Option<Text> {
// Build a new search tree
// This starts us with a node with no parents
// let search_tree = searchers::Tree::new(text.to_string());
Expand All @@ -51,6 +51,15 @@ pub fn perform_cracking(text: &str, config: Config) -> Option<String> {
searchers::search_for_plaintext(text, max_depth)
}

/// Our custom text which also has path to get there
#[derive(Debug)]
pub struct Text {
/// text we got
pub text: String,
/// decoder used so far to get this text
pub path: Vec<&'static str>,
}

#[cfg(test)]
mod tests {
use super::perform_cracking;
Expand All @@ -71,7 +80,7 @@ mod tests {
let config = Config::default();
let result = perform_cracking("b2xsZWg=", config);
assert!(result.is_some());
assert!(result.unwrap() == "hello");
assert!(result.unwrap().text == "hello");
}
#[test]
fn test_perform_cracking_returns_failure() {
Expand All @@ -85,6 +94,6 @@ mod tests {
let config = Config::default();
let result = perform_cracking("aGVsbG8gdGhlcmUgZ2VuZXJhbA==", config);
assert!(result.is_some());
assert!(result.unwrap() == "hello there general")
assert!(result.unwrap().text == "hello there general")
}
}
6 changes: 5 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ fn main() {
let (text, config) = parse_cli_args();
let result = perform_cracking(&text, config);
match result {
Some(result) => println!("SUCCESSFUL {:?}", result),
Some(result) => {
println!("SUCCESSFUL 😁");
println!("PLAINTEXT: {:?}", result.text);
println!("DECODERS USED: {}", result.path.join(" -> "))
}
None => println!("FAILED 😭"),
}
}
59 changes: 38 additions & 21 deletions src/searchers/bfs.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
use log::trace;
use std::collections::HashSet;

use crate::{decoders::crack_results::CrackResult, filtration_system::MyResults};
use crate::{filtration_system::MyResults, Text};

/// Breadth first search is our search algorithm
/// https://en.wikipedia.org/wiki/Breadth-first_search
pub fn bfs(input: &str, max_depth: Option<u32>) -> Option<String> {
pub fn bfs(input: &str, max_depth: Option<u32>) -> Option<Text> {
let initial = Text {
text: input.to_string(),
path: vec![],
};
let mut seen_strings = HashSet::new();
// all strings to search through
let mut current_strings = vec![input.to_string()];
let mut current_strings = vec![initial];

let mut exit_result: Option<CrackResult> = None;
let mut exit_result: Option<Text> = None;

let mut curr_depth: u32 = 1; // as we have input string, so we start from 1

Expand All @@ -19,38 +23,51 @@ pub fn bfs(input: &str, max_depth: Option<u32>) -> Option<String> {
trace!("Number of potential decodings: {}", current_strings.len());
trace!("Current depth is {:?}; [ {:?} max ]", curr_depth, max_depth);

let mut new_strings: Vec<String> = vec![];
let mut new_strings: Vec<Text> = vec![];

current_strings
.into_iter()
.map(|current_string| super::perform_decoding(&current_string))
.try_for_each(|elem| match elem {
current_strings.into_iter().try_for_each(|current_string| {
let res = super::perform_decoding(&current_string.text);

match res {
// if it's Break variant, we have cracked the text successfully
// so just stop processing further.
MyResults::Break(res) => {
exit_result = Some(res);
let mut decoders_used = current_string.path;
decoders_used.push(res.decoder);
decoders_used.reverse();
let result_text = Text {
text: res.unencrypted_text.unwrap_or_default(),
path: decoders_used,
};

exit_result = Some(result_text);
None // short-circuits the iterator
}
MyResults::Continue(results_vec) => {
new_strings.extend(
results_vec
.into_iter()
.flat_map(|r| r.unencrypted_text)
.filter(|s| seen_strings.insert(s.clone())),
.map(|r| {
let mut decoders_used = current_string.path.clone();
decoders_used.push(r.decoder);
Text {
text: r.unencrypted_text.unwrap_or_default(),
path: decoders_used,
}
})
.filter(|s| seen_strings.insert(s.text.clone())),
);
Some(()) // indicate we want to continue processing
}
});
}
});

// if we find an element that matches our exit condition, return it!
// technically this won't check if the initial string matches our exit condition
// but this is a demo and i'll be lazy :P
if let Some(exit_res) = exit_result {
let exit_str = exit_res
.unencrypted_text
.expect("No unencrypted text even after checker succeed!");
trace!("Found exit string: {}", exit_str);
return Some(exit_str);
if exit_result.is_some() {
trace!("Found exit result: {:?}", exit_result);
return exit_result;
}

current_strings = new_strings;
Expand All @@ -74,7 +91,7 @@ mod tests {
// assert!(result.unwrap() == "CANARY: hello");
let result = bfs("b2xsZWg=", None);
assert!(result.is_some());
assert!(result.unwrap() == "hello");
assert!(result.unwrap().text == "hello");
}

// Vector storing the strings to perform decoding in next iteraion
Expand All @@ -94,7 +111,7 @@ mod tests {
None,
);
assert!(result.is_some());
assert_eq!(result.unwrap(), "https://www.google.com");
assert_eq!(result.unwrap().text, "https://www.google.com");
}

#[test]
Expand Down
3 changes: 2 additions & 1 deletion src/searchers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::checkers::athena::Athena;
use crate::checkers::checker_type::{Check, Checker};
use crate::checkers::CheckerTypes;
use crate::filtration_system::{filter_and_get_decoders, MyResults};
use crate::Text;
/// This module provides access to the breadth first search
/// which searches for the plaintext.
mod bfs;
Expand All @@ -27,7 +28,7 @@ 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, max_depth: Option<u32>) -> Option<String> {
pub fn search_for_plaintext(input: &str, max_depth: Option<u32>) -> Option<Text> {
// Change this to select which search algorithm we want to use.
bfs::bfs(input, max_depth)
}
Expand Down

0 comments on commit f367dcb

Please sign in to comment.