Вопрос по python, pycrypto, aes, encryption, cryptography – Как AES в CTR работает для Python с PyCrypto?

3

Я использую Python 2.7.1 Я хочу зашифровать STH с помощью AES в режиме CTR. Я установил библиотеку PyCrypto для Python. Я написал следующий код:

secret = os.urandom(16)
crypto = AES.new(os.urandom(32), AES.MODE_CTR, counter=lambda: secret)
encrypted = crypto.encrypt("asdk")
print crypto.decrypt(encrypted)

Я должен запустить crypto.decrypt столько раз, сколько байт моего открытого текста, чтобы правильно получить расшифрованные данные. то есть:

encrypted = crypto.encrypt("test")
print crypto.decrypt(encrypted)
print crypto.decrypt(encrypted)
print crypto.decrypt(encrypted)
print crypto.decrypt(encrypted)

Последний вызов для расшифровки вернет мне открытый текст. Другие выходы из расшифровки - это несколько бессмысленных строк. Мне интересно, нормально это или нет? Нужно ли мне каждый раз включать в цикл размер, равный моему тексту, или я ошибся?

Ваш Ответ

4   ответа
0

Согласно @gertvdijk, AES_CTR является потоковым шифром, который делаетне нужна прокладка. Так что я'мы удалили соответствующие коды.

Вот'Что-то, что я знаю.

Вы должны использовать тот же ключ (первый параметр вAES.new(...)) в шифровании и дешифровании, и держать ключ в секрете.

Методы шифрования / дешифрованияStateful, это означаетcrypto.en(de)crypt("abcd")==crypto.en(de)crypt("abcd") являетсяне всегда правда. В вашем CTR ваш обратный вызов счетчика всегда возвращает одну и ту же вещь, поэтому при шифровании он остается без состояния (я не уверен на 100%, что это причина), но мы все же обнаруживаем, что он в некотором состоянии является состоянием при расшифровке. В заключение, мы всегда должны использовать новый объект, чтобы сделать их.

counter callback Функция как при шифровании, так и при дешифровании должна вести себя одинаково. В вашем случае, это заставить их вернуть один и тот же секрет. Все же я недумаюsecret это "секрет», Вы можете использовать случайный сгенерированный"secret" и передать его через взаимодействующие одноранговые узлы без какого-либо шифрования, чтобы другая сторона могла напрямую использовать его, покаsecret являетсяне предсказуемо

Поэтому я напишу свой шифр вот так, надеюсь, он поможет.

import os
import hashlib
import Crypto.Cipher.AES as AES

class Cipher:

        @staticmethod
        def md5sum( raw ):
                m = hashlib.md5()
                m.update(raw)
                return m.hexdigest()

        BS = AES.block_size

        @staticmethod 
        def pad( s ):
                """note that the padding is no necessary"""
                """return s + (Cipher.BS - len(s) % Cipher.BS) * chr(Cipher.BS - len(s) % Cipher.BS)"""
                return s

        @staticmethod
        def unpad( s ):
                """return s[0:-ord(s[-1])]"""
                return s

        def __init__(self, key):
                self.key = Cipher.md5sum(key)
                #the state of the counter callback 
                self.cnter_cb_called = 0 
                self.secret = None

        def _reset_counter_callback_state( self, secret ):
                self.cnter_cb_called = 0
                self.secret = secret

        def _counter_callback( self ):
                """
                this function should be stateful
                """
                self.cnter_cb_called += 1
                return self.secret[self.cnter_cb_called % Cipher.BS] * Cipher.BS


        def encrypt(self, raw):
                secret = os.urandom( Cipher.BS ) #random choose a "secret" which is not secret
                self._reset_counter_callback_state( secret )
                cipher = AES.new( self.key, AES.MODE_CTR, counter = self._counter_callback )
                raw_padded = Cipher.pad( raw )
                enc_padded = cipher.encrypt( raw_padded )
                return secret+enc_padded #yes, it is not secret

        def decrypt(self, enc):
                secret = enc[:Cipher.BS]
                self._reset_counter_callback_state( secret )
                cipher = AES.new( self.key, AES.MODE_CTR, counter = self._counter_callback )
                enc_padded = enc[Cipher.BS:] #we didn't encrypt the secret, so don't decrypt it
                raw_padded = cipher.decrypt( enc_padded )
                return Cipher.unpad( raw_padded )

Некоторый тест:

>>> from Cipher import Cipher
>>> x = Cipher("this is key")
>>> "a"==x.decrypt(x.encrypt("a"))
True
>>> "b"==x.decrypt(x.encrypt("b"))
True
>>> "c"==x.decrypt(x.encrypt("c"))
True
>>> x.encrypt("a")==x.encrypt("a")
False #though the input is same, the outputs are different

Ссылка:http://packages.python.org/pycrypto/Crypto.Cipher.blockalgo-module.html#MODE_CTR

Извините, я должен понизить это. AES-CTR - потоковый шифр и выне нужно набивка, Увидетьмой ответ Больше подробностей. gertvdijk
Я думал об этомlimited-entropy.com/padding-oracle-attacks но это'только для режима CBC curious
@ смешно, ямы никогда не думали об этом раньше. Спасибо за обмен, я хотел бы ответить на этот вопрос в ближайшее время. Marcus
@ почему, нет? ИМХО, все в порядке. Marcus
2

я собираюсь уточнить на @gertvdijk 'Объяснение того, почему шифр вел себя так же, как и в исходном вопросе (мое редактирование было отклонено), но также указывает на то, что установка счетчика для возврата статического значения является серьезным недостатком и показывает, как правильно его настроить.

Сброс счетчика для новых операций

Причина, по которой вы ведете себя так, как вы описали в вопросе, заключается в том, что ваш обычный текст (4 байта / 32 бита) в четыре раза меньше размера блоков потока ключей, которые шифрователь CTR выводит для шифрования (16 байт / 128 бит) ,

Потому что ты'Повторно используя одно и то же фиксированное значение вместо действительного счетчика, шифр продолжает выделять одни и те же 16-байтовые блоки потока ключей. Вы можете наблюдать это, многократно шифруя 16 нулевых байтов:

 >>> crypto.encrypt('\x00'*16)
'?\\-\xdc\x16`\x05p\x0f\xa7\xca\x82\xdbE\x7f/'
>>> crypto.encrypt('\x00'*16)
'?\\-\xdc\x16`\x05p\x0f\xa7\xca\x82\xdbE\x7f/'

Вы также несбросить шифрСостояние s перед выполнением дешифрования, поэтому 4 байта зашифрованного текста дешифруются против следующих 4 байтов ключа XOR из первого блока выходного потока. Это также можно наблюдать путем шифрования и дешифрования нулевых байтов:

 >>> crypto.encrypt('\x00' * 4)
'?\\-\xdc'
>>> crypto.decrypt('\x00' * 4)
'\x16`\x05p'

Если бы это работало так, как вы хотели, результат обеих этих операций должен быть одинаковым. Вместо этого вы можете увидеть первые четыре байта 16-байтового блока в первом результате и вторые четыре байта во втором результате.

После Вас'Израсходовав 16-байтовый блок ключа XOR, выполнив четыре операции с четырехбайтовыми значениями (всего 16 байт), создается новый блок ключа XOR. Первые четыре байта (как и все остальные) каждого блока ключей XOR одинаковы, поэтому, когда вы вызываете метод расшифровки в этот раз, он возвращает вам открытый текст.

Это действительно плохо! Вы не должны использовать AES-CTR таким образом - этоs эквивалентно простому шифрованию XOR с 16-байтовым повторяющимся ключом, который может быть легко взломан.

Решение

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

Вы также должны использовать правильную функцию счетчика это объединяет одноразовый номер со значением счетчика, которое увеличивается каждый раз, когда генерируется новый блок ключевого потока. PyCrypto имеет класс Counter, который может сделать это за вас.

from Crypto.Cipher import AES
from Crypto.Util import Counter
from Crypto import Random

# Set up the counter with a nonce.
# 64 bit nonce + 64 bit counter = 128 bit output
nonce = Random.get_random_bytes(8)
countf = Counter.new(64, nonce) 

key = Random.get_random_bytes(32)  # 256 bits key

# Instantiate a crypto object first for encryption
encrypto = AES.new(key, AES.MODE_CTR, counter=countf)
encrypted = encrypto.encrypt("asdk")

# Reset counter and instantiate a new crypto object for decryption
countf = Counter.new(64, nonce)
decrypto = AES.new(key, AES.MODE_CTR, counter=countf)
print decrypto.decrypt(encrypted) # prints "asdk"
2
Начните с нового криптообъекта для новых операций

Причина, по которой вы ведете себя так, как вы описали в вопросе, заключается в том, что ваш обычный текст (4 байта / 32 бита) в четыре раза меньше размера, на котором работает криптографический движок для выбранного вами режима AES (128 бит), а также многократного использования того же самого экземпляр криптообъекта. Просто нене использовать один и тот же объект, если вывыполняется операция над новым потоком данных (или другая операция над ним). Ваша проблема будет решена путем создания новойcrypto объект для расшифровки, как это:

# *NEVER* USE A FIXED LIKE COUNTER BELOW IN PRODUCTION CODE. READ THE DOCS.
counter = os.urandom(16)
key = os.urandom(32)  # 256 bits key

# Instantiate a crypto object first for encryption
encrypto = AES.new(key, AES.MODE_CTR, counter=lambda: counter)
encrypted = encrypto.encrypt("asdk")

# Instantiate a new crypto object for decryption
decrypto = AES.new(key, AES.MODE_CTR, counter=lambda: counter)
print decrypto.decrypt(encrypted) # prints "asdk"
Почему речь не идет о набивке с AES-CTR

Этот ответ начался как ответ наответ Маркуса, в котором он первоначально указал, что использование отступа решит это. Хотя я понимаю, что это похоже на симптомы проблемы с заполнением, это, конечно, не так.

Весь смысл AES-CTR в том, что выне нужно набивка, как это'спотоковый шифр (в отличие от ECB / CBC и так далее)! Потоковые шифры работают с потоками данных, а не группируют данные в блоках и не связывают их в фактические криптографические вычисления.

1

В дополнение к тому, что говорит Маркус,Crypto.Util.Counter Класс может быть использован для построения вашей функции счетчика блоков.

Похожие вопросы