Spec-Zone.ru › Ruby 2.6

класс 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, который носит название шифра, например, чтобы получить экземпляр AES, вы также можете использовать

# these are equivalent
cipher = OpenSSL::Cipher::AES.new(128, :CBC)
cipher = OpenSSL::Cipher::AES.new(128, 'CBC')
cipher = OpenSSL::Cipher::AES.new('128-CBC')

Наконец, из-за широкого распространения, для разных размеров ключей AES определены дополнительные классы

cipher = OpenSSL::Cipher::AES128.new(:CBC)
cipher = OpenSSL::Cipher::AES192.new(:CBC)
cipher = OpenSSL::Cipher::AES256.new(:CBC)

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

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

cipher.encrypt

или

cipher.decrypt

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

Выбор ключа

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

cipher = OpenSSL::AES256.new(:CFB)
cipher.encrypt
key = cipher.random_key # also sets the generated key on the Cipher

Если вам абсолютно необходимо использовать пароли в качестве ключей шифрования, следует использовать функцию вывода ключа из пароля (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::AES.new(128, :CBC)
cipher.encrypt
key = cipher.random_key
iv = cipher.random_iv

encrypted = cipher.update(data) + cipher.final
...
decipher = OpenSSL::Cipher::AES.new(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-байтовый ключ, 12-байтовый (96-битный) nonce и связанные данные auth_data. Не используйте одну и ту же пару ключ и nonce повторно. Повторное использование nonce разрушает гарантии безопасности режима GCM.

cipher = OpenSSL::Cipher::AES.new(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::AES.new(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 → массив[строка...] Показать исходный код
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(строка) → шифр Показать исходный код
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-128-CBC» или «3DES».

Список имён шифров доступен, вызвав 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.

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 может безопасно передаваться публично, но он должен быть непредсказуемым, чтобы предотвратить определенные виды атак. Вы можете использовать Cipher#random_iv для создания безопасного случайного 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 → целое Показать исходный код
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–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

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