asbs/binary/
package.rs

1use crate::{binary::bits, Reveal};
2use std::{
3    io::{self, BufReader, BufWriter, Read, Write},
4    ops::ControlFlow,
5};
6
7#[derive(Debug, PartialEq)]
8enum PayloadLength {
9    Bound(u64),
10    Unbound,
11    Embedded,
12}
13
14/// A binary package that contains a steganographic message.
15///
16/// It writes to the package writer in the [`reveal`][crate::Reveal::reveal] method until
17/// either occurs:
18///
19/// 1. The underlying reader is empty, or
20/// 2. The package no longer accepts writes, or
21/// 3. The required length of bytes was written.
22///
23/// # Examples
24///
25/// Revealing a secret message of known length hidden within the package:
26///
27/// ```no_run
28/// use asbs::{binary, Reveal};
29/// use std::fs::File;
30///
31/// let mut package = binary::Package::with_len(
32///     64,
33///     |_| Some(0b_0010_00011),
34///     File::open("package")?,
35/// );
36///
37/// package.reveal(File::open("message")?)?;
38/// # Ok::<(), std::io::Error>(())
39/// ```
40///
41/// Revealing a secret message hidden within the package with embedded length:
42///
43/// ```no_run
44/// use asbs::{binary, Reveal};
45/// use std::fs::File;
46///
47/// let mut package = binary::Package::with_embedded_len(
48///     |_| Some(0b1010),
49///     File::open("package")?,
50/// );
51///
52/// package.reveal(File::open("message")?)?;
53/// # Ok::<(), std::io::Error>(())
54/// ```
55#[derive(Debug)]
56pub struct Package<P, R>
57where
58    P: FnMut(usize) -> Option<u8>,
59    R: Read,
60{
61    pattern: P,
62    reader: BufReader<R>,
63    len: PayloadLength,
64}
65
66impl<P, R> Package<P, R>
67where
68    P: FnMut(usize) -> Option<u8>,
69    R: Read,
70{
71    /// Creates a new [`Package<P, R>`] with the supplied message length, pattern, and reader.
72    ///
73    /// This function is useful when you know the expected message length.
74    ///
75    /// # Examples
76    ///
77    /// ```no_run
78    /// use asbs::binary;
79    /// use std::fs::File;
80    ///
81    /// let mut package = binary::Package::with_len(
82    ///     32,
83    ///     |i| Some(1u8 << (i % 3)),
84    ///     File::open("package")?,
85    /// );
86    /// # Ok::<(), std::io::Error>(())
87    /// ```
88    #[must_use]
89    pub fn with_len(len: usize, pattern: P, reader: R) -> Self {
90        Self {
91            pattern,
92            reader: BufReader::new(reader),
93            len: PayloadLength::Bound(len as u64),
94        }
95    }
96
97    /// Creates a new [`Package<P, R>`] with the supplied pattern and reader.
98    ///
99    /// This function is useful if the encoded payload contains the message length as a
100    /// 64-bit integer in big-endian byte order.
101    ///
102    /// # Examples
103    ///
104    /// ```no_run
105    /// use asbs::binary;
106    /// use std::fs::File;
107    ///
108    /// let mut package = binary::Package::with_embedded_len(
109    ///     |i| Some(1u8 << (i % 4)),
110    ///     File::open("package")?,
111    /// );
112    /// # Ok::<(), std::io::Error>(())
113    /// ```
114    #[must_use]
115    pub fn with_embedded_len(pattern: P, reader: R) -> Self {
116        Self {
117            pattern,
118            reader: BufReader::new(reader),
119            len: PayloadLength::Embedded,
120        }
121    }
122
123    /// Creates a new [`Package<P, R>`] with the supplied pattern and reader.
124    ///
125    /// This does not impose any limits upon the number of bytes that will be
126    /// read from the package.
127    ///
128    /// If message length is known beforehand, use [`Package::with_len`].
129    ///
130    /// If message length is embedded, use [`Package::with_embedded_len`].
131    ///
132    /// # Examples
133    ///
134    /// ```no_run
135    /// use asbs::binary;
136    /// use std::fs::File;
137    ///
138    /// let mut package = binary::Package::new(
139    ///     |_| Some(0b1101),
140    ///     File::open("package")?,
141    /// );
142    /// # Ok::<(), std::io::Error>(())
143    /// ```
144    #[must_use]
145    pub fn new(pattern: P, reader: R) -> Self {
146        Self {
147            pattern,
148            reader: BufReader::new(reader),
149            len: PayloadLength::Unbound,
150        }
151    }
152}
153
154impl<M, R> Reveal for &mut Package<M, R>
155where
156    M: FnMut(usize) -> Option<u8>,
157    R: Read,
158{
159    type Err = io::Error;
160
161    fn reveal<W: Write>(self, output: W) -> io::Result<usize> {
162        let mut output = BufWriter::new(output);
163
164        let mut len_bytes = (self.len == PayloadLength::Embedded).then(|| Vec::with_capacity(8));
165
166        let mut bytes_written = 0usize;
167        let mut write_byte = |byte| -> Result<ControlFlow<()>, io::Error> {
168            if let Some(bytes) = len_bytes.as_mut() {
169                bytes.push(byte);
170
171                if bytes.len() == 8 {
172                    self.len = PayloadLength::Bound(u64::from_be_bytes(
173                        *bytes.first_chunk::<8>().unwrap(),
174                    ));
175
176                    if let PayloadLength::Bound(0) = self.len {
177                        return Ok(ControlFlow::Break(()));
178                    }
179
180                    len_bytes = None;
181                }
182
183                return Ok(ControlFlow::Continue(()));
184            }
185
186            bytes_written += output.write(&[byte])?;
187
188            Ok(match self.len {
189                PayloadLength::Embedded => unreachable!("`PayloadLength::Embedded` is replaced with `PayloadLength::Known(n)` before reaching this"),
190                PayloadLength::Unbound => ControlFlow::Continue(()),
191                PayloadLength::Bound(len) => {
192                    if (bytes_written as u64) < len {
193                        ControlFlow::Continue(())
194                    } else {
195                        ControlFlow::Break(())
196                    }
197                }
198            })
199        };
200
201        let mut payload_byte = 0;
202        let mut bit_count = 0usize;
203        for (index, package_byte) in self.reader.by_ref().bytes().enumerate() {
204            let Some(mask) = (self.pattern)(index) else {
205                break;
206            };
207
208            let package_byte = package_byte?;
209            for pow in bits::Ones::from(mask) {
210                payload_byte |= ((package_byte >> pow) & 1) << bit_count;
211                bit_count += 1;
212
213                if bit_count < 8 {
214                    continue;
215                }
216
217                if write_byte(payload_byte)?.is_break() {
218                    output.flush()?;
219                    return Ok(bytes_written);
220                }
221
222                bit_count = 0;
223                payload_byte = 0;
224            }
225        }
226
227        if bit_count > 0 {
228            write_byte(payload_byte)?;
229        }
230
231        output.flush()?;
232
233        Ok(bytes_written)
234    }
235}