Prerequisites:
- 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) — самый простой режим. Каждый блок шифруется независимо: одинаковые блоки открытого текста всегда дают одинаковые блоки шифротекста.
Это называется «проблема пингвина» — по знаменитому примеру с шифрованием изображения пингвина Tux. После ECB-шифрования силуэт пингвина все еще виден, потому что одинаковые пиксели (блоки) дают одинаковый шифротекст.
Последствия для безопасности:
- Злоумышленник видит структуру данных без расшифровки
- Можно определить повторяющиеся блоки (например, нулевые байты в файлах)
- Можно переставлять блоки без обнаружения
Правило: никогда не используйте ECB для реальных данных. Это режим только для обучения и тестирования.
CBC: цепочка блоков шифрования
CBC (Cipher Block Chaining) решает проблему ECB, связывая каждый блок с предыдущим через XOR.
Как работает CBC
- Генерируется случайный IV (вектор инициализации) — 16 байт
- Первый блок XOR-ится с IV, затем шифруется
- Каждый следующий блок 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 (число, используемое один раз)
- Для каждого блока формируется значение: nonce + номер блока (0, 1, 2, …)
- Это значение шифруется AES, получается keystream (ключевой поток)
- 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), который гарантирует целостность данных.
Зачем нужна аутентификация?
CBC и CTR обеспечивают только конфиденциальность — данные нельзя прочитать. Но злоумышленник может изменить шифротекст, и при расшифровке получится другой (поврежденный) открытый текст.
GCM решает эту проблему:
- Шифрование через CTR (параллельно, без паддинга)
- Вычисление тега через GHASH (полиномиальный хеш в GF(2^128))
- Тег зависит от шифротекста И дополнительных данных (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("Подделка обнаружена! Тег не совпадает.")
Сравнение режимов
| Свойство | ECB | CBC | CTR | GCM |
|---|---|---|---|---|
| Параллельное шифрование | Да | Нет | Да | Да |
| Параллельная расшифровка | Да | Да | Да | Да |
| Аутентификация | Нет | Нет | Нет | Да |
| Нужен паддинг | Да | Да | Нет | Нет |
| Распространение ошибок | Один блок | Два блока | Один бит | — |
| Безопасность | Небезопасен | Хорошая | Хорошая | Отличная |
Когда использовать какой режим
| Сценарий | Рекомендуемый режим | Почему |
|---|---|---|
| Сетевой трафик (TLS) | GCM | Аутентификация + параллелизм |
| Шифрование файлов | GCM или CTR+HMAC | Целостность критична |
| Шифрование диска | CTR (XTS вариант) | Произвольный доступ к секторам |
| Ethereum keystore | CTR | Исторический выбор (Ethereum 1.0) |
| Bitcoin wallet.dat | CBC | Исторический выбор |
| Новый проект | GCM | Аутентификация по умолчанию |
| Никогда | ECB | Раскрывает паттерны |
Правило для блокчейн-разработчиков: если сомневаетесь — используйте GCM. Он дает и конфиденциальность, и целостность, и работает параллельно.
Практика
Откройте Jupyter notebook 04-symmetric-encryption.ipynb для практических упражнений:
- Шифрование в каждом режиме и сравнение выходов
- Демонстрация проблемы ECB на одинаковых блоках
- GCM: шифрование с аутентификацией и обнаружение подделки
- Демонстрация уязвимости повторного использования nonce в CTR
Что дальше
Мы изучили симметричное шифрование, где один ключ шифрует и расшифровывает. Но как безопасно передать этот ключ? Нужна асимметричная криптография — система с двумя ключами (публичный и приватный). Начнем с RSA.
Finished the lesson?
Mark it as complete to track your progress