First Code: A Naive XOR Encryptor
Let’s write our first cryptographic algorithm — or at least something that looks like one.
We’ll implement a simple XOR cipher. This method is insecure and should never be used in real applications — but it’s the perfect teaching tool.
What’s a Cipher?
A cipher is just a method to transform readable data (plaintext) into unreadable data (ciphertext) using a key — and vice versa.
🧭 Word Origin — “Cipher” The word comes from the Arabic “ṣifr” (صفر), meaning “zero” or “empty”. It passed through Latin (cifra), then into French and English as cipher.
What started as a symbol for “nothing” evolved into a word for secret writing — and eventually, encryption algorithms.
What is XOR?
XOR stands for “exclusive or”, a bitwise operation:
A | B | A XOR B |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
In short: XOR returns 1 if the bits differ, 0 if they’re the same.
The XOR operation flips bits when they differ:
1 ^ 0 = 1
1 ^ 1 = 0
0 ^ 0 = 0
When used for encryption:
cipher = plaintext ^ key
plaintext = cipher ^ key
That’s why XOR can be used to encrypt and decrypt data — if you XOR something twice with the same key, you get the original back.
✅ Simple, reversible, fast — but also dangerously weak when misused.
XOR, Bit by Bit
To truly understand XOR in cryptography, it helps to look at bit-level behavior.
Let’s say you compute:
100 ^ 1
This doesn’t mean 100 to the power of 1. In Rust, ^ is the bitwise XOR operator.
Step-by-step:
100 = 0110 0100
1 = 0000 0001
---------------
XOR = 0110 0101 = 101
✅ Each bit is compared: If they’re different → 1 If they’re the same → 0
100 ^ 1 = 101
This is what makes XOR useful: you can toggle bits with a key, and reverse it by applying the same key again.
Why This?
This example teaches you:
- The reversible nature of XOR (
a ^ b ^ b == a
) - Handling bytes and slices in Rust
- Thinking about encryption as a transformation
- Why key reuse and simplicity are dangerous
Naive XOR in Rust
Here’s how to implement a basic XOR encryptor in Rust:
Filename: src/main.rs
fn main() { let message = b"hello world"; let key = b"key"; let encrypted = xor_encrypt(message, key); let decrypted = xor_encrypt(&encrypted, key); println!("Encrypted: {:x?}", encrypted); println!("Decrypted: {}", String::from_utf8_lossy(&decrypted)); } pub fn xor_encrypt(input: &[u8], key: &[u8]) -> Vec<u8> { input .iter() .enumerate() .map(|(i, &byte)| byte ^ key[i % key.len()]) .collect() }
Output
The output will show the encrypted bytes (in hex) and the original decrypted message.
What’s Wrong With This Cipher?
- Key reuse makes patterns obvious
- No randomness or initialization vector (IV)
- Susceptible to frequency analysis attacks
This cipher is insecure — but it demonstrates important cryptographic concepts:
- Reversibility
- Byte-wise transformations
- Why randomness and key handling matter
You’ll build on this when implementing real-world ciphers like ChaCha20 or AES.