Slide 1

Slide 1 text

Steganography in Rust hiding secrets in images Sven Assmann @5422m4n /sassman

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

tweeting coding writing @5422m4n /sassman twitter.com/@5422m4n github.com/sassman dev.to/sassman

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

About Images width height Pixel @5422m4n /sassman

Slide 7

Slide 7 text

About Pixel Pixel Red Green Blue Transparency / Alpha @5422m4n /sassman

Slide 8

Slide 8 text

About Colors R: 0xE7 G: 0xA6 B: 0x29 R: 0xE6 G: 0xA7 B: 0x28 Can you tell the difference? @5422m4n /sassman

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

Architecture @5422m4n /sassman

Slide 23

Slide 23 text

Finally Code @5422m4n /sassman

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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, carrier: Option, message: Message, } @5422m4n /sassman

Slide 26

Slide 26 text

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, carrier: Option, message: Message, } pub struct SteganoDecoder { input: Option, output: Option, } @5422m4n /sassman

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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 = (&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

Slide 29

Slide 29 text

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 = (&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)>, pub text: Option, } @5422m4n /sassman

Slide 30

Slide 30 text

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 = (&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

Slide 31

Slide 31 text

impl<'img> Write for LSBCodec<'img> { fn write(&mut self, buf: &[u8]) -> Result { 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

Slide 32

Slide 32 text

impl<'img> Write for LSBCodec<'img> { fn write(&mut self, buf: &[u8]) -> Result { 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

Slide 33

Slide 33 text

impl<'img> Write for LSBCodec<'img> { fn write(&mut self, buf: &[u8]) -> Result { 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) -> u8 { let byt = match bit { Err(_) => byte, Ok(byt) => if byt { 1 } else { 0 } }; (byte & 0xFE) | byt } @5422m4n /sassman

Slide 34

Slide 34 text

impl<'img> Write for LSBCodec<'img> { fn write(&mut self, buf: &[u8]) -> Result { 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) -> 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

Slide 35

Slide 35 text

Demo @5422m4n /sassman

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

Q & A @5422m4n /sassman