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