diff options
Diffstat (limited to 'ext/digest/digest.c')
| -rw-r--r-- | ext/digest/digest.c | 313 |
1 files changed, 260 insertions, 53 deletions
diff --git a/ext/digest/digest.c b/ext/digest/digest.c index 0b910e8c47..28f6022754 100644 --- a/ext/digest/digest.c +++ b/ext/digest/digest.c @@ -29,6 +29,69 @@ RUBY_EXTERN void Init_digest_base(void); * Document-module: Digest * * This module provides a framework for message digest libraries. + * + * You may want to look at OpenSSL::Digest as it supports more algorithms. + * + * A cryptographic hash function is a procedure that takes data and returns a + * fixed bit string: the hash value, also known as _digest_. Hash functions + * are also called one-way functions, it is easy to compute a digest from + * a message, but it is infeasible to generate a message from a digest. + * + * == Examples + * + * require 'digest' + * + * # Compute a complete digest + * Digest::SHA256.digest 'message' #=> "\xABS\n\x13\xE4Y..." + * + * sha256 = Digest::SHA256.new + * sha256.digest 'message' #=> "\xABS\n\x13\xE4Y..." + * + * # Other encoding formats + * Digest::SHA256.hexdigest 'message' #=> "ab530a13e459..." + * Digest::SHA256.base64digest 'message' #=> "q1MKE+RZFJgr..." + * + * # Compute digest by chunks + * md5 = Digest::MD5.new + * md5.update 'message1' + * md5 << 'message2' # << is an alias for update + * + * md5.hexdigest #=> "94af09c09bb9..." + * + * # Compute digest for a file + * sha256 = Digest::SHA256.file 'testfile' + * sha256.hexdigest + * + * Additionally digests can be encoded in "bubble babble" format as a sequence + * of consonants and vowels which is more recognizable and comparable than a + * hexadecimal digest. + * + * require 'digest/bubblebabble' + * + * Digest::SHA256.bubblebabble 'message' #=> "xopoh-fedac-fenyh-..." + * + * See the bubble babble specification at + * http://web.mit.edu/kenta/www/one/bubblebabble/spec/jrtrjwzi/draft-huima-01.txt. + * + * == Digest algorithms + * + * Different digest algorithms (or hash functions) are available: + * + * MD5:: + * See RFC 1321 The MD5 Message-Digest Algorithm + * RIPEMD-160:: + * As Digest::RMD160. + * See http://homes.esat.kuleuven.be/~bosselae/ripemd160.html. + * SHA1:: + * See FIPS 180 Secure Hash Standard. + * SHA2 family:: + * See FIPS 180 Secure Hash Standard which defines the following algorithms: + * * SHA512 + * * SHA384 + * * SHA256 + * + * The latest versions of the FIPS publications can be found here: + * http://csrc.nist.gov/publications/PubsFIPS.html. */ static VALUE @@ -36,7 +99,7 @@ hexencode_str_new(VALUE str_digest) { char *digest; size_t digest_len; - int i; + size_t i; VALUE str; char *p; static const char hex[] = { @@ -52,7 +115,7 @@ hexencode_str_new(VALUE str_digest) rb_raise(rb_eRuntimeError, "digest string too long"); } - str = rb_str_new(0, digest_len * 2); + str = rb_usascii_str_new(0, digest_len * 2); for (i = 0, p = RSTRING_PTR(str); i < digest_len; i++) { unsigned char byte = digest[i]; @@ -61,6 +124,8 @@ hexencode_str_new(VALUE str_digest) p[i + i + 1] = hex[byte & 0x0f]; } + RB_GC_GUARD(str_digest); + return str; } @@ -76,6 +141,8 @@ rb_digest_s_hexencode(VALUE klass, VALUE str) return hexencode_str_new(str); } +NORETURN(static void rb_digest_instance_method_unimpl(VALUE self, const char *method)); + /* * Document-module: Digest::Instance * @@ -83,6 +150,13 @@ rb_digest_s_hexencode(VALUE klass, VALUE str) * object to calculate message digest values. */ +static void +rb_digest_instance_method_unimpl(VALUE self, const char *method) +{ + rb_raise(rb_eRuntimeError, "%s does not implement %s()", + rb_obj_classname(self), method); +} + /* * call-seq: * digest_obj.update(string) -> digest_obj @@ -97,7 +171,9 @@ rb_digest_s_hexencode(VALUE klass, VALUE str) static VALUE rb_digest_instance_update(VALUE self, VALUE str) { - rb_raise(rb_eRuntimeError, "%s does not implement update()", RSTRING_PTR(rb_inspect(self))); + rb_digest_instance_method_unimpl(self, "update"); + + UNREACHABLE; } /* @@ -115,7 +191,9 @@ rb_digest_instance_update(VALUE self, VALUE str) static VALUE rb_digest_instance_finish(VALUE self) { - rb_raise(rb_eRuntimeError, "%s does not implement finish()", RSTRING_PTR(rb_inspect(self))); + rb_digest_instance_method_unimpl(self, "finish"); + + UNREACHABLE; } /* @@ -129,7 +207,9 @@ rb_digest_instance_finish(VALUE self) static VALUE rb_digest_instance_reset(VALUE self) { - rb_raise(rb_eRuntimeError, "%s does not implement reset()", RSTRING_PTR(rb_inspect(self))); + rb_digest_instance_method_unimpl(self, "reset"); + + UNREACHABLE; } /* @@ -170,10 +250,7 @@ rb_digest_instance_digest(int argc, VALUE *argv, VALUE self) 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); + value = rb_funcall(rb_obj_clone(self), id_finish, 0); } return value; @@ -218,10 +295,7 @@ rb_digest_instance_hexdigest(int argc, VALUE *argv, VALUE self) 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); + value = rb_funcall(rb_obj_clone(self), id_finish, 0); } return hexencode_str_new(value); @@ -231,8 +305,8 @@ rb_digest_instance_hexdigest(int argc, VALUE *argv, VALUE self) * call-seq: * digest_obj.hexdigest! -> string * - * Returns the resulting hash value and resets the digest to the - * initial state. + * Returns the resulting hash value in a hex-encoded form and resets + * the digest to the initial state. */ static VALUE rb_digest_instance_hexdigest_bang(VALUE self) @@ -266,7 +340,7 @@ rb_digest_instance_inspect(VALUE self) { VALUE str; size_t digest_len = 32; /* about this size at least */ - char *cname; + const char *cname; cname = rb_obj_classname(self); @@ -300,7 +374,8 @@ rb_digest_instance_equal(VALUE self, VALUE other) str2 = rb_digest_instance_digest(0, 0, other); } else { str1 = rb_digest_instance_to_s(self); - str2 = other; + str2 = rb_check_string_type(other); + if (NIL_P(str2)) return Qfalse; } /* never blindly assume that subclass methods return strings */ @@ -308,8 +383,8 @@ rb_digest_instance_equal(VALUE self, VALUE other) StringValue(str2); if (RSTRING_LEN(str1) == RSTRING_LEN(str2) && - rb_str_cmp(str1, str2) == 0) { - return Qtrue; + rb_str_cmp(str1, str2) == 0) { + return Qtrue; } return Qfalse; } @@ -331,7 +406,7 @@ rb_digest_instance_digest_length(VALUE self) /* never blindly assume that #digest() returns a string */ StringValue(digest); - return INT2NUM(RSTRING_LEN(digest)); + return LONG2NUM(RSTRING_LEN(digest)); } /* @@ -358,7 +433,9 @@ rb_digest_instance_length(VALUE self) static VALUE rb_digest_instance_block_length(VALUE self) { - rb_raise(rb_eRuntimeError, "%s does not implement block_length()", RSTRING_PTR(rb_inspect(self))); + rb_digest_instance_method_unimpl(self, "block_length"); + + UNREACHABLE; } /* @@ -409,7 +486,14 @@ rb_digest_class_s_digest(int argc, VALUE *argv, VALUE klass) static VALUE rb_digest_class_s_hexdigest(int argc, VALUE *argv, VALUE klass) { - return hexencode_str_new(rb_funcall2(klass, id_digest, argc, argv)); + return hexencode_str_new(rb_funcallv(klass, id_digest, argc, argv)); +} + +/* :nodoc: */ +static VALUE +rb_digest_class_init(VALUE self) +{ + return self; } /* @@ -417,29 +501,101 @@ rb_digest_class_s_hexdigest(int argc, VALUE *argv, VALUE klass) * * This abstract class provides a common interface to message digest * implementation classes written in C. + * + * ==Write a Digest subclass in C + * Digest::Base provides a common interface to message digest + * classes written in C. These classes must provide a struct + * of type rb_digest_metadata_t: + * typedef int (*rb_digest_hash_init_func_t)(void *); + * typedef void (*rb_digest_hash_update_func_t)(void *, unsigned char *, size_t); + * typedef int (*rb_digest_hash_finish_func_t)(void *, unsigned char *); + * + * typedef struct { + * int api_version; + * size_t digest_len; + * size_t block_len; + * size_t ctx_size; + * rb_digest_hash_init_func_t init_func; + * rb_digest_hash_update_func_t update_func; + * rb_digest_hash_finish_func_t finish_func; + * } rb_digest_metadata_t; + * + * This structure must be set as an instance variable named +metadata+ + * (without the +@+ in front of the name). By example: + * static const rb_digest_metadata_t sha1 = { + * RUBY_DIGEST_API_VERSION, + * SHA1_DIGEST_LENGTH, + * SHA1_BLOCK_LENGTH, + * sizeof(SHA1_CTX), + * (rb_digest_hash_init_func_t)SHA1_Init, + * (rb_digest_hash_update_func_t)SHA1_Update, + * (rb_digest_hash_finish_func_t)SHA1_Finish, + * }; + * + * + * rb_ivar_set(cDigest_SHA1, rb_intern("metadata"), + * rb_digest_make_metadata(&sha1)); */ +#ifdef DIGEST_USE_RB_EXT_RESOLVE_SYMBOL +static const rb_data_type_t metadata_type = { + "digest/metadata", + {0}, +}; + +RUBY_FUNC_EXPORTED VALUE +rb_digest_wrap_metadata(const rb_digest_metadata_t *meta) +{ + return rb_obj_freeze(TypedData_Wrap_Struct(0, &metadata_type, (void *)meta)); +} +#endif + +static rb_digest_metadata_t * +get_metadata_ptr(VALUE obj) +{ + rb_digest_metadata_t *algo; + +#ifdef DIGEST_USE_RB_EXT_RESOLVE_SYMBOL + if (!rb_typeddata_is_kind_of(obj, &metadata_type)) return 0; + algo = RTYPEDDATA_DATA(obj); +#else +# undef RUBY_UNTYPED_DATA_WARNING +# define RUBY_UNTYPED_DATA_WARNING 0 + Data_Get_Struct(obj, rb_digest_metadata_t, algo); +#endif + + return algo; +} + static rb_digest_metadata_t * get_digest_base_metadata(VALUE klass) { VALUE p; - VALUE obj; + VALUE obj = Qnil; rb_digest_metadata_t *algo; - for (p = klass; p; p = RCLASS(p)->super) { + for (p = klass; !NIL_P(p); p = rb_class_superclass(p)) { if (rb_ivar_defined(p, id_metadata)) { obj = rb_ivar_get(p, id_metadata); break; } } - if (!p) + if (NIL_P(p)) rb_raise(rb_eRuntimeError, "Digest::Base cannot be directly inherited in Ruby"); - Data_Get_Struct(obj, rb_digest_metadata_t, algo); + algo = get_metadata_ptr(obj); + if (!algo) { + if (p == klass) + rb_raise(rb_eTypeError, "%"PRIsVALUE"::metadata is not initialized properly", + klass); + else + rb_raise(rb_eTypeError, "%"PRIsVALUE"(%"PRIsVALUE")::metadata is not initialized properly", + klass, p); + } switch (algo->api_version) { - case 2: + case 3: break; /* @@ -453,6 +609,27 @@ get_digest_base_metadata(VALUE klass) return algo; } +static rb_digest_metadata_t * +get_digest_obj_metadata(VALUE obj) +{ + return get_digest_base_metadata(rb_obj_class(obj)); +} + +static const rb_data_type_t digest_type = { + "digest", + {0, RUBY_TYPED_DEFAULT_FREE, 0,}, + 0, 0, + (RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED), +}; + +static inline void +algo_init(const rb_digest_metadata_t *algo, void *pctx) +{ + if (algo->init_func(pctx) != 1) { + rb_raise(rb_eRuntimeError, "Digest initialization failed."); + } +} + static VALUE rb_digest_base_alloc(VALUE klass) { @@ -461,15 +638,14 @@ rb_digest_base_alloc(VALUE klass) void *pctx; if (klass == rb_cDigest_Base) { - rb_raise(rb_eNotImpError, "Digest::Base is an abstract class"); + 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); + obj = rb_data_typed_object_zalloc(klass, algo->ctx_size, &digest_type); + pctx = RTYPEDDATA_DATA(obj); + algo_init(algo, pctx); return obj; } @@ -485,44 +661,57 @@ rb_digest_base_copy(VALUE copy, VALUE obj) rb_check_frozen(copy); - algo = get_digest_base_metadata(rb_obj_class(copy)); + algo = get_digest_obj_metadata(copy); + if (algo != get_digest_obj_metadata(obj)) + rb_raise(rb_eTypeError, "different algorithms"); - Data_Get_Struct(obj, void, pctx1); - Data_Get_Struct(copy, void, pctx2); + TypedData_Get_Struct(obj, void, &digest_type, pctx1); + TypedData_Get_Struct(copy, void, &digest_type, pctx2); memcpy(pctx2, pctx1, algo->ctx_size); return copy; } -/* :nodoc: */ +/* + * call-seq: digest_base.reset -> digest_base + * + * Reset the digest to its initial state and return +self+. + */ static VALUE rb_digest_base_reset(VALUE self) { rb_digest_metadata_t *algo; void *pctx; - algo = get_digest_base_metadata(rb_obj_class(self)); + algo = get_digest_obj_metadata(self); - Data_Get_Struct(self, void, pctx); + TypedData_Get_Struct(self, void, &digest_type, pctx); - algo->init_func(pctx); + algo_init(algo, pctx); return self; } -/* :nodoc: */ +/* + * call-seq: + * digest_base.update(string) -> digest_base + * digest_base << string -> digest_base + * + * Update the digest using given _string_ and return +self+. + */ static VALUE rb_digest_base_update(VALUE self, VALUE str) { rb_digest_metadata_t *algo; void *pctx; - algo = get_digest_base_metadata(rb_obj_class(self)); + algo = get_digest_obj_metadata(self); - Data_Get_Struct(self, void, pctx); + TypedData_Get_Struct(self, void, &digest_type, pctx); StringValue(str); algo->update_func(pctx, (unsigned char *)RSTRING_PTR(str), RSTRING_LEN(str)); + RB_GC_GUARD(str); return self; } @@ -535,56 +724,75 @@ rb_digest_base_finish(VALUE self) void *pctx; VALUE str; - algo = get_digest_base_metadata(rb_obj_class(self)); + algo = get_digest_obj_metadata(self); - Data_Get_Struct(self, void, pctx); + TypedData_Get_Struct(self, void, &digest_type, pctx); str = rb_str_new(0, algo->digest_len); algo->finish_func(pctx, (unsigned char *)RSTRING_PTR(str)); /* avoid potential coredump caused by use of a finished context */ - algo->init_func(pctx); + algo_init(algo, pctx); return str; } -/* :nodoc: */ +/* + * call-seq: digest_base.digest_length -> Integer + * + * Return the length of the hash value in bytes. + */ static VALUE rb_digest_base_digest_length(VALUE self) { rb_digest_metadata_t *algo; - algo = get_digest_base_metadata(rb_obj_class(self)); + algo = get_digest_obj_metadata(self); - return INT2NUM(algo->digest_len); + return SIZET2NUM(algo->digest_len); } -/* :nodoc: */ +/* + * call-seq: digest_base.block_length -> Integer + * + * Return the block length of the digest in bytes. + */ static VALUE rb_digest_base_block_length(VALUE self) { rb_digest_metadata_t *algo; - algo = get_digest_base_metadata(rb_obj_class(self)); + algo = get_digest_obj_metadata(self); - return INT2NUM(algo->block_len); + return SIZET2NUM(algo->block_len); } void Init_digest(void) { +#undef rb_intern 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"); + id_metadata = rb_id_metadata(); + InitVM(digest); +} +void +InitVM_digest(void) +{ /* * module Digest */ rb_mDigest = rb_define_module("Digest"); +#ifdef HAVE_RB_EXT_RACTOR_SAFE + rb_ext_ractor_safe(true); +#endif + /* module functions */ rb_define_module_function(rb_mDigest, "hexencode", rb_digest_s_hexencode, 1); @@ -619,14 +827,13 @@ Init_digest(void) * class Digest::Class */ rb_cDigest_Class = rb_define_class_under(rb_mDigest, "Class", rb_cObject); + rb_define_method(rb_cDigest_Class, "initialize", rb_digest_class_init, 0); 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"); - /* class Digest::Base < Digest::Class */ rb_cDigest_Base = rb_define_class_under(rb_mDigest, "Base", rb_cDigest_Class); |
