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