Cryptopals Crypto Challenges — 2: Fixed XOR

This is the second article of the Cryptofriends series. If you want to check out the previous one head to: https://medium.com/neosavvy-labs/cryptopals-crypto-challenges-1-convert-hex-to-base64-5d7e925b6e6d

Today’s challenge is also a pretty easy one. We need to XOR a hex-based string (an &str to be more precise) against a given string of the same length.

If you want to try solving it yourself head to Cryptopals and leave a comment with your solution! There are definitely multiple ways of solving these problems, and it wouldn’t surprise me if some of them are more efficient, cleaner, smarter, greater, prettier, wonderful-er than mine.

So the input string, which we are going to call xor_input, is:

let xor_input: &str = "1c0111001f010100061a024b53535009181c";

We are told that it is hex encoded, so we need to put it through the same trouble we put the input in our previous challenge.

Let’s call the function just fixed_or . No need to get creative on this one.

use self::serialize::hex::FromHex;
fn fixed_xor(xor_input: &str, xored_against: &str) -> String {
let xor_input_unhexed: Vec<u8> = xor_input.from_hex().unwrap();
}

NOTE: If you are thinking “You are lying to me! This doesn’t work!” don’t panic. This will not compile since we are telling Mr.Rustc (head of the Rust Compiling Department) we are returning a String, but we are not doing so. Just add String::from("Unacceptable Condition!”)to the end of the function for now until we are ready to return the real result. Remember not to add a semicolon at the end to tell the function you are returning that statement.

Oh! We also need to "unhex” xored_against: &str like we have done with xor_input:

use self::serialize::hex::FromHex;
fn fixed_xor(xor_input: &str, xored_against: &str) -> String {
let xor_input_vec: Vec<u8> = xor_input
.from_hex()
.unwrap();
let xored_against_vec: Vec<u8> = xored_against
.from_hex()
.unwrap();

// String::from("Unacceptable Condition!")
}

If you don’t trust The System (TM) and you think it might cheat and feed this method two &str of different lengths, feel free to drop this if block at this point comparing the length of both vectors and panicking if they are different:

if xor_input_unhexed.len() != xored_against_vec.len() {
panic!("This is how betrayal feels like, huh?);
}

If you want to read about Exceptions and Error Handling in Rust point and click here -> https://doc.rust-lang.org/book/error-handling.html

The plan is now to iterate through both vectors at the same time and XOR the elements at the same position in each vector, resulting in a brand new Vec<u8>.

To accomplish that I’m going to use .zip (Zip Docs) which has the following signature:

fn zip<U>(self, other: U) -> Zip<Self, U::IntoIter> {}
// where U is of type IntoIterator

If you have written some C++ (first lang that came to my mind that has this feature) you probably know about “templates”. U in this case sort of kind of works that way. U in our zip's case is going to be an iterator over a Vec<u8>. The self in here is going to be the Struct over which this Trait has been implemented (for more intel about Traits click here or… spell your name backwards three times).

Our .zip is going to combine both iterators into one. Witchcraft? Maybe…

Once we have one iterator to rule them all we can just map over it and XOR the pair of values and transform the resulting iterator into a collection.

To make sure we all made it this far let’s recap:

  1. Get an .iter over the first Vec<u8>.
  2. .zip over it and pass an .iter of the second Vec<u8> as argument.
  3. .map over the iterator to rule them all XORing each pair of values.
  4. .collect the result, which transforms the iterator back to a collection.

Last step once we .collect the result, we hex-encode it to a String to then return it as the final output. Remember to use ToHex from the serialize crate too.

use self::serialize::hex::{FromHex, ToHex};
fn fixed_xor(xor_input: &str, xored_against: &str) -> String {
let xor_input_unhexed: Vec<u8> = xor_input
.from_hex()
.unwrap();
let xored_against_unhexed: Vec<u8> = xored_against
.from_hex()
.unwrap();
  if xor_input_unhexed.len() != xored_against_unhexed.len() {
panic!("This is how betrayal feels like, huh?);
}
  let xored_result: Vec<u8> = xor_input_unhexed
// get first Vec<u8>'s .iter
.iter()
// get second Vec<u8>'s .iter and .zip both iterators
.zip(xored_against_unhexed.iter())
// .map over all the pairs and XOR (^) them
.map(|(&x, &y)| x ^ y)
// transform result's iterator into collection
.collect();

// return hex-encoded result
xored_result.to_hex()
}

You might be wondering why we don’t just call .to_hex right after .collect and omit the semicolon at that point so that the value is returned. If you have already tried it, you probably got an error when compiling the code. Something like:

error: the type of this value must be known in this context

Rust’s wonderful compiler (rustc) is telling you that .to_hex Trait doesn’t have enough information at that point to infer the type of the value it’s trying to be called on. By telling the compiler xored_result is of type Vec<u8> we are solving for this issue. Now rustc knows that it’s calling .to_hex on a Vec<u8>! I really enjoy working with rust’s awesome compiler (this was not sarcasm, I truly believe it is awesome).

If we want to test our code outputs the right result, we can call our fixed_xor function from our main(). But since our midi-chlorian count is pretty high, we don’t need to do so.

Check out, clone or fork the repo @ https://github.com/pabloalonsos/cryptofriends/commit/baff4390c6d6407a8c0722d402f6476f9ac67d41

*you will tell all your friends about this article…* — Jedi mind trick