I have this task to build customizable Des Encryption where each block is 16 Bytes instead of 8 Bytes, we don’t want to using triple Des( thats not the purpose )
here are the
The block ( P ) is of size 128 bits (16 bytes).
It is divided into two blocks of 8 bytes P_1 P_2
Each key is of size 192 bits (24 bytes) divided into 3 sub-keys of 8 bytes each K_1, K_2,K_3
The encryption process is done as described in the diagram after this description, basically a feistel diagram with just 3 roundes , where each round the F function is basic Des from the library pyDes .
The encryption result of DES of each block is C
The encryption is DES with the key that changes from block to block C = C_1C_2 .
I need to write the power_des.py module that will display the algorithm described above. The module should be based on the des algorithm in the pyDes library.
we need to use the padding according to the PKCS5
Use only the CBC mode
I need be able to decrypt the encrypted file to retrieve back the plain text
**Problem description: **
Im able to encrypt and decrypt the file,
Im not able to read the decrypted file ( i can open the file, but the text inside the file looks in weird ascii )
i think the problem is one of those
1 The way i save the format of the decrypted data
2 missed something in the decryption process,
3: the unpading just before returning the plain text is not done properly
3: or something else
I will be glad to know what i need to fix to retrieve my data back and readable
In Short:
For encryption
I pad the text so it could be in blocks of 16 Bytes
i loop text in block of 16 , for each block i encrypt the block using the function __ek(block) using feistel 3 round and des function with different key for each round
I save blocks of 16 byts with CBC mode
I save the encrypted file with the iv in the first 16 Bytes
iv + text
For decryption
I retrieve the file , extract the iv and for each 16 block i decrypt with mode CBC and the the ___Dk(block) function
CODE
from os import urandom
import os
from pyDes import des
import hashlib
import sys
import numpy as np
class PowerDes:
def __init__(self, password, mode,):
# passwd.encode('utf-8').digest(), convert the str pass to 32 bytes
# [:24] we want the first 24 bytes of the 32 bytes
key = hashlib.sha256(password.encode('utf-8')).digest()[:24]
if mode != 'CBC':
raise ValueError('Illegal mode')
self.__blocksize = 16
self.key1 = key[:8]
self.key2 = key[8:16]
self.key3 = key[16:]
def __xor8(self, x, y):
a = np.frombuffer(x, dtype=np.int8)
b = np.frombuffer(y, dtype=np.int8)
z= np.bitwise_xor(a, b).tobytes()
return z
def __xor16(self, x, y):
a = np.frombuffer(x, dtype=np.int16)
b = np.frombuffer(y, dtype=np.int16)
z= np.bitwise_xor(a, b).tobytes()
return z
# this is the power Des of 1 block of 16 Bytes
def __ek(self, block):
# Split the block into two 8-byte halves
pleft = block[:8]
pright = block[8:]
# Three keys for three rounds
keys = [self.key1, self.key2, self.key3]
for i in range(3):
des_cipher = des(keys[i]) # des object with i key
# Encrypt the right half
pi = des_cipher.encrypt(pright)
# XOR with the left half
pie2Xored = self.__xor8(pleft, pi)
# Swap halves
pleft = pright
pright = pie2Xored
# After final round, swap halves back
return pright + pleft
# this is the power Des of 1 block of 16 Bytes
def __dk(self, block):
# Split the block into two 8-byte halves
pleft = block[:8]
pright = block[8:]
# Three keys for three rounds in reverse order
keys = [self.key3, self.key2, self.key1]
for i in range(3):
des_cipher = des(keys[i])
pi = des_cipher.encrypt(pright)
pie2 = self.__xor8(pleft, pi)
pleft = pright
pright = pie2
# After final round, swap halves back (back to the orginal shape)
return pleft + pright
def __pad(self, ptext):
# pad using mode # PKCS5
pad_len = 16 - (len(ptext) % 16)
padding = bytes([pad_len] * pad_len)
paddedText = ptext + padding
return paddedText
def __unpad(self, ptext):
pad_len = ptext[-1]
return ptext[:-pad_len]
def encrypt(self, ptext ):
'''
:param ptext: the text to be encrypted
:return: encrypted text
encrypt each 16 block with Mood CBC
loop the text - on each 16B chunck call ek(Block)
'''
ptext = self.__pad(ptext)
# loop the text encrypt Using Des of 16
iv = urandom(16)
c = []
c_i_minus1 = iv
blocks = [ptext[i:i + 16] for i in range(0, len(ptext), 16)]
for pi in blocks:
'''
C_i = e_k(pi XOR C_i-1 )
'''
c_block = self.__xor16(c_i_minus1, pi) # pi XOR C_i-1
ci = self.__ek(c_block)
c.append(ci)
c_i_minus1 = ci
## append the iv to the text
ciphertext = iv + b"".join(c)
return ciphertext
def decrypt(self, ctext):
decrypted = []
blocks = [ctext[i:i + 16] for i in range(0, len(ctext), 16)]
iv = blocks[0] # extrac the iv
cBLoks = blocks[1:]
for i in range(0, len(cBLoks)):
if i == 0 :
p = self.__dk(cBLoks[0])
p1 = self.__xor16(p, iv)
decrypted.append(p1)
else:
c = cBLoks[i]
prev_c = cBLoks[i - 1]
p = self.__dk(c)
p_i = self.__xor16(p, prev_c)
decrypted.append(p_i)
decrypted_data = b"".join(decrypted)
decrypted_data = self.__unpad(decrypted_data)
# unpading should go here
return decrypted_data
def saveFiletoSystem(text, file_name, post):
print("Saving the file ")
# extract file name
base = os.path.splitext(file_name)[0]
new_file_name = f"{base}.{post}"
with open(new_file_name, 'wb') as f:
f.write(text)
print("saved file toSystem .")
def runer():
command = input("Enter Command e or d: ")
file_name = input("enter file name {make sure its in the same dir}: ")
seed = input("Enter password: ")
# open the file !
if command == 'e':
with open(file_name, 'rb') as f:
ptext = f.read()
power_des = PowerDes(seed, mode='CBC') # Create the Encryption Object
cipher = power_des.encrypt(ptext) #recive the encrypted file
saveFiletoSystem(cipher , file_name , 'pds')
print("Crated Encrypted file .. .")
# Decryption
elif command == 'd':
with open(file_name, 'rb') as f:
ctext = f.read()
power_des = PowerDes(seed, mode='CBC') # Create the Encryption Object
plain = power_des.encrypt(ctext) # recive the encrypted file
saveFiletoSystem(plain, file_name, 'dec')
print("Crated Decrypted file .. .")
else:
print("Wrong input")
sys.exit()
if __name__ == '__main__':
runer()