# Cryptography

## Contents

**Where do we find cryptography?**

Cryptography : design of primitives. A lot of digitals systems use cryptography system.

Science of information security :

- Confidentiality
- Data integrety
- Entity authentification
- Availability

Example : Encryption, Digital signature, e-commerce, Blockchain, SmartCards (banking card), USB tokens, SIM card (mobile phone), Storages (bitlocker), RFID access, Netbanking, Cars.

**Cryptography**

## History

There is a lot of algorithms to encrypt. For example,

**Rot-13**:replaces one letter with another 13 letters further down the alphabet. Each letter is shifted 13 positions in the alphabet.(e.g. abcd → nopq)**Carré de Vigenère**: It is a polyalphabetic substitution encryption system. A matrix is used to find the original text.

## Symmetric (AES) & Assymetric (RSA/ECC)

**Symmetric encryption** is a type of encryption where only one key (a secret key) is used to both encrypt and decrypt electronic information. The entities communicating via symmetric encryption must exchange the key so that it can be used in the decryption process.
**Asymmetric Encryption**, also known as Public-Key Cryptography, is an example of one type. Unlike “normal” (symmetric) encryption, Asymmetric Encryption encrypts and decrypts the data using two separate yet mathematically connected cryptographic keys. These keys are known as a 'Public Key' and a 'Private Key.

## Hashing(SHA256)

**Bruteforce:**A brute force attack is a trial and error method used by application programs to decode encrypted data such as passwords or Data Encryption Standard (DES) keys, through exhaustive effort (using brute force) rather than employing intellectual strategies. Just as a criminal might break into, or "crack" a safe by trying many possible combinations, a brute force attacking application proceeds through all possible combinations of legal characters in sequence.**Dictionary:**It is simply a way to try every password into the dictonnary that you created.**Rainbow table:**Rainbow tables are tables of reversed hashes used to crack password hashes. Computer systems requiring passwords typically store the passwords as a hash value of the user's password. When a computer user enters a password, the system hashes the password and compares it to the stored hash.

## Steganography vs Cryptography

Said simply cryptography techniques hide messages. Well, if you remember at our first exercise session on Python, we tried to understand the world of steganography by hiding an image inside another. However, this could go far beyond hiding an image into an image. Then, what are the key differences between steganography and cryptography?

Steganography is a technique that hides a secret message into another a message or media. Well, the origin of steganography comes from *steganographia* which is a combination of *steganos* and *graphia*. The first one means "covered or concealed". This is the first difference. In fact, the goal of cryptography is not to "hide" (in the strict sense). Simply said, the sender converts the text into cypher text, then send it to the receiver which convert it back into readable text. So, the message is hidden but you can still see that something is not right while in steganography, the message is hidden within the bites of the media transferred.

It seems funny, right? Let us crack the riddle asked by Professor Hoffreumon at the python practical session.

The goal is to hide this text into this young image of Alan Turing.

#### Idea

The idea is to first analyse the text and the image on their own. Then to compare, we need to make the RGB value matching the text, once converted into bytes, according to if they are odd or even. Once the RGB value is matched we can then recompile the image into pillow to bring a new image with the text of Alan Turing inside it. Here a list of the steps developed below.

The decoding process is very similar to the encoding though it is reversed. However, the issue of the image was at first complicated to manage. Adapting images to the exact number of UTF-8 elements was tricky. Then I figured out that it was not a big deal if the text is "coded" only on the part of the image and we may let the rest of the image there. The trick that I used here was to determine an endpoint to the message so when decoding, the code would be able to differentiate between the real message and the rest of the image.

Aside, the goal here is to create our first steganography algorithm from scratch, even though several libraries already exists, this is not our goal ('cause we are brave women and men)

#### Encoding Process

- Open the text and image
- Create an end message that may simplify the decoding process
- The text is encoded in UTF-8 and opened as a list
- The list will pass through several functions in order to transform it into a list of ones and zeros.
- Then we will convert the coordinates of images into a straight list
- The coordinates (which are here only RGB) will be converted into 1 and 0 depending on if they are odd or even. Also, we need to take care of extreme numbers in order to not go out of 8 bits which may cause problems when encoding and decoding. When converting, it is very important to look at what the text array in a byte is and then slightly adjusting the coordinate to match the corresponding bite.
- Once the adaption made, the coordinates in pixels could be rearranged and them recompile into an image.

Some useful import (do not forget to 'pip install' them also)...

```
from os.path import split
from pprint import pprint
from PIL import Image
```

Creating some functions to make the code a bit clearer, even though it is certainly not the cleanest code (try to enhance it by yourself, it will be interesting)...

```
def decimalToBinary(liste):
binary = []
for x in liste:
t = bin(x).replace("0b", "0")
if len(t) <= 7:
factor = 8 - len(t)
t = factor*'0'+t
if len(t) > 8:
factorBis = len(t)-8
t = t[factorBis:]
binary.append(t)
return binary
def binaryToContinuousList(list):
liste = []
for x in list:
for t in x:
liste.append(int(t))
return liste
def binaryToDecimal(liste):
decimal = []
for x in liste:
t = int(x, 2)
decimal.append(t)
return decimal
def image_from_list(pixels_list, size, mode="RGB"):
img = Image.new(mode, size)
img.putdata(pixels_list)
return img
def QuickCheck(liste):
count = 0
for x in liste:
if x > 255:
count += 1
if count != 0:
print('Error, superior than 255')
def checkBytes(liste):
t = []
for x in liste:
if len(x) != 8:
t.append(x)
return t
def concanetor(liste):
number = ""
for t in liste:
number += str(t)
return number
```

Open the text and the image locally (change the root of the file in the .open(YOUR_ROOT)). Get the data from image such as its RGB coordinates or its width or height. Also, the importance to define the endpoint to the message.

```
text = list(open("YOUR_ROOT/computing_machinery_and_intelligence.txt",
'r').read().encode(encoding='UTF-8'))
im = Image.open("YOUR_ROOT/AlanTuring.jpg")
pixels = list(im.getdata())
width = im.width
height = im.height
end = list("///EndOfMessage///".encode(encoding='UTF-8'))
text = text+end
```

The encoding process

```
textBytes = decimalToBinary(text)
continuousList = binaryToContinuousList(textBytes)
pixel_in_a_row = []
for t in pixels:
x = list(t)
for i in x:
pixel_in_a_row.append(i)
i = 0
while i < len(continuousList):
if pixel_in_a_row[i] % 2 == 0:
if continuousList[i] != 0:
if pixel_in_a_row[i] != 255:
pixel_in_a_row[i] += 1
else:
pixel_in_a_row[i] -= 1
else:
if continuousList[i] != 1:
if pixel_in_a_row[i] != 255:
pixel_in_a_row[i] += 1
else:
pixel_in_a_row[i] -= 1
i += 1
QuickCheck(pixel_in_a_row)
the_hidden_meme = []
z = []
for i in pixel_in_a_row:
z.append(i)
if len(z) == 3:
r = tuple(z)
the_hidden_meme.append(r)
elif len(z) > 3:
d = z[3]
z.clear()
z.append(d)
image_from_list(the_hidden_meme, [width, height])
```

#### Decoding Process

The trick here is to do the encoding in revert, and thanks to the endpoint, the code could manage to split the real message to the rest of the image(which may induce errors). This trick allows us to not deal image size otherwise we would have to resize the image to fit the text and then decode the whole image.

Once you have run the codes above, you should have an image that you can save and then reuse for the decoding process.

```
im = Image.open("YOUR_ROOT/EncodedUnknown.png")
pixels = list(im.getdata())
```

Then here is how to decode the image:

```
pixel_in_a_row = []
for t in pixels:
x = list(t)
for i in x:
pixel_in_a_row.append(i)
continuesText = []
for i in pixel_in_a_row:
if i % 2 == 0:
continuesText.append(0)
else:
continuesText.append(1)
strings_of_numbers = []
t = []
for x in continuesText:
t.append(x)
if len(t) == 8:
strings_of_numbers.append(concanetor(t))
elif len(t) > 8:
x = t[8]
t.clear()
t.append(x)
decimalText = binaryToDecimal(strings_of_numbers)
b = bytearray(decimalText)
t = b.decode(errors='ignore')
FinalMessage = t.split(sep="///EndOfMessage///", maxsplit=1)
print(FinalMessage[0])
```

#### Conclusion

To conclude, the code above can perfectly hide a text inside an image using a very simple way to do so by checking only odd and even attribute to RGB coordinates.

However, many other alternatives to such an algorithm exist such as cosine transformations and so on. Such algorithms would take much more time to develop from scratch when compared to the algorithm above.

Another solution would be to use existing libraries such as "stegano". It will be much simpler and maybe more performant but less fun to do (and not so personally rewarding neither).

##### Limitations

The way I thought the process dispose of major drawback despite its easiness and its independency to image size. In fact, the first is common to all algorithms is that the image should not be smaller than the size of the text you want to hide in terms of bites, otherwise there are not enough pixels. Then, the second one is that the decoding process is not optimized at all since the whole image is decoded and then we split between the real message and the rest.

#### Full Code

```
import os.path
from os.path import split
from pprint import pprint
from PIL import Image
def decimalToBinary(liste):
binary = []
for x in liste:
t = bin(x).replace("0b", "0")
if len(t) <= 7:
factor = 8 - len(t)
t = factor*'0'+t
if len(t) > 8:
factorBis = len(t)-8
t = t[factorBis:]
binary.append(t)
return binary
def binaryToContinuousList(list):
liste = []
for x in list:
for t in x:
liste.append(int(t))
return liste
def binaryToDecimal(liste):
decimal = []
for x in liste:
t = int(x, 2)
decimal.append(t)
return decimal
def image_from_list(pixels_list, size, mode="RGB"):
img = Image.new(mode, size)
img.putdata(pixels_list)
return img
def QuickCheck(liste):
count = 0
for x in liste:
if x > 255:
count += 1
if count != 0:
print('Error, superior than 255')
def checkBytes(liste):
t = []
for x in liste:
if len(x) != 8:
t.append(x)
return t
def concanetor(liste):
number = ""
for t in liste:
number += str(t)
return number
def Encode():
text = list(open("YOUR_ROOT/computing_machinery_and_intelligence.txt",
'r').read().encode(encoding='UTF-8'))
im = Image.open(
"YOUR_ROOT/AlanTuring.jpg")
pixels = list(im.getdata())
width = im.width
height = im.height
end = list("///EndOfMessage///".encode(encoding='UTF-8'))
text = text+end
textBytes = decimalToBinary(text)
continuousList = binaryToContinuousList(textBytes)
pixel_in_a_row = []
for t in pixels:
x = list(t)
for i in x:
pixel_in_a_row.append(i)
i = 0
while i < len(continuousList):
if pixel_in_a_row[i] % 2 == 0:
if continuousList[i] != 0:
if pixel_in_a_row[i] != 255:
pixel_in_a_row[i] += 1
else:
pixel_in_a_row[i] -= 1
else:
if continuousList[i] != 1:
if pixel_in_a_row[i] != 255:
pixel_in_a_row[i] += 1
else:
pixel_in_a_row[i] -= 1
i += 1
QuickCheck(pixel_in_a_row)
the_hidden_meme = []
z = []
for i in pixel_in_a_row:
z.append(i)
if len(z) == 3:
r = tuple(z)
the_hidden_meme.append(r)
elif len(z) > 3:
d = z[3]
z.clear()
z.append(d)
image_from_list(the_hidden_meme, [width, height]).show()
Encode()
def Decode():
im = Image.open(
"YOUR_ROOT/EncodedAlanTuring.png")
pixels = list(im.getdata())
pixel_in_a_row = []
for t in pixels:
x = list(t)
for i in x:
pixel_in_a_row.append(i)
continuesText = []
for i in pixel_in_a_row:
if i % 2 == 0:
continuesText.append(0)
else:
continuesText.append(1)
strings_of_numbers = []
t = []
for x in continuesText:
t.append(x)
if len(t) == 8:
strings_of_numbers.append(concanetor(t))
elif len(t) > 8:
x = t[8]
t.clear()
t.append(x)
decimalText = binaryToDecimal(strings_of_numbers)
b = bytearray(decimalText)
t = b.decode(errors='ignore')
FinalMessage = t.split(sep="///EndOfMessage///", maxsplit=1)
print(FinalMessage[0])
Decode()
```