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:
parent
694b2c7a29
commit
d87fc2bdd4
8 changed files with 214 additions and 69 deletions
19
ChangeLog
19
ChangeLog
|
@ -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.
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in a new issue