Some time ago, I got intrigued by an article on the Register, finding out about the Baconian cipher.
As cited in wikipedia:
Bacon’s cipher is a method of message encoding devised by Francis Bacon in 1605. A message is concealed in the presentation of text, rather than its content.
So I started writing some code, initially implementing Baconian cipher codecs, which encode text to A
s and B
s and decode it back to text.
This was not enough, though, so I thought of implementing a steganographer, which would help hiding these A
s and B
s into text,
mapping A
s to lowercase characters and B
s to uppercase characters.
After that, I was able to hide rust InsidE thE LInux KErnEl
:)
The public message is: inside the linux kernel
.
The secret message is: rust
.
The public message that contains the secret one becomes: InsidE thE LInux KErnEl
.
Here is how:
use bacon_cipher::codecs::char_codec::CharCodec;
use bacon_cipher::stega::letter_case::LetterCaseSteganographer;
use bacon_cipher::{BaconCodec, Steganographer};
use std::iter::FromIterator;
// Define a Bacon Codec that encodes using the characters 'A' and 'B'
let codec = CharCodec::new('a', 'b');
// Apply steganography based on the case of the characters
let s = LetterCaseSteganographer::new();
// This is the public message in which we want to hide the secret one.
let public_chars: Vec<char> = "inside the linux kernel".chars().collect();
// This is the message that we want to hide.
let secret_chars: Vec<char> = "rust".chars().collect();
// This is the public message that contains the secret one
let disguised_public = s.disguise(&secret_chars, &public_chars, &codec);
let string = String::from_iter(disguised_public.unwrap().iter());
assert!(string == "InsidE thE LInux KErnEl");
And here is how to reveal the hidden message:
let public_chars: Vec<char> = "InsidE thE LInux KErnEl".chars().collect();
let output = s.reveal(&public_chars, &codec);
let hidden_message = String::from_iter(output.unwrap().iter());
assert!(hidden_message == "RUST");
More steganography
I went along and implemented more steganographers:
-
A
MarkdownSteganographer
allowed hidingrust
in the following markdown:**i**nsid**e** th**e** **li**nux **ke**rn**e**l
rendering into:
inside the linux kernel
-
A
SimpleTagSteganographer
allowed hidingrust
in the following HTML code:<b style="color: red;">i</b>nsid<b style="color: red;">e</b> th<b style="color: red;">e</b> <b style="color: red;">li</b>nux <b style="color: red;">ke</b>rn<b style="color: red;">e</b>l
rendering into:
inside the linux kernel
No limits…
Encoding and Steganography are two separate processes and they can be combined in any way.
The BaconCodec is a trait
and accepts two associated types:
-
The
ABTYPE
is the type of the substitution characters A and B. For the CharCodec, this is achar
, but we could implement codecs withbool
,isize
or whatever, even a customstruct
ortrait
. -
The
CONTENT
associated type is the type of the content that we want to encode or decode. The CharCodec defines achar
, but again, this could be implemented into anything.
Someone could implement any kind of Steganographer and hide messages in images, or even in the code itself (maybe code obfuscation works like this?).
Moreover, the secret message can be further encoded, or even encrypted, so that we could have encrypted secret messages hidden in public.
Next steps
One thing that can definately be done, is to create codecs that do encryption during encoding and decryption during decoding. Other than that, more steganographers can be implemented for processing images, or sound, or…
One day…
If someone is interested, the code is on Github and the crate is called bacon-cipher.
ThaNks fOR ReaDing aNd haVE a nice DAy!