Spec-Zone.ru › Ruby 3.2

модуль OpenSSL::KDF

Предоставляет функциональность различных KDF (функций вывода ключей).

KDF обычно используется для безопасного вывода симметричных ключей произвольной длины, которые будут использоваться с OpenSSL::Cipher, из паролей. Другой случай использования — хранение паролей: благодаря возможности настраивать сложность вычислений, увеличивая количество итераций, вычисления можно искусственно замедлить, чтобы сделать возможные атаки неэффективными.

В настоящее время OpenSSL::KDF предоставляет реализации следующих KDF:

  • PKCS #5 PBKDF2 (Password-Based Key Derivation Function 2) в сочетании с HMAC

  • scrypt

  • HKDF

Примеры

Генерация 128-битного ключа для Cipher (например, AES)

pass = "secret"
salt = OpenSSL::Random.random_bytes(16)
iter = 20_000
key_len = 16
key = OpenSSL::KDF.pbkdf2_hmac(pass, salt: salt, iterations: iter,
                               length: key_len, hash: "sha1")

Хранение паролей

pass = "secret"
# store this with the generated value
salt = OpenSSL::Random.random_bytes(16)
iter = 20_000
hash = OpenSSL::Digest.new('SHA256')
len = hash.digest_length
# the final value to be stored
value = OpenSSL::KDF.pbkdf2_hmac(pass, salt: salt, iterations: iter,
                                 length: len, hash: hash)

Важное замечание по проверке паролей

При сравнении паролей, введённых пользователем, со значениями, хранящимися ранее, распространённой ошибкой является сравнение двух значений с помощью «==». Обычно «==» выполняет короткое замыкание при оценке и поэтому уязвим к атакам по временно́й сложности. Правильный способ — использовать метод, который всегда затрачивает одинаковое время при сравнении двух значений, не раскрывая никакой информации потенциальным злоумышленникам. Для этого используйте OpenSSL.fixed_length_secure_compare.

Методы публичного класса

hkdf(ikm, salt:, info:, length:, hash:) → String Показать исходный код
static VALUE
kdf_hkdf(int argc, VALUE *argv, VALUE self)
{
    VALUE ikm, salt, info, opts, kwargs[4], str;
    static ID kwargs_ids[4];
    int saltlen, ikmlen, infolen;
    size_t len;
    const EVP_MD *md;
    EVP_PKEY_CTX *pctx;

    if (!kwargs_ids[0]) {
        kwargs_ids[0] = rb_intern_const("salt");
        kwargs_ids[1] = rb_intern_const("info");
        kwargs_ids[2] = rb_intern_const("length");
        kwargs_ids[3] = rb_intern_const("hash");
    }
    rb_scan_args(argc, argv, "1:", &ikm, &opts);
    rb_get_kwargs(opts, kwargs_ids, 4, 0, kwargs);

    StringValue(ikm);
    ikmlen = RSTRING_LENINT(ikm);
    salt = StringValue(kwargs[0]);
    saltlen = RSTRING_LENINT(salt);
    info = StringValue(kwargs[1]);
    infolen = RSTRING_LENINT(info);
    len = (size_t)NUM2LONG(kwargs[2]);
    if (len > LONG_MAX)
        rb_raise(rb_eArgError, "length must be non-negative");
    md = ossl_evp_get_digestbyname(kwargs[3]);

    str = rb_str_new(NULL, (long)len);
    pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
    if (!pctx)
        ossl_raise(eKDF, "EVP_PKEY_CTX_new_id");
    if (EVP_PKEY_derive_init(pctx) <= 0) {
        EVP_PKEY_CTX_free(pctx);
        ossl_raise(eKDF, "EVP_PKEY_derive_init");
    }
    if (EVP_PKEY_CTX_set_hkdf_md(pctx, md) <= 0) {
        EVP_PKEY_CTX_free(pctx);
        ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_md");
    }
    if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, (unsigned char *)RSTRING_PTR(salt),
                                    saltlen) <= 0) {
        EVP_PKEY_CTX_free(pctx);
        ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_salt");
    }
    if (EVP_PKEY_CTX_set1_hkdf_key(pctx, (unsigned char *)RSTRING_PTR(ikm),
                                   ikmlen) <= 0) {
        EVP_PKEY_CTX_free(pctx);
        ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_key");
    }
    if (EVP_PKEY_CTX_add1_hkdf_info(pctx, (unsigned char *)RSTRING_PTR(info),
                                    infolen) <= 0) {
        EVP_PKEY_CTX_free(pctx);
        ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_info");
    }
    if (EVP_PKEY_derive(pctx, (unsigned char *)RSTRING_PTR(str), &len) <= 0) {
        EVP_PKEY_CTX_free(pctx);
        ossl_raise(eKDF, "EVP_PKEY_derive");
    }
    rb_str_set_len(str, (long)len);
    EVP_PKEY_CTX_free(pctx);

    return str;
}

Функция вывода ключа HKDF (HMAC-based Extract-and-Expand), определенная в RFC 5869.

Впервые появилась в OpenSSL 1.1.0.

Параметры

ikm

Входные данные ключа.

salt

Соль.

info

Контекстная и прикладная информация.

length

Длина выходных данных в октетах. Должна быть <= 255 * HashLen, где HashLen — длина выходных данных функции хэширования в октетах.

hash

Функция хэширования.

Пример

# The values from https://datatracker.ietf.org/doc/html/rfc5869#appendix-A.1
ikm = ["0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"].pack("H*")
salt = ["000102030405060708090a0b0c"].pack("H*")
info = ["f0f1f2f3f4f5f6f7f8f9"].pack("H*")
p OpenSSL::KDF.hkdf(ikm, salt: salt, info: info, length: 42, hash: "SHA256").unpack1("H*")
# => "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865"
pbkdf2_hmac(pass, salt:, iterations:, length:, hash:) → aString Показать исходный код
static VALUE
kdf_pbkdf2_hmac(int argc, VALUE *argv, VALUE self)
{
    VALUE pass, salt, opts, kwargs[4], str;
    static ID kwargs_ids[4];
    int iters, len;
    const EVP_MD *md;

    if (!kwargs_ids[0]) {
        kwargs_ids[0] = rb_intern_const("salt");
        kwargs_ids[1] = rb_intern_const("iterations");
        kwargs_ids[2] = rb_intern_const("length");
        kwargs_ids[3] = rb_intern_const("hash");
    }
    rb_scan_args(argc, argv, "1:", &pass, &opts);
    rb_get_kwargs(opts, kwargs_ids, 4, 0, kwargs);

    StringValue(pass);
    salt = StringValue(kwargs[0]);
    iters = NUM2INT(kwargs[1]);
    len = NUM2INT(kwargs[2]);
    md = ossl_evp_get_digestbyname(kwargs[3]);

    str = rb_str_new(0, len);
    if (!PKCS5_PBKDF2_HMAC(RSTRING_PTR(pass), RSTRING_LENINT(pass),
                           (unsigned char *)RSTRING_PTR(salt),
                           RSTRING_LENINT(salt), iters, md, len,
                           (unsigned char *)RSTRING_PTR(str)))
        ossl_raise(eKDF, "PKCS5_PBKDF2_HMAC");

    return str;
}

PKCS #5 PBKDF2 (Password-Based Key Derivation Function 2) в сочетании с HMAC. Принимает pass, salt и iterations, а затем выводит ключ длиной length байт.

Для получения дополнительной информации о PBKDF2, см. раздел 5.2 RFC 2898 (tools.ietf.org/html/rfc2898#section-5.2).

Параметры

pass

Пароль.

salt

Соль. Соли предотвращают атаки, основанные на словарях общих паролей и атаках с использованием таблиц радужных графиков. Это общедоступное значение, которое можно безопасно хранить вместе с паролем (например, если выведенное значение используется для хранения паролей).

iterations

Количество итераций. Это позволяет настраивать алгоритм. Лучше использовать максимально возможное количество итераций для максимальной защиты от атак методом перебора.

length

Желаемая длина выведенного ключа в октетах.

hash

Алгоритм хэширования, используемый с HMAC для PRF. Может быть String, представляющим имя алгоритма, или экземпляром OpenSSL::Digest.

scrypt(pass, salt:, N:, r:, p:, length:) → aString Показать исходный код
static VALUE
kdf_scrypt(int argc, VALUE *argv, VALUE self)
{
    VALUE pass, salt, opts, kwargs[5], str;
    static ID kwargs_ids[5];
    size_t len;
    uint64_t N, r, p, maxmem;

    if (!kwargs_ids[0]) {
        kwargs_ids[0] = rb_intern_const("salt");
        kwargs_ids[1] = rb_intern_const("N");
        kwargs_ids[2] = rb_intern_const("r");
        kwargs_ids[3] = rb_intern_const("p");
        kwargs_ids[4] = rb_intern_const("length");
    }
    rb_scan_args(argc, argv, "1:", &pass, &opts);
    rb_get_kwargs(opts, kwargs_ids, 5, 0, kwargs);

    StringValue(pass);
    salt = StringValue(kwargs[0]);
    N = NUM2UINT64T(kwargs[1]);
    r = NUM2UINT64T(kwargs[2]);
    p = NUM2UINT64T(kwargs[3]);
    len = NUM2LONG(kwargs[4]);
    /*
     * OpenSSL uses 32MB by default (if zero is specified), which is too small.
     * Let's not limit memory consumption but just let malloc() fail inside
     * OpenSSL. The amount is controllable by other parameters.
     */
    maxmem = SIZE_MAX;

    str = rb_str_new(0, len);
    if (!EVP_PBE_scrypt(RSTRING_PTR(pass), RSTRING_LEN(pass),
                        (unsigned char *)RSTRING_PTR(salt), RSTRING_LEN(salt),
                        N, r, p, maxmem, (unsigned char *)RSTRING_PTR(str), len))
        ossl_raise(eKDF, "EVP_PBE_scrypt");

    return str;
}

Выводит ключ из pass с помощью указанных параметров с функцией вывода паролей scrypt. Результат может быть использован для хранения паролей.

scrypt разработан для усиления защиты от атак методом перебора и атак с использованием специализированного оборудования, по сравнению с альтернативными KDF, такими как PBKDF2 или bcrypt.

Ключевые аргументы N, r и p могут быть использованы для настройки scrypt. RFC 7914 (опубликован в 2016-08, tools.ietf.org/html/rfc7914#section-2) указывает, что использование значений r=8 и p=1, как правило, даёт хорошие результаты.

Дополнительную информацию см. в RFC 7914 (tools.ietf.org/html/rfc7914).

Параметры

pass

Пароль.

salt

Соль.

N

Параметр сложности вычислений/памяти. Должно быть степенью двойки.

r

Параметр размера блока.

p

Параметр параметризации.

length

Длина выведенного ключа в октетах.

Пример

pass = "password"
salt = SecureRandom.random_bytes(16)
dk = OpenSSL::KDF.scrypt(pass, salt: salt, N: 2**14, r: 8, p: 1, length: 32)
p dk #=> "\xDA\xE4\xE2...\x7F\xA1\x01T"

Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API