summaryrefslogtreecommitdiff
path: root/ext/openssl/ossl_cipher.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/openssl/ossl_cipher.c')
-rw-r--r--ext/openssl/ossl_cipher.c77
1 files changed, 57 insertions, 20 deletions
diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c
index 0b78f40b72..cc0114f579 100644
--- a/ext/openssl/ossl_cipher.c
+++ b/ext/openssl/ossl_cipher.c
@@ -5,7 +5,7 @@
*/
/*
* This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
*/
#include "ossl.h"
@@ -42,7 +42,7 @@ static const rb_data_type_t ossl_cipher_type = {
{
0, ossl_cipher_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
@@ -104,7 +104,7 @@ ossl_cipher_alloc(VALUE klass)
* call-seq:
* Cipher.new(string) -> cipher
*
- * The string must contain a valid cipher name like "AES-256-CBC".
+ * The string must contain a valid cipher name like "aes-256-cbc".
*
* A list of cipher names is available by calling OpenSSL::Cipher.ciphers.
*/
@@ -149,11 +149,11 @@ ossl_cipher_copy(VALUE self, VALUE other)
return self;
}
-static void*
-add_cipher_name_to_ary(const OBJ_NAME *name, VALUE ary)
+static void
+add_cipher_name_to_ary(const OBJ_NAME *name, void *arg)
{
+ VALUE ary = (VALUE)arg;
rb_ary_push(ary, rb_str_new2(name->name));
- return NULL;
}
/*
@@ -169,7 +169,7 @@ ossl_s_ciphers(VALUE self)
ary = rb_ary_new();
OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH,
- (void(*)(const OBJ_NAME*,void*))add_cipher_name_to_ary,
+ add_cipher_name_to_ary,
(void*)ary);
return ary;
@@ -384,14 +384,25 @@ ossl_cipher_update(int argc, VALUE *argv, VALUE self)
StringValue(data);
in = (unsigned char *)RSTRING_PTR(data);
- if ((in_len = RSTRING_LEN(data)) == 0)
- ossl_raise(rb_eArgError, "data must not be empty");
+ in_len = RSTRING_LEN(data);
GetCipher(self, ctx);
- out_len = in_len+EVP_CIPHER_CTX_block_size(ctx);
- if (out_len <= 0) {
+
+ /*
+ * 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);
@@ -402,7 +413,7 @@ ossl_cipher_update(int argc, VALUE *argv, VALUE self)
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));
+ assert(out_len <= RSTRING_LEN(str));
rb_str_set_len(str, out_len);
return str;
@@ -443,8 +454,8 @@ ossl_cipher_final(VALUE self)
* call-seq:
* cipher.name -> string
*
- * Returns the name of the cipher which may differ slightly from the original
- * name provided.
+ * Returns the short name of the cipher which may differ slightly from the
+ * original name provided.
*/
static VALUE
ossl_cipher_name(VALUE self)
@@ -814,6 +825,31 @@ ossl_cipher_block_size(VALUE self)
}
/*
+ * call-seq:
+ * cipher.ccm_data_len = integer -> integer
+ *
+ * Sets the length of the plaintext / ciphertext message that will be
+ * processed in CCM mode. Make sure to call this method after #key= and
+ * #iv= have been set, and before #auth_data=.
+ *
+ * Only call this method after calling Cipher#encrypt or Cipher#decrypt.
+ */
+static VALUE
+ossl_cipher_set_ccm_data_len(VALUE self, VALUE data_len)
+{
+ int in_len, out_len;
+ EVP_CIPHER_CTX *ctx;
+
+ in_len = NUM2INT(data_len);
+
+ GetCipher(self, ctx);
+ if (EVP_CipherUpdate(ctx, NULL, &out_len, NULL, in_len) != 1)
+ ossl_raise(eCipherError, NULL);
+
+ return data_len;
+}
+
+/*
* INIT
*/
void
@@ -849,7 +885,7 @@ Init_ossl_cipher(void)
* individual components name, key length and mode. Either all uppercase
* or all lowercase strings may be used, for example:
*
- * cipher = OpenSSL::Cipher.new('AES-128-CBC')
+ * cipher = OpenSSL::Cipher.new('aes-128-cbc')
*
* === Choosing either encryption or decryption mode
*
@@ -879,7 +915,7 @@ Init_ossl_cipher(void)
* without processing the password further. A simple and secure way to
* create a key for a particular Cipher is
*
- * cipher = OpenSSL::Cipher.new('AES-256-CFB')
+ * cipher = OpenSSL::Cipher.new('aes-256-cfb')
* cipher.encrypt
* key = cipher.random_key # also sets the generated key on the Cipher
*
@@ -947,14 +983,14 @@ Init_ossl_cipher(void)
*
* data = "Very, very confidential data"
*
- * cipher = OpenSSL::Cipher.new('AES-128-CBC')
+ * 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 = OpenSSL::Cipher.new('aes-128-cbc')
* decipher.decrypt
* decipher.key = key
* decipher.iv = iv
@@ -990,7 +1026,7 @@ Init_ossl_cipher(void)
* not to reuse the _key_ and _nonce_ pair. Reusing an nonce ruins the
* security guarantees of GCM mode.
*
- * cipher = OpenSSL::Cipher.new('AES-128-GCM').encrypt
+ * cipher = OpenSSL::Cipher.new('aes-128-gcm').encrypt
* cipher.key = key
* cipher.iv = nonce
* cipher.auth_data = auth_data
@@ -1006,7 +1042,7 @@ Init_ossl_cipher(void)
* ciphertext with a probability of 1/256.
*
* raise "tag is truncated!" unless tag.bytesize == 16
- * decipher = OpenSSL::Cipher.new('AES-128-GCM').decrypt
+ * decipher = OpenSSL::Cipher.new('aes-128-gcm').decrypt
* decipher.key = key
* decipher.iv = nonce
* decipher.auth_tag = tag
@@ -1043,6 +1079,7 @@ Init_ossl_cipher(void)
rb_define_method(cCipher, "iv_len", ossl_cipher_iv_length, 0);
rb_define_method(cCipher, "block_size", ossl_cipher_block_size, 0);
rb_define_method(cCipher, "padding=", ossl_cipher_set_padding, 1);
+ rb_define_method(cCipher, "ccm_data_len=", ossl_cipher_set_ccm_data_len, 1);
id_auth_tag_len = rb_intern_const("auth_tag_len");
id_key_set = rb_intern_const("key_set");