Комментарии 16
Большое спасибо. Отличная статья! Сам в свое время занимался подобными разработками, правда спускаться до пространства пользователя не было необходимости. И так же, как и Вы, изучал это Crypto API по исходникам ядра))
А как бы вы посоветовали реализовать операцию диверсификации ключа?
Регистрировать новый алгоритм для этого?
Или есть возможность реализовать свой шаблон?
Регистрировать новый алгоритм для этого?
Или есть возможность реализовать свой шаблон?
Хороший вопрос. В своё время у нас тоже появлялась необходимость в такой операции, но, в итоге, мы решили реализовать и применять её во «фронтенде». То есть Crypto API лишь обрабатывает данные на некотором, установленном ранее, ключе. А вот та часть, что непосредственно этот ключ устанавливает — проводит диверсификацию, не обращаясь для этого к Crypto API.
Насчёт регистрации нового алгоритма не уверен, поскольку в Crypto API нет подходящего для диверсификации типа преобразования. И да, я забыл написать об это в статье, но возможность реализовать свой шаблон похоже есть.
Насчёт регистрации нового алгоритма не уверен, поскольку в Crypto API нет подходящего для диверсификации типа преобразования. И да, я забыл написать об это в статье, но возможность реализовать свой шаблон похоже есть.
Спасибо огромное! Сейчас как раз есть необходимость погрузиться в эту тему и тут ваша статья.
Здравствуйте, статья актуальна, но к сожалению, на первых строчках есть ошибки к примеру:
При компиляции с помощью GCC при оптимизации -O2 memset будет вырезан и таким образом Ваше «затирание» памяти не произойдёт. Такого рода вещи очень важны при разработке крипто-модулей.
Вот подтверждение (результаты дизассемблирования), компиляция с помощью GCC 7.3 -O2:
Могу порекомендовать вот этот сайт для подобных экспериментов.
Небольшая неточность, Вы забыли преобразование типов (это мелочь), calloc возвращает void*.
void xor_cipher_free(xor_cipher_ctx *ctx)
{
memset(ctx->key, 0xFF, XOR_CIPHER_KEY_SIZE);
free(ctx);
}
При компиляции с помощью GCC при оптимизации -O2 memset будет вырезан и таким образом Ваше «затирание» памяти не произойдёт. Такого рода вещи очень важны при разработке крипто-модулей.
Вот подтверждение (результаты дизассемблирования), компиляция с помощью GCC 7.3 -O2:
xor_cipher_allocate():
mov esi, 16
mov edi, 1
jmp calloc
xor_cipher_free(xor_cipher_ctx*):
jmp free
main:
xor eax, eax
ret
Могу порекомендовать вот этот сайт для подобных экспериментов.
Небольшая неточность, Вы забыли преобразование типов (это мелочь), calloc возвращает void*.
xor_cipher_ctx *cipher = calloc(1, sizeof(xor_cipher_ctx));
Спасибо за комментарий.
Да, я знаю о том, что gcc может обрезать такие вызовы и чем это черевато. Здесь, по хорошему, стоило занулить массив в простом цикле for, но я не стал этого делать, что бы не усложнять (пришлось бы объяснять почему gcc вырезает memset). Но упомянуть об этом безусловно стоило. В ближайшее время я постараюсь это исправить (или, вы можете предложить свою редакцию на github)
Насчёт второго — нет, я не забыл. Это преобразование избыточно, поскольку, в этом случае, void* автоматически и безопасно приводится к нужному типу. Литература по системному программированию в Linux также рекомендует (и обосновывает это) избегать явного приведения типов в таких случаях.
Да, я знаю о том, что gcc может обрезать такие вызовы и чем это черевато. Здесь, по хорошему, стоило занулить массив в простом цикле for, но я не стал этого делать, что бы не усложнять (пришлось бы объяснять почему gcc вырезает memset). Но упомянуть об этом безусловно стоило. В ближайшее время я постараюсь это исправить (или, вы можете предложить свою редакцию на github)
Насчёт второго — нет, я не забыл. Это преобразование избыточно, поскольку, в этом случае, void* автоматически и безопасно приводится к нужному типу. Литература по системному программированию в Linux также рекомендует (и обосновывает это) избегать явного приведения типов в таких случаях.
Цикл for точно так же может быть вырезан.
Проверил, при компиляции с -O3 "for" действительно вырезается.
Для Linux нашёл такой вариант надёжного мемсета:
void memset_s(void *v, int c, size_t n)
{
volatile unsigned char *p = (unsigned char*)v;
while(n--)
{
*p++ = c;
}
}
Этот не вырезается даже на O3.
Всё ещё проще, volatile в сигнатуре функции (именно на память):
Работает на GCC 7.3 -O2 и -O3.
void xor_cipher_free(xor_cipher_ctx* volatile ctx)
{
memset(ctx->key, 0xFF, XOR_CIPHER_KEY_SIZE);
free(ctx);
}
Работает на GCC 7.3 -O2 и -O3.
Можно сделать чуть эффективнее:
А вообще в C11 функция memset_s предусмотрена стандартом, но, кажется, ни один компилятор ее так и не реализовал.
void *(*volatile memset_s)(void *, int, size_t) = memset;
А вообще в C11 функция memset_s предусмотрена стандартом, но, кажется, ни один компилятор ее так и не реализовал.
А разве использование Crypto API из userspace не будет заведомо медленнее собственной реализации (да даже того же OpenSSL)? Ведь нужно копирование блоков в kernel-space и обратно.
Поэтому к Crypto API из юзерспейса можно обратиться через системные вызовы splice/vmsplice, это помогает избежать такого копирования. Подробнее смотрите здесь.
А вообще, возможно, вы подбросили мне идею для ещё одной статьи)
А вообще, возможно, вы подбросили мне идею для ещё одной статьи)
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Расширение и использование Linux Crypto API