Understanding Basic Symmetric Key Encryption in Python

In this article, I will implement a basic Symmetric key Encryption algorithm from scratch in Python...

ThefCraft
3 min · Jun 22, 2024
487
441

Basic Symmetric Key Encryption Algorithm Explanation And Implementation in Python

https://www.ssl2buy.com/wp-content/uploads/2015/12/Symmetric-Encryption.png

https://www.ssl2buy.com/wp-content/uploads/2015/12/Symmetric-Encryption.png

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

https://i.pcmag.com/imagery/encyclopedia-terms/xor-xor.fit<em>lim.size</em>768x.gif

https://i.pcmag.com/imagery/encyclopedia-terms/xor-xor.fitlim.size768x.gif

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)