From b2c7fe1bbffe9e5fe741b7dd3725017b55250c1e Mon Sep 17 00:00:00 2001 From: knu Date: Fri, 20 Oct 2006 12:48:35 +0000 Subject: [PATCH] * ext/digest: Prefix C constants with RUBY_ and C type names with rb_ to avoid name clash in writing extensions. * ext/digest: Introduce Digest::Class and Digest::Instance for ease of implementing subclasses and add-ons, inspried by gotoyuzo. * ext/digest: The Digest::Instance module now requires and assumes that any instance be resettable and clonable, and add some convenient instance methods such as "new()", for creating a new copy, parameter taking "digest()" and "hexdigest()", for instant calculation. These methods make digest instances work just like digest classes. * ext/digest/sha2/lib/digest/sha2.rb: Add the Digest::SHA2 class to wrap up SHA2 variants: SHA256, SHA384 and SHA512, hoping this module would make a decent example of a digest subclass written in Ruby. * ext/digest/lib/digest.rb: Adjust autoload entries for SHA2 classes. * ext/digest/lib/digest/hmac.rb: Follow the framework updates. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11197 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 26 + ext/digest/bubblebabble/bubblebabble.c | 28 +- ext/digest/bubblebabble/depend | 3 + ext/digest/bubblebabble/extconf.rb | 3 + ext/digest/digest.c | 736 ++++++++++++++----------- ext/digest/digest.h | 16 +- ext/digest/lib/digest.rb | 6 +- ext/digest/lib/digest/hmac.rb | 59 +- ext/digest/md5/md5init.c | 10 +- ext/digest/rmd160/rmd160init.c | 15 +- ext/digest/sha1/sha1init.c | 14 +- ext/digest/sha2/lib/digest/sha2.rb | 73 +++ ext/digest/sha2/sha2init.c | 10 +- 13 files changed, 595 insertions(+), 404 deletions(-) create mode 100644 ext/digest/bubblebabble/depend create mode 100644 ext/digest/sha2/lib/digest/sha2.rb diff --git a/ChangeLog b/ChangeLog index 30aad5cbb6471d..7a984c4c6ffacb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,29 @@ +Fri Oct 20 20:28:37 2006 Akinori MUSHA + + * ext/digest: Prefix C constants with RUBY_ and C type names with + rb_ to avoid name clash in writing extensions. + + * ext/digest: Introduce Digest::Class and Digest::Instance for + ease of implementing subclasses and add-ons, inspried by + gotoyuzo. + + * ext/digest: The Digest::Instance module now requires and assumes + that any instance be resettable and clonable, and add some + convenient instance methods such as "new()", for creating a new + copy, parameter taking "digest()" and "hexdigest()", for instant + calculation. These methods make digest instances work just like + digest classes. + + * ext/digest/sha2/lib/digest/sha2.rb: + Add the Digest::SHA2 class to wrap up SHA2 variants: SHA256, + SHA384 and SHA512, hoping this module would make a decent + example of a digest subclass written in Ruby. + + * ext/digest/lib/digest.rb: Adjust autoload entries for SHA2 + classes. + + * ext/digest/lib/digest/hmac.rb: Follow the framework updates. + Fri Oct 20 10:47:43 2006 NAKAMURA Usaku * lib/mkmf.rb: fixed the bug of handling COMMON_MACROS. diff --git a/ext/digest/bubblebabble/bubblebabble.c b/ext/digest/bubblebabble/bubblebabble.c index a7e4d9f7d60c41..3a03ceced00836 100644 --- a/ext/digest/bubblebabble/bubblebabble.c +++ b/ext/digest/bubblebabble/bubblebabble.c @@ -12,8 +12,8 @@ ************************************************/ #include "ruby.h" +#include "digest.h" -static VALUE mDigest, cDigest_Base; static ID id_digest; static VALUE @@ -83,9 +83,6 @@ bubblebabble_str_new(VALUE str_digest) * Digest.bubblebabble(string) -> bubblebabble_string * * Returns a BubbleBabble encoded version of a given _string_. - * - * If extra arguments are given, they are passed to - * Digest::ALGORITHM.digest() along with the _string_. */ static VALUE rb_digest_s_bubblebabble(VALUE klass, VALUE str) @@ -95,12 +92,12 @@ rb_digest_s_bubblebabble(VALUE klass, VALUE str) /* * call-seq: - * Digest::ALGORITHM.bubblebabble(string, ...) -> hash_string + * Digest::Class.bubblebabble(string, ...) -> hash_string * * Returns the BubbleBabble encoded hash value of a given _string_. */ static VALUE -rb_digest_base_s_bubblebabble(int argc, VALUE *argv, VALUE klass) +rb_digest_class_s_bubblebabble(int argc, VALUE *argv, VALUE klass) { return bubblebabble_str_new(rb_funcall2(klass, id_digest, argc, argv)); } @@ -112,7 +109,7 @@ rb_digest_base_s_bubblebabble(int argc, VALUE *argv, VALUE klass) * Returns the resulting hash value in a Bubblebabble encoded form. */ static VALUE -rb_digest_base_bubblebabble(VALUE self) +rb_digest_instance_bubblebabble(VALUE self) { return bubblebabble_str_new(rb_funcall(self, id_digest, 0)); } @@ -124,17 +121,22 @@ rb_digest_base_bubblebabble(VALUE self) void Init_bubblebabble(void) { - mDigest = rb_define_module("Digest"); - cDigest_Base = rb_define_class_under(mDigest, "Base", rb_cObject); + VALUE mDigest, mDigest_Instance, cDigest_Class; + + rb_require("digest"); + + mDigest = rb_path2class("Digest"); + mDigest_Instance = rb_path2class("Digest::Instance"); + cDigest_Class = rb_path2class("Digest::Class"); /* Digest::bubblebabble() */ rb_define_module_function(mDigest, "bubblebabble", rb_digest_s_bubblebabble, 1); - /* Digest::Base::bubblebabble() */ - rb_define_singleton_method(cDigest_Base, "bubblebabble", rb_digest_base_s_bubblebabble, -1); + /* Digest::Class::bubblebabble() */ + rb_define_singleton_method(cDigest_Class, "bubblebabble", rb_digest_class_s_bubblebabble, -1); - /* Digest::Base#bubblebabble() */ - rb_define_method(cDigest_Base, "bubblebabble", rb_digest_base_bubblebabble, 0); + /* Digest::Instance#bubblebabble() */ + rb_define_method(mDigest_Instance, "bubblebabble", rb_digest_instance_bubblebabble, 0); id_digest = rb_intern("digest"); } diff --git a/ext/digest/bubblebabble/depend b/ext/digest/bubblebabble/depend new file mode 100644 index 00000000000000..b20148ded45fe1 --- /dev/null +++ b/ext/digest/bubblebabble/depend @@ -0,0 +1,3 @@ +bubblebabble.o: bubblebabble.c $(srcdir)/../digest.h $(hdrdir)/ruby.h \ + $(topdir)/config.h $(hdrdir)/defines.h $(hdrdir)/intern.h \ + $(srcdir)/../defs.h diff --git a/ext/digest/bubblebabble/extconf.rb b/ext/digest/bubblebabble/extconf.rb index 5b7f911e3308be..53cb83934aef47 100644 --- a/ext/digest/bubblebabble/extconf.rb +++ b/ext/digest/bubblebabble/extconf.rb @@ -1,3 +1,6 @@ require 'mkmf' +$defs << "-DHAVE_CONFIG_H" +$INCFLAGS << " -I$(srcdir)/.." + create_makefile('digest/bubblebabble') diff --git a/ext/digest/digest.c b/ext/digest/digest.c index 65a78c0d6c07ac..4a5245f911dd5b 100644 --- a/ext/digest/digest.c +++ b/ext/digest/digest.c @@ -15,48 +15,24 @@ #include "digest.h" -static VALUE mDigest, cDigest_Base; -static ID id_metadata, id_new, id_initialize, id_update, id_digest; +static VALUE rb_mDigest; +static VALUE rb_mDigest_Instance; +static VALUE rb_cDigest_Class; +static VALUE rb_cDigest_Base; -/* - * Document-class: Digest - * - * This module provides a framework for message digest libraries. - */ +static ID id_reset, id_update, id_finish, id_digest, id_hexdigest, id_digest_length; +static ID id_metadata; + +RUBY_EXTERN void Init_digest_base(void); /* - * Document-class: Digest::Base + * Document-module: Digest * - * This class provides a common interface to message digest - * algorithms. + * This module provides a framework for message digest libraries. */ -static algo_t * -get_digest_base_metadata(VALUE klass) -{ - VALUE obj; - algo_t *algo; - - if (rb_ivar_defined(klass, id_metadata) == Qfalse) { - return NULL; - } - - obj = rb_ivar_get(klass, id_metadata); - - Data_Get_Struct(obj, algo_t, algo); - - if (algo->api_version != 1) { - /* - * put conversion here if possible when API is updated - */ - rb_raise(rb_eRuntimeError, "Incompatible digest API version"); - } - - return algo; -} - static VALUE -hexdigest_str_new(VALUE str_digest) +hexencode_str_new(VALUE str_digest) { char *digest; size_t digest_len; @@ -88,259 +64,195 @@ hexdigest_str_new(VALUE str_digest) return str; } +/* + * call-seq: + * Digest.hexencode(string) -> hexencoded_string + * + * Generates a hex-encoded version of a given _string_. + */ static VALUE -rb_digest_base_alloc(VALUE klass) +rb_digest_s_hexencode(VALUE klass, VALUE str) { - algo_t *algo; - VALUE obj; - void *pctx; - - if (klass == cDigest_Base) { - rb_raise(rb_eNotImpError, "Digest::Base is an abstract class"); - } - - algo = get_digest_base_metadata(klass); - - if (algo == NULL) { - return Data_Wrap_Struct(klass, 0, free, 0); - } - - pctx = xmalloc(algo->ctx_size); - algo->init_func(pctx); + return hexencode_str_new(str); +} - obj = Data_Wrap_Struct(klass, 0, free, pctx); +/* + * Document-module: Digest::Instance + * + * This module provides instance methods for a digest implementation + * object to calculate message digest values. + */ - return obj; +/* + * call-seq: + * digest_obj.update(string) -> digest_obj + * digest_obj << string -> digest_obj + * + * Updates the digest using a given _string_ and returns self. + * + * The update() method and the left-shift operator are overridden by + * each implementation subclass. (One should be an alias for the + * other) + */ +static VALUE +rb_digest_instance_update(VALUE self, VALUE str) +{ + rb_raise(rb_eRuntimeError, "%s does not implement update()", rb_inspect(self)); } /* * call-seq: - * Digest::ALGORITHM.digest(string[, ...]) -> hash_string + * digest_obj.instance_eval { finish } -> digest_obj + * + * Finishes the digest and returns the resulting hash value. * - * Returns the hash value of a given string _data_. This is almost - * equivalent to Digest::ALGORITHM.new(...).update(string).digest() - * where extra arguments, if any, are passed through to the - * constructor. + * This method is overridden by each implementation subclass and often + * made private, because some of those subclasses may leave internal + * data uninitialized. Do not call this method from outside. Use + * #digest!() instead, which ensures that internal data be reset for + * security reasons. */ static VALUE -rb_digest_base_s_digest(int argc, VALUE *argv, VALUE klass) +rb_digest_instance_finish(VALUE self) { - VALUE str; - algo_t *algo; - void *pctx; - volatile VALUE obj; - - if (argc < 1) { - rb_raise(rb_eArgError, "no data given"); - } - - str = *argv++; - argc--; - - StringValue(str); - - algo = get_digest_base_metadata(klass); - - if (algo == NULL) { - VALUE obj = rb_funcall2(klass, id_new, argc, argv); - rb_funcall(obj, id_update, 1, str); - return rb_funcall(obj, id_digest, 0); - } - - obj = rb_digest_base_alloc(klass); - Data_Get_Struct(obj, void, pctx); - - algo->update_func(pctx, RSTRING_PTR(str), RSTRING_LEN(str)); - - str = rb_str_new(0, algo->digest_len); - algo->finish_func(pctx, RSTRING_PTR(str)); - - return str; + rb_raise(rb_eRuntimeError, "%s does not implement finish()", rb_inspect(self)); } /* * call-seq: - * Digest::ALGORITHM.hexdigest(string[, ...]) -> hash_string + * digest_obj.reset -> digest_obj + * + * Resets the digest to the initial state and returns self. * - * Returns the hex-encoded hash value of a given _string_. This - * method just hex-encode the return value of - * Digest::ALGORITHM.digest(string[, ...]) where extra arguments, if - * any, are passed through along with the _string_. + * This method is overridden by each implementation subclass. */ static VALUE -rb_digest_base_s_hexdigest(int argc, VALUE *argv, VALUE klass) +rb_digest_instance_reset(VALUE self) { - return hexdigest_str_new(rb_funcall2(klass, id_digest, argc, argv)); + rb_raise(rb_eRuntimeError, "%s does not implement reset()", rb_inspect(self)); } -/* :nodoc: */ +/* + * call-seq: + * digest_obj.new -> another_digest_obj + * + * Returns a new, initialized copy of the digest object. Equivalent + * to digest_obj.clone().reset(). + */ static VALUE -rb_digest_base_copy(VALUE copy, VALUE obj) +rb_digest_instance_new(VALUE self) { - algo_t *algo; - void *pctx1, *pctx2; - - if (copy == obj) return copy; - rb_check_frozen(copy); - algo = get_digest_base_metadata(rb_obj_class(copy)); - - if (algo == NULL) { - /* initialize_copy() is undefined or something */ - rb_notimplement(); - } - - /* get_digest_base_metadata() may return a NULL */ - if (algo != get_digest_base_metadata(rb_obj_class(obj))) { - rb_raise(rb_eTypeError, "wrong argument class"); - } - Data_Get_Struct(obj, void, pctx1); - Data_Get_Struct(copy, void, pctx2); - memcpy(pctx2, pctx1, algo->ctx_size); - - return copy; + VALUE clone = rb_obj_clone(self); + rb_funcall(clone, id_reset, 0); + return clone; } /* * call-seq: - * digest_obj.reset -> digest_obj + * digest_obj.digest -> string + * digest_obj.digest(string) -> string * - * Resets the digest to the initial state and returns self. + * If none is given, returns the resulting hash value of the digest, + * keeping the digest's state. * - * Every implementation subclass which constructor takes arguments - * must redefine this method because Digest::Base#reset() internally - * calls initialize() with no argument. + * If a _string_ is given, returns the hash value for the given + * _string_, resetting the digest to the initial state before and + * after the process. */ static VALUE -rb_digest_base_reset(VALUE self) +rb_digest_instance_digest(int argc, VALUE *argv, VALUE self) { - algo_t *algo; - void *pctx; - - algo = get_digest_base_metadata(rb_obj_class(self)); + VALUE str, value; - if (algo == NULL) { - rb_funcall(self, id_initialize, 0); + if (rb_scan_args(argc, argv, "01", &str) > 0) { + rb_funcall(self, id_reset, 0); + rb_funcall(self, id_update, 1, str); + value = rb_funcall(self, id_finish, 0); + rb_funcall(self, id_reset, 0); + } else { + VALUE clone = rb_obj_clone(self); - return self; + value = rb_funcall(clone, id_finish, 0); + rb_funcall(clone, id_reset, 0); } - Data_Get_Struct(self, void, pctx); - - memset(pctx, 0, algo->ctx_size); - algo->init_func(pctx); - - return self; + return value; } /* * call-seq: - * digest_obj.update(string) -> digest_obj - * - * Updates the digest using a given _string_ and returns self. + * digest_obj.digest! -> string * - * Implementation subclasses must redefine this method, and should - * make `<<' an alias to it. + * Returns the resulting hash value and resets the digest to the + * initial state. */ static VALUE -rb_digest_base_update(VALUE self, VALUE str) +rb_digest_instance_digest_bang(VALUE self) { - algo_t *algo; - void *pctx; - - algo = get_digest_base_metadata(rb_obj_class(self)); - - if (algo == NULL) { - /* subclasses must define update() */ - rb_notimplement(); - } + VALUE value = rb_funcall(self, id_finish, 0); + rb_funcall(self, id_reset, 0); - Data_Get_Struct(self, void, pctx); - - StringValue(str); - algo->update_func(pctx, RSTRING_PTR(str), RSTRING_LEN(str)); - - return self; + return value; } /* * call-seq: - * digest_obj << string -> digest_obj + * digest_obj.hexdigest -> string + * digest_obj.hexdigest(string) -> string * - * Calls update(string). + * If none is given, returns the resulting hash value of the digest in + * a hex-encoded form, keeping the digest's state. * - * Implementation subclasses need not but should alias this method to - * update() to eliminate chain calls. + * If a _string_ is given, returns the hash value for the given + * _string_ in a hex-encoded form, resetting the digest to the initial + * state before and after the process. */ static VALUE -rb_digest_base_lshift(VALUE self, VALUE str) +rb_digest_instance_hexdigest(int argc, VALUE *argv, VALUE self) { - algo_t *algo; - void *pctx; - - algo = get_digest_base_metadata(rb_obj_class(self)); + VALUE str, value; - if (algo == NULL) { - /* subclasses just need to define update(), not << */ + if (rb_scan_args(argc, argv, "01", &str) > 0) { + rb_funcall(self, id_reset, 0); rb_funcall(self, id_update, 1, str); + value = rb_funcall(self, id_finish, 0); + rb_funcall(self, id_reset, 0); + } else { + VALUE clone = rb_obj_clone(self); - return self; + value = rb_funcall(clone, id_finish, 0); + rb_funcall(clone, id_reset, 0); } - Data_Get_Struct(self, void, pctx); - - StringValue(str); - algo->update_func(pctx, RSTRING_PTR(str), RSTRING_LEN(str)); - - return self; + return hexencode_str_new(value); } /* * call-seq: - * digest_obj.digest -> string - * - * Returns the resulting hash value. + * digest_obj.hexdigest! -> string * - * Implementation subclasses must redefine this method. + * Returns the resulting hash value and resets the digest to the + * initial state. */ static VALUE -rb_digest_base_digest(VALUE self) +rb_digest_instance_hexdigest_bang(VALUE self) { - algo_t *algo; - void *pctx1, *pctx2; - size_t ctx_size; - VALUE str; + VALUE value = rb_funcall(self, id_finish, 0); + rb_funcall(self, id_reset, 0); - algo = get_digest_base_metadata(rb_obj_class(self)); - - if (algo == NULL) { - /* subclasses must define update() */ - rb_notimplement(); - } - - Data_Get_Struct(self, void, pctx1); - - ctx_size = algo->ctx_size; - pctx2 = xmalloc(ctx_size); - memcpy(pctx2, pctx1, ctx_size); - - str = rb_str_new(0, algo->digest_len); - algo->finish_func(pctx2, RSTRING_PTR(str)); - free(pctx2); - - return str; + return hexencode_str_new(value); } /* * call-seq: - * digest_obj.hexdigest -> string * digest_obj.to_s -> string * - * Returns the resulting hash value in a hex-encoded form. + * Returns digest_obj.hexdigest(). */ static VALUE -rb_digest_base_hexdigest(VALUE self) +rb_digest_instance_to_s(VALUE self) { - return hexdigest_str_new(rb_funcall(self, id_digest, 0)); + return rb_funcall(self, id_hexdigest, 0); } /* @@ -350,202 +262,378 @@ rb_digest_base_hexdigest(VALUE self) * Creates a printable version of the digest object. */ static VALUE -rb_digest_base_inspect(VALUE self) +rb_digest_instance_inspect(VALUE self) { - algo_t *algo; - VALUE klass, str; - size_t digest_len = 32; /* no need to be just the right size */ + VALUE str; + size_t digest_len = 32; /* about this size at least */ char *cname; - klass = rb_obj_class(self); - algo = get_digest_base_metadata(klass); - - if (algo != NULL) - digest_len = algo->digest_len; - cname = rb_obj_classname(self); - /* # */ + /* # */ str = rb_str_buf_new(2 + strlen(cname) + 2 + digest_len * 2 + 1); rb_str_buf_cat2(str, "#<"); rb_str_buf_cat2(str, cname); rb_str_buf_cat2(str, ": "); - rb_str_buf_append(str, rb_digest_base_hexdigest(self)); + rb_str_buf_append(str, rb_digest_instance_hexdigest(0, 0, self)); rb_str_buf_cat2(str, ">"); return str; } /* * call-seq: - * digest_obj == string -> boolean * digest_obj == another_digest_obj -> boolean + * digest_obj == string -> boolean * - * If a string is given, checks whether it is equal to the hash value - * of the digest object. If another instance of the same digest class - * is given, checks whether they have the same hash value. Otherwise - * returns false. + * If a string is given, checks whether it is equal to the hex-encoded + * hash value of the digest object. If another instance of the same + * digest class is given, checks whether they have the same hash + * value. Otherwise returns false. */ static VALUE -rb_digest_base_equal(VALUE self, VALUE other) +rb_digest_instance_equal(VALUE self, VALUE other) { - algo_t *algo; - VALUE klass; VALUE str1, str2; - klass = rb_obj_class(self); - - if (rb_obj_class(other) == klass) { - str1 = rb_funcall(self, id_digest, 0); - str2 = rb_funcall(other, id_digest, 0); + if (rb_obj_class(self) == rb_obj_class(other)) { + str1 = rb_digest_instance_digest(0, 0, self); + str2 = rb_digest_instance_digest(0, 0, other); } else { - StringValue(other); + str1 = rb_digest_instance_to_s(self); str2 = other; - - algo = get_digest_base_metadata(klass); - - if (RSTRING_LEN(str2) == algo->digest_len) - str1 = rb_funcall(self, id_digest, 0); - else - str1 = rb_digest_base_hexdigest(self); } - if (RSTRING_LEN(str1) == RSTRING_LEN(str2) - && rb_str_cmp(str1, str2) == 0) - return Qtrue; + /* never blindly assume that subclass methods return strings */ + StringValue(str1); + StringValue(str2); + if (RSTRING_LEN(str1) == RSTRING_LEN(str2) && + rb_str_cmp(str1, str2) == 0) { + return Qtrue; + } return Qfalse; } /* * call-seq: - * Digest::ALGORITHM.block_length(...) -> integer + * digest_obj.digest_length -> integer * - * Returns the digest length of the digest algorithm. Parameters - * follow the same specification as the constructor. + * Returns the length of the hash value of the digest. * - * If an implementation subclass does not redefine this method, - * returns Digest::ALGORITHM.new(...).digest_length(). + * This method should be overridden by each implementation subclass. + * If not, digest_obj.digest().length() is returned. */ static VALUE -rb_digest_base_s_digest_length(int argc, VALUE *argv,VALUE klass) +rb_digest_instance_digest_length(VALUE self) { - algo_t *algo; - - algo = get_digest_base_metadata(klass); + /* subclasses really should redefine this method */ + VALUE digest = rb_digest_instance_digest(0, 0, self); - if (algo == NULL) { - /* Subclasses really should redefine this method */ - VALUE obj = rb_funcall2(klass, id_new, argc, argv); - return rb_funcall(obj, rb_intern("digest_length"), 0); - } + /* never blindly assume that #digest() returns a string */ + StringValue(digest); + return INT2NUM(RSTRING_LEN(digest)); +} - return INT2NUM(algo->digest_len); +/* + * call-seq: + * digest_obj.length -> integer + * digest_obj.size -> integer + * + * Returns digest_obj.digest_length(). + */ +static VALUE +rb_digest_instance_length(VALUE self) +{ + return rb_funcall(self, id_digest_length, 0); } /* * call-seq: * digest_obj.block_length -> integer * - * Returns the length of the hash value of the digest object. + * Returns the block length of the digest. * - * If an implementation subclass does not redefine this method, - * returns digest_obj.digest().length(). + * This method is overridden by each implementation subclass. */ static VALUE -rb_digest_base_digest_length(VALUE self) +rb_digest_instance_block_length(VALUE self) { - algo_t *algo; + rb_raise(rb_eRuntimeError, "%s does not implement block_length()", rb_inspect(self)); +} - algo = get_digest_base_metadata(rb_obj_class(self)); +/* + * Document-class: Digest::Class + * + * This module stands as a base class for digest implementation + * classes. + */ - if (algo == NULL) { - /* subclasses really should redefine this method */ - VALUE digest = rb_funcall(self, id_digest, 0); - StringValue(digest); - return INT2NUM(RSTRING_LEN(digest)); +/* + * call-seq: + * Digest::Class.digest(string, *parameters) -> hash_string + * + * Returns the hash value of a given _string_. This is equivalent to + * Digest::Class.new(*parameters).digest(string), where extra + * _parameters_, if any, are passed through to the constructor and the + * _string_ is passed to #digest(). + */ +static VALUE +rb_digest_class_s_digest(int argc, VALUE *argv, VALUE klass) +{ + VALUE str; + void *pctx; + volatile VALUE obj; + + if (argc < 1) { + rb_raise(rb_eArgError, "no data given"); } - return INT2NUM(algo->digest_len); + str = *argv++; + argc--; + + StringValue(str); + + obj = rb_obj_alloc(klass); + rb_obj_call_init(obj, argc, argv); + + return rb_funcall(obj, id_digest, 1, str); } /* * call-seq: - * Digest::ALGORITHM.block_length(...) -> integer + * Digest::Class.hexdigest(string[, ...]) -> hash_string * - * Returns the block length of the digest algorithm. Parameters - * follow the same specification as the constructor. + * Returns the hex-encoded hash value of a given _string_. This is + * almost equivalent to + * Digest.hexencode(Digest::Class.new(*parameters).digest(string)). + */ +static VALUE +rb_digest_class_s_hexdigest(int argc, VALUE *argv, VALUE klass) +{ + return hexencode_str_new(rb_funcall2(klass, id_digest, argc, argv)); +} + +/* + * Document-class: Digest::Base * - * If an implementation subclass does not redefine this method, - * returns Digest::ALGORITHM.new(...).block_length(). + * This abstract class provides a common interface to message digest + * implementation classes written in C. */ + +static rb_digest_metadata_t * +get_digest_base_metadata(VALUE klass) +{ + VALUE obj; + rb_digest_metadata_t *algo; + + if (rb_ivar_defined(klass, id_metadata) == Qfalse) { + /* This class should not be subclassed in Ruby */ + rb_notimplement(); + } + + obj = rb_ivar_get(klass, id_metadata); + + Data_Get_Struct(obj, rb_digest_metadata_t, algo); + + switch (algo->api_version) { + case 2: + break; + + /* + * put conversion here if possible when API is updated + */ + + default: + rb_raise(rb_eRuntimeError, "Incompatible digest API version"); + } + + return algo; +} + static VALUE -rb_digest_base_s_block_length(int argc, VALUE *argv,VALUE klass) +rb_digest_base_alloc(VALUE klass) { - algo_t *algo; + rb_digest_metadata_t *algo; + VALUE obj; + void *pctx; + + if (klass == rb_cDigest_Base) { + rb_raise(rb_eNotImpError, "Digest::Base is an abstract class"); + } algo = get_digest_base_metadata(klass); - if (algo == NULL) { - VALUE obj = rb_funcall2(klass, id_new, argc, argv); - return rb_funcall(obj, rb_intern("block_length"), 0); - } + pctx = xmalloc(algo->ctx_size); + algo->init_func(pctx); - return INT2NUM(algo->block_len); + obj = Data_Wrap_Struct(klass, 0, free, pctx); + + return obj; } -/* - * call-seq: - * digest_obj.block_length -> length - * - * Returns the block length of the digest. - * - * Implementation subclasses must redefine this method if used. - */ +/* :nodoc: */ static VALUE -rb_digest_base_block_length(VALUE self) +rb_digest_base_copy(VALUE copy, VALUE obj) +{ + rb_digest_metadata_t *algo; + void *pctx1, *pctx2; + + if (copy == obj) return copy; + + rb_check_frozen(copy); + + algo = get_digest_base_metadata(rb_obj_class(copy)); + + Data_Get_Struct(obj, void, pctx1); + Data_Get_Struct(copy, void, pctx2); + memcpy(pctx2, pctx1, algo->ctx_size); + + return copy; +} + +/* :nodoc: */ +static VALUE +rb_digest_base_reset(VALUE self) { - algo_t *algo; + rb_digest_metadata_t *algo; + void *pctx; algo = get_digest_base_metadata(rb_obj_class(self)); - if (algo == NULL) { - /* subclasses must define this method (only if used) */ - rb_notimplement(); - } + Data_Get_Struct(self, void, pctx); - return INT2NUM(algo->block_len); + algo->init_func(pctx); + + return self; } -void -Init_digest(void) +/* :nodoc: */ +static VALUE +rb_digest_base_update(VALUE self, VALUE str) { - mDigest = rb_define_module("Digest"); + rb_digest_metadata_t *algo; + void *pctx; - cDigest_Base = rb_define_class_under(mDigest, "Base", rb_cObject); + algo = get_digest_base_metadata(rb_obj_class(self)); - rb_define_alloc_func(cDigest_Base, rb_digest_base_alloc); - rb_define_singleton_method(cDigest_Base, "digest", rb_digest_base_s_digest, -1); - rb_define_singleton_method(cDigest_Base, "hexdigest", rb_digest_base_s_hexdigest, -1); + Data_Get_Struct(self, void, pctx); - rb_define_singleton_method(cDigest_Base, "digest_length", rb_digest_base_s_digest_length, -1); - rb_define_singleton_method(cDigest_Base, "block_length", rb_digest_base_s_block_length, -1); + StringValue(str); + algo->update_func(pctx, RSTRING_PTR(str), RSTRING_LEN(str)); - rb_define_method(cDigest_Base, "initialize_copy", rb_digest_base_copy, 1); - rb_define_method(cDigest_Base, "reset", rb_digest_base_reset, 0); - rb_define_method(cDigest_Base, "update", rb_digest_base_update, 1); - rb_define_method(cDigest_Base, "<<", rb_digest_base_lshift, 1); - rb_define_method(cDigest_Base, "digest", rb_digest_base_digest, 0); - rb_define_method(cDigest_Base, "hexdigest", rb_digest_base_hexdigest, 0); - rb_define_method(cDigest_Base, "to_s", rb_digest_base_hexdigest, 0); - rb_define_method(cDigest_Base, "inspect", rb_digest_base_inspect, 0); - rb_define_method(cDigest_Base, "==", rb_digest_base_equal, 1); + return self; +} - rb_define_method(cDigest_Base, "digest_length", rb_digest_base_digest_length, 0); - rb_define_method(cDigest_Base, "block_length", rb_digest_base_block_length, 0); +/* :nodoc: */ +static VALUE +rb_digest_base_finish(VALUE self) +{ + rb_digest_metadata_t *algo; + void *pctx; + VALUE str; + + algo = get_digest_base_metadata(rb_obj_class(self)); + + Data_Get_Struct(self, void, pctx); + + str = rb_str_new(0, algo->digest_len); + algo->finish_func(pctx, RSTRING_PTR(str)); + + /* avoid potential coredump caused by use of a finished context */ + algo->init_func(pctx); + + return str; +} + +/* :nodoc: */ +static VALUE +rb_digest_base_digest_length(VALUE self) +{ + rb_digest_metadata_t *algo; + + algo = get_digest_base_metadata(rb_obj_class(self)); + + return INT2NUM(algo->digest_len); +} + +/* :nodoc: */ +static VALUE +rb_digest_base_block_length(VALUE self) +{ + rb_digest_metadata_t *algo; + + algo = get_digest_base_metadata(rb_obj_class(self)); + + return INT2NUM(algo->block_len); +} + +void +Init_digest(void) +{ + id_reset = rb_intern("reset"); + id_update = rb_intern("update"); + id_finish = rb_intern("finish"); + id_digest = rb_intern("digest"); + id_hexdigest = rb_intern("hexdigest"); + id_digest_length = rb_intern("digest_length"); + + /* + * module Digest + */ + rb_mDigest = rb_define_module("Digest"); + + /* module functions */ + rb_define_module_function(rb_mDigest, "hexencode", rb_digest_s_hexencode, 1); + + /* + * module Digest::Instance + */ + rb_mDigest_Instance = rb_define_module_under(rb_mDigest, "Instance"); + + /* instance methods that should be overridden */ + rb_define_method(rb_mDigest_Instance, "update", rb_digest_instance_update, 1); + rb_define_method(rb_mDigest_Instance, "<<", rb_digest_instance_update, 1); + rb_define_private_method(rb_mDigest_Instance, "finish", rb_digest_instance_finish, 0); + rb_define_method(rb_mDigest_Instance, "reset", rb_digest_instance_reset, 0); + rb_define_method(rb_mDigest_Instance, "digest_length", rb_digest_instance_digest_length, 0); + rb_define_method(rb_mDigest_Instance, "block_length", rb_digest_instance_block_length, 0); + + /* instance methods that may be overridden */ + rb_define_method(rb_mDigest_Instance, "==", rb_digest_instance_equal, 1); + rb_define_method(rb_mDigest_Instance, "inspect", rb_digest_instance_inspect, 0); + + /* instance methods that need not usually be overridden */ + rb_define_method(rb_mDigest_Instance, "new", rb_digest_instance_new, 0); + rb_define_method(rb_mDigest_Instance, "digest", rb_digest_instance_digest, -1); + rb_define_method(rb_mDigest_Instance, "digest!", rb_digest_instance_digest_bang, 0); + rb_define_method(rb_mDigest_Instance, "hexdigest", rb_digest_instance_hexdigest, -1); + rb_define_method(rb_mDigest_Instance, "hexdigest!", rb_digest_instance_hexdigest_bang, 0); + rb_define_method(rb_mDigest_Instance, "to_s", rb_digest_instance_hexdigest, 0); + rb_define_method(rb_mDigest_Instance, "length", rb_digest_instance_length, 0); + rb_define_method(rb_mDigest_Instance, "size", rb_digest_instance_length, 0); + + /* + * class Digest::Class + */ + rb_cDigest_Class = rb_define_class_under(rb_mDigest, "Class", rb_cObject); + rb_include_module(rb_cDigest_Class, rb_mDigest_Instance); + + /* class methods */ + rb_define_singleton_method(rb_cDigest_Class, "digest", rb_digest_class_s_digest, -1); + rb_define_singleton_method(rb_cDigest_Class, "hexdigest", rb_digest_class_s_hexdigest, -1); id_metadata = rb_intern("metadata"); - id_new = rb_intern("new"); - id_initialize = rb_intern("initialize"); - id_update = rb_intern("update"); - id_digest = rb_intern("digest"); + + /* class Digest::Base < Digest::Class */ + rb_cDigest_Base = rb_define_class_under(rb_mDigest, "Base", rb_cDigest_Class); + + rb_define_alloc_func(rb_cDigest_Base, rb_digest_base_alloc); + + rb_define_method(rb_cDigest_Base, "initialize_copy", rb_digest_base_copy, 1); + rb_define_method(rb_cDigest_Base, "reset", rb_digest_base_reset, 0); + rb_define_method(rb_cDigest_Base, "update", rb_digest_base_update, 1); + rb_define_method(rb_cDigest_Base, "<<", rb_digest_base_update, 1); + rb_define_private_method(rb_cDigest_Base, "finish", rb_digest_base_finish, 0); + rb_define_method(rb_cDigest_Base, "digest_length", rb_digest_base_digest_length, 0); + rb_define_method(rb_cDigest_Base, "block_length", rb_digest_base_block_length, 0); } diff --git a/ext/digest/digest.h b/ext/digest/digest.h index f8c5dd39069c0f..6e4906c8594895 100644 --- a/ext/digest/digest.h +++ b/ext/digest/digest.h @@ -15,16 +15,18 @@ #include "ruby.h" -typedef void (*hash_init_func_t)(void *); -typedef void (*hash_update_func_t)(void *, unsigned char *, size_t); -typedef void (*hash_finish_func_t)(void *, unsigned char *); +#define RUBY_DIGEST_API_VERSION 2 + +typedef void (*rb_digest_hash_init_func_t)(void *); +typedef void (*rb_digest_hash_update_func_t)(void *, unsigned char *, size_t); +typedef void (*rb_digest_hash_finish_func_t)(void *, unsigned char *); typedef struct { int api_version; size_t digest_len; size_t block_len; size_t ctx_size; - hash_init_func_t init_func; - hash_update_func_t update_func; - hash_finish_func_t finish_func; -} algo_t; + rb_digest_hash_init_func_t init_func; + rb_digest_hash_update_func_t update_func; + rb_digest_hash_finish_func_t finish_func; +} rb_digest_metadata_t; diff --git a/ext/digest/lib/digest.rb b/ext/digest/lib/digest.rb index dc91e9444e826d..1d1e420a27d375 100644 --- a/ext/digest/lib/digest.rb +++ b/ext/digest/lib/digest.rb @@ -1,9 +1,9 @@ require 'digest.so' module Digest - autoload "SHA256", "digest/sha2" - autoload "SHA384", "digest/sha2" - autoload "SHA512", "digest/sha2" + autoload "SHA256", "digest/sha2.so" + autoload "SHA384", "digest/sha2.so" + autoload "SHA512", "digest/sha2.so" def self.const_missing(name) begin diff --git a/ext/digest/lib/digest/hmac.rb b/ext/digest/lib/digest/hmac.rb index bc2f3bcbc40ae5..b542a23577b7c1 100644 --- a/ext/digest/lib/digest/hmac.rb +++ b/ext/digest/lib/digest/hmac.rb @@ -39,17 +39,14 @@ require 'digest' module Digest - class HMAC < Digest::Base - def initialize(key, digest_class, *digest_params) - @digest_class = digest_class.freeze - @digest_params = digest_params.freeze - @md = digest_class.new(*digest_params) - @tmp_md = @md.clone + class HMAC < Digest::Class + def initialize(key, digester) + @md = digester.new block_len = @md.block_length if key.length > block_len - key = @tmp_md.reset.update(key).digest + key = @md.digest(key) end ipad = Array.new(block_len).fill(0x36) @@ -66,11 +63,11 @@ def initialize(key, digest_class, *digest_params) end def initialize_copy(other) - @md = other.instance_eval { @md } + @md = @md.new end def update(text) - @md.reset.update(@opad + @tmp_md.reset.update(@ipad + text).digest) + @md.reset.update(@opad + @md.digest(@ipad + text)) self end @@ -106,36 +103,36 @@ def inspect require 'test/unit' module TM_HMAC - def hmac_new(key) - Digest::HMAC.new(key, *digest_spec()) - end - def test_s_hexdigest - spec = digest_spec() - cases.each { |h| - assert_equal(h[:hexdigest], Digest::HMAC.hexdigest(h[:data], h[:key], *spec)) + digesters { |d| + assert_equal(h[:hexdigest], Digest::HMAC.hexdigest(h[:data], h[:key], d)) + } } end def test_hexdigest cases.each { |h| - hmac = hmac_new(h[:key]) - hmac.update(h[:data]) + digesters { |d| + hmac = Digest::HMAC.new(h[:key], d) - assert_equal(h[:hexdigest], hmac.hexdigest) + hmac.update(h[:data]) + + assert_equal(h[:hexdigest], hmac.hexdigest) + } } end def test_reset cases.each { |h| - hmac = hmac_new(h[:key]) - hmac.update("test") - hmac.reset - hmac.update(h[:data]) + digesters { |d| + hmac = Digest::HMAC.new(h[:key], d) + hmac.update("test") + hmac.reset + hmac.update(h[:data]) - p hmac - assert_equal(h[:hexdigest], hmac.hexdigest) + assert_equal(h[:hexdigest], hmac.hexdigest) + } } end end @@ -143,8 +140,8 @@ def test_reset class TC_HMAC_MD5 < Test::Unit::TestCase include TM_HMAC - def digest_spec - [Digest::MD5] + def digesters + [Digest::MD5, Digest::MD5.new] end # Taken from RFC 2202: Test Cases for HMAC-MD5 and HMAC-SHA-1 @@ -186,8 +183,8 @@ def cases class TC_HMAC_SHA1 < Test::Unit::TestCase include TM_HMAC - def digest_spec - [Digest::SHA1] + def digesters + [Digest::SHA1, Digest::SHA1.new] end # Taken from RFC 2202: Test Cases for HMAC-MD5 and HMAC-SHA-1 @@ -229,8 +226,8 @@ def cases class TC_HMAC_RMD160 < Test::Unit::TestCase include TM_HMAC - def digest_spec - [Digest::RMD160] + def digesters + [Digest::RMD160, Digest::RMD160.new] end # Taken from RFC 2286: Test Cases for HMAC-RIPEMD160 and HMAC-RIPEMD128 diff --git a/ext/digest/md5/md5init.c b/ext/digest/md5/md5init.c index 1230c17b57e805..17658f4fce2fd6 100644 --- a/ext/digest/md5/md5init.c +++ b/ext/digest/md5/md5init.c @@ -8,14 +8,14 @@ #include "md5.h" #endif -static algo_t md5 = { - 1, +static rb_digest_metadata_t md5 = { + RUBY_DIGEST_API_VERSION, MD5_DIGEST_LENGTH, MD5_BLOCK_LENGTH, sizeof(MD5_CTX), - (hash_init_func_t)MD5_Init, - (hash_update_func_t)MD5_Update, - (hash_finish_func_t)MD5_Finish, + (rb_digest_hash_init_func_t)MD5_Init, + (rb_digest_hash_update_func_t)MD5_Update, + (rb_digest_hash_finish_func_t)MD5_Finish, }; /* diff --git a/ext/digest/rmd160/rmd160init.c b/ext/digest/rmd160/rmd160init.c index a0667636065002..763867df86c069 100644 --- a/ext/digest/rmd160/rmd160init.c +++ b/ext/digest/rmd160/rmd160init.c @@ -8,14 +8,14 @@ #include "rmd160.h" #endif -static algo_t rmd160 = { - 1, +static rb_digest_metadata_t rmd160 = { + RUBY_DIGEST_API_VERSION, RMD160_DIGEST_LENGTH, RMD160_BLOCK_LENGTH, sizeof(RMD160_CTX), - (hash_init_func_t)RMD160_Init, - (hash_update_func_t)RMD160_Update, - (hash_finish_func_t)RMD160_Finish, + (rb_digest_hash_init_func_t)RMD160_Init, + (rb_digest_hash_update_func_t)RMD160_Update, + (rb_digest_hash_finish_func_t)RMD160_Finish, }; /* @@ -27,7 +27,6 @@ void Init_rmd160() { VALUE mDigest, cDigest_Base, cDigest_RMD160; - ID id_metadata; rb_require("digest"); @@ -36,8 +35,6 @@ Init_rmd160() cDigest_RMD160 = rb_define_class_under(mDigest, "RMD160", cDigest_Base); - id_metadata = rb_intern("metadata"); - - rb_ivar_set(cDigest_RMD160, id_metadata, + rb_ivar_set(cDigest_RMD160, rb_intern("metadata"), Data_Wrap_Struct(rb_cObject, 0, 0, &rmd160)); } diff --git a/ext/digest/sha1/sha1init.c b/ext/digest/sha1/sha1init.c index 68c7637ab832c1..b2146f05a9a6d6 100644 --- a/ext/digest/sha1/sha1init.c +++ b/ext/digest/sha1/sha1init.c @@ -8,14 +8,14 @@ #include "sha1.h" #endif -static algo_t sha1 = { - 1, +static rb_digest_metadata_t sha1 = { + RUBY_DIGEST_API_VERSION, SHA1_DIGEST_LENGTH, SHA1_BLOCK_LENGTH, sizeof(SHA1_CTX), - (hash_init_func_t)SHA1_Init, - (hash_update_func_t)SHA1_Update, - (hash_finish_func_t)SHA1_Finish, + (rb_digest_hash_init_func_t)SHA1_Init, + (rb_digest_hash_update_func_t)SHA1_Update, + (rb_digest_hash_finish_func_t)SHA1_Finish, }; /* @@ -27,9 +27,9 @@ void Init_sha1() { VALUE mDigest, cDigest_Base, cDigest_SHA1; - + rb_require("digest"); - + mDigest = rb_path2class("Digest"); cDigest_Base = rb_path2class("Digest::Base"); diff --git a/ext/digest/sha2/lib/digest/sha2.rb b/ext/digest/sha2/lib/digest/sha2.rb new file mode 100644 index 00000000000000..683b9904a3662a --- /dev/null +++ b/ext/digest/sha2/lib/digest/sha2.rb @@ -0,0 +1,73 @@ +#-- +# sha2.rb - defines Digest::SHA2 class which wraps up the SHA256, +# SHA384, and SHA512 classes. +#++ +# Copyright (c) 2006 Akinori MUSHA +# +# All rights reserved. You can redistribute and/or modify it under the same +# terms as Ruby. +# +# $Id$ + +require 'digest' + +module Digest + # + # A meta digest provider class for SHA256, SHA384 and SHA512. + # + class SHA2 < Digest::Class + # call-seq: + # Digest::SHA2.new(bitlen = 256) -> digest_obj + # + # Creates a new SHA2 hash object with a given bit length. + def initialize(bitlen = 256) + case bitlen + when 256 + @sha2 = Digest::SHA256.new + when 384 + @sha2 = Digest::SHA384.new + when 512 + @sha2 = Digest::SHA512.new + else + raise ArgumentError, "unsupported bit length: %s" % bitlen.inspect + end + @bitlen = bitlen + end + + # :nodoc: + def reset + @sha2.reset + self + end + + # :nodoc: + def update(str) + @sha2.update(str) + self + end + alias << update + + def finish + @sha2.digest! + end + private :finish + + def block_length + @sha2.block_length + end + + def digest_length + @sha2.digest_length + end + + # :nodoc: + def initialize_copy(other) + @sha2 = @sha2.clone + end + + # :nodoc: + def inspect + "#<%s:%d %s>" % [self.class.name, @bitlen, hexdigest] + end + end +end diff --git a/ext/digest/sha2/sha2init.c b/ext/digest/sha2/sha2init.c index ee184819b841e2..c83a29316a881a 100644 --- a/ext/digest/sha2/sha2init.c +++ b/ext/digest/sha2/sha2init.c @@ -7,14 +7,14 @@ #define FOREACH_BITLEN(func) func(256) func(384) func(512) #define DEFINE_ALGO_METADATA(bitlen) \ -static algo_t sha##bitlen = { \ - 1, \ +static rb_digest_metadata_t sha##bitlen = { \ + RUBY_DIGEST_API_VERSION, \ SHA##bitlen##_DIGEST_LENGTH, \ SHA##bitlen##_BLOCK_LENGTH, \ sizeof(SHA##bitlen##_CTX), \ - (hash_init_func_t)SHA##bitlen##_Init, \ - (hash_update_func_t)SHA##bitlen##_Update, \ - (hash_finish_func_t)SHA##bitlen##_Finish, \ + (rb_digest_hash_init_func_t)SHA##bitlen##_Init, \ + (rb_digest_hash_update_func_t)SHA##bitlen##_Update, \ + (rb_digest_hash_finish_func_t)SHA##bitlen##_Finish, \ }; FOREACH_BITLEN(DEFINE_ALGO_METADATA)