Understand the Security of One Time Pad and how to Implement it in Python

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.

One Time Pad illustrated.
One Time Pad illustrated.

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.

One Time Pad is unbreakable - it is information-theoretical secure.
One Time Pad is unbreakable – it is information-theoretical secure.

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.

If the key-stream is unknown, then the cipher text can decrypt to any message.
If the key-stream is unknown, then the cipher text can decrypt to any message.

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):
    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.

Understand the Password Validation in Mac in 3 Steps – Implement the Validation in Python

What will you learn?

  • The password validation process in Mac
  • How to extract the password validation values
  • Implementing the check in Python
  • Understand why the values are as they are
  • The importance of using a salt value with the password
  • Learn why the hash function is iterated multiple times

The Mac password validation process

Every time you log into your Mac it needs to verify that you used the correct password before giving you access.

The validation process reads hash, salt and iteration values from storage and uses them to validate your password.

The 3 steps below helps you to locate your values and how the validation process is done.

Step 1: Locating and extracting the hash, salt and iteration values

You need to use a terminal to extract the values. By using the following command you should get it printed in a readable way.

sudo defaults read /var/db/dslocal/nodes/Default/users/<username>.plist ShadowHashData | tr -dc 0-9a-f | xxd -r -p | plutil -convert xml1 - -o -

Where you need to exchange <username> with your actual user name. The command will prompt you for admin password.

This should result in an output similar to this.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>SALTED-SHA512-PBKDF2</key>
	<dict>
		<key>entropy</key>
		<data>
                1meJW2W6Zugz3rKm/n0yysV+5kvTccA7EuGejmyIX8X/MFoPxmmbCf3BE62h
                6wGyWk/TXR7pvXKg\njrWjZyI+Fc3aKfv1LNQ0/Qrod3lVJcWd9V6Ygt+MYU
                8Eptv3uwDcYf6Z5UuF+Hg67rpoDAWhJrC1\nPEfL3vcN7IoBqC5NkIU=
		</data>
		<key>iterations</key>
		<integer>45454</integer>
		<key>salt</key>
		<data>
		6VuJKkHVTdDelbNMPBxzw7INW2NkYlR/LoW4OL7kVAI=
		</data>
	</dict>
</dict>
</plist>

Step 2: Understand the output

The output consists of four pieces.

  • Key value: SALTED-SHA512-PBKDF2
  • Entropy: Base64 encoded data.
  • Number of iteration: 45454
  • Salt: Base64 encoded data

The Key value is the tells you which algorithm is used (SHA512) and how it is used (PBKDF2).

The entropy is the actual result of the validation algorithm determined by the key value . This “value” is not an encryption of the password, which means you cannot recover the password from that value, but you can validate if the password matches this value.

Confused? I know. But you will understand when we implement the solution

The number of iterations, here 45454, is the number of times the hash function is called. Also, why would you call the hash function multiple times? Follow along and you will see.

Finally, we have the salt value. That is to ensure that you cannot determine the password from the entropy value itself. This will also get explained with example below.

Step 3: Validating the password with Python

Before we explain the above, we need to be have Python do the check of the password.

import hashlib
import base64

iterations = 45454
salt = base64.b64decode("6VuJKkHVTdDelbNMPBxzw7INW2NkYlR/LoW4OL7kVAI=".encode())
password = "password".encode()

value = hashlib.pbkdf2_hmac('sha512', password, salt, iterations, 128)
print(base64.b64encode(value))

Which will generate the following output

b'1meJW2W6Zugz3rKm/n0yysV+5kvTccA7EuGejmyIX8X/MFoPxmmbCf3BE62h6wGyWk/TXR7pvXKgjrWjZyI+Fc3aKfv1LNQ0/Qrod3lVJcWd9V6Ygt+MYU8Eptv3uwDcYf6Z5UuF+Hg67rpoDAWhJrC1PEfL3vcN7IoBqC5NkIU='

That matches the entropy content of the file.

So what happened in the above Python code?

We use the hashlib library to do all the work for us. It takes the algorithm (sha512), the password (Yes, I used the password ‘password’ in this example, you should not actually use that for anything you want to keep secret from the public), the salt and the number of iterations.

Now we are ready to explore the questions.

Why use a Hash value and not an encryption of the password?

If the password was encrypted, then an admin on your network would be able to decrypt it and misuse it.

Hence, to keep it safe from that, an iterated hash value of your password is used.

A hash function is a one-way function that can map any input to a fixed sized output. A hash function will have these important properties in regards to passwords.

  • It will always map the same input to the same output. Hence, your password will always be mapped to the same value.
  • A small change in the input will give a big change in output. Hence, if you change one character in the password (say, from ‘password’ to ‘passward’) the hash value will be totally different.
  • It is not easy to find the given input to a hash value. Hence, it is not easily feasible to find your password given the hash value.

Why use multiple iterations of the hash function?

To slow it down.

Basically, the way your find passwords is by trying all possibilities. You try ‘a’ and map it to check if that gives the password. Then you try ‘b’ and see.

If that process is slow, you decrease the odds of someone finding your password.

To demonstrate this we can use the cProfile library to investigate the difference in run-time. First let us try it with the 45454 iterations in the hash function.

import hashlib
import base64
import cProfile


def crack_password(entropy, iterations, salt):
    alphabet = "abcdefghijklmnopqrtsuvwxyz"
    for c1 in alphabet:
        for c2 in alphabet:
            password = str.encode(c1 + c2)
            value = base64.b64encode(hashlib.pbkdf2_hmac('sha512', password, salt, iterations, 128))
            if value == entropy:
                return password


entropy = "kRqabDBsvkyAhpzzVWJtdqbtqgkgNPwr5gqWG6jvw73hxc7CCvC4E33WyR5bxKmAXG5vAG9/ue+DC7BYLHRfOTE/dLKSMdpE9RFH7ZlTp7GHdH5b5vaqQCcKlXAwkky786zvpucDIgGGTOyw6kKB5hqIXLX9chDvcPQksVrjmUs=".encode()
iterations = 45454
salt = base64.b64decode("6VuJKkHVTdDelbNMPBxzw7INW2NkYlR/LoW4OL7kVAI=".encode())

cProfile.run("crack_password(entropy, iterations, salt)")

This results in a run time of.

        1    0.011    0.011   58.883   58.883 ShadowFile.py:6(crack_password)

About 1 minute.

If we change the number of iterations to 1.

import hashlib
import base64
import cProfile


def crack_password(entropy, iterations, salt):
    alphabet = "abcdefghijklmnopqrtsuvwxyz"
    for c1 in alphabet:
        for c2 in alphabet:
            password = str.encode(c1 + c2)
            value = base64.b64encode(hashlib.pbkdf2_hmac('sha512', password, salt, iterations, 128))
            if value == entropy:
                return password


entropy = "kRqabDBsvkyAhpzzVWJtdqbtqgkgNPwr5gqWG6jvw73hxc7CCvC4E33WyR5bxKmAXG5vAG9/ue+DC7BYLHRfOTE/dLKSMdpE9RFH7ZlTp7GHdH5b5vaqQCcKlXAwkky786zvpucDIgGGTOyw6kKB5hqIXLX9chDvcPQksVrjmUs=".encode()
iterations = 1
salt = base64.b64decode("6VuJKkHVTdDelbNMPBxzw7INW2NkYlR/LoW4OL7kVAI=".encode())

cProfile.run("crack_password(entropy, iterations, salt)")

I guess you are not surprised it takes less than 1 second.

        1    0.002    0.002    0.010    0.010 ShadowFile.py:6(crack_password)

Hence, you can check way more passwords if only iterated 1 time.

Why use a Salt?

This is interesting.

Well, say that another user used the password ‘password’ and there was no salt.

import hashlib
import base64

iterations = 45454
salt = base64.b64decode("".encode())
password = "password".encode()

value = hashlib.pbkdf2_hmac('sha512', password, salt, iterations, 128)
print(base64.b64encode(value))
b'kRqabDBsvkyAhpzzVWJtdqbtqgkgNPwr5gqWG6jvw73hxc7CCvC4E33WyR5bxKmAXG5vAG9/ue+DC7BYLHRfOTE/dLKSMdpE9RFH7ZlTp7GHdH5b5vaqQCcKlXAwkky786zvpucDIgGGTOyw6kKB5hqIXLX9chDvcPQksVrjmUs='

Then you would get the same hash value.

Hence, for each user password, there is a new random salt used.

How to proceed from here?

If you want to crack passwords, then I would recommend you use Hashcat.

How Caesar Cipher Teaches us the Most Valuable Lesson – Learn Kerckhoff’s Principle in 5 Steps with Python Code

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 is a Substitution Cipher and is it Secure?

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.

Substitution Cipher Example
Substitution Cipher Example

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 = chars.pop(random.randint(0, len(chars) - 1))
    return key


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


def encrypt(key, message):
    cipher = ""
    for c in message:
        if c in key:
            cipher += key
        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.

Substitution Cipher with key-space calculation
Substitution Cipher with key-space calculation

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 = 0

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

for c in freq:
    freq = round(freq/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.

Understand Caesar Cipher by Implementing it in Python

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 = chars[(cnt + n) % len(chars)]
        cnt += 1
    return key


def encrypt(key, message):
    cipher = ""
    for c in message:
        if c in key:
            cipher += key
        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 = chars[(cnt + n) % len(chars)]
        cnt += 1
    return key


def encrypt(key, message):
    cipher = ""
    for c in message:
        if c in key:
            cipher += key
        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.