72 lines
7.9 KiB
Markdown
72 lines
7.9 KiB
Markdown
## psycher
|
||
|
||
В данной задаче необходимо реализовать 128-битный линейный [блочный шифр](https://ru.wikipedia.org/wiki/Блочный_шифр). Этот cipher назван в честь psycho, придумавшего эту задачу.
|
||
|
||
### Шифрование за 5 минут
|
||
|
||
Шифрование - это способ обмена информацией так, чтобы противник, стоящий посередине, не узнал **ничего**.
|
||
|
||
Для такого свойства необходим секрет, которым владеют участники обмена информацией. Этот секрет может быть общим для обоих участников, тогда соответствующий шифр называется **симметричным** (примеры: aes, des). Если же ключи разные, то шифрование **асимметричное** (примеры: rsa, ecdsa).
|
||
|
||
С помощью секрета делается хитрое преобразование информации и передается собеседнику. Собеседник дешифрует со своим секретом и узнает, что было передано. Противник посередине видит преобразованное значение и ничего не понимает.
|
||
|
||
Блочный шифр - это разновидность симметричного шифра. Отличительная особенность в том, что имеется простой интерфейс - принять блок данных фиксированного размера (обычно 64-256 бит) и зашифровать/дешифровать его. По сути шифр - функция {0,1}^n -> {0,1}^n, причем такая, что значение на выходе *не отличимо* от рандомно сгенерированного значения
|
||
|
||
Для шифрования данных произвольного размера используются режимы шифрования. По сути данные разбиваются на привычные блоки для блочного шифра, шифруются им и комбинируются хитрым образом (комбинирование и есть суть режима шифрования). По сути результат шифрования более поздних блоков зависит от предыдущих. Такую зависимость нужно вводить, чтобы не показывать противнику паттерны в данных от блоков. (режимы шифрования в задаче не потребуются, это рассказано просто в образовательных целях)
|
||
|
||
### Легенда psycher
|
||
|
||
Представьте себе пространство {0,1}^128 над полем {0,1}. А теперь давайте возьмем в нем какой-нибудь базис и будем переводить в него векторы из тривиального базиса [{1, 0, 0, ...}, {0, 1, 0, ...}, ...]
|
||
|
||
Вы спросите, зачем нам это делать? А теперь я расскажу что придумал
|
||
![](./img.png)
|
||
|
||
Выбранный базис в виде 128 векторов размерности 128 будет нашим симметричным ключом. Шифрование данных - это перевод вектора в этот базис. Дешифрование - обратное преобразование.
|
||
|
||
При выборе произвольного базиса результат преобразования будет определенно неотличим от произвольной последовательности бит (ну, кроме нулевого вектора). То есть получился полноценный шифр!
|
||
|
||
Вообще теория говорит, что линейные шифры - это некруто и на практике их не так сложно взломать, как кажется. Но зато такой шифр отлично подходит для оттачивания навыков с курса :)
|
||
|
||
### Я программист, можно на человеческом?
|
||
|
||
Да, можно. При создании объекта шифра нам передается список из 128 значений, каждое - 128 бит (нигде не проверяется, но давайте считать, что данные представляют собой линейно независимые векторы).
|
||
|
||
При шифровании нужно сделать следующее:
|
||
1. Собираем все позиции в битовом представлении входных данных (128-битный блок)
|
||
2. Применяем xor всех значений с данными позициями из списка инициализации объекта
|
||
3. Возвращаем результат
|
||
|
||
Более того, этот функционал уже реализован в тестовом файле.
|
||
|
||
Для дешифрования нужно решать систему линейных уравнений. Это скучно, давайте не реализовывать дешифрование.
|
||
|
||
### Ничего не понял, а задача-то в чем?
|
||
|
||
А предоставленное решение-то медленное. Надо его ускорить.
|
||
|
||
Здесь вам помогут знания об оптимизациях компилятора, умение работать с бенчмарками и pprof.
|
||
|
||
Авторское решение лучше наивного примерно в 60 раз. Здесь не учитывается время создания шифра, но не стоит злоупотреблять этим. В идеале нужно понимать, какого размера шифротексты в среднем подаются на вход, т.е. сколько раз вызовется функция шифрования, чтобы правильно выбрать баланс между более тяжелым New и более тяжелым Encrypt. Rule of thumb - использовать не более единиц-десятков КБ доп памяти.
|
||
|
||
```
|
||
cpu: Intel(R) Core(TM) i7-9850H CPU @ 2.60GHz
|
||
BenchmarkSlowCipher
|
||
BenchmarkSlowCipher-12 731576 1592 ns/op 10.05 MB/s 0 B/op 0 allocs/op
|
||
BenchmarkCipher
|
||
BenchmarkCipher-12 39978231 26.47 ns/op 604.54 MB/s 0 B/op 0 allocs/op
|
||
PASS
|
||
```
|
||
|
||
### Подсказка по оптимизации
|
||
|
||
Можно использовать следующую схему. Она описывает порядок, в котором нужно оптимизировать программу.
|
||
|
||
1. Алгоритм (улучшение асимптотической сложности, константы)
|
||
2. Распараллеливание (разделяй и властвуй, здесь надо понять стоят ли накладные расходы того)
|
||
3. Микрооптимизации кода (pprof, gcflags -m, аллокации, ...)
|
||
4. Hardware оптимизации (кэш процессора, asm, unsafe)
|
||
|
||
На каждом шаге можно вернуться наверх, если приходит хорошая идея. Но тогда дальнейшие шаги возможно нужно перепридумать. И не обязательно в каждом что-то делать, это скорее порядок, в котором надо подумать.
|
||
|
||
|
||
|