Spec-Zone.ru › Ruby 3

класс OpenSSL::Cipher

Родитель:
Объект

Предоставляет симметричные алгоритмы для шифрования и дешифрования. Доступные алгоритмы зависят от конкретной версии установленной библиотеки 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

Если вам абсолютно необходимо использовать пароли в качестве ключей шифрования, вы должны использовать функцию вывода ключа на основе пароля PBKDF2 (Password-Based Key Derivation Function 2), генерируя ключ с помощью функциональности, предоставляемой 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 поддерживает аутентифицированное шифрование только в сочетании со связанными данными (AEAD), где дополнительные связанные данные включаются в процесс шифрования для вычисления тега в конце шифрования. Этот тег также будет использоваться в процессе дешифрования и, проверив его корректность, устанавливается подлинность данного шифрованного текста.

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

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

Пример с использованием GCM (Galois/Counter Mode). У вас есть 16 байт ключа, 12 байт (96 бит) nonce и связанные данные auth_data. Убедитесь, что вы не используете пару ключ и 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

Теперь вы получатель. Вам известен ключ, и вы получили 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,
                    (void(*)(const OBJ_NAME*,void*))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.

END_OF_DOCUMENT_MARKER

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

auth_data = строка → строка Показать исходный код
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) → Строка Показать исходный код
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 = строка → строка Показать исходный код
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 = Целое число → Целое число Показать исходный код
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 → целое число Показать исходный код
static VALUE
ossl_cipher_block_size(VALUE self)
{
    EVP_CIPHER_CTX *ctx;

    GetCipher(self, ctx);

    return INT2NUM(EVP_CIPHER_CTX_block_size(ctx));
}

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

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 → строка Показать исходный код
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 = строка → строка Показать исходный код
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 можно безопасно передавать публично, но он должен быть непредсказуемым, чтобы предотвратить определенные виды атак. Вы можете использовать Cipher#random_iv для создания безопасного случайного IV.

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

iv_len → целое число Показать исходный код
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 = целое число → целое число Показать исходный код
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/номера Cipher. Обычно шифры с блоками не позволяют изменять длину IV, но некоторые используют IV для 'номера'. Возможно, вам потребуется это для межплатформенной совместимости с другими приложениями.

key = строка → строка Показать исходный код
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 → целое Показать исходный код
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 = целое → целое Показать исходный код
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 → строка Показать исходный код
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 = целое → целое Показать исходный код
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 v1.5 только при использовании RC2, RC4-40 или DES с MD5 или SHA1. Использование чего-либо ещё (например, AES) сгенерирует ключ/iv с использованием метода, специфичного для OpenSSL. Этот метод устарел и больше не должен использоваться. Используйте метод генерации ключа PKCS5 v2 из 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]) → строка или буфер Показать исходный код
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);
    if ((in_len = RSTRING_LEN(data)) == 0)
        ossl_raise(rb_eArgError, "data must not be empty");
    GetCipher(self, ctx);
    out_len = in_len+EVP_CIPHER_CTX_block_size(ctx);
    if (out_len <= 0) {
        ossl_raise(rb_eRangeError,
                   "data too big to make output buffer: %ld bytes", in_len);
    }

    if (NIL_P(str)) {
        str = rb_str_new(0, out_len);
    } else {
        StringValue(str);
        rb_str_resize(str, out_len);
    }

    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–2020 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

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