Алгоритм шифрования / дешифрования # 2

Это дополнительный вопрос к этому.

Я попытался реализовать все рекомендуемые в ответах (кроме комментариев и не привязанности к ОС). Опять же, если вы видите что-то, что нуждается в улучшении или может сделать это быстрее, сообщите мне!

Вот код. На этот раз всего 222 строки :).

#!/usr/bin/env python 
# not sure if I did this right

import base64
import random
import os


def add_padding(plain_text, block_size=128):
    plain_text = plain_text.encode()
    padding = -(len(plain_text) + 1) % block_size  # Amount of padding needed to fill block

    padded_text = plain_text + b'=' * padding + bytes([padding + 1])

    return decimal_to_binary(padded_text)


def xor_string(key, secret):
    xored_secret=""

    for i in range(len(secret) // len(key)):
        if i > 0:
            key = get_round_key(key)

        xored_secret += decimal_to_binary([bin_to_decimal(key, len(key))[0] ^ bin_to_decimal(secret[i * len(key):len(key) + (i * len(key))], len(key))[0]], len(key))

    return xored_secret


def generate_key(key):
    if len(key) >= 128:
        key = decimal_to_binary(key.encode())
        return key[:1024]
    elif len(key) < 128:

        key = key.encode()

        for i in range(128 - len(key)):
            b = decimal_to_binary([key[i]])
            b = xor_string(decimal_to_binary([sum(key) // len(key)]), b[::-1])

            key += bytes([int(b, 2)])

        new_key = ''.join(str(i) for i in key)

        half1 = new_key[:len(new_key) // 2]
        half2 = new_key[len(new_key) // 2:]
        new_key = decimal_to_binary([int(half1 + half2)])
        new_key = new_key[:1024]
        return new_key


def bin_to_base64(binary):
    return base64.b64encode(bytes([int(binary[i * 8:8 + i * 8], 2) for i in range(len(binary) // 8)])).decode()


def bin_to_decimal(binary, length=8):
    b = [binary[i * length:length + (i * length)] for i in range(len(binary) // length)]

    decimal = [int(i, 2) for i in b]

    return decimal


def decimal_to_binary(decimal, length=8):
    return ''.join(str(bin(num)[2:].zfill(length)) for num in decimal)


def base64_to_bin(base):
    decoded = ''
    for letter in base64.b64decode(base):
        decoded += bin(letter)[2:].zfill(8)

    return decoded


def matrix_to_str(m):
    return ''.join(str(m[i][j]) for i in range(32) for j in range(32))


def obfuscate(binary, key, encrypting, loops):
    shuffled_binary = ''
    round_key = key

    for i in range(len(binary) // 1024):
        if i > 0:
            round_key = get_round_key(round_key)

        if encrypting:
            m = [list(binary[j * 32 + i * 1024:j * 32 + i * 1024 + 32]) for j in range(32)]
            m = shuffle(m, bin_to_decimal(round_key, 1024)[0], loops)
            shuffled_binary += xor_string(round_key, matrix_to_str(m))
        else:
            xor = xor_string(round_key, binary[i * 1024:i * 1024 + 1024])
            m = [list(xor[j * 32:j * 32 + 32]) for j in range(32)]
            m = reverse_shuffle(m, bin_to_decimal(round_key, 1024)[0], loops)
            shuffled_binary += matrix_to_str(m)

    return xor_string(key, shuffled_binary)


def shuffle(m, key, loops):
    for j in range(loops):
        # move columns to the right
        m = [row[-1:] + row[:-1] for row in m]

        # move rows down
        m = m[-1:] + m[:-1]

        shuffled_m = [[0] * 32 for _ in range(32)]

        for idx, sidx in enumerate(test(key)):
            shuffled_m[idx // 32][idx % 32] = m[sidx // 32][sidx % 32]

        m = shuffled_m

        # cut in half and flip halves
        m = m[len(m) // 2:] + m[:len(m) // 2]

        # test
        m = list(map(list, zip(*m)))

    return m


def reverse_shuffle(m, key, loops):
    for j in range(loops):
        # test
        m = list(map(list, zip(*m)))

        # cut in half and flip halves
        m = m[len(m) // 2:] + m[:len(m) // 2]

        shuffled_m = [[0] * 32 for _ in range(32)]

        for idx, sidx in enumerate(test(key)):
            shuffled_m[sidx // 32][sidx % 32] = m[idx // 32][idx % 32]

        m = shuffled_m

        # move rows up
        m = m[1:] + m[:1]

        # move columns to the left
        m = [row[1:] + row[:1] for row in m]

    return m


def test(seed):
    random.seed(seed)
    lst = list(range(1024))
    random.shuffle(lst)

    return lst


def get_round_key(key):
    key = [[key[(j * 32 + n)] for n in range(32)] for j in range(32)]
    # get the last column
    col = [i[-1] for i in key]
    # interweave
    col = [x for i in range(len(col) // 2) for x in (col[-i - 1], col[i])]
    new_key = ''
    for i in range(32):
        cols=""
        for row in key:
            cols += row[i]

        cols = cols[16:] + cols[:16]
        new_key += xor_string(''.join(str(ele) for ele in col), cols)

    return new_key


def bin_to_bytes(binary):
    return int(binary, 2).to_bytes(len(binary) // 8, byteorder="big")


def encrypt(password, secret, loops):
    key = generate_key(password)
    secret = add_padding(secret)
    secret = xor_string(key, secret)
    secret = obfuscate(secret, key, True, loops)
    secret = bin_to_base64(secret)
    return secret


def decrypt(password, base, loops):
    key = generate_key(password)
    binary = base64_to_bin(base)
    binary = xor_string(key, binary)
    binary = obfuscate(binary, key, False, loops)
    binary = bin_to_bytes(binary)
    pad = binary[-1]
    binary = binary[:-pad]
    return binary.decode()


if __name__ == '__main__':
    while True:
        os.system('cls')
        com = input('1)Encrypt Text n2)Decrypt Textn3)Exitn')

        if com == '1':
            os.system('cls')
            secret = input('Enter the text you wish to encrypt: ')
            os.system('cls')
            key = input('Enter your key: ')
            os.system('cls')
            print(f'Encrypted text: {encrypt(key, secret, 1)}')
            input()
        elif com == '2':
            os.system('cls')
            b64 = input('Enter the text you wish to decrypt: ')
            os.system('cls')
            key = input('Enter your key: ')
            os.system('cls')
            print(f'Decrypted text: {decrypt(key, b64, 1)}')
            input()
        elif com == '3':
            break

1 ответ
1

  • Рассмотрите возможность добавления подсказок типа PEP484. Мне нужно было пройти через это, чтобы понять ценности, которые вы передаете.

  • не зависит от ОС — действительно. Ваш звонок cls имеет сомнительную ценность для безопасности, и если вы считаете, что она имеет такую ​​ценность, лучше вызвать кросс-платформенную библиотеку, которая выполнит то же самое. В настоящее время вы привязаны к Windows, и это плохо. Вы так близки к созданию кросс-совместимого приложения; было бы стыдно, если это останется вашим единственным препятствием. На данный момент в этом примере я просто удалил ваш cls звонки. Если бы они предназначались только для эстетических целей, вы должны оставить это так.

  • Гораздо (намного) более высокое значение безопасности имеет getpass вместо input, чтобы предотвратить пересылку паролей.

  • obfuscate не очень хорошее название для симметричной криптографической функции; он «запутывает» только если encrypting=True. Имена трудны; может называть это process_crypto или что-то в этом роде.

  • Строка из символов 0 и 1 или, что еще хуже, список строк длиной 1, каждая из которых представляет собой символ 0 или 1, является очень неэффективным и непрактичным внутренним представлением двоичных данных. Это больше работы, чем я готов сделать, но для приложения, которое чем-либо выходит за рамки поверхностного учебного кода для начинающих, критически важно, чтобы вы реорганизовали его, чтобы использовать bytes массивы (в случае неизменяемых данных) или bytearray() (в случае изменяемых данных)

  • В связи с вышеизложенным — вероятно, не самая лучшая идея носить с собой целое число произвольной длины, состоящее из более 300 цифр (!!). Очередной раз bytes это лучшее представление.

  • Избегайте возрастающей конкатенации строк в цикле, O (n ^ 2) во времени.

  • Вам нужно расслабиться с остротами. Этот:

      xored_secret += decimal_to_binary([bin_to_decimal(key, len(key))[0] ^ bin_to_decimal(secret[i * len(key):len(key) + (i * len(key))], len(key))[0]], len(key))
    

неразборчиво и невозможно поддерживать, и я вижу там как минимум три разных выражения, которые должны получать свою собственную временную переменную в отдельной строке.

  • Переназначение key к значению другого типа — str к bytes — не рекомендуется; сделайте другое имя переменной.
  • Иметь __main__ Охрана недостаточна для создания размаха. Чтобы поместить ваши основные переменные в область видимости функции, вам нужна фактическая функция.
  • Звонок в random пагубно сказывается на безопасности вашей криптовалюты и является одной из ошибок, которую, вероятно, совершает каждый новичок в криптовалюте. Позвонить в secrets вместо.

Охватывает некоторые (конечно, не все) из вышеперечисленного:

#!/usr/bin/env python
# not sure if I did this right

import base64
import random
from getpass import getpass
from typing import List


def add_padding(plain_text: str, block_size: int = 128) -> str:
    plain_text = plain_text.encode()
    padding = -(len(plain_text) + 1) % block_size  # Amount of padding needed to fill block

    padded_text = plain_text + b'=' * padding + bytes([padding + 1])

    return decimal_to_binary(padded_text)


def xor_string(key: str, secret: str) -> str:
    xored_secret=""

    for i in range(len(secret) // len(key)):
        if i > 0:
            key = get_round_key(key)

        some_decimals = bin_to_decimal(secret[i * len(key):len(key) + (i * len(key))], len(key))

        some_values = [
            bin_to_decimal(key, len(key))[0] ^ some_decimals[0]
        ]

        xored_secret += decimal_to_binary(some_values, len(key))

    return xored_secret


def generate_key(key: str) -> str:
    if len(key) >= 128:
        key = decimal_to_binary(key.encode())
        return key[:1024]
    elif len(key) < 128:

        key = key.encode()

        for i in range(128 - len(key)):
            b = decimal_to_binary([key[i]])
            b = xor_string(decimal_to_binary([sum(key) // len(key)]), b[::-1])

            key += bytes([int(b, 2)])

        new_key = ''.join(str(i) for i in key)

        half1 = new_key[:len(new_key) // 2]
        half2 = new_key[len(new_key) // 2:]
        new_key = decimal_to_binary([int(half1 + half2)])
        new_key = new_key[:1024]
        return new_key


def bin_to_base64(binary: str) -> str:
    ints = [
        int(binary[i * 8:8 + i * 8], 2)
        for i in range(len(binary) // 8)
    ]
    return base64.b64encode(bytes(ints)).decode()


def bin_to_decimal(binary: str, length: int = 8) -> List[int]:
    b = [binary[i * length:length + (i * length)] for i in range(len(binary) // length)]

    decimal = [int(i, 2) for i in b]

    return decimal


def decimal_to_binary(decimal: List[int], length: int=8) -> str:
    return ''.join(
        str(bin(num)[2:].zfill(length))
        for num in decimal
    )


def base64_to_bin(base: str) -> str:
    decoded = ''
    for letter in base64.b64decode(base):
        decoded += bin(letter)[2:].zfill(8)

    return decoded


def matrix_to_str(m: List[List[str]]) -> str:
    return ''.join(
        str(m[i][j])
        for i in range(32) for j in range(32)
    )


def obfuscate(binary: str, key: str, encrypting: bool, loops: int) -> str:
    shuffled_binary = ''
    round_key = key

    for i in range(len(binary) // 1024):
        if i > 0:
            round_key = get_round_key(round_key)

        if encrypting:
            m = [list(binary[j * 32 + i * 1024:j * 32 + i * 1024 + 32]) for j in range(32)]
            m = shuffle(m, bin_to_decimal(round_key, 1024)[0], loops)
            shuffled_binary += xor_string(round_key, matrix_to_str(m))
        else:
            xor = xor_string(round_key, binary[i * 1024:i * 1024 + 1024])
            m = [list(xor[j * 32:j * 32 + 32]) for j in range(32)]
            m = reverse_shuffle(m, bin_to_decimal(round_key, 1024)[0], loops)
            shuffled_binary += matrix_to_str(m)

    return xor_string(key, shuffled_binary)


def shuffle(m: List[List[str]], key: int, loops: int) -> List[List[str]]:
    for j in range(loops):
        # move columns to the right
        m = [row[-1:] + row[:-1] for row in m]

        # move rows down
        m = m[-1:] + m[:-1]

        shuffled_m = [[0] * 32 for _ in range(32)]

        for idx, sidx in enumerate(test(key)):
            shuffled_m[idx // 32][idx % 32] = m[sidx // 32][sidx % 32]

        m = shuffled_m

        # cut in half and flip halves
        m = m[len(m) // 2:] + m[:len(m) // 2]

        # test
        m = list(map(list, zip(*m)))

    return m


def reverse_shuffle(m: List[List[str]], key: int, loops: int) -> List[List[str]]:
    for j in range(loops):
        # test
        m = list(map(list, zip(*m)))

        # cut in half and flip halves
        m = m[len(m) // 2:] + m[:len(m) // 2]

        shuffled_m = [[0] * 32 for _ in range(32)]

        for idx, sidx in enumerate(test(key)):
            shuffled_m[sidx // 32][sidx % 32] = m[idx // 32][idx % 32]

        m = shuffled_m

        # move rows up
        m = m[1:] + m[:1]

        # move columns to the left
        m = [row[1:] + row[:1] for row in m]

    return m


def test(seed: int) -> List[int]:
    random.seed(seed)
    lst = list(range(1024))
    random.shuffle(lst)

    return lst


def get_round_key(key):
    key = [[key[(j * 32 + n)] for n in range(32)] for j in range(32)]
    # get the last column
    col = [i[-1] for i in key]
    # interweave
    col = [x for i in range(len(col) // 2) for x in (col[-i - 1], col[i])]
    new_key = ''
    for i in range(32):
        cols=""
        for row in key:
            cols += row[i]

        cols = cols[16:] + cols[:16]
        new_key += xor_string(''.join(str(ele) for ele in col), cols)

    return new_key


def bin_to_bytes(binary: str) -> bytes:
    return int(binary, 2).to_bytes(len(binary) // 8, byteorder="big")


def encrypt(password: str, secret: str, loops: int = 1) -> str:
    key = generate_key(password)
    secret = add_padding(secret)
    secret = xor_string(key, secret)
    secret = obfuscate(secret, key, True, loops)
    secret = bin_to_base64(secret)
    return secret


def decrypt(password: str, base: str, loops: int = 1) -> str:
    key = generate_key(password)
    binary = base64_to_bin(base)
    binary = xor_string(key, binary)
    binary = obfuscate(binary, key, False, loops)
    binary = bin_to_bytes(binary)
    pad = binary[-1]
    binary = binary[:-pad]
    return binary.decode()


def main():
    while True:
        com = input(
            '1) Encrypt Textn'
            '2) Decrypt Textn'
            '3) Exitn'
        )

        input_text = input('Enter the text: ')
        key = getpass('Enter your key: ')

        if com == '1':
            print(f'Encrypted text: {encrypt(key, input_text)}')

        elif com == '2':
            print(f'Decrypted text: {decrypt(key, input_text)}')

        elif com == '3':
            break

        print()


if __name__ == '__main__':
    main()

Говоря в более общем плане, писать такой код в образовательных и развлекательных целях — это весело. Однако криптографические реализации ужасно трудно сделать правильно, а иногда даже труднее. доказывать что они правы. В реальном, производственном мире, пожалуйста, не используйте это; просто позвони в библиотеку.

  • Что касается произвольной длины int, как мне заменить это на bytes множество?

    — Хорошего дня

  • Насколько я понимаю, вы используете только key int в случайное семя. Не используйте случайный выбор. Вам необходимо вызвать безопасный PRNG, который принимает байтовый массив в качестве начального состояния; secrets не поддерживает это.

    — Райндериен

  • Я планировал перейти на os.urandom(). Это сработает?

    — Хорошего дня

  • нет, потому что он не может быть посеян

    — Райндериен

  • И со списками, как бы мне провести рефакторинг, чтобы bytes?

    — Хорошего дня

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *