Это дополнительный вопрос к этому.
Я попытался реализовать все рекомендуемые в ответах (кроме комментариев и не привязанности к ОС). Опять же, если вы видите что-то, что нуждается в улучшении или может сделать это быстрее, сообщите мне!
Вот код. На этот раз всего 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 ответ
Рассмотрите возможность добавления подсказок типа 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множество?— Хорошего дня
Насколько я понимаю, вы используете только
keyint в случайное семя. Не используйте случайный выбор. Вам необходимо вызвать безопасный PRNG, который принимает байтовый массив в качестве начального состояния;secretsне поддерживает это.— Райндериен
Я планировал перейти на
os.urandom(). Это сработает?— Хорошего дня
нет, потому что он не может быть посеян
— Райндериен
И со списками, как бы мне провести рефакторинг, чтобы
bytes?— Хорошего дня
Показывать 6 больше комментариев