00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "digest.h"
00017
00018 static VALUE rb_mDigest;
00019 static VALUE rb_mDigest_Instance;
00020 static VALUE rb_cDigest_Class;
00021 static VALUE rb_cDigest_Base;
00022
00023 static ID id_reset, id_update, id_finish, id_digest, id_hexdigest, id_digest_length;
00024 static ID id_metadata;
00025
00026 RUBY_EXTERN void Init_digest_base(void);
00027
00028
00029
00030
00031
00032
00033
00034 static VALUE
00035 hexencode_str_new(VALUE str_digest)
00036 {
00037 char *digest;
00038 size_t digest_len;
00039 size_t i;
00040 VALUE str;
00041 char *p;
00042 static const char hex[] = {
00043 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
00044 'a', 'b', 'c', 'd', 'e', 'f'
00045 };
00046
00047 StringValue(str_digest);
00048 digest = RSTRING_PTR(str_digest);
00049 digest_len = RSTRING_LEN(str_digest);
00050
00051 if (LONG_MAX / 2 < digest_len) {
00052 rb_raise(rb_eRuntimeError, "digest string too long");
00053 }
00054
00055 str = rb_str_new(0, digest_len * 2);
00056
00057 for (i = 0, p = RSTRING_PTR(str); i < digest_len; i++) {
00058 unsigned char byte = digest[i];
00059
00060 p[i + i] = hex[byte >> 4];
00061 p[i + i + 1] = hex[byte & 0x0f];
00062 }
00063
00064 return str;
00065 }
00066
00067
00068
00069
00070
00071
00072
00073 static VALUE
00074 rb_digest_s_hexencode(VALUE klass, VALUE str)
00075 {
00076 return hexencode_str_new(str);
00077 }
00078
00079 NORETURN(static void rb_digest_instance_method_unimpl(VALUE self, const char *method));
00080
00081
00082
00083
00084
00085
00086
00087
00088 static void
00089 rb_digest_instance_method_unimpl(VALUE self, const char *method)
00090 {
00091 rb_raise(rb_eRuntimeError, "%s does not implement %s()",
00092 rb_obj_classname(self), method);
00093 }
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106 static VALUE
00107 rb_digest_instance_update(VALUE self, VALUE str)
00108 {
00109 rb_digest_instance_method_unimpl(self, "update");
00110 }
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124 static VALUE
00125 rb_digest_instance_finish(VALUE self)
00126 {
00127 rb_digest_instance_method_unimpl(self, "finish");
00128 }
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138 static VALUE
00139 rb_digest_instance_reset(VALUE self)
00140 {
00141 rb_digest_instance_method_unimpl(self, "reset");
00142 }
00143
00144
00145
00146
00147
00148
00149
00150
00151 static VALUE
00152 rb_digest_instance_new(VALUE self)
00153 {
00154 VALUE clone = rb_obj_clone(self);
00155 rb_funcall(clone, id_reset, 0);
00156 return clone;
00157 }
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171 static VALUE
00172 rb_digest_instance_digest(int argc, VALUE *argv, VALUE self)
00173 {
00174 VALUE str, value;
00175
00176 if (rb_scan_args(argc, argv, "01", &str) > 0) {
00177 rb_funcall(self, id_reset, 0);
00178 rb_funcall(self, id_update, 1, str);
00179 value = rb_funcall(self, id_finish, 0);
00180 rb_funcall(self, id_reset, 0);
00181 } else {
00182 value = rb_funcall(rb_obj_clone(self), id_finish, 0);
00183 }
00184
00185 return value;
00186 }
00187
00188
00189
00190
00191
00192
00193
00194
00195 static VALUE
00196 rb_digest_instance_digest_bang(VALUE self)
00197 {
00198 VALUE value = rb_funcall(self, id_finish, 0);
00199 rb_funcall(self, id_reset, 0);
00200
00201 return value;
00202 }
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216 static VALUE
00217 rb_digest_instance_hexdigest(int argc, VALUE *argv, VALUE self)
00218 {
00219 VALUE str, value;
00220
00221 if (rb_scan_args(argc, argv, "01", &str) > 0) {
00222 rb_funcall(self, id_reset, 0);
00223 rb_funcall(self, id_update, 1, str);
00224 value = rb_funcall(self, id_finish, 0);
00225 rb_funcall(self, id_reset, 0);
00226 } else {
00227 value = rb_funcall(rb_obj_clone(self), id_finish, 0);
00228 }
00229
00230 return hexencode_str_new(value);
00231 }
00232
00233
00234
00235
00236
00237
00238
00239
00240 static VALUE
00241 rb_digest_instance_hexdigest_bang(VALUE self)
00242 {
00243 VALUE value = rb_funcall(self, id_finish, 0);
00244 rb_funcall(self, id_reset, 0);
00245
00246 return hexencode_str_new(value);
00247 }
00248
00249
00250
00251
00252
00253
00254
00255 static VALUE
00256 rb_digest_instance_to_s(VALUE self)
00257 {
00258 return rb_funcall(self, id_hexdigest, 0);
00259 }
00260
00261
00262
00263
00264
00265
00266
00267 static VALUE
00268 rb_digest_instance_inspect(VALUE self)
00269 {
00270 VALUE str;
00271 size_t digest_len = 32;
00272 const char *cname;
00273
00274 cname = rb_obj_classname(self);
00275
00276
00277 str = rb_str_buf_new(2 + strlen(cname) + 2 + digest_len * 2 + 1);
00278 rb_str_buf_cat2(str, "#<");
00279 rb_str_buf_cat2(str, cname);
00280 rb_str_buf_cat2(str, ": ");
00281 rb_str_buf_append(str, rb_digest_instance_hexdigest(0, 0, self));
00282 rb_str_buf_cat2(str, ">");
00283 return str;
00284 }
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296 static VALUE
00297 rb_digest_instance_equal(VALUE self, VALUE other)
00298 {
00299 VALUE str1, str2;
00300
00301 if (rb_obj_is_kind_of(other, rb_mDigest_Instance) == Qtrue) {
00302 str1 = rb_digest_instance_digest(0, 0, self);
00303 str2 = rb_digest_instance_digest(0, 0, other);
00304 } else {
00305 str1 = rb_digest_instance_to_s(self);
00306 str2 = other;
00307 }
00308
00309
00310 StringValue(str1);
00311 StringValue(str2);
00312
00313 if (RSTRING_LEN(str1) == RSTRING_LEN(str2) &&
00314 rb_str_cmp(str1, str2) == 0) {
00315 return Qtrue;
00316 }
00317 return Qfalse;
00318 }
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329 static VALUE
00330 rb_digest_instance_digest_length(VALUE self)
00331 {
00332
00333 VALUE digest = rb_digest_instance_digest(0, 0, self);
00334
00335
00336 StringValue(digest);
00337 return INT2NUM(RSTRING_LEN(digest));
00338 }
00339
00340
00341
00342
00343
00344
00345
00346
00347 static VALUE
00348 rb_digest_instance_length(VALUE self)
00349 {
00350 return rb_funcall(self, id_digest_length, 0);
00351 }
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361 static VALUE
00362 rb_digest_instance_block_length(VALUE self)
00363 {
00364 rb_digest_instance_method_unimpl(self, "block_length");
00365 }
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383 static VALUE
00384 rb_digest_class_s_digest(int argc, VALUE *argv, VALUE klass)
00385 {
00386 VALUE str;
00387 volatile VALUE obj;
00388
00389 if (argc < 1) {
00390 rb_raise(rb_eArgError, "no data given");
00391 }
00392
00393 str = *argv++;
00394 argc--;
00395
00396 StringValue(str);
00397
00398 obj = rb_obj_alloc(klass);
00399 rb_obj_call_init(obj, argc, argv);
00400
00401 return rb_funcall(obj, id_digest, 1, str);
00402 }
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412 static VALUE
00413 rb_digest_class_s_hexdigest(int argc, VALUE *argv, VALUE klass)
00414 {
00415 return hexencode_str_new(rb_funcall2(klass, id_digest, argc, argv));
00416 }
00417
00418
00419
00420
00421
00422
00423
00424
00425 static rb_digest_metadata_t *
00426 get_digest_base_metadata(VALUE klass)
00427 {
00428 VALUE p;
00429 VALUE obj;
00430 rb_digest_metadata_t *algo;
00431
00432 for (p = klass; p; p = RCLASS_SUPER(p)) {
00433 if (rb_ivar_defined(p, id_metadata)) {
00434 obj = rb_ivar_get(p, id_metadata);
00435 break;
00436 }
00437 }
00438
00439 if (!p)
00440 rb_raise(rb_eRuntimeError, "Digest::Base cannot be directly inherited in Ruby");
00441
00442 Data_Get_Struct(obj, rb_digest_metadata_t, algo);
00443
00444 switch (algo->api_version) {
00445 case 2:
00446 break;
00447
00448
00449
00450
00451
00452 default:
00453 rb_raise(rb_eRuntimeError, "Incompatible digest API version");
00454 }
00455
00456 return algo;
00457 }
00458
00459 static VALUE
00460 rb_digest_base_alloc(VALUE klass)
00461 {
00462 rb_digest_metadata_t *algo;
00463 VALUE obj;
00464 void *pctx;
00465
00466 if (klass == rb_cDigest_Base) {
00467 rb_raise(rb_eNotImpError, "Digest::Base is an abstract class");
00468 }
00469
00470 algo = get_digest_base_metadata(klass);
00471
00472 pctx = xmalloc(algo->ctx_size);
00473 algo->init_func(pctx);
00474
00475 obj = Data_Wrap_Struct(klass, 0, xfree, pctx);
00476
00477 return obj;
00478 }
00479
00480
00481 static VALUE
00482 rb_digest_base_copy(VALUE copy, VALUE obj)
00483 {
00484 rb_digest_metadata_t *algo;
00485 void *pctx1, *pctx2;
00486
00487 if (copy == obj) return copy;
00488
00489 rb_check_frozen(copy);
00490
00491 algo = get_digest_base_metadata(rb_obj_class(copy));
00492
00493 Data_Get_Struct(obj, void, pctx1);
00494 Data_Get_Struct(copy, void, pctx2);
00495 memcpy(pctx2, pctx1, algo->ctx_size);
00496
00497 return copy;
00498 }
00499
00500
00501 static VALUE
00502 rb_digest_base_reset(VALUE self)
00503 {
00504 rb_digest_metadata_t *algo;
00505 void *pctx;
00506
00507 algo = get_digest_base_metadata(rb_obj_class(self));
00508
00509 Data_Get_Struct(self, void, pctx);
00510
00511 algo->init_func(pctx);
00512
00513 return self;
00514 }
00515
00516
00517 static VALUE
00518 rb_digest_base_update(VALUE self, VALUE str)
00519 {
00520 rb_digest_metadata_t *algo;
00521 void *pctx;
00522
00523 algo = get_digest_base_metadata(rb_obj_class(self));
00524
00525 Data_Get_Struct(self, void, pctx);
00526
00527 StringValue(str);
00528 algo->update_func(pctx, (unsigned char *)RSTRING_PTR(str), RSTRING_LEN(str));
00529
00530 return self;
00531 }
00532
00533
00534 static VALUE
00535 rb_digest_base_finish(VALUE self)
00536 {
00537 rb_digest_metadata_t *algo;
00538 void *pctx;
00539 VALUE str;
00540
00541 algo = get_digest_base_metadata(rb_obj_class(self));
00542
00543 Data_Get_Struct(self, void, pctx);
00544
00545 str = rb_str_new(0, algo->digest_len);
00546 algo->finish_func(pctx, (unsigned char *)RSTRING_PTR(str));
00547
00548
00549 algo->init_func(pctx);
00550
00551 return str;
00552 }
00553
00554
00555 static VALUE
00556 rb_digest_base_digest_length(VALUE self)
00557 {
00558 rb_digest_metadata_t *algo;
00559
00560 algo = get_digest_base_metadata(rb_obj_class(self));
00561
00562 return INT2NUM(algo->digest_len);
00563 }
00564
00565
00566 static VALUE
00567 rb_digest_base_block_length(VALUE self)
00568 {
00569 rb_digest_metadata_t *algo;
00570
00571 algo = get_digest_base_metadata(rb_obj_class(self));
00572
00573 return INT2NUM(algo->block_len);
00574 }
00575
00576 void
00577 Init_digest(void)
00578 {
00579 id_reset = rb_intern("reset");
00580 id_update = rb_intern("update");
00581 id_finish = rb_intern("finish");
00582 id_digest = rb_intern("digest");
00583 id_hexdigest = rb_intern("hexdigest");
00584 id_digest_length = rb_intern("digest_length");
00585
00586
00587
00588
00589 rb_mDigest = rb_define_module("Digest");
00590
00591
00592 rb_define_module_function(rb_mDigest, "hexencode", rb_digest_s_hexencode, 1);
00593
00594
00595
00596
00597 rb_mDigest_Instance = rb_define_module_under(rb_mDigest, "Instance");
00598
00599
00600 rb_define_method(rb_mDigest_Instance, "update", rb_digest_instance_update, 1);
00601 rb_define_method(rb_mDigest_Instance, "<<", rb_digest_instance_update, 1);
00602 rb_define_private_method(rb_mDigest_Instance, "finish", rb_digest_instance_finish, 0);
00603 rb_define_method(rb_mDigest_Instance, "reset", rb_digest_instance_reset, 0);
00604 rb_define_method(rb_mDigest_Instance, "digest_length", rb_digest_instance_digest_length, 0);
00605 rb_define_method(rb_mDigest_Instance, "block_length", rb_digest_instance_block_length, 0);
00606
00607
00608 rb_define_method(rb_mDigest_Instance, "==", rb_digest_instance_equal, 1);
00609 rb_define_method(rb_mDigest_Instance, "inspect", rb_digest_instance_inspect, 0);
00610
00611
00612 rb_define_method(rb_mDigest_Instance, "new", rb_digest_instance_new, 0);
00613 rb_define_method(rb_mDigest_Instance, "digest", rb_digest_instance_digest, -1);
00614 rb_define_method(rb_mDigest_Instance, "digest!", rb_digest_instance_digest_bang, 0);
00615 rb_define_method(rb_mDigest_Instance, "hexdigest", rb_digest_instance_hexdigest, -1);
00616 rb_define_method(rb_mDigest_Instance, "hexdigest!", rb_digest_instance_hexdigest_bang, 0);
00617 rb_define_method(rb_mDigest_Instance, "to_s", rb_digest_instance_to_s, 0);
00618 rb_define_method(rb_mDigest_Instance, "length", rb_digest_instance_length, 0);
00619 rb_define_method(rb_mDigest_Instance, "size", rb_digest_instance_length, 0);
00620
00621
00622
00623
00624 rb_cDigest_Class = rb_define_class_under(rb_mDigest, "Class", rb_cObject);
00625 rb_include_module(rb_cDigest_Class, rb_mDigest_Instance);
00626
00627
00628 rb_define_singleton_method(rb_cDigest_Class, "digest", rb_digest_class_s_digest, -1);
00629 rb_define_singleton_method(rb_cDigest_Class, "hexdigest", rb_digest_class_s_hexdigest, -1);
00630
00631 id_metadata = rb_intern("metadata");
00632
00633
00634 rb_cDigest_Base = rb_define_class_under(rb_mDigest, "Base", rb_cDigest_Class);
00635
00636 rb_define_alloc_func(rb_cDigest_Base, rb_digest_base_alloc);
00637
00638 rb_define_method(rb_cDigest_Base, "initialize_copy", rb_digest_base_copy, 1);
00639 rb_define_method(rb_cDigest_Base, "reset", rb_digest_base_reset, 0);
00640 rb_define_method(rb_cDigest_Base, "update", rb_digest_base_update, 1);
00641 rb_define_method(rb_cDigest_Base, "<<", rb_digest_base_update, 1);
00642 rb_define_private_method(rb_cDigest_Base, "finish", rb_digest_base_finish, 0);
00643 rb_define_method(rb_cDigest_Base, "digest_length", rb_digest_base_digest_length, 0);
00644 rb_define_method(rb_cDigest_Base, "block_length", rb_digest_base_block_length, 0);
00645 }
00646