1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

* 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>
* 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);
if (algo->api_version != 1) {
/*
* put conversion here if possible when API is updated
*/
rb_raise(rb_eRuntimeError, "Incompatible digest API version");
}
return algo;
}
@ -231,7 +238,8 @@ rb_digest_base_reset(VALUE 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
rb_digest_base_update(VALUE self, VALUE str)
@ -260,7 +268,8 @@ rb_digest_base_update(VALUE self, VALUE str)
*
* 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
rb_digest_base_lshift(VALUE self, VALUE str)
@ -407,6 +416,106 @@ rb_digest_base_equal(VALUE self, VALUE other)
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
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, "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, "reset", rb_digest_base_reset, 0);
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, "==", 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_new = rb_intern("new");
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 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;

View file

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

View file

@ -9,7 +9,9 @@
#endif
static algo_t md5 = {
1,
MD5_DIGEST_LENGTH,
MD5_BLOCK_LENGTH,
sizeof(MD5_CTX),
(hash_init_func_t)MD5_Init,
(hash_update_func_t)MD5_Update,
@ -33,9 +35,6 @@ Init_md5()
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"),
Data_Wrap_Struct(rb_cObject, 0, 0, &md5));
}

View file

@ -9,7 +9,9 @@
#endif
static algo_t rmd160 = {
1,
RMD160_DIGEST_LENGTH,
RMD160_BLOCK_LENGTH,
sizeof(RMD160_CTX),
(hash_init_func_t)RMD160_Init,
(hash_update_func_t)RMD160_Update,
@ -34,9 +36,6 @@ Init_rmd160()
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");
rb_ivar_set(cDigest_RMD160, id_metadata,

View file

@ -9,7 +9,9 @@
#endif
static algo_t sha1 = {
1,
SHA1_DIGEST_LENGTH,
SHA1_BLOCK_LENGTH,
sizeof(SHA1_CTX),
(hash_init_func_t)SHA1_Init,
(hash_update_func_t)SHA1_Update,
@ -33,9 +35,6 @@ Init_sha1()
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"),
Data_Wrap_Struct(rb_cObject, 0, 0, &sha1));
}

View file

@ -8,7 +8,9 @@
#define DEFINE_ALGO_METADATA(bitlen) \
static algo_t sha##bitlen = { \
1, \
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, \
@ -42,9 +44,6 @@ Init_sha2()
#define DEFINE_ALGO_CLASS(bitlen) \
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, \
Data_Wrap_Struct(rb_cObject, 0, 0, &sha##bitlen));