digest-kangarootwelve 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d66d4525e67457535b7ab5b8232c3c44955e147a
4
- data.tar.gz: 6aca633b39fef2afba603e14aa37095e10b1fb7f
3
+ metadata.gz: 30d451f9dc1fc4f3e7e87a42fc2c0d3c21339300
4
+ data.tar.gz: a8ac32cff2303adf783383fcfaace5fd6ab27e3e
5
5
  SHA512:
6
- metadata.gz: 4ba12fd8c227b4d5384456adc00b5326828e82c792dd967516b41b20b09885c62f19a133a8e2f3c028160ac822686e35db00a9a60ba8fc6eb967a8e0d5dc2eca
7
- data.tar.gz: 96fcb84f82a6025bbed1b30ed5e295ac8432e51d3fd3d4c047cb1a351f214516facd2d358022f396ca75972a10ad65fc44df19baf8ecafbc973cf4599c7fa3dd
6
+ metadata.gz: 32e3bd67264b5eb97b781f403da7501237cd63cc2e10ac2f1e214998143a7cf6b070ecf2ee416c6a443827fb5867b4c4c8569d56ec715e5dc561034165bddc4c
7
+ data.tar.gz: 7ff11f64ed95a86df337e4518416ce0b27178f545a5b90d9c1192eaf7b5bca5f5f95aa2feb6266877e83424f9f7d40e94880db5d9905b5738c6930e1420889a5
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # digest-kangarootwelve-ruby
2
2
 
3
- An implementation of KangarooTwelve for Ruby that works on top of Digest::Base.
3
+ The digest-kangarootwelve gem is an implementation of KangarooTwelve for Ruby
4
+ that works on top of Digest::Base.
4
5
 
5
6
  It allows hashing with different digest lengths and different customization
6
7
  strings.
@@ -29,51 +30,40 @@ Or install it yourself as:
29
30
 
30
31
  ## Example Usage
31
32
 
32
- require 'digest/kangarootwelve'
33
+ Digest::KangarooTwelve[32].digest("abc")
34
+ => "\xAB\x17O2\x8CU\xA5Q\v\v \x97\x91\xBF\x8B`\xE8\x01\xA7\xCF\xC2\xAAB\x04-\xCB\x8FT\x7F\xBE:}"
33
35
 
34
- klass = Digest::KangarooTwelve[32]
35
- => Digest::KangarooTwelve_32
36
-
37
- hash = klass.new
38
- => #<Digest::KangarooTwelve_32|32|65536||1ac2d450fc3b4205d19da7bfca1b37513c0803577ac7167f06fe2ce1f0ef39e5>
39
-
40
- hash.hexdigest("abc")
36
+ Digest::KangarooTwelve[32].hexdigest("abc")
41
37
  => "ab174f328c55a5510b0b209791bf8b60e801a7cfc2aa42042dcb8f547fbe3a7d"
42
38
 
43
- Digest::KangarooTwelve[32].new.hexdigest("abc")
39
+ Digest::KangarooTwelve[32].new.update("a").update("b").update("c").hexdigest
44
40
  => "ab174f328c55a5510b0b209791bf8b60e801a7cfc2aa42042dcb8f547fbe3a7d"
45
41
 
46
- Digest::KangarooTwelve.default
47
- => Digest::KangarooTwelve_64
48
-
49
- Digest::KangarooTwelve.implement(digest_length: 32, customization: "secret")
50
- => Digest::KangarooTwelve_32_65536_736563726574
51
-
52
42
  Digest::KangarooTwelve.implement(name: "SecretHash", digest_length: 32, customization: "secret")
53
43
  => Digest::SecretHash
54
44
 
55
45
  Digest::KangarooTwelve.implement(n: "SecretHash", d: 32, c: "secret")
56
46
  => Digest::SecretHash
57
47
 
58
- Digest::SecretHash.new.hexdigest("abc")
48
+ Digest::SecretHash.hexdigest("abc")
59
49
  => "dc1fd53f85402e2b34fa92bd87593dd9c3fe6cc49d9db6c05dc0cf26c6a7e03f"
60
50
 
61
- Digest::KangarooTwelve.implement(name: nil, digest_length: 48)
62
- => #<Class:0x000000015fe8e8>
51
+ Digest::KangarooTwelve.default
52
+ => Digest::KangarooTwelve_64
63
53
 
64
54
  ## Details
65
55
 
66
- For details on how to use the methods, please examine the comments in
67
- `ext/digest/kangarootwelve/ext.c`, or try to run
68
- `ri 'Digest::KangarooTwelve'` or
69
- `ri 'Digest::KangarooTwelve::<method_name>'`. I'll try to provide a more
70
- readable format of the documentation for the API soon.
71
-
72
- You can use the implementation classes produced by `[]`, `default` or
73
- `implement`, just like any other implementation class in `Digest`
74
- (e.g. `Digest::SHA1`, `Digest::SHA512`), since they are also derived from
75
- `Digest::Base`, and are programmed to work the way an implementation class
76
- that's based on the `Digest` framework should.
56
+ The implementation classes produced by `[]`, `default` or
57
+ `implement` can be used just like any other implementation class in `Digest`
58
+ (like `Digest::SHA1` or `Digest::SHA512`), since the implementation classes are
59
+ based on `Digest::Base`.
60
+
61
+ For details on how to use these methods, please examine the comments in
62
+ `ext/digest/kangarootwelve/ext.c`, or run `ri` with
63
+ `ri 'Digest::KangarooTwelve'`, or `ri 'Digest::KangarooTwelve::<method_name>'`.
64
+
65
+ You can also visit the automatically generated API documentation pages provided
66
+ by RubyDoc.info in http://www.rubydoc.info/gems/digest-kangarootwelve/.
77
67
 
78
68
  ## Contributing
79
69
 
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.authors = ["konsolebox"]
11
11
  spec.email = ["konsolebox@gmail.com"]
12
12
  spec.summary = "KangarooTwelve for Ruby"
13
- spec.description = "An implementation of KangarooTwelve for Ruby that works on top of Digest::Base."
13
+ spec.description = "A KangarooTwelve library that works on top of Digest::Base."
14
14
  spec.homepage = "https://github.com/konsolebox/digest-kangarootwelve-ruby"
15
15
  spec.license = "MIT"
16
16
 
@@ -23,14 +23,15 @@
23
23
  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
24
  */
25
25
 
26
- #include "KangarooTwelve.h"
27
26
  #include <ruby.h>
28
27
  #include <ruby/digest.h>
29
28
 
29
+ #include "KangarooTwelve.h"
30
+ #include "utils.h"
31
+
30
32
  #define KT_DEFAULT_DIGEST_LENGTH 64 /* 512 bits */
31
- #define KT_DEFAULT_BLOCK_LENGTH 8192 * 8 /* chunkSize * Parallellism */
33
+ #define KT_BLOCK_LENGTH 8192 /* chunkSize */
32
34
  #define KT_MIN_DIGEST_LENGTH 1
33
- #define KT_MIN_BLOCK_LENGTH 64
34
35
 
35
36
  #define KT_DIGEST_API_VERSION_IS_SUPPORTED(version) (version == 3)
36
37
 
@@ -44,7 +45,9 @@ static ID _id_auto;
44
45
  static ID _id_block_length;
45
46
  static ID _id_b;
46
47
  static ID _id_customization;
48
+ static ID _id_customization_hex;
47
49
  static ID _id_c;
50
+ static ID _id_ch;
48
51
  static ID _id_default;
49
52
  static ID _id_digest_length;
50
53
  static ID _id_d;
@@ -55,11 +58,10 @@ static ID _id_new;
55
58
  static ID _id_n;
56
59
  static ID _id_unpack;
57
60
 
58
- static VALUE _module_Digest;
59
- static VALUE _class_Digest_Base;
60
- static VALUE _module_Digest_KangarooTwelve;
61
- static VALUE _class_Digest_KangarooTwelve_Impl;
62
- static VALUE _class_Digest_KangarooTwelve_Metadata;
61
+ static VALUE _Digest;
62
+ static VALUE _Digest_KangarooTwelve;
63
+ static VALUE _Digest_KangarooTwelve_Impl;
64
+ static VALUE _Digest_KangarooTwelve_Metadata;
63
65
 
64
66
  typedef struct {
65
67
  KangarooTwelve_Instance instance;
@@ -75,10 +77,25 @@ static void check_digest_length(int digest_length)
75
77
  rb_raise(rb_eArgError, "Digest length lesser than minimum (%d): %d", KT_MIN_DIGEST_LENGTH, digest_length);
76
78
  }
77
79
 
78
- static void check_block_length(int block_length)
80
+ static VALUE hex_encode_str(VALUE str)
79
81
  {
80
- if (!(block_length >= KT_MIN_BLOCK_LENGTH))
81
- rb_raise(rb_eArgError, "Block length lesser than minimum (%d): %d", KT_MIN_BLOCK_LENGTH, block_length);
82
+ int len = RSTRING_LEN(str);
83
+ VALUE hex = rb_str_new(0, len * 2);
84
+ hex_encode_str_implied(RSTRING_PTR(str), len, RSTRING_PTR(hex));
85
+ return hex;
86
+ }
87
+
88
+ static VALUE hex_decode_str(VALUE str)
89
+ {
90
+ int len = RSTRING_LEN(str);
91
+ VALUE decoded = rb_str_new(0, calc_hex_decoded_str_length(len));
92
+
93
+ if (! hex_decode_str_implied(RSTRING_PTR(str), len, RSTRING_PTR(decoded))) {
94
+ VALUE inspect = rb_inspect(str);
95
+ rb_raise(rb_eArgError, "Failed to decode hex string %s.", RSTRING_PTR(inspect));
96
+ }
97
+
98
+ return decoded;
82
99
  }
83
100
 
84
101
  static int kangarootwelve_init(void *ctx)
@@ -88,7 +105,7 @@ static int kangarootwelve_init(void *ctx)
88
105
 
89
106
  VALUE klass_or_instance = rb_current_receiver();
90
107
 
91
- if (TYPE(klass_or_instance) == T_CLASS && klass_or_instance == _class_Digest_KangarooTwelve_Impl)
108
+ if (TYPE(klass_or_instance) == T_CLASS && klass_or_instance == _Digest_KangarooTwelve_Impl)
92
109
  rb_raise(rb_eStandardError, "Digest::KangarooTwelve::Impl is a base class and cannot be instantiated.");
93
110
 
94
111
  VALUE digest_length = rb_funcall(klass_or_instance, _id_digest_length, 0);
@@ -143,17 +160,12 @@ static int kangarootwelve_finish(void *ctx, unsigned char *data)
143
160
  }
144
161
  }
145
162
 
146
- static VALUE do_nothing(VALUE self)
147
- {
148
- return Qnil;
149
- }
150
-
151
- static VALUE implement(VALUE name, VALUE digest_length, VALUE block_length, VALUE customization)
163
+ static VALUE implement(VALUE name, VALUE digest_length, VALUE customization)
152
164
  {
153
165
  if (!KT_DIGEST_API_VERSION_IS_SUPPORTED(RUBY_DIGEST_API_VERSION))
154
166
  rb_raise(rb_eRuntimeError, "Digest API version is not supported.");
155
167
 
156
- int digest_length_int, block_length_int;
168
+ int digest_length_int;
157
169
 
158
170
  switch (TYPE(digest_length)) {
159
171
  case T_NIL:
@@ -167,18 +179,6 @@ static VALUE implement(VALUE name, VALUE digest_length, VALUE block_length, VALU
167
179
  rb_raise(rb_eTypeError, "Invalid value type for digest length.");
168
180
  }
169
181
 
170
- switch (TYPE(block_length)) {
171
- case T_NIL:
172
- block_length_int = KT_DEFAULT_BLOCK_LENGTH;
173
- break;
174
- case T_FIXNUM:
175
- block_length_int = FIX2INT(block_length);
176
- check_block_length(block_length_int);
177
- break;
178
- default:
179
- rb_raise(rb_eTypeError, "Invalid value type for block length.");
180
- }
181
-
182
182
  switch (TYPE(customization)) {
183
183
  case T_NIL:
184
184
  case T_STRING:
@@ -198,12 +198,7 @@ static VALUE implement(VALUE name, VALUE digest_length, VALUE block_length, VALU
198
198
 
199
199
  if (id == _id_auto) {
200
200
  if (customization != Qnil) {
201
- VALUE customization_hex_in_array = rb_funcall(customization, _id_unpack, 1, rb_str_new_literal("H*"));
202
- VALUE customization_hex = rb_ary_pop(customization_hex_in_array);
203
- impl_class_name = rb_sprintf("KangarooTwelve_%d_%d_%s", digest_length_int, block_length_int,
204
- StringValueCStr(customization_hex));
205
- } else if (block_length_int != KT_DEFAULT_BLOCK_LENGTH) {
206
- impl_class_name = rb_sprintf("KangarooTwelve_%d_%d", digest_length_int, block_length_int);
201
+ impl_class_name = hex_encode_str(customization);
207
202
  } else {
208
203
  impl_class_name = rb_sprintf("KangarooTwelve_%d", digest_length_int);
209
204
  }
@@ -231,10 +226,10 @@ static VALUE implement(VALUE name, VALUE digest_length, VALUE block_length, VALU
231
226
  VALUE impl_class;
232
227
 
233
228
  if (impl_class_name == Qnil) {
234
- impl_class = rb_funcall(rb_cClass, _id_new, 1, _class_Digest_KangarooTwelve_Impl);
229
+ impl_class = rb_funcall(rb_cClass, _id_new, 1, _Digest_KangarooTwelve_Impl);
235
230
  } else {
236
- if (rb_const_defined(_module_Digest, impl_class_name_id)) {
237
- impl_class = rb_const_get(_module_Digest, impl_class_name_id);
231
+ if (rb_const_defined(_Digest, impl_class_name_id)) {
232
+ impl_class = rb_const_get(_Digest, impl_class_name_id);
238
233
 
239
234
  if (TYPE(impl_class) != T_CLASS) {
240
235
  rb_raise(rb_eTypeError,
@@ -242,7 +237,7 @@ static VALUE implement(VALUE name, VALUE digest_length, VALUE block_length, VALU
242
237
  StringValueCStr(impl_class_name));
243
238
  }
244
239
 
245
- if (rb_class_superclass(impl_class) != _class_Digest_KangarooTwelve_Impl) {
240
+ if (rb_class_superclass(impl_class) != _Digest_KangarooTwelve_Impl) {
246
241
  rb_raise(rb_eTypeError,
247
242
  "Digest::%s was already defined but not derived from Digest::KangarooTwelve::Impl.",
248
243
  StringValueCStr(impl_class_name));
@@ -266,37 +261,19 @@ static VALUE implement(VALUE name, VALUE digest_length, VALUE block_length, VALU
266
261
  digest_length_int);
267
262
  }
268
263
 
269
- VALUE prev_block_length = rb_ivar_get(impl_class, _id_block_length);
270
-
271
- if (TYPE(prev_block_length) != T_FIXNUM) {
272
- rb_raise(rb_eRuntimeError,
273
- "Previous definition of Digest::%s has invalid block length value type.",
274
- StringValueCStr(impl_class_name));
275
- }
276
-
277
- int prev_block_length_int = FIX2INT(prev_block_length);
278
-
279
- if (prev_block_length_int != block_length_int) {
280
- rb_raise(rb_eTypeError,
281
- "Digest::%s was already defined but has different block length (%d instead of %d).",
282
- StringValueCStr(impl_class_name),
283
- prev_block_length_int,
284
- block_length_int);
285
- }
286
-
287
264
  return impl_class;
288
265
  }
289
266
 
290
- impl_class = rb_define_class_id_under(_module_Digest, impl_class_name_id, _class_Digest_KangarooTwelve_Impl);
267
+ impl_class = rb_define_class_id_under(_Digest, impl_class_name_id, _Digest_KangarooTwelve_Impl);
291
268
  }
292
269
 
293
270
  VALUE metadata_obj;
294
271
  rb_digest_metadata_t *metadata;
295
- metadata_obj = Data_Make_Struct(_class_Digest_KangarooTwelve_Metadata, rb_digest_metadata_t, 0, -1, metadata);
272
+ metadata_obj = Data_Make_Struct(_Digest_KangarooTwelve_Metadata, rb_digest_metadata_t, 0, -1, metadata);
296
273
 
297
274
  metadata->api_version = RUBY_DIGEST_API_VERSION;
298
275
  metadata->digest_len = digest_length_int;
299
- metadata->block_len = block_length_int;
276
+ metadata->block_len = KT_BLOCK_LENGTH;
300
277
  metadata->ctx_size = sizeof(KT_CONTEXT);
301
278
  metadata->init_func = kangarootwelve_init;
302
279
  metadata->update_func = kangarootwelve_update;
@@ -304,7 +281,7 @@ static VALUE implement(VALUE name, VALUE digest_length, VALUE block_length, VALU
304
281
 
305
282
  rb_ivar_set(impl_class, _id_metadata, metadata_obj);
306
283
  rb_ivar_set(impl_class, _id_digest_length, INT2FIX(digest_length_int));
307
- rb_ivar_set(impl_class, _id_block_length, INT2FIX(block_length_int));
284
+ rb_ivar_set(impl_class, _id_block_length, INT2FIX(KT_BLOCK_LENGTH));
308
285
  rb_ivar_set(impl_class, _id_customization, customization);
309
286
 
310
287
  return impl_class;
@@ -314,16 +291,25 @@ static VALUE implement(VALUE name, VALUE digest_length, VALUE block_length, VALU
314
291
  * Document-module: Digest::KangarooTwelve
315
292
  *
316
293
  * The Digest::KangarooTwelve module is the main component of the KangarooTwelve
317
- * extension.
294
+ * extension for Ruby.
295
+ *
296
+ * It contains singleton methods that can be used to create implementation
297
+ * classes. These implementation classes can then be used to create instances
298
+ * for producing hashes.
299
+ *
300
+ * Example:
318
301
  *
319
- * To create a hashing object, one must use one of the singleton methods in this
320
- * module to produce an implementation class, which can then be used to create
321
- * the hashing object.
302
+ * <tt>Digest::KangarooTwelve[32].new.update("one").update("two").digest</tt>
322
303
  *
323
- * An example to this is <code>hash = Digest::KangarooTwelve[32].new</code>.
304
+ * The implementation classes can also be used to directly produce hashes.
324
305
  *
325
- * The produced implementation class and the hash object can be used just like
326
- * any other implementation classes and instances in Digest.
306
+ * Example:
307
+ *
308
+ * <tt>Digest::KangarooTwelve[32].hexdigest("1234")</tt>
309
+ *
310
+ * The produced implementation classes and their instances can be used just like
311
+ * any other classes and instances in the Digest namespace that's based on
312
+ * Digest::Base.
327
313
  */
328
314
 
329
315
  /*
@@ -332,12 +318,12 @@ static VALUE implement(VALUE name, VALUE digest_length, VALUE block_length, VALU
332
318
  * Returns the default implementation class which has a digest length of 64
333
319
  * bytes, and doesn't have a customization string.
334
320
  */
335
- VALUE rbx_Digest_KangarooTwelve_singleton_default(VALUE self)
321
+ static VALUE _Digest_KangarooTwelve_singleton_default(VALUE self)
336
322
  {
337
323
  VALUE default_ = rb_ivar_get(self, _id_default);
338
324
 
339
325
  if (NIL_P(default_)) {
340
- default_ = implement(ID2SYM(_id_auto), INT2FIX(KT_DEFAULT_DIGEST_LENGTH), Qnil, Qnil);
326
+ default_ = implement(ID2SYM(_id_auto), INT2FIX(KT_DEFAULT_DIGEST_LENGTH), Qnil);
341
327
  rb_ivar_set(self, _id_default, default_);
342
328
  }
343
329
 
@@ -360,15 +346,12 @@ VALUE rbx_Digest_KangarooTwelve_singleton_default(VALUE self)
360
346
  * module.
361
347
  *
362
348
  * The default value for this option is +:auto+, and it implies automatic
363
- * generation of the name. The generated name is in the format of
364
- * Digest::KangarooTwelve_<digest_length> if neither a customization string
365
- * nor a custom block length is specified.
349
+ * generation of the name. The generated name is in the form of
350
+ * Digest::KangarooTwelve_<digest_length> if a customization string is not
351
+ * specified.
366
352
  *
367
- * If a customization string is specified, the format would become
368
- * Digest::KangarooTwelve_<digest_length>_<block_length>_<cust_str_hex>.
369
- *
370
- * If no customization string is specified but a custom block is, the format
371
- * would be Digest::KangarooTwelve_<digest_length>_<block_length>.
353
+ * If a customization string is specified, the format would be
354
+ * Digest::KangarooTwelve_<digest_length>_<cust_str_hex>.
372
355
  *
373
356
  * Specifying a string would make the method produce Digest::<string>.
374
357
  *
@@ -381,30 +364,25 @@ VALUE rbx_Digest_KangarooTwelve_singleton_default(VALUE self)
381
364
  *
382
365
  * See Digest::KangarooTwelve::DEFAULT_DIGEST_LENGTH for the default value.
383
366
  *
384
- * :b, :block_length ::
385
- * Specifies a custom block length. This doesn't affect the digest results
386
- * and is mostly just used for tweaking performance or memory usage. Examine
387
- * the update function in KangarooTwelve.c to know if this is needed to be
388
- * configured.
389
- *
390
- * See Digest::KangarooTwelve::DEFAULT_BLOCK_LENGTH for the default value.
391
- *
392
367
  * :c, :customization ::
393
368
  * Specifies the customization string. Adding a customization string changes
394
369
  * the resulting digest of every input.
395
370
  *
396
- * Calling the method with no argument would be the same as calling the
371
+ * :ch, :customization_hex ::
372
+ * Specifies the customization string in hex mode.
373
+ *
374
+ * Calling the method with no argument is the same as calling the
397
375
  * Digest::KangarooTwelve::default method.
398
376
  */
399
- VALUE rbx_Digest_KangarooTwelve_singleton_implement(int argc, VALUE *argv, VALUE self)
377
+ static VALUE _Digest_KangarooTwelve_singleton_implement(int argc, VALUE *argv, VALUE self)
400
378
  {
401
- VALUE opts, name, digest_length, block_length, customization;
379
+ VALUE opts, name, digest_length, customization;
402
380
 
403
381
  rb_scan_args(argc, argv, "0:", &opts);
404
382
 
405
383
  if (NIL_P(opts)) {
406
384
  name = ID2SYM(_id_auto);
407
- digest_length = block_length = customization = Qnil;
385
+ digest_length = customization = Qnil;
408
386
  } else {
409
387
  name = rb_hash_lookup2(opts, ID2SYM(_id_n), Qundef);
410
388
 
@@ -416,18 +394,29 @@ VALUE rbx_Digest_KangarooTwelve_singleton_implement(int argc, VALUE *argv, VALUE
416
394
  if (digest_length == Qundef)
417
395
  digest_length = rb_hash_lookup2(opts, ID2SYM(_id_digest_length), Qnil);
418
396
 
419
- block_length = rb_hash_lookup2(opts, ID2SYM(_id_b), Qundef);
420
-
421
- if (block_length == Qundef)
422
- block_length = rb_hash_lookup2(opts, ID2SYM(_id_block_length), Qnil);
423
-
424
397
  customization = rb_hash_lookup2(opts, ID2SYM(_id_c), Qundef);
425
398
 
426
399
  if (customization == Qundef)
427
- customization = rb_hash_lookup2(opts, ID2SYM(_id_customization), Qnil);
400
+ customization = rb_hash_lookup2(opts, ID2SYM(_id_customization), Qundef);
401
+
402
+ if (customization == Qundef) {
403
+ VALUE customization_hex = rb_hash_lookup2(opts, ID2SYM(_id_customization_hex), Qundef);
404
+
405
+ if (customization_hex == Qundef)
406
+ customization_hex = rb_hash_lookup2(opts, ID2SYM(_id_ch), Qundef);
407
+
408
+ if (customization_hex == Qundef) {
409
+ customization = Qnil;
410
+ } else {
411
+ if (TYPE(customization_hex) != T_STRING)
412
+ rb_raise(rb_eTypeError, "Customization argument not a string.");
413
+
414
+ customization = hex_decode_str(customization_hex);
415
+ }
416
+ }
428
417
  }
429
418
 
430
- return implement(name, digest_length, block_length, customization);
419
+ return implement(name, digest_length, customization);
431
420
  }
432
421
 
433
422
  /*
@@ -440,9 +429,9 @@ VALUE rbx_Digest_KangarooTwelve_singleton_implement(int argc, VALUE *argv, VALUE
440
429
  * Digest::KangarooTwelve_<digest_length>, and can be directly referenced after
441
430
  * this method is called.
442
431
  */
443
- VALUE rbx_Digest_KangarooTwelve_singleton_implement_simple(VALUE self, VALUE digest_length)
432
+ static VALUE _Digest_KangarooTwelve_singleton_implement_simple(VALUE self, VALUE digest_length)
444
433
  {
445
- return implement(ID2SYM(_id_auto), digest_length, Qnil, Qnil);
434
+ return implement(ID2SYM(_id_auto), digest_length, Qnil);
446
435
  }
447
436
 
448
437
  /*
@@ -459,12 +448,12 @@ VALUE rbx_Digest_KangarooTwelve_singleton_implement_simple(VALUE self, VALUE dig
459
448
  *
460
449
  * Creates a new object instance of the implementation class.
461
450
  */
462
- VALUE rbx_Digest_KangarooTwelve_Impl_singleton_new(VALUE self)
451
+ static VALUE _Digest_KangarooTwelve_Impl_singleton_new(VALUE self)
463
452
  {
464
- if (self == _class_Digest_KangarooTwelve_Impl)
453
+ if (self == _Digest_KangarooTwelve_Impl)
465
454
  rb_raise(rb_eRuntimeError, "Digest::KangarooTwelve::Impl is an abstract class.");
466
455
 
467
- if (rb_obj_class(rb_ivar_get(self, _id_metadata)) != _class_Digest_KangarooTwelve_Metadata)
456
+ if (rb_obj_class(rb_ivar_get(self, _id_metadata)) != _Digest_KangarooTwelve_Metadata)
468
457
  rb_raise(rb_eRuntimeError, "Metadata not set or invalid. Please do not manually inherit KangarooTwelve.");
469
458
 
470
459
  return rb_call_super(0, 0);
@@ -475,9 +464,9 @@ VALUE rbx_Digest_KangarooTwelve_Impl_singleton_new(VALUE self)
475
464
  *
476
465
  * Returns configured digest length of the implementation class.
477
466
  */
478
- VALUE rbx_Digest_KangarooTwelve_Impl_singleton_digest_length(VALUE self)
467
+ static VALUE _Digest_KangarooTwelve_Impl_singleton_digest_length(VALUE self)
479
468
  {
480
- if (self == _class_Digest_KangarooTwelve_Impl)
469
+ if (self == _Digest_KangarooTwelve_Impl)
481
470
  rb_raise(rb_eRuntimeError, "Digest::KangarooTwelve::Impl is an abstract class.");
482
471
 
483
472
  return rb_ivar_get(self, _id_digest_length);
@@ -486,14 +475,11 @@ VALUE rbx_Digest_KangarooTwelve_Impl_singleton_digest_length(VALUE self)
486
475
  /*
487
476
  * call-seq: block_length -> int
488
477
  *
489
- * Returns configured block length of the implementation class.
478
+ * Returns 8192.
490
479
  */
491
- VALUE rbx_Digest_KangarooTwelve_Impl_singleton_block_length(VALUE self)
480
+ static VALUE _Digest_KangarooTwelve_Impl_singleton_block_length(VALUE self)
492
481
  {
493
- if (self == _class_Digest_KangarooTwelve_Impl)
494
- rb_raise(rb_eRuntimeError, "Digest::KangarooTwelve::Impl is an abstract class.");
495
-
496
- return rb_ivar_get(self, _id_block_length);
482
+ return INT2FIX(KT_BLOCK_LENGTH);
497
483
  }
498
484
 
499
485
  /*
@@ -501,20 +487,34 @@ VALUE rbx_Digest_KangarooTwelve_Impl_singleton_block_length(VALUE self)
501
487
  *
502
488
  * Returns configured customization string of the implementation class.
503
489
  */
504
- VALUE rbx_Digest_KangarooTwelve_Impl_singleton_customization(VALUE self)
490
+ static VALUE _Digest_KangarooTwelve_Impl_singleton_customization(VALUE self)
505
491
  {
506
- if (self == _class_Digest_KangarooTwelve_Impl)
492
+ if (self == _Digest_KangarooTwelve_Impl)
507
493
  rb_raise(rb_eRuntimeError, "Digest::KangarooTwelve::Impl is an abstract class.");
508
494
 
509
495
  return rb_ivar_get(self, _id_customization);
510
496
  }
511
497
 
498
+ /*
499
+ * call-seq: customization_hex -> string or nil
500
+ *
501
+ * Returns configured customization string of the implementation class in hex.
502
+ */
503
+ static VALUE _Digest_KangarooTwelve_Impl_singleton_customization_hex(VALUE self)
504
+ {
505
+ if (self == _Digest_KangarooTwelve_Impl)
506
+ rb_raise(rb_eRuntimeError, "Digest::KangarooTwelve::Impl is an abstract class.");
507
+
508
+ VALUE customization = rb_ivar_get(self, _id_customization);
509
+ return hex_encode_str(customization);
510
+ }
511
+
512
512
  /*
513
513
  * call-seq: customization -> string or nil
514
514
  *
515
515
  * Returns configured customization string of the implementation object.
516
516
  */
517
- VALUE rbx_Digest_KangarooTwelve_Impl_customization(VALUE self)
517
+ static VALUE _Digest_KangarooTwelve_Impl_customization(VALUE self)
518
518
  {
519
519
  VALUE customization;
520
520
 
@@ -529,12 +529,23 @@ VALUE rbx_Digest_KangarooTwelve_Impl_customization(VALUE self)
529
529
  return customization;
530
530
  }
531
531
 
532
+ /*
533
+ * call-seq: customization_hex -> string or nil
534
+ *
535
+ * Returns configured customization string of the implementation object in hex.
536
+ */
537
+ static VALUE _Digest_KangarooTwelve_Impl_customization_hex(VALUE self)
538
+ {
539
+ VALUE customization = rb_funcall(self, _id_customization, 0);
540
+ return hex_encode_str(customization);
541
+ }
542
+
532
543
  /*
533
544
  * call-seq: inspect -> string
534
545
  *
535
- * Returns a string in the format of #<implementation_class_name|digest_length|block_length|customization_string|digest>
546
+ * Returns a string in the form of #<impl_class_name|digest_length|hex_cust_string|hex_digest>
536
547
  */
537
- VALUE rbx_Digest_KangarooTwelve_inspect(VALUE self)
548
+ static VALUE _Digest_KangarooTwelve_Impl_inspect(VALUE self)
538
549
  {
539
550
  VALUE klass = rb_obj_class(self);
540
551
  VALUE klass_name = rb_class_name(klass);
@@ -543,12 +554,11 @@ VALUE rbx_Digest_KangarooTwelve_inspect(VALUE self)
543
554
  klass_name = rb_inspect(klass);
544
555
 
545
556
  VALUE digest_length = rb_funcall(self, _id_digest_length, 0);
546
- VALUE block_length = rb_funcall(self, _id_block_length, 0);
547
- VALUE customization = rb_funcall(self, _id_customization, 0);
557
+ VALUE customization_hex = rb_funcall(self, _id_customization_hex, 0);
548
558
  VALUE hexdigest = rb_funcall(self, _id_hexdigest, 0);
549
559
 
550
- VALUE args[] = { klass_name, digest_length, block_length, customization, hexdigest };
551
- return rb_str_format(sizeof(args), args, rb_str_new_literal("#<%s|%d|%d|%s|%s>"));
560
+ VALUE args[] = { klass_name, digest_length, customization_hex, hexdigest };
561
+ return rb_str_format(sizeof(args), args, rb_str_new_literal("#<%s:%d|%s|%s>"));
552
562
  }
553
563
 
554
564
  /*
@@ -562,7 +572,9 @@ void Init_kangarootwelve()
562
572
  DEFINE_ID(auto)
563
573
  DEFINE_ID(block_length)
564
574
  DEFINE_ID(b)
575
+ DEFINE_ID(ch)
565
576
  DEFINE_ID(customization)
577
+ DEFINE_ID(customization_hex)
566
578
  DEFINE_ID(c)
567
579
  DEFINE_ID(default)
568
580
  DEFINE_ID(digest_length)
@@ -575,66 +587,77 @@ void Init_kangarootwelve()
575
587
  DEFINE_ID(unpack)
576
588
 
577
589
  rb_require("digest");
578
- _module_Digest = rb_path2class("Digest");
579
- _class_Digest_Base = rb_path2class("Digest::Base");
590
+ _Digest = rb_path2class("Digest");
580
591
 
581
592
  #if 0
582
- _module_Digest = rb_define_module("Digest"); /* Tell RDoc about Digest since it doesn't recognize rb_path2class. */
593
+ _Digest = rb_define_module("Digest"); /* Tell RDoc about Digest since it doesn't recognize rb_path2class. */
583
594
  #endif
584
595
 
585
596
  /*
586
- * module Digest::KangarooTwelve
597
+ * Document-module: Digest::KangarooTwelve
587
598
  */
588
599
 
589
- _module_Digest_KangarooTwelve = rb_define_module_under(_module_Digest, "KangarooTwelve");
600
+ _Digest_KangarooTwelve = rb_define_module_under(_Digest, "KangarooTwelve");
590
601
 
591
- rb_define_singleton_method(_module_Digest_KangarooTwelve, "default",
592
- rbx_Digest_KangarooTwelve_singleton_default, 0);
593
- rb_define_singleton_method(_module_Digest_KangarooTwelve, "implement",
594
- rbx_Digest_KangarooTwelve_singleton_implement, -1);
595
- rb_define_singleton_method(_module_Digest_KangarooTwelve, "[]",
596
- rbx_Digest_KangarooTwelve_singleton_implement_simple, 1);
602
+ rb_define_singleton_method(_Digest_KangarooTwelve, "default",
603
+ _Digest_KangarooTwelve_singleton_default, 0);
604
+ rb_define_singleton_method(_Digest_KangarooTwelve, "implement",
605
+ _Digest_KangarooTwelve_singleton_implement, -1);
606
+ rb_define_singleton_method(_Digest_KangarooTwelve, "[]",
607
+ _Digest_KangarooTwelve_singleton_implement_simple, 1);
597
608
 
598
609
  /*
599
- * Currently 65536 bytes (8192 x 8)
610
+ * Document-const: Digest::KangarooTwelve::BLOCK_LENGTH
611
+ *
612
+ * 8192 bytes
600
613
  */
601
614
 
602
- rb_define_const(_module_Digest_KangarooTwelve, "DEFAULT_BLOCK_LENGTH", INT2FIX(KT_DEFAULT_BLOCK_LENGTH));
615
+ /* 8192 bytes */
616
+
617
+ rb_define_const(_Digest_KangarooTwelve, "BLOCK_LENGTH", INT2FIX(KT_BLOCK_LENGTH));
603
618
 
604
619
  /*
620
+ * Document-const: Digest::KangarooTwelve::DEFAULT_DIGEST_LENGTH
621
+ *
605
622
  * 64 bytes (512 bits)
606
623
  */
607
624
 
608
- rb_define_const(_module_Digest_KangarooTwelve, "DEFAULT_DIGEST_LENGTH", INT2FIX(KT_DEFAULT_DIGEST_LENGTH));
625
+ /* 64 bytes (512 bits) */
626
+
627
+ rb_define_const(_Digest_KangarooTwelve, "DEFAULT_DIGEST_LENGTH", INT2FIX(KT_DEFAULT_DIGEST_LENGTH));
609
628
 
610
629
  /*
611
- * class Digest::KangarooTwelve::Impl < Digest::Base
630
+ * Document-class: Digest::KangarooTwelve::Impl
612
631
  */
613
632
 
614
- _class_Digest_KangarooTwelve_Impl = rb_define_class_under(_module_Digest_KangarooTwelve, "Impl",
615
- _class_Digest_Base);
616
-
617
- rb_define_singleton_method(_class_Digest_KangarooTwelve_Impl, "new",
618
- rbx_Digest_KangarooTwelve_Impl_singleton_new, 0);
619
- rb_define_singleton_method(_class_Digest_KangarooTwelve_Impl, "block_length",
620
- rbx_Digest_KangarooTwelve_Impl_singleton_block_length, 0);
621
- rb_define_singleton_method(_class_Digest_KangarooTwelve_Impl, "digest_length",
622
- rbx_Digest_KangarooTwelve_Impl_singleton_digest_length, 0);
623
- rb_define_singleton_method(_class_Digest_KangarooTwelve_Impl, "customization",
624
- rbx_Digest_KangarooTwelve_Impl_singleton_customization, 0);
625
-
626
- rb_define_method(_class_Digest_KangarooTwelve_Impl, "customization",
627
- rbx_Digest_KangarooTwelve_Impl_customization, 0);
628
- rb_define_method(_class_Digest_KangarooTwelve_Impl, "inspect",
629
- rbx_Digest_KangarooTwelve_inspect, 0);
633
+ _Digest_KangarooTwelve_Impl = rb_define_class_under(_Digest_KangarooTwelve, "Impl",
634
+ rb_path2class("Digest::Base"));
635
+
636
+ rb_define_singleton_method(_Digest_KangarooTwelve_Impl, "new",
637
+ _Digest_KangarooTwelve_Impl_singleton_new, 0);
638
+ rb_define_singleton_method(_Digest_KangarooTwelve_Impl, "block_length",
639
+ _Digest_KangarooTwelve_Impl_singleton_block_length, 0);
640
+ rb_define_singleton_method(_Digest_KangarooTwelve_Impl, "digest_length",
641
+ _Digest_KangarooTwelve_Impl_singleton_digest_length, 0);
642
+ rb_define_singleton_method(_Digest_KangarooTwelve_Impl, "customization",
643
+ _Digest_KangarooTwelve_Impl_singleton_customization, 0);
644
+ rb_define_singleton_method(_Digest_KangarooTwelve_Impl, "customization_hex",
645
+ _Digest_KangarooTwelve_Impl_singleton_customization_hex, 0);
646
+
647
+ rb_define_method(_Digest_KangarooTwelve_Impl, "customization",
648
+ _Digest_KangarooTwelve_Impl_customization, 0);
649
+ rb_define_method(_Digest_KangarooTwelve_Impl, "customization_hex",
650
+ _Digest_KangarooTwelve_Impl_customization_hex, 0);
651
+ rb_define_method(_Digest_KangarooTwelve_Impl, "inspect",
652
+ _Digest_KangarooTwelve_Impl_inspect, 0);
630
653
 
631
654
  /*
632
- * class Digest::KangarooTwelve::Metadata < Data
655
+ * Document-class: Digest::KangarooTwelve::Metadata
633
656
  *
634
657
  * This class represents the internal metadata produced for the
635
658
  * implementation classes.
636
659
  */
637
660
 
638
- _class_Digest_KangarooTwelve_Metadata = rb_define_class_under(_module_Digest_KangarooTwelve, "Metadata",
661
+ _Digest_KangarooTwelve_Metadata = rb_define_class_under(_Digest_KangarooTwelve, "Metadata",
639
662
  rb_path2class("Data"));
640
663
  }
@@ -0,0 +1,99 @@
1
+ /*
2
+ * These works are licensed under the Creative Commons Attribution-ShareAlike
3
+ * 3.0 Unported License. To view a copy of this license, visit
4
+ * http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative
5
+ * Commons, PO Box 1866, Mountain View, CA 94042, USA.
6
+ */
7
+
8
+ #ifndef UTILS_H
9
+ #define UTILS_H
10
+
11
+ /*
12
+ * A simplified hex encoder based on Yannuth's answer in StackOverflow
13
+ * (https://stackoverflow.com/a/17147874/445221).
14
+ *
15
+ * Length of `dest[]` is implied to be twice of `len`.
16
+ */
17
+ static void hex_encode_str_implied(const unsigned char *src, size_t len, unsigned char *dest)
18
+ {
19
+ static const unsigned char table[] = "0123456789abcdef";
20
+
21
+ for (; len > 0; --len) {
22
+ unsigned char c = *src++;
23
+ *dest++ = table[c >> 4];
24
+ *dest++ = table[c & 0x0f];
25
+ }
26
+ }
27
+
28
+ /*
29
+ * Decodes hex string.
30
+ *
31
+ * Length of `dest[]` is implied to be calculated with calc_hex_decoded_str_length.
32
+ *
33
+ * Returns nonzero is successful.
34
+ */
35
+ static int hex_decode_str_implied(const unsigned char *src, size_t len, unsigned char *dest)
36
+ {
37
+ if (len % 2) {
38
+ unsigned char low = *src++;
39
+
40
+ if (low >= '0' && low <= '9') {
41
+ low -= '0';
42
+ } else if (low >= 'A' && low <= 'F') {
43
+ low -= 'A' - 10;
44
+ } else if (low >= 'a' && low <= 'f') {
45
+ low -= 'a' - 10;
46
+ } else {
47
+ return 0;
48
+ }
49
+
50
+ *dest++ = low;
51
+ --len;
52
+ }
53
+
54
+ for (; len > 0; len -= 2) {
55
+ unsigned char high = *src++;
56
+
57
+ if (high >= '0' && high <= '9') {
58
+ high -= '0';
59
+ } else if (high >= 'A' && high <= 'F') {
60
+ high -= 'A' - 10;
61
+ } else if (high >= 'a' && high <= 'f') {
62
+ high -= 'a' - 10;
63
+ } else {
64
+ return 0;
65
+ }
66
+
67
+ unsigned char low = *src++;
68
+
69
+ if (low >= '0' && low <= '9') {
70
+ low -= '0';
71
+ } else if (low >= 'A' && low <= 'F') {
72
+ low -= 'A' - 10;
73
+ } else if (low >= 'a' && low <= 'f') {
74
+ low -= 'a' - 10;
75
+ } else {
76
+ return 0;
77
+ }
78
+
79
+ *dest++ = high << 4 | low;
80
+ }
81
+
82
+ return -1;
83
+ }
84
+
85
+ /*
86
+ * Calculates length of string that would store decoded hex.
87
+ */
88
+ static size_t calc_hex_decoded_str_length(size_t hex_encoded_length)
89
+ {
90
+ if (hex_encoded_length == 0)
91
+ return 0;
92
+
93
+ if (hex_encoded_length % 2)
94
+ ++hex_encoded_length;
95
+
96
+ return hex_encoded_length / 2;
97
+ }
98
+
99
+ #endif
@@ -1,5 +1,5 @@
1
1
  module Digest
2
2
  class KangarooTwelve
3
- VERSION = "0.0.2"
3
+ VERSION = "0.1.0"
4
4
  end
5
5
  end
@@ -1,5 +1,4 @@
1
1
  require 'minitest/autorun'
2
- require 'yaml'
3
2
 
4
3
  TEST_DIR = File.dirname(__FILE__)
5
4
  require File.join(TEST_DIR, %w{ .. lib digest kangarootwelve })
@@ -17,6 +16,10 @@ def get_repeated_0xff(length)
17
16
  [str].cycle(cycles).to_a.join[0...length]
18
17
  end
19
18
 
19
+ def hex_encode(str)
20
+ str.unpack('H*').pop
21
+ end
22
+
20
23
  describe Digest::KangarooTwelve do
21
24
  it "produces implementation classes" do
22
25
  Digest::KangarooTwelve[32].superclass.must_equal Digest::KangarooTwelve::Impl
@@ -42,25 +45,50 @@ describe Digest::KangarooTwelve do
42
45
  Digest::KangarooTwelve.implement(digest_length: 48).new.digest_length.must_equal Digest::KangarooTwelve_48.digest_length
43
46
  end
44
47
 
45
- it "produces a hash" do
46
- Digest::KangarooTwelve.default.new.digest("")
48
+ it "produces hashes" do
49
+ Digest::KangarooTwelve.default.digest("").class.must_equal String
50
+ Digest::KangarooTwelve.default.hexdigest("").class.must_equal String
51
+ Digest::KangarooTwelve.default.new.digest("").class.must_equal String
52
+ Digest::KangarooTwelve.default.new.hexdigest("").class.must_equal String
53
+ end
54
+
55
+ it "produces similar output with its digest and hexdigest methods" do
56
+ digest_a = Digest::KangarooTwelve[32].digest("abcd")
57
+ digest_a.class.must_equal String
58
+ digest_b = Digest::KangarooTwelve[32].new.digest("abcd")
59
+ digest_b.class.must_equal String
60
+ digest_a.must_equal digest_b
61
+ hex_digest_a = Digest::KangarooTwelve[32].hexdigest("abcd")
62
+ hex_digest_a.class.must_equal String
63
+ hex_digest_b = Digest::KangarooTwelve[32].new.hexdigest("abcd")
64
+ hex_digest_b.class.must_equal String
65
+ hex_digest_a.must_equal hex_digest_b
66
+ hex_digest_a.must_equal hex_encode(digest_a)
47
67
  end
48
68
 
49
69
  it "works with customization strings" do
50
- Digest::KangarooTwelve.implement(customization: "abc").new.digest("")
70
+ Digest::KangarooTwelve.implement(customization: "abcd").new.digest("")
51
71
  end
52
72
 
53
73
  it "produces implementations with small and long option names" do
54
- a = Digest::KangarooTwelve.implement(n: "KangarooTwelveTestA", d: 48, b: 512, c: "abc")
55
- b = Digest::KangarooTwelve.implement(name: "KangarooTwelveTestB", digest_length: 48, block_length: 512, customization: "abc")
74
+ a = Digest::KangarooTwelve.implement(n: "KangarooTwelveTestA", d: 48, c: "abcd")
75
+ b = Digest::KangarooTwelve.implement(name: "KangarooTwelveTestB", digest_length: 48, customization: "abcd")
76
+ c = Digest::KangarooTwelve.implement(name: "KangarooTwelveTestC", digest_length: 48, customization_hex: "61626364")
77
+ d = Digest::KangarooTwelve.implement(name: "KangarooTwelveTestD", digest_length: 48, ch: "61626364")
56
78
  a.name.must_equal "Digest::KangarooTwelveTestA"
57
79
  b.name.must_equal "Digest::KangarooTwelveTestB"
58
80
  a.digest_length.must_equal 48
59
- a.digest_length.must_equal b.digest_length
60
- a.block_length.must_equal 512
61
- a.block_length.must_equal b.block_length
62
- a.customization.must_equal "abc"
63
- a.customization.must_equal b.customization
81
+ b.digest_length.must_equal a.digest_length
82
+ a.customization.must_equal "abcd"
83
+ b.customization.must_equal a.customization
84
+ c.customization.must_equal a.customization
85
+ d.customization.must_equal a.customization
86
+ end
87
+
88
+ it "has a declared block length of 8192 bytes" do
89
+ Digest::KangarooTwelve::BLOCK_LENGTH == 8192
90
+ Digest::KangarooTwelve.default.block_length == 8192
91
+ Digest::KangarooTwelve.default.new.block_length == 8192
64
92
  end
65
93
 
66
94
  it "produces valid hashes" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: digest-kangarootwelve
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - konsolebox
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-12 00:00:00.000000000 Z
11
+ date: 2017-08-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -52,7 +52,7 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '5.8'
55
- description: An implementation of KangarooTwelve for Ruby that works on top of Digest::Base.
55
+ description: A KangarooTwelve library that works on top of Digest::Base.
56
56
  email:
57
57
  - konsolebox@gmail.com
58
58
  executables: []
@@ -86,6 +86,7 @@ files:
86
86
  - ext/digest/kangarootwelve/brg_endian.h
87
87
  - ext/digest/kangarootwelve/ext.c
88
88
  - ext/digest/kangarootwelve/extconf.rb
89
+ - ext/digest/kangarootwelve/utils.h
89
90
  - lib/digest/kangarootwelve/version.rb
90
91
  - test/test.rb
91
92
  homepage: https://github.com/konsolebox/digest-kangarootwelve-ruby
@@ -108,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
108
109
  version: '0'
109
110
  requirements: []
110
111
  rubyforge_project:
111
- rubygems_version: 2.6.8
112
+ rubygems_version: 2.6.12
112
113
  signing_key:
113
114
  specification_version: 4
114
115
  summary: KangarooTwelve for Ruby