## What is a One Time Pad?

A One Time Pad is a information-theoretical secure encryption function. But what does that mean?

On a high level the One Time Pad is a simple XOR function that takes the input and xor’s that with a key-stream.

Encryption and decryption are identical.

• Encryption: Takes the plaintext and the key-stream and xor’s it to get the cipher text.
• Decryption: Takes the cipher text and the (same) key-stream and xor’s it to get the plaintext.

Some requirements of the One Time Pad are.

• Key-stream should only be used once.
• Key-stream should only be known by the sender and the receiver.
• Key-stream should be generated by true randomness

Hence, the requirement are only on the key-stream, which obviously is the only input to the algorithm.

The beauty of the algorithm is the simplicity.

## Understand the Security of the One Time Pad

The One Time Pad is information-theoretical secure.

That means, that even if the evil adversary had infinite computing power, it could not break it.

The simples way to understand why that is the case is the following. If an adversary catches an encrypted message, which has length, say 10 characters. It can decrypt to any message of length 10.

The reason is, that the key-stream can be anything and is a long as the message itself. That implies, that the plaintext can be possible message of 10 characters.

## Implementation in Python

Obviously, we have a dilemma. We cannot generate a key like that in Python.

The actual implementation of the One Time Pad is done by a simple xor.

```def xor_bytes(key_stream, message):
return bytes([key_stream[i] ^ message[i] for i in range(length)])
```

Of course, this requires that the key_stream and message have the same length.

It also leaves out the problem of where the key_stream comes from. The problem is, that you cannot create a key_stream with the required properties in Python.

## Demonstrate the security in Python

If you were to receive a message encrypted by a One Time Pad, then for any guess of the plaintext, there is a matching key-stream to get it.

See the code for better understanding it.

```def xor_bytes(key_stream, message):
length = min(len(key_stream), len(message))
return bytes([key_stream[i] ^ message[i] for i in range(length)])

cipher
# cipher is the cipher text
# len(cipher) = 10

# If we guess that the plaintext is "DO ATTACK"
# Then the corresponding key_stream can be computes as follows
message = "DO ATTACK"
message = message.encode()
key_stream = xor_bytes(message, cipher)

# Similar, if we guess the plaintext is "NO ATTACK"
# Then the corresponding key_stream can be computes as follows
message = "NO ATTACK"
message = message.encode()
guess_key = xor_bytes(message, cipher)

```

## Conclusion

While One Time Pads are ideal encryption system, they are not practical. The reason is, that there is no efficient way to generate and distribute a true random key-stream, which is only used once and not known by others than sender and receiver.

The Stream Cipher is often used, as a compromise for that. Examples of stream ciphers are A5/1.

## What will we cover?

• Understand the challenge to send a secret message
• Understand the Caesar Cipher
• How to create an implementation of that in Python
• How to break the Caesar Cipher
• Understand the importance of Kerckhoff’s Principle

## Step 1: Understand the challenge to send a secret message

In cryptography you have three people involved in almost any scenario. We have Alice that wants to send a message to Bob. But Alice want to send it in a way, such that she ensures that Eve (the evil person) cannot understand it.

But let’s break with tradition and introduce an addition person, Mike. Mike is the messenger. Because we are back in the times of Caesar. Alice represent one of Caesar close generals that needs to send a message to the front lines of the army. Bob is in the front line and waits for a command from Alice. DO ATTACK or NO ATTACK.

Alice will use Mike, the messenger, to send that message to Bob.

Alice is of course afraid of that Eve, the evil enemy, will capture Mike along the way.

Of course, as Alice is smart, she knows that Mike should not understand the message he is delivering, and Eve should not be able to understand it as well. It should only add value to Bob, when Mike gives him the message.

That is the problem that Caesar wanted to solve with his cipher system.

## Step 2: Understand the Caesar Cipher

Let’s do this a bit backwards.

You receive the message. BRX DUH DZHVRPH

That is pretty impossible to understand. But if you were told that this is the Caesar Cipher using the shift of 3 characters. Then maybe it makes sense.

As you can see, then green letters are the plaintext characters and the red letters are the encrypted cipher text letters. Hence, A will be a D. That is the letter A is shifted 3 characters down the row.

Reversing this, you see the the encrypted B, will map to the plaintext Y.

If you continue this process you will get.

That is a nice message to get.

## Step 3: How to create an implementation of that in Python

Well, that is easy. There are many ways to do it. I will make use of the dictionary to make my life easy.

```def generate_key(n):
letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
key = {}
cnt = 0
for c in letters:
key[ c] = letters[(cnt + n) % len(letters)]
cnt += 1
return key

def get_decryption_key(key):
dkey = {}
for c in key:
dkey[key[ c]] = c
return dkey

def encrypt(key, message):
cipher = ""
for c in message:
if c in key:
cipher += key[ c]
else:
cipher += c
return cipher

# This is setting up your Caesar Cipher key
key = generate_key(3)
# Hmm... I guess this will print the key
print(key)
# This will encrypt the message you have chose with your key
message = "YOU ARE AWESOME"
cipher = encrypt(key, message)
# I guess we should print out your AWESOME message
print(cipher)
```

## Step 4: How to break the Caesar Cipher

If you look at it like this. There is a flaw in the system. Can you see what?

Yes, of course you can. We are in the 2020ies and not back in the times of Caesar.

The key space is too small.

Breaking it basically takes the following code.

```# this is us breaking the cipher
print(cipher)
for i in range(26):
dkey = generate_key(i)
message = encrypt(dkey, cipher)
print(message)
```

You read the code correct. There are only 26 keys. That means, that even back in the days of Caesar this could be done in hand.

This leads us to the most valuable lesson in cryptography and most important principle.

## Step 5: Understand the importance of Kerckhoff’s Principle

Let’s just recap what happened here.

Alice sent a message to Bob that Eve captured. Eve did not understand it.

But the reason why Eve did not understand it, was not because she did not have the key.

No, if she knew the algorithm.

Yes, if Eve knew the algorithm of Caesar Cipher, she would not need the secret key to break it.

This leads to the most important lesson in cryptography. Kerckhoff’s Principle.

Eve should not be able to break the ciphers even when she knows the cipher.

Kerckhoff’s Principle

That is seems counterintuitive, right? Yes, but think about it, if you system is secure against any attack even if you reveal your algorithm, then it would give you more confidence that it is secure.

You security should not be based on keeping the algorithm secret. No it should be based on the secret key.

## Is that principle followed?

No.

Most government ciphers are kept secret.

Many secret encryption algorithms that leaked were broken.

This also includes the one used for mobile traffic in the old G2 network. A5/1 and the export version A5/2.

## What will you learn?

• What is a Substitution Cipher?
• How to implement the Substitution Cipher in Python
• Understand the key space of Substitution Cipher
• The weakness and how to break the Substitution Cipher

## What is a Substitution Cipher

Imagine you receive this message: IQL WPV WCVJQEV

What does it mean?

You were told it is a Substitution Cipher, but how will that help you?

First of all, we need to understand what a Substitution Cipher is. Basically, it is just rearranging the characters. That is every time you write an A, you exchange that with, say, Q. And B with G. C with, hey, let’s keep the C.

See the mapping in the picture below.

That seems pretty solid. Right?

But can we figure out what your message means?

First, let’s try to implement a Substitution Cipher.

## Implementing Substitution Cipher in Python

We will use the random library to generate random keys. We’ll get back to how many keys are there.

```import random

def generate_key():
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
chars = list(alphabet)
key = {}
for c in alphabet:
key[c] = chars.pop(random.randint(0, len(chars) - 1))
return key

def print_key(key):
for c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
print(c, key[c])

def encrypt(key, message):
cipher = ""
for c in message:
if c in key:
cipher += key[c]
else:
cipher += " "
return cipher

def get_decrypt_key(key):
dkey = {}
for k in key:
dkey[key[k]] = k
return dkey

key = generate_key()
print(key)
cipher = encrypt(key, "YOU ARE AWESOME")
print(cipher)
dkey = get_decrypt_key(key)
message = encrypt(dkey, cipher)
print(message)
```

Well, let’s understand the key-space. How secure is the Substitution Cipher?

## The key-space of the Substitution Cipher

Let’s examine how the keys are generated.

The first letter, A, can be mapped to 26 possibilities (including A itself). The next letter, B, can be mapped to 25 possibilities. The third letter, C, can be mapped to (difficult to guess) 24 possibilities. And so forth.

This generates a space of (read the large number in the picture above). That is right. It is more than 88 bits of security.

That is a lot.

88 bits of security? That is you need to try (more than) 309485009821345068724781056 possibilities.

A whole lot. And that, I promise you, should be considered secure.

But wait, Substitution Cipher is not used any more, why? Read on, and you will know in a moment.

## The weakness of Substitution Cipher

If the underlying language is English, then you can make a simple frequency analysis of how often the letters occur on average in English.

It turns out to be quite simple.

```letter_freq = {'a': 0.0817, 'b': 0.0150, 'c': 0.0278, 'd': 0.0425, 'e': 0.1270, 'f': 0.0223,
'g': 0.0202, 'h': 0.0609, 'i': 0.0697, 'j': 0.0015, 'k': 0.0077, 'l': 0.0403,
'm': 0.0241, 'n': 0.0675, 'o': 0.0751, 'p': 0.0193, 'q': 0.0010, 'r': 0.0599,
's': 0.0633, 't': 0.0906, 'u': 0.0276, 'v': 0.0098, 'w': 0.0236, 'x': 0.0015,
'y': 0.0197, 'z': 0.0007}
```

That is, the letter ‘a’ occurs 8.17% percent probability. ‘b’ with 1.5%. ‘c’ with 2.78%.

Hence, given the text: IQL WPV WCVJQEV

Well, we are out of luck, because it is too short to have any frequency analysis to have any significance.

But the following text.

```lrvmnir bpr sumvbwvr jx bpr lmiwv yjeryrkbi jx qmbm wi
bpr xjvni mkd ymibrut jx irhx wi bpr riirkvr jx
ymbinlmtmipw utn qmumbr dj w ipmhh but bj rhnvwdmbr bpr
yjeryrkbi jx bpr qmbm mvvjudwko bj yt wkbrusurbmbwjk
lmird jk xjubt trmui jx ibndt
wb wi kjb mk rmit bmiq bj rashmwk rmvp yjeryrkb mkd wbi
iwokwxwvmkvr mkd ijyr ynib urymwk nkrashmwkrd bj ower m
vjyshrbr rashmkmbwjk jkr cjnhd pmer bj lr fnmhwxwrd mkd
wkiswurd bj invp mk rabrkb bpmb pr vjnhd urmvp bpr ibmbr
jx rkhwopbrkrd ywkd vmsmlhr jx urvjokwgwko ijnkdhrii
ijnkd mkd ipmsrhrii ipmsr w dj kjb drry ytirhx bpr xwkmh
mnbpjuwbt lnb yt rasruwrkvr cwbp qmbm pmi hrxb kj djnlb
bpmb bpr xjhhjcwko wi bpr sujsru msshwvmbwjk mkd
wkbrusurbmbwjk w jxxru yt bprjuwri wk bpr pjsr bpmb bpr
riirkvr jx jqwkmcmk qmumbr cwhh urymwk wkbmvb
```

This has enough letters to make an analysis of the letters. Let’s try.

```cipher = """lrvmnir bpr sumvbwvr jx bpr lmiwv yjeryrkbi jx qmbm wi
bpr xjvni mkd ymibrut jx irhx wi bpr riirkvr jx
ymbinlmtmipw utn qmumbr dj w ipmhh but bj rhnvwdmbr bpr
yjeryrkbi jx bpr qmbm mvvjudwko bj yt wkbrusurbmbwjk
lmird jk xjubt trmui jx ibndt
wb wi kjb mk rmit bmiq bj rashmwk rmvp yjeryrkb mkd wbi
iwokwxwvmkvr mkd ijyr ynib urymwk nkrashmwkrd bj ower m
vjyshrbr rashmkmbwjk jkr cjnhd pmer bj lr fnmhwxwrd mkd
wkiswurd bj invp mk rabrkb bpmb pr vjnhd urmvp bpr ibmbr
jx rkhwopbrkrd ywkd vmsmlhr jx urvjokwgwko ijnkdhrii
ijnkd mkd ipmsrhrii ipmsr w dj kjb drry ytirhx bpr xwkmh
mnbpjuwbt lnb yt rasruwrkvr cwbp qmbm pmi hrxb kj djnlb
bpmb bpr xjhhjcwko wi bpr sujsru msshwvmbwjk mkd
wkbrusurbmbwjk w jxxru yt bprjuwri wk bpr pjsr bpmb bpr
riirkvr jx jqwkmcmk qmumbr cwhh urymwk wkbmvb"""

alphabet = "abcdefghijklmnopqrstuvwxyz"
freq = {}
for c in alphabet:
freq[c] = 0

cnt = 0
for c in cipher:
if c in alphabet:
freq[c] += 1
cnt += 1

for c in freq:
freq[c] = round(freq[c]/cnt, 4)

print(freq)
```

Will give you the following output.

```{'a': 0.0077, 'b': 0.1053, 'c': 0.0077, 'd': 0.0356, 'e': 0.0077, 'f': 0.0015, 'g': 0.0015, 'h': 0.0356, 'i': 0.0635, 'j': 0.0743, 'k': 0.0759, 'l': 0.0124, 'm': 0.096, 'n': 0.0263, 'o': 0.0108, 'p': 0.0464, 'q': 0.0108, 'r': 0.13, 's': 0.0263, 't': 0.0201, 'u': 0.0372, 'v': 0.0341, 'w': 0.0728, 'x': 0.031, 'y': 0.0294, 'z': 0.0}
```

This gives you some hints on how the letters are mapped.

## What will we cover?

• Understand what Caesar Cipher is
• Implement Caesar Cipher in Python
• Understand the weakness of Caesar Cipher

## What is Caesar Cipher

Caesar Cipher is a simple substitution cipher, which is limited to only shift the characters by fix number.

Imagine you got the message: BRX DUH DZHVRPH. What to make out of it. Makes no sense. Just ignore it, right?

Well, for the untrained eye, yes, ignore it. But why would anyone bother sending you that message? Aren’t you curious?

I would be. Let’s say your friend told you it was a Caesar Cipher with a 3-key shift. What does that mean?

In the picture below, you can see how the letters are shifted from the green letters (plain text), to the encrypted red letters (cipher text). As an example, A is encrypted to D, and B is encrypted to E, and so forth.

By using that strategy, you can also figure out that a cipher B (red letters) maps to the plain Y (green letters). A cipher R maps to the plain O. And so forth. That results in that your secret message decrypts to YOU ARE AWESOME.

What a nice message to get.

## Implementation of Caesar Cipher in Python

Below is an example of how you could implement the Caesar Cipher in Python.

```def generate_key(n):
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
key = {}
cnt = 0
for c in chars:
key[c] = chars[(cnt + n) % len(chars)]
cnt += 1
return key

def encrypt(key, message):
cipher = ""
for c in message:
if c in key:
cipher += key[c]
else:
cipher += c
return cipher

def get_decrypt_key(key):
dkey = {}
for k in key:
dkey[key[k]] = k
return dkey

key = generate_key(3)
dkey = generate_key(23)
cipher = encrypt(key, "YOU ARE AWESOME")
print(cipher)
message = encrypt(dkey, cipher)
print(message)
dkey = get_decrypt_key(key)
print(encrypt(dkey, cipher))
```

There are some things to notice. First of all, the generate_key function comes in handy, when we extend our cipher function to the more general substitution cipher.

Also, notice, that encryption and decryption are done with the same function, just different keys. The decryption key of key-3 is key-23.

See that you can actually calculate the decryption key quite nice, as done in the get_decrypt_key function, which can be used if this is extended to a general substitution cipher.

## How to de-cipher a Caesar Cipher message

Image you had received the message BRX DUH DZHVRPH, but didn’t know the key. What to do?

No worries, there is a way to solve that problem efficiently.

The scenario is that Alice wants to send Bob a secret message, but someone (you) get’s a copy of the message (and you are called Eve).

The secrecy of the message was intended to be, that you (Eve) does not know the Algorithm (how the encryption is done). That might seems naive today, but back in the time of Caesar, this was the state of the art, and people were not that knowledgable about the art of cryptography.

But you are in luck. Yes, I am talking to you Eve.

You know the Algorithm and you are soon to figure out, that the key space is quite small. Actually it only contains 26 possible keys.

That is in the reach of you manually trying out all possible keys.

But you are in more luck, because you realised that you also have Python. So let’s try to fix it in Python.

```def generate_key(n):
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
key = {}
cnt = 0
for c in chars:
key[c] = chars[(cnt + n) % len(chars)]
cnt += 1
return key

def encrypt(key, message):
cipher = ""
for c in message:
if c in key:
cipher += key[c]
else:
cipher += c
return cipher

cipher = "BRX DUH DZHVRPH"
for i in range(26):
key = generate_key(i)
message = encrypt(key, cipher)
print(message)
```

That will generate the following output.

```CSY EVI EAIWSQI
DTZ FWJ FBJXTRJ
EUA GXK GCKYUSK
FVB HYL HDLZVTL
GWC IZM IEMAWUM
HXD JAN JFNBXVN
IYE KBO KGOCYWO
JZF LCP LHPDZXP
KAG MDQ MIQEAYQ
LBH NER NJRFBZR
MCI OFS OKSGCAS
NDJ PGT PLTHDBT
OEK QHU QMUIECU
PFL RIV RNVJFDV
QGM SJW SOWKGEW
RHN TKX TPXLHFX
SIO ULY UQYMIGY
TJP VMZ VRZNJHZ
UKQ WNA WSAOKIA
VLR XOB XTBPLJB
WMS YPC YUCQMKC
XNT ZQD ZVDRNLD
YOU ARE AWESOME
ZPV BSF BXFTPNF
AQW CTG CYGUQOG
```

That was awesome right.

## Conclusion

The security of Caesar Cipher was by keeping the Algorithm secret. That approach to security is not used anymore. Kerckhoffs’ Principle states that the adversary (Eve) should not be able to break the cipher even when she knows the Algorithm.