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.
@@ -0,0 +1,311 @@
1
+ #include "sp800_185.h"
2
+
3
+ #include "sha3.h"
4
+
5
+ /*** Function table for SP800-185 algorithms ***/
6
+ sp800_185_function_table_t sp800_185_functions[] = {{.algorithm = SP800_185_CSHAKE_128,
7
+ .name = "CSHAKE128",
8
+ .state_size = sizeof(cSHAKE_Instance),
9
+ .cshake = {.init = (sp800_185_init_fn)cSHAKE128_Initialize,
10
+ .update = (sp800_185_update_fn)cSHAKE128_Update,
11
+ .final = (sp800_185_final_fn)cSHAKE128_Final,
12
+ .squeeze = (sp800_185_squeeze_fn)cSHAKE128_Squeeze}},
13
+ {.algorithm = SP800_185_CSHAKE_256,
14
+ .name = "CSHAKE256",
15
+ .state_size = sizeof(cSHAKE_Instance),
16
+ .cshake = {.init = (sp800_185_init_fn)cSHAKE256_Initialize,
17
+ .update = (sp800_185_update_fn)cSHAKE256_Update,
18
+ .final = (sp800_185_final_fn)cSHAKE256_Final,
19
+ .squeeze = (sp800_185_squeeze_fn)cSHAKE256_Squeeze}},
20
+ {.algorithm = SP800_185_KMAC_128,
21
+ .name = "KMAC128",
22
+ .state_size = sizeof(KMAC_Instance),
23
+ .kmac = {.init = (sp800_185_init_key_fn)KMAC128_Initialize,
24
+ .update = (sp800_185_update_fn)KMAC128_Update,
25
+ .final = (sp800_185_final_fn)KMAC128_Final,
26
+ .squeeze = (sp800_185_squeeze_fn)KMAC128_Squeeze}},
27
+ {.algorithm = SP800_185_KMAC_256,
28
+ .name = "KMAC256",
29
+ .state_size = sizeof(KMAC_Instance),
30
+ .kmac = {.init = (sp800_185_init_key_fn)KMAC256_Initialize,
31
+ .update = (sp800_185_update_fn)KMAC256_Update,
32
+ .final = (sp800_185_final_fn)KMAC256_Final,
33
+ .squeeze = (sp800_185_squeeze_fn)KMAC256_Squeeze}}};
34
+
35
+ // Generic context allocation function
36
+ sp800_185_context_t *sp800_185_alloc_context(size_t context_size, size_t state_size) {
37
+ sp800_185_context_t *context = (sp800_185_context_t *)malloc(context_size);
38
+ if (!context) return NULL;
39
+
40
+ context->state = calloc(1, state_size);
41
+ if (!context->state) {
42
+ free(context);
43
+ return NULL;
44
+ }
45
+
46
+ context->error_class = Qnil; // Initialize error class to nil
47
+
48
+ return context;
49
+ }
50
+
51
+ // Generic context freeing function
52
+ void sp800_185_free_context(sp800_185_context_t *context) {
53
+ if (context) {
54
+ if (context->state) {
55
+ free(context->state);
56
+ }
57
+ free(context);
58
+ }
59
+ }
60
+
61
+ // Generic context size function
62
+ size_t sp800_185_context_size(const sp800_185_context_t *context, size_t struct_size) {
63
+ size_t size = struct_size;
64
+ if (context && context->functions) {
65
+ size += context->functions->state_size;
66
+ }
67
+ return size;
68
+ }
69
+
70
+ // Generic state copy function
71
+ void *sp800_185_copy_state(sp800_185_context_t *context) {
72
+ if (context->functions->state_size <= 0) {
73
+ rb_raise(context->error_class, "invalid state size");
74
+ }
75
+ void *state_copy = malloc(context->functions->state_size);
76
+
77
+ if (!state_copy) {
78
+ rb_raise(rb_eNoMemError, "failed to allocate memory for state copy");
79
+ }
80
+
81
+ memcpy(state_copy, context->state, context->functions->state_size);
82
+
83
+ return state_copy;
84
+ }
85
+
86
+ VALUE sp800_185_update(sp800_185_context_t *context, VALUE data) {
87
+ StringValue(data);
88
+ size_t data_len = (RSTRING_LEN(data) * 8);
89
+
90
+ if (data_len == 0) {
91
+ return Qnil;
92
+ }
93
+
94
+ // Use the function table to call the appropriate update function
95
+ int result;
96
+
97
+ // KMAC, cSHAKE, and ParallelHash share the same update function signature
98
+ switch (context->functions->algorithm) {
99
+ case SP800_185_CSHAKE_128:
100
+ case SP800_185_CSHAKE_256:
101
+ result =
102
+ context->functions->cshake.update(context->state, (const BitSequence *)RSTRING_PTR(data), data_len);
103
+ break;
104
+ case SP800_185_KMAC_128:
105
+ case SP800_185_KMAC_256:
106
+ result = context->functions->kmac.update(context->state, (const BitSequence *)RSTRING_PTR(data), data_len);
107
+ break;
108
+ default:
109
+ rb_raise(context->error_class, "unknown algorithm");
110
+ return Qnil;
111
+ }
112
+
113
+ if (result != 0) {
114
+ rb_raise(context->error_class, "failed to update %s state", context->functions->name);
115
+ }
116
+
117
+ return Qnil;
118
+ }
119
+
120
+ VALUE sp800_185_finish(sp800_185_context_t *context, VALUE output) {
121
+ // Create a new string if output isn't provided
122
+ if (NIL_P(output)) {
123
+ output = rb_str_new(0, context->output_length / 8);
124
+ } else {
125
+ StringValue(output);
126
+ rb_str_resize(output, context->output_length / 8);
127
+ }
128
+
129
+ // Use the function table to call the appropriate final function
130
+ int result;
131
+
132
+ switch (context->functions->algorithm) {
133
+ case SP800_185_CSHAKE_128:
134
+ case SP800_185_CSHAKE_256:
135
+ result = context->functions->cshake.final(context->state, (BitSequence *)RSTRING_PTR(output));
136
+ break;
137
+ case SP800_185_KMAC_128:
138
+ case SP800_185_KMAC_256:
139
+ result = context->functions->kmac.final(context->state, (BitSequence *)RSTRING_PTR(output));
140
+ break;
141
+ default:
142
+ rb_raise(context->error_class, "unknown algorithm");
143
+ return Qnil;
144
+ }
145
+
146
+ if (result != 0) {
147
+ rb_raise(context->error_class, "failed to finalize %s state", context->functions->name);
148
+ }
149
+
150
+ return output;
151
+ }
152
+
153
+ const char *sp800_185_name(sp800_185_context_t *context) { return context->functions->name; }
154
+
155
+ VALUE sp800_185_digest(sp800_185_context_t *context, VALUE data) {
156
+ if (context->output_length == 0) {
157
+ rb_raise(context->error_class, "use squeeze methods for arbitrary length output");
158
+ }
159
+
160
+ // Create a copy of the state for processing
161
+ void *state_copy = malloc(context->functions->state_size);
162
+ if (!state_copy) {
163
+ rb_raise(rb_eNoMemError, "failed to allocate memory for state copy");
164
+ }
165
+
166
+ memcpy(state_copy, context->state, context->functions->state_size);
167
+
168
+ int result;
169
+
170
+ // If data is provided, update the copy
171
+ if (!NIL_P(data)) {
172
+ StringValue(data);
173
+ size_t data_len = (RSTRING_LEN(data) * 8);
174
+
175
+ if (data_len > 0) {
176
+ switch (context->functions->algorithm) {
177
+ case SP800_185_CSHAKE_128:
178
+ case SP800_185_CSHAKE_256:
179
+ result =
180
+ context->functions->cshake.update(state_copy, (const BitSequence *)RSTRING_PTR(data), data_len);
181
+ break;
182
+ case SP800_185_KMAC_128:
183
+ case SP800_185_KMAC_256:
184
+ result =
185
+ context->functions->kmac.update(state_copy, (const BitSequence *)RSTRING_PTR(data), data_len);
186
+ break;
187
+ default:
188
+ free(state_copy);
189
+ rb_raise(context->error_class, "unknown algorithm");
190
+ return Qnil;
191
+ }
192
+
193
+ if (result != 0) {
194
+ free(state_copy);
195
+ rb_raise(context->error_class, "failed to update %s state", context->functions->name);
196
+ }
197
+ }
198
+ }
199
+
200
+ // Prepare output and finalize
201
+ VALUE output = rb_str_new(0, context->output_length / 8);
202
+
203
+ switch (context->functions->algorithm) {
204
+ case SP800_185_CSHAKE_128:
205
+ case SP800_185_CSHAKE_256:
206
+ result = context->functions->cshake.final(state_copy, (BitSequence *)RSTRING_PTR(output));
207
+ break;
208
+ case SP800_185_KMAC_128:
209
+ case SP800_185_KMAC_256:
210
+ result = context->functions->kmac.final(state_copy, (BitSequence *)RSTRING_PTR(output));
211
+ break;
212
+ default:
213
+ free(state_copy);
214
+ rb_raise(context->error_class, "unknown algorithm");
215
+ return Qnil;
216
+ }
217
+
218
+ free(state_copy);
219
+
220
+ if (result != 0) {
221
+ rb_raise(context->error_class, "failed to finalize %s state", context->functions->name);
222
+ }
223
+
224
+ return output;
225
+ }
226
+
227
+ VALUE sp800_185_hexdigest(sp800_185_context_t *context, VALUE data) {
228
+ VALUE bin_str = sp800_185_digest(context, data);
229
+ return rb_funcall(bin_str, rb_intern("unpack1"), 1, rb_str_new2("H*"));
230
+ }
231
+
232
+ VALUE sp800_185_squeeze(sp800_185_context_t *context, VALUE length) {
233
+ if (context->output_length != 0) {
234
+ rb_raise(context->error_class, "use digest methods for fixed-length output");
235
+ }
236
+
237
+ size_t output_byte_len;
238
+ VALUE str;
239
+
240
+ Check_Type(length, T_FIXNUM);
241
+ output_byte_len = NUM2ULONG(length);
242
+
243
+ if (output_byte_len <= 0) {
244
+ rb_raise(context->error_class, "output length must be specified");
245
+ }
246
+
247
+ // Create a copy of the state for processing
248
+ void *state_copy = malloc(context->functions->state_size);
249
+ if (!state_copy) {
250
+ rb_raise(rb_eNoMemError, "failed to allocate memory for state copy");
251
+ }
252
+
253
+ memcpy(state_copy, context->state, context->functions->state_size);
254
+
255
+ // First transition the state to FINAL
256
+ VALUE dummy_output = rb_str_new(0, 0);
257
+ int result;
258
+
259
+ switch (context->functions->algorithm) {
260
+ case SP800_185_CSHAKE_128:
261
+ case SP800_185_CSHAKE_256:
262
+ result = context->functions->cshake.final(state_copy, (BitSequence *)RSTRING_PTR(dummy_output));
263
+ break;
264
+ case SP800_185_KMAC_128:
265
+ case SP800_185_KMAC_256:
266
+ result = context->functions->kmac.final(state_copy, (BitSequence *)RSTRING_PTR(dummy_output));
267
+ break;
268
+ default:
269
+ free(state_copy);
270
+ rb_raise(context->error_class, "unknown algorithm");
271
+ return Qnil;
272
+ }
273
+
274
+ if (result != 0) {
275
+ free(state_copy);
276
+ rb_raise(context->error_class, "failed to finalize %s state", context->functions->name);
277
+ }
278
+
279
+ // Allocate the output buffer for the specified number of bytes
280
+ str = rb_str_new(0, output_byte_len);
281
+
282
+ // Use the function table to call the appropriate squeeze function
283
+ switch (context->functions->algorithm) {
284
+ case SP800_185_CSHAKE_128:
285
+ case SP800_185_CSHAKE_256:
286
+ result =
287
+ context->functions->cshake.squeeze(state_copy, (BitSequence *)RSTRING_PTR(str), output_byte_len * 8);
288
+ break;
289
+ case SP800_185_KMAC_128:
290
+ case SP800_185_KMAC_256:
291
+ result = context->functions->kmac.squeeze(state_copy, (BitSequence *)RSTRING_PTR(str), output_byte_len * 8);
292
+ break;
293
+ default:
294
+ free(state_copy);
295
+ rb_raise(context->error_class, "unknown algorithm");
296
+ return Qnil;
297
+ }
298
+
299
+ free(state_copy);
300
+
301
+ if (result != 0) {
302
+ rb_raise(context->error_class, "failed to squeeze %s", context->functions->name);
303
+ }
304
+
305
+ return str;
306
+ }
307
+
308
+ VALUE sp800_185_hex_squeeze(sp800_185_context_t *context, VALUE length) {
309
+ VALUE binary_result = sp800_185_squeeze(context, length);
310
+ return rb_funcall(binary_result, rb_intern("unpack1"), 1, rb_str_new2("H*"));
311
+ }
@@ -0,0 +1,94 @@
1
+ #ifndef _SHA3_SP800_185_H_
2
+ #define _SHA3_SP800_185_H_
3
+
4
+ #include <ruby.h>
5
+ #include <ruby/thread.h>
6
+
7
+ #include "KeccakHash.h"
8
+ #include "SP800-185.h"
9
+
10
+ #ifdef __cplusplus
11
+ extern "C" {
12
+ #endif
13
+
14
+ /* SP800-185 algorithm family */
15
+ typedef enum {
16
+ SP800_185_CSHAKE_128 = 0,
17
+ SP800_185_CSHAKE_256,
18
+ SP800_185_KMAC_128,
19
+ SP800_185_KMAC_256,
20
+ } sp800_185_algorithm_t;
21
+
22
+ /* Common function pointer typedefs for SP800-185 algorithms */
23
+ typedef int (*sp800_185_init_fn)(void *state, size_t capacity, const BitSequence *N, size_t NLen, const BitSequence *S,
24
+ size_t SLen);
25
+
26
+ typedef int (*sp800_185_init_key_fn)(void *state, const BitSequence *key, BitLength keyBitLen, BitLength outputBitLen,
27
+ const BitSequence *customization, BitLength customBitLen);
28
+
29
+ typedef int (*sp800_185_update_fn)(void *state, const BitSequence *data, size_t dataLen);
30
+ typedef int (*sp800_185_final_fn)(void *state, BitSequence *output);
31
+ typedef int (*sp800_185_squeeze_fn)(void *state, BitSequence *output, size_t outputLen);
32
+
33
+ /* Function table for SP800-185 algorithm operations */
34
+ typedef struct {
35
+ sp800_185_algorithm_t algorithm;
36
+ const char *name;
37
+ size_t state_size;
38
+
39
+ union {
40
+ struct {
41
+ sp800_185_init_fn init;
42
+ sp800_185_update_fn update;
43
+ sp800_185_final_fn final;
44
+ sp800_185_squeeze_fn squeeze;
45
+ } cshake;
46
+
47
+ struct {
48
+ sp800_185_init_key_fn init;
49
+ sp800_185_update_fn update;
50
+ sp800_185_final_fn final;
51
+ sp800_185_squeeze_fn squeeze;
52
+ } kmac;
53
+ };
54
+ } sp800_185_function_table_t;
55
+
56
+ /* Base context structure for SP800-185 algorithms */
57
+ typedef struct {
58
+ void *state;
59
+ const sp800_185_function_table_t *functions;
60
+ size_t output_length;
61
+ VALUE error_class;
62
+ } sp800_185_context_t;
63
+
64
+ /* Global variables */
65
+ extern sp800_185_function_table_t sp800_185_functions[];
66
+
67
+ extern sp800_185_context_t *sp800_185_alloc_context(size_t, size_t);
68
+ extern size_t sp800_185_context_size(const sp800_185_context_t *, size_t);
69
+ extern void sp800_185_free_context(sp800_185_context_t *);
70
+
71
+ /* Helper functions - these are now internal C functions with an additional context parameter */
72
+ VALUE sp800_185_update(sp800_185_context_t *context, VALUE data);
73
+ VALUE sp800_185_digest(sp800_185_context_t *context, VALUE data);
74
+ VALUE sp800_185_hexdigest(sp800_185_context_t *context, VALUE data);
75
+ VALUE sp800_185_finish(sp800_185_context_t *context, VALUE output);
76
+ VALUE sp800_185_squeeze(sp800_185_context_t *context, VALUE length);
77
+ VALUE sp800_185_hex_squeeze(sp800_185_context_t *context, VALUE length);
78
+ const char *sp800_185_name(sp800_185_context_t *context);
79
+
80
+ // Macro to define common Ruby methods
81
+ #define DEFINE_SP800_185_METHOD(name) static VALUE rb_sp800_185_##name(int argc, VALUE *argv, VALUE self);
82
+
83
+ DEFINE_SP800_185_METHOD(update)
84
+ DEFINE_SP800_185_METHOD(finish)
85
+ DEFINE_SP800_185_METHOD(digest)
86
+ DEFINE_SP800_185_METHOD(hexdigest)
87
+ DEFINE_SP800_185_METHOD(squeeze)
88
+ DEFINE_SP800_185_METHOD(hex_squeeze)
89
+
90
+ #ifdef __cplusplus
91
+ }
92
+ #endif
93
+
94
+ #endif /* _SHA3_SP800_185_H_ */
data/lib/constants.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SHA3
4
- VERSION = '2.1.0'
4
+ VERSION = '2.2.0'
5
5
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sha3
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Johanns Gregorian
@@ -34,12 +34,12 @@ cert_chain:
34
34
  UjZtrp/rHLfHln46RvB+a1NlMRWxtJ7mQc/CMEbT+cpHlzuYa9qGakA4TmMpK10h
35
35
  uYUv/V6CD4iTEMby0dopwHt5NqE=
36
36
  -----END CERTIFICATE-----
37
- date: 2025-03-03 00:00:00.000000000 Z
37
+ date: 2025-03-20 00:00:00.000000000 Z
38
38
  dependencies: []
39
39
  description: |
40
40
  A high-performance native binding to the SHA3 (FIPS 202) cryptographic hashing algorithms, based on the XKCP - eXtended Keccak Code Package.
41
41
  This gem provides support for the standard SHA-3 fixed-length functions (224, 256, 384, and 512 bits),
42
- as well as the SHAKE128/SHAKE256 extendable-output functions (XOFs) and KMAC (Keccak Message Authentication Code) as specified in NIST SP 800-185.'
42
+ as well as the SHAKE128/SHAKE256 extendable-output functions (XOFs), cSHAKE128/256, and KMAC as specified in NIST SP 800-185.'
43
43
  email:
44
44
  - io+sha3@jsg.io
45
45
  executables: []
@@ -52,6 +52,9 @@ files:
52
52
  - ".rdoc_options"
53
53
  - ".rspec"
54
54
  - ".rubocop.yml"
55
+ - ".vscode/launch.json"
56
+ - ".vscode/settings.json"
57
+ - ".vscode/tasks.json"
55
58
  - CHANGELOG.md
56
59
  - Gemfile
57
60
  - LICENSE.txt
@@ -60,6 +63,8 @@ files:
60
63
  - certs/io+sha3@jsg.io.pem
61
64
  - doc/sha3.rb
62
65
  - ext/sha3/config.h
66
+ - ext/sha3/cshake.c
67
+ - ext/sha3/cshake.h
63
68
  - ext/sha3/digest.c
64
69
  - ext/sha3/digest.h
65
70
  - ext/sha3/extconf.rb
@@ -94,6 +99,8 @@ files:
94
99
  - ext/sha3/lib/low/common/SnP-Relaned.h
95
100
  - ext/sha3/sha3.c
96
101
  - ext/sha3/sha3.h
102
+ - ext/sha3/sp800_185.c
103
+ - ext/sha3/sp800_185.h
97
104
  - lib/constants.rb
98
105
  - lib/sha3.rb
99
106
  homepage: https://github.com/johanns/sha3
@@ -124,6 +131,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
124
131
  requirements: []
125
132
  rubygems_version: 3.6.2
126
133
  specification_version: 4
127
- summary: 'SHA3 (FIPS 202) cryptographic hashing algorithms: SHA3-224/256/384/512,
128
- SHAKE128/256, and KMAC'
134
+ summary: SHA-3 (FIPS 202), SHAKE128/SHAKE256, cSHAKE128/cSHAKE256, and KMAC (NIST
135
+ SP 800-185), powered by XKCP.
129
136
  test_files: []
metadata.gz.sig CHANGED
Binary file