sha3 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sha3 might be problematic. Click here for more details.
- data/.document +2 -1
- data/.gitignore +3 -6
- data/.travis.yml +8 -2
- data/.yardopts +1 -1
- data/ChangeLog.rdoc +3 -0
- data/README.rdoc +82 -21
- data/Rakefile +1 -1
- data/ext/sha3/KeccakF-1600-reference.c-arch +300 -0
- data/ext/sha3/_sha3.c +288 -20
- data/ext/sha3/_sha3.h +32 -0
- data/ext/sha3/extconf.rb +5 -2
- data/lib/sha3.rb +1 -21
- data/lib/sha3/version.rb +5 -1
- data/sha3.gemspec +1 -1
- data/spec/generate_tests.rb +84 -0
- data/spec/sha3_core_spec.rb +123 -0
- data/tests.sh +26 -0
- metadata +11 -15
- data/spec/_sha3_spec.rb +0 -8
- data/spec/long_msg_kat_224_spec.rb +0 -520
- data/spec/short_msg_kat_224_spec.rb +0 -2055
- data/spec/short_msg_kat_256_spec.rb +0 -2055
- data/spec/short_msg_kat_384_spec.rb +0 -2055
- data/spec/short_msg_kat_512_spec.rb +0 -2055
data/ext/sha3/_sha3.c
CHANGED
@@ -1,38 +1,306 @@
|
|
1
|
-
#include
|
2
|
-
#include "KeccakNISTInterface.h"
|
1
|
+
#include "_sha3.h"
|
3
2
|
|
4
|
-
|
3
|
+
/* Document-module: SHA3
|
4
|
+
* SHA3
|
5
|
+
*/
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
/* Document-class: SHA3::Digest < Digest::Class
|
8
|
+
* SHA3::Digest allows you to compute message digests
|
9
|
+
* (interchangeably called "hashes") of arbitrary data that are
|
10
|
+
* cryptographically secure using SHA3 (Keccak) algorithm.
|
11
|
+
*
|
12
|
+
* == Usage
|
13
|
+
*
|
14
|
+
* require 'sha3'
|
9
15
|
*
|
10
|
-
*
|
11
|
-
*
|
12
|
-
*
|
16
|
+
* === Basics
|
17
|
+
*
|
18
|
+
* # Instantiate a new SHA3::Digest class with 256 bit length
|
19
|
+
* s = SHA3::Digest.new(:sha256)
|
20
|
+
* # => #<SHA3::Digest: c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470>
|
21
|
+
*
|
22
|
+
* # Update hash state, and compute new value
|
23
|
+
* s.update "Compute Me"
|
24
|
+
* # => #<SHA3::Digest: c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470>
|
25
|
+
*
|
26
|
+
* # << is an .update() alias
|
27
|
+
* s << "Me too"
|
28
|
+
* # => #<SHA3::Digest: e26f539eee3a05c52eb1f9439652d23343adea9764f011da232d24cd6d19924a>
|
29
|
+
*
|
30
|
+
* # Print digest bytes string
|
31
|
+
* puts s.digest
|
32
|
+
*
|
33
|
+
* # Print digest hex string
|
34
|
+
* puts s.hexdigest
|
35
|
+
*
|
36
|
+
* === Hashing a file
|
37
|
+
*
|
38
|
+
* # Compute the hash value for given file, and return the result as hex
|
39
|
+
* s = SHA3::Digest.new(224).file("my_awesome_file.bin").hexdigest
|
13
40
|
*
|
14
|
-
*
|
15
|
-
|
16
|
-
|
41
|
+
* === Bit operation
|
42
|
+
*
|
43
|
+
* # Compute hash of "011"
|
44
|
+
* SHA3::Digest.compute(:sha224, "\xC0", 3).unpack("H*")
|
45
|
+
* # => ["2b695a6fd92a2b3f3ce9cfca617d22c9bb52815dd59a9719b01bad25"]
|
46
|
+
*
|
47
|
+
* == Notes
|
48
|
+
*
|
49
|
+
* ::Digest::Class call sequence ->
|
50
|
+
* | .alloc() ->
|
51
|
+
* | .new() ->
|
52
|
+
* | .update() ->
|
53
|
+
* | .digest or .hexdigest or .inspect -> (Instance.digest or .hexdigest()) ->
|
54
|
+
* --| .alloc() ->
|
55
|
+
* | .copy() ->
|
56
|
+
* | .finish() ->
|
57
|
+
*
|
58
|
+
*/
|
59
|
+
|
60
|
+
static int get_hlen(VALUE obj)
|
61
|
+
{
|
62
|
+
int hlen;
|
63
|
+
|
64
|
+
if (TYPE(obj) == T_SYMBOL) {
|
65
|
+
ID symid;
|
66
|
+
|
67
|
+
symid = SYM2ID(obj);
|
68
|
+
|
69
|
+
if (rb_intern("sha224") == symid)
|
70
|
+
hlen = 224;
|
71
|
+
else if (rb_intern("sha256") == symid)
|
72
|
+
hlen = 256;
|
73
|
+
else if (rb_intern("sha384") == symid)
|
74
|
+
hlen = 384;
|
75
|
+
else if (rb_intern("sha512") == symid)
|
76
|
+
hlen = 512;
|
77
|
+
else
|
78
|
+
rb_raise(eDigestError, "invalid hash bit symbol (should be: :sha224, :sha256, :sha384, or :sha512");
|
79
|
+
}
|
80
|
+
else if (TYPE(obj) == T_FIXNUM) {
|
81
|
+
hlen = NUM2INT(obj);
|
82
|
+
|
83
|
+
if ((hlen != 224) && (hlen != 256) && (hlen != 384) && (hlen != 512))
|
84
|
+
rb_raise(rb_eArgError, "invalid hash bit length (should be: 224, 256, 384, or 512)");
|
85
|
+
}
|
86
|
+
else
|
87
|
+
rb_raise(eDigestError, "unknown type value");
|
88
|
+
|
89
|
+
return hlen;
|
90
|
+
}
|
91
|
+
|
92
|
+
static void free_allox(MDX *mdx)
|
93
|
+
{
|
94
|
+
if (mdx) {
|
95
|
+
if (mdx->state)
|
96
|
+
free(mdx->state);
|
97
|
+
|
98
|
+
free(mdx);
|
99
|
+
}
|
100
|
+
|
101
|
+
return;
|
102
|
+
}
|
103
|
+
|
104
|
+
static VALUE c_digest_alloc(VALUE klass)
|
17
105
|
{
|
18
|
-
|
106
|
+
MDX *mdx;
|
107
|
+
VALUE obj;
|
19
108
|
|
20
|
-
|
21
|
-
|
109
|
+
mdx = (MDX *) malloc(sizeof(*mdx));
|
110
|
+
if (!mdx)
|
111
|
+
rb_raise(eDigestError, "failed to allocate object memory");
|
112
|
+
|
113
|
+
mdx->state = (hashState *) malloc(sizeof(*mdx->state));
|
114
|
+
memset(mdx->state, 0, sizeof(*mdx->state));
|
115
|
+
|
116
|
+
if (!mdx->state)
|
117
|
+
rb_raise(eDigestError, "failed to allocate state memory");
|
118
|
+
|
119
|
+
obj = Data_Wrap_Struct(klass, 0, free_allox, mdx);
|
120
|
+
|
121
|
+
return obj;
|
122
|
+
}
|
123
|
+
|
124
|
+
static VALUE c_digest_update(VALUE, VALUE);
|
125
|
+
|
126
|
+
// SHA3::Digest.new(type, [data]) -> self
|
127
|
+
static VALUE c_digest_init(int argc, VALUE *argv, VALUE self)
|
128
|
+
{
|
129
|
+
MDX *mdx;
|
130
|
+
VALUE hlen, data;
|
131
|
+
|
132
|
+
rb_scan_args(argc, argv, "02", &hlen, &data);
|
133
|
+
GETMDX(self, mdx);
|
134
|
+
|
135
|
+
if (!NIL_P(hlen))
|
136
|
+
mdx->hashbitlen = get_hlen(hlen);
|
137
|
+
else
|
138
|
+
mdx->hashbitlen = 256;
|
139
|
+
|
140
|
+
if (Init(mdx->state, mdx->hashbitlen) != SUCCESS)
|
141
|
+
rb_raise(eDigestError, "failed to initialize algorithm state");
|
142
|
+
|
143
|
+
if (!NIL_P(data))
|
144
|
+
return c_digest_update(self, data);
|
145
|
+
|
146
|
+
return self;
|
147
|
+
}
|
148
|
+
|
149
|
+
// SHA3::Digest.update(data) -> self
|
150
|
+
static VALUE c_digest_update(VALUE self, VALUE data)
|
151
|
+
{
|
152
|
+
MDX *mdx;
|
153
|
+
DataLength dlen;
|
22
154
|
|
23
155
|
StringValue(data);
|
156
|
+
GETMDX(self, mdx);
|
24
157
|
|
25
|
-
|
158
|
+
dlen = (RSTRING_LEN(data) * 8);
|
26
159
|
|
27
|
-
if (
|
28
|
-
rb_raise(
|
160
|
+
if (Update(mdx->state, RSTRING_PTR(data), dlen) != SUCCESS)
|
161
|
+
rb_raise(eDigestError, "failed to update hash data");
|
29
162
|
|
30
|
-
return
|
163
|
+
return self;
|
164
|
+
}
|
165
|
+
|
166
|
+
// SHA3::Digest.reset() -> self
|
167
|
+
static VALUE c_digest_reset(VALUE self)
|
168
|
+
{
|
169
|
+
MDX *mdx;
|
170
|
+
|
171
|
+
GETMDX(self, mdx);
|
172
|
+
|
173
|
+
memset(mdx->state, 0, sizeof(*mdx->state));
|
174
|
+
|
175
|
+
if (Init(mdx->state, mdx->hashbitlen) != SUCCESS)
|
176
|
+
rb_raise(eDigestError, "failed to reset internal state");
|
177
|
+
|
178
|
+
return self;
|
179
|
+
}
|
180
|
+
|
181
|
+
// SHA3::Digest.copy(obj) -> self
|
182
|
+
static VALUE c_digest_copy(VALUE self, VALUE obj)
|
183
|
+
{
|
184
|
+
MDX *mdx1, *mdx2;
|
185
|
+
|
186
|
+
rb_check_frozen(self);
|
187
|
+
if (self == obj)
|
188
|
+
return self;
|
189
|
+
|
190
|
+
GETMDX(self, mdx1);
|
191
|
+
SAFEGETMDX(obj, mdx2);
|
192
|
+
|
193
|
+
memcpy(mdx1->state, mdx2->state, sizeof(hashState));
|
194
|
+
mdx1->hashbitlen = mdx2->hashbitlen;
|
195
|
+
|
196
|
+
// Fetch the data again to make sure it was copied
|
197
|
+
GETMDX(self, mdx1);
|
198
|
+
SAFEGETMDX(obj, mdx2);
|
199
|
+
if ((mdx1->state != mdx2->state) && (mdx1->hashbitlen != mdx2->hashbitlen))
|
200
|
+
rb_raise(eDigestError, "failed to copy state");
|
201
|
+
|
202
|
+
return self;
|
203
|
+
}
|
204
|
+
|
205
|
+
// SHA3::Digest.digest_length -> Integer
|
206
|
+
static VALUE c_digest_length(VALUE self)
|
207
|
+
{
|
208
|
+
MDX *mdx;
|
209
|
+
GETMDX(self, mdx);
|
210
|
+
|
211
|
+
return ULL2NUM(mdx->hashbitlen / 8);
|
212
|
+
}
|
213
|
+
|
214
|
+
// SHA3::Digest.block_length -> Integer
|
215
|
+
static VALUE c_digest_block_length(VALUE self)
|
216
|
+
{
|
217
|
+
MDX *mdx;
|
218
|
+
GETMDX(self, mdx);
|
219
|
+
|
220
|
+
return ULL2NUM(200 - (2 * (mdx->hashbitlen / 8)));
|
221
|
+
}
|
222
|
+
|
223
|
+
// SHA3::Digest.name -> String
|
224
|
+
static VALUE c_digest_name(VALUE self)
|
225
|
+
{
|
226
|
+
return rb_str_new2("SHA3");
|
227
|
+
}
|
228
|
+
|
229
|
+
// SHA3::Digest.finish() -> String
|
230
|
+
static VALUE c_digest_finish(int argc, VALUE *argv, VALUE self)
|
231
|
+
{
|
232
|
+
MDX *mdx;
|
233
|
+
VALUE str;
|
234
|
+
|
235
|
+
rb_scan_args(argc, argv, "01", &str);
|
236
|
+
GETMDX(self, mdx);
|
237
|
+
|
238
|
+
if (NIL_P(str)) {
|
239
|
+
str = rb_str_new(0, mdx->hashbitlen / 8);
|
240
|
+
}
|
241
|
+
else {
|
242
|
+
StringValue(str);
|
243
|
+
rb_str_resize(str, mdx->hashbitlen / 8);
|
244
|
+
}
|
245
|
+
|
246
|
+
if (Final(mdx->state, RSTRING_PTR(str)) != SUCCESS)
|
247
|
+
rb_raise(eDigestError, "failed to finalize digest");
|
248
|
+
|
249
|
+
return str;
|
250
|
+
}
|
251
|
+
|
252
|
+
// SHA3::Digest.compute(type, data, [datalen]) -> String (bytes)
|
253
|
+
// TO-DO: styled output (hex)
|
254
|
+
VALUE c_digest_compute(int argc, VALUE *argv, VALUE self)
|
255
|
+
{
|
256
|
+
VALUE hlen, data, dlen, str;
|
257
|
+
int hashbitlen;
|
258
|
+
DataLength datalen;
|
259
|
+
|
260
|
+
rb_scan_args(argc, argv, "21", &hlen, &data, &dlen);
|
261
|
+
|
262
|
+
hashbitlen = get_hlen(hlen);
|
263
|
+
|
264
|
+
StringValue(data);
|
265
|
+
|
266
|
+
if (!NIL_P(dlen))
|
267
|
+
datalen = NUM2ULL(dlen);
|
268
|
+
else
|
269
|
+
datalen = (RSTRING_LEN(data) * 8);
|
270
|
+
|
271
|
+
str = rb_str_new(0, hashbitlen / 8);
|
272
|
+
|
273
|
+
if (Hash(hashbitlen, RSTRING_PTR(data), datalen, RSTRING_PTR(str)) != SUCCESS)
|
274
|
+
rb_raise(eDigestError, "failed to generate hash");
|
275
|
+
|
276
|
+
return str;
|
31
277
|
}
|
32
278
|
|
33
279
|
void Init_sha3_n()
|
34
280
|
{
|
281
|
+
rb_require("digest");
|
282
|
+
|
35
283
|
mSHA3 = rb_define_module("SHA3");
|
284
|
+
/* SHA3::Digest (class) */
|
285
|
+
cDigest = rb_define_class_under(mSHA3, "Digest", rb_path2class("Digest::Class"));
|
286
|
+
/* SHA3::Digest::DigestError (class) */
|
287
|
+
eDigestError = rb_define_class_under(cDigest, "DigestError", rb_eStandardError);
|
288
|
+
|
289
|
+
// SHA3::Digest (class) methods
|
290
|
+
rb_define_alloc_func(cDigest, c_digest_alloc);
|
291
|
+
rb_define_method(cDigest, "initialize", c_digest_init, -1);
|
292
|
+
rb_define_method(cDigest, "update", c_digest_update, 1);
|
293
|
+
rb_define_method(cDigest, "reset", c_digest_reset, 0);
|
294
|
+
rb_define_method(cDigest, "initialize_copy", c_digest_copy, 1);
|
295
|
+
rb_define_method(cDigest, "digest_length", c_digest_length, 0);
|
296
|
+
rb_define_method(cDigest, "block_length", c_digest_block_length, 0);
|
297
|
+
rb_define_method(cDigest, "name", c_digest_name, 0);
|
298
|
+
rb_define_private_method(cDigest, "finish", c_digest_finish, -1);
|
299
|
+
|
300
|
+
rb_define_alias(cDigest, "<<", "update");
|
301
|
+
|
302
|
+
// SHA3 (module) functions (support bit operations)
|
303
|
+
rb_define_singleton_method(cDigest, "compute", c_digest_compute, -1);
|
36
304
|
|
37
|
-
|
305
|
+
return;
|
38
306
|
}
|
data/ext/sha3/_sha3.h
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#ifndef __SHA3_H_
|
2
|
+
#define __SHA3_H_
|
3
|
+
|
4
|
+
#include <ruby.h>
|
5
|
+
#include "KeccakNISTInterface.h"
|
6
|
+
|
7
|
+
// From ruby/ext/openssl/ossl_digest.c
|
8
|
+
#define GETMDX(obj, mdx) do { \
|
9
|
+
Data_Get_Struct((obj), MDX, (mdx)); \
|
10
|
+
if (!(mdx)) { \
|
11
|
+
rb_raise(rb_eRuntimeError, "Digest data not initialized!"); \
|
12
|
+
} \
|
13
|
+
} while (0)
|
14
|
+
|
15
|
+
#define SAFEGETMDX(obj, mdx) do { \
|
16
|
+
if (!rb_obj_is_kind_of(obj, cDigest)) { \
|
17
|
+
rb_raise(rb_eTypeError, "wrong argument (%s)! (expected %s)",\
|
18
|
+
rb_obj_classname(obj), rb_class2name(cDigest)); \
|
19
|
+
} \
|
20
|
+
GETMDX(obj, mdx); \
|
21
|
+
} while(0)
|
22
|
+
|
23
|
+
VALUE mSHA3;
|
24
|
+
VALUE cDigest;
|
25
|
+
VALUE eDigestError;
|
26
|
+
|
27
|
+
typedef struct {
|
28
|
+
hashState *state;
|
29
|
+
int hashbitlen;
|
30
|
+
} MDX;
|
31
|
+
|
32
|
+
#endif
|
data/ext/sha3/extconf.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
require 'mkmf'
|
2
2
|
|
3
|
-
|
3
|
+
case 1.size
|
4
|
+
when 4 # 32bit optimized code
|
4
5
|
FileUtils.cp "#{$srcdir}/KeccakF-1600-opt32.c-arch", "#{$srcdir}/KeccakF-1600-opt.c"
|
5
|
-
|
6
|
+
when 8 # 64bit optimized code
|
6
7
|
FileUtils.cp "#{$srcdir}/KeccakF-1600-opt64.c-arch", "#{$srcdir}/KeccakF-1600-opt.c"
|
8
|
+
else # Ha? Use reference code
|
9
|
+
FileUtils.cp "#{$srcdir}/KeccakF-1600-reference.c-arch", "#{$srcdir}/KeccakF-1600-opt.c"
|
7
10
|
end
|
8
11
|
|
9
12
|
$CFLAGS = ' -fomit-frame-pointer -O3 -g0 -march=nocona '
|
data/lib/sha3.rb
CHANGED
@@ -1,22 +1,2 @@
|
|
1
|
-
require 'sha3/version'
|
2
1
|
require 'sha3_n'
|
3
|
-
|
4
|
-
module SHA3
|
5
|
-
extend self
|
6
|
-
|
7
|
-
def digest_224(data)
|
8
|
-
SHA3::digest(data, data.length * 8, 224)
|
9
|
-
end
|
10
|
-
|
11
|
-
def digest_256(data)
|
12
|
-
SHA3::digest(data, data.length * 8, 256)
|
13
|
-
end
|
14
|
-
|
15
|
-
def digest_384(data)
|
16
|
-
SHA3::digest(data, data.length * 8, 384)
|
17
|
-
end
|
18
|
-
|
19
|
-
def digest_512(data)
|
20
|
-
SHA3::digest(data, data.length * 8, 512)
|
21
|
-
end
|
22
|
-
end
|
2
|
+
require 'sha3/version'
|
data/lib/sha3/version.rb
CHANGED
data/sha3.gemspec
CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |gem|
|
|
6
6
|
gem.name = "sha3"
|
7
7
|
gem.version = SHA3::VERSION
|
8
8
|
gem.summary = %q{SHA3 for Ruby}
|
9
|
-
gem.description = %q{
|
9
|
+
gem.description = %q{SHA3 for Ruby is a native (C) implementation of Keccak (SHA3) cryptographic hashing algorithm. See https://github.com/johanns/sha3#readme for details.}
|
10
10
|
gem.license = "MIT"
|
11
11
|
gem.authors = ["Johanns Gregorian"]
|
12
12
|
gem.email = "io+sha3@jsani.com"
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# Based on python-sha3's / digest-sha3 test generator.
|
2
|
+
|
3
|
+
FILES = [
|
4
|
+
['data/ShortMsgKAT_224.txt', 224],
|
5
|
+
['data/ShortMsgKAT_256.txt', 256],
|
6
|
+
['data/ShortMsgKAT_384.txt', 384],
|
7
|
+
['data/ShortMsgKAT_512.txt', 512],
|
8
|
+
['data/LongMsgKAT_224.txt', 224],
|
9
|
+
]
|
10
|
+
|
11
|
+
def gen_digest_byte_tests
|
12
|
+
FILES.each do |path, hashlen|
|
13
|
+
name = File.basename(path).split('.')[0]
|
14
|
+
|
15
|
+
f = File.new("sha3_digest_#{name}_spec.rb", "w")
|
16
|
+
f.puts(
|
17
|
+
%Q{require 'spec_helper'
|
18
|
+
require 'sha3'
|
19
|
+
|
20
|
+
describe "SHA3::Digest.new(#{hashlen})" do
|
21
|
+
it "should match byte-length test vectors (#{name})." do
|
22
|
+
})
|
23
|
+
contents = File.read(path).split('Len = ')
|
24
|
+
contents.each do |test|
|
25
|
+
lines = test.split("\n")
|
26
|
+
if !lines.empty? && lines[0] !~ /^#/
|
27
|
+
length = lines[0].to_i
|
28
|
+
if length % 8 == 0 && length != 0
|
29
|
+
msg_raw = [lines[1].split(' = ').last].pack("H*")
|
30
|
+
md = lines[2].split(' = ').last.downcase
|
31
|
+
f.puts(
|
32
|
+
%Q{ SHA3::Digest.new(#{hashlen}, #{msg_raw.inspect}).hexdigest.should(eq("#{md}"))
|
33
|
+
})
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
f.puts(
|
38
|
+
%Q{ end
|
39
|
+
end
|
40
|
+
})
|
41
|
+
f.close
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def gen_compute_bit_tests
|
46
|
+
FILES.each do |path, hashlen|
|
47
|
+
name = File.basename(path).split('.')[0]
|
48
|
+
|
49
|
+
f = File.new("sha3_compute_#{name}_spec.rb", "w")
|
50
|
+
f.puts(
|
51
|
+
%Q{require 'spec_helper'
|
52
|
+
require 'sha3'
|
53
|
+
|
54
|
+
describe "SHA3::Digest.compute(#{hashlen})" do
|
55
|
+
it "should match bit-length test vectors (#{name})." do
|
56
|
+
})
|
57
|
+
contents = File.read(path).split('Len = ')
|
58
|
+
contents.each do |test|
|
59
|
+
lines = test.split("\n")
|
60
|
+
if !lines.empty? && lines[0] !~ /^#/
|
61
|
+
length = lines[0].to_i
|
62
|
+
if length != 0
|
63
|
+
msg_raw = [lines[1].split(' = ').last].pack("H*")
|
64
|
+
md = lines[2].split(' = ').last.downcase
|
65
|
+
f.puts(
|
66
|
+
%Q{ SHA3::Digest.compute(#{hashlen}, #{msg_raw.inspect}, #{length}).unpack("H*").first.should(eq("#{md}"))
|
67
|
+
})
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
f.puts(
|
72
|
+
%Q{ end
|
73
|
+
end
|
74
|
+
})
|
75
|
+
f.close
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def setup
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
gen_digest_byte_tests
|
84
|
+
gen_compute_bit_tests
|