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