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.

8aadcbab574f26c33bb356d3987edc39?s=128

Sven Assmann

June 03, 2020
Tweet

Transcript

  1. Steganography in Rust hiding secrets in images Sven Assmann @5422m4n

    /sassman
  2. 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
  3. tweeting coding writing @5422m4n /sassman twitter.com/@5422m4n github.com/sassman dev.to/sassman

  4. 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
  5. 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
  6. About Images width height Pixel @5422m4n /sassman

  7. About Pixel Pixel Red Green Blue Transparency / Alpha @5422m4n

    /sassman
  8. About Colors R: 0xE7 G: 0xA6 B: 0x29 R: 0xE6

    G: 0xA7 B: 0x28 Can you tell the difference? @5422m4n /sassman
  9. 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
  10. 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
  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 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
  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 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
  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 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
  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 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
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. Architecture @5422m4n /sassman

  23. Finally Code @5422m4n /sassman

  24. 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
  25. 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
  26. 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
  27. 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
  28. 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
  29. 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
  30. 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
  31. 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
  32. 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
  33. 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
  34. 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
  35. Demo @5422m4n /sassman

  36. @5422m4n /sassman github.com/steganogram www.stegano.org/

  37. Q & A @5422m4n /sassman