sha3 1.0.5 → 2.0.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.clang-format +54 -0
- data/.document +4 -3
- data/.rdoc_options +10 -0
- data/.rspec +2 -2
- data/.rubocop.yml +5 -1
- data/CHANGELOG.md +23 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +1 -1
- data/README.md +154 -67
- data/Rakefile +9 -3
- data/certs/io+sha3@jsg.io.pem +26 -0
- data/doc/sha3.rb +81 -0
- data/ext/sha3/digest.c +635 -163
- data/ext/sha3/digest.h +71 -35
- data/ext/sha3/extconf.rb +42 -38
- data/ext/sha3/lib/high/Keccak/KeccakDuplex.c +81 -0
- data/ext/sha3/lib/high/Keccak/KeccakDuplex.h +73 -0
- data/ext/sha3/lib/high/Keccak/KeccakDuplex.inc +201 -0
- data/ext/sha3/lib/high/Keccak/KeccakSponge.c +2 -18
- data/ext/sha3/lib/high/Keccak/KeccakSponge.h +4 -10
- data/ext/sha3/lib/high/Keccak/KeccakSponge.inc +27 -31
- data/ext/sha3/lib/high/Keccak/PRG/KeccakPRG.c +61 -0
- data/ext/sha3/lib/high/Keccak/PRG/KeccakPRG.h +67 -0
- data/ext/sha3/lib/high/Keccak/PRG/KeccakPRG.inc +128 -0
- data/ext/sha3/lib/high/Keccak/SP800-185/SP800-185.c +93 -0
- data/ext/sha3/lib/high/Keccak/SP800-185/SP800-185.h +599 -0
- data/ext/sha3/lib/high/Keccak/SP800-185/SP800-185.inc +573 -0
- data/ext/sha3/lib/high/common/Phases.h +25 -0
- data/ext/sha3/lib/low/KeccakP-1600/common/KeccakP-1600-64.macros +19 -9
- data/ext/sha3/lib/low/KeccakP-1600/ref-32bits/KeccakP-1600-SnP.h +18 -12
- data/ext/sha3/lib/low/KeccakP-1600/ref-32bits/KeccakP-1600-reference32BI.c +28 -36
- data/ext/sha3/lib/low/KeccakP-1600/ref-64bits/KeccakP-1600-SnP.h +18 -12
- data/ext/sha3/lib/low/KeccakP-1600/ref-64bits/KeccakP-1600-reference.c +28 -59
- data/ext/sha3/lib/low/common/PlSnP-Fallback.inc +291 -0
- data/ext/sha3/lib/low/common/SnP-Relaned.h +145 -0
- data/lib/sha3.rb +25 -28
- data.tar.gz.sig +0 -0
- metadata +55 -115
- metadata.gz.sig +0 -0
- data/.yardopts +0 -1
- data/ChangeLog.rdoc +0 -27
- data/certs/johanns.pem +0 -25
- data/ext/sha3/sha3.c +0 -62
- data/ext/sha3/sha3.h +0 -26
- data/lib/sha3/doc.rb +0 -121
- data/lib/sha3/version.rb +0 -9
- data/sha3.gemspec +0 -54
- data/tests.sh +0 -29
data/ext/sha3/digest.c
CHANGED
@@ -1,9 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
#include "sha3.h"
|
4
|
-
|
5
|
-
VALUE cSHA3Digest;
|
6
|
-
VALUE eSHA3DigestError;
|
1
|
+
#include "digest.h"
|
7
2
|
|
8
3
|
/*
|
9
4
|
* == Notes
|
@@ -12,259 +7,736 @@ VALUE eSHA3DigestError;
|
|
12
7
|
* | .alloc() ->
|
13
8
|
* | .new() ->
|
14
9
|
* | .update() ->
|
15
|
-
* | .digest or .hexdigest or .inspect -> (Instance.digest or .hexdigest())
|
10
|
+
* | .digest or .hexdigest or .inspect -> (Instance.digest or .hexdigest())
|
11
|
+
* ->
|
16
12
|
* --| .alloc() ->
|
17
13
|
* | .copy() ->
|
18
14
|
* | .finish() ->
|
19
15
|
*
|
20
16
|
*/
|
21
17
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
18
|
+
/*
|
19
|
+
* SHA3 module
|
20
|
+
*/
|
21
|
+
void Init_sha3_digest(void) {
|
22
|
+
rb_require("digest");
|
23
|
+
|
24
|
+
/* Initialize static symbol IDs for faster lookup in get_hlen() */
|
25
|
+
sha3_224_id = rb_intern("sha3_224");
|
26
|
+
sha3_256_id = rb_intern("sha3_256");
|
27
|
+
sha3_384_id = rb_intern("sha3_384");
|
28
|
+
sha3_512_id = rb_intern("sha3_512");
|
29
|
+
shake_128_id = rb_intern("shake_128");
|
30
|
+
shake_256_id = rb_intern("shake_256");
|
31
|
+
|
32
|
+
/*
|
33
|
+
* Document-module: SHA3
|
34
|
+
*
|
35
|
+
* This hosts the SHA3::Digest classes.
|
36
|
+
*/
|
37
|
+
sha3_module = rb_define_module("SHA3");
|
38
|
+
|
39
|
+
/*
|
40
|
+
* Document-class: SHA3::Digest
|
41
|
+
*
|
42
|
+
* It is a subclass of the Digest::Class class, which provides a framework for
|
43
|
+
* creating and manipulating hash digests.
|
44
|
+
*/
|
45
|
+
digest_class = rb_define_class_under(sha3_module, "Digest", rb_path2class("Digest::Class"));
|
46
|
+
|
47
|
+
/*
|
48
|
+
* Default-const: SHA3::VERSION
|
49
|
+
*
|
50
|
+
* It is the version of the SHA3 module.
|
51
|
+
*/
|
52
|
+
rb_define_const(sha3_module, "VERSION", rb_str_new2("2.0.0"));
|
53
|
+
|
54
|
+
/*
|
55
|
+
* Document-class: SHA3::Digest::DigestError
|
56
|
+
*
|
57
|
+
* It is a subclass of the StandardError class -- see the Ruby documentation
|
58
|
+
* for more information.
|
59
|
+
*/
|
60
|
+
digest_error_class = rb_define_class_under(digest_class, "DigestError", rb_eStandardError);
|
61
|
+
|
62
|
+
rb_define_alloc_func(digest_class, rb_digest_alloc);
|
63
|
+
rb_define_method(digest_class, "initialize", rb_digest_init, -1);
|
64
|
+
rb_define_method(digest_class, "update", rb_digest_update, 1);
|
65
|
+
rb_define_method(digest_class, "reset", rb_digest_reset, 0);
|
66
|
+
rb_define_method(digest_class, "initialize_copy", rb_digest_copy, 1);
|
67
|
+
rb_define_method(digest_class, "digest_length", rb_digest_length, 0);
|
68
|
+
rb_define_method(digest_class, "block_length", rb_digest_block_length, 0);
|
69
|
+
rb_define_method(digest_class, "name", rb_digest_name, 0);
|
70
|
+
rb_define_method(digest_class, "squeeze", rb_digest_squeeze, 1);
|
71
|
+
rb_define_method(digest_class, "hex_squeeze", rb_digest_hex_squeeze, 1);
|
72
|
+
rb_define_method(digest_class, "digest", rb_digest_digest, -1);
|
73
|
+
rb_define_method(digest_class, "hexdigest", rb_digest_hexdigest, -1);
|
74
|
+
rb_define_private_method(digest_class, "finish", rb_digest_finish, -1);
|
75
|
+
|
76
|
+
/* Define the class method self.digest */
|
77
|
+
rb_define_singleton_method(digest_class, "digest", rb_digest_self_digest, 2);
|
78
|
+
rb_define_singleton_method(digest_class, "hexdigest", rb_digest_self_hexdigest, 2);
|
79
|
+
rb_define_alias(digest_class, "<<", "update");
|
80
|
+
}
|
81
|
+
|
82
|
+
int get_hlen(VALUE obj, algorithm_type* algorithm) {
|
83
|
+
if (TYPE(obj) == T_SYMBOL) {
|
84
|
+
ID symid = SYM2ID(obj);
|
85
|
+
|
86
|
+
if (symid == sha3_224_id) {
|
87
|
+
*algorithm = SHA3_224;
|
88
|
+
return 224;
|
89
|
+
} else if (symid == sha3_256_id) {
|
90
|
+
*algorithm = SHA3_256;
|
91
|
+
return 256;
|
92
|
+
} else if (symid == sha3_384_id) {
|
93
|
+
*algorithm = SHA3_384;
|
94
|
+
return 384;
|
95
|
+
} else if (symid == sha3_512_id) {
|
96
|
+
*algorithm = SHA3_512;
|
97
|
+
return 512;
|
98
|
+
} else if (symid == shake_128_id) {
|
99
|
+
*algorithm = SHAKE_128;
|
100
|
+
return 128;
|
101
|
+
} else if (symid == shake_256_id) {
|
102
|
+
*algorithm = SHAKE_256;
|
103
|
+
return 256;
|
29
104
|
}
|
30
105
|
|
31
|
-
|
106
|
+
rb_raise(digest_error_class,
|
107
|
+
"invalid hash algorithm symbol (should be: :sha3_224, "
|
108
|
+
":sha3_256, :sha3_384, :sha3_512, :shake_128, or :shake_256)");
|
32
109
|
}
|
33
110
|
|
34
|
-
|
111
|
+
rb_raise(digest_error_class, "unknown type value");
|
112
|
+
return 0; // Never reached, but silences compiler warnings
|
113
|
+
}
|
114
|
+
|
115
|
+
static void mdx_free(void* ptr) {
|
116
|
+
MDX* mdx = (MDX*)ptr;
|
117
|
+
if (mdx) {
|
118
|
+
if (mdx->state) {
|
119
|
+
free(mdx->state);
|
120
|
+
}
|
121
|
+
free(mdx);
|
122
|
+
}
|
35
123
|
}
|
36
124
|
|
37
|
-
static
|
38
|
-
|
39
|
-
MDX
|
40
|
-
VALUE obj;
|
125
|
+
static size_t mdx_memsize(const void* ptr) {
|
126
|
+
const MDX* mdx = (const MDX*)ptr;
|
127
|
+
size_t size = sizeof(MDX);
|
41
128
|
|
42
|
-
mdx
|
43
|
-
|
44
|
-
{
|
45
|
-
rb_raise(eSHA3DigestError, "failed to allocate object memory");
|
129
|
+
if (mdx && mdx->state) {
|
130
|
+
size += sizeof(Keccak_HashInstance);
|
46
131
|
}
|
47
132
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
133
|
+
return size;
|
134
|
+
}
|
135
|
+
|
136
|
+
/* TypedData structure for MDX */
|
137
|
+
const rb_data_type_t mdx_type = {"SHA3::Digest",
|
138
|
+
{
|
139
|
+
NULL,
|
140
|
+
mdx_free,
|
141
|
+
mdx_memsize,
|
142
|
+
},
|
143
|
+
NULL,
|
144
|
+
NULL,
|
145
|
+
RUBY_TYPED_FREE_IMMEDIATELY};
|
146
|
+
|
147
|
+
static VALUE rb_digest_alloc(VALUE klass) {
|
148
|
+
MDX* mdx = (MDX*)malloc(sizeof(MDX));
|
149
|
+
if (!mdx) {
|
150
|
+
rb_raise(digest_error_class, "failed to allocate object memory");
|
53
151
|
}
|
54
152
|
|
55
|
-
|
153
|
+
mdx->state = (Keccak_HashInstance*)calloc(1, sizeof(Keccak_HashInstance));
|
154
|
+
if (!mdx->state) {
|
155
|
+
mdx_free(mdx);
|
156
|
+
rb_raise(digest_error_class, "failed to allocate state memory");
|
157
|
+
}
|
56
158
|
|
57
|
-
|
159
|
+
VALUE obj = TypedData_Wrap_Struct(klass, &mdx_type, mdx);
|
58
160
|
mdx->hashbitlen = 0;
|
161
|
+
mdx->algorithm = SHA3_256; // Default algorithm
|
59
162
|
|
60
163
|
return obj;
|
61
164
|
}
|
62
165
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
case 512:
|
81
|
-
r = Keccak_HashInitialize_SHA3_512(mdx->state);
|
82
|
-
break;
|
83
|
-
}
|
84
|
-
|
85
|
-
return r;
|
166
|
+
HashReturn keccak_hash_initialize(MDX* mdx) {
|
167
|
+
switch (mdx->algorithm) {
|
168
|
+
case SHA3_224:
|
169
|
+
return Keccak_HashInitialize_SHA3_224(mdx->state);
|
170
|
+
case SHA3_256:
|
171
|
+
return Keccak_HashInitialize_SHA3_256(mdx->state);
|
172
|
+
case SHA3_384:
|
173
|
+
return Keccak_HashInitialize_SHA3_384(mdx->state);
|
174
|
+
case SHA3_512:
|
175
|
+
return Keccak_HashInitialize_SHA3_512(mdx->state);
|
176
|
+
case SHAKE_128:
|
177
|
+
return Keccak_HashInitialize_SHAKE128(mdx->state);
|
178
|
+
case SHAKE_256:
|
179
|
+
return Keccak_HashInitialize_SHAKE256(mdx->state);
|
180
|
+
}
|
181
|
+
|
182
|
+
return KECCAK_FAIL;
|
86
183
|
}
|
87
184
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
185
|
+
/*
|
186
|
+
* :call-seq:
|
187
|
+
* ::new() -> instance
|
188
|
+
* ::new([algorithm], [message]) -> instance
|
189
|
+
*
|
190
|
+
* Creates a new digest object.
|
191
|
+
*
|
192
|
+
* +algorithm+::
|
193
|
+
* _optional_ The algorithm to use.
|
194
|
+
* Valid algorithms are:
|
195
|
+
* - :sha3_224
|
196
|
+
* - :sha3_256
|
197
|
+
* - :sha3_384
|
198
|
+
* - :sha3_512
|
199
|
+
* - :shake_128
|
200
|
+
* - :shake_256
|
201
|
+
*
|
202
|
+
* +message+::
|
203
|
+
* _optional_ The message to hash.
|
204
|
+
*
|
205
|
+
* = example
|
206
|
+
* SHA3::Digest.new(:sha3_256)
|
207
|
+
* SHA3::Digest.new(:shake_128, "initial data")
|
208
|
+
*/
|
209
|
+
static VALUE rb_digest_init(int argc, VALUE* argv, VALUE self) {
|
210
|
+
MDX* mdx;
|
92
211
|
VALUE hlen, data;
|
93
212
|
|
94
213
|
rb_scan_args(argc, argv, "02", &hlen, &data);
|
95
|
-
|
214
|
+
get_mdx(self, &mdx);
|
96
215
|
|
97
|
-
if (
|
98
|
-
|
99
|
-
mdx->hashbitlen = get_hlen(hlen);
|
100
|
-
}
|
101
|
-
else
|
102
|
-
{
|
216
|
+
if (NIL_P(hlen)) {
|
217
|
+
mdx->algorithm = SHA3_256;
|
103
218
|
mdx->hashbitlen = 256;
|
219
|
+
} else {
|
220
|
+
mdx->hashbitlen = get_hlen(hlen, &mdx->algorithm);
|
104
221
|
}
|
105
222
|
|
106
|
-
if (
|
107
|
-
|
108
|
-
rb_raise(eSHA3DigestError, "failed to initialize algorithm state");
|
223
|
+
if (keccak_hash_initialize(mdx) != KECCAK_SUCCESS) {
|
224
|
+
rb_raise(digest_error_class, "failed to initialize algorithm state");
|
109
225
|
}
|
110
226
|
|
111
|
-
if (!NIL_P(data))
|
112
|
-
|
113
|
-
return c_digest_update(self, data);
|
227
|
+
if (!NIL_P(data)) {
|
228
|
+
return rb_digest_update(self, data);
|
114
229
|
}
|
115
230
|
|
116
231
|
return self;
|
117
232
|
}
|
118
233
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
234
|
+
/*
|
235
|
+
* :call-seq:
|
236
|
+
* update(string) -> digest
|
237
|
+
*
|
238
|
+
* Updates the digest with the given string.
|
239
|
+
*
|
240
|
+
* +string+::
|
241
|
+
* The string to update the digest with.
|
242
|
+
*
|
243
|
+
* = example
|
244
|
+
* digest.update("more data")
|
245
|
+
* digest << "more data" # alias for update
|
246
|
+
*/
|
247
|
+
static VALUE rb_digest_update(VALUE self, VALUE data) {
|
248
|
+
MDX* mdx;
|
123
249
|
BitLength dlen;
|
124
250
|
|
125
251
|
StringValue(data);
|
126
|
-
|
252
|
+
get_mdx(self, &mdx);
|
253
|
+
|
254
|
+
// Check for empty data
|
255
|
+
if (RSTRING_LEN(data) == 0) {
|
256
|
+
return self;
|
257
|
+
}
|
258
|
+
|
259
|
+
// Check for NULL data pointer
|
260
|
+
if (RSTRING_PTR(data) == NULL) {
|
261
|
+
rb_raise(digest_error_class, "cannot update with NULL data");
|
262
|
+
}
|
127
263
|
|
128
264
|
dlen = (RSTRING_LEN(data) * 8);
|
129
265
|
|
130
|
-
if (Keccak_HashUpdate(mdx->state, (BitSequence
|
131
|
-
|
132
|
-
rb_raise(eSHA3DigestError, "failed to update hash data");
|
266
|
+
if (Keccak_HashUpdate(mdx->state, (BitSequence*)RSTRING_PTR(data), dlen) != KECCAK_SUCCESS) {
|
267
|
+
rb_raise(digest_error_class, "failed to update hash data");
|
133
268
|
}
|
134
269
|
|
135
270
|
return self;
|
136
271
|
}
|
137
272
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
273
|
+
/*
|
274
|
+
* :call-seq:
|
275
|
+
* reset -> digest
|
276
|
+
*
|
277
|
+
* Resets the digest to its initial state.
|
278
|
+
*
|
279
|
+
* = example
|
280
|
+
* digest.reset
|
281
|
+
*/
|
282
|
+
static VALUE rb_digest_reset(VALUE self) {
|
283
|
+
MDX* mdx;
|
284
|
+
get_mdx(self, &mdx);
|
144
285
|
|
145
286
|
memset(mdx->state, 0, sizeof(Keccak_HashInstance));
|
146
287
|
|
147
|
-
if (
|
148
|
-
|
149
|
-
rb_raise(eSHA3DigestError, "failed to reset internal state");
|
288
|
+
if (keccak_hash_initialize(mdx) != KECCAK_SUCCESS) {
|
289
|
+
rb_raise(digest_error_class, "failed to reset internal state");
|
150
290
|
}
|
151
291
|
|
152
292
|
return self;
|
153
293
|
}
|
154
294
|
|
155
|
-
static int cmp_states(MDX
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
295
|
+
static int cmp_states(const MDX* mdx1, const MDX* mdx2) {
|
296
|
+
// First check the hashbitlen and algorithm
|
297
|
+
if (mdx1->hashbitlen != mdx2->hashbitlen || mdx1->algorithm != mdx2->algorithm) {
|
298
|
+
return 0;
|
299
|
+
}
|
300
|
+
|
301
|
+
// Compare the internal state structure
|
302
|
+
if (memcmp(&(mdx1->state->sponge.state), &(mdx2->state->sponge.state),
|
303
|
+
sizeof(mdx1->state->sponge.state)) != 0) {
|
304
|
+
return 0;
|
305
|
+
}
|
306
|
+
|
307
|
+
// Compare sponge parameters
|
308
|
+
if ((mdx1->state->sponge.rate != mdx2->state->sponge.rate) ||
|
309
|
+
(mdx1->state->sponge.byteIOIndex != mdx2->state->sponge.byteIOIndex) ||
|
310
|
+
(mdx1->state->sponge.squeezing != mdx2->state->sponge.squeezing)) {
|
311
|
+
return 0;
|
312
|
+
}
|
313
|
+
|
314
|
+
// Compare hash-specific parameters
|
315
|
+
if ((mdx1->state->fixedOutputLength != mdx2->state->fixedOutputLength) ||
|
316
|
+
(mdx1->state->delimitedSuffix != mdx2->state->delimitedSuffix)) {
|
317
|
+
return 0;
|
318
|
+
}
|
319
|
+
|
320
|
+
// All comparisons passed
|
321
|
+
return 1;
|
165
322
|
}
|
166
323
|
|
167
|
-
|
168
|
-
|
169
|
-
|
324
|
+
/*
|
325
|
+
* :call-seq:
|
326
|
+
* initialize_copy(other) -> digest
|
327
|
+
*
|
328
|
+
* Initializes the digest with the state of another digest.
|
329
|
+
*
|
330
|
+
* +other+::
|
331
|
+
* The digest to copy the state from.
|
332
|
+
*
|
333
|
+
* = example
|
334
|
+
* new_digest = digest.dup
|
335
|
+
*/
|
336
|
+
static VALUE rb_digest_copy(VALUE self, VALUE obj) {
|
170
337
|
MDX *mdx1, *mdx2;
|
171
338
|
|
172
339
|
rb_check_frozen(self);
|
173
|
-
if (self == obj)
|
174
|
-
{
|
340
|
+
if (self == obj) {
|
175
341
|
return self;
|
176
342
|
}
|
177
343
|
|
178
|
-
|
179
|
-
|
344
|
+
get_mdx(self, &mdx1);
|
345
|
+
safe_get_mdx(obj, &mdx2);
|
180
346
|
|
181
347
|
memcpy(mdx1->state, mdx2->state, sizeof(Keccak_HashInstance));
|
182
348
|
mdx1->hashbitlen = mdx2->hashbitlen;
|
349
|
+
mdx1->algorithm = mdx2->algorithm;
|
183
350
|
|
184
|
-
|
185
|
-
|
186
|
-
SAFEGETMDX(obj, mdx2);
|
187
|
-
|
188
|
-
if (!cmp_states(mdx1, mdx2))
|
189
|
-
{
|
190
|
-
rb_raise(eSHA3DigestError, "failed to copy state");
|
351
|
+
if (!cmp_states(mdx1, mdx2)) {
|
352
|
+
rb_raise(digest_error_class, "failed to copy state");
|
191
353
|
}
|
192
354
|
|
193
355
|
return self;
|
194
356
|
}
|
195
357
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
358
|
+
/*
|
359
|
+
* :call-seq:
|
360
|
+
* length -> Integer
|
361
|
+
*
|
362
|
+
* Returns the length of the digest in bytes.
|
363
|
+
*
|
364
|
+
* = example
|
365
|
+
* digest.length #=> 32 for SHA3-256
|
366
|
+
*/
|
367
|
+
static VALUE rb_digest_length(VALUE self) {
|
368
|
+
MDX* mdx;
|
369
|
+
get_mdx(self, &mdx);
|
201
370
|
|
202
371
|
return ULL2NUM(mdx->hashbitlen / 8);
|
203
372
|
}
|
204
373
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
374
|
+
/*
|
375
|
+
* :call-seq:
|
376
|
+
* block_length -> Integer
|
377
|
+
*
|
378
|
+
* Returns the block length of the algorithm in bytes.
|
379
|
+
*
|
380
|
+
* = example
|
381
|
+
* digest.block_length
|
382
|
+
*/
|
383
|
+
static VALUE rb_digest_block_length(VALUE self) {
|
384
|
+
MDX* mdx;
|
385
|
+
get_mdx(self, &mdx);
|
210
386
|
|
211
387
|
return ULL2NUM(200 - (2 * (mdx->hashbitlen / 8)));
|
212
388
|
}
|
213
389
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
390
|
+
/*
|
391
|
+
* :call-seq:
|
392
|
+
* name -> String
|
393
|
+
*
|
394
|
+
* Returns the name of the algorithm.
|
395
|
+
*
|
396
|
+
* = example
|
397
|
+
* digest.name #=> "SHA3-256"
|
398
|
+
*/
|
399
|
+
static VALUE rb_digest_name(VALUE self) {
|
400
|
+
MDX* mdx;
|
401
|
+
get_mdx(self, &mdx);
|
402
|
+
|
403
|
+
switch (mdx->algorithm) {
|
404
|
+
case SHA3_224:
|
405
|
+
return rb_str_new2("SHA3-224");
|
406
|
+
case SHA3_256:
|
407
|
+
return rb_str_new2("SHA3-256");
|
408
|
+
case SHA3_384:
|
409
|
+
return rb_str_new2("SHA3-384");
|
410
|
+
case SHA3_512:
|
411
|
+
return rb_str_new2("SHA3-512");
|
412
|
+
case SHAKE_128:
|
413
|
+
return rb_str_new2("SHAKE128");
|
414
|
+
case SHAKE_256:
|
415
|
+
return rb_str_new2("SHAKE256");
|
416
|
+
default:
|
417
|
+
rb_raise(digest_error_class, "unknown algorithm");
|
418
|
+
}
|
218
419
|
}
|
219
420
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
421
|
+
/*
|
422
|
+
* :call-seq:
|
423
|
+
* finish([message]) -> String
|
424
|
+
*
|
425
|
+
* Returns the final digest as a binary string.
|
426
|
+
*
|
427
|
+
* +message+::
|
428
|
+
* _optional_ Update state with additional data before finalizing.
|
429
|
+
*
|
430
|
+
* = example
|
431
|
+
* digest.finish
|
432
|
+
* digest.finish("final chunk")
|
433
|
+
*/
|
434
|
+
static VALUE rb_digest_finish(int argc, VALUE* argv, VALUE self) {
|
435
|
+
MDX* mdx;
|
224
436
|
VALUE str;
|
437
|
+
int digest_bytes;
|
225
438
|
|
226
439
|
rb_scan_args(argc, argv, "01", &str);
|
227
|
-
|
440
|
+
get_mdx(self, &mdx);
|
228
441
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
442
|
+
// For both SHA3 and SHAKE algorithms, use the security strength (hashbitlen)
|
443
|
+
// as the default output length
|
444
|
+
digest_bytes = mdx->hashbitlen / 8;
|
445
|
+
|
446
|
+
if (NIL_P(str)) {
|
447
|
+
str = rb_str_new(0, digest_bytes);
|
448
|
+
} else {
|
235
449
|
StringValue(str);
|
236
|
-
rb_str_resize(str,
|
450
|
+
rb_str_resize(str, digest_bytes);
|
237
451
|
}
|
238
452
|
|
239
|
-
if (Keccak_HashFinal(mdx->state, (BitSequence
|
240
|
-
|
241
|
-
rb_raise(eSHA3DigestError, "failed to finalize digest");
|
453
|
+
if (Keccak_HashFinal(mdx->state, (BitSequence*)RSTRING_PTR(str)) != KECCAK_SUCCESS) {
|
454
|
+
rb_raise(digest_error_class, "failed to finalize digest");
|
242
455
|
}
|
243
456
|
|
244
457
|
return str;
|
245
458
|
}
|
246
459
|
|
247
|
-
|
248
|
-
|
249
|
-
|
460
|
+
/*
|
461
|
+
* :call-seq:
|
462
|
+
* squeeze(length) -> String
|
463
|
+
*
|
464
|
+
* Returns the squeezed output as a binary string. Only available for SHAKE algorithms.
|
465
|
+
*
|
466
|
+
* +length+::
|
467
|
+
* The length in bytes of the output to squeeze.
|
468
|
+
*
|
469
|
+
* = example
|
470
|
+
* digest.squeeze(32) # Get 32 bytes of output
|
471
|
+
*/
|
472
|
+
static VALUE rb_digest_squeeze(VALUE self, VALUE length) {
|
473
|
+
MDX* mdx;
|
474
|
+
VALUE str, copy;
|
475
|
+
int output_bytes;
|
476
|
+
|
477
|
+
Check_Type(length, T_FIXNUM);
|
478
|
+
output_bytes = NUM2INT(length);
|
479
|
+
|
480
|
+
if (output_bytes <= 0) {
|
481
|
+
rb_raise(digest_error_class, "output length must be positive");
|
482
|
+
}
|
483
|
+
|
484
|
+
get_mdx(self, &mdx);
|
485
|
+
|
486
|
+
// Only SHAKE algorithms support arbitrary-length output
|
487
|
+
if (mdx->algorithm != SHAKE_128 && mdx->algorithm != SHAKE_256) {
|
488
|
+
rb_raise(digest_error_class, "squeeze is only supported for SHAKE algorithms");
|
489
|
+
}
|
490
|
+
|
491
|
+
// Create a copy of the digest object to avoid modifying the original
|
492
|
+
copy = rb_obj_clone(self);
|
493
|
+
|
494
|
+
// Get the MDX struct from the copy
|
495
|
+
MDX* mdx_copy;
|
496
|
+
get_mdx(copy, &mdx_copy);
|
497
|
+
|
498
|
+
str = rb_str_new(0, output_bytes);
|
499
|
+
|
500
|
+
// Finalize the hash on the copy
|
501
|
+
if (Keccak_HashFinal(mdx_copy->state, NULL) != KECCAK_SUCCESS) {
|
502
|
+
rb_raise(digest_error_class, "failed to finalize digest");
|
503
|
+
}
|
504
|
+
|
505
|
+
// Then squeeze out the desired number of bytes
|
506
|
+
if (Keccak_HashSqueeze(mdx_copy->state, (BitSequence*)RSTRING_PTR(str), output_bytes * 8) !=
|
507
|
+
KECCAK_SUCCESS) {
|
508
|
+
rb_raise(digest_error_class, "failed to squeeze output");
|
509
|
+
}
|
510
|
+
|
511
|
+
// NOTE: We don't need the copy anymore...Ruby's GC will handle freeing it
|
512
|
+
|
513
|
+
return str;
|
514
|
+
}
|
515
|
+
|
516
|
+
/*
|
517
|
+
* :call-seq:
|
518
|
+
* hex_squeeze(length) -> String
|
519
|
+
*
|
520
|
+
* Returns the hexadecimal representation of the squeezed output. Only available for SHAKE
|
521
|
+
* algorithms.
|
522
|
+
*
|
523
|
+
* +length+::
|
524
|
+
* The length in bytes of the output to squeeze.
|
525
|
+
*
|
526
|
+
* = example
|
527
|
+
* digest.hex_squeeze(32) # Get 64 hex characters (32 bytes)
|
528
|
+
*/
|
529
|
+
static VALUE rb_digest_hex_squeeze(VALUE self, VALUE length) {
|
530
|
+
VALUE bin_str, result_array;
|
531
|
+
|
532
|
+
// Get the binary output using the existing squeeze function
|
533
|
+
bin_str = rb_digest_squeeze(self, length);
|
534
|
+
|
535
|
+
// Use Ruby's built-in unpack method to convert to hex
|
536
|
+
result_array = rb_funcall(bin_str, rb_intern("unpack"), 1, rb_str_new2("H*"));
|
537
|
+
|
538
|
+
// Extract the first element from the array
|
539
|
+
return rb_ary_entry(result_array, 0);
|
540
|
+
}
|
541
|
+
|
542
|
+
/*
|
543
|
+
* :call-seq:
|
544
|
+
* digest() -> string
|
545
|
+
* digest([data]) -> string
|
546
|
+
* digest(length) -> string
|
547
|
+
* digest(length, data) -> string
|
548
|
+
*
|
549
|
+
* Returns the binary representation of the digest.
|
550
|
+
*
|
551
|
+
* +length+::
|
552
|
+
* The length of the output to squeeze when using SHAKE algorithms.
|
553
|
+
* This parameter is required for SHAKE algorithms.
|
554
|
+
*
|
555
|
+
* +data+::
|
556
|
+
* _optional_ Update state with additional data before returning digest.
|
557
|
+
*
|
558
|
+
* = example
|
559
|
+
* digest.digest()
|
560
|
+
* digest.digest('compute me')
|
561
|
+
* digest.digest(12) # For SHAKE algorithms
|
562
|
+
* digest.digest(12, 'compute me') # For SHAKE algorithms
|
563
|
+
*/
|
564
|
+
static VALUE rb_digest_digest(int argc, VALUE* argv, VALUE self) {
|
565
|
+
MDX* mdx;
|
566
|
+
get_mdx(self, &mdx);
|
567
|
+
|
568
|
+
if (mdx->algorithm != SHAKE_128 && mdx->algorithm != SHAKE_256) {
|
569
|
+
return rb_call_super(argc, argv);
|
570
|
+
}
|
571
|
+
|
572
|
+
VALUE length, data;
|
573
|
+
rb_scan_args(argc, argv, "02", &length, &data);
|
574
|
+
|
575
|
+
// For SHAKE algorithms
|
576
|
+
if (NIL_P(length)) {
|
577
|
+
rb_raise(digest_error_class, "output length must be specified for SHAKE algorithms");
|
578
|
+
}
|
579
|
+
|
580
|
+
// If data is provided, update the state before squeezing
|
581
|
+
if (!NIL_P(data)) {
|
582
|
+
rb_digest_update(self, data);
|
583
|
+
}
|
584
|
+
|
585
|
+
return rb_digest_squeeze(self, length);
|
586
|
+
}
|
587
|
+
|
588
|
+
/*
|
589
|
+
* :call-seq:
|
590
|
+
* hexdigest() -> string
|
591
|
+
* hexdigest([data]) -> string
|
592
|
+
* hexdigest(length) -> string
|
593
|
+
* hexdigest(length, data) -> string
|
594
|
+
*
|
595
|
+
* Returns the hexadecimal representation of the digest.
|
596
|
+
*
|
597
|
+
* +length+::
|
598
|
+
* The length of the output to squeeze when using SHAKE algorithms.
|
599
|
+
* This parameter is required for SHAKE algorithms.
|
600
|
+
*
|
601
|
+
* +data+::
|
602
|
+
* _optional_ Update state with additional data before returning digest.
|
603
|
+
*
|
604
|
+
* = example
|
605
|
+
* digest.hexdigest()
|
606
|
+
* digest.hexdigest('compute me')
|
607
|
+
* digest.hexdigest(12) # For SHAKE algorithms
|
608
|
+
* digest.hexdigest(12, 'compute me') # For SHAKE algorithms
|
609
|
+
*/
|
610
|
+
static VALUE rb_digest_hexdigest(int argc, VALUE* argv, VALUE self) {
|
611
|
+
MDX* mdx;
|
612
|
+
get_mdx(self, &mdx);
|
613
|
+
|
614
|
+
if (mdx->algorithm != SHAKE_128 && mdx->algorithm != SHAKE_256) {
|
615
|
+
return rb_call_super(argc, argv);
|
616
|
+
}
|
617
|
+
|
618
|
+
VALUE length, data;
|
619
|
+
rb_scan_args(argc, argv, "02", &length, &data);
|
620
|
+
|
621
|
+
if (NIL_P(length)) {
|
622
|
+
rb_raise(digest_error_class, "output length must be specified for SHAKE algorithms");
|
623
|
+
}
|
624
|
+
|
625
|
+
// If data is provided, update the state before squeezing
|
626
|
+
if (!NIL_P(data)) {
|
627
|
+
rb_digest_update(self, data);
|
628
|
+
}
|
629
|
+
|
630
|
+
return rb_digest_hex_squeeze(self, length);
|
631
|
+
}
|
632
|
+
|
633
|
+
/*
|
634
|
+
* :call-seq:
|
635
|
+
* SHA3::Digest.digest(name, data) -> string
|
636
|
+
*
|
637
|
+
* Returns the binary digest of the given +data+ using the algorithm specified by +name+.
|
638
|
+
*
|
639
|
+
* +name+::
|
640
|
+
* The hash algorithm to use (as a Symbol).
|
641
|
+
* Valid algorithms are:
|
642
|
+
* - :sha3_224
|
643
|
+
* - :sha3_256
|
644
|
+
* - :sha3_384
|
645
|
+
* - :sha3_512
|
646
|
+
* - :shake_128
|
647
|
+
* - :shake_256
|
648
|
+
*
|
649
|
+
* +data+::
|
650
|
+
* The data to hash.
|
651
|
+
*
|
652
|
+
* = example
|
653
|
+
* SHA3::Digest.digest(:sha3_256, "data to hash")
|
654
|
+
*
|
655
|
+
* = note
|
656
|
+
* This method defaults to squeezing 16 bytes for SHAKE128 and 32 bytes for SHAKE256.
|
657
|
+
* To squeeze a different length, use #squeeze instance method.
|
658
|
+
*/
|
659
|
+
static VALUE rb_digest_self_digest(VALUE klass, VALUE name, VALUE data) {
|
660
|
+
VALUE args[2];
|
661
|
+
algorithm_type algorithm;
|
662
|
+
|
663
|
+
/* For SHAKE algorithms, we need to handle them differently */
|
664
|
+
if (TYPE(name) == T_SYMBOL) {
|
665
|
+
ID symid = SYM2ID(name);
|
666
|
+
if (symid == shake_128_id || symid == shake_256_id) {
|
667
|
+
/* Create a new digest instance with the specified algorithm */
|
668
|
+
VALUE digest = rb_class_new_instance(1, &name, klass);
|
669
|
+
|
670
|
+
/* Update it with the data */
|
671
|
+
rb_digest_update(digest, data);
|
672
|
+
|
673
|
+
/* For SHAKE algorithms, use a default output length based on the security strength */
|
674
|
+
int output_length = (symid == shake_128_id) ? 16 : 32; /* 128/8 or 256/8 */
|
675
|
+
|
676
|
+
/* Return the squeezed output */
|
677
|
+
return rb_digest_squeeze(digest, INT2NUM(output_length));
|
678
|
+
}
|
679
|
+
}
|
680
|
+
|
681
|
+
/* Call the superclass method with arguments in reverse order */
|
682
|
+
args[0] = data;
|
683
|
+
args[1] = name;
|
684
|
+
|
685
|
+
return rb_call_super(2, args);
|
686
|
+
}
|
687
|
+
|
688
|
+
/*
|
689
|
+
* :call-seq:
|
690
|
+
* SHA3::Digest.hexdigest(name, data) -> string
|
691
|
+
*
|
692
|
+
* Returns the hexadecimal representation of the given +data+ using the algorithm specified by
|
693
|
+
* +name+.
|
694
|
+
*
|
695
|
+
* +name+::
|
696
|
+
* The hash algorithm to use (as a Symbol).
|
697
|
+
* Valid algorithms are:
|
698
|
+
* - :sha3_224
|
699
|
+
* - :sha3_256
|
700
|
+
* - :sha3_384
|
701
|
+
* - :sha3_512
|
702
|
+
* - :shake_128
|
703
|
+
* - :shake_256
|
704
|
+
*
|
705
|
+
* +data+::
|
706
|
+
* The data to hash.
|
707
|
+
*
|
708
|
+
* = example
|
709
|
+
* SHA3::Digest.hexdigest(:sha3_256, "data to hash")
|
710
|
+
*
|
711
|
+
* = note
|
712
|
+
* This method defaults to squeezing 16 bytes for SHAKE128 and 32 bytes for SHAKE256.
|
713
|
+
* To squeeze a different length, use #hex_squeeze instance method.
|
714
|
+
*/
|
715
|
+
static VALUE rb_digest_self_hexdigest(VALUE klass, VALUE name, VALUE data) {
|
716
|
+
VALUE args[2];
|
717
|
+
algorithm_type algorithm;
|
718
|
+
|
719
|
+
/* For SHAKE algorithms, we need to handle them differently */
|
720
|
+
if (TYPE(name) == T_SYMBOL) {
|
721
|
+
ID symid = SYM2ID(name);
|
722
|
+
if (symid == shake_128_id || symid == shake_256_id) {
|
723
|
+
/* Create a new digest instance with the specified algorithm */
|
724
|
+
VALUE digest = rb_class_new_instance(1, &name, klass);
|
725
|
+
|
726
|
+
/* Update it with the data */
|
727
|
+
rb_digest_update(digest, data);
|
728
|
+
|
729
|
+
/* For SHAKE algorithms, use a default output length based on the security strength */
|
730
|
+
int output_length = (symid == shake_128_id) ? 16 : 32; /* 128/8 or 256/8 */
|
731
|
+
|
732
|
+
/* Return the hexadecimal representation of the squeezed output */
|
733
|
+
return rb_digest_hex_squeeze(digest, INT2NUM(output_length));
|
734
|
+
}
|
735
|
+
}
|
736
|
+
|
737
|
+
/* Call the superclass method with arguments in reverse order */
|
738
|
+
args[0] = data;
|
739
|
+
args[1] = name;
|
250
740
|
|
251
|
-
|
252
|
-
cSHA3Digest = rb_define_class_under(mSHA3, "Digest", rb_path2class("Digest::Class"));
|
253
|
-
/* SHA3::Digest::DigestError (class) */
|
254
|
-
eSHA3DigestError = rb_define_class_under(cSHA3Digest, "DigestError", rb_eStandardError);
|
255
|
-
|
256
|
-
// SHA3::Digest (class) methods
|
257
|
-
rb_define_alloc_func(cSHA3Digest, c_digest_alloc);
|
258
|
-
rb_define_method(cSHA3Digest, "initialize", c_digest_init, -1);
|
259
|
-
rb_define_method(cSHA3Digest, "update", c_digest_update, 1);
|
260
|
-
rb_define_method(cSHA3Digest, "reset", c_digest_reset, 0);
|
261
|
-
rb_define_method(cSHA3Digest, "initialize_copy", c_digest_copy, 1);
|
262
|
-
rb_define_method(cSHA3Digest, "digest_length", c_digest_length, 0);
|
263
|
-
rb_define_method(cSHA3Digest, "block_length", c_digest_block_length, 0);
|
264
|
-
rb_define_method(cSHA3Digest, "name", c_digest_name, 0);
|
265
|
-
rb_define_private_method(cSHA3Digest, "finish", c_digest_finish, -1);
|
266
|
-
|
267
|
-
rb_define_alias(cSHA3Digest, "<<", "update");
|
268
|
-
|
269
|
-
return;
|
741
|
+
return rb_call_super(2, args);
|
270
742
|
}
|