From: Akinori MUSHA Date: 2006-10-13T21:25:14+09:00 Subject: [ruby-dev:29651] Digest'ifying OpenSSL::Digest (主に)ゆぞさん  OpenSSL::Digest::* を Digest::Base のサブクラスにするべく修正 してみたので、レビューをお願いします。なお openssl/digest.rb の 処理は ossl_digest.c に取り込みました。  メリットとしては、苦もなくAPIが統一される、 #file() などの追加 メソッドが使える、 bubblebabble や hmac (OpenSSL にもあるが)等の アドインの恩恵を受けられる、などです。  ところで、作業の中で OpenSSL::Digest::Digest::new(name) なるインターフェースを発見しました。Digest のだぶりが見苦しい 感じがするので、代わりに OpenSSL::Digest(name) を定義して OpenSSL::Digest("MD5").new と書けるようにしてみましたが、どうでしょうか。  あとは、 size() と name() はインスタンスメソッドとして存在すべき なのかよくわかりませんでしたが、互換性を考慮して残してあります。  以上、よろしくお願いします。 -- / /__ __ Akinori.org / MUSHA.org / ) ) ) ) / FreeBSD.org / Ruby-lang.org Akinori MUSHA aka / (_ / ( (__( @ iDaemons.org / and.or.jp "Different eyes see different things, Different hearts beat on different strings -- But there are times for you and me when all such things agree" M ext/openssl/ossl_digest.c M ext/openssl/lib/openssl.rb R ext/openssl/lib/openssl/digest.rb M test/openssl/test_digest.rb Index: ext/openssl/ossl_digest.c =================================================================== RCS file: /src/ruby/ext/openssl/ossl_digest.c,v retrieving revision 1.7 diff -u -r1.7 ossl_digest.c --- ext/openssl/ossl_digest.c 31 Aug 2006 10:27:46 -0000 1.7 +++ ext/openssl/ossl_digest.c 13 Oct 2006 12:03:37 -0000 @@ -2,6 +2,7 @@ * $Id: ossl_digest.c,v 1.7 2006/08/31 10:27:46 matz Exp $ * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos + * Copyright (C) 2006 Akinori MUSHA * All rights reserved. */ /* @@ -10,17 +11,6 @@ */ #include "ossl.h" -#define GetDigest(obj, ctx) do { \ - Data_Get_Struct(obj, EVP_MD_CTX, ctx); \ - if (!ctx) { \ - ossl_raise(rb_eRuntimeError, "Digest CTX wasn't initialized!"); \ - } \ -} while (0) -#define SafeGetDigest(obj, ctx) do { \ - OSSL_Check_Kind(obj, cDigest); \ - GetDigest(obj, ctx); \ -} while (0) - /* * Classes */ @@ -28,267 +18,272 @@ VALUE cDigest; VALUE eDigestError; -static VALUE ossl_digest_alloc(VALUE klass); +static ID id_evp_md, id_new; + +static const char *digest_names[] = { + "DSS", + "DSS1", + "MD2", + "MD4", + "MD5", + "MDC2", + "RIPEMD160", + "SHA", + "SHA1", +#if OPENSSL_VERSION_NUMBER > 0x00908000 + "SHA224", + "SHA256", + "SHA384", + "SHA512", +#endif +}; /* - * Public + * Digest::Base */ -const EVP_MD * -GetDigestPtr(VALUE obj) + +static EVP_MD * +get_digest_base_metadata(VALUE klass) { - EVP_MD_CTX *ctx; + VALUE obj; + EVP_MD *md; - SafeGetDigest(obj, ctx); + if (rb_ivar_defined(klass, id_evp_md) == Qfalse) { + return NULL; + } - return EVP_MD_CTX_md(ctx); /*== ctx->digest*/ -} + obj = rb_ivar_get(klass, id_evp_md); -VALUE -ossl_digest_new(const EVP_MD *md) -{ - VALUE ret; - EVP_MD_CTX *ctx; + Data_Get_Struct(obj, EVP_MD, md); - ret = ossl_digest_alloc(cDigest); - GetDigest(ret, ctx); - EVP_MD_CTX_init(ctx); - EVP_DigestInit_ex(ctx, md, NULL); - - return ret; + return md; } -/* - * Private - */ static VALUE -ossl_digest_alloc(VALUE klass) +rb_ossl_digest_base_alloc(VALUE klass) { + EVP_MD *md; EVP_MD_CTX *ctx; VALUE obj; + md = get_digest_base_metadata(klass); + + if (md == NULL) { + return Data_Wrap_Struct(klass, 0, free, 0); + } + ctx = EVP_MD_CTX_create(); - if (ctx == NULL) - ossl_raise(rb_eRuntimeError, "EVP_MD_CTX_create() failed"); - EVP_MD_CTX_init(ctx); + obj = Data_Wrap_Struct(klass, 0, EVP_MD_CTX_destroy, ctx); - + return obj; } -VALUE ossl_digest_update(VALUE, VALUE); - static VALUE -ossl_digest_initialize(int argc, VALUE *argv, VALUE self) +rb_ossl_digest_base_copy(VALUE copy, VALUE obj) { - EVP_MD_CTX *ctx; - const EVP_MD *md; - char *name; - VALUE type, data; - - rb_scan_args(argc, argv, "11", &type, &data); - StringValue(type); - if (!NIL_P(data)) StringValue(data); - name = StringValuePtr(type); - - md = EVP_get_digestbyname(name); - if (!md) { - ossl_raise(rb_eRuntimeError, "Unsupported digest algorithm (%s).", name); + EVP_MD *md; + EVP_MD_CTX *ctx1, *ctx2; + + if (copy == obj) return copy; + rb_check_frozen(copy); + md = get_digest_base_metadata(rb_obj_class(copy)); + + if (md == NULL) { + /* initialize_copy() is undefined or something */ + rb_notimplement(); } - GetDigest(self, ctx); - EVP_DigestInit_ex(ctx, md, NULL); - - if (!NIL_P(data)) return ossl_digest_update(self, data); - return self; + + /* get_digest_base_metadata() may return a NULL */ + if (md != get_digest_base_metadata(rb_obj_class(obj))) { + ossl_raise(rb_eTypeError, "wrong argument class"); + } + Data_Get_Struct(obj, EVP_MD_CTX, ctx1); + Data_Get_Struct(copy, EVP_MD_CTX, ctx2); + EVP_MD_CTX_copy(ctx2, ctx1); + + return copy; } static VALUE -ossl_digest_copy(VALUE self, VALUE other) +rb_ossl_digest_base_update(VALUE self, VALUE str) { - EVP_MD_CTX *ctx1, *ctx2; - - rb_check_frozen(self); - if (self == other) return self; + EVP_MD_CTX *ctx; - GetDigest(self, ctx1); - SafeGetDigest(other, ctx2); + Data_Get_Struct(self, EVP_MD_CTX, ctx); - if (!EVP_MD_CTX_copy(ctx1, ctx2)) { - ossl_raise(eDigestError, NULL); + if (ctx == NULL) { + ossl_raise(rb_eRuntimeError, "Unsupported digest algorithm"); } + + StringValue(str); + EVP_DigestUpdate(ctx, RSTRING_PTR(str), RSTRING_LEN(str)); + return self; } static VALUE -ossl_digest_reset(VALUE self) +rb_ossl_digest_base_init(int argc, VALUE *argv, VALUE self) { + EVP_MD *md; EVP_MD_CTX *ctx; + VALUE arg; - GetDigest(self, ctx); - EVP_DigestInit_ex(ctx, EVP_MD_CTX_md(ctx), NULL); + md = get_digest_base_metadata(rb_obj_class(self)); - return self; -} + if (md == NULL) { + ossl_raise(rb_eRuntimeError, "Unsupported digest algorithm"); + } -VALUE -ossl_digest_update(VALUE self, VALUE data) -{ - EVP_MD_CTX *ctx; + Data_Get_Struct(self, EVP_MD_CTX, ctx); - StringValue(data); - GetDigest(self, ctx); - EVP_DigestUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data)); + EVP_DigestInit_ex(ctx, md, NULL); + + rb_scan_args(argc, argv, "01", &arg); + + if (!NIL_P(arg)) rb_ossl_digest_base_update(self, arg); return self; } -static void -digest_final(EVP_MD_CTX *ctx, char **buf, int *buf_len) +static VALUE +rb_ossl_digest_base_digest(VALUE self) { - EVP_MD_CTX final; + EVP_MD *md; + EVP_MD_CTX *ctx1, *ctx2; + size_t ctx_size; + VALUE str; - if (!EVP_MD_CTX_copy(&final, ctx)) { - ossl_raise(eDigestError, NULL); - } - if (!(*buf = OPENSSL_malloc(EVP_MD_CTX_size(&final)))) { - EVP_MD_CTX_cleanup(&final); - ossl_raise(eDigestError, "Cannot allocate mem for digest"); + md = get_digest_base_metadata(rb_obj_class(self)); + + if (md == NULL) { + /* subclasses must define update() */ + rb_notimplement(); } - EVP_DigestFinal_ex(&final, *buf, buf_len); - EVP_MD_CTX_cleanup(&final); -} -static VALUE -ossl_digest_digest(VALUE self) -{ - EVP_MD_CTX *ctx; - char *buf; - int buf_len; - VALUE digest; - - GetDigest(self, ctx); - digest_final(ctx, &buf, &buf_len); - digest = ossl_buf2str(buf, buf_len); - - return digest; + Data_Get_Struct(self, EVP_MD_CTX, ctx1); + + ctx2 = EVP_MD_CTX_create(); + EVP_MD_CTX_copy(ctx2, ctx1); + + str = rb_str_new(0, EVP_MD_CTX_size(ctx2)); + EVP_DigestFinal_ex(ctx2, RSTRING_PTR(str), NULL); + EVP_MD_CTX_destroy(ctx2); + + return str; } static VALUE -ossl_digest_hexdigest(VALUE self) +rb_ossl_digest_base_name(VALUE self) { - EVP_MD_CTX *ctx; - char *buf, *hexbuf; - int buf_len; - VALUE hexdigest; - - GetDigest(self, ctx); - digest_final(ctx, &buf, &buf_len); - if (string2hex(buf, buf_len, &hexbuf, NULL) != 2 * buf_len) { - OPENSSL_free(buf); - ossl_raise(eDigestError, "Memory alloc error"); + EVP_MD *md; + + md = get_digest_base_metadata(rb_obj_class(self)); + + if (md == NULL) { + /* subclasses must define update() */ + rb_notimplement(); } - OPENSSL_free(buf); - hexdigest = ossl_buf2str(hexbuf, 2 * buf_len); - return hexdigest; + return rb_str_new2(EVP_MD_name(md)); } static VALUE -ossl_digest_s_digest(VALUE klass, VALUE str, VALUE data) +rb_ossl_digest_base_size(VALUE self) { - VALUE obj = rb_class_new_instance(1, &str, klass); + EVP_MD *md; + + md = get_digest_base_metadata(rb_obj_class(self)); - ossl_digest_update(obj, data); + if (md == NULL) { + /* subclasses must define update() */ + rb_notimplement(); + } - return ossl_digest_digest(obj); + return INT2NUM(EVP_MD_size(md)); } static VALUE -ossl_digest_s_hexdigest(VALUE klass, VALUE str, VALUE data) +ossl_digest_class_by_name(const char *name) { - VALUE obj = rb_class_new_instance(1, &str, klass); + VALUE klass = rb_const_get(mDigest, rb_intern(name)); - ossl_digest_update(obj, data); + if (klass == Qnil) { + ossl_raise(rb_eRuntimeError, "Unsupported digest algorithm"); + } - return ossl_digest_hexdigest(obj); + return klass; } static VALUE -ossl_digest_equal(VALUE self, VALUE other) +rb_ossl_s_Digest(VALUE klass, VALUE name) { - EVP_MD_CTX *ctx; - VALUE str1, str2; + StringValue(name); - if (rb_obj_is_kind_of(other, cDigest) == Qtrue) { - str2 = ossl_digest_digest(other); - } else { - StringValue(other); - str2 = other; - } - GetDigest(self, ctx); - if (RSTRING_LEN(str2) == EVP_MD_CTX_size(ctx)) { - str1 = ossl_digest_digest(self); - } else { - str1 = ossl_digest_hexdigest(self); - } - if (RSTRING_LEN(str1) == RSTRING_LEN(str2) - && rb_str_cmp(str1, str2) == 0) { - return Qtrue; - } - - return Qfalse; + return ossl_digest_class_by_name(RSTRING_PTR(name)); } -static VALUE -ossl_digest_name(VALUE self) +const EVP_MD * +GetDigestPtr(VALUE obj) { - EVP_MD_CTX *ctx; + EVP_MD *md = get_digest_base_metadata(rb_obj_class(obj)); - GetDigest(self, ctx); + if (md == NULL) { + ossl_raise(rb_eRuntimeError, "Unsupported digest algorithm"); + } - return rb_str_new2(EVP_MD_name(EVP_MD_CTX_md(ctx))); + return md; } -static VALUE -ossl_digest_size(VALUE self) +VALUE +ossl_digest_new(const EVP_MD *md) { - EVP_MD_CTX *ctx; - - GetDigest(self, ctx); + VALUE klass = ossl_digest_class_by_name(EVP_MD_name(md)); - return INT2NUM(EVP_MD_CTX_size(ctx)); + return rb_funcall(klass, id_new, 0); } -/* - * INIT - */ void -Init_ossl_digest() +Init_ossl_digest(void) { + int i; + VALUE klass; + VALUE rb_cDigest_Base; + + id_evp_md = rb_intern("evp_md"); + id_new = rb_intern("new"); + + rb_define_module_function(mOSSL, "Digest", rb_ossl_s_Digest, 1); + mDigest = rb_define_module_under(mOSSL, "Digest"); - eDigestError = rb_define_class_under(mDigest, "DigestError", eOSSLError); - - cDigest = rb_define_class_under(mDigest, "Digest", rb_cObject); - - rb_define_alloc_func(cDigest, ossl_digest_alloc); - rb_define_singleton_method(cDigest, "digest", ossl_digest_s_digest, 2); - rb_define_singleton_method(cDigest, "hexdigest", ossl_digest_s_hexdigest, 2); - - rb_define_method(cDigest, "initialize", ossl_digest_initialize, -1); - rb_define_method(cDigest, "reset", ossl_digest_reset, 0); - - rb_define_copy_func(cDigest, ossl_digest_copy); - - rb_define_method(cDigest, "digest", ossl_digest_digest, 0); - rb_define_method(cDigest, "hexdigest", ossl_digest_hexdigest, 0); - rb_define_alias(cDigest, "inspect", "hexdigest"); - rb_define_alias(cDigest, "to_s", "hexdigest"); - - rb_define_method(cDigest, "update", ossl_digest_update, 1); - rb_define_alias(cDigest, "<<", "update"); - - rb_define_method(cDigest, "==", ossl_digest_equal, 1); - - rb_define_method(cDigest, "name", ossl_digest_name, 0); - rb_define_method(cDigest, "size", ossl_digest_size, 0); + + rb_require("digest"); + rb_cDigest_Base = rb_path2class("Digest::Base"); + + for (i = 0; i < sizeof(digest_names); i++) { + const char *name = digest_names[i]; + const EVP_MD *md = EVP_get_digestbyname(name); + + if (md == NULL) + continue; + + klass = rb_define_class_under(mDigest, name, rb_cDigest_Base); + + rb_ivar_set(klass, id_evp_md, Data_Wrap_Struct(rb_cObject, 0, 0, (void *)md)); + + rb_define_const(klass, "DIGEST_LENGTH", INT2NUM(EVP_MD_size(md))); + rb_define_const(klass, "BLOCK_LENGTH", INT2NUM(EVP_MD_block_size(md))); + + rb_define_alloc_func(klass, rb_ossl_digest_base_alloc); + + rb_define_method(klass, "initialize", rb_ossl_digest_base_init, -1); + rb_define_method(klass, "initialize_copy", rb_ossl_digest_base_copy, 1); + rb_define_method(klass, "update", rb_ossl_digest_base_update, 1); + rb_define_method(klass, "digest", rb_ossl_digest_base_digest, 0); + + rb_define_method(klass, "name", rb_ossl_digest_base_name, 0); + rb_define_method(klass, "size", rb_ossl_digest_base_size, 0); + rb_define_method(klass, "length", rb_ossl_digest_base_size, 0); + } } Index: ext/openssl/lib/openssl.rb =================================================================== RCS file: /src/ruby/ext/openssl/lib/openssl.rb,v retrieving revision 1.1 diff -u -r1.1 openssl.rb --- ext/openssl/lib/openssl.rb 23 Jul 2003 16:11:29 -0000 1.1 +++ ext/openssl/lib/openssl.rb 13 Oct 2006 12:03:37 -0000 @@ -18,7 +18,6 @@ require 'openssl/bn' require 'openssl/cipher' -require 'openssl/digest' require 'openssl/ssl' require 'openssl/x509' Index: ext/openssl/lib/openssl/digest.rb =================================================================== RCS file: ext/openssl/lib/openssl/digest.rb diff -N ext/openssl/lib/openssl/digest.rb --- ext/openssl/lib/openssl/digest.rb 8 May 2006 00:11:59 -0000 1.3 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,49 +0,0 @@ -=begin -= $RCSfile: digest.rb,v $ -- Ruby-space predefined Digest subclasses - -= Info - 'OpenSSL for Ruby 2' project - Copyright (C) 2002 Michal Rokos - All rights reserved. - -= Licence - This program is licenced under the same licence as Ruby. - (See the file 'LICENCE'.) - -= Version - $Id: digest.rb,v 1.3 2006/05/08 00:11:59 gotoyuzo Exp $ -=end - -## -# Should we care what if somebody require this file directly? -#require 'openssl' - -module OpenSSL - module Digest - - alg = %w(DSS DSS1 MD2 MD4 MD5 MDC2 RIPEMD160 SHA SHA1) - if OPENSSL_VERSION_NUMBER > 0x00908000 - alg += %w(SHA224 SHA256 SHA384 SHA512) - end - - 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 - super(name, data.first) - } - } - singleton = (class <