Шифр Вернама (одноразовый блокнот): как работает и в чем его абсолютная стойкость

Шифр Вернама (одноразовый блокнот): как работает и в чем его абсолютная стойкость Полезное

Шифр Вернама — это симметричный шифр, в котором каждый бит открытого текста складывается по модулю два (операцией XOR) с соответствующим битом ключа. Если ключ при этом случайный, равен по длине сообщению и используется только один раз, шифр называют одноразовым блокнотом (one-time pad) и он обладает абсолютной (совершенной) стойкостью, доказанной Клодом Шенноном.

Ниже разберу, как устроено XOR-гаммирование и почему расшифровка тем же ключом возвращает исходный текст, при каких именно условиях достигается абсолютная стойкость и что она на самом деле означает, почему такой шифр почти не применяют на практике и чем он отличается от потоковых шифров. Примеры на Python можно скопировать и запустить.

Как работает шифр Вернама: XOR-гаммирование

В основе лежит побитовая операция «исключающее ИЛИ» (XOR, сложение по модулю два). Ее таблица истинности короткая: результат равен единице тогда и только тогда, когда биты различаются.

a b a XOR b
0 0 0
0 1 1
1 0 1
1 1 0

Шифрование — это наложение ключа (гаммы) на открытый текст бит за битом:

  • шифртекст: C = M XOR K, где M — открытый текст, K — ключ;
  • расшифровка: M = C XOR K, тем же самым ключом.

Расшифровка работает тем же ключом из-за ключевого свойства XOR: он сам себе обратен. Дважды наложить один и тот же бит — значит вернуться к исходному значению, потому что x XOR y XOR y = x (второе наложение отменяет первое). Подставив C = M XOR K, получаем C XOR K = M XOR K XOR K = M. Никакой отдельной процедуры расшифровки нет: и там, и там одна операция XOR с ключом.

Такое наложение потока ключа на поток данных называют гаммированием, а сам ключевой поток — гаммой. Вот минимальный рабочий пример на Python. Ключ здесь задан явными байтами, чтобы вывод был воспроизводимым:

def xor_bytes(a: bytes, b: bytes) -> bytes:
    return bytes(x ^ y for x, y in zip(a, b))

message = b"HELLO"
key     = bytes([0x13, 0x24, 0x35, 0x46, 0x57])  # 5 случайных байт, длина = сообщению

cipher = xor_bytes(message, key)   # C = M XOR K
plain  = xor_bytes(cipher, key)    # M = C XOR K

print("шифртекст (hex):", cipher.hex())
print("расшифровано:   ", plain.decode())

Вывод:

шифртекст (hex): 5b61790a18
расшифровано:    HELLO

Одна и та же функция xor_bytes и шифрует, и расшифровывает — разница только в том, что ей подают на вход. Обратите внимание: длина ключа здесь равна длине сообщения, и это не случайность, а одно из условий стойкости, о которых ниже.

Важная оговорка про практику: для настоящего ключа нельзя брать random — этот генератор предсказуем. Криптографически стойкие случайные байты в Python дает модуль secrets (например, secrets.token_bytes(len(message))). В примере выше ключ фиксирован намеренно, только ради повторяемого вывода.

Условия абсолютной стойкости по Шеннону

Сам по себе XOR с ключом стойкости не гарантирует. Абсолютной (в терминах Шеннона — совершенной) стойкостью обладает только одноразовый блокнот, а для этого ключ должен удовлетворять трем условиям одновременно:

  1. Случайность. Ключ — это действительно случайная последовательность (каждый бит равновероятен и независим), а не результат работы обычного псевдослучайного генератора.
  2. Длина. Ключ не короче сообщения. Укорачивать и повторять ключ нельзя — именно повтор ломает всю схему (см. ниже).
  3. Одноразовость. Каждый ключ используется ровно один раз и после этого уничтожается. Отсюда и название — одноразовый блокнот.

Совершенную секретность этой схемы математически доказал Клод Шеннон (работа велась в 1940-х, опубликована в 1949 году). Формально она означает, что шифртекст не дает о сообщении никакой информации: вероятность конкретного открытого текста при известном шифртексте равна его вероятности без всякого шифртекста. Иначе говоря, шифртекст и открытый текст статистически независимы.

На интуитивном уровне это выглядит так: имея только шифртекст длины n, злоумышленник видит, что ему подходит абсолютно любой открытый текст той же длины — под каждый вариант найдется свой ключ, который дал бы ровно этот шифртекст. Поэтому перебор бесполезен: он выдаст все осмысленные сообщения сразу, без признака, какое из них настоящее. Стойкость тут не вычислительная (не «слишком долго считать»), а информационная — нужной информации в шифртексте просто нет.

Из доказательства Шеннона следует и цена такой стойкости: энтропия ключа должна быть не меньше энтропии сообщения, то есть секретного ключевого материала нужно столько же, сколько данных вы хотите защитить.

Чтобы не создавать мифов, назову и границы. Абсолютная стойкость — это про конфиденциальность, и только при выполнении всех трех условий:

  • она не защищает от подмены. Перевернув бит в шифртексте, атакующий перевернет тот же бит в расшифрованном тексте (шифр «пластичен»), поэтому в реальных системах отдельно нужна проверка целостности;
  • она не скрывает сам факт и длину сообщения;
  • при нарушении любого условия стойкость рушится. Повторное использование ключа — разрушительно: для двух сообщений с одним ключом C1 XOR C2 = (M1 XOR K) XOR (M2 XOR K) = M1 XOR M2, ключ сокращается, и атакующий работает уже с текстами напрямую.

Этот эффект повтора легко показать кодом — у атакующего нет ключа, но XOR двух шифртекстов дает XOR двух открытых текстов:

def xor_bytes(a, b):
    return bytes(x ^ y for x, y in zip(a, b))

key = bytes([0x13, 0x24, 0x35, 0x46, 0x57])  # один и тот же ключ на два сообщения - ошибка
c1 = xor_bytes(b"HELLO", key)
c2 = xor_bytes(b"WORLD", key)

leak = xor_bytes(c1, c2)  # ключ сокращается
print("C1 XOR C2 == M1 XOR M2:", leak == xor_bytes(b"HELLO", b"WORLD"))

Вывод:

C1 XOR C2 == M1 XOR M2: True

Именно на повторном использовании ключей строились реальные вскрытия шифрпереписки в XX веке.

Почему шифр Вернама редко применяют на практике

Проблема не в стойкости, а в ключе. Условия, которые дают совершенную секретность, же делают шифр неудобным:

  • Объем ключа. Ключа нужно ровно столько же, сколько данных. Чтобы защитить гигабайт трафика, нужен гигабайт случайного ключа, и это на каждое сообщение заново.
  • Доставка ключа. Ключ надо заранее и безопасно передать обеим сторонам. Но безопасно передать длинный секрет — задача не проще, чем безопасно передать само сообщение. Это замкнутый круг распределения ключей.
  • Хранение и уничтожение. Ключевой материал надо надежно хранить до использования и гарантированно уничтожать после, иначе теряется одноразовость.
  • Настоящая случайность. Нужен источник действительно случайных данных, а не псевдослучайный генератор — иначе стойкость становится не абсолютной, а лишь вычислительной.

Поэтому одноразовый блокнот применяют там, где секретность критична, стороны заранее обмениваются ключами, а объем переписки невелик. Классические примеры — дипломатическая и агентурная связь (в том числе «числовые» одноразовые блокноты у разведчиков), защищенные правительственные линии. Современное развитие идеи — квантовое распределение ключей (QKD), которое как раз позволяет двум сторонам получить общий случайный ключ, пригодный для такой схемы.

Для массовых задач — переписки, сайтов, мессенджеров — вместо него используют шифры с коротким ключом, о которых дальше.

Чем шифр Вернама отличается от потоковых шифров

Потоковые шифры (например, ChaCha20 или устаревший RC4) используют ту же идею гаммирования: генерируют ключевой поток (гамму) и накладывают его на данные операцией XOR. Разница в природе гаммы.

Признак Шифр Вернама (одноразовый блокнот) Потоковый шифр
Источник гаммы Истинно случайный ключ Псевдослучайный поток из короткого ключа
Длина ключа Равна длине сообщения Короткий (например, 256 бит)
Повторное использование Запрещено (каждый ключ — один раз) Ключ можно переиспользовать со сменой одноразового значения (nonce)
Тип стойкости Абсолютная (информационная) Вычислительная (взлом теоретически возможен, но непрактичен)
Практичность Низкая (проблема ключей) Высокая, основа реального шифрования

Ключевая мысль: потоковый шифр — это попытка получить удобство ценой замены истинно случайной гаммы на псевдослучайную, порожденную из короткого ключа. За это он платит тем, что стойкость становится вычислительной, а не абсолютной: гамма предсказуема тому, кто знает короткий ключ или сумеет вскрыть генератор. Одноразовый блокнот такую предсказуемость исключает по построению, но за счет непрактичного ключа.

Смежные темы: двоичная система счисления и бинарный код, случайные числа в Python, взлом компьютерного устройства: виды атак и защита.

FAQ

Шифр Вернама и одноразовый блокнот — это одно и то же? Не совсем. Шифр Вернама — это сам механизм наложения ключа операцией XOR. Одноразовым блокнотом его называют, когда ключ дополнительно случаен, равен по длине сообщению и одноразов. Исходная схема Вернама с повторяющейся ключевой лентой этим условиям не отвечала и стойкой не была.

Можно ли взломать одноразовый блокнот перебором ключей? Формально перебор возможен, но бесполезен: он выдаст все осмысленные тексты нужной длины одновременно, и среди них нельзя отличить настоящее сообщение от подделки. Информации, которая позволила бы выбрать верный вариант, в шифртексте нет.

Устойчив ли он к квантовым компьютерам? Да. Его стойкость информационная, а не вычислительная, поэтому рост вычислительной мощности, в том числе квантовой, ее не подрывает. Практическое ограничение остается прежним — распределение длинных случайных ключей.

Выводы

Шифр Вернама сводится к одной операции: побитовому XOR открытого текста с ключом, где та же операция и расшифровывает. Абсолютную стойкость он дает не сам по себе, а только как одноразовый блокнот — при случайном, равном по длине сообщению и одноразовом ключе; это и доказал Шеннон. Нарушьте любое условие, особенно одноразовость, и стойкость исчезает. Именно неудобство ключей, а не слабость шифра, вытеснило его из массовой практики в пользу потоковых шифров с коротким ключом и вычислительной стойкостью.

Освойте тему на практике

Разобраться в симметричных и потоковых шифрах, режимах и управлении ключами системно можно на курсе по криптографической защите информации, а познакомиться с форматом и преподавателями — на открытых уроках и вебинарах.

OTUS Журнал