Симметричное шифрование: AES
Зачем это нужно в блокчейне
Зашифрованные кошельки (keystore файлы Ethereum, wallet.dat Bitcoin) используют AES для защиты приватных ключей паролем. Если кто-то получит ваш keystore файл, без пароля он бесполезен — благодаря AES.
Когда вы создаете кошелек в MetaMask или Geth, ваш приватный ключ шифруется AES-128-CTR (Ethereum) или AES-256-CBC (Bitcoin) перед сохранением на диск. Пароль, который вы вводите, превращается в ключ шифрования через функцию Key Derivation (scrypt или PBKDF2).
Симметричное шифрование — это фундамент защиты данных. Без понимания AES невозможно разобраться, как кошельки хранят ваши ключи.
Интуитивное объяснение
Симметричное шифрование — это замок с одним ключом. Один и тот же ключ запирает (шифрует) и отпирает (расшифровывает) данные.
AES (Advanced Encryption Standard) — это самый распространенный симметричный шифр в мире. Принят как стандарт в 2001 году после конкурса, организованного NIST (Национальный институт стандартов и технологий США).
Как работает AES на высоком уровне:
- Берет блок данных фиксированного размера (128 бит = 16 байт)
- Применяет серию перемешиваний (раундов) с использованием ключа
- Выдает блок зашифрованных данных того же размера
Количество раундов зависит от длины ключа:
Каждый раунд — это набор из четырех операций, которые перемешивают данные так тщательно, что восстановить исходные данные без ключа невозможно.
Алгоритмический уровень: четыре операции раунда
AES обрабатывает данные как матрицу 4x4 байтов (так называемое состояние — state). Каждый раунд применяет четыре операции:
SubBytes — подстановка
Каждый байт заменяется другим байтом через таблицу подстановки (S-box). Это нелинейное преобразование — именно оно делает AES устойчивым к линейному криптоанализу.
S-box — это таблица 16x16, где каждому входному байту (0x00-0xFF) соответствует один выходной байт. Математически S-box построен на обратных элементах в поле GF(2^8) — это связь с конечными полями из предыдущих уроков.
ShiftRows — сдвиг строк
Строки матрицы состояния сдвигаются влево:
- Строка 0: без сдвига
- Строка 1: сдвиг на 1 позицию
- Строка 2: сдвиг на 2 позиции
- Строка 3: сдвиг на 3 позиции
Это обеспечивает диффузию между столбцами.
MixColumns — перемешивание столбцов
Каждый столбец умножается на фиксированную матрицу в поле GF(2^8). Эта операция обеспечивает максимальную диффузию — изменение одного байта влияет на все четыре байта столбца.
Последний раунд пропускает MixColumns. Это не ошибка — без MixColumns в последнем раунде шифрование и расшифровка имеют одинаковую структуру (важно для реализации).
AddRoundKey — наложение ключа
Состояние XOR-ится с раундовым ключом. Раундовые ключи генерируются из основного ключа через процедуру Key Expansion.
Размеры ключей AES
- AES-128: 10 раундов, достаточен для большинства задач
- AES-192: 12 раундов, редко используется
- AES-256: 14 раундов, рекомендуется для криптовалютных приложений
Bitcoin использует AES-256-CBC для шифрования wallet.dat. Ethereum keystore файлы используют AES-128-CTR — этого достаточно, потому что ключ шифрования выводится через scrypt с высоким параметром сложности.
Почему AES безопасен
AES безопасен благодаря двум принципам Клода Шеннона:
-
Confusion (запутывание): SubBytes делает связь между ключом и шифротекстом максимально сложной. Даже зная часть ключа, нельзя предсказать, как изменится шифротекст.
-
Diffusion (диффузия): ShiftRows и MixColumns обеспечивают, что изменение одного бита открытого текста меняет в среднем половину битов шифротекста (эффект лавины).
За 25+ лет не найдено практических атак на AES. Лучшие теоретические атаки (biclique) снижают сложность полного перебора с 2^256 до 2^254.4 для AES-256 — это все еще астрономически много.
Код на Python
Мы не реализуем AES вручную — это сложно и опасно (ошибки в реализации создают уязвимости). Вместо этого используем pycryptodome:
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
# AES-256 базовое шифрование блока (ECB — только для обучения!)
key = get_random_bytes(32) # 256-бит ключ
cipher = AES.new(key, AES.MODE_ECB)
# Открытый текст должен быть ровно 16 байт для ECB
plaintext = b"Exactly16Bytes!!"
ciphertext = cipher.encrypt(plaintext)
print(f"Шифротекст: {ciphertext.hex()}")
# Расшифровка
decipher = AES.new(key, AES.MODE_ECB)
decrypted = decipher.decrypt(ciphertext)
print(f"Расшифровано: {decrypted}")
assert decrypted == plaintext
# ВАЖНО: ECB небезопасен для реальных данных!
# Используйте CBC, CTR или GCM — см. следующий урок
Почему мы не пишем AES с нуля? Реализация AES на чистом Python была бы в 1000 раз медленнее C-реализации pycryptodome и содержала бы ошибки. AES — это алгоритм, который нужно понимать (через диаграммы), но использовать через проверенные библиотеки.
Математический уровень
Для тех, кто хочет глубже:
SubBytes использует мультипликативную инверсию в поле GF(2^8), определенном неприводимым полиномом x^8 + x^4 + x^3 + x + 1. Каждый байт интерпретируется как элемент этого поля, инвертируется (0 отображается в 0), затем проходит через аффинное преобразование.
Это прямая связь с конечными полями (GF(p)) из предыдущих уроков — но здесь поле расширяется до GF(2^8) с полиномиальной арифметикой.
MixColumns — это умножение вектора-столбца на фиксированную матрицу в GF(2^8):
[2 3 1 1] [s0]
[1 2 3 1] * [s1]
[1 1 2 3] [s2]
[3 1 1 2] [s3]
Умножение и сложение выполняются в GF(2^8), где сложение — это XOR, а умножение — полиномиальное по модулю неприводимого полинома.
Практика
Откройте Jupyter notebook 04-symmetric-encryption.ipynb для практических упражнений:
- Шифрование и расшифровка с AES в режиме ECB
- Демонстрация проблемы одинаковых блоков в ECB
- Переход к безопасным режимам (CBC, CTR, GCM) — следующий урок
Что дальше
AES шифрует один блок (16 байт). Но реальные данные длиннее 16 байт. Как шифровать произвольные объемы данных? Нужны режимы работы блочных шифров — ECB, CBC, CTR, GCM. Об этом в следующем уроке.
Finished the lesson?
Mark it as complete to track your progress