Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some messages and keys being incompatible (returns exception) #2

Open
Lol202 opened this issue Oct 12, 2024 · 0 comments
Open

Some messages and keys being incompatible (returns exception) #2

Lol202 opened this issue Oct 12, 2024 · 0 comments

Comments

@Lol202
Copy link

Lol202 commented Oct 12, 2024

Hi! I found the following issue:

For any key, about 7-8% of the randomly generated messages will return an exception raise Exception(f"Hex: {CIPHER}, should be non-empty multiple of 32bits"). The returned cipher will always be one bit short of being a multiple of 32. so 31, 63, 127 etc. On the pictures ((the same issue occurs for normal english letters keys)) you can see an example of that. Same key, different message, one works - the other doesent.
image
BUT
image-1
I wrote a code to test how many messages will work and how many messages will work and how many will not. I did not change anything in class AES. For 5000 messages this returned 4665 work, 365 dont. Any clue as to why this is happening would be much appreciated

from logging import exception
from random import random

import numpy as np
from base64 import b64encode, b64decode
from utils.converter import keyToHexArray, arrayShift, arraySbox, xorArray, addRoundKey, subBytes, shiftRow, mixColumn
from utils.converter import hexToMatrix, inverseMixColumn
import secrets
import string
import time
alphabet = string.ascii_letters + string.digits

class AES:

    def __init__(self):
        self.ROUND = 14
        self.ROUNDKEY = []

    # Key Scheduling
    def __keySchedule(self, KEY):
        ROW, COL = 8, 4
        EXPANSION = 7
        hexKey = keyToHexArray(KEY, ROW, COL)
        self.ROUNDKEY.append(hexKey)
        for i in range(0, EXPANSION):
            prev_arr = self.ROUNDKEY[-1]
            last_col = prev_arr[ROW - 1]
            shift_col = arrayShift(last_col)
            sbox_col = arraySbox(shift_col)
            col_1 = xorArray(prev_arr[0], sbox_col, i)
            col_2 = xorArray(col_1, prev_arr[1])
            col_3 = xorArray(col_2, prev_arr[2])
            col_4 = xorArray(col_3, prev_arr[3])
            # additional non-linear transformation after the fourth column
            # https://crypto.stackexchange.com/questions/20/what-are-the-practical-differences-between-256-bit-192-bit-and-128-bit-aes-enc#answer-1527
            col_5 = xorArray(arraySbox(np.copy(col_4)), prev_arr[4])
            col_6 = xorArray(col_5, prev_arr[5])
            col_7 = xorArray(col_6, prev_arr[6])
            col_8 = xorArray(col_7, prev_arr[7])
            new_arr = np.array([col_1, col_2, col_3, col_4, col_5, col_6, col_7, col_8])
            self.ROUNDKEY.append(new_arr)
        self.__convertRoundKey()

    # Convert 8 4*8 Matrix to 15 4*4 Matrix
    def __convertRoundKey(self):
        self.ROUNDKEY = np.concatenate(self.ROUNDKEY)
        temp = []
        for i in range(self.ROUND + 1):
            temp.append(self.ROUNDKEY[i * 4:i * 4 + 4])
        self.ROUNDKEY = temp

    # Encryption Process
    def __encryptProcess(self, TEXT):
        hexData = keyToHexArray(TEXT)
        cipher_arr = addRoundKey(hexData, self.ROUNDKEY[0])
        for i in range(1, self.ROUND + 1):
            arr = cipher_arr
            arr = subBytes(arr)
            arr = shiftRow(arr)
            if (i != self.ROUND):
                arr = mixColumn(arr)
            arr = addRoundKey(arr, self.ROUNDKEY[i])
            cipher_arr = arr
        return cipher_arr

    # Encryption Add Padding
    def __addPadding(self, data):
        bytes = 16
        bits_arr = []
        while (True):
            if (len(data) > bytes):
                bits_arr.append(data[:bytes])
                data = data[bytes:]
            else:
                space = bytes - len(data)
                bits_arr.append(data + chr(space) * space)
                break
        return bits_arr

    # Decryption Process
    def __decryptProcess(self, CIPHER_HEX):
        hexData = hexToMatrix(CIPHER_HEX)
        plain_arr = addRoundKey(hexData, self.ROUNDKEY[-1])
        for i in range(self.ROUND - 1, -1, -1):
            arr = plain_arr
            arr = shiftRow(arr, left=False)
            arr = subBytes(arr, inverse=True)
            arr = addRoundKey(arr, self.ROUNDKEY[i])
            if (i != 0):
                arr = inverseMixColumn(arr)
            plain_arr = arr
        return plain_arr

    # Decryption Delete Padding
    def __delPadding(self, data):
        verify = data[-1]
        bytes = 16
        if (verify >= 1 and verify <= bytes - 1):
            pad = data[bytes - verify:]
            sameCount = pad.count(verify)
            if (sameCount == verify):
                return data[:bytes - verify]
            return data
        return data

    # Encryption
    def encrypt(self, KEY, TEXT, type='hex'):
        text_arr = self.__addPadding(TEXT)
        self.__keySchedule(KEY)
        hex_ecrypt = ''
        for i in text_arr:
            cipher_matrix = self.__encryptProcess(i)
            cipher_text = list(np.array(cipher_matrix).reshape(-1, ))
            for j in cipher_text:
                hex_ecrypt += f'{j:02x}'
        self.ROUNDKEY = []
        # conversion
        if (type == 'b64'):
            return b64encode(bytes.fromhex(hex_ecrypt)).decode()
        if (type == '0b'):
            return f'{int(hex_ecrypt, 16):0>b}'
        if (type == '__all__'):
            return {
                'hex': hex_ecrypt,
                'b64': b64encode(bytes.fromhex(hex_ecrypt)).decode(),
                '0b': bin(int(hex_ecrypt, 16))[2:].zfill(len(hex_ecrypt) * 4)
            }
        return hex_ecrypt

    # Decryption
    def decrypt(self, KEY, CIPHER, type='hex'):
        if type in ['hex', '0b', 'b64']:
            self.__keySchedule(KEY)
            data = ''

            if (type == 'b64'):
                CIPHER = b64decode(CIPHER).hex()

            if (type == '0b'):
                CIPHER = hex(int(CIPHER, 2)).replace('0x', '')

            if (len(CIPHER) % 32 == 0 and len(CIPHER) > 0):
                examine = CIPHER
                while (len(examine) != 0):
                    plain_matrix = self.__decryptProcess(examine[:32])
                    plain_arr = list(np.array(plain_matrix).reshape(-1, ))
                    plain_arr = self.__delPadding(plain_arr)
                    for j in plain_arr:
                        data += chr(j)
                    if (len(examine) == 32):
                        examine = ''
                    else:
                        examine = examine[32:]
                self.ROUNDKEY = []
                return data

            else:
                raise Exception(f"Hex: {CIPHER}, should be non-empty multiple of 32bits")

        else:
            raise Exception(f"type := ['hex', '0b', 'b64'] but got '{type}'")
def randompassword():
    return ''.join(secrets.choice(alphabet) for i in range(32))

if (__name__ == '__main__'):
    worked =[]
    didntwork = []
    cou=0
    co=0
    aes256 = AES()
    key = randompassword()
    for i in range(1000):
        msg = randompassword()
        x = aes256.encrypt(key, msg, '0b')
        try:
            y = aes256.decrypt(key, x, '0b')
            worked.append(msg)
            cou += 1
        except:
            didntwork.append(msg)
            co += 1
            pass
    print('How many messages worked: ' + str(cou))
    print('How many messages did NOT work: ' + str(co))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant