ares/decoders/
base58_ripple_decoder.rs

1use crate::checkers::CheckerTypes;
2use crate::decoders::interface::check_string_success;
3
4use super::crack_results::CrackResult;
5///! Decodes a base58 ripple string
6///! Performs error handling and returns a string
7///! Call base58_ripple_decoder.crack to use. It returns option<String> and check with
8///! `result.is_some()` to see if it returned okay.
9///
10use super::interface::Crack;
11use super::interface::Decoder;
12
13use log::{debug, info, trace};
14
15/// The Base58_ripple decoder, call:
16/// `let base58_ripple_decoder = Decoder::<Base58RippleDecoder>::new()` to create a new instance
17/// And then call:
18/// `result = base58_ripple_decoder.crack(input)` to decode a base58_ripple string
19/// The struct generated by new() comes from interface.rs
20/// ```
21/// use ares::decoders::base58_ripple_decoder::{Base58RippleDecoder};
22/// use ares::decoders::interface::{Crack, Decoder};
23/// use ares::checkers::{athena::Athena, CheckerTypes, checker_type::{Check, Checker}};
24///
25/// let decode_base58_ripple = Decoder::<Base58RippleDecoder>::new();
26/// let athena_checker = Checker::<Athena>::new();
27/// let checker = CheckerTypes::CheckAthena(athena_checker);
28///
29/// let result = decode_base58_ripple.crack("StVrDLaUATiyKyV", &checker).unencrypted_text;
30/// assert!(result.is_some());
31/// assert_eq!(result.unwrap()[0], "hello world");
32/// ```
33pub struct Base58RippleDecoder;
34
35impl Crack for Decoder<Base58RippleDecoder> {
36    fn new() -> Decoder<Base58RippleDecoder> {
37        Decoder {
38            name: "Base58 Ripple",
39            description: "Base58 is a group of binary-to-text encoding schemes that represent binary data (more specifically, a sequence of 8-bit bytes) in an ASCII string format by translating the data into a radix-32 representation.",
40            link: "https://en.wikipedia.org/wiki/Base58",
41            tags: vec!["base58_ripple", "base58", "ripple", "cryptocurrency", "decoder", "base"],
42            popularity: 0.8,
43            phantom: std::marker::PhantomData,
44        }
45    }
46
47    /// This function does the actual decoding
48    /// It returns an Option<string> if it was successful
49    /// Else the Option returns nothing and the error is logged in Trace
50    fn crack(&self, text: &str, checker: &CheckerTypes) -> CrackResult {
51        trace!("Trying Base58_ripple with text {:?}", text);
52        let decoded_text = decode_base58_ripple_no_error_handling(text);
53        let mut results = CrackResult::new(self, text.to_string());
54
55        if decoded_text.is_none() {
56            debug!("Failed to decode base58_ripple because Base58RippleDecoder::decode_base58_ripple_no_error_handling returned None");
57            return results;
58        }
59
60        let decoded_text = decoded_text.unwrap();
61        if !check_string_success(&decoded_text, text) {
62            info!(
63                "Failed to decode base58_ripple because check_string_success returned false on string {}",
64                decoded_text
65            );
66            return results;
67        }
68
69        let checker_result = checker.check(&decoded_text);
70        results.unencrypted_text = Some(vec![decoded_text]);
71
72        results.update_checker(&checker_result);
73
74        results
75    }
76    /// Gets all tags for this decoder
77    fn get_tags(&self) -> &Vec<&str> {
78        &self.tags
79    }
80    /// Gets the name for the current decoder
81    fn get_name(&self) -> &str {
82        self.name
83    }
84}
85
86/// helper function
87fn decode_base58_ripple_no_error_handling(text: &str) -> Option<String> {
88    // Runs the code to decode base58_ripple
89    // Doesn't perform error handling, call from_base58_ripple
90    if let Ok(decoded_text) = bs58::decode(text)
91        .with_alphabet(bs58::Alphabet::RIPPLE)
92        .into_vec()
93    {
94        return Some(String::from_utf8_lossy(&decoded_text).to_string());
95    }
96    None
97}
98
99#[cfg(test)]
100mod tests {
101    use super::Base58RippleDecoder;
102    use crate::{
103        checkers::{
104            athena::Athena,
105            checker_type::{Check, Checker},
106            CheckerTypes,
107        },
108        decoders::interface::{Crack, Decoder},
109    };
110
111    // helper for tests
112    fn get_athena_checker() -> CheckerTypes {
113        let athena_checker = Checker::<Athena>::new();
114        CheckerTypes::CheckAthena(athena_checker)
115    }
116
117    #[test]
118    fn successful_decoding() {
119        let base58_ripple_decoder = Decoder::<Base58RippleDecoder>::new();
120
121        let result = base58_ripple_decoder.crack("StVrDLaUATiyKyV", &get_athena_checker());
122        let decoded_str = &result
123            .unencrypted_text
124            .expect("No unencrypted text for base58_ripple");
125        assert_eq!(decoded_str[0], "hello world");
126    }
127
128    #[test]
129    fn base58_ripple_decode_empty_string() {
130        // Bsae58_ripple returns an empty string, this is a valid base58_ripple string
131        // but returns False on check_string_success
132        let base58_ripple_decoder = Decoder::<Base58RippleDecoder>::new();
133        let result = base58_ripple_decoder
134            .crack("", &get_athena_checker())
135            .unencrypted_text;
136        assert!(result.is_none());
137    }
138
139    #[test]
140    fn base58_ripple_decode_handles_panics() {
141        let base58_ripple_decoder = Decoder::<Base58RippleDecoder>::new();
142        let result = base58_ripple_decoder
143            .crack(
144                "hello my name is panicky mc panic face!",
145                &get_athena_checker(),
146            )
147            .unencrypted_text;
148        if result.is_some() {
149            panic!("Decode_base58_ripple did not return an option with Some<t>.")
150        } else {
151            // If we get here, the test passed
152            // Because the base58_ripple_decoder.crack function returned None
153            // as it should do for the input
154            assert_eq!(true, true);
155        }
156    }
157
158    #[test]
159    fn base58_ripple_handle_panic_if_empty_string() {
160        let base58_ripple_decoder = Decoder::<Base58RippleDecoder>::new();
161        let result = base58_ripple_decoder
162            .crack("", &get_athena_checker())
163            .unencrypted_text;
164        if result.is_some() {
165            assert_eq!(true, true);
166        }
167    }
168
169    #[test]
170    fn base58_ripple_work_if_string_not_base58_ripple() {
171        // You can base58_ripple decode a string that is not base58_ripple
172        // This string decodes to:
173        // ```.ée¢
174        // (uÖ²```
175        // https://gchq.github.io/CyberChef/#recipe=From_Base58('A-Za-z0-9%2B/%3D',true)&input=aGVsbG8gZ29vZCBkYXkh
176        let base58_ripple_decoder = Decoder::<Base58RippleDecoder>::new();
177        let result = base58_ripple_decoder
178            .crack("hello good day!", &get_athena_checker())
179            .unencrypted_text;
180        if result.is_some() {
181            assert_eq!(true, true);
182        }
183    }
184
185    #[test]
186    fn base58_ripple_handle_panic_if_emoji() {
187        let base58_ripple_decoder = Decoder::<Base58RippleDecoder>::new();
188        let result = base58_ripple_decoder
189            .crack("😂", &get_athena_checker())
190            .unencrypted_text;
191        if result.is_some() {
192            assert_eq!(true, true);
193        }
194    }
195}