From 215b54806b035509272779fefd57b432a1a0f9e5 Mon Sep 17 00:00:00 2001 From: emboss Date: Thu, 20 Dec 2012 06:03:03 +0000 Subject: * ext/openssl/ossl_cipher.c: add support for Authenticated Encryption with Associated Data (AEAD) for OpenSSL versions that support the GCM encryption mode. It's the only mode supported for now by OpenSSL itself. Add Cipher#authenticated? to detect whether a chosen mode does support Authenticated Encryption. * test/openssl/test_cipher.rb: add tests for Authenticated Encryption. [Feature #6980] [ruby-core:47426] Thank you, Stephen Touset for providing a patch! git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38488 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- test/openssl/test_cipher.rb | 141 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 137 insertions(+), 4 deletions(-) (limited to 'test/openssl/test_cipher.rb') diff --git a/test/openssl/test_cipher.rb b/test/openssl/test_cipher.rb index ab131b8b1f..64c89be8b1 100644 --- a/test/openssl/test_cipher.rb +++ b/test/openssl/test_cipher.rb @@ -3,6 +3,25 @@ require_relative 'utils' if defined?(OpenSSL) class OpenSSL::TestCipher < Test::Unit::TestCase + + class << self + + def has_cipher?(name) + ciphers = OpenSSL::Cipher.ciphers + # redefine method so we can use the cached ciphers value from the closure + # and need not recompute the list each time + define_singleton_method :has_cipher? do |name| + ciphers.include?(name) + end + has_cipher?(name) + end + + def has_ciphers?(list) + list.all? { |name| has_cipher?(name) } + end + + end + def setup @c1 = OpenSSL::Cipher::Cipher.new("DES-EDE3-CBC") @c2 = OpenSSL::Cipher::DES.new(:EDE3, "CBC") @@ -78,11 +97,8 @@ class OpenSSL::TestCipher < Test::Unit::TestCase cipher.decrypt cipher.pkcs5_keyivgen('password') assert_equal('hello,world', cipher.update(c) + cipher.final) - rescue RuntimeError => e - # CTR is from OpenSSL 1.0.1, and for an environment that disables CTR; No idea it exists. - assert_match(/unsupported cipher algorithm/, e.message) end - end + end if has_cipher?('aes-128-ctr') if OpenSSL::OPENSSL_VERSION_NUMBER > 0x00907000 def test_ciphers @@ -116,6 +132,123 @@ class OpenSSL::TestCipher < Test::Unit::TestCase end end end + + if has_ciphers?(['aes-128-gcm', 'aes-192-gcm', 'aes-128-gcm']) + + def test_authenticated + cipher = OpenSSL::Cipher.new('aes-128-gcm') + assert(cipher.authenticated?) + cipher = OpenSSL::Cipher.new('aes-128-cbc') + refute(cipher.authenticated?) + end + + def test_aes_gcm + ['aes-128-gcm', 'aes-192-gcm', 'aes-128-gcm'].each do |algo| + pt = "You should all use Authenticated Encryption!" + cipher, key, iv = new_encryptor(algo) + + cipher.auth_data = "aad" + ct = cipher.update(pt) + cipher.final + tag = cipher.auth_tag + assert_equal(16, tag.size) + + decipher = new_decryptor(algo, key, iv) + decipher.auth_tag = tag + decipher.auth_data = "aad" + + assert_equal(pt, decipher.update(ct) + decipher.final) + end + end + + def test_aes_gcm_short_tag + ['aes-128-gcm', 'aes-192-gcm', 'aes-128-gcm'].each do |algo| + pt = "You should all use Authenticated Encryption!" + cipher, key, iv = new_encryptor(algo) + + cipher.auth_data = "aad" + ct = cipher.update(pt) + cipher.final + tag = cipher.auth_tag(8) + assert_equal(8, tag.size) + + decipher = new_decryptor(algo, key, iv) + decipher.auth_tag = tag + decipher.auth_data = "aad" + + assert_equal(pt, decipher.update(ct) + decipher.final) + end + end + + def test_aes_gcm_wrong_tag + pt = "You should all use Authenticated Encryption!" + cipher, key, iv = new_encryptor('aes-128-gcm') + + cipher.auth_data = "aad" + ct = cipher.update(pt) + cipher.final + tag = cipher.auth_tag + + decipher = new_decryptor('aes-128-gcm', key, iv) + decipher.auth_tag = tag[0..-2] << tag[-1].succ + decipher.auth_data = "aad" + + assert_raise OpenSSL::Cipher::CipherError do + decipher.update(ct) + decipher.final + end + end + + def test_aes_gcm_wrong_auth_data + pt = "You should all use Authenticated Encryption!" + cipher, key, iv = new_encryptor('aes-128-gcm') + + cipher.auth_data = "aad" + ct = cipher.update(pt) + cipher.final + tag = cipher.auth_tag + + decipher = new_decryptor('aes-128-gcm', key, iv) + decipher.auth_tag = tag + decipher.auth_data = "daa" + + assert_raise OpenSSL::Cipher::CipherError do + decipher.update(ct) + decipher.final + end + end + + def test_aes_gcm_wrong_ciphertext + pt = "You should all use Authenticated Encryption!" + cipher, key, iv = new_encryptor('aes-128-gcm') + + cipher.auth_data = "aad" + ct = cipher.update(pt) + cipher.final + tag = cipher.auth_tag + + decipher = new_decryptor('aes-128-gcm', key, iv) + decipher.auth_tag = tag + decipher.auth_data = "aad" + + assert_raise OpenSSL::Cipher::CipherError do + decipher.update(ct[0..-2] << ct[-1].succ) + decipher.final + end + end + + end + + private + + def new_encryptor(algo) + cipher = OpenSSL::Cipher.new(algo) + cipher.encrypt + key = cipher.random_key + iv = cipher.random_iv + [cipher, key, iv] + end + + def new_decryptor(algo, key, iv) + OpenSSL::Cipher.new(algo).tap do |cipher| + cipher.decrypt + cipher.key = key + cipher.iv = iv + end + end + end end -- cgit v1.2.3