Cryptography

The Cryptopals Crypto Challenges: Set 1 - Fixed XOR

Write a method that takes two strings of fixed equal length and produce their XOR combination.

Abdush Shakoor December 12th, 2017

This is the second challenge of Set 1 in The Cryptopals Crypto Challenges website. Previously, I spoke about these challenges and provided a walkthrough for the first challenge, if you haven't read them, here are the links:

For this challenge, you must write a method that takes two strings of fixed equal length and produce their XOR combination:

When you feed the following Hexadecimal string:

    
    1c0111001f010100061a024b53535009181c
    

And perform an XOR operation against another Hexadecimal string:

    
    686974207468652062756c6c277320657965
    

The method should return the following result:

    
    746865206b696420646f6e277420706c6179
    

Like the first challenge, this is sort of a warmup and a simple challenge to tackle. Just to give you a heads up, every challenge that you solve in this set would all make sense, in the end, as the challenges will get tougher and much more interesting.

How do I solve this?

As I mentioned above, this problem is simple and pretty straightforward. In my previous post, I talked about Bitwise Manipulations and it's operators, I will be using the XOR (\^) operator, if you want to know more about it, check out my previous post. Also, I will reuse some of the functions that I had used in the first challenge. So let's dive in to the code:

Methods that are being reused:

    
    //Hashmap that contain hex key and binary values
    map<char, string> CryptoLib::gen_hex_table()
    {
        map<char, string> map;

        map['0'] = "0000";
        map['1'] = "0001";
        map['2'] = "0010";
        map['3'] = "0011";
        map['4'] = "0100";
        map['5'] = "0101";
        map['6'] = "0110";
        map['7'] = "0111";
        map['8'] = "1000";
        map['9'] = "1001";
        map['a'] = "1010";
        map['b'] = "1011";
        map['c'] = "1100";
        map['d'] = "1101";
        map['e'] = "1110";
        map['f'] = "1111";

        return map;
    }

    //Convert hex to string
    string CryptoLib::con_hex_2_bin(string hexStr)
    {
        map<char,string> m = gen_hex_table();

        string newStr = "";
        for(int i=0; i<hexStr.size(); i++)
        {
            if(isdigit(hexStr[i]))
            {
                newStr += m.find(hexStr[i])->second;
            }
            else
            {
                newStr += m.find(hexStr[i])->second;
            }
            // newStr += m.find(hexStr[i])->second;
        }
        return newStr;
    }

    //Convert binary to decimal
    vector<int> CryptoLib::con_bin_2_dec(string str, double power)
    {
        vector<int> v;
        string newStr = "";
        istringstream iss(str);
        string x;

        while(iss >> x)
        {
            double p = power;
            double decimal = 0.0;

            for(int i=0; i<x.size(); i++)
            {
                if(x[i] == '1')
                {
                    decimal += pow(2.0, p);
                }
                p--;
            }
            v.push_back((int)decimal);
        }
        return v;
    }

    //Add spaces between strings
    string CryptoLib::add_spaces(string str, int spaces)
    {
        string newStr = "";
        int count = 0;

        for(int i=0; i<str.size(); i++)
        {

            // newStr += str[i];
            if(count == spaces)
            {
                newStr += " ";
                i--;
                count = 0;
            }
            else
            {
                newStr += str[i];
                count++;
            }
        }

        return newStr;
    }

    //Convert ASCII to HEX
    string CryptoLib::con_ascii_2_hex(string str)
    {
        stringstream ss;
        for(int i=0; i<str.size(); i++)
        {
            ss << std::hex << (int)str[i];
        }
        return ss.str();
    }
    

Implementation of the method:

    
    //Fixed XOR implementation
    string CryptoLib::fixedXOR(string str1, string str2)
    {
        //Check if the length of both the strings are equal
        if(str1.size() != str2.size())
        {
            return "The strings are not of equal length.";
        }
        else
        {
            string newStr = "";

            //Step 1. convert hex to binary of 8 bits
            str1 = add_spaces(con_hex_2_bin(str1), 8);
            str2 = add_spaces(con_hex_2_bin(str2), 8);

            //Step 2. convert binary to decimal
            vector<int> v1 = con_bin_2_dec(str1, 7.0);
            vector<int> v2 = con_bin_2_dec(str2, 7.0);

            //Step 3. XOR the decimals of v1 with decimals of v2
            for(int i=0; i<v1.size(); i++)
            {
                //Get the char of the first string
                unsigned char a = v1[i];

                //Get the char of the second string
                unsigned char b = v2[i];

                //Perform XOR operation against each other
                unsigned char c = a ^ b;

                //Concatenate the string
                newStr += c;
            }

            //ASCII result: the kid don't play.

            //Final result - Convert the ASCII string to Hexadecimal
            return con_ascii_2_hex(newStr); 
        }
    }
    

Final code:

    
    //CryptoPals Set 1 Challenge 2
    #include "crypto.h"

    int main()
    {
        CryptoLib crypt;

        //The test cases provided
        string str1 = "1c0111001f010100061a024b53535009181c";
        string str2 = "686974207468652062756c6c277320657965";

        cout << crypt.fixedXOR(str1, str2) << endl;
        return 0;
    }
    

Note: This solution and the library named crypto.h was built using the C++ programming language. The source code for this solution can be found on Github.

Stay tuned for the next challenge!