summaryrefslogtreecommitdiff
path: root/ext/digest
diff options
context:
space:
mode:
Diffstat (limited to 'ext/digest')
-rw-r--r--ext/digest/digest.c119
-rw-r--r--ext/digest/digest.h2
-rw-r--r--ext/digest/lib/digest/hmac.rb109
-rw-r--r--ext/digest/md5/md5init.c5
-rw-r--r--ext/digest/rmd160/rmd160init.c5
-rw-r--r--ext/digest/sha1/sha1init.c5
-rw-r--r--ext/digest/sha2/sha2init.c5
7 files changed, 188 insertions, 62 deletions
diff --git a/ext/digest/digest.c b/ext/digest/digest.c
index 9d56cb8..65a78c0 100644
--- a/ext/digest/digest.c
+++ b/ext/digest/digest.c
@@ -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");
diff --git a/ext/digest/digest.h b/ext/digest/digest.h
index ee3c680..f8c5dd3 100644
--- a/ext/digest/digest.h
+++ b/ext/digest/digest.h
@@ -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;
diff --git a/ext/digest/lib/digest/hmac.rb b/ext/digest/lib/digest/hmac.rb
index e6870f9..bc2f3bc 100644
--- a/ext/digest/lib/digest/hmac.rb
+++ b/ext/digest/lib/digest/hmac.rb
@@ -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 }
+ key.bytes.each_with_index { |c, i|
+ ipad[i] ^= c
+ opad[i] ^= c
+ }
- @md = @@algo.new
+ @key = key.freeze
+ @ipad = ipad.inject('') { |s, c| s << c.chr }.freeze
+ @opad = opad.inject('') { |s, c| s << c.chr }.freeze
+ end
- update(text) unless text.nil?
- end
+ def initialize_copy(other)
+ @md = other.instance_eval { @md }
+ end
- def update(text)
- @md = @@algo.new(@opad + @@algo.digest(@ipad + text))
+ def update(text)
+ @md.reset.update(@opad + @tmp_md.reset.update(@ipad + text).digest)
- self
- end
+ self
+ end
- def digest
- @md.digest
- end
+ def reset
+ @md.reset
+ end
- def self.inspect
- sprintf('#<%s.hmac(%s)>', @@algo.name, KEY.inspect);
- end
+ def digest
+ @md.digest
+ end
- def inspect
- sprintf('#<%s.hmac(%s): %s>', @@algo.name, KEY.inspect, hexdigest());
- 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 hmac_new(key)
+ Digest::HMAC.new(key, *digest_spec())
+ end
+
def test_s_hexdigest
- cases.each { |h|
- hmac_class = digest_class.hmac(h[:key])
+ spec = digest_spec()
- assert_equal(h[:hexdigest], hmac_class.hexdigest(h[:data]))
+ 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
diff --git a/ext/digest/md5/md5init.c b/ext/digest/md5/md5init.c
index f48b56d..1230c17 100644
--- a/ext/digest/md5/md5init.c
+++ b/ext/digest/md5/md5init.c
@@ -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));
}
diff --git a/ext/digest/rmd160/rmd160init.c b/ext/digest/rmd160/rmd160init.c
index f0a750e..a066763 100644
--- a/ext/digest/rmd160/rmd160init.c
+++ b/ext/digest/rmd160/rmd160init.c
@@ -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,
diff --git a/ext/digest/sha1/sha1init.c b/ext/digest/sha1/sha1init.c
index 60fb457..68c7637 100644
--- a/ext/digest/sha1/sha1init.c
+++ b/ext/digest/sha1/sha1init.c
@@ -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));
}
diff --git a/ext/digest/sha2/sha2init.c b/ext/digest/sha2/sha2init.c
index 7d145c1..ee18481 100644
--- a/ext/digest/sha2/sha2init.c
+++ b/ext/digest/sha2/sha2init.c
@@ -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, \
@@ -43,9 +45,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));