how it works

introduction

what is steganography?

steganography hides messages in plain sight. unlike encryption which scrambles a message so it can't be read, steganography hides the message inside something ordinary like an image. anyone looking at the image sees just a normal picture.

what is post-quantum encryption?

today's encryption (RSA, elliptic curves) can be broken by quantum computers using Shor's algorithm. post-quantum cryptography uses mathematical problems that remain hard even for quantum computers. ML-KEM is a lattice-based algorithm standardized by NIST in 2024 specifically for this purpose.

what is LSB encoding?

every pixel in an image has color values stored as 8-bit numbers (0-255). the least significant bit (LSB) is the rightmost bit, and changing it only shifts the value by 1. for example, changing a red value from 180 to 181 is invisible to the human eye.

lsb embedding

lsb embedding example

each pixel has three color channels (red, green, blue), so each pixel can hide 3 bits of data. a 1000×1000 image has 1 million pixels, giving you 375 KB of hiding capacity.

LSB steganography is hard to detect because the changes are statistically random and visually imperceptible. detection tools look for unusual patterns, but at low embedding rates the modifications blend into the natural noise of any image.

the big picture

plainsight combines these ideas: your message is encrypted with ML-KEM-768 and AES-256-GCM, then hidden inside an image using LSB encoding. the image can be shared on social media, email, or messaging apps without raising suspicion.

encryption flow

encryption pipeline

the receiver extracts and decrypts the message using their private key. if anyone tampers with the image, decryption fails and the system detects the modification.

decryption flow

decryption pipeline

key generation

the receiver generates a keypair using ML-KEM-768. the public key can be shared with anyone who wants to send them a message. the private key is kept secret and used for decryption.

key generation

ml-kem-768 keypair generation

encryption steps

encryption happens in three steps. first, the sender uses the receiver's public key to create a shared secret that only the receiver can recover.

key encapsulation

key encapsulation

the shared secret is used to derive an AES-256-GCM key. this symmetric cipher encrypts the actual message and adds an authentication tag that detects any tampering.

symmetric encryption

symmetric encryption

finally, the encrypted data is hidden inside an image using LSB steganography.

steganography

lsb steganography

decryption

the receiver reverses the process: extract the hidden data, recover the shared secret using their private key, and decrypt the message. only the private key holder can read it.

full decryption

complete decryption process

performance

post-quantum cryptography has a reputation for being slow, but ML-KEM is fast. all operations complete in under 100 microseconds. these benchmarks were measured using a rust implementation.

ml-kem performance

ml-kem operation times across variants (lower is better)

  operation                    time
  ─────────────────────────────────────
  ml-kem-768 key generation    44 μs
  ml-kem-768 encapsulation     41 μs
  ml-kem-768 decapsulation     53 μs
  aes-256-gcm encryption       < 1 μs
  lsb embedding                29 μs
  lsb extraction               17 μs
  ─────────────────────────────────────
  total encryption            ~115 μs
  total decryption             ~71 μs
				

benchmarks on amd ryzen 5 7535HS

image quality

PSNR (peak signal-to-noise ratio) measures image quality. values above 40 dB are considered imperceptible to humans. even at 100% capacity, PSNR stays above 51 dB. in practice, a typical message uses less than 1% of capacity.

psnr chart

psnr vs embedding capacity (higher is better)

data sizes

each message has about 1.1 KB of overhead for the cryptographic data. a typical photograph can hide tens or hundreds of kilobytes of data.

  component              size
  ─────────────────────────────────────
  ml-kem public key      1,184 bytes
  ml-kem private key     2,400 bytes
  ml-kem ciphertext      1,088 bytes
  aes-gcm iv                12 bytes
  aes-gcm auth tag          16 bytes
  ─────────────────────────────────────
  overhead per message   ~1,116 bytes

  500×500 image capacity     ~93 kb
  1000×1000 image capacity  ~375 kb
				

implementation uses @noble/post-quantum for ml-kem, web crypto api for aes-gcm, and canvas api for steganography. all operations run client-side in your browser.