* ext/digest/digest.c, ext/digest/digest.h,

ext/digest/md5/md5init.c, ext/digest/rmd160/rmd160init.c,
  ext/digest/sha1/sha1init.c, ext/digest/sha2/sha2init.c:
  Introduce API versioning.

* ext/digest/digest.c, ext/digest/digest.h,
  ext/digest/md5/md5init.c, ext/digest/rmd160/rmd160init.c,
  ext/digest/sha1/sha1init.c, ext/digest/sha2/sha2init.c: Remove
  the constants DIGEST_LENGTH and BLOCK_LENGTH and turn them into
  instance methods digest_length() and block_length().  Class
  methods with the same names are also provided, which take extra
  parameters for a digest method.

* ext/digest/lib/digest/hmac.rb: Completely redesign the API which
  is similar to Perl's, now that Digest classes can take hashing
  parameters.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11166 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
knu 2006-10-13 23:23:18 +00:00
parent 694b2c7a29
commit d87fc2bdd4
8 changed files with 214 additions and 69 deletions

View File

@ -1,3 +1,22 @@
Sat Oct 14 08:15:42 2006 Akinori MUSHA <knu@iDaemons.org>
* ext/digest/digest.c, ext/digest/digest.h,
ext/digest/md5/md5init.c, ext/digest/rmd160/rmd160init.c,
ext/digest/sha1/sha1init.c, ext/digest/sha2/sha2init.c:
Introduce API versioning.
* ext/digest/digest.c, ext/digest/digest.h,
ext/digest/md5/md5init.c, ext/digest/rmd160/rmd160init.c,
ext/digest/sha1/sha1init.c, ext/digest/sha2/sha2init.c: Remove
the constants DIGEST_LENGTH and BLOCK_LENGTH and turn them into
instance methods digest_length() and block_length(). Class
methods with the same names are also provided, which take extra
parameters for a digest method.
* ext/digest/lib/digest/hmac.rb: Completely redesign the API which
is similar to Perl's, now that Digest classes can take hashing
parameters.
Sat Oct 14 05:54:05 2006 Akinori MUSHA <knu@iDaemons.org> Sat Oct 14 05:54:05 2006 Akinori MUSHA <knu@iDaemons.org>
* ext/digest/digest.c: Improve RDoc documentation further more. * ext/digest/digest.c: Improve RDoc documentation further more.

View File

@ -45,6 +45,13 @@ get_digest_base_metadata(VALUE klass)
Data_Get_Struct(obj, algo_t, algo); 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; return algo;
} }
@ -231,7 +238,8 @@ rb_digest_base_reset(VALUE self)
* *
* Updates the digest using a given _string_ and returns self. * Updates the digest using a given _string_ and returns self.
* *
* Implementation subclasses must redefine this method. * 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)
@ -260,7 +268,8 @@ rb_digest_base_update(VALUE self, VALUE str)
* *
* Calls update(string). * Calls update(string).
* *
* Subclasses need not redefine this method. * 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_lshift(VALUE self, VALUE str)
@ -407,6 +416,106 @@ rb_digest_base_equal(VALUE self, VALUE other)
return Qfalse; 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
rb_digest_base_digest_length(VALUE self)
{
algo_t *algo;
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);
}
/*
* 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
rb_digest_base_block_length(VALUE self)
{
algo_t *algo;
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);
}
void void
Init_digest(void) Init_digest(void)
{ {
@ -418,6 +527,9 @@ Init_digest(void)
rb_define_singleton_method(cDigest_Base, "digest", rb_digest_base_s_digest, -1); 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); 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);
rb_define_method(cDigest_Base, "initialize_copy", rb_digest_base_copy, 1); 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, "reset", rb_digest_base_reset, 0);
rb_define_method(cDigest_Base, "update", rb_digest_base_update, 1); rb_define_method(cDigest_Base, "update", rb_digest_base_update, 1);
@ -428,6 +540,9 @@ Init_digest(void)
rb_define_method(cDigest_Base, "inspect", rb_digest_base_inspect, 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, "==", rb_digest_base_equal, 1);
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);
id_metadata = rb_intern("metadata"); id_metadata = rb_intern("metadata");
id_new = rb_intern("new"); id_new = rb_intern("new");
id_initialize = rb_intern("initialize"); id_initialize = rb_intern("initialize");

View File

@ -20,7 +20,9 @@ typedef void (*hash_update_func_t)(void *, unsigned char *, size_t);
typedef void (*hash_finish_func_t)(void *, unsigned char *); typedef void (*hash_finish_func_t)(void *, unsigned char *);
typedef struct { typedef struct {
int api_version;
size_t digest_len; size_t digest_len;
size_t block_len;
size_t ctx_size; size_t ctx_size;
hash_init_func_t init_func; hash_init_func_t init_func;
hash_update_func_t update_func; hash_update_func_t update_func;

View File

@ -12,10 +12,10 @@
# require 'digest/hmac' # require 'digest/hmac'
# #
# # one-liner example # # one-liner example
# puts Digest::SHA1.hmac("hash key").hexdigest("data") # puts Digest::HMAC.hexdigest("data", "hash key", Digest::SHA1)
# #
# # rather longer one # # rather longer one
# hmac = Digest::RMD160.hmac("foo").new # hmac = Digest::HMAC.new("foo", Digest::RMD160)
# #
# buf = "" # buf = ""
# while stream.read(16384, buf) # while stream.read(16384, buf)
@ -39,52 +39,60 @@
require 'digest' require 'digest'
module Digest module Digest
class Base class HMAC < Digest::Base
def self.hmac(key) def initialize(key, digest_class, *digest_params)
algo = self @digest_class = digest_class.freeze
key = digest(key) if key.length > algo::BLOCK_LENGTH @digest_params = digest_params.freeze
@md = digest_class.new(*digest_params)
@tmp_md = @md.clone
(@digest_hmac_class_cache ||= {})[key] ||= Class.new(algo) { block_len = @md.block_length
const_set(:DIGEST_LENGTH, algo::DIGEST_LENGTH)
const_set(:BLOCK_LENGTH, algo::BLOCK_LENGTH)
const_set(:KEY, key)
@@algo = superclass
def initialize(text = nil) if key.length > block_len
ipad = Array.new(BLOCK_LENGTH).fill(0x36) key = @tmp_md.reset.update(key).digest
opad = Array.new(BLOCK_LENGTH).fill(0x5c) end
KEY.bytes.each_with_index { |c, i| ipad = Array.new(block_len).fill(0x36)
opad = Array.new(block_len).fill(0x5c)
key.bytes.each_with_index { |c, i|
ipad[i] ^= c ipad[i] ^= c
opad[i] ^= c opad[i] ^= c
} }
@ipad = ipad.inject('') { |s, c| s << c.chr } @key = key.freeze
@opad = opad.inject('') { |s, c| s << c.chr } @ipad = ipad.inject('') { |s, c| s << c.chr }.freeze
@opad = opad.inject('') { |s, c| s << c.chr }.freeze
end
@md = @@algo.new def initialize_copy(other)
@md = other.instance_eval { @md }
update(text) unless text.nil?
end end
def update(text) def update(text)
@md = @@algo.new(@opad + @@algo.digest(@ipad + text)) @md.reset.update(@opad + @tmp_md.reset.update(@ipad + text).digest)
self self
end end
def reset
@md.reset
end
def digest def digest
@md.digest @md.digest
end end
def self.inspect def digest_length
sprintf('#<%s.hmac(%s)>', @@algo.name, KEY.inspect); @md.digest_length
end
def block_length
@md.block_length
end end
def inspect def inspect
sprintf('#<%s.hmac(%s): %s>', @@algo.name, KEY.inspect, hexdigest()); sprintf('#<%s: key=%s, digest=%s: %s>', self.class.name, @key.inspect, @tmp_md.reset.inspect, hexdigest());
end
}
end end
end end
end end
@ -98,17 +106,21 @@ __END__
require 'test/unit' require 'test/unit'
module TM_HMAC module TM_HMAC
def test_s_hexdigest def hmac_new(key)
cases.each { |h| Digest::HMAC.new(key, *digest_spec())
hmac_class = digest_class.hmac(h[:key]) end
assert_equal(h[:hexdigest], hmac_class.hexdigest(h[:data])) def test_s_hexdigest
spec = digest_spec()
cases.each { |h|
assert_equal(h[:hexdigest], Digest::HMAC.hexdigest(h[:data], h[:key], *spec))
} }
end end
def test_hexdigest def test_hexdigest
cases.each { |h| cases.each { |h|
hmac = digest_class.hmac(h[:key]).new hmac = hmac_new(h[:key])
hmac.update(h[:data]) hmac.update(h[:data])
assert_equal(h[:hexdigest], hmac.hexdigest) assert_equal(h[:hexdigest], hmac.hexdigest)
@ -117,11 +129,12 @@ module TM_HMAC
def test_reset def test_reset
cases.each { |h| cases.each { |h|
hmac = digest_class.hmac(h[:key]).new hmac = hmac_new(h[:key])
hmac.update("test") hmac.update("test")
hmac.reset hmac.reset
hmac.update(h[:data]) hmac.update(h[:data])
p hmac
assert_equal(h[:hexdigest], hmac.hexdigest) assert_equal(h[:hexdigest], hmac.hexdigest)
} }
end end
@ -130,8 +143,8 @@ end
class TC_HMAC_MD5 < Test::Unit::TestCase class TC_HMAC_MD5 < Test::Unit::TestCase
include TM_HMAC include TM_HMAC
def digest_class def digest_spec
Digest::MD5 [Digest::MD5]
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
@ -173,8 +186,8 @@ end
class TC_HMAC_SHA1 < Test::Unit::TestCase class TC_HMAC_SHA1 < Test::Unit::TestCase
include TM_HMAC include TM_HMAC
def digest_class def digest_spec
Digest::SHA1 [Digest::SHA1]
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
@ -216,8 +229,8 @@ end
class TC_HMAC_RMD160 < Test::Unit::TestCase class TC_HMAC_RMD160 < Test::Unit::TestCase
include TM_HMAC include TM_HMAC
def digest_class def digest_spec
Digest::RMD160 [Digest::RMD160]
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

View File

@ -9,7 +9,9 @@
#endif #endif
static algo_t md5 = { static algo_t md5 = {
1,
MD5_DIGEST_LENGTH, MD5_DIGEST_LENGTH,
MD5_BLOCK_LENGTH,
sizeof(MD5_CTX), sizeof(MD5_CTX),
(hash_init_func_t)MD5_Init, (hash_init_func_t)MD5_Init,
(hash_update_func_t)MD5_Update, (hash_update_func_t)MD5_Update,
@ -33,9 +35,6 @@ Init_md5()
cDigest_MD5 = rb_define_class_under(mDigest, "MD5", cDigest_Base); cDigest_MD5 = rb_define_class_under(mDigest, "MD5", cDigest_Base);
rb_define_const(cDigest_MD5, "DIGEST_LENGTH", INT2NUM(MD5_DIGEST_LENGTH));
rb_define_const(cDigest_MD5, "BLOCK_LENGTH", INT2NUM(MD5_BLOCK_LENGTH));
rb_ivar_set(cDigest_MD5, rb_intern("metadata"), rb_ivar_set(cDigest_MD5, rb_intern("metadata"),
Data_Wrap_Struct(rb_cObject, 0, 0, &md5)); Data_Wrap_Struct(rb_cObject, 0, 0, &md5));
} }

View File

@ -9,7 +9,9 @@
#endif #endif
static algo_t rmd160 = { static algo_t rmd160 = {
1,
RMD160_DIGEST_LENGTH, RMD160_DIGEST_LENGTH,
RMD160_BLOCK_LENGTH,
sizeof(RMD160_CTX), sizeof(RMD160_CTX),
(hash_init_func_t)RMD160_Init, (hash_init_func_t)RMD160_Init,
(hash_update_func_t)RMD160_Update, (hash_update_func_t)RMD160_Update,
@ -34,9 +36,6 @@ Init_rmd160()
cDigest_RMD160 = rb_define_class_under(mDigest, "RMD160", cDigest_Base); cDigest_RMD160 = rb_define_class_under(mDigest, "RMD160", cDigest_Base);
rb_define_const(cDigest_RMD160, "DIGEST_LENGTH", INT2NUM(RMD160_DIGEST_LENGTH));
rb_define_const(cDigest_RMD160, "BLOCK_LENGTH", INT2NUM(RMD160_BLOCK_LENGTH));
id_metadata = rb_intern("metadata"); id_metadata = rb_intern("metadata");
rb_ivar_set(cDigest_RMD160, id_metadata, rb_ivar_set(cDigest_RMD160, id_metadata,

View File

@ -9,7 +9,9 @@
#endif #endif
static algo_t sha1 = { static algo_t sha1 = {
1,
SHA1_DIGEST_LENGTH, SHA1_DIGEST_LENGTH,
SHA1_BLOCK_LENGTH,
sizeof(SHA1_CTX), sizeof(SHA1_CTX),
(hash_init_func_t)SHA1_Init, (hash_init_func_t)SHA1_Init,
(hash_update_func_t)SHA1_Update, (hash_update_func_t)SHA1_Update,
@ -33,9 +35,6 @@ Init_sha1()
cDigest_SHA1 = rb_define_class_under(mDigest, "SHA1", cDigest_Base); cDigest_SHA1 = rb_define_class_under(mDigest, "SHA1", cDigest_Base);
rb_define_const(cDigest_SHA1, "DIGEST_LENGTH", INT2NUM(SHA1_DIGEST_LENGTH));
rb_define_const(cDigest_SHA1, "BLOCK_LENGTH", INT2NUM(SHA1_BLOCK_LENGTH));
rb_ivar_set(cDigest_SHA1, rb_intern("metadata"), rb_ivar_set(cDigest_SHA1, rb_intern("metadata"),
Data_Wrap_Struct(rb_cObject, 0, 0, &sha1)); Data_Wrap_Struct(rb_cObject, 0, 0, &sha1));
} }

View File

@ -8,7 +8,9 @@
#define DEFINE_ALGO_METADATA(bitlen) \ #define DEFINE_ALGO_METADATA(bitlen) \
static algo_t sha##bitlen = { \ static algo_t sha##bitlen = { \
1, \
SHA##bitlen##_DIGEST_LENGTH, \ SHA##bitlen##_DIGEST_LENGTH, \
SHA##bitlen##_BLOCK_LENGTH, \
sizeof(SHA##bitlen##_CTX), \ sizeof(SHA##bitlen##_CTX), \
(hash_init_func_t)SHA##bitlen##_Init, \ (hash_init_func_t)SHA##bitlen##_Init, \
(hash_update_func_t)SHA##bitlen##_Update, \ (hash_update_func_t)SHA##bitlen##_Update, \
@ -42,9 +44,6 @@ Init_sha2()
#define DEFINE_ALGO_CLASS(bitlen) \ #define DEFINE_ALGO_CLASS(bitlen) \
cDigest_SHA##bitlen = rb_define_class_under(mDigest, "SHA" #bitlen, cDigest_Base); \ cDigest_SHA##bitlen = rb_define_class_under(mDigest, "SHA" #bitlen, cDigest_Base); \
\
rb_define_const(cDigest_SHA##bitlen, "DIGEST_LENGTH", INT2NUM(SHA##bitlen##_DIGEST_LENGTH)); \
rb_define_const(cDigest_SHA##bitlen, "BLOCK_LENGTH", INT2NUM(SHA##bitlen##_BLOCK_LENGTH)); \
\ \
rb_ivar_set(cDigest_SHA##bitlen, id_metadata, \ rb_ivar_set(cDigest_SHA##bitlen, id_metadata, \
Data_Wrap_Struct(rb_cObject, 0, 0, &sha##bitlen)); Data_Wrap_Struct(rb_cObject, 0, 0, &sha##bitlen));