[ruby-dev:29651] Digest'ifying OpenSSL::Digest
From:
"Akinori MUSHA" <knu@...>
Date:
2006-10-13 12:25:14 UTC
List:
ruby-dev #29651
(主に)ゆぞさん
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 <m.rokos@sh.cvut.cz>
+ * Copyright (C) 2006 Akinori MUSHA <knu@iDaemons.org>
* 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 <m.rokos@sh.cvut.cz>
- 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 <<klass; self; end)
- singleton.class_eval{
- define_method(:digest){|data| Digest.digest(name, data) }
- define_method(:hexdigest){|data| Digest.hexdigest(name, data) }
- }
- const_set(name, klass)
- }
-
- end # Digest
-end # OpenSSL
-
Index: test/openssl/test_digest.rb
===================================================================
RCS file: /src/ruby/test/openssl/test_digest.rb,v
retrieving revision 1.3
diff -u -r1.3 test_digest.rb
--- test/openssl/test_digest.rb 8 May 2006 00:12:00 -0000 1.3
+++ test/openssl/test_digest.rb 13 Oct 2006 12:03:38 -0000
@@ -9,7 +9,7 @@
class OpenSSL::TestDigest < Test::Unit::TestCase
def setup
- @d1 = OpenSSL::Digest::Digest::new("MD5")
+ @d1 = OpenSSL::Digest("MD5").new
@d2 = OpenSSL::Digest::MD5.new
@md = Digest::MD5.new
@data = "DATA"