use image::{Pixel, RgbImage};
use itertools_num::linspace;
use rand::Rng;
use rand_pcg::Pcg64;
use rand_seeder::Seeder;
use std::convert::From;
use std::fmt::Write;
use crate::cli::{BitDistribution, EncodeOpts, StegMethod};
use crate::StegError;
const END: &[u8] = b"$T3G";
pub trait Steganography {
fn encode(&mut self, img: &RgbImage, msg: &[u8]) -> Result<RgbImage, StegError>;
fn decode(&mut self, img: &RgbImage) -> Result<Vec<u8>, StegError>;
fn max_len(&self, img: &RgbImage) -> usize;
}
pub fn encoder_from_opts(opts: EncodeOpts) -> Box<dyn Steganography> {
let steg_method = opts.method.unwrap_or_default();
let encoder: Box<dyn Steganography> = match &steg_method {
StegMethod::LeastSignificantBit => {
let lsb = Box::<Lsb>::default();
Box::new(BitEncoder::new(
lsb,
Some(opts.distribution.unwrap_or_default()),
))
}
StegMethod::RandomSignificantBit => {
let rsb = Box::new(Rsb::new(opts.max_bit.unwrap(), &opts.seed.unwrap()));
Box::new(BitEncoder::new(
rsb,
Some(opts.distribution.unwrap_or_default()),
))
}
};
encoder
}
#[derive(Clone)]
enum BitMask {
One = 0b0000_0001,
Two = 0b0000_0010,
Four = 0b0000_0100,
Eight = 0b0000_1000,
}
impl From<u8> for BitMask {
fn from(num: u8) -> Self {
match num {
1 => BitMask::One,
2 => BitMask::Two,
3 => BitMask::Four,
4 => BitMask::Eight,
other => panic!("cannot create bitmask from value {}", other),
}
}
}
pub trait BitEncoding {
fn encode(&mut self, bit: &u8, color_val: &mut u8);
fn decode(&mut self, color_val: &u8) -> u8;
}
pub struct BitEncoder {
encoder: Box<dyn BitEncoding>,
bit_dist: BitDistribution,
end_sequence: bool,
}
impl BitEncoder {
pub fn new(encoder: Box<dyn BitEncoding>, bd: Option<BitDistribution>) -> Self {
BitEncoder {
encoder,
bit_dist: bd.unwrap_or_default(),
end_sequence: true,
}
}
}
pub struct Lsb;
impl Lsb {
pub fn new() -> Self {
Lsb {}
}
}
impl Default for Lsb {
fn default() -> Self {
Lsb::new()
}
}
pub struct Rsb {
max: u8,
rng: Pcg64,
}
impl Rsb {
pub fn new(max: u8, seed: &str) -> Self {
let rng: Pcg64 = Seeder::from(seed).make_rng();
Rsb { max, rng }
}
fn next_mask(&mut self) -> BitMask {
let n: u8 = self.rng.gen_range(1..=self.max);
BitMask::from(n)
}
}
impl BitEncoding for Rsb {
fn encode(&mut self, bit: &u8, color_val: &mut u8) {
let mask = self.next_mask();
if *bit == 0 {
*color_val &= !(mask as u8);
} else if *bit == 1 {
*color_val |= mask as u8;
}
}
fn decode(&mut self, color_val: &u8) -> u8 {
let mask = self.next_mask();
let c = color_val & mask as u8;
u8::from(c > 0)
}
}
impl BitEncoding for Lsb {
fn encode(&mut self, bit: &u8, color_val: &mut u8) {
if *bit == 0 {
*color_val &= !(BitMask::One as u8);
} else if *bit == 1 {
*color_val |= BitMask::One as u8;
}
}
fn decode(&mut self, color_val: &u8) -> u8 {
color_val & BitMask::One as u8
}
}
impl Steganography for BitEncoder {
fn max_len(&self, img: &RgbImage) -> usize {
((img.width() * img.height() * 3) as usize - (END.len() * 8)) / 8
}
fn encode(&mut self, img: &RgbImage, msg: &[u8]) -> Result<RgbImage, StegError> {
let msg = if self.end_sequence {
[msg, END].concat()
} else {
msg.to_owned()
};
let mut binary_msg = String::with_capacity(msg.len() * 8);
for byte in msg {
let _ = write!(binary_msg, "{:08b}", byte);
}
let binary_msg: Vec<u8> = binary_msg
.chars()
.map(|c| c.to_digit(10).unwrap() as u8)
.collect();
let mut img = img.clone();
let linspace_length = (binary_msg.len() as f64 / 3.).ceil() as usize;
let linear_pixel_dist = get_linspace(
0.,
((img.width() * img.height()) - 1) as f64,
linspace_length,
);
let mut linear_pixel_dist = linear_pixel_dist.iter();
for (ctr, chunk) in binary_msg.chunks(3).enumerate() {
let (x, y) = match self.bit_dist {
BitDistribution::Sequential => {
let x = ctr as u32 % img.width();
let y = ctr as u32 / img.width();
(x, y)
}
BitDistribution::Linear { length: _ } => {
let pixel_num = linear_pixel_dist.next().unwrap();
let x = *pixel_num as u32 % img.width();
let y = *pixel_num as u32 / img.width();
(x, y)
}
};
let pixel = img.get_pixel_mut(x, y);
for (idx, bit) in chunk.iter().enumerate() {
self.encoder.encode(bit, &mut pixel[idx]);
}
}
if let BitDistribution::Linear { length: _ } = self.bit_dist {
println!(
"Note: use length '{}' when decoding with linear distribution",
linspace_length
);
}
Ok(img)
}
fn decode(&mut self, img: &RgbImage) -> Result<Vec<u8>, StegError> {
let mut bitstream: Vec<u8> = Vec::new();
let mut endstream = String::new();
for byte in END {
let _ = write!(endstream, "{:08b}", byte);
}
let end = endstream
.chars()
.map(|c| c.to_digit(10).unwrap() as u8)
.collect::<Vec<u8>>();
match self.bit_dist {
BitDistribution::Sequential => {
'outer_seq: for (_, _, pixel) in img.enumerate_pixels() {
for value in pixel.channels() {
if has_end(&bitstream, &end) {
break 'outer_seq;
}
bitstream.push(self.encoder.decode(value));
}
}
}
BitDistribution::Linear { length } => {
let linear_pixel_dist =
get_linspace(0., ((img.width() * img.height()) - 1) as f64, length);
'outer_lin: for pixel_num in linear_pixel_dist {
let x = pixel_num as u32 % img.width();
let y = pixel_num as u32 / img.width();
let pixel = img.get_pixel(x, y);
for value in pixel.channels() {
if has_end(&bitstream, &end) {
break 'outer_lin;
}
bitstream.push(self.encoder.decode(value));
}
}
}
}
if self.end_sequence {
if !has_end(&bitstream, &end) {
return Err(StegError::EncodingNotFound);
}
bitstream.truncate(bitstream.len() - end.len());
}
let mut msg = Vec::new();
for chrs in bitstream.chunks(8) {
let binval = u8::from_str_radix(
&chrs
.iter()
.map(|c| format! {"{}",c})
.collect::<Vec<String>>()
.join(""),
2,
)
.map_err(|e| StegError::Decoding(format!("reconstructing byte: {}", e)))?;
msg.push(binval);
}
Ok(msg)
}
}
pub fn has_end(bytes: &[u8], end: &[u8]) -> bool {
bytes
.iter()
.rev()
.take(end.len())
.rev()
.copied()
.collect::<Vec<u8>>()
.iter()
.eq(end.iter())
}
pub fn get_linspace(a: f64, b: f64, n: usize) -> Vec<usize> {
linspace(a, b, n)
.map(|p| p.floor() as usize)
.collect::<Vec<usize>>()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lsb_steganography() {
let img = RgbImage::new(32, 32);
let lsb = Box::<Lsb>::default();
let mut enc: Box<dyn Steganography> = Box::from(BitEncoder::new(lsb, None));
let secret_message = "🦕 hiding text!".as_bytes();
let encoded: RgbImage = enc.encode(&img, secret_message).unwrap();
assert_eq!(enc.decode(&encoded).unwrap(), secret_message);
}
#[test]
fn test_rsb_steganography() {
let img = RgbImage::new(32, 32);
let rsb_enc = Box::new(Rsb::new(2, "seed"));
let mut enc: Box<dyn Steganography> = Box::from(BitEncoder::new(rsb_enc, None));
let rsb_dec = Box::new(Rsb::new(2, "seed"));
let mut dec: Box<dyn Steganography> = Box::from(BitEncoder::new(rsb_dec, None));
let secret_message = "🦕 hiding text!".as_bytes();
let encoded: RgbImage = enc.encode(&img, secret_message).unwrap();
assert_eq!(dec.decode(&encoded).unwrap(), secret_message);
}
#[test]
fn test_rsb_random_determined_from_seed() {
let mut rsb1 = Rsb::new(2, "seed");
let mut rsb2 = Rsb::new(2, "seed");
for _ in 0..10 {
assert_eq!(rsb1.rng.gen::<u8>(), rsb2.rng.gen::<u8>());
}
}
#[test]
fn test_rsb_random_determined_from_seed_different() {
let mut rsb1 = Rsb::new(2, "seed");
let mut rsb2 = Rsb::new(2, "seeb");
let it = 1000;
let mut matches = Vec::with_capacity(it);
for _ in 0..it {
matches.push(rsb1.rng.gen::<u8>() == rsb2.rng.gen::<u8>());
}
assert!(matches.contains(&false));
}
#[test]
fn test_rsb_1_decrypts_with_lsb() {
let img = RgbImage::new(32, 32);
let rsb = Box::new(Rsb::new(1, "seed"));
let mut rsb_enc: Box<dyn Steganography> = Box::from(BitEncoder::new(rsb, None));
let lsb = Box::<Lsb>::default();
let mut lsb_enc: Box<dyn Steganography> = Box::from(BitEncoder::new(lsb, None));
let secret_message = "🦕 hiding text!".as_bytes();
let encoded: RgbImage = rsb_enc.encode(&img, secret_message).unwrap();
assert_eq!(lsb_enc.decode(&encoded).unwrap(), secret_message);
}
#[test]
fn test_rsb_3_not_decrypts_with_lsb() {
let img = RgbImage::new(32, 32);
let rsb = Box::new(Rsb::new(3, "seed"));
let mut rsb_enc: Box<dyn Steganography> = Box::from(BitEncoder::new(rsb, None));
let lsb = Box::<Lsb>::default();
let mut lsb_enc: Box<dyn Steganography> = Box::from(BitEncoder::new(lsb, None));
let secret_message = "🦕 hiding text!".as_bytes();
let encoded: RgbImage = rsb_enc.encode(&img, secret_message).unwrap();
let result = lsb_enc.decode(&encoded);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), StegError::EncodingNotFound);
}
#[test]
fn test_linear_distribution_encoding() {
let mut img = RgbImage::new(4, 4);
for x in 0..4 {
for y in 0..4 {
img.put_pixel(x, y, image::Rgb([0, 0, 0]));
}
}
for pixel in img.pixels() {
assert_eq!(*pixel, image::Rgb::<u8>([0, 0, 0]));
}
let lsb = Box::<Lsb>::default();
let mut lsb_enc: Box<dyn Steganography> = Box::from(BitEncoder {
encoder: lsb,
bit_dist: BitDistribution::Linear { length: 0 },
end_sequence: false,
});
let new_img = lsb_enc.encode(&img, b"\xFF").unwrap();
assert_eq!(new_img.get_pixel(0, 0), &image::Rgb::<u8>([1, 1, 1]));
assert_eq!(new_img.get_pixel(3, 1), &image::Rgb::<u8>([1, 1, 1]));
assert_eq!(new_img.get_pixel(3, 3), &image::Rgb::<u8>([1, 1, 0]));
}
#[test]
fn test_linear_distribution_decoding() {
let mut img = RgbImage::new(4, 4);
for x in 0..4 {
for y in 0..4 {
img.put_pixel(x, y, image::Rgb([0, 0, 0]));
}
}
for pixel in img.pixels() {
assert_eq!(*pixel, image::Rgb::<u8>([0, 0, 0]));
}
let lsb = Box::<Lsb>::default();
let mut lsb_enc: Box<dyn Steganography> = Box::from(BitEncoder {
encoder: lsb,
bit_dist: BitDistribution::Linear { length: 0 },
end_sequence: false,
});
let new_img = lsb_enc.encode(&img, b"\xFF").unwrap();
let lsb = Box::<Lsb>::default();
let mut lsb_dec: Box<dyn Steganography> = Box::from(BitEncoder {
encoder: lsb,
bit_dist: BitDistribution::Linear { length: 3 },
end_sequence: false,
});
let result = lsb_dec.decode(&new_img).unwrap();
assert_eq!(result[0], 255);
let lsb = Box::<Lsb>::default();
let mut lsb_dec: Box<dyn Steganography> = Box::from(BitEncoder {
encoder: lsb,
bit_dist: BitDistribution::Linear { length: 4 },
end_sequence: false,
});
let result = lsb_dec.decode(&new_img).unwrap();
assert_ne!(result[0], 255);
}
}