Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Steganography in Rust

Steganography in Rust

An introduction for what steganography is, how it works for images and most importantly how it is implemented in rust inside the stegano-cli tool.

Sven Assmann

June 03, 2020
Tweet

Other Decks in Programming

Transcript

  1. I do lot of business application development with web technologies

    and big databases Sometimes I also write about learning rust Hi, I’m Sven @5422m4n /sassman
  2. What is Steganography? in bloomy words: “the art of hiding

    information behind something” speaking more tech: “hiding data in image, audio or video data” @5422m4n /sassman
  3. Cryptography vs Steganography hiding information nobody should know Alice and

    Bob are communicating protects the sender and recipient inaccessible information anyone can know Alice and Bob are communicating protects the message Steganography can be combined with Cryptography @5422m4n /sassman
  4. About Colors R: 0xE7 G: 0xA6 B: 0x29 R: 0xE6

    G: 0xA7 B: 0x28 Can you tell the difference? @5422m4n /sassman
  5. Least Significant Bit (LSB) R: 0xE7 G: 0xA6 B: 0x29

    R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 0xE6 G: 0xA7 B: 0x28 R: 1110 0110 G: 1010 0111 B: 0010 1000 1 bit 1 bit 1 bit - + - @5422m4n /sassman
  6. About Data R: 1110 0111 G: 1010 0110 B: 0010

    1001 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0111 G: 1010 0110 B: 0010 1001 @5422m4n /sassman
  7. About Data R: 1110 0110 G: 1010 0110 B: 0010

    1000 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0111 G: 1010 0110 B: 0010 1001 @5422m4n /sassman
  8. About Data R: 1110 0110 G: 1010 0110 B: 0010

    1000 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0111 G: 1010 0110 B: 0010 1000 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0111 G: 1010 0110 B: 0010 1001 @5422m4n /sassman
  9. About Data R: 1110 0110 G: 1010 0110 B: 0010

    1000 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0111 G: 1010 0110 B: 0010 1000 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0111 G: 1010 0110 B: 0010 1001 @5422m4n /sassman
  10. About Data R: 1110 0110 G: 1010 0110 B: 0010

    1000 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0111 G: 1010 0110 B: 0010 1000 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0111 G: 1010 0110 B: 0010 1001 @5422m4n /sassman
  11. About Data R: 1110 0110 G: 1010 0110 B: 0010

    1000 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0111 G: 1010 0110 B: 0010 1000 R: 1110 0110 G: 1010 0110 B: 0010 1000 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0111 G: 1010 0110 B: 0010 1001 @5422m4n /sassman
  12. About Data R: 1110 0110 G: 1010 0110 B: 0010

    1000 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0111 G: 1010 0110 B: 0010 1000 R: 1110 0110 G: 1010 0110 B: 0010 1000 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0110 G: 1010 0111 B: 0010 1001 @5422m4n /sassman
  13. About Data R: 1110 0110 G: 1010 0110 B: 0010

    1000 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0111 G: 1010 0110 B: 0010 1000 R: 1110 0110 G: 1010 0110 B: 0010 1000 R: 1110 0110 G: 1010 0110 B: 0010 1001 R: 1110 0110 G: 1010 0111 B: 0010 1001 @5422m4n /sassman
  14. About Data R: 1110 0110 G: 1010 0110 B: 0010

    1000 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0111 G: 1010 0110 B: 0010 1000 R: 1110 0110 G: 1010 0110 B: 0010 1000 R: 1110 0110 G: 1010 0110 B: 0010 1000 R: 1110 0110 G: 1010 0111 B: 0010 1001 @5422m4n /sassman
  15. R: 1110 0110 G: 1010 0110 B: 0010 1000 What

    about the end of data? How do we end the message? R: 1110 0110 G: 1010 0110 B: 0010 1000 R: 1110 0111 G: 1010 0110 B: 0010 1000 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0110 G: 1010 0110 B: 0010 1000 R: 1110 0110 G: 1010 0111 B: 0010 1001 R: 1110 0110 G: 1010 0110 B: 0010 1000 @5422m4n /sassman
  16. R: 1110 0111 G: 1010 0110 B: 0010 1000 R:

    1110 0110 G: 1010 0111 B: 0010 1000 What about the end of data? R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0110 G: 1010 0110 B: 0010 1000 “Zero out” the LSB for all Pixel Terminate the “Payload” with a marker byte sequence (like zip) Put the size of “Payload” at first (like UDP packages) R: 1110 0110 G: 1010 0110 B: 0010 1000 R: 1110 0111 G: 1010 0110 B: 0010 1000 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0110 G: 1010 0110 B: 0010 1000 R: 1110 0110 G: 1010 0111 B: 0010 1001 R: 1110 0110 G: 1010 0110 B: 0010 1000 @5422m4n /sassman
  17. R: 1110 0111 G: 1010 0110 B: 0010 1000 R:

    1110 0110 G: 1010 0111 B: 0010 1000 What about the end of data? R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0110 G: 1010 0110 B: 0010 1000 “Zero out” the LSB for all Pixel Terminate the “Payload” with a marker byte sequence (like zip) Put the size of “Payload” at first (like UDP packages) R: 1110 0110 G: 1010 0110 B: 0010 1000 R: 1110 0111 G: 1010 0110 B: 0010 1000 R: 1110 0111 G: 1010 0110 B: 0010 1001 R: 1110 0110 G: 1010 0110 B: 0010 1000 R: 1110 0110 G: 1010 0111 B: 0010 1001 R: 1110 0110 G: 1010 0110 B: 0010 1000 @5422m4n /sassman
  18. use stegano_core::{SteganoCore, SteganoEncoder, SteganoDecoder, Hide, Unveil}; #[test] fn should_hide_and_unveil_one_text_file() ->

    Result<()> { SteganoCore::encoder() .hide_file("Cargo.toml") .use_carrier_image("resources/plain/carrier-image.png") .write_to("/tmp/image-with-a-file-inside.png") .hide(); SteganoCore::decoder() .use_source_image("/tmp/image-with-a-file-inside.png") .write_to_folder("/tmp/") .unveil(); assert_eq_file_content( "/tmp/Cargo.toml".as_ref(), "Cargo.toml".as_ref(), "Unveiled data did not match the original content”, ); Ok(()) } @5422m4n /sassman
  19. use stegano_core::{SteganoCore, SteganoEncoder, SteganoDecoder, Hide, Unveil}; #[test] fn should_hide_and_unveil_one_text_file() ->

    Result<()> { SteganoCore::encoder() .hide_file("Cargo.toml") .use_carrier_image("resources/plain/carrier-image.png") .write_to("/tmp/image-with-a-file-inside.png") .hide(); SteganoCore::decoder() .use_source_image("/tmp/image-with-a-file-inside.png") .write_to_folder("/tmp/") .unveil(); assert_eq_file_content( "/tmp/Cargo.toml".as_ref(), "Cargo.toml".as_ref(), "Unveiled data did not match the original content”, ); Ok(()) } pub struct SteganoEncoder { target: Option<String>, carrier: Option<RgbaImage>, message: Message, } @5422m4n /sassman
  20. use stegano_core::{SteganoCore, SteganoEncoder, SteganoDecoder, Hide, Unveil}; #[test] fn should_hide_and_unveil_one_text_file() ->

    Result<()> { SteganoCore::encoder() .hide_file("Cargo.toml") .use_carrier_image("resources/plain/carrier-image.png") .write_to("/tmp/image-with-a-file-inside.png") .hide(); SteganoCore::decoder() .use_source_image("/tmp/image-with-a-file-inside.png") .write_to_folder("/tmp/") .unveil(); assert_eq_file_content( "/tmp/Cargo.toml".as_ref(), "Cargo.toml".as_ref(), "Unveiled data did not match the original content”, ); Ok(()) } pub struct SteganoEncoder { target: Option<String>, carrier: Option<RgbaImage>, message: Message, } pub struct SteganoDecoder { input: Option<RgbaImage>, output: Option<String>, } @5422m4n /sassman
  21. use stegano_core::{SteganoCore, SteganoEncoder, SteganoDecoder, Hide, Unveil}; #[test] fn should_hide_and_unveil_one_text_file() ->

    Result<()> { SteganoCore::encoder() .hide_file("Cargo.toml") .use_carrier_image("resources/plain/carrier-image.png") .write_to("/tmp/image-with-a-file-inside.png") .hide(); SteganoCore::decoder() .use_source_image("/tmp/image-with-a-file-inside.png") .write_to_folder("/tmp/") .unveil(); assert_eq_file_content( "/tmp/Cargo.toml".as_ref(), "Cargo.toml".as_ref(), "Unveiled data did not match the original content”, ); Ok(()) } @5422m4n /sassman
  22. impl Hide for SteganoEncoder { fn hide(&mut self) -> &Self

    { let mut img = self.carrier.as_mut().unwrap(); let mut dec = LSBCodec::new(&mut img); let buf: Vec<u8> = (&self.message).into(); dec.write_all(&buf[..]) .expect("Failed to hide data in carrier image."); // ... self.carrier .as_mut() .expect("Image was not there for saving.") .save(self.target.as_ref().unwrap()) .expect("Failed to save final image"); self } } @5422m4n /sassman
  23. impl Hide for SteganoEncoder { fn hide(&mut self) -> &Self

    { let mut img = self.carrier.as_mut().unwrap(); let mut dec = LSBCodec::new(&mut img); let buf: Vec<u8> = (&self.message).into(); dec.write_all(&buf[..]) .expect("Failed to hide data in carrier image."); // ... self.carrier .as_mut() .expect("Image was not there for saving.") .save(self.target.as_ref().unwrap()) .expect("Failed to save final image"); self } } pub struct Message { pub header: ContentVersion, pub files: Vec<(String, Vec<u8>)>, pub text: Option<String>, } @5422m4n /sassman
  24. impl Hide for SteganoEncoder { fn hide(&mut self) -> &Self

    { let mut img = self.carrier.as_mut().unwrap(); let mut dec = LSBCodec::new(&mut img); let buf: Vec<u8> = (&self.message).into(); dec.write_all(&buf[..]) .expect("Failed to hide data in carrier image."); // ... self.carrier .as_mut() .expect("Image was not there for saving.") .save(self.target.as_ref().unwrap()) .expect("Failed to save final image"); self } } @5422m4n /sassman
  25. impl<'img> Write for LSBCodec<'img> { fn write(&mut self, buf: &[u8])

    -> Result<usize> { let carrier = &mut self.subject; let (width, height) = carrier.dimensions(); let mut bit_iter = BitReader::endian( Cursor::new(buf), LittleEndian, ); let mut bits_written = 0; for x in 0..width { for y in 0..height { let image::Rgba(mut rgba) = carrier.get_pixel(x, y); for c in 0..3 as usize { rgba[c] = bit_wave(rgba[c], bit_iter.read_bit()); bits_written += 1; } carrier.put_pixel(x, y, Rgba(rgba)); } } Ok((bits_written / 8) as usize) } } @5422m4n /sassman
  26. impl<'img> Write for LSBCodec<'img> { fn write(&mut self, buf: &[u8])

    -> Result<usize> { let carrier = &mut self.subject; let (width, height) = carrier.dimensions(); let mut bit_iter = BitReader::endian( Cursor::new(buf), LittleEndian, ); let mut bits_written = 0; for x in 0..width { for y in 0..height { let image::Rgba(mut rgba) = carrier.get_pixel(x, y); for c in 0..3 as usize { rgba[c] = bit_wave(rgba[c], bit_iter.read_bit()); bits_written += 1; } carrier.put_pixel(x, y, Rgba(rgba)); } } Ok((bits_written / 8) as usize) } } @5422m4n /sassman
  27. impl<'img> Write for LSBCodec<'img> { fn write(&mut self, buf: &[u8])

    -> Result<usize> { let carrier = &mut self.subject; let (width, height) = carrier.dimensions(); let mut bit_iter = BitReader::endian( Cursor::new(buf), LittleEndian, ); let mut bits_written = 0; for x in 0..width { for y in 0..height { let image::Rgba(mut rgba) = carrier.get_pixel(x, y); for c in 0..3 as usize { rgba[c] = bit_wave(rgba[c], bit_iter.read_bit()); bits_written += 1; } carrier.put_pixel(x, y, Rgba(rgba)); } } Ok((bits_written / 8) as usize) } } #[inline] fn bit_wave(byte: u8, bit: Result<bool>) -> u8 { let byt = match bit { Err(_) => byte, Ok(byt) => if byt { 1 } else { 0 } }; (byte & 0xFE) | byt } @5422m4n /sassman
  28. impl<'img> Write for LSBCodec<'img> { fn write(&mut self, buf: &[u8])

    -> Result<usize> { let carrier = &mut self.subject; let (width, height) = carrier.dimensions(); let mut bit_iter = BitReader::endian( Cursor::new(buf), LittleEndian, ); let mut bits_written = 0; for x in 0..width { for y in 0..height { let image::Rgba(mut rgba) = carrier.get_pixel(x, y); for c in 0..3 as usize { rgba[c] = bit_wave(rgba[c], bit_iter.read_bit()); bits_written += 1; } carrier.put_pixel(x, y, Rgba(rgba)); } } Ok((bits_written / 8) as usize) } } #[inline] fn bit_wave(byte: u8, bit: Result<bool>) -> u8 { let byt = match bit { Err(_) => byte, Ok(byt) => if byt { 1 } else { 0 } }; (byte & 0xFE) | byt } R: 1110 0111 G: 1010 0110 B: 0010 1000 @5422m4n /sassman