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

class 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.

Выбор IV

Режимы шифрования 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

Аутентифицированное шифрование с ассоциированными данными (AEAD)

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

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

Ассоциированные данные используются в случаях, когда есть дополнительная информация, например заголовки или метаданные, которую также необходимо аутентифицировать, но не обязательно шифровать. Если для шифрования и последующего расшифрования ассоциированные данные не нужны, библиотека OpenSSL всё равно требует задать значение — если данных нет, можно использовать «».

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

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 # produces 16 bytes tag by default

Теперь вы — получатель. Вам известен key, а 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
decipher.auth_data = auth_data

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

puts data == decrypted #=> true

Открытые методы класса

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;
    char *name;

    name = StringValueCStr(str);
    GetCipherInit(self, ctx);
    if (ctx) {
        ossl_raise(rb_eRuntimeError, "Cipher already initialized!");
    }
    AllocCipher(self, ctx);
    if (!(cipher = EVP_get_cipherbyname(name))) {
        ossl_raise(rb_eRuntimeError, "unsupported cipher algorithm (%"PRIsVALUE")", str);
    }
    if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, -1) != 1)
        ossl_raise(eCipherError, NULL);

    return self;
}

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

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

Открытые методы экземпляра

auth_data = string → 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;
}

Задаёт дополнительные аутентифицируемые данные шифра. Это поле необходимо задать при использовании режимов шифрования AEAD, например GCM или CCM. Даже если ассоциированные данные использовать не нужно, этот метод всё равно необходимо вызвать, передав значение «». В этом поле следует указывать нечувствительные данные, которые будут добавлены к шифротексту для формирования тега аутентификации, подтверждающего целостность содержимого шифротекста.

AAD необходимо задать до начала шифрования или расшифрования. В режиме шифрования это нужно сделать после вызова Cipher#encrypt и установки Cipher#key= и Cipher#iv=. При расшифровании аутентифицируемые данные необходимо задать после ключа, IV и, в особенности, после установки тега аутентификации. То есть сначала вызовите Cipher#decrypt, Cipher#key=, Cipher#iv= и Cipher#auth_tag=.

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;
}

Возвращает тег аутентификации, сформированный режимами Cipher с аутентифицированным шифрованием (например, GCM). Этот тег можно сохранить вместе с шифротекстом, а затем задать для шифра расшифрования, чтобы проверить содержимое шифротекста на предмет изменений. Если указан необязательный целочисленный параметр tag_len, возвращаемый тег будет иметь длину tag_len байт. Если параметр не указан, используется длина по умолчанию — 16 байт — или длина, ранее заданная с помощью auth_tag_len=. Для максимальной безопасности следует выбирать максимально возможную длину.

Тег можно получить только после вызова Cipher#final.

auth_tag = string → 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;
}

Задаёт тег аутентификации для проверки целостности шифротекста. Этот метод можно вызвать только при поддержке AE шифром. Тег необходимо задать после вызова Cipher#decrypt, Cipher#key= и Cipher#iv=, но до вызова Cipher#final. После завершения расшифрования тег проверяется автоматически при вызове Cipher#final.

Для режима OCB длину тега необходимо предварительно задать с помощью auth_tag_len=.

auth_tag_len = Integer → 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;
}

Задаёт длину тега аутентификации, который будет сформирован или передан в качестве входного параметра для шифров AEAD, требующих этого. Обратите внимание, что этот метод поддерживается не всеми шифрами AEAD.

В режиме OCB длину необходимо задать как при шифровании, так и при расшифровании, причём до указания IV.

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 режим аутентифицированного шифрования.

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 → 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;
}

Задаёт длину сообщения с открытым текстом/шифротекстом, которое будет обработано в режиме CCM. Убедитесь, что вызвали этот метод после установки key= и iv=, но до вызова auth_data=.

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

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

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

Перед использованием любого из следующих методов обязательно вызовите Cipher#encrypt или Cipher#decrypt:

  • key=, iv=, random_key, random_iv, pkcs5_keyivgen

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

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

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

Перед использованием любого из следующих методов обязательно вызовите Cipher#encrypt или Cipher#decrypt:

  • key=, iv=, random_key, random_iv, pkcs5_keyivgen

Внутренне вызывает 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))
        ossl_raise(eCipherError, NULL);
    assert(out_len <= RSTRING_LEN(str));
    rb_str_set_len(str, out_len);

    return str;
}

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

Если использовался аутентифицированный шифр, возникает исключение CipherError, если тег не удалось проверить. Вызывайте этот метод только после установки тега аутентификации и передачи шифру всего содержимого шифротекста.

initialize_copy (p1) Показать исходный код
static VALUE
ossl_cipher_copy(VALUE self, VALUE other)
{
    EVP_CIPHER_CTX *ctx1, *ctx2;

    rb_check_frozen(self);
    if (self == other) return self;

    GetCipherInit(self, ctx1);
    if (!ctx1) {
        AllocCipher(self, ctx1);
    }
    GetCipher(other, ctx2);
    if (EVP_CIPHER_CTX_copy(ctx1, ctx2) != 1)
        ossl_raise(eCipherError, NULL);

    return self;
}
iv = string → 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 можно безопасно передавать по открытым каналам, однако он должен быть непредсказуемым, чтобы предотвратить некоторые виды атак. Для создания надёжного случайного IV можно использовать Cipher#random_iv.

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

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 → 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. Обычно блочные шифры не позволяют изменять длину IV, но некоторые используют IV в качестве nonce. Это может потребоваться для обеспечения совместимости с другими приложениями.

key = string → 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.

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 → 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 = integer → integer Показать исходный код
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;
    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_get_digestbyname(vdigest);
    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 на основе пароля.

ПРЕДУПРЕЖДЕНИЕ: Этот метод соответствует PKCS5 версии 1.5 только при использовании RC2, RC4-40 или DES с MD5 или SHA1. При использовании любых других алгоритмов (например, AES) ключ/IV будет создан с помощью специфичного для OpenSSL метода. Этот метод устарел и больше не должен использоваться. Вместо него используйте метод создания ключа PKCS5 версии 2 из OpenSSL::PKCS5.

Параметры

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

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

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

Рекомендуется использовать не менее 1000 итераций.

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 будет изменён автоматически.

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