sha3 2.1.0 → 2.2.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 +4 -51
- data/.rubocop.yml +1 -1
- data/.vscode/launch.json +15 -0
- data/.vscode/settings.json +9 -0
- data/.vscode/tasks.json +29 -0
- data/CHANGELOG.md +15 -0
- data/README.md +103 -53
- data/ext/sha3/cshake.c +391 -0
- data/ext/sha3/cshake.h +17 -0
- data/ext/sha3/digest.c +90 -86
- data/ext/sha3/digest.h +2 -5
- data/ext/sha3/extconf.rb +11 -5
- data/ext/sha3/kmac.c +188 -218
- data/ext/sha3/kmac.h +3 -3
- data/ext/sha3/sha3.c +6 -2
- data/ext/sha3/sp800_185.c +311 -0
- data/ext/sha3/sp800_185.h +94 -0
- data/lib/constants.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +12 -5
- metadata.gz.sig +0 -0
data/ext/sha3/cshake.c
ADDED
@@ -0,0 +1,391 @@
|
|
1
|
+
#include "cshake.h"
|
2
|
+
|
3
|
+
#include "sha3.h"
|
4
|
+
#include "sp800_185.h"
|
5
|
+
|
6
|
+
/*** Types and structs ***/
|
7
|
+
typedef struct {
|
8
|
+
sp800_185_context_t base;
|
9
|
+
} sha3_cshake_context_t;
|
10
|
+
|
11
|
+
/*** Function prototypes ***/
|
12
|
+
static void sha3_cshake_free_context(void *);
|
13
|
+
static size_t sha3_cshake_context_size(const void *);
|
14
|
+
|
15
|
+
/* Allocation and initialization */
|
16
|
+
static VALUE rb_sha3_cshake_alloc(VALUE);
|
17
|
+
static VALUE rb_sha3_cshake_init(int, VALUE *, VALUE);
|
18
|
+
static VALUE rb_sha3_cshake_copy(VALUE, VALUE);
|
19
|
+
|
20
|
+
/* Core digest operations */
|
21
|
+
static VALUE rb_sha3_cshake_finish(int argc, VALUE *argv, VALUE self);
|
22
|
+
static VALUE rb_sha3_cshake_update(VALUE, VALUE);
|
23
|
+
|
24
|
+
/* Digest properties */
|
25
|
+
static VALUE rb_sha3_cshake_name(VALUE self);
|
26
|
+
|
27
|
+
/* Output methods */
|
28
|
+
static VALUE rb_sha3_cshake_digest(int argc, VALUE *argv, VALUE self);
|
29
|
+
static VALUE rb_sha3_cshake_hexdigest(int argc, VALUE *argv, VALUE self);
|
30
|
+
static VALUE rb_sha3_cshake_squeeze(VALUE self, VALUE length);
|
31
|
+
static VALUE rb_sha3_cshake_hex_squeeze(VALUE self, VALUE length);
|
32
|
+
|
33
|
+
/*** Global variables ***/
|
34
|
+
VALUE _sha3_cshake_class;
|
35
|
+
VALUE _sha3_cshake_error_class;
|
36
|
+
|
37
|
+
/* Define the ID variables */
|
38
|
+
static ID _cshake_128_id;
|
39
|
+
static ID _cshake_256_id;
|
40
|
+
|
41
|
+
/* TypedData structure for sha3_cshake_context_t */
|
42
|
+
static const rb_data_type_t sha3_cshake_data_type = {
|
43
|
+
"SHA3::CSHAKE",
|
44
|
+
{
|
45
|
+
NULL,
|
46
|
+
sha3_cshake_free_context, // Use our free function directly
|
47
|
+
sha3_cshake_context_size, /* We'll do our own size calculation */
|
48
|
+
NULL, /* dcompact field */
|
49
|
+
},
|
50
|
+
NULL,
|
51
|
+
NULL,
|
52
|
+
RUBY_TYPED_FREE_IMMEDIATELY};
|
53
|
+
|
54
|
+
// Helper function to extract context from a Ruby object
|
55
|
+
void get_cshake_context(VALUE obj, sp800_185_context_t **context) {
|
56
|
+
sha3_cshake_context_t *cshake_ctx;
|
57
|
+
TypedData_Get_Struct(obj, sha3_cshake_context_t, &sha3_cshake_data_type, cshake_ctx);
|
58
|
+
*context = &cshake_ctx->base;
|
59
|
+
}
|
60
|
+
|
61
|
+
void Init_sha3_cshake(void) {
|
62
|
+
_cshake_128_id = rb_intern("cshake_128");
|
63
|
+
_cshake_256_id = rb_intern("cshake_256");
|
64
|
+
|
65
|
+
if (!_sha3_module) {
|
66
|
+
_sha3_module = rb_define_module("SHA3");
|
67
|
+
}
|
68
|
+
|
69
|
+
/*
|
70
|
+
* Document-class: SHA3::CSHAKE
|
71
|
+
*
|
72
|
+
* CSHAKE (Customizable SHAKE) is a family of functions that allow for domain separation and customization of the
|
73
|
+
* output. It is based on the cSHAKE algorithm defined in NIST SP800-185.
|
74
|
+
*/
|
75
|
+
_sha3_cshake_class = rb_define_class_under(_sha3_module, "CSHAKE", rb_cObject);
|
76
|
+
|
77
|
+
/*
|
78
|
+
* Document-class: SHA3::CSHAKE::Error
|
79
|
+
*
|
80
|
+
* All KMAC methods raise this exception on error.
|
81
|
+
*
|
82
|
+
* It is a subclass of the StandardError class -- see the Ruby documentation
|
83
|
+
* for more information.
|
84
|
+
*/
|
85
|
+
_sha3_cshake_error_class = rb_define_class_under(_sha3_cshake_class, "Error", rb_eStandardError);
|
86
|
+
|
87
|
+
rb_define_alloc_func(_sha3_cshake_class, rb_sha3_cshake_alloc);
|
88
|
+
rb_define_method(_sha3_cshake_class, "initialize", rb_sha3_cshake_init, -1);
|
89
|
+
rb_define_method(_sha3_cshake_class, "initialize_copy", rb_sha3_cshake_copy, 1);
|
90
|
+
|
91
|
+
// Define instance methods
|
92
|
+
rb_define_method(_sha3_cshake_class, "update", rb_sha3_cshake_update, 1);
|
93
|
+
rb_define_method(_sha3_cshake_class, "name", rb_sha3_cshake_name, 0);
|
94
|
+
|
95
|
+
rb_define_method(_sha3_cshake_class, "digest", rb_sha3_cshake_digest, -1);
|
96
|
+
rb_define_method(_sha3_cshake_class, "hexdigest", rb_sha3_cshake_hexdigest, -1);
|
97
|
+
|
98
|
+
rb_define_method(_sha3_cshake_class, "squeeze", rb_sha3_cshake_squeeze, 1);
|
99
|
+
rb_define_method(_sha3_cshake_class, "hex_squeeze", rb_sha3_cshake_hex_squeeze, 1);
|
100
|
+
|
101
|
+
rb_define_private_method(_sha3_cshake_class, "finish", rb_sha3_cshake_finish, -1);
|
102
|
+
|
103
|
+
rb_define_alias(_sha3_cshake_class, "<<", "update");
|
104
|
+
|
105
|
+
return;
|
106
|
+
}
|
107
|
+
|
108
|
+
static void sha3_cshake_free_context(void *ptr) { sp800_185_free_context((sp800_185_context_t *)ptr); }
|
109
|
+
|
110
|
+
static size_t sha3_cshake_context_size(const void *ptr) {
|
111
|
+
return sp800_185_context_size((const sp800_185_context_t *)ptr, sizeof(sha3_cshake_context_t));
|
112
|
+
}
|
113
|
+
|
114
|
+
static VALUE rb_sha3_cshake_alloc(VALUE klass) {
|
115
|
+
sha3_cshake_context_t *context =
|
116
|
+
(sha3_cshake_context_t *)sp800_185_alloc_context(sizeof(sha3_cshake_context_t), sizeof(cSHAKE_Instance));
|
117
|
+
|
118
|
+
if (!context) {
|
119
|
+
rb_raise(_sha3_cshake_error_class, "failed to allocate memory");
|
120
|
+
}
|
121
|
+
|
122
|
+
// Create the Ruby object with TypedData - this will automatically handle freeing
|
123
|
+
VALUE obj = TypedData_Wrap_Struct(klass, &sha3_cshake_data_type, context);
|
124
|
+
|
125
|
+
return obj;
|
126
|
+
}
|
127
|
+
|
128
|
+
/*
|
129
|
+
* :call-seq:
|
130
|
+
* ::new(algorithm, output_length) -> cshake
|
131
|
+
* ::new(algorithm, output_length, name: "", customization: "") -> cshake
|
132
|
+
*
|
133
|
+
* Initializes a new CSHAKE instance with the specified algorithm and output length.
|
134
|
+
*
|
135
|
+
* +algorithm+::
|
136
|
+
* The CSHAKE algorithm to use (as a Symbol) - :cshake_128 or :cshake_256
|
137
|
+
*
|
138
|
+
* +output_length+::
|
139
|
+
* The length of the output in bytes. Set to 0 for an arbitrarily-long output using "squeeze" (XOF) methods.
|
140
|
+
*
|
141
|
+
* +name+::
|
142
|
+
* _optional_ The name string to use for domain separation
|
143
|
+
*
|
144
|
+
* +customization+::
|
145
|
+
* _optional_ The customization string to use
|
146
|
+
*
|
147
|
+
* = example
|
148
|
+
* # Initialize instance for fix-ed-length operation
|
149
|
+
* cshake = SHA3::CSHAKE.new(:cshake_128, 32, name: 'my-app')
|
150
|
+
* cshake << 'data...'
|
151
|
+
* cshake.hexdigest
|
152
|
+
*
|
153
|
+
* # Initialize instance for XOF operation (arbitrary-long output)
|
154
|
+
* cshake = SHA3::CSHAKE.new(:cshake_256, 0, customization: 'Email Signature')
|
155
|
+
* cshask.update('data...')
|
156
|
+
* cshake.squeeze(64)
|
157
|
+
*/
|
158
|
+
static VALUE rb_sha3_cshake_init(int argc, VALUE *argv, VALUE self) {
|
159
|
+
VALUE algorithm, length, keywords;
|
160
|
+
|
161
|
+
rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS, argc, argv, "2:", &algorithm, &length, &keywords);
|
162
|
+
|
163
|
+
if (NIL_P(algorithm)) {
|
164
|
+
rb_raise(rb_eArgError, "missing keyword: algorithm");
|
165
|
+
}
|
166
|
+
Check_Type(algorithm, T_SYMBOL);
|
167
|
+
|
168
|
+
if (NIL_P(length)) {
|
169
|
+
rb_raise(rb_eArgError, "missing keyword: length");
|
170
|
+
}
|
171
|
+
Check_Type(length, T_FIXNUM);
|
172
|
+
|
173
|
+
if (NUM2INT(length) < 0) {
|
174
|
+
rb_raise(rb_eArgError, "output length must be non-negative");
|
175
|
+
}
|
176
|
+
|
177
|
+
ID table[] = {
|
178
|
+
rb_intern("name"),
|
179
|
+
rb_intern("customization"),
|
180
|
+
};
|
181
|
+
|
182
|
+
VALUE values[3];
|
183
|
+
rb_get_kwargs(keywords, table, 0, 3, values);
|
184
|
+
|
185
|
+
VALUE name_str = values[0] == Qundef ? rb_str_new2("") : values[0];
|
186
|
+
StringValue(name_str);
|
187
|
+
|
188
|
+
VALUE customization = values[1] == Qundef ? rb_str_new2("") : values[1];
|
189
|
+
StringValue(customization);
|
190
|
+
|
191
|
+
sha3_cshake_context_t *context;
|
192
|
+
TypedData_Get_Struct(self, sha3_cshake_context_t, &sha3_cshake_data_type, context);
|
193
|
+
|
194
|
+
// Store the output length in bits
|
195
|
+
context->base.output_length = NUM2INT(length) * 8;
|
196
|
+
context->base.error_class = _sha3_cshake_error_class;
|
197
|
+
|
198
|
+
// Find the appropriate function table based on the algorithm
|
199
|
+
if (algorithm == ID2SYM(_cshake_128_id)) {
|
200
|
+
context->base.functions = &sp800_185_functions[SP800_185_CSHAKE_128];
|
201
|
+
} else if (algorithm == ID2SYM(_cshake_256_id)) {
|
202
|
+
context->base.functions = &sp800_185_functions[SP800_185_CSHAKE_256];
|
203
|
+
} else {
|
204
|
+
rb_raise(rb_eArgError, "invalid algorithm: %s", rb_id2name(SYM2ID(algorithm)));
|
205
|
+
}
|
206
|
+
|
207
|
+
// Initialize the state using the function table
|
208
|
+
int result = context->base.functions->cshake.init(
|
209
|
+
context->base.state, context->base.output_length, (BitSequence *)RSTRING_PTR(name_str),
|
210
|
+
RSTRING_LEN(name_str) * 8, (BitSequence *)RSTRING_PTR(customization), RSTRING_LEN(customization) * 8);
|
211
|
+
|
212
|
+
if (result != 0) {
|
213
|
+
rb_raise(_sha3_cshake_error_class, "failed to initialize %s algorithm", context->base.functions->name);
|
214
|
+
}
|
215
|
+
|
216
|
+
return self;
|
217
|
+
}
|
218
|
+
|
219
|
+
/*
|
220
|
+
* :call-seq:
|
221
|
+
* ::copy(other) -> cshake
|
222
|
+
*
|
223
|
+
* Creates a copy of the CSHAKE instance.
|
224
|
+
*
|
225
|
+
* +other+::
|
226
|
+
* The CSHAKE instance to copy.
|
227
|
+
*
|
228
|
+
* = example
|
229
|
+
* cshake2 = cshake.dup
|
230
|
+
*/
|
231
|
+
static VALUE rb_sha3_cshake_copy(VALUE self, VALUE other) {
|
232
|
+
sha3_cshake_context_t *context, *other_context;
|
233
|
+
|
234
|
+
rb_check_frozen(self);
|
235
|
+
if (self == other) {
|
236
|
+
return self;
|
237
|
+
}
|
238
|
+
|
239
|
+
if (!rb_obj_is_kind_of(other, _sha3_cshake_class)) {
|
240
|
+
rb_raise(rb_eTypeError, "wrong argument (%s)! (expected %s)", rb_obj_classname(other),
|
241
|
+
rb_class2name(_sha3_cshake_class));
|
242
|
+
}
|
243
|
+
|
244
|
+
TypedData_Get_Struct(other, sha3_cshake_context_t, &sha3_cshake_data_type, other_context);
|
245
|
+
TypedData_Get_Struct(self, sha3_cshake_context_t, &sha3_cshake_data_type, context);
|
246
|
+
|
247
|
+
// Copy the base context attributes
|
248
|
+
context->base.functions = other_context->base.functions;
|
249
|
+
context->base.output_length = other_context->base.output_length;
|
250
|
+
|
251
|
+
// Copy the algorithm-specific state
|
252
|
+
memcpy(context->base.state, other_context->base.state, context->base.functions->state_size);
|
253
|
+
|
254
|
+
return self;
|
255
|
+
}
|
256
|
+
|
257
|
+
/*
|
258
|
+
* :call-seq:
|
259
|
+
* update(string) -> cshake
|
260
|
+
*
|
261
|
+
* Updates the CSHAKE instance with the provided string.
|
262
|
+
*
|
263
|
+
* +string+::
|
264
|
+
* The string to update the CSHAKE with.
|
265
|
+
*
|
266
|
+
* = example
|
267
|
+
* cshake.update("more data")
|
268
|
+
* cshake << "more data" # alias for update
|
269
|
+
*/
|
270
|
+
static VALUE rb_sha3_cshake_update(VALUE self, VALUE data) {
|
271
|
+
sp800_185_context_t *context;
|
272
|
+
get_cshake_context(self, &context);
|
273
|
+
sp800_185_update(context, data);
|
274
|
+
|
275
|
+
return self;
|
276
|
+
}
|
277
|
+
|
278
|
+
/*
|
279
|
+
* :call-seq:
|
280
|
+
* name -> string
|
281
|
+
*
|
282
|
+
* Returns the name of the CSHAKE instance.
|
283
|
+
*/
|
284
|
+
static VALUE rb_sha3_cshake_name(VALUE self) {
|
285
|
+
sp800_185_context_t *context;
|
286
|
+
get_cshake_context(self, &context);
|
287
|
+
|
288
|
+
return rb_str_new2(sp800_185_name(context));
|
289
|
+
}
|
290
|
+
|
291
|
+
/*
|
292
|
+
* :call-seq:
|
293
|
+
* finish([message]) -> string
|
294
|
+
*
|
295
|
+
* Returns the final CSHAKE digest as a binary string.
|
296
|
+
*
|
297
|
+
* +message+::
|
298
|
+
* _optional_ Output buffer to receive the final CSHAKE value.
|
299
|
+
*
|
300
|
+
* = example
|
301
|
+
* cshake.finish
|
302
|
+
*/
|
303
|
+
static VALUE rb_sha3_cshake_finish(int argc, VALUE *argv, VALUE self) {
|
304
|
+
sp800_185_context_t *context;
|
305
|
+
get_cshake_context(self, &context);
|
306
|
+
|
307
|
+
VALUE output = argc > 0 ? argv[0] : Qnil;
|
308
|
+
return sp800_185_finish(context, output);
|
309
|
+
}
|
310
|
+
|
311
|
+
/*
|
312
|
+
* :call-seq:
|
313
|
+
* digest([data]) -> string
|
314
|
+
*
|
315
|
+
* Returns the digest of the CSHAKE instance.
|
316
|
+
*
|
317
|
+
* +data+::
|
318
|
+
* _optional_ Additional data to include in the digest.
|
319
|
+
*
|
320
|
+
* = example
|
321
|
+
* cshake.digest
|
322
|
+
* cshake.digest("final chunk")
|
323
|
+
*/
|
324
|
+
static VALUE rb_sha3_cshake_digest(int argc, VALUE *argv, VALUE self) {
|
325
|
+
sp800_185_context_t *context;
|
326
|
+
get_cshake_context(self, &context);
|
327
|
+
|
328
|
+
VALUE data = argc > 0 ? argv[0] : Qnil;
|
329
|
+
|
330
|
+
return sp800_185_digest(context, data);
|
331
|
+
}
|
332
|
+
|
333
|
+
/*
|
334
|
+
* :call-seq:
|
335
|
+
* hexdigest([data]) -> string
|
336
|
+
*
|
337
|
+
* Returns the hexadecimal digest of the CSHAKE instance.
|
338
|
+
*
|
339
|
+
* +data+::
|
340
|
+
* _optional_ Additional data to include in the digest.
|
341
|
+
*
|
342
|
+
* = example
|
343
|
+
* cshake.hexdigest
|
344
|
+
* cshake.hexdigest("final chunk")
|
345
|
+
*/
|
346
|
+
static VALUE rb_sha3_cshake_hexdigest(int argc, VALUE *argv, VALUE self) {
|
347
|
+
sp800_185_context_t *context;
|
348
|
+
get_cshake_context(self, &context);
|
349
|
+
|
350
|
+
VALUE data = argc > 0 ? argv[0] : Qnil;
|
351
|
+
|
352
|
+
return sp800_185_hexdigest(context, data);
|
353
|
+
}
|
354
|
+
|
355
|
+
/*
|
356
|
+
* :call-seq:
|
357
|
+
* squeeze(length) -> string
|
358
|
+
*
|
359
|
+
* Returns the CSHAKE digest with the specified length.
|
360
|
+
*
|
361
|
+
* +length+::
|
362
|
+
* The length of the output in bytes.
|
363
|
+
*
|
364
|
+
* = example
|
365
|
+
* cshake.squeeze(32)
|
366
|
+
*/
|
367
|
+
static VALUE rb_sha3_cshake_squeeze(VALUE self, VALUE length) {
|
368
|
+
sp800_185_context_t *context;
|
369
|
+
get_cshake_context(self, &context);
|
370
|
+
|
371
|
+
return sp800_185_squeeze(context, length);
|
372
|
+
}
|
373
|
+
|
374
|
+
/*
|
375
|
+
* :call-seq:
|
376
|
+
* hex_squeeze(length) -> string
|
377
|
+
*
|
378
|
+
* Returns the hexadecimal CSHAKE digest with the specified length.
|
379
|
+
*
|
380
|
+
* +length+::
|
381
|
+
* The length of the output in bytes.
|
382
|
+
*
|
383
|
+
* = example
|
384
|
+
* cshake.hex_squeeze(32)
|
385
|
+
*/
|
386
|
+
static VALUE rb_sha3_cshake_hex_squeeze(VALUE self, VALUE length) {
|
387
|
+
sp800_185_context_t *context;
|
388
|
+
get_cshake_context(self, &context);
|
389
|
+
|
390
|
+
return sp800_185_hex_squeeze(context, length);
|
391
|
+
}
|
data/ext/sha3/cshake.h
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
// Copyright (c) 2012 - 2025 Johanns Gregorian <io+sha3@jsg.io>
|
2
|
+
|
3
|
+
#ifndef _SHA3_CSHAKE_H
|
4
|
+
#define _SHA3_CSHAKE_H
|
5
|
+
|
6
|
+
#ifdef __cplusplus
|
7
|
+
extern "C" {
|
8
|
+
#endif
|
9
|
+
|
10
|
+
/* Function prototypes */
|
11
|
+
void Init_sha3_cshake(void);
|
12
|
+
|
13
|
+
#ifdef __cplusplus
|
14
|
+
}
|
15
|
+
#endif
|
16
|
+
|
17
|
+
#endif /* SHA3_CSHAKE_H */
|