1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use std::{error::Error, fmt, sync::mpsc::Sender};

use log::{debug, trace};

use crate::{binary::Bit, cli::progress::ProgressStatus, context::Context};

/// Possible results of data encoding
#[derive(Debug, Clone)]
pub enum EncoderResult {
    Success,
    NoDataLeft,
}

/// Trait that should be implemented by the [Encoders](crate::econder::Encoder).
/// Gives amount of bits that are encoded per text fragment.
/// Concrete text fragment (e.g. line) is determined by the [Context](crate::context::Context).
pub trait Capacity {
    /// Returns how many bits are encoded per text fragment.
    fn bitrate(&self) -> usize;
}

/// Base trait for all data encoders.
/// The generic type should contain data need by the encoder implementation.
pub trait Encoder<E>: Capacity
where
    E: Context,
{
    /// Encodes bits provided by `data` iterator.
    /// Every Encoder has Context which exposes access to cover text. See [Context] for more info.
    ///
    /// # Arguments
    ///
    /// * `context` - context of the steganography method, can contain various needed info like pivot etc.
    /// * `data` - data iterator which return [Bit] with each iteration
    ///
    /// # Returns
    /// It returns whether the encoding was successful. See [EncoderResult] and [EncodingError].
    ///
    /// [Context]: crate::context::Context
    /// [EncoderResult]: EncoderResult
    /// [EncodingError]: EncodingError
    /// [Bit]: crate::binary::Bit
    fn partial_encode(
        &self,
        context: &mut E,
        data: &mut dyn Iterator<Item = Bit>,
    ) -> Result<EncoderResult, Box<dyn Error>>;

    fn encode(
        &self,
        context: &mut E,
        data: &mut dyn Iterator<Item = Bit>,
        progress_channel: Option<&Sender<ProgressStatus>>,
    ) -> Result<String, Box<dyn Error>> {
        let mut stego_text = String::new();

        let mut no_data_left = false;
        while !no_data_left {
            context.load_text()?;
            trace!("Current line '{}'", context.get_current_text()?);
            match self.partial_encode(context, data)? {
                EncoderResult::Success => {
                    if let Some(tx) = progress_channel {
                        tx.send(ProgressStatus::Step(self.bitrate() as u64)).ok();
                    }
                }
                EncoderResult::NoDataLeft => {
                    debug!("No data left to encode, stopping");
                    no_data_left = true;
                }
            }
            let line = context.get_current_text()?;
            stego_text.push_str(&format!("{}\n", &line));
        }
        // Append the rest of possible missing cover text
        let mut appended_line_count = 0;
        while let Ok(line) = context.load_text() {
            appended_line_count += 1;
            stego_text.push_str(&format!("{}\n", &line));
        }
        debug!("Appended the {} of left lines", appended_line_count);

        if !no_data_left {
            debug!("Capacity exceeded by {} bits", data.count());
            Err(EncodingError::capacity_error().into())
        } else {
            Ok(stego_text)
        }
    }
}

/// Enum for data encoding errors types
#[derive(Debug, Clone)]
pub enum EncodingErrorKind {
    CapacityTooLow,
    NoWordsLeft,
}

/// Represents encoding error. Concrete error if differentiated by the [EncodingErrorKind](EncodingErrorKind)
#[derive(Debug, Clone)]
pub struct EncodingError {
    kind: EncodingErrorKind,
}

impl EncodingError {
    /// Facade for creating [CapacityTooLow](EncodingErrorKind) error.
    pub fn capacity_error() -> Self {
        EncodingError {
            kind: EncodingErrorKind::CapacityTooLow,
        }
    }
    /// Facade for creating [NoWordsLeft](EncodingErrorKind) error.
    pub fn no_words_error() -> Self {
        EncodingError {
            kind: EncodingErrorKind::NoWordsLeft,
        }
    }
}

#[cfg(not(tarpaulin_include))]
impl fmt::Display for EncodingError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match &self.kind {
            EncodingErrorKind::CapacityTooLow => write!(f, "Exceeded cover text capacity"),
            EncodingErrorKind::NoWordsLeft => write!(
                f,
                "No extra words found in cover text when tried to encode a bit"
            ),
        }
    }
}

impl Error for EncodingError {}