Как это устроено
История — просто ещё одна запись байтов, как hex или слова сид-фразы. В каждом предложении семь мест для слов: имя (1024 слова, 10 бит), глагол в прошедшем времени (512, 9 бит), число (16, 4 бита), прилагательное (1024, 10 бит), существительное во множественном числе (1024, 10 бит), предлог (in, near, outside, beyond, behind, above, below, around: 3 бита) и место (512, 9 бит). Итого 55 бит на предложение. Четыре шаблона предложений чередуются, чтобы текст не читался как таблица: «Margaret painted ...», «Near Oslo, Hugo borrowed ...», «Then ...», «... also ...».
- Первое слово, «Apparently», — версия формата (v0).
- Дальше идут 3 бита длины, сами байты, нулевое дополнение и 30-битная контрольная сумма RS1024 — тот же код, что в долях SLIP-39 (строка настройки «storykey»).
- 16 байт занимают 3 предложения, 32 байта — 6, а 77-байтный зашифрованный ключ-файл — 12.
Опечатки и автозамена
Регистр, знаки препинания, диакритика и переносы строк не важны, короткая строка заголовка перед историей пропускается. Слово узнаётся по первым четырём буквам, поэтому «lighthouse» всё равно значит «lighthouses». В каждом списке префиксы из четырёх букв уникальны и нет похожих слов (отличающихся одной буквой). Про неизвестное слово страница скажет, в каком оно предложении, и предложит похожие. Если слово заменено другим верным словом (автозамена, описка), не сойдётся контрольная сумма: замена одного слова обнаруживается всегда и никогда не даёт другой ключ. Тогда страница предложит исправить одно слово — это догадка, сверьте её со своей копией.
Шифрование паролем
Сама история не зашифрована: кто её прочитал, у того и ключ. Для защиты сначала зашифруйте: в истории окажутся байты версия (1) + соль (16) + IV (12) + шифртекст и тег, где ключ — Argon2id(пароль, соль) с 64 МиБ памяти и 3 проходами, шифр — AES-256-GCM. Открытого нет ничего: ни отпечатка, ни идентификатора, ни поля длины, поэтому байты выглядят случайными. 32-байтный ключ даёт ровно 77 байт — размер ключ-файла менеджера паролей.
Папка-приманка
Сделайте N историй из случайных байтов той же длины, с той же версией и верной контрольной суммой, и положите настоящую среди них, у каждой случайный заголовок — как папка с рассказами. Приманки не отличить даже тому, кто знает эту страницу, потому что настоящая история — шифртекст без открытых данных. Подбор пароля теперь стоит N + 1 запусков Argon2id на попытку: 100 приманок добавляют около 6,7 бита. Выигрыш скромный, главная защита — пароль. В zip файлы лежат в случайном порядке и со случайными датами за последние пять лет, настоящая история тоже, так что ни порядок, ни даты на неё не указывают; только не держите рядом с папкой отдельную копию настоящей истории. Запомните заголовок настоящей истории или выберите все файлы на вкладке «Расшифровать» — страница попробует каждый (около секунды на файл).
Чем это не является
- Не «brain wallet». Направление всегда одно: случайные байты в текст. Нельзя написать свою историю или любимую цитату и получить из неё ключ: у человеческого текста мало энтропии, известные тексты есть в словарях, и медленный хеш не спасает.
- Не нейросетевая стеганография. Языковая модель написала бы глаже, но для раскодирования нужны те же веса и побитово одинаковая арифметика. Другая видеокарта, библиотека или версия модели — и ключ потерян. Фиксированные списки слов раскодируются одинаково всегда.
Формат v0 экспериментальный
Списки слов будут заморожены навсегда, когда формат станет окончательным: история должна раскодироваться и через 20 лет. До этого они могут измениться. Для ключа, который нельзя потерять, держите и другую копию, например доли Шамира.