Spec-Zone.ru › Ruby 4.0
  1. OpenSSL::
  2. Шифр

класс OpenSSL::Cipher

Родительский класс:
Object

Предоставляет симметричные алгоритмы шифрования и расшифрования. Доступные алгоритмы зависят от установленной версии OpenSSL.

Список всех поддерживаемых алгоритмов

Список поддерживаемых алгоритмов можно получить с помощью

puts OpenSSL::Cipher.ciphers

Создание экземпляра Cipher

Существует несколько способов создать экземпляр Cipher. Как правило, алгоритм Cipher определяется его названием, длиной ключа в битах и режимом шифрования. Самый универсальный способ создать Cipher выглядит так:

cipher = OpenSSL::Cipher.new('<name>-<key length>-<mode>')

То есть это строка, состоящая из названий отдельных компонентов, объединённых дефисами: имени, длины ключа и режима. Можно использовать строки как в верхнем, так и в нижнем регистре, например:

cipher = OpenSSL::Cipher.new('aes-128-cbc')

Выбор режима шифрования или расшифрования

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

cipher.encrypt

или

cipher.decrypt

для экземпляра Cipher. Этот вызов должен быть первым после создания экземпляра, иначе уже заданные настройки могут быть утрачены.

Выбор ключа

Для симметричного шифрования требуется ключ, одинаковый у шифрующей и расшифровывающей сторон. После первоначального установления ключа его следует хранить в секрете. Существует множество способов создать ненадёжные ключи; самый очевидный — просто использовать пароль в качестве ключа, не обрабатывая его дополнительно. Простой и безопасный способ создать ключ для конкретного Cipher выглядит так:

cipher = OpenSSL::Cipher.new('aes-256-cfb')
cipher.encrypt
key = cipher.random_key # also sets the generated key on the Cipher

Если вам совершенно необходимо использовать пароли в качестве ключей шифрования, следует применить функцию выработки ключа на основе пароля 2 (PBKDF2), создав ключ с помощью функциональности, предоставляемой OpenSSL::PKCS5.pbkdf2_hmac_sha1 или OpenSSL::PKCS5.pbkdf2_hmac.

Хотя существует метод Cipher#pkcs5_keyivgen, его использование считается устаревшим. Его следует применять только в устаревших приложениях, поскольку он не использует более новые алгоритмы PKCS#5 v2.

Выбор вектора инициализации

Режимы шифрования CBC, CFB, OFB и CTR требуют «вектора инициализации», или сокращённо IV. Режим ECB — единственный режим, которому не требуется IV, однако для него почти нет оправданных сценариев использования, поскольку он недостаточно скрывает шаблоны открытого текста. Поэтому

Не используйте режим ECB, если только вы не уверены, что он вам действительно необходим

В результате в любом случае вы будете использовать режим, для которого явно требуется IV. Хотя IV можно считать общедоступной информацией, то есть после создания его можно передавать открыто, он всё равно должен оставаться непредсказуемым, чтобы предотвратить некоторые виды атак. Поэтому в идеале

Всегда создавайте надёжный случайный IV для каждого шифрования с помощью Cipher

Для каждого шифрования данных следует создавать новый случайный IV. Считайте IV одноразовым числом (nonce): он общедоступен, но случаен и непредсказуем. Надёжный случайный IV можно создать так:

cipher = ...
cipher.encrypt
key = cipher.random_key
iv = cipher.random_iv # also sets the generated IV on the Cipher

Хотя ключ, как правило, тоже является случайным значением, использовать его в качестве IV не следует. Существуют сложные способы, с помощью которых атакующий может извлечь выгоду из такого IV. Общее практическое правило: следует любой ценой избегать прямого или косвенного раскрытия ключа и отступать от этого правила только при наличии веских причин.

Вызов Cipher#final

ECB (который не следует использовать) и CBC — блочные режимы. Это означает, что, в отличие от других потоковых режимов, они обрабатывают данные блоками фиксированного размера, поэтому для формирования или корректного расшифрования последнего блока данных требуется шаг «завершения», на котором соответствующим образом обрабатывается заполнение. Поэтому обязательно добавляйте результат OpenSSL::Cipher#final в буфер шифрования/расшифрования, иначе возникнут ошибки расшифрования или данные будут усечены.

Хотя для потоковых шифров это не является строго необходимым, рекомендуется применять тот же подход и добавлять туда результат Cipher#final — это также упростит переход между режимами в будущем.

Шифрование и расшифрование данных

data = "Very, very confidential data"

cipher = OpenSSL::Cipher.new('aes-128-cbc')
cipher.encrypt
key = cipher.random_key
iv = cipher.random_iv

encrypted = cipher.update(data) + cipher.final
...
decipher = OpenSSL::Cipher.new('aes-128-cbc')
decipher.decrypt
decipher.key = key
decipher.iv = iv

plain = decipher.update(encrypted) + decipher.final

puts data == plain #=> true

Аутентифицированное шифрование и связанные Data (AEAD)

Если используемая версия OpenSSL поддерживает этот режим, всегда следует отдавать предпочтение режиму аутентифицированного шифрования (например, GCM или CCM), а не режимам без аутентификации. В настоящее время OpenSSL поддерживает аутентифицированное шифрование только в сочетании со связанными Data (AEAD), при котором дополнительные связанные данные включаются в процесс шифрования для вычисления тега в его конце. Этот тег также используется при расшифровании: проверка его корректности позволяет установить подлинность данного шифротекста.

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

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

Пример с использованием режима GCM (Galois/Counter Mode). У вас есть 16-байтовый ключ, 12-байтовый (96 бит) nonce и связанные данные auth_data. Не используйте повторно пару ключ и nonce. Повторное использование nonce нарушает гарантии безопасности режима GCM.

key = OpenSSL::Random.random_bytes(16)
nonce = OpenSSL::Random.random_bytes(12)
auth_data = "authenticated but unencrypted data"
data = "encrypted data"

cipher = OpenSSL::Cipher.new('aes-128-gcm').encrypt
cipher.key = key
cipher.iv = nonce
cipher.auth_data = auth_data

encrypted = cipher.update(data) + cipher.final
tag = cipher.auth_tag(16)

Теперь вы — получатель. Вам известен ключ, и вы получили nonce, auth_data, encrypted и tag через ненадёжную сеть. Обратите внимание, что GCM допускает длину тега от 1 до 16 байт. Возможно, вам также потребуется проверить, что полученный тег имеет правильную длину. Иначе вы позволите атакующим подделать корректный однобайтовый тег для изменённого шифротекста с вероятностью 1/256.

raise "tag is truncated!" unless tag.bytesize == 16
decipher = OpenSSL::Cipher.new('aes-128-gcm').decrypt
decipher.key = key
decipher.iv = nonce
decipher.auth_tag = tag # could be called at any time before #final
decipher.auth_data = auth_data

decrypted = decipher.update(encrypted) + decipher.final

puts data == decrypted #=> true

Обратите внимание, что для других шифров AEAD могут потребоваться дополнительные шаги, например предварительная установка ожидаемой длины тега (auth_tag_len=) или общей длины данных (ccm_data_len=). Подробности см. на соответствующей странице руководства.

Публичные методы класса

OpenSSL::Cipher.ciphers → array[string...] Показать исходный код
static VALUE
ossl_s_ciphers(VALUE self)
{
    VALUE ary;

    ary = rb_ary_new();
    OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH,
                           add_cipher_name_to_ary,
                           (void*)ary);

    return ary;
}

Возвращает массив с именами всех доступных шифров.

new(string) → cipher Показать исходный код
static VALUE
ossl_cipher_initialize(VALUE self, VALUE str)
{
    EVP_CIPHER_CTX *ctx;
    const EVP_CIPHER *cipher;
    VALUE cipher_holder;

    GetCipherInit(self, ctx);
    if (ctx) {
        ossl_raise(rb_eRuntimeError, "Cipher already initialized!");
    }
    cipher = ossl_evp_cipher_fetch(str, &cipher_holder);
    AllocCipher(self, ctx);
    if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, -1) != 1)
        ossl_raise(eCipherError, "EVP_CipherInit_ex");
    rb_ivar_set(self, id_cipher_holder, cipher_holder);

    return self;
}

Строка должна содержать допустимое имя шифра, например «aes-256-cbc».

Список имён шифров можно получить, вызвав OpenSSL::Cipher.ciphers.

Публичные методы экземпляра

auth_data = string Показать исходный код
static VALUE
ossl_cipher_set_auth_data(VALUE self, VALUE data)
{
    EVP_CIPHER_CTX *ctx;
    unsigned char *in;
    long in_len, out_len;

    StringValue(data);

    in = (unsigned char *) RSTRING_PTR(data);
    in_len = RSTRING_LEN(data);

    GetCipher(self, ctx);
    if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER))
        ossl_raise(eCipherError, "AEAD not supported by this cipher");

    if (!ossl_cipher_update_long(ctx, NULL, &out_len, in, in_len))
        ossl_raise(eCipherError, "couldn't set additional authenticated data");

    return data;
}

Задаёт для этого Cipher дополнительные аутентифицированные данные (AAD), также называемые связанными данными. Этот метод доступен для шифров AEAD.

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

Этот метод необходимо вызвать после задания key= и iv=, но до начала шифрования или расшифрования с помощью update. В некоторых режимах шифрования перед вызовом этого метода также необходимо вызвать auth_tag_len= и ccm_data_len=.

См. также раздел «AEAD Interface» на странице руководства EVP_EncryptInit(3). Этот метод внутренне вызывает EVP_CipherUpdate() с выходным буфером, равным NULL.

auth_tag(tag_len = 16) → String Показать исходный код
static VALUE
ossl_cipher_get_auth_tag(int argc, VALUE *argv, VALUE self)
{
    VALUE vtag_len, ret;
    EVP_CIPHER_CTX *ctx;
    int tag_len = 16;

    rb_scan_args(argc, argv, "01", &vtag_len);
    if (NIL_P(vtag_len))
        vtag_len = rb_attr_get(self, id_auth_tag_len);
    if (!NIL_P(vtag_len))
        tag_len = NUM2INT(vtag_len);

    GetCipher(self, ctx);

    if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER))
        ossl_raise(eCipherError, "authentication tag not supported by this cipher");

    ret = rb_str_new(NULL, tag_len);
    if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, RSTRING_PTR(ret)))
        ossl_raise(eCipherError, "retrieving the authentication tag failed");

    return ret;
}

Получает сгенерированный тег аутентификации. Этот метод доступен для шифров AEAD; его следует вызывать после завершения шифрования вызовом final.

Длина возвращаемого тега составляет tag_len байт. В некоторых режимах шифрования требуемую длину необходимо задать заранее отдельным вызовом auth_tag_len=, до начала шифрования.

См. также раздел «AEAD Interface» на странице руководства EVP_EncryptInit(3). Этот метод внутренне вызывает EVP_CIPHER_CTX_ctrl() с EVP_CTRL_AEAD_GET_TAG.

auth_tag = string Показать исходный код
static VALUE
ossl_cipher_set_auth_tag(VALUE self, VALUE vtag)
{
    EVP_CIPHER_CTX *ctx;
    unsigned char *tag;
    int tag_len;

    StringValue(vtag);
    tag = (unsigned char *) RSTRING_PTR(vtag);
    tag_len = RSTRING_LENINT(vtag);

    GetCipher(self, ctx);
    if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER))
        ossl_raise(eCipherError, "authentication tag not supported by this cipher");

    if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, tag))
        ossl_raise(eCipherError, "unable to set AEAD tag");

    return vtag;
}

Задаёт тег аутентификации для проверки целостности шифротекста.

Тег аутентификации необходимо задать до вызова final. Проверка тега выполняется при вызове final.

Обратите внимание: в режимах CCM и OCB ожидаемую длину тега необходимо задать до начала расшифрования отдельным вызовом auth_tag_len=. Содержимое тега можно передать в любой момент до вызова final.

ПРИМЕЧАНИЕ: Вызывающий код должен убедиться, что объект String, переданный этому методу, имеет нужную длину. Некоторые режимы шифрования поддерживают теги переменной длины, поэтому этот метод может принять усечённый тег, не вызвав исключения.

См. также раздел «AEAD Interface» на странице руководства EVP_EncryptInit(3). Этот метод внутренне вызывает EVP_CIPHER_CTX_ctrl() с EVP_CTRL_AEAD_SET_TAG.

auth_tag_len = integer Показать исходный код
static VALUE
ossl_cipher_set_auth_tag_len(VALUE self, VALUE vlen)
{
    int tag_len = NUM2INT(vlen);
    EVP_CIPHER_CTX *ctx;

    GetCipher(self, ctx);
    if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER))
        ossl_raise(eCipherError, "AEAD not supported by this cipher");

    if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, NULL))
        ossl_raise(eCipherError, "unable to set authentication tag length");

    /* for #auth_tag */
    rb_ivar_set(self, id_auth_tag_len, INT2NUM(tag_len));

    return vlen;
}

Задаёт длину ожидаемого тега аутентификации для этого Cipher. Этот метод доступен для некоторых шифров AEAD, которым необходимо задать длину до начала шифрования или расшифрования, например для режимов CCM и OCB.

В режимах CCM и OCB длину тега необходимо задать до задания iv=.

См. также раздел «AEAD Interface» на странице руководства EVP_EncryptInit(3). Этот метод внутренне вызывает EVP_CIPHER_CTX_ctrl() с EVP_CTRL_AEAD_SET_TAG и буфером NULL.

authenticated? → true | false Показать исходный код
static VALUE
ossl_cipher_is_authenticated(VALUE self)
{
    EVP_CIPHER_CTX *ctx;

    GetCipher(self, ctx);

    return (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER) ? Qtrue : Qfalse;
}

Показывает, использует ли этот экземпляр Cipher режим AEAD.

block_size → integer Показать исходный код
static VALUE
ossl_cipher_block_size(VALUE self)
{
    EVP_CIPHER_CTX *ctx;

    GetCipher(self, ctx);

    return INT2NUM(EVP_CIPHER_CTX_block_size(ctx));
}

Возвращает размер в байтах блоков, с которыми работает этот Cipher.

ccm_data_len = integer Показать исходный код
static VALUE
ossl_cipher_set_ccm_data_len(VALUE self, VALUE data_len)
{
    int in_len, out_len;
    EVP_CIPHER_CTX *ctx;

    in_len = NUM2INT(data_len);

    GetCipher(self, ctx);
    if (EVP_CipherUpdate(ctx, NULL, &out_len, NULL, in_len) != 1)
        ossl_raise(eCipherError, NULL);

    return data_len;
}

Задаёт общую длину сообщения с открытым текстом / шифротекстом, которое будет обработано методом update в режиме CCM.

Вызывайте этот метод после задания key= и iv=, но до вызова auth_data= или update.

Этот метод доступен только для шифров в режиме CCM.

См. также раздел «AEAD Interface» на странице руководства EVP_EncryptInit(3).

decrypt → self Показать исходный код
static VALUE
ossl_cipher_decrypt(VALUE self)
{
    return ossl_cipher_init(self, 0);
}

Инициализирует Cipher для расшифрования.

Перед выполнением любых операций с Cipher или заданием параметров обязательно вызовите encrypt либо decrypt.

Внутренне вызывает EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, 0).

encrypt → self Показать исходный код
static VALUE
ossl_cipher_encrypt(VALUE self)
{
    return ossl_cipher_init(self, 1);
}

Инициализирует Cipher для шифрования.

Перед выполнением любых операций с Cipher или заданием параметров обязательно вызовите encrypt либо decrypt.

Внутренне вызывает EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, 1).

final → string Показать исходный код
static VALUE
ossl_cipher_final(VALUE self)
{
    EVP_CIPHER_CTX *ctx;
    int out_len;
    VALUE str;

    GetCipher(self, ctx);
    str = rb_str_new(0, EVP_CIPHER_CTX_block_size(ctx));
    if (!EVP_CipherFinal_ex(ctx, (unsigned char *)RSTRING_PTR(str), &out_len)) {
        /* For AEAD ciphers, this is likely an authentication failure */
        if (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER) {
            /* For AEAD ciphers, EVP_CipherFinal_ex failures are authentication tag verification failures */
            ossl_raise(eAuthTagError, "AEAD authentication tag verification failed");
        }
        else {
            /* For non-AEAD ciphers */
            ossl_raise(eCipherError, "cipher final failed");
        }
    }
    assert(out_len <= RSTRING_LEN(str));
    rb_str_set_len(str, out_len);

    return str;
}

Возвращает оставшиеся данные, хранящиеся в объекте шифра. Последующие вызовы Cipher#update или Cipher#final недопустимы. Этот метод всегда следует вызывать последним при шифровании или расшифровании, после передачи всего открытого текста или шифротекста экземпляру Cipher.

При шифровании с помощью шифра AEAD тег аутентификации можно получить вызовом auth_tag после вызова final.

При расшифровании с помощью шифра AEAD этот метод проверяет целостность шифротекста и связанных данных по тегу аутентификации, который необходимо задать вызовом auth_tag= до вызова этого метода. Если проверка не пройдёт, будет вызвано исключение CipherError.

iv = string Показать исходный код
static VALUE
ossl_cipher_set_iv(VALUE self, VALUE iv)
{
    EVP_CIPHER_CTX *ctx;
    int iv_len = 0;

    StringValue(iv);
    GetCipher(self, ctx);

    if (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)
        iv_len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx);
    if (!iv_len)
        iv_len = EVP_CIPHER_CTX_iv_length(ctx);
    if (RSTRING_LEN(iv) != iv_len)
        ossl_raise(rb_eArgError, "iv must be %d bytes", iv_len);

    if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, (unsigned char *)RSTRING_PTR(iv), -1) != 1)
        ossl_raise(eCipherError, NULL);

    return iv;
}

Задаёт вектор инициализации (IV) шифра. Обратите внимание: поскольку режим ECB использовать не следует, IV всегда требуется задавать явно до начала шифрования. Сам IV можно безопасно передавать открыто.

Этот метод ожидает, что длина объекта String будет равна значению, возвращаемому iv_len. Чтобы использовать IV другой длины с шифром AEAD, перед вызовом этого метода необходимо задать iv_len=.

ПРИМЕЧАНИЕ: В соглашениях API OpenSSL значение IV в некоторых режимах шифрования может соответствовать «nonce». Подробности см. на страницах руководства OpenSSL.

См. также страницу руководства EVP_CipherInit_ex(3).

iv_len → integer Показать исходный код
static VALUE
ossl_cipher_iv_length(VALUE self)
{
    EVP_CIPHER_CTX *ctx;
    int len = 0;

    GetCipher(self, ctx);
    if (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)
        len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx);
    if (!len)
        len = EVP_CIPHER_CTX_iv_length(ctx);

    return INT2NUM(len);
}

Возвращает ожидаемую длину IV в байтах для этого Cipher.

iv_len = integer Показать исходный код
static VALUE
ossl_cipher_set_iv_length(VALUE self, VALUE iv_length)
{
    int len = NUM2INT(iv_length);
    EVP_CIPHER_CTX *ctx;

    GetCipher(self, ctx);
    if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER))
        ossl_raise(eCipherError, "cipher does not support AEAD");

    if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, len, NULL))
        ossl_raise(eCipherError, "unable to set IV length");

    /*
     * EVP_CIPHER_CTX_iv_length() returns the default length. So we need to save
     * the length somewhere. Luckily currently we aren't using app_data.
     */
    EVP_CIPHER_CTX_set_app_data(ctx, (void *)(VALUE)len);

    return iv_length;
}

Задаёт длину IV/nonce для этого Cipher. Этот метод доступен для шифров AEAD, поддерживающих переменную длину IV. Его можно вызвать, если требуется длина IV, отличная от стандартной длины OpenSSL, до вызова iv=.

См. также раздел «AEAD Interface» на странице руководства EVP_EncryptInit(3). Этот метод внутренне вызывает EVP_CIPHER_CTX_ctrl() с EVP_CTRL_AEAD_SET_IVLEN.

key = string Показать исходный код
static VALUE
ossl_cipher_set_key(VALUE self, VALUE key)
{
    EVP_CIPHER_CTX *ctx;
    int key_len;

    StringValue(key);
    GetCipher(self, ctx);

    key_len = EVP_CIPHER_CTX_key_length(ctx);
    if (RSTRING_LEN(key) != key_len)
        ossl_raise(rb_eArgError, "key must be %d bytes", key_len);

    if (EVP_CipherInit_ex(ctx, NULL, NULL, (unsigned char *)RSTRING_PTR(key), NULL, -1) != 1)
        ossl_raise(eCipherError, NULL);

    rb_ivar_set(self, id_key_set, Qtrue);

    return key;
}

Задаёт ключ шифра. Для создания ключа следует использовать либо безопасную случайную байтовую строку, либо, если ключ нужно получить из пароля, функциональность PBKDF2, предоставляемую OpenSSL::PKCS5. Для создания ключа на основе безопасных случайных данных можно использовать Cipher#random_key.

Вызывайте этот метод только после вызова Cipher#encrypt или Cipher#decrypt.

См. также страницу руководства EVP_CipherInit_ex(3).

key_len → integer Показать исходный код
static VALUE
ossl_cipher_key_length(VALUE self)
{
    EVP_CIPHER_CTX *ctx;

    GetCipher(self, ctx);

    return INT2NUM(EVP_CIPHER_CTX_key_length(ctx));
}

Возвращает длину ключа шифра Cipher в байтах.

key_len = integer Показать исходный код
static VALUE
ossl_cipher_set_key_length(VALUE self, VALUE key_length)
{
    int len = NUM2INT(key_length);
    EVP_CIPHER_CTX *ctx;

    GetCipher(self, ctx);
    if (EVP_CIPHER_CTX_set_key_length(ctx, len) != 1)
        ossl_raise(eCipherError, NULL);

    return key_length;
}

Задаёт длину ключа шифра. Если шифр имеет фиксированную длину ключа, попытка задать любое другое значение приведёт к ошибке.

В обычных условиях вызывать этот метод не нужно (и, скорее всего, не следует).

Дополнительные сведения см. в EVP_CIPHER_CTX_set_key_length.

name → string Показать исходный код
static VALUE
ossl_cipher_name(VALUE self)
{
    EVP_CIPHER_CTX *ctx;

    GetCipher(self, ctx);

    return rb_str_new2(EVP_CIPHER_name(EVP_CIPHER_CTX_cipher(ctx)));
}

Возвращает короткое имя шифра, которое может незначительно отличаться от исходного имени.

padding = 1 or 0 Показать исходный код
static VALUE
ossl_cipher_set_padding(VALUE self, VALUE padding)
{
    EVP_CIPHER_CTX *ctx;
    int pad = NUM2INT(padding);

    GetCipher(self, ctx);
    if (EVP_CIPHER_CTX_set_padding(ctx, pad) != 1)
        ossl_raise(eCipherError, NULL);
    return padding;
}

Включает или отключает дополнение. По умолчанию при шифровании используется стандартное дополнение блоков; при расшифровании дополнение проверяется и удаляется. Если параметр pad равен нулю, дополнение не выполняется. В этом случае общий объём шифруемых или расшифровываемых данных должен быть кратен размеру блока, иначе возникнет ошибка.

Дополнительные сведения см. в EVP_CIPHER_CTX_set_padding.

pkcs5_keyivgen(pass, salt = nil, iterations = 2048, digest = "MD5") → nil Показать исходный код
static VALUE
ossl_cipher_pkcs5_keyivgen(int argc, VALUE *argv, VALUE self)
{
    EVP_CIPHER_CTX *ctx;
    const EVP_MD *digest;
    VALUE vpass, vsalt, viter, vdigest, md_holder;
    unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH], *salt = NULL;
    int iter;

    rb_scan_args(argc, argv, "13", &vpass, &vsalt, &viter, &vdigest);
    StringValue(vpass);
    if(!NIL_P(vsalt)){
        StringValue(vsalt);
        if(RSTRING_LEN(vsalt) != PKCS5_SALT_LEN)
            ossl_raise(eCipherError, "salt must be an 8-octet string");
        salt = (unsigned char *)RSTRING_PTR(vsalt);
    }
    iter = NIL_P(viter) ? 2048 : NUM2INT(viter);
    if (iter <= 0)
        rb_raise(rb_eArgError, "iterations must be a positive integer");
    digest = NIL_P(vdigest) ? EVP_md5() : ossl_evp_md_fetch(vdigest, &md_holder);
    GetCipher(self, ctx);
    EVP_BytesToKey(EVP_CIPHER_CTX_cipher(ctx), digest, salt,
                   (unsigned char *)RSTRING_PTR(vpass), RSTRING_LENINT(vpass), iter, key, iv);
    if (EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, -1) != 1)
        ossl_raise(eCipherError, NULL);
    OPENSSL_cleanse(key, sizeof key);
    OPENSSL_cleanse(iv, sizeof iv);

    rb_ivar_set(self, id_key_set, Qtrue);

    return Qnil;
}

Создаёт и задаёт ключ/IV на основе пароля.

ПРЕДУПРЕЖДЕНИЕ: Этот метод устарел и не должен использоваться. Метод соответствует EVP_BytesToKey — нестандартному расширению OpenSSL для устаревшей функции получения ключа PKCS #5 v1.5. Другие способы получения ключей из паролей см. в разделе OpenSSL::KDF.

Параметры

  • Если указан параметр salt, он должен быть строкой длиной 8 байт.

  • iterations — целое число; значение по умолчанию — 2048.

  • digest — объект Digest; по умолчанию используется ‘MD5’

random_iv → iv Показать исходный код
# File ext/openssl/lib/openssl/cipher.rb, line 55
def random_iv
  str = OpenSSL::Random.random_bytes(self.iv_len)
  self.iv = str
end

Создаёт случайный IV с помощью OpenSSL::Random.random_bytes, задаёт его для шифра и возвращает его.

Перед вызовом этого метода необходимо вызвать encrypt или decrypt.

random_key → key Показать исходный код
# File ext/openssl/lib/openssl/cipher.rb, line 43
def random_key
  str = OpenSSL::Random.random_bytes(self.key_len)
  self.key = str
end

Создаёт случайный ключ с помощью OpenSSL::Random.random_bytes, задаёт его для шифра и возвращает его.

Перед вызовом этого метода необходимо вызвать encrypt или decrypt.

reset → self Показать исходный код
static VALUE
ossl_cipher_reset(VALUE self)
{
    EVP_CIPHER_CTX *ctx;

    GetCipher(self, ctx);
    if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, -1) != 1)
        ossl_raise(eCipherError, NULL);

    return self;
}

Полностью сбрасывает внутреннее состояние Cipher. Это позволяет повторно использовать один и тот же экземпляр Cipher для нескольких операций шифрования или расшифрования.

Внутренне вызывает EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, -1).

update(data [, buffer]) → string or buffer Показать исходный код
static VALUE
ossl_cipher_update(int argc, VALUE *argv, VALUE self)
{
    EVP_CIPHER_CTX *ctx;
    unsigned char *in;
    long in_len, out_len;
    VALUE data, str;

    rb_scan_args(argc, argv, "11", &data, &str);

    if (!RTEST(rb_attr_get(self, id_key_set)))
        ossl_raise(eCipherError, "key not set");

    StringValue(data);
    in = (unsigned char *)RSTRING_PTR(data);
    in_len = RSTRING_LEN(data);
    GetCipher(self, ctx);

    /*
     * As of OpenSSL 3.2, there is no reliable way to determine the required
     * output buffer size for arbitrary cipher modes.
     * https://github.com/openssl/openssl/issues/22628
     *
     * in_len+block_size is usually sufficient, but AES key wrap with padding
     * ciphers require in_len+15 even though they have a block size of 8 bytes.
     *
     * Using EVP_MAX_BLOCK_LENGTH (32) as a safe upper bound for ciphers
     * currently implemented in OpenSSL, but this can change in the future.
     */
    if (in_len > LONG_MAX - EVP_MAX_BLOCK_LENGTH) {
        ossl_raise(rb_eRangeError,
                   "data too big to make output buffer: %ld bytes", in_len);
    }
    out_len = in_len + EVP_MAX_BLOCK_LENGTH;

    if (NIL_P(str)) {
        str = rb_str_new(0, out_len);
    } else {
        StringValue(str);
        if ((long)rb_str_capacity(str) >= out_len)
            rb_str_modify(str);
        else
            rb_str_modify_expand(str, out_len - RSTRING_LEN(str));
    }

    if (!ossl_cipher_update_long(ctx, (unsigned char *)RSTRING_PTR(str), &out_len, in, in_len))
        ossl_raise(eCipherError, NULL);
    assert(out_len <= RSTRING_LEN(str));
    rb_str_set_len(str, out_len);

    return str;
}

Шифрует данные потоком. Для шифрования передавайте последовательные блоки данных методу update. Метод возвращает зашифрованный фрагмент данных. По завершении к результату также необходимо добавить вывод метода Cipher#final.

Если указан параметр buffer, результат шифрования/расшифрования будет записан в него. Размер buffer изменяется автоматически.

ПРИМЕЧАНИЕ: При расшифровании с помощью шифра AEAD целостность выходных данных не проверяется до вызова final.

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

Spec-Zone.ru

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