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.
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 */