Перейти к содержанию
Learning Platform
Средний
25 минут
ECB CBC CTR GCM Аутентифицированное шифрование IV Nonce

Требуемые знания:

  • 06-symmetric-aes

Режимы работы блочных шифров

Зачем это нужно в блокчейне

Ethereum keystore файлы используют AES-128-CTR. Почему не ECB? Потому что ECB — это «пингвин-проблема». Bitcoin wallet.dat использует AES-256-CBC. А современные протоколы (TLS 1.3, которым защищены RPC-узлы) используют AES-GCM.

Выбор режима шифрования часто важнее выбора самого шифра. AES с плохим режимом — это как банковский сейф с картонной дверью.

Режим работы определяет, как блочный шифр обрабатывает данные длиннее одного блока (16 байт).

ECB и проблема пингвина

ECB (Electronic Codebook) — самый простой режим. Каждый блок шифруется независимо: одинаковые блоки открытого текста всегда дают одинаковые блоки шифротекста.

ECB: видимый паттерн (проблема пингвина)
Исходное изображение
ECB шифрование
Паттерн все еще виден!
CBC/CTR шифрование
Паттерн скрыт
Проблема ECB: Одинаковые блоки открытого текста всегда дают одинаковые блоки шифротекста. Это означает, что паттерны в данных видны после шифрования. Это катастрофично для изображений и структурированных данных злоумышленник видит структуру данных без расшифровки.

Это называется «проблема пингвина» — по знаменитому примеру с шифрованием изображения пингвина Tux. После ECB-шифрования силуэт пингвина все еще виден, потому что одинаковые пиксели (блоки) дают одинаковый шифротекст.

Последствия для безопасности:

  • Злоумышленник видит структуру данных без расшифровки
  • Можно определить повторяющиеся блоки (например, нулевые байты в файлах)
  • Можно переставлять блоки без обнаружения

Правило: никогда не используйте ECB для реальных данных. Это режим только для обучения и тестирования.

CBC: цепочка блоков шифрования

CBC (Cipher Block Chaining) решает проблему ECB, связывая каждый блок с предыдущим через XOR.

CBC: цепочка блоков шифрования
Открытый текст (заметьте: P1 и P2 одинаковы!)
IV
7a3f9c1e
Блок 1
P1=41414141
Блок 2
P2=41414141
Блок 3
P3=42424242
Шифротекст
C1
????????
C2
????????
C3
????????
0/3
Шифрование последовательное: C(n) зависит от C(n-1). Нельзя параллелизировать.

Как работает CBC

  1. Генерируется случайный IV (вектор инициализации) — 16 байт
  2. Первый блок XOR-ится с IV, затем шифруется
  3. Каждый следующий блок XOR-ится с предыдущим шифротекстом, затем шифруется

Важные свойства CBC:

  • Одинаковые блоки открытого текста дают разные блоки шифротекста (благодаря цепочке)
  • Шифрование последовательное — нельзя параллелизировать (каждый блок зависит от предыдущего)
  • Расшифровка параллельна — каждый блок можно расшифровать независимо
  • Требуется паддинг (выравнивание до 16 байт) — обычно PKCS7

Уязвимость: Padding Oracle Attack (Vaudenay, 2002). Если сервер по-разному реагирует на ошибку паддинга и ошибку расшифровки, злоумышленник может расшифровать данные байт за байтом.

from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad

key = get_random_bytes(16)  # AES-128
data = b"Hello, blockchain world! This is longer than 16 bytes."

# Шифрование CBC
cipher_enc = AES.new(key, AES.MODE_CBC)
ct = cipher_enc.encrypt(pad(data, AES.block_size))
iv = cipher_enc.iv  # Сохраняем IV для расшифровки

# Расшифровка CBC
cipher_dec = AES.new(key, AES.MODE_CBC, iv=iv)
pt = unpad(cipher_dec.decrypt(ct), AES.block_size)
assert pt == data
print(f"IV: {iv.hex()}")
print(f"Шифротекст: {ct.hex()[:32]}...")

CTR: режим счетчика

CTR (Counter) превращает блочный шифр в потоковый шифр. Вместо шифрования данных напрямую, CTR шифрует последовательность счетчиков и XOR-ит результат с открытым текстом.

CTR: режим счетчика
Nonce: a1b2c3 | Открытый текст:
Блок 1
P1=48656c6c
Блок 2
P2=6f20576f
Блок 3
P3=726c6421
Шифротекст
C1
????????
C2
????????
C3
????????
0/3
Ethereum keystore файлы используют AES-128-CTR. Никогда не используйте один nonce дважды с одним ключом!

Как работает CTR

  1. Выбирается уникальный nonce (число, используемое один раз)
  2. Для каждого блока формируется значение: nonce + номер блока (0, 1, 2, …)
  3. Это значение шифруется AES, получается keystream (ключевой поток)
  4. Keystream XOR-ится с открытым текстом

Преимущества CTR:

  • Параллельное шифрование и расшифровка — каждый блок независим
  • Не нужен паддинг — можно шифровать данные произвольной длины
  • Расшифровка = повторное шифрование (симметричная операция)
  • Доступ к произвольному блоку без расшифровки предыдущих

Критически важно: никогда не используйте один nonce дважды с одним ключом! Повторное использование nonce раскрывает XOR двух открытых текстов:

C1 = Keystream XOR P1
C2 = Keystream XOR P2
C1 XOR C2 = P1 XOR P2  // утечка!
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes

key = get_random_bytes(16)
data = b"Ethereum keystore uses AES-128-CTR"

# Шифрование CTR
cipher_enc = AES.new(key, AES.MODE_CTR)
ct = cipher_enc.encrypt(data)
nonce = cipher_enc.nonce

# Расшифровка CTR
cipher_dec = AES.new(key, AES.MODE_CTR, nonce=nonce)
pt = cipher_dec.decrypt(ct)
assert pt == data
print(f"Nonce: {nonce.hex()}")
print(f"Шифротекст: {ct.hex()[:32]}...")

# Заметьте: не нужен padding!

GCM: аутентифицированное шифрование

GCM (Galois/Counter Mode) — это CTR + аутентификация. Помимо шифрования, GCM вычисляет тег аутентификации (authentication tag), который гарантирует целостность данных.

GCM: аутентифицированное шифрование
Входные данные
Nonce
b3c4d5
AAD
заголовок
P1
P1=данные1
P2
P2=данные2
Шифрование (CTR)
Ожидание...
Аутентификация (GHASH)
Ожидание...
0/3
GCM = CTR (шифрование) + GHASH (аутентификация). Стандарт для TLS 1.3 и большинства современных протоколов.

Зачем нужна аутентификация?

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

GCM решает эту проблему:

  1. Шифрование через CTR (параллельно, без паддинга)
  2. Вычисление тега через GHASH (полиномиальный хеш в GF(2^128))
  3. Тег зависит от шифротекста И дополнительных данных (AAD — Associated Authenticated Data)

Если кто-то изменит хотя бы один бит шифротекста, тег не совпадет — подделка обнаружена.

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

key = get_random_bytes(16)
data = b"Transfer 1.5 ETH to 0xAbC..."
header = b"tx_version=2"  # AAD: аутентифицируется, но не шифруется

# Шифрование GCM
cipher_enc = AES.new(key, AES.MODE_GCM)
cipher_enc.update(header)  # Добавляем AAD
ct, tag = cipher_enc.encrypt_and_digest(data)
nonce = cipher_enc.nonce

print(f"Шифротекст: {ct.hex()[:32]}...")
print(f"Тег: {tag.hex()}")

# Расшифровка GCM с проверкой целостности
cipher_dec = AES.new(key, AES.MODE_GCM, nonce=nonce)
cipher_dec.update(header)
pt = cipher_dec.decrypt_and_verify(ct, tag)
assert pt == data

# Что будет при подделке?
tampered_ct = bytearray(ct)
tampered_ct[0] ^= 0x01  # Изменяем один бит
cipher_check = AES.new(key, AES.MODE_GCM, nonce=nonce)
cipher_check.update(header)
try:
    cipher_check.decrypt_and_verify(bytes(tampered_ct), tag)
except ValueError:
    print("Подделка обнаружена! Тег не совпадает.")

Сравнение режимов

СвойствоECBCBCCTRGCM
Параллельное шифрованиеДаНетДаДа
Параллельная расшифровкаДаДаДаДа
АутентификацияНетНетНетДа
Нужен паддингДаДаНетНет
Распространение ошибокОдин блокДва блокаОдин бит
БезопасностьНебезопасенХорошаяХорошаяОтличная

Когда использовать какой режим

СценарийРекомендуемый режимПочему
Сетевой трафик (TLS)GCMАутентификация + параллелизм
Шифрование файловGCM или CTR+HMACЦелостность критична
Шифрование дискаCTR (XTS вариант)Произвольный доступ к секторам
Ethereum keystoreCTRИсторический выбор (Ethereum 1.0)
Bitcoin wallet.datCBCИсторический выбор
Новый проектGCMАутентификация по умолчанию
НикогдаECBРаскрывает паттерны

Правило для блокчейн-разработчиков: если сомневаетесь — используйте GCM. Он дает и конфиденциальность, и целостность, и работает параллельно.

Практика

Откройте Jupyter notebook 04-symmetric-encryption.ipynb для практических упражнений:

  • Шифрование в каждом режиме и сравнение выходов
  • Демонстрация проблемы ECB на одинаковых блоках
  • GCM: шифрование с аутентификацией и обнаружение подделки
  • Демонстрация уязвимости повторного использования nonce в CTR

Что дальше

Мы изучили симметричное шифрование, где один ключ шифрует и расшифровывает. Но как безопасно передать этот ключ? Нужна асимметричная криптография — система с двумя ключами (публичный и приватный). Начнем с RSA.

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс