* 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
This commit is contained in:
knu 2006-10-20 12:48:35 +00:00
Родитель 03f19e27ed
Коммит b2c7fe1bbf
13 изменённых файлов: 643 добавлений и 452 удалений

Просмотреть файл

@ -1,3 +1,29 @@
Fri Oct 20 20:28:37 2006 Akinori MUSHA <knu@iDaemons.org>
* 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 <usa@ruby-lang.org> Fri Oct 20 10:47:43 2006 NAKAMURA Usaku <usa@ruby-lang.org>
* lib/mkmf.rb: fixed the bug of handling COMMON_MACROS. * lib/mkmf.rb: fixed the bug of handling COMMON_MACROS.

Просмотреть файл

@ -12,8 +12,8 @@
************************************************/ ************************************************/
#include "ruby.h" #include "ruby.h"
#include "digest.h"
static VALUE mDigest, cDigest_Base;
static ID id_digest; static ID id_digest;
static VALUE static VALUE
@ -83,9 +83,6 @@ bubblebabble_str_new(VALUE str_digest)
* Digest.bubblebabble(string) -> bubblebabble_string * Digest.bubblebabble(string) -> bubblebabble_string
* *
* Returns a BubbleBabble encoded version of a given _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 static VALUE
rb_digest_s_bubblebabble(VALUE klass, VALUE str) rb_digest_s_bubblebabble(VALUE klass, VALUE str)
@ -95,12 +92,12 @@ rb_digest_s_bubblebabble(VALUE klass, VALUE str)
/* /*
* call-seq: * call-seq:
* Digest::ALGORITHM.bubblebabble(string, ...) -> hash_string * Digest::Class.bubblebabble(string, ...) -> hash_string
* *
* Returns the BubbleBabble encoded hash value of a given _string_. * Returns the BubbleBabble encoded hash value of a given _string_.
*/ */
static VALUE 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)); 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. * Returns the resulting hash value in a Bubblebabble encoded form.
*/ */
static VALUE static VALUE
rb_digest_base_bubblebabble(VALUE self) rb_digest_instance_bubblebabble(VALUE self)
{ {
return bubblebabble_str_new(rb_funcall(self, id_digest, 0)); return bubblebabble_str_new(rb_funcall(self, id_digest, 0));
} }
@ -124,17 +121,22 @@ rb_digest_base_bubblebabble(VALUE self)
void void
Init_bubblebabble(void) Init_bubblebabble(void)
{ {
mDigest = rb_define_module("Digest"); VALUE mDigest, mDigest_Instance, cDigest_Class;
cDigest_Base = rb_define_class_under(mDigest, "Base", rb_cObject);
rb_require("digest");
mDigest = rb_path2class("Digest");
mDigest_Instance = rb_path2class("Digest::Instance");
cDigest_Class = rb_path2class("Digest::Class");
/* Digest::bubblebabble() */ /* Digest::bubblebabble() */
rb_define_module_function(mDigest, "bubblebabble", rb_digest_s_bubblebabble, 1); rb_define_module_function(mDigest, "bubblebabble", rb_digest_s_bubblebabble, 1);
/* Digest::Base::bubblebabble() */ /* Digest::Class::bubblebabble() */
rb_define_singleton_method(cDigest_Base, "bubblebabble", rb_digest_base_s_bubblebabble, -1); rb_define_singleton_method(cDigest_Class, "bubblebabble", rb_digest_class_s_bubblebabble, -1);
/* Digest::Base#bubblebabble() */ /* Digest::Instance#bubblebabble() */
rb_define_method(cDigest_Base, "bubblebabble", rb_digest_base_bubblebabble, 0); rb_define_method(mDigest_Instance, "bubblebabble", rb_digest_instance_bubblebabble, 0);
id_digest = rb_intern("digest"); id_digest = rb_intern("digest");
} }

Просмотреть файл

@ -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

Просмотреть файл

@ -1,3 +1,6 @@
require 'mkmf' require 'mkmf'
$defs << "-DHAVE_CONFIG_H"
$INCFLAGS << " -I$(srcdir)/.."
create_makefile('digest/bubblebabble') create_makefile('digest/bubblebabble')

Просмотреть файл

@ -15,48 +15,24 @@
#include "digest.h" #include "digest.h"
static VALUE mDigest, cDigest_Base; static VALUE rb_mDigest;
static ID id_metadata, id_new, id_initialize, id_update, id_digest; static VALUE rb_mDigest_Instance;
static VALUE rb_cDigest_Class;
static VALUE rb_cDigest_Base;
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 * Document-module: Digest
* *
* This module provides a framework for message digest libraries. * This module provides a framework for message digest libraries.
*/ */
/*
* Document-class: Digest::Base
*
* This class provides a common interface to message digest
* algorithms.
*/
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 static VALUE
hexdigest_str_new(VALUE str_digest) hexencode_str_new(VALUE str_digest)
{ {
char *digest; char *digest;
size_t digest_len; size_t digest_len;
@ -88,45 +64,323 @@ hexdigest_str_new(VALUE str_digest)
return str; return str;
} }
/*
* call-seq:
* Digest.hexencode(string) -> hexencoded_string
*
* Generates a hex-encoded version of a given _string_.
*/
static VALUE static VALUE
rb_digest_base_alloc(VALUE klass) rb_digest_s_hexencode(VALUE klass, VALUE str)
{ {
algo_t *algo; return hexencode_str_new(str);
VALUE obj; }
void *pctx;
if (klass == cDigest_Base) { /*
rb_raise(rb_eNotImpError, "Digest::Base is an abstract class"); * Document-module: Digest::Instance
} *
* This module provides instance methods for a digest implementation
* object to calculate message digest values.
*/
algo = get_digest_base_metadata(klass); /*
* call-seq:
if (algo == NULL) { * digest_obj.update(string) -> digest_obj
return Data_Wrap_Struct(klass, 0, free, 0); * digest_obj << string -> digest_obj
} *
* Updates the digest using a given _string_ and returns self.
pctx = xmalloc(algo->ctx_size); *
algo->init_func(pctx); * The update() method and the left-shift operator are overridden by
* each implementation subclass. (One should be an alias for the
obj = Data_Wrap_Struct(klass, 0, free, pctx); * other)
*/
return obj; static VALUE
rb_digest_instance_update(VALUE self, VALUE str)
{
rb_raise(rb_eRuntimeError, "%s does not implement update()", rb_inspect(self));
} }
/* /*
* call-seq: * call-seq:
* Digest::ALGORITHM.digest(string[, ...]) -> hash_string * digest_obj.instance_eval { finish } -> digest_obj
* *
* Returns the hash value of a given string _data_. This is almost * Finishes the digest and returns the resulting hash value.
* equivalent to Digest::ALGORITHM.new(...).update(string).digest() *
* where extra arguments, if any, are passed through to the * This method is overridden by each implementation subclass and often
* constructor. * 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 static VALUE
rb_digest_base_s_digest(int argc, VALUE *argv, VALUE klass) rb_digest_instance_finish(VALUE self)
{
rb_raise(rb_eRuntimeError, "%s does not implement finish()", rb_inspect(self));
}
/*
* call-seq:
* digest_obj.reset -> digest_obj
*
* Resets the digest to the initial state and returns self.
*
* This method is overridden by each implementation subclass.
*/
static VALUE
rb_digest_instance_reset(VALUE self)
{
rb_raise(rb_eRuntimeError, "%s does not implement reset()", rb_inspect(self));
}
/*
* 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_instance_new(VALUE self)
{
VALUE clone = rb_obj_clone(self);
rb_funcall(clone, id_reset, 0);
return clone;
}
/*
* call-seq:
* digest_obj.digest -> string
* digest_obj.digest(string) -> string
*
* If none is given, returns the resulting hash value of the digest,
* keeping the digest's state.
*
* 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_instance_digest(int argc, VALUE *argv, VALUE self)
{
VALUE str, value;
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);
value = rb_funcall(clone, id_finish, 0);
rb_funcall(clone, id_reset, 0);
}
return value;
}
/*
* call-seq:
* digest_obj.digest! -> string
*
* Returns the resulting hash value and resets the digest to the
* initial state.
*/
static VALUE
rb_digest_instance_digest_bang(VALUE self)
{
VALUE value = rb_funcall(self, id_finish, 0);
rb_funcall(self, id_reset, 0);
return value;
}
/*
* call-seq:
* digest_obj.hexdigest -> string
* digest_obj.hexdigest(string) -> string
*
* If none is given, returns the resulting hash value of the digest in
* a hex-encoded form, keeping the digest's state.
*
* 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_instance_hexdigest(int argc, VALUE *argv, VALUE self)
{
VALUE str, value;
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);
value = rb_funcall(clone, id_finish, 0);
rb_funcall(clone, id_reset, 0);
}
return hexencode_str_new(value);
}
/*
* call-seq:
* digest_obj.hexdigest! -> string
*
* Returns the resulting hash value and resets the digest to the
* initial state.
*/
static VALUE
rb_digest_instance_hexdigest_bang(VALUE self)
{
VALUE value = rb_funcall(self, id_finish, 0);
rb_funcall(self, id_reset, 0);
return hexencode_str_new(value);
}
/*
* call-seq:
* digest_obj.to_s -> string
*
* Returns digest_obj.hexdigest().
*/
static VALUE
rb_digest_instance_to_s(VALUE self)
{
return rb_funcall(self, id_hexdigest, 0);
}
/*
* call-seq:
* digest_obj.inspect -> string
*
* Creates a printable version of the digest object.
*/
static VALUE
rb_digest_instance_inspect(VALUE self)
{
VALUE str;
size_t digest_len = 32; /* about this size at least */
char *cname;
cname = rb_obj_classname(self);
/* #<Digest::ClassName: xxxxx...xxxx> */
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_instance_hexdigest(0, 0, self));
rb_str_buf_cat2(str, ">");
return str;
}
/*
* call-seq:
* digest_obj == another_digest_obj -> boolean
* digest_obj == string -> boolean
*
* 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_instance_equal(VALUE self, VALUE other)
{
VALUE str1, str2;
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 {
str1 = rb_digest_instance_to_s(self);
str2 = other;
}
/* 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_obj.digest_length -> integer
*
* Returns the length of the hash value of the digest.
*
* This method should be overridden by each implementation subclass.
* If not, digest_obj.digest().length() is returned.
*/
static VALUE
rb_digest_instance_digest_length(VALUE self)
{
/* subclasses really should redefine this method */
VALUE digest = rb_digest_instance_digest(0, 0, self);
/* never blindly assume that #digest() returns a string */
StringValue(digest);
return INT2NUM(RSTRING_LEN(digest));
}
/*
* 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 block length of the digest.
*
* This method is overridden by each implementation subclass.
*/
static VALUE
rb_digest_instance_block_length(VALUE self)
{
rb_raise(rb_eRuntimeError, "%s does not implement block_length()", rb_inspect(self));
}
/*
* Document-class: Digest::Class
*
* This module stands as a base class for digest implementation
* classes.
*/
/*
* 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; VALUE str;
algo_t *algo;
void *pctx; void *pctx;
volatile VALUE obj; volatile VALUE obj;
@ -139,60 +393,97 @@ rb_digest_base_s_digest(int argc, VALUE *argv, VALUE klass)
StringValue(str); StringValue(str);
algo = get_digest_base_metadata(klass); obj = rb_obj_alloc(klass);
rb_obj_call_init(obj, argc, argv);
if (algo == NULL) { return rb_funcall(obj, id_digest, 1, str);
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;
} }
/* /*
* call-seq: * call-seq:
* Digest::ALGORITHM.hexdigest(string[, ...]) -> hash_string * Digest::Class.hexdigest(string[, ...]) -> hash_string
* *
* Returns the hex-encoded hash value of a given _string_. This * Returns the hex-encoded hash value of a given _string_. This is
* method just hex-encode the return value of * almost equivalent to
* Digest::ALGORITHM.digest(string[, ...]) where extra arguments, if * Digest.hexencode(Digest::Class.new(*parameters).digest(string)).
* any, are passed through along with the _string_.
*/ */
static VALUE static VALUE
rb_digest_base_s_hexdigest(int argc, VALUE *argv, VALUE klass) rb_digest_class_s_hexdigest(int argc, VALUE *argv, VALUE klass)
{ {
return hexdigest_str_new(rb_funcall2(klass, id_digest, argc, argv)); return hexencode_str_new(rb_funcall2(klass, id_digest, argc, argv));
}
/*
* Document-class: Digest::Base
*
* 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_alloc(VALUE klass)
{
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);
pctx = xmalloc(algo->ctx_size);
algo->init_func(pctx);
obj = Data_Wrap_Struct(klass, 0, free, pctx);
return obj;
} }
/* :nodoc: */ /* :nodoc: */
static VALUE static VALUE
rb_digest_base_copy(VALUE copy, VALUE obj) rb_digest_base_copy(VALUE copy, VALUE obj)
{ {
algo_t *algo; rb_digest_metadata_t *algo;
void *pctx1, *pctx2; void *pctx1, *pctx2;
if (copy == obj) return copy; if (copy == obj) return copy;
rb_check_frozen(copy); rb_check_frozen(copy);
algo = get_digest_base_metadata(rb_obj_class(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(obj, void, pctx1);
Data_Get_Struct(copy, void, pctx2); Data_Get_Struct(copy, void, pctx2);
memcpy(pctx2, pctx1, algo->ctx_size); memcpy(pctx2, pctx1, algo->ctx_size);
@ -200,60 +491,31 @@ rb_digest_base_copy(VALUE copy, VALUE obj)
return copy; return copy;
} }
/* /* :nodoc: */
* call-seq:
* digest_obj.reset -> digest_obj
*
* Resets the digest to the initial state and returns self.
*
* Every implementation subclass which constructor takes arguments
* must redefine this method because Digest::Base#reset() internally
* calls initialize() with no argument.
*/
static VALUE static VALUE
rb_digest_base_reset(VALUE self) rb_digest_base_reset(VALUE self)
{ {
algo_t *algo; rb_digest_metadata_t *algo;
void *pctx; void *pctx;
algo = get_digest_base_metadata(rb_obj_class(self)); algo = get_digest_base_metadata(rb_obj_class(self));
if (algo == NULL) {
rb_funcall(self, id_initialize, 0);
return self;
}
Data_Get_Struct(self, void, pctx); Data_Get_Struct(self, void, pctx);
memset(pctx, 0, algo->ctx_size);
algo->init_func(pctx); algo->init_func(pctx);
return self; return self;
} }
/* /* :nodoc: */
* call-seq:
* digest_obj.update(string) -> digest_obj
*
* Updates the digest using a given _string_ and returns self.
*
* Implementation subclasses must redefine this method, and should
* make `<<' an alias to it.
*/
static VALUE static VALUE
rb_digest_base_update(VALUE self, VALUE str) rb_digest_base_update(VALUE self, VALUE str)
{ {
algo_t *algo; rb_digest_metadata_t *algo;
void *pctx; void *pctx;
algo = get_digest_base_metadata(rb_obj_class(self)); algo = get_digest_base_metadata(rb_obj_class(self));
if (algo == NULL) {
/* subclasses must define update() */
rb_notimplement();
}
Data_Get_Struct(self, void, pctx); Data_Get_Struct(self, void, pctx);
StringValue(str); StringValue(str);
@ -262,290 +524,116 @@ rb_digest_base_update(VALUE self, VALUE str)
return self; return self;
} }
/* /* :nodoc: */
* call-seq:
* digest_obj << string -> digest_obj
*
* Calls update(string).
*
* Implementation subclasses need not but should alias this method to
* update() to eliminate chain calls.
*/
static VALUE static VALUE
rb_digest_base_lshift(VALUE self, VALUE str) rb_digest_base_finish(VALUE self)
{ {
algo_t *algo; rb_digest_metadata_t *algo;
void *pctx; void *pctx;
algo = get_digest_base_metadata(rb_obj_class(self));
if (algo == NULL) {
/* subclasses just need to define update(), not << */
rb_funcall(self, id_update, 1, str);
return self;
}
Data_Get_Struct(self, void, pctx);
StringValue(str);
algo->update_func(pctx, RSTRING_PTR(str), RSTRING_LEN(str));
return self;
}
/*
* call-seq:
* digest_obj.digest -> string
*
* Returns the resulting hash value.
*
* Implementation subclasses must redefine this method.
*/
static VALUE
rb_digest_base_digest(VALUE self)
{
algo_t *algo;
void *pctx1, *pctx2;
size_t ctx_size;
VALUE str; VALUE str;
algo = get_digest_base_metadata(rb_obj_class(self)); algo = get_digest_base_metadata(rb_obj_class(self));
if (algo == NULL) { Data_Get_Struct(self, void, pctx);
/* 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); str = rb_str_new(0, algo->digest_len);
algo->finish_func(pctx2, RSTRING_PTR(str)); algo->finish_func(pctx, RSTRING_PTR(str));
free(pctx2);
/* avoid potential coredump caused by use of a finished context */
algo->init_func(pctx);
return str; return str;
} }
/* /* :nodoc: */
* call-seq:
* digest_obj.hexdigest -> string
* digest_obj.to_s -> string
*
* Returns the resulting hash value in a hex-encoded form.
*/
static VALUE
rb_digest_base_hexdigest(VALUE self)
{
return hexdigest_str_new(rb_funcall(self, id_digest, 0));
}
/*
* call-seq:
* digest_obj.inspect -> string
*
* Creates a printable version of the digest object.
*/
static VALUE
rb_digest_base_inspect(VALUE self)
{
algo_t *algo;
VALUE klass, str;
size_t digest_len = 32; /* no need to be just the right size */
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);
/* #<Digest::Alg: xxxxx...xxxx> */
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_cat2(str, ">");
return str;
}
/*
* call-seq:
* digest_obj == string -> boolean
* digest_obj == another_digest_obj -> 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.
*/
static VALUE
rb_digest_base_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);
} else {
StringValue(other);
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;
return Qfalse;
}
/*
* call-seq:
* Digest::ALGORITHM.block_length(...) -> integer
*
* Returns the digest length of the digest algorithm. Parameters
* follow the same specification as the constructor.
*
* If an implementation subclass does not redefine this method,
* returns Digest::ALGORITHM.new(...).digest_length().
*/
static VALUE
rb_digest_base_s_digest_length(int argc, VALUE *argv,VALUE klass)
{
algo_t *algo;
algo = get_digest_base_metadata(klass);
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);
}
return INT2NUM(algo->digest_len);
}
/*
* call-seq:
* digest_obj.block_length -> integer
*
* Returns the length of the hash value of the digest object.
*
* If an implementation subclass does not redefine this method,
* returns digest_obj.digest().length().
*/
static VALUE static VALUE
rb_digest_base_digest_length(VALUE self) rb_digest_base_digest_length(VALUE self)
{ {
algo_t *algo; rb_digest_metadata_t *algo;
algo = get_digest_base_metadata(rb_obj_class(self)); algo = get_digest_base_metadata(rb_obj_class(self));
if (algo == NULL) {
/* subclasses really should redefine this method */
VALUE digest = rb_funcall(self, id_digest, 0);
StringValue(digest);
return INT2NUM(RSTRING_LEN(digest));
}
return INT2NUM(algo->digest_len); return INT2NUM(algo->digest_len);
} }
/* /* :nodoc: */
* call-seq:
* Digest::ALGORITHM.block_length(...) -> integer
*
* Returns the block length of the digest algorithm. Parameters
* follow the same specification as the constructor.
*
* If an implementation subclass does not redefine this method,
* returns Digest::ALGORITHM.new(...).block_length().
*/
static VALUE
rb_digest_base_s_block_length(int argc, VALUE *argv,VALUE klass)
{
algo_t *algo;
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);
}
return INT2NUM(algo->block_len);
}
/*
* call-seq:
* digest_obj.block_length -> length
*
* Returns the block length of the digest.
*
* Implementation subclasses must redefine this method if used.
*/
static VALUE static VALUE
rb_digest_base_block_length(VALUE self) rb_digest_base_block_length(VALUE self)
{ {
algo_t *algo; rb_digest_metadata_t *algo;
algo = get_digest_base_metadata(rb_obj_class(self)); algo = get_digest_base_metadata(rb_obj_class(self));
if (algo == NULL) {
/* subclasses must define this method (only if used) */
rb_notimplement();
}
return INT2NUM(algo->block_len); return INT2NUM(algo->block_len);
} }
void void
Init_digest(void) Init_digest(void)
{ {
mDigest = rb_define_module("Digest"); 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");
cDigest_Base = rb_define_class_under(mDigest, "Base", rb_cObject); /*
* module Digest
*/
rb_mDigest = rb_define_module("Digest");
rb_define_alloc_func(cDigest_Base, rb_digest_base_alloc); /* module functions */
rb_define_singleton_method(cDigest_Base, "digest", rb_digest_base_s_digest, -1); rb_define_module_function(rb_mDigest, "hexencode", rb_digest_s_hexencode, 1);
rb_define_singleton_method(cDigest_Base, "hexdigest", rb_digest_base_s_hexdigest, -1);
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); * module Digest::Instance
*/
rb_mDigest_Instance = rb_define_module_under(rb_mDigest, "Instance");
rb_define_method(cDigest_Base, "initialize_copy", rb_digest_base_copy, 1); /* instance methods that should be overridden */
rb_define_method(cDigest_Base, "reset", rb_digest_base_reset, 0); rb_define_method(rb_mDigest_Instance, "update", rb_digest_instance_update, 1);
rb_define_method(cDigest_Base, "update", rb_digest_base_update, 1); rb_define_method(rb_mDigest_Instance, "<<", rb_digest_instance_update, 1);
rb_define_method(cDigest_Base, "<<", rb_digest_base_lshift, 1); rb_define_private_method(rb_mDigest_Instance, "finish", rb_digest_instance_finish, 0);
rb_define_method(cDigest_Base, "digest", rb_digest_base_digest, 0); rb_define_method(rb_mDigest_Instance, "reset", rb_digest_instance_reset, 0);
rb_define_method(cDigest_Base, "hexdigest", rb_digest_base_hexdigest, 0); rb_define_method(rb_mDigest_Instance, "digest_length", rb_digest_instance_digest_length, 0);
rb_define_method(cDigest_Base, "to_s", rb_digest_base_hexdigest, 0); rb_define_method(rb_mDigest_Instance, "block_length", rb_digest_instance_block_length, 0);
rb_define_method(cDigest_Base, "inspect", rb_digest_base_inspect, 0);
rb_define_method(cDigest_Base, "==", rb_digest_base_equal, 1);
rb_define_method(cDigest_Base, "digest_length", rb_digest_base_digest_length, 0); /* instance methods that may be overridden */
rb_define_method(cDigest_Base, "block_length", rb_digest_base_block_length, 0); 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_metadata = rb_intern("metadata");
id_new = rb_intern("new");
id_initialize = rb_intern("initialize"); /* class Digest::Base < Digest::Class */
id_update = rb_intern("update"); rb_cDigest_Base = rb_define_class_under(rb_mDigest, "Base", rb_cDigest_Class);
id_digest = rb_intern("digest");
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);
} }

Просмотреть файл

@ -15,16 +15,18 @@
#include "ruby.h" #include "ruby.h"
typedef void (*hash_init_func_t)(void *); #define RUBY_DIGEST_API_VERSION 2
typedef void (*hash_update_func_t)(void *, unsigned char *, size_t);
typedef void (*hash_finish_func_t)(void *, unsigned char *); 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 { typedef struct {
int api_version; int api_version;
size_t digest_len; size_t digest_len;
size_t block_len; size_t block_len;
size_t ctx_size; size_t ctx_size;
hash_init_func_t init_func; rb_digest_hash_init_func_t init_func;
hash_update_func_t update_func; rb_digest_hash_update_func_t update_func;
hash_finish_func_t finish_func; rb_digest_hash_finish_func_t finish_func;
} algo_t; } rb_digest_metadata_t;

Просмотреть файл

@ -1,9 +1,9 @@
require 'digest.so' require 'digest.so'
module Digest module Digest
autoload "SHA256", "digest/sha2" autoload "SHA256", "digest/sha2.so"
autoload "SHA384", "digest/sha2" autoload "SHA384", "digest/sha2.so"
autoload "SHA512", "digest/sha2" autoload "SHA512", "digest/sha2.so"
def self.const_missing(name) def self.const_missing(name)
begin begin

Просмотреть файл

@ -39,17 +39,14 @@
require 'digest' require 'digest'
module Digest module Digest
class HMAC < Digest::Base class HMAC < Digest::Class
def initialize(key, digest_class, *digest_params) def initialize(key, digester)
@digest_class = digest_class.freeze @md = digester.new
@digest_params = digest_params.freeze
@md = digest_class.new(*digest_params)
@tmp_md = @md.clone
block_len = @md.block_length block_len = @md.block_length
if key.length > block_len if key.length > block_len
key = @tmp_md.reset.update(key).digest key = @md.digest(key)
end end
ipad = Array.new(block_len).fill(0x36) ipad = Array.new(block_len).fill(0x36)
@ -66,11 +63,11 @@ module Digest
end end
def initialize_copy(other) def initialize_copy(other)
@md = other.instance_eval { @md } @md = @md.new
end end
def update(text) def update(text)
@md.reset.update(@opad + @tmp_md.reset.update(@ipad + text).digest) @md.reset.update(@opad + @md.digest(@ipad + text))
self self
end end
@ -106,36 +103,36 @@ __END__
require 'test/unit' require 'test/unit'
module TM_HMAC module TM_HMAC
def hmac_new(key)
Digest::HMAC.new(key, *digest_spec())
end
def test_s_hexdigest def test_s_hexdigest
spec = digest_spec()
cases.each { |h| 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 end
def test_hexdigest def test_hexdigest
cases.each { |h| cases.each { |h|
hmac = hmac_new(h[:key]) digesters { |d|
hmac.update(h[:data]) hmac = Digest::HMAC.new(h[:key], d)
assert_equal(h[:hexdigest], hmac.hexdigest) hmac.update(h[:data])
assert_equal(h[:hexdigest], hmac.hexdigest)
}
} }
end end
def test_reset def test_reset
cases.each { |h| cases.each { |h|
hmac = hmac_new(h[:key]) digesters { |d|
hmac.update("test") hmac = Digest::HMAC.new(h[:key], d)
hmac.reset hmac.update("test")
hmac.update(h[:data]) hmac.reset
hmac.update(h[:data])
p hmac assert_equal(h[:hexdigest], hmac.hexdigest)
assert_equal(h[:hexdigest], hmac.hexdigest) }
} }
end end
end end
@ -143,8 +140,8 @@ end
class TC_HMAC_MD5 < Test::Unit::TestCase class TC_HMAC_MD5 < Test::Unit::TestCase
include TM_HMAC include TM_HMAC
def digest_spec def digesters
[Digest::MD5] [Digest::MD5, Digest::MD5.new]
end end
# Taken from RFC 2202: Test Cases for HMAC-MD5 and HMAC-SHA-1 # Taken from RFC 2202: Test Cases for HMAC-MD5 and HMAC-SHA-1
@ -186,8 +183,8 @@ end
class TC_HMAC_SHA1 < Test::Unit::TestCase class TC_HMAC_SHA1 < Test::Unit::TestCase
include TM_HMAC include TM_HMAC
def digest_spec def digesters
[Digest::SHA1] [Digest::SHA1, Digest::SHA1.new]
end end
# Taken from RFC 2202: Test Cases for HMAC-MD5 and HMAC-SHA-1 # Taken from RFC 2202: Test Cases for HMAC-MD5 and HMAC-SHA-1
@ -229,8 +226,8 @@ end
class TC_HMAC_RMD160 < Test::Unit::TestCase class TC_HMAC_RMD160 < Test::Unit::TestCase
include TM_HMAC include TM_HMAC
def digest_spec def digesters
[Digest::RMD160] [Digest::RMD160, Digest::RMD160.new]
end end
# Taken from RFC 2286: Test Cases for HMAC-RIPEMD160 and HMAC-RIPEMD128 # Taken from RFC 2286: Test Cases for HMAC-RIPEMD160 and HMAC-RIPEMD128

Просмотреть файл

@ -8,14 +8,14 @@
#include "md5.h" #include "md5.h"
#endif #endif
static algo_t md5 = { static rb_digest_metadata_t md5 = {
1, RUBY_DIGEST_API_VERSION,
MD5_DIGEST_LENGTH, MD5_DIGEST_LENGTH,
MD5_BLOCK_LENGTH, MD5_BLOCK_LENGTH,
sizeof(MD5_CTX), sizeof(MD5_CTX),
(hash_init_func_t)MD5_Init, (rb_digest_hash_init_func_t)MD5_Init,
(hash_update_func_t)MD5_Update, (rb_digest_hash_update_func_t)MD5_Update,
(hash_finish_func_t)MD5_Finish, (rb_digest_hash_finish_func_t)MD5_Finish,
}; };
/* /*

Просмотреть файл

@ -8,14 +8,14 @@
#include "rmd160.h" #include "rmd160.h"
#endif #endif
static algo_t rmd160 = { static rb_digest_metadata_t rmd160 = {
1, RUBY_DIGEST_API_VERSION,
RMD160_DIGEST_LENGTH, RMD160_DIGEST_LENGTH,
RMD160_BLOCK_LENGTH, RMD160_BLOCK_LENGTH,
sizeof(RMD160_CTX), sizeof(RMD160_CTX),
(hash_init_func_t)RMD160_Init, (rb_digest_hash_init_func_t)RMD160_Init,
(hash_update_func_t)RMD160_Update, (rb_digest_hash_update_func_t)RMD160_Update,
(hash_finish_func_t)RMD160_Finish, (rb_digest_hash_finish_func_t)RMD160_Finish,
}; };
/* /*
@ -27,7 +27,6 @@ void
Init_rmd160() Init_rmd160()
{ {
VALUE mDigest, cDigest_Base, cDigest_RMD160; VALUE mDigest, cDigest_Base, cDigest_RMD160;
ID id_metadata;
rb_require("digest"); rb_require("digest");
@ -36,8 +35,6 @@ Init_rmd160()
cDigest_RMD160 = rb_define_class_under(mDigest, "RMD160", cDigest_Base); cDigest_RMD160 = rb_define_class_under(mDigest, "RMD160", cDigest_Base);
id_metadata = rb_intern("metadata"); rb_ivar_set(cDigest_RMD160, rb_intern("metadata"),
rb_ivar_set(cDigest_RMD160, id_metadata,
Data_Wrap_Struct(rb_cObject, 0, 0, &rmd160)); Data_Wrap_Struct(rb_cObject, 0, 0, &rmd160));
} }

Просмотреть файл

@ -8,14 +8,14 @@
#include "sha1.h" #include "sha1.h"
#endif #endif
static algo_t sha1 = { static rb_digest_metadata_t sha1 = {
1, RUBY_DIGEST_API_VERSION,
SHA1_DIGEST_LENGTH, SHA1_DIGEST_LENGTH,
SHA1_BLOCK_LENGTH, SHA1_BLOCK_LENGTH,
sizeof(SHA1_CTX), sizeof(SHA1_CTX),
(hash_init_func_t)SHA1_Init, (rb_digest_hash_init_func_t)SHA1_Init,
(hash_update_func_t)SHA1_Update, (rb_digest_hash_update_func_t)SHA1_Update,
(hash_finish_func_t)SHA1_Finish, (rb_digest_hash_finish_func_t)SHA1_Finish,
}; };
/* /*
@ -27,9 +27,9 @@ void
Init_sha1() Init_sha1()
{ {
VALUE mDigest, cDigest_Base, cDigest_SHA1; VALUE mDigest, cDigest_Base, cDigest_SHA1;
rb_require("digest"); rb_require("digest");
mDigest = rb_path2class("Digest"); mDigest = rb_path2class("Digest");
cDigest_Base = rb_path2class("Digest::Base"); cDigest_Base = rb_path2class("Digest::Base");

Просмотреть файл

@ -0,0 +1,73 @@
#--
# sha2.rb - defines Digest::SHA2 class which wraps up the SHA256,
# SHA384, and SHA512 classes.
#++
# Copyright (c) 2006 Akinori MUSHA <knu@iDaemons.org>
#
# 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

Просмотреть файл

@ -7,14 +7,14 @@
#define FOREACH_BITLEN(func) func(256) func(384) func(512) #define FOREACH_BITLEN(func) func(256) func(384) func(512)
#define DEFINE_ALGO_METADATA(bitlen) \ #define DEFINE_ALGO_METADATA(bitlen) \
static algo_t sha##bitlen = { \ static rb_digest_metadata_t sha##bitlen = { \
1, \ RUBY_DIGEST_API_VERSION, \
SHA##bitlen##_DIGEST_LENGTH, \ SHA##bitlen##_DIGEST_LENGTH, \
SHA##bitlen##_BLOCK_LENGTH, \ SHA##bitlen##_BLOCK_LENGTH, \
sizeof(SHA##bitlen##_CTX), \ sizeof(SHA##bitlen##_CTX), \
(hash_init_func_t)SHA##bitlen##_Init, \ (rb_digest_hash_init_func_t)SHA##bitlen##_Init, \
(hash_update_func_t)SHA##bitlen##_Update, \ (rb_digest_hash_update_func_t)SHA##bitlen##_Update, \
(hash_finish_func_t)SHA##bitlen##_Finish, \ (rb_digest_hash_finish_func_t)SHA##bitlen##_Finish, \
}; };
FOREACH_BITLEN(DEFINE_ALGO_METADATA) FOREACH_BITLEN(DEFINE_ALGO_METADATA)