Криптография
Шифрование, кодирование и хеширование: что делает каждое
Разделяем кодирование, шифрование и хеширование на лабораторных с Base64, SHA-256 и OpenSSL. Почему Base64 ничего не защищает, а хеши необратимы.
Напишите шифр Цезаря на Python, протестируйте его с unittest и убедитесь, что такой шифр сдвига не даёт настоящей секретности.
Вы напишете функцию caesar(). Она сдвигает английские буквы на заданное число позиций, не трогает все остальные символы и отменяет сдвиг, если передать отрицательное число. Затем вы запустите небольшой набор тестов и разберёте, что они проверяют.
Нужен только Python 3. Если идея шифра сдвига для вас нова, сначала прочитайте Что такое шифр Цезаря?.
Установите Python 3 с python.org. Код использует только стандартную библиотеку, поэтому устанавливать через pip нечего. Создайте папку для лабораторной и сохраняйте файлы в неё.
Вывод в этой статье получен на Python 3.12.10 в Windows 11. Другие версии я не проверял.
Функция ord() в Python возвращает кодовую позицию Unicode для символа. chr() делает обратное: превращает кодовую позицию обратно в символ. Для прописной буквы A кодовая позиция равна 65.
Выполните в терминале:
python -I -c "print(ord('A'), chr(65))"Вывод:
65 AФлаг -I запускает Python в изолированном режиме. Переменные окружения, начинающиеся с PYTHON, игнорируются, а текущая папка не добавляется в путь поиска модулей. Используйте его, когда запускаете код из папки, содержимое которой писали не вы.
Сдвиг работает с позицией, отсчитываемой от нуля. Вычитание 65 превращает A в 0, а Z в 25. Шаг % 26 возвращает результат в пределы алфавита, а прибавление 65 восстанавливает диапазон прописных букв:
chr((ord(letter) - 65 + shift) % 26 + 65)Строчные буквы начинаются с 97, поэтому та же идея работает с 97 вместо 65.
Оператор % в Python никогда не возвращает отрицательное число, если делитель положительный. Поэтому отрицательный сдвиг работает без дополнительного кода:
python -I -c "print((0 - 3) % 26)"Вывод:
23Ноль минус три с переходом через начало алфавита даёт 23, это позиция X.
Сохраните это как caesar_cipher.py. Это тот же код, что и в файле лабораторной.
"""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.
Запустите файл:
python -I caesar_cipher.pyВывод:
KHOOR, Zruog!
HELLO, World!Функция меняет только буквы от A до Z и от a до z. Цифры, пробелы, пунктуация и буквы с диакритикой проходят без изменений. Благодаря этому код остаётся коротким. Шифру, который обрабатывает буквы с диакритикой, нужно правило для каждой из них, а в этой лабораторной такое правило не задано.
В лабораторной есть и test_caesar_cipher.py. Откройте терминал в папке с обоими файлами и выполните:
python -I -m unittest discover -p 'test_*.py' -vВывод:
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 и ожидает получить исходный текст. В примере есть пунктуация, цифра и á, поэтому тест заодно проверяет, что символы вне алфавита остаются целыми.Если вы измените код и тест упадёт, в сообщении об ошибке будут вход, ожидаемое и фактическое значения. Прочитайте это сообщение, прежде чем менять что-либо ещё.
У шифра Цезаря только 25 полезных сдвигов. Сдвиг 0 и сдвиг 26 возвращают исходный текст. Человек может перебрать все сдвиги вручную, а программа переберёт их мгновенно.
Вторая проблема — частотный анализ. В английском языке такие буквы, как E и T, встречаются чаще, чем Q или Z. В длинном сообщении самые частые буквы шифртекста часто указывают на самые частые буквы открытого текста.
Поэтому настоящей конфиденциальности этот шифр не даёт. Не используйте его для паролей, записей о клиентах, токенов и всего остального, что вам важно. Если нужно настоящее шифрование, используйте хорошо проверенную библиотеку и стандартный алгоритм. Базовые правила защиты хранимых данных перечислены в OWASP Cryptographic Storage Cheat Sheet.
Считайте это упражнение способом изучить арифметику по модулю, коды символов и разницу между игрушечным преобразованием и настоящим шифром.
Когда тесты пройдут, можно попросить языковую модель объяснить строку с модулем, предложить граничные случаи или написать дополнительные тесты. Полезный промпт сразу называет ограничения:
Проверь этот учебный шифр Цезаря. Что происходит при отрицательных сдвигах, символах с диакритикой и нелатинском тексте? Не описывай его как надёжное шифрование.
Запускайте каждый тест, предложенный моделью, прежде чем его принять. Модели ошибаются, а упавший тест скажет больше, чем уверенное объяснение.
Когда лабораторная заработает, поместите папку в систему контроля версий, чтобы отслеживать изменения. Как это сделать, объясняет следующая статья, Что такое GitHub?.
Криптография
Разделяем кодирование, шифрование и хеширование на лабораторных с Base64, SHA-256 и OpenSSL. Почему Base64 ничего не защищает, а хеши необратимы.
Криптография
Почему быстрые хеши не годятся для паролей, что делают соль и коэффициент нагрузки и как хешировать и проверять пароли с Argon2id в Python через argon2-cffi.
Криптография
Публичные и приватные ключи с нуля: что доказывает подпись, почему приватный ключ остаётся в секрете, и лабораторная на Ed25519, где изменённый файл не проходит проверку.