Crypto Algs (Part 3.0): XOR

Hello again. I know Fust3rCluck covered this, but I had something different in mind…

XOR, or Exclusive XOR, is a Boolean Logic Gate. Like most of the other logic gates, XOR takes two parameters. Thus, it is a binary logic gate.

XOR truth table

XOR is a computationally easy form of encryption. However, it certainly is breakable. What follows is my take on it.


Proof

Given: n XOR n = 0
  and: n XOR 0 = n
Plaintext: p
Ciphertext: p XOR n = c
Decrypted: c XOR n = p

p XOR n = c
c XOR n = p

(p XOR n) XOR n = p
p XOR (n XOR n) = p
n XOR n = 0
p XOR (0) = p
p = p

Implementation

encrypt.cpp

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

namespace XOR {
    std::vector<unsigned char>
    encrypt(const std::vector<unsigned char> str, const std::string& key)
    {
        std::vector<unsigned char> res(str.begin(), str.end());

        for (int i = 0, n = str.size(), m = key.length(); i < n; i++) {
            res[i] ^= key[i % m];
        }

        return res;
    }

    std::vector<unsigned char>
    decrypt(const std::vector<unsigned char>& str, const std::string& key)
    {
        return encrypt(str,key);
    }
};

The above code is pretty straightforward. I am using the std::vector container in place of std::string to avoid any awkward null-terminators, which could be present when copying an encrypted message into a new std::string. Essentially, a std::vector<unsigned char> is a string.

Notice that the decrypt(...) function simply calls the above encrypt(...) function. Unlike in the Vigenere Cipher code, there is no need to complement the key before decryption. This is because of the above proof.

#include "./encrypt.cpp"
#include <iomanip>

void hex_print(const std::vector<unsigned char>& s) {
    std::cout << std::hex;
    for (auto& c : s) {
        std::cout << int(c) << " ";
    }
    std::cout << std::hex << std::endl;
}

int main() {
    std::string s,k;
    std::getline(std::cin, s);
    std::getline(std::cin, k);

    std::vector<unsigned char> v(s.begin(),s.end());
    v = XOR::encrypt(v,k);

    hex_print(v);

    v = XOR::decrypt(v, k);

    for(auto& c : v) {
        std::cout << c;
    }
    std::cout << std::endl;

    return 0;   
}

The above code interfaces with that mini library I wrote (encrypt.cpp). We hex_print(...) the ciphertext because some of the characters could be unreadable ASCII characters. We don’t have to worry about this when we decrypt the ciphertext – as long as our encryption scheme is implemented correctly, that is…


Conclusion

You may see a few similarities between the Vigenere program I wrote and the above code. The program outlined here allows for a variable-length key, rather than a one-byte key or a statically defined key. This is both convenient and more secure.

Feel free to play around with the code. It’s all there.

Later…
@oaktree

8 Likes

Loved it mate! Keep it up! Love the series as a whole!

1 Like

This topic was automatically closed after 30 days. New replies are no longer allowed.