Pull to refresh

Comments 16

Большое спасибо. Отличная статья! Сам в свое время занимался подобными разработками, правда спускаться до пространства пользователя не было необходимости. И так же, как и Вы, изучал это Crypto API по исходникам ядра))
Спасибо за комментарий.
Вот как раз из-за того, что изучать приходилось по исходникам, я и решил эти знания, так сказать, увековечить)
А как бы вы посоветовали реализовать операцию диверсификации ключа?
Регистрировать новый алгоритм для этого?
Или есть возможность реализовать свой шаблон?
Хороший вопрос. В своё время у нас тоже появлялась необходимость в такой операции, но, в итоге, мы решили реализовать и применять её во «фронтенде». То есть Crypto API лишь обрабатывает данные на некотором, установленном ранее, ключе. А вот та часть, что непосредственно этот ключ устанавливает — проводит диверсификацию, не обращаясь для этого к Crypto API.

Насчёт регистрации нового алгоритма не уверен, поскольку в Crypto API нет подходящего для диверсификации типа преобразования. И да, я забыл написать об это в статье, но возможность реализовать свой шаблон похоже есть.
Спасибо огромное! Сейчас как раз есть необходимость погрузиться в эту тему и тут ваша статья.
Если будут какие-нибудь вопросы, обращайтесь, постараюсь ответить)
Здравствуйте, статья актуальна, но к сожалению, на первых строчках есть ошибки к примеру:
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 также рекомендует (и обосновывает это) избегать явного приведения типов в таких случаях.
Цикл 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 в сигнатуре функции (именно на память):

void xor_cipher_free(xor_cipher_ctx* volatile ctx)
{
    memset(ctx->key, 0xFF, XOR_CIPHER_KEY_SIZE);
    free(ctx);
}


Работает на GCC 7.3 -O2 и -O3.
Можно сделать чуть эффективнее:

void *(*volatile memset_s)(void *, int, size_t) = memset;

А вообще в C11 функция memset_s предусмотрена стандартом, но, кажется, ни один компилятор ее так и не реализовал.
Да, действительно. Спасибо.

memset_s, по крайней мере, точно есть в Visual Studio.

А разве использование Crypto API из userspace не будет заведомо медленнее собственной реализации (да даже того же OpenSSL)? Ведь нужно копирование блоков в kernel-space и обратно.

Поэтому к Crypto API из юзерспейса можно обратиться через системные вызовы splice/vmsplice, это помогает избежать такого копирования. Подробнее смотрите здесь.

А вообще, возможно, вы подбросили мне идею для ещё одной статьи)
Sign up to leave a comment.

Articles