Ruby 2.4
OpenSSL

OpenSSL::Cipher

类 OpenSSL :: Cipher

家长:对象 (Parent:Object)

提供加密和解密的对称算法。可用的算法取决于安装的特定版本的 OpenSSL。

列出所有支持的算法

支持的算法列表可以通过

puts OpenSSL::Cipher.ciphers

实例化密码

有几种方法可以创建密码实例。通常,密码算法按其名称,密钥长度(以位为单位)和要使用的密码模式进行分类。创建密码的最普通的方式如下

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 = OpenSSL::AES256.new(:CFB) cipher.encrypt key = cipher.random_key # also sets the generated key on the Cipher

如果您绝对需要使用密码作为加密密钥,则应使用由 OpenSSL :: PKCS5.pbkdf2_hmac_sha1 或 OpenSSL :: PKCS5.pbkdf2_hmac 提供的功能来生成密钥,以便使用基于密码的密钥派生函数2(PBKDF2)。

虽然有#pkcs5_keyivgen,但它的使用已被弃用,并且只能用于传统应用程序,因为它不使用较新的 PKCS#5 v2算法。

选择 IV

密码模式 CBC,CFB,OFB 和 CTR 都需要一个“初始化向量”,或简称为 IV。ECB 模式是唯一不需要 IV 的模式,但由于它没有充分隐藏纯文本模式,因此该模式几乎没有合法用例。因此

除非您绝对确定您绝对需要,否则绝对不应该使用 ECB 模式

正因为如此,你最终会得到一种在任何情况下都明确需要 IV 的模式。尽管IV可以看作是公共信息,也就是说一旦生成它就可以在公共场合传播,但它仍然不可预知,以防止某些类型的攻击。因此,理想情况下

对于 密码的每个加密,始终创建一个安全的随机 IV

应该为每个数据加密创建一个新的随机 IV。把 IV 看作一个随机数(使用一次) - 它是公开的但是随机且不可预测的。一个安全的随机IV可以创建如下

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

虽然关键一般也是一个随机值,但作为 IV 是一个不好的选择。攻击者可以利用这种IV的方式有详细的方法。根据一般经验,直接或间接暴露密钥应该不惜一切代价予以避免,例外只能有充分的理由。

调用 #final

ECB(不应该使用)和 CBC 都是基于块的模式。这意味着与其他基于流的模式不同,它们在固定大小的数据块上运行,因此它们需要“完成”步骤来通过适当处理某种形式的填充来产生或正确解密最后一块数据。因此,必须将 #final 的输出添加到加密/解密缓冲区,否则最终会出现解密错误或截断数据。

尽管对于流模式密码来说这并不是真的必要,但仍然建议采用相同的模式来添加 #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 和相关数据(AEAD)的组合,其中附加的相关数据包含在加密过程中,以便在加密结束时计算标签。该标签也将用于解密过程中,并通过验证其有效性,建立给定密文的真实性。

这比未验证的模式更好,因为它允许检测是否有人在密文被加密后有效地改变了密文。这样可以防止恶意修改密文,否则这些密文可能会被利用来修改密文,从而有利于潜在的攻击者。

在有附加信息(如标题或某些元数据)的情况下使用关联数据,这些信息也必须经过身份验证,但不一定需要加密。如果不需要关联数据进行加密和稍后解密,则 OpenSSL 库仍需要设置值 - “”可用于没有可用的情况。

一个使用 GCM(伽罗瓦/计数器模式)的例子。你有16个字节key,12个字节(96位)nonce和相关的数据auth_data。一定不要重复使用keynonce配对。重用随机数会破坏 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

现在你是接收器。你知道key,并已收到nonceauth_dataencryptedtag通过一个不可信赖的网络。请注意,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 → arraystring...()

返回数组中所有可用密码的名称。

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 显示源

该字符串必须包含有效的密码名称,如“AES-128-CBC”或“3DES”。

通过调用:: ciphers 可获得密码名称列表。

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 inititalized!" } 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; }

公共实例方法

auth_data = string → string 显示源

设置密码的附加认证数据。使用 AEAD 密码模式(如 GCM 或 CCM)时,必须设置此字段。如果不使用关联数据,则仍然必须使用值“”调用此方法。该字段的内容应该是非敏感数据,它将被添加到密文中以生成验证标签,验证密文的内容。

AAD 必须在加密或解密之前设置。在加密模式下,必须在调用#encrypt并设置#key =和#iv =之后进行设置。解密时,必须在密钥,iv 之后设置认证数据,特别是在设置了认证标签。即只在调用#decrypt,#key =,#iv =和#auth_tag = first之后才设置它。

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 (!ossl_cipher_update_long(ctx, NULL, &out_len, in, in_len)) ossl_raise(eCipherError, "couldn't set additional authenticated data" return data; }

auth_tag(tag_len = 16) → String Show source

获取通过身份验证加密密码模式生成的身份验证标记(例如,GCM)。该标签可以与密文一起存储,然后设置在解密密码上,以根据改变来认证密文的内容。如果tag_len给出可选的整型参数,则返回的标签将是tag_len字节长。如果省略该参数,则将使用16字节的默认长度或由 auth_tag_len =先前设置的长度。为了最大限度地提高安全性,应尽可能选择最长的。

标签只能在调用 #final 之后检索。

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

auth_tag = string → string显示源文件

设置验证标记以验证密文的内容。在调用#decrypt,#key =和#iv =之后,但在使用#auth_data =分配相关的认证数据之前,必须设置标签,当然,在解密任何密文之前。在所有解密完成后,标签会在#final的调用中自动验证。

对于 OCB 模式,标签长度必须事先提供auth_tag_len =。

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

auth_tag_len = Integer → Intege 显示源

设置需要输入参数的 AEAD 密码要生成或要提供的认证标记的长度。请注意,并非所有 AEAD 密码都支持此方法。

在 OCB 模式下,加密时和解密时必须提供长度,并且必须在指定IV之前提供。

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

authenticated? → true | false显示源

指示此 Cipher 实例是否使用认证加密模式。

static VALUE ossl_cipher_is_authenticated(VALUE self) { EVP_CIPHER_CTX *ctx; GetCipher(self, ctx #if defined(HAVE_AUTHENTICATED_ENCRYPTION) return (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER) ? Qtrue : Qfalse; #else return Qfalse; #endif }

block_size→ integer 显示源

返回此 Cipher 操作的块的大小(以字节为单位)。

static VALUE ossl_cipher_block_size(VALUE self) { EVP_CIPHER_CTX *ctx; GetCipher(self, ctx return INT2NUM(EVP_CIPHER_CTX_block_size(ctx) }

decrypt → self Show source

初始化密码以解密。

请确保在使用以下任何方法之前调用 #encrypt 或 #decrypt:

  • key =,iv =,random_key,random_iv,pkcs5_keyivgen内部调用EVP_CipherInit_ex(ctx,NULL,NULL,NULL,NULL,0).static VALUE ossl_cipher_decrypt(int argc,VALUE * argv,VALUE self){return ossl_cipher_init(argc,argv,自我,0); } encrypt→self显示源初始化密码以进行加密。在使用以下任何方法之前,请务必调用#encrypt或#decrypt:

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

Internally calls EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, 1).

static VALUE ossl_cipher_encrypt(int argc, VALUE *argv, VALUE self) { return ossl_cipher_init(argc, argv, self, 1 }

final → string 显示源

返回密码对象中保留的剩余数据。进一步调用 #update 或 #final 将返回垃圾。在将完整的明文或密文送入 Cipher 实例之后,应始终将此呼叫作为加密或解密操作的最后一次呼叫。

如果使用经过身份验证的密码,则会在标签无法成功验证时引发 CipherError。只有在设置了认证标签并将密文的全部内容传递给密码后才调用此方法。

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

iv = string → string 显示源

设置密码 IV。请注意,因为你永远不应该使用 ECB 模式,所以总是明确要求 IV,并且应该在加密之前设置。IV 本身可以在公共场合安全传输,但防止某些类型的攻击应该是不可预测的。你可以使用 #random_iv 来创建一个安全的随机 IV。

调用 #encrypt 或 #decrypt 后,只调用此方法。

static VALUE ossl_cipher_set_iv(VALUE self, VALUE iv) { EVP_CIPHER_CTX *ctx; int iv_len = 0; StringValue(iv GetCipher(self, ctx #if defined(HAVE_AUTHENTICATED_ENCRYPTION) if (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER) iv_len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx #endif 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_len→integer 显示来源

返回此密码的 IV 的预期长度(以字节为单位)。

static VALUE ossl_cipher_iv_length(VALUE self) { EVP_CIPHER_CTX *ctx; int len = 0; GetCipher(self, ctx #if defined(HAVE_AUTHENTICATED_ENCRYPTION) if (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER) len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx #endif if (!len) len = EVP_CIPHER_CTX_iv_length(ctx return INT2NUM(len }

iv_len = integer → integer 显示源

设置密码的 IV /随机长度。通常,分组密码不允许改变IV的长度,但有些使用 IV 作为'nonce'。您可能需要这样才能与其他应用程序进行互操作。

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

key = string → string 显示源

设置密码密钥。要生成密钥,您应该使用安全的随机字节字符串,或者如果密钥要从密码派生,则应该依赖 OpenSSL :: PKCS5 提供的 PBKDF2 功能。要生成安全的随机密钥,可以使用 #random_key。

调用 #encrypt 或 #decrypt 后,只调用此方法。

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

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

key_len = integer → integer 显示源

设置密码的密钥长度。如果密码是一个固定长度的密码,那么尝试将密钥长度设置为固定值以外的任何值都是错误。

在正常情况下,你不需要调用这个方法(也可能不应该)。

有关更多信息,请参阅 EVP_CIPHER_CTX_set_key_length。

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

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 显示源

启用或禁用填充。默认情况下,加密操作使用标准块填充进行填充,并在解密时检查并删除填充。如果 pad 参数为零,则不执行填充,则加密或解密的数据总量必须是块大小的倍数,否则将发生错误。

有关更多信息,请参阅 EVP_CIPHER_CTX_set_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; }

pkcs5_keyivgen(pass,salt = nil,iterations = 2048,digest =“MD5”)→nil 显示源代码

根据密码生成并设置密钥/ IV。

警告:当使用 RC2,RC4-40 或 DES 与 MD5 或 SHA1时,此方法仅符合 PKCS5 v1.5。使用其他任何东西(如 AES)将使用 OpenSSL特定方法生成密钥/ iv。此方法已弃用,不应再使用。改为使用 OpenSSL :: PKCS5中的 PKCS5 v2密钥生成方法。

参数

  • salt 必须是8字节的字符串(如果提供)。

  • iterations 是一个默认值为2048的整数。

  • digest 是一个 Digest 对象,默认为'MD5'

建议至少进行1000次迭代。

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 digest = NIL_P(vdigest) ? EVP_md5() : GetDigestPtr(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; }

random_iv → iv 显示源

用 OpenSSL :: Random.random_bytes 生成随机 IV 并将其设置为密码,然后返回。

调用此方法之前,您必须调用加密或解密。

# File ext/openssl/lib/openssl/cipher.rb, line 54 def random_iv str = OpenSSL::Random.random_bytes(self.iv_len) self.iv = str end

random_key → key 显示源

用 OpenSSL :: Random.random_bytes 生成一个随机密钥,并将其设置为密码,并将其返回。

调用此方法之前,您必须调用加密或解密。

# File ext/openssl/lib/openssl/cipher.rb, line 42 def random_key str = OpenSSL::Random.random_bytes(self.key_len) self.key = str end

reset → self 显示源

完全重置密码的内部状态。通过使用这个,相同的 Cipher 实例可能会被多次用于加密或解密任务。

内部调用 EVP_CipherInit_ex(ctx,NULL,NULL,NULL,NULL,-1)。

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

update(data , buffer) → string or buffer 显示源

以流媒体的方式加密数据。将连续的数据块传递给该update方法以便对其进行加密。返回加密的数据块。完成后,#final 的输出应另外添加到结果中。

如果buffer给出,加密/解密结果将被写入它。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; }