From cc7413f602d5e4f00f5d81336be879c4e199f1e6 Mon Sep 17 00:00:00 2001 From: tomykaira Date: Thu, 23 Oct 2014 11:45:34 +0900 Subject: [PATCH] OpenSSL::Cipher accepts IV --- ext/openssl/ossl_cipher.c | 31 ++++++++++++++++++++++++++++++- test/openssl/test_cipher.rb | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c index 0efadd1..da6aad4 100644 --- a/ext/openssl/ossl_cipher.c +++ b/ext/openssl/ossl_cipher.c @@ -694,6 +694,35 @@ ossl_cipher_set_key_length(VALUE self, VALUE key_length) return key_length; } +/* + * call-seq: + * cipher.iv_len = integer -> integer + * + * Sets the iv length of the cipher. This method is available only for GCM or + * CCM (which is not supported yet) ciphers. + * + * See EVP_CIPHER_CTX_ctrl and EVP_CTRL_GCM_SET_IVLEN for further information. + */ +static VALUE +ossl_cipher_set_iv_length(VALUE self, VALUE iv_length) +{ + EVP_CIPHER_CTX *ctx; + int nid; + int len = NUM2INT(iv_length); + + GetCipher(self, ctx); + nid = EVP_CIPHER_CTX_nid(ctx); + + if (ossl_is_gcm(nid)) { + if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, len, NULL)) + ossl_raise(eCipherError, "unable to set GCM IV length"); + } else { + ossl_raise(eCipherError, "IV length is not variable for this cipher"); + } + + return iv_length; +} + #if defined(HAVE_EVP_CIPHER_CTX_SET_PADDING) /* * call-seq: @@ -981,7 +1010,7 @@ Init_ossl_cipher(void) rb_define_method(cCipher, "key_len", ossl_cipher_key_length, 0); rb_define_method(cCipher, "iv=", ossl_cipher_set_iv, 1); rb_define_method(cCipher, "iv_len", ossl_cipher_iv_length, 0); + rb_define_method(cCipher, "iv_len=", ossl_cipher_set_iv_length, 1); rb_define_method(cCipher, "block_size", ossl_cipher_block_size, 0); rb_define_method(cCipher, "padding=", ossl_cipher_set_padding, 1); } - diff --git a/test/openssl/test_cipher.rb b/test/openssl/test_cipher.rb index 156fa2a..52c9554 100644 --- a/test/openssl/test_cipher.rb +++ b/test/openssl/test_cipher.rb @@ -230,6 +230,38 @@ class OpenSSL::TestCipher < Test::Unit::TestCase end end + def test_aes_gcm_custom_iv + pt = "You should all use Authenticated Encryption!" + cipher, key, iv = new_encryptor('aes-128-gcm') + cipher.iv = "\0" * 16 + + cipher2 = OpenSSL::Cipher.new('aes-128-gcm') + cipher2.encrypt + cipher2.key = key + cipher2.iv_len = 16 + cipher2.iv = "\0" * 16 + + ct12 = cipher.update(pt) + cipher.final + ct16 = cipher2.update(pt) + cipher2.final + assert ct12 != ct16 + + decipher = OpenSSL::Cipher.new('aes-128-gcm') + decipher.auth_tag = cipher2.auth_tag + decipher.decrypt + decipher.key = key + decipher.iv_len = 16 + decipher.iv = "\0" * 16 + assert_equal pt, decipher.update(ct16) + decipher.final + end + + def test_aes_gcm_set_wrong_iv_len + cipher, key, iv = new_encryptor('aes-128-gcm') + cipher = OpenSSL::Cipher.new('aes-128-gcm') + assert_raise OpenSSL::Cipher::CipherError do + cipher.iv_len = -1 + end + end + end private -- 2.1.1