Caesar cipher without knowing the Key

2024/11/19 22:52:31

Hey guys if you look at my code below you will be able to see that i was able to create a program that can open a file decode the content of the file and save it into another file but i need to input the key every time but what if i don't know they key how can i calculate the key using the frequency analysis.

import sysdef decrypt(cipher, key):plain = ""
for index in range(len(cipher)):if cipher[index].isalpha():if cipher[index].isupper():plain = plain + chr((ord(cipher[index]) - 64 - key) % 26 + 64)elif cipher[index].islower():plain = plain + chr((ord(cipher[index]) - 96 - key) % 26 + 96)else:plain = plain + cipher[index]return plainin_filename = sys.argv[1]
key = int(sys.argv[2])
out_filename = sys.argv[3] with open(in_filename, "r") as f:encrypted = f.read()decrypted = decrypt(encrypted, key)with open(out_filename, "w+") as f:f.write(decrypted)
Answer

A Caesar-Cipher is a linear substitution cipher. Explanation:

Have p be your plaintext. Have k be our numerical key (<26 for the sake of this explanation). Have c be one character in p. Have I(c) be the index of c in p. Have fc(i) be a function which maps an index i to it's letter in the alphabet. Have e(c) be the 'encrypted' character of c.

Then:

e(c) = fc( I(c) + k)

Or in plain English: Each characters gets shifted by the value of k. It's therefore a Linear substitution since it always gets shifted by the same amount and the characters are substituted.

The problem with this encryption algorithm is, that you don't really scramble the plaintext or add entropy. (In harsh words, the Caesar-Cipher is closer to an encoding rather than an encryption).

Given that, we know that all matching letters in our cipher-text substitute for the exact same plain-text character.

This means, you'll be able to run statistical analysis on your Cipher-text and simply analyse the letter distribution. Different sounds/letters occur different amount of times.

So how do we apply this technique?

Based on your name, I'd guess you are German and I'll just assume that your Cipher-text is as well.

In German, the most common letters are E and I.

We're almost there!

Now, you could simply find the most common letter in your cipher-text and calculate the difference between that letter and E (difference between their indexes, of course). That will yield you the secret key!

Here is a code example:

"""
This module aims to break Caesar-ciphers from German plaintext
based     on statistics of letter distribution.The caesar cipher is a character substitution algorithm.
Bob chooses a number as the key n.
Bob then shifts every character of the plain-text by n, cycling  
through the entire alphabet.e.g.: if n = 3: "ABC" -> "DEF".Since it has a very small keyspace (26^1), it can be easily broken, 
and Mallory could even guess or bruteforce the key.
(Note: You could choose a number higher than 26. However, this won't 
increase your keyspace since any number x will
be reduced to 1-26. See: (x - (26*(x // 26)).Common letters in the german language are (in descending order):
"E","N","I","S","R" (subject to verification)The statistical approach works well for long sentences since one has 
a greater samplesize for distribution analysis.
If two or more letters appear the same amount of times, you have to 
check which key is actually correct, either
by simply reading the outputs or running them against a 
distribution_dict of that language."""ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
SPECIAL_CHARS = " ,.-;:_?!="def encrypt(plain_text, key):"""Encrypts plaintext using a caesar.:param plain_text:  The plaintext:param key: The key (Integer):return: The cipher-text"""cipher_text = ""for letter in plain_text:if letter in SPECIAL_CHARS:cipher_text += lettercontinueindex = ALPHABET.find(letter.upper())new_index = flatten(index + key)cipher_text += ALPHABET[new_index]return cipher_textdef decrypt(cipher_text, key=None):"""This function decrypts plaintext. If no key is specified, it will be found using distribution analysis.:param cipher_text: The cipher-text:param key: The key:return: the plain-text"""if key is None:key = find_key_from_cipher(cipher_text)plain_text = ""for letter in cipher_text:#Skipping special characters (incomplete solution)if letter in SPECIAL_CHARS:plain_text += lettercontinueindex = ALPHABET.find(letter.upper())new_index = flatten(index - key)plain_text += ALPHABET[new_index]return plain_textdef flatten(number) :"""Flattens the key back to be in range(1,26):param number: :return: """return number - (26*(number//26))def find_key_from_cipher(cipher_text):index_of_most_common_letter = 4 #Index of 'e'#Calculate distributiondistribution_dict = analyse_letter_distribution(cipher_text)#Get common letterscommon_letters = sorted(distribution_dict, key=distribution_dict.get, reverse=True)#Use most common letter to get keykey = ALPHABET.find(common_letters[0].upper()) - index_of_most_common_letterreturn keydef analyse_letter_distribution(cipher_text):distribution_dict = {}for letter in cipher_text:if letter in SPECIAL_CHARS:continueif letter not in distribution_dict:distribution_dict[letter] = 1else:distribution_dict[letter] += 1if len(distribution_dict.values()) != len(distribution_dict.values()):print("Multiple letters appear the same amount of times! Uh oh.")return distribution_dictif __name__ == "__main__":secret = encrypt("This sentence is encrypted. Encryption is broken     by using awesome encryption algorithms!",5)print(decrypt(secret))

One last note: Since it's a statistical analysis, this approach works best if the captured cipher-text is long.

One last last note: This description is a little incomplete (and formatting looks ugly, I'm on mobile.) I'd highly recommend this german book by Klaus Schmeh for further reading.

https://en.xdnf.cn/q/119896.html

Related Q&A

how to convert u\uf04a to unicode in python [duplicate]

This question already has answers here:Python unicode codepoint to unicode character(4 answers)Closed 2 years ago.I am trying to decode u\uf04a in python thus I can print it without error warnings. In …

How can I display a nxn matrix depending on users input?

For a school task I need to display a nxn matrix depending on users input: heres an example: 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0(users input: 5) And here is my code until now:n = int(inpu…

How to launch 100 workers in multiprocessing?

I am trying to use python to call my function, my_function() 100 times. Since my_function takes a while to run, I want to parallelize this process. I tried reading the docs for https://docs.python.org/…

Indexes of a list Python

I am trying to find how to print the indexes of words in a list in Python. If the sentence is "Hello world world hello name") I want it to print the list "1, 2, 2, 1, 3")I removed a…

str object is not callable - CAUTION: DO NO USE SPECIAL FUNCTIONS AS VARIABLES

EDIT: If you define a predefined type such as: str = 5 then theoriginal functionality of that predefined will change to a new one. Lesson Learnt: Do not give variables names that are predefined or bel…

Using `wb.save` results in UnboundLocalError: local variable rel referenced before assignment

I am trying to learn how to place an image in an Excel worksheet but I am having a problem with wb.save. My program ends with the following error:"C:\Users\Don\PycharmProjects\Test 2\venv\Scripts\…

Passing a Decimal(str(value)) to a dictionary for raw value

Im needing to pass values to a dictionary as class decimal.Decimal, and the following keeps happening:from decimal import *transaction_amount = 100.03 transaction_amount = Decimal(str(transaction_amoun…

Delete regex matching part of file

I have a file ,and i need to delete the regex matching part and write remaining lines to a file.Regex matching Code to delete file:import re with open("in1.txt") as f:lines = f.read()m = re.f…

How do I download files from the web using the requests module?

Im trying to download a webpage data to samplefile.txt on my hard drive using the following code:import requests res = requests.get(http://www.gutenberg.org/cache/epub/1112/pg1112.txt) res.raise_for_s…

how to get queryset from django orm create

i want to get queryset as a return value when i use the create in django ormnewUserTitle = User_Title.objects.none() newUserTitle = newUserQuestTitle | newUserReviewTitle newUserTitle = newUserQues…