Basic Symmetric Key Encryption Algorithm Explanation And Implementation in Python
Introduction
What Symmetric Key Encryption is? Symmetric Key Encryption involves using the same key for both encryption and decryption processes. The security of the encryption process is relies on keeping the key safe and private from unknown/unauthorized systems.
In this article, I will implement the a Basic Symmetric key Encryption from scratch in Python using XOR and feedback loop.
What is XOR
XOR (eXclusive OR) A Boolean logic operation that is widely used in cryptography. XOR takes two bits as input and return a output bit based on the input bits i.e., if the both input bits are same then the result is 0 else the result is 1.
What is Feedback Loop and Why to use it?
Feedback loop use previously generated ciphertext (or a chunk of it) as input to the encryption algorithm for next chunk. But We can't use it without any modification in xor as we can't use generated cypher text as next chunk xor input but we can use last chunk input as next chunk xor key which can do some kind of basic encryption. but in this code i will use sha256 of last chunk data and old key to make it little bit safer. Why to use it? A feedback mechanism plays a crucial role in improve the security of XOR based symmetric encryption algorithms as they were not protected against pattern recognition by attackers.
Python Implementation: BasicSymmetricKeyEncrpter
Initialization
import secrets, struct
# from sha256 import sha256 as minesha256 # https://github.com/thefcraft/thefcraft-cryptography/blob/main/sha256.py
from hashlib import sha256 as hashlibsha256
from itertools import cycle
def sha256(x):
# return minesha256(x)
return hashlibsha256(x).hexdigest()
class BasicSymmetricKeyEncrpter:
def __init__(self, key) -> None:
self.key = key
self.key_len = len(key)
@classmethod
def from_random_key(cls, key_len=32):
return BasicSymmetricKeyEncrpter(key = secrets.token_bytes(key_len))
Encryption and Decryption Methods
def encrypt(self, data):
data = data
result = b''
key = self.key
data_len = len(data)
i = 0
while data_len > 0:
chunk = data[i:i+len(key)]
i+=len(key)
result += self.encrypt_chunk(chunk, key)
data_len -= len(key)
key = self.new_key(key, chunk)
return result
def decrypt(self, data):
result = b''
key = self.key
data_len = len(data)
i = 0
while data_len > 0:
chunk = data[i:i+len(key)]
i+=len(key)
decrypted = self.encrypt_chunk(chunk, key)
result += decrypted
data_len -= len(key)
key = self.new_key(key, decrypted)
return result
Key Operations
def encrypt_chunk(self, chunk, key):
return bytes(a ^ b for a, b in zip(chunk, cycle(key)))
@staticmethod
def new_key(old_key, data_last):
# print(bytes.fromhex(sha256(old_key + data_last)[2:]))
# result_key = bytearray(b for b in sha256(old_key + data_last)[2:].encode())
result_key = bytes.fromhex(sha256(old_key + data_last)[2:])
return result_key
Demo
if __name__ == '__main__':
original_data = b'Hello, My...'*7
encrpter = BasicSymmetricKeyEncrpter.from_random_key(key_len=23)
encrypted_data = encrpter.encrypt(original_data)
decrypted_data = encrpter.decrypt(encrypted_data)
assert (original_data == decrypted_data)
print(f"LEN[{len(original_data)}] original_data : ", original_data)
print(f"LEN[{len(encrypted_data)}] encrypted_data : ", encrypted_data)
print(f"LEN[{len(decrypted_data)}] decrypted_data : ", decrypted_data)
output
LEN[84] original_data : b'Hello, My...Hello, My...Hello, My...Hello, My...Hello, My...Hello, My...Hello, My...'
LEN[84] encrypted_data : b"\xea\xa6R\xc7IluC-\xdf\xe9\x17\xe9\x03p\x9e(\xc2t\x87\xf9.\x05\x1e\x03B\xbe\x06\x96\xdd\xaf\xbbMs|7'HC\x9b\x15\x14\xe9\xcd-\xc9\x8e|F\x13\x08Py\xe2}\xa2M\x16\x9aH\x0b\xc5\xaa\xf2\xa8F;\xe2^\xa3\xd1\x04\xe8\xf4i\x9bp\x0f\x1f&\x0f\xb8\x90\xbd"
LEN[84] decrypted_data : b'Hello, My...Hello, My...Hello, My...Hello, My...Hello, My...Hello, My...Hello, My...'
what next
This is a very basic simple implementation of symmetric key encryption. but there are already standard implementations for this task e.g., AES (Advanced Encryption Standard) etc. learn them.
Full Code
you can download full code from my github account.
import secrets, struct
# from sha256 import sha256 as minesha256
from hashlib import sha256 as hashlibsha256
from itertools import cycle
def sha256(x):
# return minesha256(x)
return hashlibsha256(x).hexdigest()
class BasicSymmetricKeyEncrpter:
def __init__(self, key) -> None:
self.key = key
self.key_len = len(key)
@classmethod
def from_random_key(cls, key_len=32):
return BasicSymmetricKeyEncrpter(key = secrets.token_bytes(key_len))
def encrypt_chunk(self, chunk, key):
return bytes(a ^ b for a, b in zip(chunk, cycle(key)))
@staticmethod
def new_key(old_key, data_last):
# print(bytes.fromhex(sha256(old_key + data_last)[2:]))
# result_key = bytearray(b for b in sha256(old_key + data_last)[2:].encode())
result_key = bytes.fromhex(sha256(old_key + data_last)[2:])
return result_key
def encrypt(self, data):
data = data
result = b''
key = self.key
data_len = len(data)
i = 0
while data_len > 0:
chunk = data[i:i+len(key)]
i+=len(key)
result += self.encrypt_chunk(chunk, key)
data_len -= len(key)
key = self.new_key(key, chunk)
return result
def decrypt(self, data):
result = b''
key = self.key
data_len = len(data)
i = 0
while data_len > 0:
chunk = data[i:i+len(key)]
i+=len(key)
decrypted = self.encrypt_chunk(chunk, key)
result += decrypted
data_len -= len(key)
key = self.new_key(key, decrypted)
return result
if __name__ == '__main__':
original_data = b'Hello, My...'*7
encrpter = BasicSymmetricKeyEncrpter.from_random_key(key_len=23)
encrypted_data = encrpter.encrypt(original_data)
decrypted_data = encrpter.decrypt(encrypted_data)
assert (original_data == decrypted_data)
# with open('basicSymmetricKeyEncryption.crypt', 'wb') as f:
# f.write(encrypted_data)
print(f"LEN[{len(original_data)}] original_data : ", original_data)
print(f"LEN[{len(encrypted_data)}] encrypted_data : ", encrypted_data)
print(f"LEN[{len(decrypted_data)}] decrypted_data : ", decrypted_data)