Spec-Zone.ru › Ruby 3.3

модуль 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 (Extract-and-Expand Key Derivation Function), как указано в 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

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

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