Уведомления

Группа в Telegram: @pythonsu

#1 Март 3, 2016 17:29:12

tetrius
Зарегистрирован: 2016-03-03
Сообщения: 2
Репутация: +  0  -
Профиль   Отправить e-mail  

Помогите разобраться с PyCrypto

Функция decrypt работает а вот не могу разобраться как наоборот зашифровать строку

from Crypto.Cipher import AES
import base64
def _decrypt(msg, key = None):
    if key is None:
        key = 'mysecretpassword'
    obj = AES.new(key, AES.MODE_CBC, '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
    r = obj.decrypt(base64.b64decode(msg))
    padding = ord(r[-1])
    print padding
    return r[0:(-padding)]
def decrypt_request(msg):
    return _decrypt(msg)
request_crypted ='dasdasdsada'
request = decrypt_request(request_crypted)
print request

Офлайн

#2 Март 4, 2016 03:36:05

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 9716
Репутация: +  842  -
Профиль   Отправить e-mail  

Помогите разобраться с PyCrypto

Алгоритм AES шифрует блоками по 16 байт. Последовательность байт, которую надо зашифровать, в конце может быть короче 16 байт, поэтому там применяется заполнитель. А чтобы отделить заполнитель от шифруемой последовательности, записывается ещё длина заполнителя.

Поэтому
1. Входную последовательность нужно поделить на 16 и взять остаток от деления.
2. Затем из 16 вычесть этот остаток от деления и сохранить полученную разность.
3. Затем к входной последовательности нужно дописать нулевые байты в количестве, которое равно этой разности.
4. Затем вместо последнего нулевого байта нужно записать эту разность.



Отредактировано py.user.next (Март 4, 2016 03:37:06)

Офлайн

#3 Июнь 25, 2016 06:43:24

scidam
Зарегистрирован: 2016-06-15
Сообщения: 288
Репутация: +  35  -
Профиль   Отправить e-mail  

Помогите разобраться с PyCrypto

py.user.next
…нужно дописать нулевые байты…

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

Вот несколько преобразованный код:

import random
import string
import base64
from Crypto import Random
from Crypto.Cipher import AES
import hashlib
def padding(s, bs=AES.block_size):
    if len(s) % bs == 0:
        return s + ''.join(random.SystemRandom().choice(string.ascii_lowercase + string.digits) for _ in range(bs - 1)) + chr(96 - bs)
    if len(s) % bs > 0 and len(s) > bs:
        res = s + ''.join(random.SystemRandom().choice(string.ascii_lowercase + string.digits) for _ in range(bs - len(s) % bs -1)) + chr(96 + len(s) % bs - bs)
    else:
        res = s + ''.join(random.SystemRandom().choice(string.ascii_lowercase + string.digits) for _ in range(bs - len(s) - 1)) + chr(96 + len(s) - bs)
    return res
def unpadding(s, bs=AES.block_size):
    return s[:ord(s[-1])-96] if len(s) % bs == 0 else ''
class AESCipher:
    def __init__(self, key):
        # Лучше использовать хэш от ключа, тогда длина ключа может быть произвольной,
        # а иначе нужно использовать ключи длиной 16, 24, 32 только...
        self.key = hashlib.md5(key).hexdigest()
    def encrypt(self, raw):
        raw = padding(raw)
        iv = Random.new().read(AES.block_size)
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return base64.b64encode(iv + cipher.encrypt(raw))
    def decrypt(self, enc):
        enc = base64.b64decode(enc)
        iv = enc[:AES.block_size]
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return unpadding(cipher.decrypt(enc[AES.block_size:]))
mycif = AESCipher(key='mypasskey')
msg = 'My secret message'
encrypted_msg1 = mycif.encrypt(msg)
print encrypted_msg1
encrypted_msg2 = mycif.encrypt(msg)
print encrypted_msg2 # Одинаковый текст каждый раз кодируется по-разному!
print mycif.decrypt(encrypted_msg1) # Хотя и разный, но одинаково расшифровывается!
print mycif.decrypt(encrypted_msg2)

padding как раз тa функция дополнения до кратности 16…

Отредактировано scidam (Июнь 25, 2016 06:56:16)

Офлайн

#4 Июнь 25, 2016 13:28:36

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 9716
Репутация: +  842  -
Профиль   Отправить e-mail  

Помогите разобраться с PyCrypto

scidam
Дополнять лучше не нулевыми байтами, а случайными
А как ты отличишь потом расшифрованный блок с заполнителем от расшифрованного блока без заполнителя?
По идее, и с нулями тоже не отличишь, но можно тогда служебный блок сделать в конце, который захватывает себя и свободное место из предыдущего.

scidam
тогда результат шифрования для одного и того же сообщения будет различный
Для последнего блока. Сообщение разбивается на блоки, которые потом проходят ряд трансформаций по очереди (блочный шифр). Если ты в последнем что-то поменяешь, остальные останутся неизменными.



Отредактировано py.user.next (Июнь 25, 2016 13:33:28)

Офлайн

#5 Июнь 29, 2016 04:52:42

scidam
Зарегистрирован: 2016-06-15
Сообщения: 288
Репутация: +  35  -
Профиль   Отправить e-mail  

Помогите разобраться с PyCrypto

py.user.next
А как ты отличишь потом расшифрованный блок с заполнителем от расшифрованного блока без заполнителя?

Можно всегда использовать заполнитель даже для сообщений кратных 16, при этом последний байт заполнителя - служебный - закодированная длина заполнителя; остальные - можно случайные; (такое реализовано в коде выше - функции padding, unpadding)
Строка нулевой длины при таком заполнении будет иметь 16 байт длины.
Стррка из 15 байт будет дополнена до 16 байт, последний байт будет кодировать длину заполнителя, в данном случае число 1.

Офлайн

#6 Июнь 29, 2016 05:48:10

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 9716
Репутация: +  842  -
Профиль   Отправить e-mail  

Помогите разобраться с PyCrypto

scidam
Стррка из 15 байт будет дополнена до 16 байт, последний байт будет кодировать длину заполнителя, в данном случае число 1.
А строка из 16 байт? Как ты отличишь строку “aaaaaaaaaaaaaaa” от строки “aaaaaaaaaaaaaaa\x01” ? Вот тебе надо зашифровать две эти разные строки одним алгоритмом. И получится, что при расшифровке ты вместо строки “aaaaaaaaaaaaaaa\x01” будешь получать “aaaaaaaaaaaaaaa”. А с чего вдруг, ты же шифровал изначально вот эту “aaaaaaaaaaaaaaa\x01”?
Как ты поймёшь, какая из них - выравненная 15-байтовая строка, а какая - 16-байтовая строка без выравнивания?
Вот для этого нужен дополнительный блок в конце, в котором и записывается в последнем байте количество байт в заполнителе, включая и этот блок. А сам блок заполняется какой-то рандомной хренью.



Отредактировано py.user.next (Июнь 29, 2016 05:53:33)

Офлайн

#7 Июнь 29, 2016 07:56:27

scidam
Зарегистрирован: 2016-06-15
Сообщения: 288
Репутация: +  35  -
Профиль   Отправить e-mail  

Помогите разобраться с PyCrypto

py.user.next
Как ты поймёшь, какая из них - выравненная 15-байтовая строка, а какая - 16-байтовая строка без выравнивания?

Прошу прощения, видимо я неправильно объяснял… Я предлагаю всегда использовать заполнитель, такой, что последний его байт будет кодировать длину заполнителя.

В случае 16-байтовой исходной строки - строка преобразуется в 32-байтовую: 15 добавочных случайных байт и последний - кодирующий длину добавки - в данном случае число 16.

В случае 15-байтовой исходной строки – добавляется лишь 1 служебный байт, кодирующий длину добавки - т.е. 1.

В случае строки нулевой длины - добавляется также 15 случайных байт и байт кодирующий число 16.

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

py.user.next
Как ты отличишь строку “aaaaaaaaaaaaaaa” от строки “aaaaaaaaaaaaaaa\x01” ?

А этот случай, мне кажется, не относится к делу, т.к. строка “aaaaaaaaaaaaaaa\x01” интерпретируется как ‘a’*16; мы же тут просто кодируем строки – нужно всем строкам добавить ‘raw’ статус тогда: r“aaaaaaaaaaaaaaa\x01” отлична
от r“aaaaaaaaaaaaaaa”.

Офлайн

#8 Июнь 29, 2016 09:12:51

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 9716
Репутация: +  842  -
Профиль   Отправить e-mail  

Помогите разобраться с PyCrypto

scidam
А этот случай, мне кажется, не относится к делу
С чего это ты взял? Я могу вообще взять строку из одних нулей и зашифровать её. И при расшифровке мне нужно получить её же прямо в точности. Кто сказал, что шифровать можно только текст?

scidam
“aaaaaaaaaaaaaaa\x01” интерпретируется как ‘a’*16
С чего ты взял, вот она как интерпретируется
>>> b'aaaaaaaaaaaaaaa\x01'
b'aaaaaaaaaaaaaaa\x01'
>>> len(_)
16
>>>
Вот эту строку я и шифрую, и при расшифровке нужно получить именно вот это. А по твоей схеме получается, что там просто этот последний байт откусывается и всё, потому что по твоей схеме невозможно отличить этот случай и она начинает рассматривать его как выравнивание, когда там никакого выравнивания нет.

А у тебя получается знаешь, как: “если нужно откусывать, то там не должно быть дополнительного блока; а если нужно оставить, то там должен быть дополнительный блок”. А мой вариант предполагает наличие дополнительного блока в любом случае. И то, не факт, что он должен быть там один, их вообще можно рандомно выбирать. Может там один служебный блок, а может несколько служебных блоков. Ты когда расшифровал, то по последнему числу числу можешь определить протяжённость заполнителя. И то, необязательно там в одном байте это хранить, можно вообще выделить несколько байт и как-то это пометить. Тогда тот, кто расшифровывает, будет думать “а сколько там байт в конце под число выделено? а он там один? а может он там не один? это неизвестно”.



Отредактировано py.user.next (Июнь 29, 2016 09:29:19)

Офлайн

#9 Июль 1, 2016 01:28:20

scidam
Зарегистрирован: 2016-06-15
Сообщения: 288
Репутация: +  35  -
Профиль   Отправить e-mail  

Помогите разобраться с PyCrypto

Но…. Вот мой полностью работающий вариант:

                                                                                                                          
>>> import random, string                                                                                                                                                                                  
>>>                                                                                                                                                                                                        
>>> def padding(s, bs=16):                                                                                                                                                                                 
...     if len(s) % bs == 0:                                                                                                                                                                               
...         return s + ''.join(random.SystemRandom().choice(string.ascii_lowercase + string.digits) for _ in range(bs - 1)) + chr(96 - bs)                                                                 
...     if len(s) % bs > 0 and len(s) > bs:                                                                                                                                                                
...         res = s + ''.join(random.SystemRandom().choice(string.ascii_lowercase + string.digits) for _ in range(bs - len(s) % bs -1)) + chr(96 + len(s) % bs - bs)                                       
...     else:                                                                                                                                                                                              
...         res = s + ''.join(random.SystemRandom().choice(string.ascii_lowercase + string.digits) for _ in range(bs - len(s) - 1)) + chr(96 + len(s) - bs)
...     return res
... 
>>> def unpadding(s, bs=16):
...     return s[:ord(s[-1])-96] if len(s) % bs == 0 else ''
...     
... 
>>> msg = b'aaaaaaaaaaaaaaa\x01'
>>> 
>>> print msg, padding(msg), unpadding(padding(msg))
aaaaaaaaaaaaaaa aaaaaaaaaaaaaaa1km4m7rnx50twwcP aaaaaaaaaaaaaaa
>>> 
>>> final = unpadding(padding(msg))
>>> 
>>> print final == msg
True
>>>

Офлайн

#10 Июль 1, 2016 20:29:51

don_pedro
Зарегистрирован: 2016-07-01
Сообщения: 12
Репутация: +  0  -
Профиль   Отправить e-mail  

Помогите разобраться с PyCrypto

Как вам такая ф-цыя?
WTF(aaaaaaaaaaaaaaa1km4m7rnx50twwcP)

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version