Я пишу часть кода для шифрования текста с помощью симметричного шифрования. Но это не возвращается с правильным результатом...
from Crypto.Cipher import AES
import os
crypto = AES.new(os.urandom(32), AES.MODE_CTR, counter = lambda : os.urandom(16))
encrypted = crypto.encrypt("aaaaaaaaaaaaaaaa")
print crypto.decrypt(encrypted)
Здесь, дешифрованный текст отличается от оригинала.
Я действительно не понимаю много о криптографии, поэтому терпите меня. Я понимаю, что режим CTR требует, чтобы "встречная" функция предоставила случайный счетчик каждый раз, но почему ему нужен он, чтобы быть 16 байтов, когда мой ключ составляет 32 байта, и он настаивает, что мое сообщение находится в кратных числах 16 байтов также? Действительно ли это нормально?
Я предполагаю, что это не возвращается к исходному сообщению, потому что счетчик, измененный между, шифрует и дешифрует. Но затем, как это, как предполагается, работает теоретически так или иначе? Что я делаю неправильно? Так или иначе я вынужден обратиться назад к ЕЦБ, пока я не понимаю это :(
Счетчик
должен возвращать то же самое при расшифровке, что и при шифровании, как вы интуитивно понимаете, поэтому один ( ВООБЩЕ НЕ БЕЗОПАСНЫЙ ) способ сделать это:
>>> secret = os.urandom(16)
>>> crypto = AES.new(os.urandom(32), AES.MODE_CTR, counter=lambda: secret)
>>> encrypted = crypto.encrypt("aaaaaaaaaaaaaaaa")
>>> print crypto.decrypt(encrypted)
aaaaaaaaaaaaaaaa
CTR - это блочный шифр, поэтому ограничение «16-за-раз», которое вас удивляет, является довольно естественным.
Конечно, так называемый «счетчик», возвращающий одно и то же значение при каждом вызове , является крайне небезопасным . Не нужно много делать лучше, например ...:
import array
class Secret(object):
def __init__(self, secret=None):
if secret is None: secret = os.urandom(16)
self.secret = secret
self.reset()
def counter(self):
for i, c in enumerate(self.current):
self.current[i] = c + 1
if self.current: break
return self.current.tostring()
def reset(self):
self.current = array.array('B', self.secret)
secret = Secret()
crypto = AES.new(os.urandom(32), AES.MODE_CTR, counter=secret.counter)
encrypted = crypto.encrypt(16*'a' + 16*'b' + 16*'c')
secret.reset()
print crypto.decrypt(encrypted)
Вектор инициализации ("счетчик") должен оставаться неизменным, как и ключ, между шифрованием и дешифрованием. Он используется для того, чтобы вы могли кодировать один и тот же текст миллион раз и каждый раз получать разный шифротекст (предотвращая некоторые известные атаки с открытым текстом и атаки с подбором шаблона). При расшифровке необходимо использовать тот же IV, что и при шифровании. Обычно, когда вы начинаете расшифровывать поток, вы инициализируете IV тем же значением, что и при шифровании этого потока.
См. http://en.wikipedia.org/wiki/Initialization_vector для получения информации о векторах инициализации.
Обратите внимание, что os.urandom(16) не является "детерминированной", что является требованием для функций счетчика. Я предлагаю вам использовать функцию инкремента, поскольку именно так устроен режим CTR. Начальное значение счетчика должно быть случайным, но последующие значения должны быть полностью предсказуемы от начального значения (детерминированы). Возможно, о начальном значении даже позаботятся за вас (я не знаю деталей)
Что касается размеров ключа, IV и входных данных, похоже, что выбранный вами шифр имеет размер блока 16 байт. Все, что вы описали, соответствует этому и кажется мне нормальным.