PyCrypto - Как работает вектор инициализации?

Я пытаюсь понять, как PyCrypto работает для использования в проекте, но я не полностью понимаю значение вектора инициализации (IV). Я обнаружил, что при декодировании строки я могу использовать неправильный IV, и я все еще, кажется, вернул сообщение, за исключением первых 16 байтов (размер блока). Просто использовать его неправильно или не понимать что-то?

Вот пример кода для демонстрации:

import Crypto
import Crypto.Random
from Crypto.Cipher import AES

def pad_data(data):
    if len(data) % 16 == 0:
        return data
    databytes = bytearray(data)
    padding_required = 15 - (len(databytes) % 16)
    databytes.extend(b'\x80')
    databytes.extend(b'\x00' * padding_required)
    return bytes(databytes)

def unpad_data(data):
    if not data:
        return data

    data = data.rstrip(b'\x00')
    if data[-1] == 128: # b'\x80'[0]:
        return data[:-1]
    else:
        return data


def generate_aes_key():
    rnd = Crypto.Random.OSRNG.posix.new().read(AES.block_size)
    return rnd

def encrypt(key, iv, data):
    aes = AES.new(key, AES.MODE_CBC, iv)
    data = pad_data(data)
    return aes.encrypt(data)

def decrypt(key, iv, data):
    aes = AES.new(key, AES.MODE_CBC, iv)
    data = aes.decrypt(data)
    return unpad_data(data)

def test_crypto ():
    key = generate_aes_key()
    iv = generate_aes_key() # get some random value for IV
    msg = b"This is some super secret message.  Please don't tell anyone about it or I'll have to shoot you."
    code = encrypt(key, iv, msg)

    iv = generate_aes_key() # change the IV to something random

    decoded = decrypt(key, iv, code)

    print(decoded)

if __name__ == '__main__':
    test_crypto()

Я использую Python 3.3.

Выход будет зависеть от исполнения, но я получаю что-то вроде этого: b"1^,Kp}Vl\x85\x8426M\xd2b\x1aer secret message. Please don't tell anyone about it or I'll have to shoot you."

Ответ 1

Поведение, которое вы видите, зависит от режима CBC. С CBC дешифрование можно визуализировать следующим образом (из Википедии):

CBC decryption

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

Другие режимы менее просты. Если вы ошиблись в IV, все сообщение искажается при расшифровке. Возьмите режим CTR, например, где nonce принимает почти то же значение IV:

CTR mode

Ответ 2

Разработчик PyCrypto вытащил спецификацию режима AES CBC из NIST:

AES Mode_CBC → ссылка NIST 800-38a (Рекомендация для Cipher Mode Operations)

Из этого, страница 8:

5.3 Инициализационные векторы

Ввод в процессы шифрования режимов CBC, CFB и OFB включает, помимо открытого текста, блок данных, называемый вектором инициализации (IV), обозначенным IV. IV используется на начальном этапе шифрования сообщения и в соответствующем расшифровке сообщения. IV не обязательно должен быть секретным; однако для режимов CBC и CFB IV для любого конкретного выполнение процесса шифрования должно быть непредсказуемым, и для режима OFB для каждого выполнения процесса шифрования необходимо использовать уникальные IV. Генерация IVs обсуждается в Приложении C.


Помните, что каждый раз, когда вы составляете сообщение, вам нужно использовать случайный IV, это добавляет "соль" к сообщению, что делает сообщение уникальным; даже если "соль" находится в открытом состоянии, это не поможет сломать шифрование, если ключ шифрования AES неизвестен. Если вы не используете рандомизированное IV, скажем, вы используете одно и то же 16 байт каждого сообщения, ваши сообщения, если вы повторяетесь, будут выглядеть одинаково по всем проводам, и вы можете подвергать себя частоте и/или повторным атакам.

Тестирование результатов случайных IVs vs static:

def test_crypto ():
    print("Same IVs same key:")
    key = generate_aes_key()
    iv = b"1234567890123456"
    msg = b"This is some super secret message.  Please don't tell anyone about it or I'll have to shoot you."
    code = encrypt(key, iv, msg)
    print(code.encode('hex'))
    decoded = decrypt(key, iv, code)
    print(decoded)

    code = encrypt(key, iv, msg)
    print(code.encode('hex'))
    decoded = decrypt(key, iv, code)
    print(decoded)

    print("Different IVs same key:")
    iv = generate_aes_key()
    code = encrypt(key, iv, msg)
    print(code.encode('hex'))
    decoded = decrypt(key, iv, code)
    print(decoded)

    iv = generate_aes_key()
    code = encrypt(key, iv, msg)
    print(code.encode('hex'))
    decoded = decrypt(key, iv, code)
    print(decoded)

Надеюсь, это поможет!