---
# System prepended metadata

title: README
tags: [Cryptography Engineering]

---

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