update,
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
#!/usr/bin/env python
|
||||
import os,sys
|
||||
|
||||
ORD_a = ord('a') # 97
|
||||
ORD_A = ord('A') # 65
|
||||
|
||||
def shift_cipher_encrypt(plaintext, key):
|
||||
# apply encryption to text with given key
|
||||
encrypted_message = ''
|
||||
|
||||
for char in plaintext:
|
||||
if char.isalpha():
|
||||
ascii_offset = ORD_A if char.isupper() else ORD_a # Determine ASCII offset based on uppercase or lowercase letter
|
||||
|
||||
# the comment shown below are the pseudo code, it demonstrate the ideas only
|
||||
# let say the input is 'the' // without quote
|
||||
# find distance of target character with reference to A or a
|
||||
# i.e. t - a = 19 , h - a = 7 , e - a = 4
|
||||
distance = ord(char) - ascii_offset
|
||||
|
||||
# [19,7,4] + [8,8,8] (key) = [27, 15, 12]
|
||||
# Shift the character by adding the key and taking modulo 26 to wrap around
|
||||
# [27,15,12] % [26,26,26] = [1,15,12] // get modules
|
||||
shifted_distance = (distance + key) % 26
|
||||
|
||||
# [1,15,12] + [97,97,97] = [98,112,109]
|
||||
# chr(98) , chr(112) , chr(109) = 'bpm'
|
||||
shifted_char = chr(shifted_distance + ascii_offset)
|
||||
|
||||
# so: the -> bpm
|
||||
encrypted_message += shifted_char
|
||||
|
||||
else:
|
||||
# consider integer case, retain
|
||||
encrypted_message += char
|
||||
|
||||
return encrypted_message
|
||||
|
||||
|
||||
def shift_cipher_decrypt(ciphertext, key):
|
||||
plaintext = ""
|
||||
|
||||
for char in ciphertext:
|
||||
if char.isalpha():
|
||||
ascii_offset = ORD_a if char.islower() else ORD_A # Determine ASCII offset based on lowercase or uppercase letter
|
||||
|
||||
# Calculate the distance of the target character from a or A
|
||||
distance = ord(char) - ascii_offset
|
||||
|
||||
# apply shift, get the remainder of 26
|
||||
shifted_distance = (distance - key) % 26
|
||||
|
||||
# Convert back to ASCII
|
||||
decrypted_char = chr(shifted_distance + ascii_offset)
|
||||
|
||||
plaintext += decrypted_char
|
||||
else:
|
||||
# If it is not an alphabetic character, retain as is.
|
||||
plaintext += char
|
||||
|
||||
return plaintext
|
||||
|
||||
|
||||
def count_letter_e(txt_in):
|
||||
# reserved function for demonstration purpose
|
||||
occurence = 0
|
||||
for char in txt_in:
|
||||
if char.isalpha():
|
||||
if char.lower() == 'e':
|
||||
occurence += 1
|
||||
return occurence
|
||||
|
||||
def count_most_occurrence_letter(txt_in):
|
||||
# letter e, as stated have the most occurrence in the message by statistics.
|
||||
# as 'Shift Cipher' is a encryption by letter shifting, the letters have good chance
|
||||
# to have the most occurrence too in the encrypted text.
|
||||
output = [0] * 26 # bucket for 26 letters
|
||||
|
||||
for char in txt_in:
|
||||
if char.isalpha():
|
||||
output[ord(char.lower()) - ORD_a] += 1
|
||||
|
||||
# output contains the statistics of paragraph letter by letter
|
||||
return output
|
||||
|
||||
def find_max_occurrence(char_occurrences):
|
||||
# get the letter of the most occurrences. i.e. m
|
||||
# by subtract between this letter to e, k can be guess
|
||||
|
||||
# find max occurrence and its index
|
||||
max_idx = char_occurrences.index(max(char_occurrences))
|
||||
|
||||
# subtract it with index of e -> 4
|
||||
return max_idx - 4
|
||||
|
||||
def encrypt_file(file_path, key=8):
|
||||
# open a file and apply encryption
|
||||
output_file = file_path.replace('.txt','_e.txt')
|
||||
|
||||
# convert it to integer
|
||||
key = int(key)
|
||||
|
||||
# open source file (plaintext)
|
||||
with open(file_path,'r',encoding="utf-8") as fi:
|
||||
temp = ''.join(fi.readlines())
|
||||
|
||||
# open target file (encrypted text)
|
||||
with open(output_file,'a+') as fo:
|
||||
fo.truncate(0)
|
||||
fo.writelines([shift_cipher_encrypt(temp, key)])
|
||||
|
||||
print(f'encryption done and file saved to {output_file}')
|
||||
return
|
||||
|
||||
def decrypt_file(file_path):
|
||||
# will open an encrypted file and decrypt it by a guessed key
|
||||
|
||||
with open(file_path,'r') as fi:
|
||||
# beginning of the process
|
||||
# read file and join the lines all
|
||||
lines = fi.readlines()
|
||||
e_temp = ''.join(lines)
|
||||
|
||||
characters_distribution = count_most_occurrence_letter(e_temp)
|
||||
|
||||
print('')
|
||||
print('distribution of letters in encrypted text (case insensitive, from a to z)')
|
||||
print(characters_distribution)
|
||||
|
||||
print('')
|
||||
guess_k = find_max_occurrence(characters_distribution)
|
||||
print(f'guessed k: {guess_k}')
|
||||
|
||||
print('')
|
||||
print('decrypted text:')
|
||||
decrypted_text = shift_cipher_decrypt(e_temp, guess_k)
|
||||
print(decrypted_text)
|
||||
|
||||
while True:
|
||||
# show menu
|
||||
print()
|
||||
print("1. Encrypt File")
|
||||
print("2. Decrypt File")
|
||||
print("q. quit")
|
||||
print()
|
||||
option = input("Select an option (1/2/q): ")
|
||||
|
||||
if option == "1":
|
||||
# run if user want to encrypt file
|
||||
# check if user entered a file
|
||||
user_not_enter_file = True
|
||||
while user_not_enter_file:
|
||||
file_path = input("Enter the path of the file to encrypt: ")
|
||||
if len(file_path) > 0:
|
||||
if os.path.exists(file_path):
|
||||
user_not_enter_file = False
|
||||
else:
|
||||
print('sorry but the file not exist')
|
||||
else:
|
||||
print('please enter a file path')
|
||||
|
||||
# check if user entered a key
|
||||
user_not_enter_key = True
|
||||
while user_not_enter_key:
|
||||
key = input("Enter the key(k) to encrypt: ")
|
||||
if (len(key) > 0):
|
||||
user_not_enter_key = False
|
||||
else:
|
||||
print('please enter a key(k)')
|
||||
|
||||
if os.path.exists(file_path):
|
||||
encrypt_file(file_path, key)
|
||||
print('encryption done')
|
||||
else:
|
||||
print("File does not exist.")
|
||||
|
||||
elif option == "2":
|
||||
# run if user want to decrypt file
|
||||
file_path = input("Enter the path of the file to decrypt: ")
|
||||
if os.path.exists(file_path):
|
||||
decrypt_file(file_path)
|
||||
print('decryption done')
|
||||
else:
|
||||
print("File does not exist.")
|
||||
|
||||
elif option.lower() == "q":
|
||||
print('quitting bye ...')
|
||||
break
|
||||
else:
|
||||
print('')
|
||||
print('ERROR !')
|
||||
print('please enter either [1/2/q]')
|
||||
input("press a key to continue ...")
|
||||
print('')
|
||||
|
||||
print("Exiting...")
|
Reference in New Issue
Block a user