Как правильно использовать libsodium так, чтобы он совместим между версиями?
Я планирую хранить кучу записей в файле, где каждая запись подписывается с libsodium. Тем не менее, я хотел бы, чтобы будущие версии моей программы могли проверять подписи, которые была сделана в текущей версии, и в идеале наоборот.
Для текущей версии Sodium подписи создаются с использованием алгоритма Ed25519. Я предполагаю, что примитив по умолчанию может измениться в новых версиях Sodium (иначе libsodium не будет выставлять способ выбрать конкретный, я думаю).
Должен ли я...
- Всегда используйте примитив по умолчанию (т.е.
crypto_sign
)
- Используйте определенный примитив (т.е.
crypto_sign_ed25519
)
- Сделайте (1), но сохраните значение
sodium_library_version_major()
в файле (либо в выделенном поле "натриевая версия", либо в общем поле "изменение формата файла" ) и закройте, если текущая версия ниже
- Сделайте (3), но также сохраните
crypto_sign_primitive()
- Сделайте (4), но также сохраните
crypto_sign_bytes()
и друзей
... или я должен делать что-то еще?
Моя программа будет записана на C.
Ответы
Ответ 1
Просто используйте API высокого уровня.
Функции из API высокого уровня не будут использовать другой алгоритм без перегрузки основной версии библиотеки.
Единственное нарушение, которое можно ожидать в libsodium 1.x.y - это удаление устаревших/недокументированных функций (которые даже не существуют в текущих версиях, скомпилированных с помощью переключателя --enable-minimal
). Все остальное будет оставаться обратно совместимым.
Новые алгоритмы могут быть введены в версии 1.x.y без высокоуровневых оберток и будут стабилизированы и выставлены через новый API высокого уровня в libsodium 2.
Поэтому не надо звонить crypto_sign_ed25519()
. Просто используйте crypto_sign()
.
Ответ 2
Пусть сначала идентифицирует множество возможных проблем, а затем попытается его решить. У нас есть некоторые данные (запись) и подпись. Подпись может быть вычислена с помощью разных алгоритмов. Программа может развиваться и изменять свое поведение, libsodium также может (независимо) развиваться и изменять свое поведение. На фронте генерации подписи мы имеем:
-
crypto_sign()
, который использует некоторый алгоритм по умолчанию для создания сигнатур (на момент написания просто вызывает crypto_sign_ed25519()
)
-
crypto_sign_ed25519()
, который создает сигнатуры на основе конкретного алгоритма ed25519
Я предполагаю, что для одного конкретного алгоритма, заданного теми же входными данными и одним и тем же ключом, мы всегда получим тот же результат, что и его математика и любое отклонение от этого правила сделают библиотеку полностью непригодной.
Посмотрим на два основных варианта:
- Используя
crypto_sign_ed25519()
все время и никогда не изменяя это. Не так уж плохо, потому что он простой и до тех пор, пока crypto_sign_ed25519()
существует в libsodium и стабилен в своем выходе, вам не о чем беспокоиться со стабильной фиксированной подписью и нулевыми затратами на управление для этого. Конечно, в будущем кто-то может обнаружить какую-то ужасную проблему с этим алгоритмом, и если вы не готовы изменить алгоритм, который может означать ужасную проблему для вас.
- Использование
crypto_sign()
. С этим у нас внезапно возникает много проблем, потому что алгоритм может измениться, поэтому вы должны хранить некоторые метаданные вместе с подписью, что открывает набор вопросов:
- что хранить?
- должны ли эти метаданные быть уровня записи или уровня файла?
Что мы имеем в упомянутых функциях для второго подхода?
-
sodium_library_version_major()
- это функция, чтобы сообщить нам версию API библиотеки. Он напрямую не связан с изменениями в поддерживаемых/стандартных алгоритмах, поэтому он мало используется для наших проблем.
-
crypto_sign_primitive()
- это функция, которая возвращает строку, идентифицирующую алгоритм, используемый в crypto_sign()
. Это идеальное соответствие для того, что нам нужно, потому что, предположительно, его выход будет меняться точно в то время, когда алгоритм изменился.
-
crypto_sign_bytes()
- это функция, которая возвращает размер подписи, создаваемой crypto_sign()
в байтах. Это полезно для определения объема хранилища, необходимого для подписи, но он может легко оставаться неизменным, если алгоритм изменяется, поэтому он не должен содержать явные метаданные.
Теперь, когда мы знаем, что хранить, возникает вопрос об обработке сохраненных данных. Вам нужно получить имя алгоритма и использовать его для вызова соответствующей функции проверки. К сожалению, из того, что я вижу, сам libsodium не предоставляет какой-либо простой способ получить правильную функцию, учитывая имя алгоритма (например, EVP_get_cipherbyname()
или EVP_get_digestbyname()
в openssl), поэтому вам нужно сделать это самостоятельно (что, конечно, должно fail для неизвестного имени). И если вам нужно сделать это самостоятельно, возможно, было бы еще проще хранить некоторый числовой идентификатор вместо имени из библиотеки (еще больше кода).
Теперь вернемся к уровням файлов и уровня записи. Чтобы решить, есть еще два вопроса, чтобы спросить — вы можете генерировать новые подписи для старых записей в любой момент времени (это технически возможно, это разрешено политикой), и вам нужно добавить новые записи в старые файлы?
Если вы не можете генерировать новые подписи для старых записей или вам нужно добавить новые записи и не хотите снижения производительности для регенерации подписей, то у вас нет большого выбора, и вам нужно:
- у вас есть поле динамического размера для вашей подписи
- хранить алгоритм (динамическое строковое поле или внутренний (для вашего приложения) ID), используемый для генерации подписи вместе с самой сигнатурой
Если вы можете создавать новые подписи или, особенно, если вам не нужно добавлять новые записи, то при хранении алгоритма, используемого в специальном поле на уровне файла, вы можете уйти с более простым подходом к файловому уровню и, если изменения алгоритма подписки, регенерировать все подписи при сохранении файла (или использовать старый при добавлении новых записей, что также связано с вопросом политики совместимости).
Другие варианты? Ну, что такого особенного в crypto_sign()
? Это то, что его поведение не под вашим контролем, разработчики libsodium выбирают для вас алгоритм (без сомнения, они выбирают хороший), но если у вас есть какая-либо информация о версиях в вашей файловой структуре (не подписи, я имею в виду), ничто не мешает вам делая свой собственный выбор и используя один алгоритм с одной версией файла, а другой с другим (с кодом конверсии, когда это необходимо, конечно). Опять же, это также основано на предположении, что вы можете сгенерировать новую подпись и которая разрешена политикой.
Это возвращает нас к первоначальным двум вариантам с вопросом о том, стоит ли делать все это по сравнению с использованием crypto_sign_ed25519()
. Это в основном зависит от продолжительности вашей программы, я бы, наверное, сказал (как мнение), что если это менее 5 лет, то проще просто использовать один конкретный алгоритм. Если это может быть легко более 10 лет, то нет, вам действительно нужно уметь выжить алгоритм (и, возможно, даже целая крипто библиотека).