Implementing the Bacon Cipher in Rust

Jan 30, 2025

Introduction


The Bacon Cipher is a simple steganographic technique invented by Sir Francis Bacon in the 16th century. It encodes messages by representing each letter with a binary sequence, usually consisting of two distinguishable symbols. In this tutorial, we will walk you through implementing the Bacon Cipher in Rust, covering both encoding and decoding functionalities.



Project Setup


Before we dive into the code, make sure you have Rust installed on your system. You can check this by running:

rustc --version

If Rust is not installed, follow the official installation guide.


Step 1: Creating the Rust Project

Create a new Rust project using Cargo:

cargo new bacon_cipher
cd bacon_cipher



Step 2: Implementing the Bacon Cipher in Rust


Open the src/main.rs file and follow the steps below.


Step 2.1: Define the Bacon Mapping

use std::collections::HashMap;

fn get_bacon_mapping() -> HashMap<char, &'static str> {
let mut mapping = HashMap::new();
let alphabet = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
];

let codes = [
"AAAAA", "AAAAB", "AAABA", "AAABB", "AABAA", "AABAB", "AABBA", "AABBB",
"ABAAA", "ABAAB", "ABABA", "ABABB", "ABBAA", "ABBAB", "ABBBA", "ABBBB",
"BAAAA", "BAAAB", "BAABA", "BAABB", "BABAA", "BABAB", "BABBA", "BABBB",
"BBAAA", "BBAAB",
];

for (i, &ch) in alphabet.iter().enumerate() {
mapping.insert(ch, codes[i]);
}

mapping
}

This function returns a HashMap containing the letter-to-code mappings according to the Bacon Cipher.



Step 2.2: Encoding a Message

fn encode_message(message: &str) -> String {
let mapping = get_bacon_mapping();
let mut encoded = String::new();

for ch in message.to_uppercase().chars() {
if let Some(&code) = mapping.get(&ch) {
encoded.push_str(code);
}
}

encoded
}

fn main() {
let message = "HELLO";
let encoded = encode_message(message);
println!("Encoded message: {}", encoded);
}

In this example, the message HELLO is encoded using the Bacon mapping, outputting a sequence of A's and B's.



Step 2.3: Decoding a Message

To decode a message, we need to reverse the mapping and convert sequences of A's and B's back to letters.

fn decode_message(encoded: &str) -> String {
let mapping = get_bacon_mapping();
let mut reversed_mapping = HashMap::new();
for (&key, &value) in &mapping {
reversed_mapping.insert(value, key);
}

let mut decoded = String::new();
let mut buffer = String::new();

for ch in encoded.chars() {
buffer.push(ch);
if buffer.len() == 5 {
if let Some(&decoded_char) = reversed_mapping.get(&buffer.as_str()) {
decoded.push(decoded_char);
}
buffer.clear();
}
}

decoded
}

fn main() {
let message = "HELLO";
let encoded = encode_message(message);
println!("Encoded message: {}", encoded);

let decoded = decode_message(&encoded);
println!("Decoded message: {}", decoded);
}

The decode_message function reads chunks of 5 characters and translates them back to their corresponding letters using the reversed mapping.



Step 3: Running the Program

To test the implementation, run:

cargo run

You should see output similar to:

Encoded message: AABAAABABAABBABAAABBB
Decoded message: HELLO