# 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`
`