Всем хаброчитатилям привет, для начала расскажу что из себя представляет алгоритм "маской" (название придумал я).

И да, как можно прочитать в названии нам нужно сгенерировать маску чтобы потом её на что-то наложить. Вот так легко она генерируется:

# code n1
from random import shuffle
password = 'пароль'
data = ['какие-небудь данные']
mask = list(range(len(data)))
seed(password)
shuffle(mask)

Мой код может быть не идеален

Теперь давайте определимся что мы в принципе и будем шифровать

Мой выбор пал на байты, так как в них легко превратить почти всё в Python

А теперь давайте напишем сам алгоритм наложения маски!

Алгоритм кодирования:

#code n2
result = [0] * len(data)
for d in range(len(data)):
  result[mask[d]] = data[d]
print(result)

А вот и алгоритм расшифровки:

#code n3
for d in mask:
  result.append(data[d])
print(result)

Да, всё так просто

НО! Теперь давайте представим что мы просто зашифровали b'hello' и у нас получилось b'oelhl', даже человек простым подбором может прочитать это.

Поэтому нужно во что-то превратить эти байты чтобы они после шифровки небыли читабельными

Я выбрал превращать байты в биты и эти биты уже и шифровать

Вот код превращения из байтов в биты:

#code n4
result = []
for b in _bytes:
  result.extend(map(int, f'{b:08b}'))
print(result)

А вот код обратного превращения:

#code n5
result = bytes(int(''.join(str(bit) for bit in bits), 2) for bits in [self.data[i:i + 8] for i in range(0, len(self.data), 8)])
print(result)
# Извините, если всё написал в одной строке и это не читабельно

А вот теперь и можно использовать этот алгоритм на практике. Давайте что-нибудь зашифруем!

password = 'привет'
data = b'hello'
mask = createMask(password) # создаём маску (code n1)
bits_data = toBits(data) # превращаем байты в биты (code n4)
enc_data = encode(data, mask) # кодируем алгиритмом "маски" (code n2)
bytes_enc_data = toBytes(enc_data) # расшифровывем биты в байты (code n5)
print(bytes_enc_data)
  b'\xdb6T,^'

Круто, да! Мы получили полностью не читабельный рузультат, зашифрованный паролем!

Ну и также этот результат можно расшифровать:

bits_enc_data = toBits(bytes_enc_data) # превращаем шифрованый результат обратно в биты (code n4)
dec_bits = decode(bits_enc_data, mask) # расшифровывем биты (code n3)
bytes_dec_data = toBytes(dec_bits) # превращаем биты в байты (code n5)

print(dec_result)
  b'hello'

И да, я сделал модуль на python

Исходный код модуля можно найти на

[https://github.com/michazaxm/shifrator]

И проект на pypi:

[https://pypi/project/enc-shifrator]

Комментарии (8)


  1. michazaxm Автор
    00.00.0000 00:00

    P.S. Строка в utf-8 тут взята только для примера

    Впринципе можно шифровать любые байты


  1. andreymal
    00.00.0000 00:00
    +9

    Если заранее известно, что зашифрована валидная utf-8 строка, то простейший брутфорсер пишется на коленке минут за двадцать, и это я ещё даже не пытался анализировать алгоритм


    Скрытый текст
    def to_bits(data: bytes) -> list[int]:
        data_bits: list[int] = []
        for b in data:
            data_bits.extend(map(int, f'{b:08b}'))
        return data_bits
    
    def to_bytes(data_bits: list[int]) -> bytes:
        return bytes(int(''.join(str(bit) for bit in bits), 2) for bits in [data_bits[i:i + 8] for i in range(0, len(data_bits), 8)])
    
    def shifrator_bruteforce(data: bytes, start_seed: int = 0):
        from random import Random
    
        data_bits: list[int] = to_bits(data)  # [0, 1, 0, ...]
    
        rnd = Random()
        seed = start_seed
        while True:
            seed += 1
            rnd.seed(seed)
            mask = list(range(len(data_bits)))  # [0, 1, 2, ...]
            rnd.shuffle(mask)
            result_bits = [data_bits[i] for i in mask]
            try:
                yield seed, to_bytes(result_bits).decode("utf-8")
            except UnicodeDecodeError:
                pass
            seed += 1

    encoded = b"r\x10~\xd8\xfd\xcc\xa3\x9bm\xba\"\xfd\xcc\ry\xcc\x17\xdb\x86D\xc0\x96\x97V\xf0\xe1\xb8\x1e\xa1\xa5w|"
    for seed, decoded in shifrator_bruteforce(encoded):
        print(seed, repr(decoded))


    1. michazaxm Автор
      00.00.0000 00:00

      Ok я это исправил и теперь на вход шифратора подаётся чистая строка с паролем


  1. dprotopopov
    00.00.0000 00:00
    +7

    Чукча не читатель, чукча писатель?

    Это знакомо https://ru.wikipedia.org/wiki/Перестановочный_шифр ?

    А то что вы назвали "маской" во всём мире называется перестановкой https://ru.wikipedia.org/wiki/Перестановка


  1. deus
    00.00.0000 00:00
    +2

    Сделал тебе не ломаемый! Не благодари!

    import random
    import operator
    from binascii import hexlify
    
    
    def shifrator(text):
        mask = random.randbytes(len(text))
        return bytes([operator.xor(ord(s), mask[i])
                      for (i, s) in enumerate(text)]), mask
    
    
    def deshifrator(ct, mask):
        return "".join([chr(operator.xor(s, mask[i]))
                        for (i, s) in enumerate(ct)])
    
    
    text = "Hello world"
    print(f'text: {hexlify(text.encode())}')
    ct, key = shifrator(text)
    pt = deshifrator(ct, key)
    print(f'CT: {hexlify(ct)}')
    print(f'Key: {hexlify(key)}')
    print(f'PT: {hexlify(pt.encode())}')
    
    y@sweet ~/work $ python 1.py
    text: b'48656c6c6f20776f726c64'
    CT: b'd1b472f41906fb69d72cd0'
    Key: b'99d11e9876268c06a540b4'
    PT: b'48656c6c6f20776f726c64'
    


  1. udik_chudik
    00.00.0000 00:00
    +1

    1. Рекомендовал бы автору пересмотреть процедуру генерации "ключа" из строки-пароля. Судя по исходному коду, энтропия вызванная порядком букв в пароле вообще не принимается во внимание (пароли "привет" и "твиреп" дают одинаковый seed). Стоит изучить базовые принципы KDF.

    2. Т.к. seed в итоге принимает на вход int, реально размер "ключа" составляет 32 бита, что в сочитании с атакой на основе открытого текста позволяет "взломать" "шифр" без особого труда


  1. michazaxm Автор
    00.00.0000 00:00

    P.S. Строка в utf-8 тут взята только для примера

    Впринципе можно шифровать любые байты


  1. sunnyfox
    00.00.0000 00:00

    Ну, лиха беда начало, главное человек думает и пытается. Если не окажется подвержен эффекту Даннинга-Крюгера, то может что-то вырасти )


  1. KillJ0y
    00.00.0000 00:00

    Только один вопрос зачем?!

    Gpg может шифровать и паролем и открытым ключом, это раз.

    Второе с точки зрения безопасности - ну тут её нет.

    Третье, если нужно шифровать файлы то тут проще если все очень конфиденциально то только fde и криптоконтейнеры, если же нет дешевле (в ресурсах) использовать не полное шифрование файлов (так шифровальщики делают). Мог бы больше рассказать с примерами с работы, но это затянется на целую статью.