класс 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 поддерживает режим 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
Методы публичного класса
Исходный код
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;
} Возвращает имена всех доступных шифров в массиве.
Исходный код
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.
Публичные методы экземпляров
Исходный код
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=.
Исходный код
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.
Исходный код
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=.
Исходный код
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.
Исходный код
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.
Исходный код
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.
Исходный код
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». Возможно, это потребуется для межплатформенной совместимости с другими приложениями.
Исходный код
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.
Исходный код
static VALUE
ossl_cipher_key_length(VALUE self)
{
EVP_CIPHER_CTX *ctx;
GetCipher(self, ctx);
return INT2NUM(EVP_CIPHER_CTX_key_length(ctx));
} Возвращает длину ключа в байтах для Cipher.
Исходный код
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 для получения дополнительной информации.
Исходный код
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)));
} Возвращает короткое имя шифра, которое может незначительно отличаться от исходного имени.
Исходный код
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 для получения дополнительной информации.
Исходный код
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 вместо него.
Параметры
-
соль должна быть строкой из 8 байтов, если она предоставлена.
-
итерации — целое число по умолчанию 2048.
-
дайджест — объект
Digest, по умолчанию ‘MD5’
Рекомендуется использовать минимум 1000 итераций.
Исходный код
# 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 перед вызовом этого метода.
Исходный код
# 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 перед вызовом этого метода.
Исходный код
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).
Исходный код
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 следует добавить к результату.
Если задан буфер, результат шифрования/дешифрования будет записан в него. Буфер будет автоматически изменён.
Ruby Core © 1993–2024 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.