diff --git a/ChangeLog b/ChangeLog index b8d76f3173..7826e17764 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +Mon May 8 09:10:31 2006 GOTOU Yuuzou + + * ext/openssl/extconf.rb: add check for OBJ_NAME_do_all_sorted. + + * ext/openssl/ossl_cipher.c (ossl_s_ciphers): new method + OpenSSL::Cipher.ciphers. it returns all the cipher names. + + * ext/openssl/ossl_cipher.c (ossl_cipher_init): refine warning message. + + * ext/openssl/lib/openssl/cipher.rb: reimplement without eval() and + add constants AES128, AES192, AES256. [ruby-dev:28610] + + * ext/openssl/lib/openssl/digest.rb: reimplement without eval(). + + * test/openssl/test_cipher.rb, test_digest: fix about reimplemented + features. + + * sample/openssl/cipher.rb: rewrite all. + Sun May 7 03:09:51 2006 Stephan Maka * lib/resolv.rb (Resolv::DNS::Requester::ConnectedUDP#initialize): diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index 42afc9608c..04c86e7505 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -91,6 +91,7 @@ have_func("X509_CRL_set_version") have_func("X509_CRL_sort") have_func("X509_STORE_get_ex_data") have_func("X509_STORE_set_ex_data") +have_func("OBJ_NAME_do_all_sorted") have_func("OPENSSL_cleanse") if try_compile("#define FOO(a, ...) foo(a, ##__VA_ARGS__)\n int x(){FOO(1);FOO(1,2);FOO(1,2,3);}\n") $defs.push("-DHAVE_VA_ARGS_MACRO") diff --git a/ext/openssl/lib/openssl/cipher.rb b/ext/openssl/lib/openssl/cipher.rb index 9f8776d6fc..049533d06b 100644 --- a/ext/openssl/lib/openssl/cipher.rb +++ b/ext/openssl/lib/openssl/cipher.rb @@ -20,19 +20,25 @@ module OpenSSL module Cipher - %w(AES CAST5 BF DES IDEA RC2 RC4 RC5).each{|cipher| - eval(<<-EOD) - class #{cipher} < Cipher - def initialize(*args) - args = args.join('-') - if args.size == 0 - super(\"#{cipher}\") - else - super(\"#{cipher}-#\{args\}\") - end - end - end - EOD + %w(AES CAST5 BF DES IDEA RC2 RC4 RC5).each{|name| + klass = Class.new(Cipher){ + define_method(:initialize){|*args| + cipher_name = args.inject(name){|n, arg| "#{n}-#{arg}" } + super(cipher_name) + } + } + const_set(name, klass) + } + + %w(128 192 256).each{|keylen| + klass = Class.new(Cipher){ + define_method(:initialize){|mode| + mode ||= "CBC" + cipher_name = "AES-#{keylen}-#{mode}" + super(cipher_name) + } + } + const_set("AES#{keylen}", klass) } class Cipher diff --git a/ext/openssl/lib/openssl/digest.rb b/ext/openssl/lib/openssl/digest.rb index 5745661c3c..b3e4484805 100644 --- a/ext/openssl/lib/openssl/digest.rb +++ b/ext/openssl/lib/openssl/digest.rb @@ -26,22 +26,22 @@ module OpenSSL alg += %w(SHA224 SHA256 SHA384 SHA512) end - alg.each{|digest| - self.module_eval(<<-EOD) - class #{digest} < Digest - def initialize(data=nil) - super(\"#{digest}\", data) + alg.each{|name| + klass = Class.new(Digest){ + define_method(:initialize){|*data| + if data.length > 1 + raise ArgumentError, + "wrong number of arguments (#{data.length} for 1)" end - - def #{digest}::digest(data) - Digest::digest(\"#{digest}\", data) - end - - def #{digest}::hexdigest(data) - Digest::hexdigest(\"#{digest}\", data) - end - end - EOD + super(name, data.first) + } + } + singleton = (class <name)); +} + +static VALUE +ossl_s_ciphers(VALUE self) +{ +#ifdef HAVE_OBJ_NAME_DO_ALL_SORTED + 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; +#else + rb_notimplement(); +#endif +} + static VALUE ossl_cipher_reset(VALUE self) { @@ -143,13 +166,14 @@ ossl_cipher_init(int argc, VALUE *argv, VALUE self, int mode) * We deprecated the arguments for this method, but we decided * keeping this behaviour for backward compatibility. */ + char *cname = rb_class2name(rb_obj_class(self)); + rb_warn("argumtents for %s#encrypt and %s#decrypt were deprecated; " + "use %s#pkcs5_keyivgen to derive key and IV", + cname, cname, cname); StringValue(pass); GetCipher(self, ctx); if (NIL_P(init_v)) memcpy(iv, "OpenSSL for Ruby rulez!", sizeof(iv)); else{ - char *cname = rb_class2name(rb_obj_class(self)); - rb_warning("key derivation by %s#encrypt is deprecated; " - "use %s::pkcs5_keyivgen instead", cname, cname); StringValue(init_v); if (EVP_MAX_IV_LENGTH > RSTRING(init_v)->len) { memset(iv, 0, EVP_MAX_IV_LENGTH); @@ -352,6 +376,7 @@ Init_ossl_cipher(void) rb_define_alloc_func(cCipher, ossl_cipher_alloc); rb_define_copy_func(cCipher, ossl_cipher_copy); + rb_define_module_function(mCipher, "ciphers", ossl_s_ciphers, 0); rb_define_method(cCipher, "initialize", ossl_cipher_initialize, 1); rb_define_method(cCipher, "reset", ossl_cipher_reset, 0); rb_define_method(cCipher, "encrypt", ossl_cipher_encrypt, -1); diff --git a/sample/openssl/cipher.rb b/sample/openssl/cipher.rb index 844b6eea4e..58b10d6046 100644 --- a/sample/openssl/cipher.rb +++ b/sample/openssl/cipher.rb @@ -1,29 +1,54 @@ #!/usr/bin/env ruby require 'openssl' +def crypt_by_password(alg, pass, salt, text) + puts "--Setup--" + puts %(cipher alg: "#{alg}") + puts %(plain text: "#{text}") + puts %(password: "#{pass}") + puts %(salt: "#{salt}") + puts + + puts "--Encrypting--" + enc = OpenSSL::Cipher::Cipher.new(alg) + enc.encrypt + enc.pkcs5_keyivgen(pass, salt) + cipher = enc.update(text) + cipher << enc.final + puts %(encrypted text: #{cipher.inspect}) + puts + + puts "--Decrypting--" + dec = OpenSSL::Cipher::Cipher.new(alg) + dec.decrypt + dec.pkcs5_keyivgen(pass, salt) + plain = dec.update(cipher) + plain << dec.final + puts %(decrypted text: "#{plain}") + puts +end + +def ciphers + ciphers = OpenSSL::Cipher.ciphers.sort + ciphers.each{|i| + if i.upcase != i && ciphers.include?(i.upcase) + ciphers.delete(i) + end + } + return ciphers +end + +puts "Supported ciphers in #{OpenSSL::OPENSSL_VERSION}:" +ciphers.each_with_index{|name, i| + printf("%-15s", name) + puts if (i + 1) % 5 == 0 +} +puts +puts + +alg = ARGV.shift || ciphers.first +pass = "secret password" +salt = "8 octets" # or nil text = "abcdefghijklmnopqrstuvwxyz" -key = "key" -alg = "DES-EDE3-CBC" -#alg = "AES-128-CBC" -puts "--Setup--" -puts %(clear text: "#{text}") -puts %(symmetric key: "#{key}") -puts %(cipher alg: "#{alg}") -puts - -puts "--Encrypting--" -des = OpenSSL::Cipher::Cipher.new(alg) -des.encrypt(key) #, "iv12345678") -cipher = des.update(text) -cipher << des.final -puts %(encrypted text: #{cipher.inspect}) -puts - -puts "--Decrypting--" -des = OpenSSL::Cipher::Cipher.new(alg) -des.decrypt(key) #, "iv12345678") -out = des.update(cipher) -out << des.final -puts %(decrypted text: "#{out}") -puts +crypt_by_password(alg, pass, salt, text) diff --git a/test/openssl/test_cipher.rb b/test/openssl/test_cipher.rb index c84b47b7f4..d671908165 100644 --- a/test/openssl/test_cipher.rb +++ b/test/openssl/test_cipher.rb @@ -11,7 +11,7 @@ class OpenSSL::TestCipher < Test::Unit::TestCase @c1 = OpenSSL::Cipher::Cipher.new("DES-EDE3-CBC") @c2 = OpenSSL::Cipher::DES.new(:EDE3, "CBC") @key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - @iv = @key + @iv = "\0\0\0\0\0\0\0\0" @hexkey = "0000000000000000000000000000000000000000000000" @hexiv = "0000000000000000" @data = "DATA" @@ -22,11 +22,16 @@ class OpenSSL::TestCipher < Test::Unit::TestCase end def test_crypt - s1 = @c1.encrypt(@key, @iv).update(@data) + @c1.final - s2 = @c2.encrypt(@key, @iv).update(@data) + @c2.final + @c1.encrypt.pkcs5_keyivgen(@key, @iv) + @c2.encrypt.pkcs5_keyivgen(@key, @iv) + s1 = @c1.update(@data) + @c1.final + s2 = @c2.update(@data) + @c2.final assert_equal(s1, s2, "encrypt") - assert_equal(@data, @c1.decrypt(@key, @iv).update(s2)+@c1.final, "decrypt") - assert_equal(@data, @c2.decrypt(@key, @iv).update(s1)+@c2.final, "decrypt") + + @c1.decrypt.pkcs5_keyivgen(@key, @iv) + @c2.decrypt.pkcs5_keyivgen(@key, @iv) + assert_equal(@data, @c1.update(s1)+@c1.final, "decrypt") + assert_equal(@data, @c2.update(s2)+@c2.final, "decrypt") end def test_info @@ -62,6 +67,29 @@ class OpenSSL::TestCipher < Test::Unit::TestCase @c1.encrypt assert_raises(ArgumentError){ @c1.update("") } end + + if OpenSSL::OPENSSL_VERSION_NUMBER > 0x00907000 + def test_ciphers + OpenSSL::Cipher.ciphers.each{|name| + assert(OpenSSL::Cipher::Cipher.new(name).is_a?(OpenSSL::Cipher::Cipher)) + } + end + + def test_AES + pt = File.read(__FILE__) + %w(ECB CBC CFB OFB).each{|mode| + c1 = OpenSSL::Cipher::AES256.new(mode) + c1.encrypt + c1.pkcs5_keyivgen("passwd") + ct = c1.update(pt) + c1.final + + c2 = OpenSSL::Cipher::AES256.new(mode) + c2.decrypt + c2.pkcs5_keyivgen("passwd") + assert_equal(pt, c2.update(ct) + c2.final) + } + end + end end end diff --git a/test/openssl/test_digest.rb b/test/openssl/test_digest.rb index da5b6e49c5..8941588b97 100644 --- a/test/openssl/test_digest.rb +++ b/test/openssl/test_digest.rb @@ -62,11 +62,25 @@ class OpenSSL::TestDigest < Test::Unit::TestCase end if OpenSSL::OPENSSL_VERSION_NUMBER > 0x00908000 + def encode16(str) + str.unpack("H*").first + end + def test_098_features - assert_equal("abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5", OpenSSL::Digest::SHA224.hexdigest("a")) - assert_equal("ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb", OpenSSL::Digest::SHA256.hexdigest("a")) - assert_equal("54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31", OpenSSL::Digest::SHA384.hexdigest("a")) - assert_equal("1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75", OpenSSL::Digest::SHA512.hexdigest("a")) + sha224_a = "abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5" + sha256_a = "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb" + sha384_a = "54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31" + sha512_a = "1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75" + + assert_equal(sha224_a, OpenSSL::Digest::SHA224.hexdigest("a")) + assert_equal(sha256_a, OpenSSL::Digest::SHA256.hexdigest("a")) + assert_equal(sha384_a, OpenSSL::Digest::SHA384.hexdigest("a")) + assert_equal(sha512_a, OpenSSL::Digest::SHA512.hexdigest("a")) + + assert_equal(sha224_a, encode16(OpenSSL::Digest::SHA224.digest("a"))) + assert_equal(sha256_a, encode16(OpenSSL::Digest::SHA256.digest("a"))) + assert_equal(sha384_a, encode16(OpenSSL::Digest::SHA384.digest("a"))) + assert_equal(sha512_a, encode16(OpenSSL::Digest::SHA512.digest("a"))) end end end