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}