ptero/method/complex/
extended_line.rs

1//! # Description
2//!
3//! This method implements the *Extended Line* steganography algorithm. It consist of three
4//! simpler methods:
5//! * [RandomWhitespaceMethod](crate::method::random_whitespace::RandomWhitespaceMethod),
6//! * [LineExtendMethod](crate::method::line_extend::LineExtendMethod),
7//! * [TrailingWhitespaceMethod](crate::trailing_whitespace::TrailingWhitespaceMethod).
8//!
9//! For more info read docs on each one of the above encoders.
10
11use crate::{
12    context::{PivotByLineContext, PivotByRawLineContext},
13    impl_complex_decoder, impl_complex_encoder,
14    method::{line_extend, random_whitespace, trailing_whitespace, Method},
15};
16
17type ExtendedLineSubmethod = Box<dyn Method<PivotByLineContext, PivotByRawLineContext>>;
18
19/// Structure representing the Extended Line algorithm.
20/// Contains the vector of used methods. Uses macros to implement the required traits.
21pub struct ExtendedLineMethod {
22    methods: Vec<ExtendedLineSubmethod>,
23}
24
25impl Default for ExtendedLineMethod {
26    fn default() -> Self {
27        ExtendedLineMethodBuilder::new().build()
28    }
29}
30
31#[derive(Debug, PartialEq)]
32pub enum ExtendedLineMethodVariant {
33    Variant1,
34    Variant2,
35    Variant3,
36}
37
38pub struct ExtendedLineMethodBuilder {
39    variant: ExtendedLineMethodVariant,
40}
41
42impl ExtendedLineMethodBuilder {
43    pub fn new() -> Self {
44        ExtendedLineMethodBuilder {
45            variant: ExtendedLineMethodVariant::Variant1,
46        }
47    }
48
49    pub fn variant(mut self, variant: ExtendedLineMethodVariant) -> Self {
50        self.variant = variant;
51        self
52    }
53
54    fn select_methods(&self) -> Vec<ExtendedLineSubmethod> {
55        let indices = match self.variant {
56            ExtendedLineMethodVariant::Variant1 => &[0, 1, 2],
57            ExtendedLineMethodVariant::Variant2 => &[1, 0, 2],
58            ExtendedLineMethodVariant::Variant3 => &[1, 2, 0],
59        };
60
61        indices
62            .iter()
63            .map(|i| {
64                let method: ExtendedLineSubmethod = match i {
65                    0 => Box::new(random_whitespace::RandomWhitespaceMethod::default()),
66                    1 => Box::new(line_extend::LineExtendMethod::default()),
67                    _ => Box::new(trailing_whitespace::TrailingWhitespaceMethod::default()),
68                };
69                method
70            })
71            .collect()
72    }
73
74    pub fn build(&self) -> ExtendedLineMethod {
75        ExtendedLineMethod {
76            methods: self.select_methods(),
77        }
78    }
79}
80
81impl_complex_encoder!(ExtendedLineMethod, PivotByLineContext);
82impl_complex_decoder!(ExtendedLineMethod, PivotByRawLineContext);
83
84impl Method<PivotByLineContext, PivotByRawLineContext> for ExtendedLineMethod {
85    fn method_name(&self) -> String {
86        format!(
87            "ExtendedLineMethod({},{},{})",
88            self.methods[0].method_name(),
89            self.methods[1].method_name(),
90            self.methods[2].method_name(),
91        )
92    }
93}
94
95#[allow(unused_imports)]
96mod test {
97    use std::error::Error;
98
99    use crate::{
100        binary::BitIterator,
101        context::{PivotByLineContext, PivotByRawLineContext},
102        decoder::Decoder,
103        encoder::Encoder,
104        method::Method,
105    };
106
107    use super::{ExtendedLineMethod, ExtendedLineMethodBuilder, ExtendedLineMethodVariant};
108
109    #[test]
110    fn encodes_text_data() -> Result<(), Box<dyn Error>> {
111        let cover_input = "a b c".repeat(5);
112        let data_input = "a";
113        let pivot: usize = 4;
114
115        let mut data_iterator = BitIterator::new(&data_input.as_bytes());
116        let method = ExtendedLineMethod::default();
117        let mut context = PivotByLineContext::new(&cover_input, pivot);
118        let stego_text = method.encode(&mut context, &mut data_iterator, None)?;
119
120        assert_eq!(&stego_text, "a b ca \nb ca\nb ca b\nca b\nc \n");
121        Ok(())
122    }
123
124    #[test]
125    fn encodes_binary_data() -> Result<(), Box<dyn Error>> {
126        let cover_input = "a b c ".repeat(5);
127        let data_input: Vec<u8> = vec![0b11111111];
128        let pivot: usize = 3;
129
130        let mut data_iterator = BitIterator::new(&data_input);
131        let method = ExtendedLineMethod::default();
132        let mut context = PivotByLineContext::new(&cover_input, pivot);
133        let stego_text = method.encode(&mut context, &mut data_iterator, None)?;
134
135        assert_eq!(&stego_text, "a  b c \na  b c \na  b c\na b\nc a\nb c \n");
136        Ok(())
137    }
138
139    #[test]
140    fn decodes_binary_data() -> Result<(), Box<dyn Error>> {
141        let stego_text = "a  bc\na bcd\na  b d \n";
142        let pivot: usize = 4;
143
144        let method = ExtendedLineMethod::default();
145        let mut context = PivotByRawLineContext::new(&stego_text, pivot);
146        let secret_data = method.decode(&mut context, None)?;
147
148        assert_eq!(&secret_data, &[0b100_010_11, 0b100_000_00]);
149        Ok(())
150    }
151
152    #[test]
153    fn decodes_zeroes_if_no_data_encoded() -> Result<(), Box<dyn Error>> {
154        let stego_text = "a\n".repeat(5);
155        let pivot: usize = 4;
156
157        let method = ExtendedLineMethod::default();
158        let mut context = PivotByRawLineContext::new(&stego_text, pivot);
159        let secret_data = method.decode(&mut context, None)?;
160
161        assert_eq!(&secret_data, &[0, 0]);
162        Ok(())
163    }
164
165    #[test]
166    fn default_method_is_variant_1() -> Result<(), Box<dyn Error>> {
167        assert_eq!(
168            ExtendedLineMethod::default().method_name(),
169            "ExtendedLineMethod(RandomWhitespaceMethod,LineExtendMethod,TrailingWhitespaceMethod)"
170        );
171        Ok(())
172    }
173
174    #[test]
175    fn builder_properly_constructs_variants() -> Result<(), Box<dyn Error>> {
176        assert_eq!(
177            ExtendedLineMethodBuilder::new()
178                .variant(ExtendedLineMethodVariant::Variant1)
179                .build()
180                .method_name(),
181            "ExtendedLineMethod(RandomWhitespaceMethod,LineExtendMethod,TrailingWhitespaceMethod)"
182        );
183        assert_eq!(
184            ExtendedLineMethodBuilder::new()
185                .variant(ExtendedLineMethodVariant::Variant2)
186                .build()
187                .method_name(),
188            "ExtendedLineMethod(LineExtendMethod,RandomWhitespaceMethod,TrailingWhitespaceMethod)"
189        );
190        assert_eq!(
191            ExtendedLineMethodBuilder::new()
192                .variant(ExtendedLineMethodVariant::Variant3)
193                .build()
194                .method_name(),
195            "ExtendedLineMethod(LineExtendMethod,TrailingWhitespaceMethod,RandomWhitespaceMethod)"
196        );
197        Ok(())
198    }
199}