Перейти к содержимому
New

DeepSeek V4.1 Flash free for 7 days: 10M tokens a day after a top-up of $10 or more. Until Nov 30.

How it works

Шифр Цезаря на Python: шифрование и расшифровка шаг за шагом

Напишите шифр Цезаря на Python, протестируйте его с unittest и убедитесь, что такой шифр сдвига не даёт настоящей секретности.

Начальный4 мин чтенияАвтор: NotHoly

Что вы напишете

Вы напишете функцию caesar(). Она сдвигает английские буквы на заданное число позиций, не трогает все остальные символы и отменяет сдвиг, если передать отрицательное число. Затем вы запустите небольшой набор тестов и разберёте, что они проверяют.

Нужен только Python 3. Если идея шифра сдвига для вас нова, сначала прочитайте Что такое шифр Цезаря?.

Перед началом

Установите Python 3 с python.org. Код использует только стандартную библиотеку, поэтому устанавливать через pip нечего. Создайте папку для лабораторной и сохраняйте файлы в неё.

Вывод в этой статье получен на Python 3.12.10 в Windows 11. Другие версии я не проверял.

Шаг 1: буквы как числа

Функция ord() в Python возвращает кодовую позицию Unicode для символа. chr() делает обратное: превращает кодовую позицию обратно в символ. Для прописной буквы A кодовая позиция равна 65.

Выполните в терминале:

bash
python -I -c "print(ord('A'), chr(65))"

Вывод:

text
65 A

Флаг -I запускает Python в изолированном режиме. Переменные окружения, начинающиеся с PYTHON, игнорируются, а текущая папка не добавляется в путь поиска модулей. Используйте его, когда запускаете код из папки, содержимое которой писали не вы.

Сдвиг работает с позицией, отсчитываемой от нуля. Вычитание 65 превращает A в 0, а Z в 25. Шаг % 26 возвращает результат в пределы алфавита, а прибавление 65 восстанавливает диапазон прописных букв:

python
chr((ord(letter) - 65 + shift) % 26 + 65)

Строчные буквы начинаются с 97, поэтому та же идея работает с 97 вместо 65.

Оператор % в Python никогда не возвращает отрицательное число, если делитель положительный. Поэтому отрицательный сдвиг работает без дополнительного кода:

bash
python -I -c "print((0 - 3) % 26)"

Вывод:

text
23

Ноль минус три с переходом через начало алфавита даёт 23, это позиция X.

Шаг 2: напишите шифр

Сохраните это как caesar_cipher.py. Это тот же код, что и в файле лабораторной.

python
"""Educational Caesar cipher only. NOT secure encryption.

Shift ASCII English A-Z/a-z; preserve every other Unicode character as-is.
"""

def caesar(text: str, shift: int) -> str:
    shift %= 26
    result = []
    for character in text:
        if 'A' <= character <= 'Z':
            result.append(chr((ord(character) - 65 + shift) % 26 + 65))
        elif 'a' <= character <= 'z':
            result.append(chr((ord(character) - 97 + shift) % 26 + 97))
        else:
            result.append(character)
    return ''.join(result)

if __name__ == '__main__':
    print(caesar('HELLO, World!', 3))  # KHOOR, Zruog!
    print(caesar('KHOOR, Zruog!', -3)) # HELLO, World!

Первая строка, shift %= 26, приводит любой сдвиг к диапазону от 0 до 25. Сдвиг 29 ведёт себя как сдвиг 3, а сдвиг -3 превращается в 23.

Запустите файл:

bash
python -I caesar_cipher.py

Вывод:

text
KHOOR, Zruog!
HELLO, World!

Функция меняет только буквы от A до Z и от a до z. Цифры, пробелы, пунктуация и буквы с диакритикой проходят без изменений. Благодаря этому код остаётся коротким. Шифру, который обрабатывает буквы с диакритикой, нужно правило для каждой из них, а в этой лабораторной такое правило не задано.

Шаг 3: запустите тесты

В лабораторной есть и test_caesar_cipher.py. Откройте терминал в папке с обоими файлами и выполните:

bash
python -I -m unittest discover -p 'test_*.py' -v

Вывод:

text
test_large_shift (test_caesar_cipher.CaesarCipherTest.test_large_shift) ... ok
test_lower (test_caesar_cipher.CaesarCipherTest.test_lower) ... ok
test_negative (test_caesar_cipher.CaesarCipherTest.test_negative) ... ok
test_roundtrip (test_caesar_cipher.CaesarCipherTest.test_roundtrip) ... ok
test_upper (test_caesar_cipher.CaesarCipherTest.test_upper) ... ok
test_wrap (test_caesar_cipher.CaesarCipherTest.test_wrap) ... ok

----------------------------------------------------------------------
Ran 6 tests in 0.001s

OK

Каждый тест проверяет одно поведение:

  • test_upper и test_lower подтверждают, что HELLO становится KHOOR, а attack становится dwwdfn.
  • test_wrap подтверждает, что Z сдвигается вперёд в C, а это работает только при правильном переходе через конец алфавита.
  • test_large_shift подтверждает, что сдвиг 29 даёт тот же результат, что и сдвиг 3.
  • test_negative проверяет расшифровку, сдвигая DEF на -3.
  • test_roundtrip шифрует текст со сдвигом 7, расшифровывает с -7 и ожидает получить исходный текст. В примере есть пунктуация, цифра и á, поэтому тест заодно проверяет, что символы вне алфавита остаются целыми.

Если вы измените код и тест упадёт, в сообщении об ошибке будут вход, ожидаемое и фактическое значения. Прочитайте это сообщение, прежде чем менять что-либо ещё.

Шаг 4: разберитесь с ограничением

У шифра Цезаря только 25 полезных сдвигов. Сдвиг 0 и сдвиг 26 возвращают исходный текст. Человек может перебрать все сдвиги вручную, а программа переберёт их мгновенно.

Вторая проблема — частотный анализ. В английском языке такие буквы, как E и T, встречаются чаще, чем Q или Z. В длинном сообщении самые частые буквы шифртекста часто указывают на самые частые буквы открытого текста.

Поэтому настоящей конфиденциальности этот шифр не даёт. Не используйте его для паролей, записей о клиентах, токенов и всего остального, что вам важно. Если нужно настоящее шифрование, используйте хорошо проверенную библиотеку и стандартный алгоритм. Базовые правила защиты хранимых данных перечислены в OWASP Cryptographic Storage Cheat Sheet.

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

Дополнительно: проверка кода с помощью ИИ-модели

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

Проверь этот учебный шифр Цезаря. Что происходит при отрицательных сдвигах, символах с диакритикой и нелатинском тексте? Не описывай его как надёжное шифрование.

Запускайте каждый тест, предложенный моделью, прежде чем его принять. Модели ошибаются, а упавший тест скажет больше, чем уверенное объяснение.

Следующий шаг

Когда лабораторная заработает, поместите папку в систему контроля версий, чтобы отслеживать изменения. Как это сделать, объясняет следующая статья, Что такое GitHub?.

Источники

Источники

  1. Python built-in functions (ord, chr)
  2. Python unittest documentation
  3. Python command-line options (-I)
  4. OWASP Cryptographic Storage Cheat Sheet

Ещё по теме: Криптография

Криптография

Пароли и хеширование с Argon2id

Почему быстрые хеши не годятся для паролей, что делают соль и коэффициент нагрузки и как хешировать и проверять пароли с Argon2id в Python через argon2-cffi.

Средний9 мин чтения