# README ## Cryptography Engineering:Quiz 3-Encrypted the Vigenere cipher ### Purpose The quiz is about giving us two cipher and want us figure out: (1) keylength (2) key (3) plantext In my code, it can only solve the cipher whose keylength $\leq$ 6 ### Screenshots No! there is no Screenshots Why am I screenshoting this code there is nothig to show ### How to use #### cmd ```bash! python xxxx.py < "cipher.txt" ``` #### result ```bash of the cipher ypu input the key length is : {keyLength_of_cipher} the key is :{key_of_cipher} ``` And the code will also generate a {StudentNumber}_msg.txt under the same directory The plantext will be in the .txt file ### Sorcecode I am suspecting the necessity of providing the Sorcecode Any way, here comes the sorce code ```python= import string import sys #the expected is from PDF of Quiz 1 expected = { 'A': 0.085, 'B': 0.0207, 'C': 0.0454, 'D': 0.0338, 'E': 0.1116, 'F': 0.0181, 'G': 0.0247, 'H': 0.03, 'I': 0.0754, 'J': 0.002, 'K': 0.011, 'L': 0.0549, 'M': 0.0301, 'N': 0.0665, 'O': 0.0716, 'P': 0.0317, 'Q': 0.0020, 'R': 0.0758, 'S': 0.0574, 'T': 0.0695, 'U': 0.0363, 'V': 0.0101, 'W': 0.0129, 'X': 0.0029, 'Y': 0.0178, 'Z': 0.00027 } #this method is to separate the cipher into 1-6 group since #the Prof. said the keylength is at most 6 def grouping(text): text = text.replace(" ", "").replace('\n','') arr=[[''*i]*i for i in range(1,7) ] arr[0][0]+=text for i in range (len(text)): for j in range(1,6): arr[j][i%(j+1)]+=text[i] return arr # this method is copy from my quiz 2 program def calculate_ic(text): text = text.replace(' ','').replace('\n','') freq = {char: text.count(char) for char in string.ascii_uppercase} n = len(text) ic = sum(freq[char] * (freq[char] - 1) for char in string.ascii_uppercase) / (n * (n - 1)) return ic #this method is to read input def read_file(): data='' for line in sys.stdin: line=line.strip() data+=line return data def find_the_keylength(data): IC=[0 for _ in range (6)] for i in range (6): ic=calculate_ic(data[i][0]) for j in range (i): ic+=calculate_ic(data[i][j]) IC[i]=ic/(i+1) closest_num = min(IC, key=lambda x: abs(x-0.067)) closest_index = IC.index(closest_num) return closest_index+1 def chi_square(text,shift): shifted='' for letter in text: shifted += string.ascii_uppercase[(string.ascii_uppercase.index(letter) + shift) % 26] freq={letter: shifted.count(letter)/len(shifted) for letter in string.ascii_uppercase} chi_square=sum(((freq[letter]-expected[letter])**2/expected[letter]) for letter in string.ascii_uppercase) return chi_square def vigenere_decrypt(ciphertext, key): plaintext = "" ciphertext=ciphertext.replace(" ",'').replace('\n','') key_len = len(key) for i, c in enumerate(ciphertext): shift = ord(key[i % key_len]) - ord('A') plaintext += chr((ord(c) - shift - 65) % 26 + 65) return plaintext f=open('{StudentNumber}_msg.txt','w') dataStr=read_file() data=grouping(dataStr) keyLength=find_the_keylength(data) target=data[keyLength-1] key='' for i in range (keyLength): chiSq=[0 for _ in range (26)] for j in range(26): chiSq[j]=chi_square(target[i],j) key+=chr(ord('Z')-chiSq.index(min(chiSq))+1) print(f''' of the cipher you input the key length is : {keyLength} the key is :{key} ''') print(vigenere_decrypt(dataStr,key),file=f) ``` ### Functions that I use #### [Index of Coincidance](https://en.wikipedia.org/wiki/Index_of_coincidence) $I.C.=\frac{\sum_{i=A}^{i=Z}(f_i)(f_i-1)}{N(N-1)}$ #### [Chi-Squared test](https://en.wikipedia.org/wiki/Chi-squared_test) $\chi^2=\frac{(observed-expected)^2}{expected}$ ### Test and Result #### Cipher ```txt! IVIKDKDQMJGLPWLZGMPFBJIIDBBYSLJDXFGBIWWEHAPHEYSGNCCYOOTSTZABCOBVRTAZEYWVWWAZAIDGAZPETHPVBPWOBVJXGFMDOBCGPFKXKSZZAIGCJRPETACJHUTHPVHKJHPZHFPMEVZEQSBYOMHSDVFTASFGZTCOBZCGHFMDOBCWVNVBRVKRGXDBMKFBTGBVGMPTBVFMTGBLBMXZWESHGCBYSKDTBYSFWOARQHCJQEQBCUIDCNCHWWGNEDWIHPTKQCZGDKIGDENHPZGIGWVTWIASBFHATQIJSBCDWZBMPGQKKTHTQIGMEFMJSGISLKCFTHPVFXLSZVHAGSMGCLHWJCSXMDTRBTIWWEGHUHPVGXRZCJWHCCZZBVPFKVFTIWWECYIVQJUXCHTVATCWVRBHJHPFILTCNYWLUOBYSKHAIEGBDBBYSKTKIJHATSFGZTCOBZCGIVIKVXLOAZBAXRQEUYDFITFBBSWIHAPHPVKTHAIUOGSHPRHMWSGNWLWSLKCTKCQUOGPGGCIFDFBYOMWSPRRLDAMUWLTOAVKAXQPTONHSLYWLHSOISZPHQFBBRCCCRMWWVBCYCCWKVXGOLVENPHMJCEJHQFBLIVMJSMWSVYOWICJVGBUHMUOGSPICOGRSLRUTXBAKSTRVWKVXG ``` #### result ##### Showed on cmd ```! of the cipher you input the key length is : 6 the key is :POIROT ``` ##### Generated txt file ```txt! THATPROCESSSAIDISTARTSUPONTHESUPPOSITIONTHATWHENYOUHAVEELIMINATEDALLWHICHISIMPOSSIBLETHENWHATEVERREMAINSHOWEVERIMPROBABLEMUSTBETHETRUTHITMAYWELLBETHATSEVERALEXPLANATIONSREMAININWHICHCASEONETRIESTESTAFTERTESTUNTILONEOROTHEROFTHEMHASACONVINCINGAMOUNTOFSUPPORTWEWILLNOWAPPLYTHISPRINCIPLETOTHECASEINPOINTASITWASFIRSTPRESENTEDTOMETHEREWERETHREEPOSSIBLEEXPLANATIONSOFTHESECLUSIONORINCARCERATIONOFTHISGENTLEMANINANOUTHOUSEOFHISFATHERSMANSIONTHEREWASTHEEXPLANATIONTHATHEWASINHIDINGFORACRIMEORTHATHEWASMADANDTHATTHEYWISHEDTOAVOIDANASYLUMORTHATHEHADSOMEDISEASEWHICHCAUSEDHISSEGREGATIONICOULDTHINKOFNOOTHERADEQUATESOLUTIONSTHESETHENHADTOBESIFTEDANDBALANCEDAGAINSTEACHOTHER ``` ## Bonus 1 :MD5 Hash Cracker ### Sorcecode the Sorcecode is 90% copy from [Creating a simple MD5 cracker in Python.](https://nicholasmordecai.co.uk/programming/creating-simple-python-md5-cracker/) ```python!= import hashlib import string HASH = '5f4dcc3b5aa765d61d8327deb882cf99' #PASSLIST here is the top 200 pws of 2019 2020 2021 #from the site below PASSLIST=[] def main(): for word in PASSLIST: guess = hashlib.md5(word.encode('utf-8')).hexdigest() if guess.upper() == HASH or guess.lower() == HASH: print(f'[+] Password found: {word}') exit(0) else: print(f'[-] Guess: {word} incorrect... {guess}') print(f'Password not found in wordlist...') if __name__ == '__main__': main() ``` where the `PASSLIST` in my code is all password from [Top 200 most common passwords ](https://s1.nordcdn.com/nord/misc/0.55.0/nordpass/200-most-common-passwords-en.pdf) ### How to use Typically, it is only to show what the target MD5 being You can only change the sourcecode to change the target hash ### result ```= [-] Guess: 123456 incorrect... e10adc3949ba59abbe56e057f20f883e [-] Guess: 123456789 incorrect... 25f9e794323b453885f5181f1b624d0b [-] Guess: picture1 incorrect... 2712e0b4e97c4a17a90a6417ccf757ba [+] Password found: password ``` ## Bonus2 No! there is nothing for bonus 2 ###### tags: `Cryptography Engineering` `