Overview of this book

Cryptography is essential for protecting sensitive information, but it is often performed inadequately or incorrectly. Hands-On Cryptography with Python starts by showing you how to encrypt and evaluate your data. The book will then walk you through various data encryption methods,such as obfuscation, hashing, and strong encryption, and will show how you can attack cryptographic systems. You will learn how to create hashes, crack them, and will understand why they are so different from each other. In the concluding chapters, you will use three NIST-recommended systems: the Advanced Encryption Standard (AES), the Secure Hash Algorithm (SHA), and the Rivest-Shamir-Adleman (RSA). By the end of this book, you will be able to deal with common errors in encryption.
Title Page
Packt Upsell
Contributor
Preface
Free Chapter
Obfuscation
Hashing
Strong Encryption
Other Books You May Enjoy
Index

XOR

This section explains what XOR is on single bits with a truth table, and then shows how to do it on bytes. XOR undoes itself, so decryption is the same operation as encryption. You can use single bytes or multiple byte keys for XOR, and we will use looping to test keys. Here's the XOR truth table:

• `0 ^ 0 = 0`
• `0 ^ 1 = 1`
• `1 ^ 0 = 1`
• `1 ^ 1 = 0`

If you feed in two bits and the two bits are the same, the answer is `0.` If the bits are different, the answer is `1`.

Note

XOR operates on one bit at a time. Python indicates XOR with the `^` operator.

The truth table shows how it works. You feed in bits that are equally likely to be `0` and `1` and XOR them together, then you end up with 50% ones and zeros, which means that XOR does not destroy any information.

Here's the XOR for bytes:

• `A 0b01000001`
• `B 0b01000010`
• `XOR 0b00000011`

`A` is the number `65`, so you have `1` for `64` and `1` for `1``B` is `1` larger, and if you XOR the two of them together, all the bits match for the first 6 bits, and they're all `0`. The last two bits are different, and they turn into `1`. This is the binary value `3`, which is not a printable character, but you can express it as an integer.

The key can be single byte or multibyte. If the key is a single byte, such as `B`, then you use the same byte to encrypt every plaintext character. Just keep repeating the key over and over:

Repeat `B` for this byte, `B` for that byte, and so on. If the key is multibyte, then you repeat the pattern:

You use `B` for the first byte, `C` for the next byte, then again `B` for the next byte, `C` for the next byte, and so on.

To do this in Python, you need to loop through the bytes of a string and calculate an index to show which byte you're on. Then we enter some text from the user, calculate its length, then go through the indices from `1` up to the length of the string, starting at`0`. Then we take the text byte and just print it out here so you can see how the loop works. So, if we give it a five-character plaintext, such as `HELLO`, it just prints out the characters one by one.

To do the XOR, we'll input a plaintext and a key and then take a byte of text and a byte of key, XOR them together, and print out the results

Note `%len( key)`, which is what prevents you from running off the end of the key. It will just keep repeating the bytes in the key. So, if the key is three bytes long, this will be modulus three, so it will count as `0`, `1`, `2`, and then back to `0 1 2 0 1 2`, and so on. In this way, you can handle any length of plaintext.

If you combine uppercase and lowercase letters, you'll often find the case that XOR produces unprintable bytes. In the example that follows, we have used`HELLO``Kitty`, and a key of`qrs`. Note that some of these bytes are readily printable and some of them contain strange characters, such asEsc and Tab, which are difficult to print. Therefore, the best way to handle the output is not to attempt to print it as ASCII, but instead print it as`hex`encoded values. Instead of trying to print the bytes one by one, we combine them into a`cipher`variable, and in the end, we print out the entire plaintext, the entire key, and then the entire ciphertext in hex. In this way, it can correctly handle these strange values that are difficult to print.

Let's try this looping in Python:

1. We open the Terminal and enter the following command:
`\$ nano xor1.py`
1. When you run it, you will get the following output:

1. This is the first one that is `xor1.py`, so we input text from the user, calculate it's length, and then just print out the bytes one by one to see how the loop works. Let's run it and give it `HELLO`:
1.  It just prints out the bytes one by one. Now, let's look at the next XOR 2:

This inputs `text` and `key` the same way and goes through each byte of `text`, picks out the correct byte of `key` using the modular arithmetic, performs the XOR, and prints out the results.

1. So if we run the same file here, we take `HELLO` and a `key` as shown:
```\$ nano xor2.py
\$ python xor2.py```

So, the output is as follows:

It calculates the bytes one by one. Note how we get two equals signs here, which is the reason why you would use a multiple by `key` because the plaintext is changing but the key, is also changing and that pattern is not reflected in the output, so it's more effective obfuscation.

1. Clear that and look at the third `xor2a.py` file:

You can see that this handles the problem of unprintable bytes.

1. So, we create a variable named `cipher`, combine each byte of output here, and at the end, we encode it with `hex` instead of trying to `print` it out directly:
1. If you give it `HELLO` and then text a key of `qrs`, it will give you the plaintext `HELLO Kitty`, the key, and then the hexadecimal-encoded output, which can easily handle funny characters, such as `0 7` and `0 5`. In the next section, you'll see challenge 1—the Caesar cipher.