4 # AUTHOR: Aaron D. Gifford - http://www.aarongifford.com/
6 # Copyright (c) 2001, Aaron D. Gifford
9 # Redistribution and use in source and binary forms, with or without
10 # modification, are permitted provided that the following conditions
12 # 1. Redistributions of source code must retain the above copyright
13 # notice, this list of conditions and the following disclaimer.
14 # 2. Redistributions in binary form must reproduce the above copyright
15 # notice, this list of conditions and the following disclaimer in the
16 # documentation and/or other materials provided with the distribution.
17 # 3. Neither the name of the copyright holder nor the names of contributors
18 # may be used to endorse or promote products derived from this software
19 # without specific prior written permission.
21 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
22 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
25 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 # $Id: sha2test.pl,v 1.1 2001/11/08 00:02:37 adg Exp adg $
37 my ($err) = shift(@_);
43 $0 [<options>] [<test-vector-info-file> [<test-vector-info-file> ...]]
46 -256 Use SHA-256 hashes during testing
47 -384 Use SHA-384 hashes during testing
48 -512 Use SHA-512 hashes during testing
49 -ALL Use all three hashes during testing
50 -c256 <command-spec> Specify a command to execute to generate a
51 SHA-256 hash. Be sure to include a '%'
52 character which will be replaced by the
53 test vector data filename containing the
54 data to be hashed. This command implies
56 -c384 <command-spec> Specify a command to execute to generate a
57 SHA-384 hash. See above. Implies -384.
58 -c512 <command-spec> Specify a command to execute to generate a
59 SHA-512 hash. See above. Implies -512.
60 -cALL <command-spec> Specify a command to execute that will
61 generate all three hashes at once and output
62 the data in hexadecimal. See above for
63 information about the <command-spec>.
64 This option implies the -ALL option, and
65 also overrides any other command options if
68 By default, this program expects to execute the command ./sha2 within the
69 current working directory to generate all hashes. If no test vector
70 information files are specified, this program expects to read a series of
71 files ending in ".info" within a subdirectory of the current working
72 directory called "testvectors".
78 $c256 = $c384 = $c512 = $cALL = "";
82 # Read all command-line options and files:
83 while ($opt = shift(@ARGV)) {
84 if ($opt =~ s/^\-//) {
87 } elsif ($opt eq "384") {
89 } elsif ($opt eq "512") {
91 } elsif ($opt =~ /^ALL$/i) {
93 } elsif ($opt =~ /^c256$/i) {
95 $opt = $c256 = shift(@ARGV);
97 if (!$c256 || $c256 !~ /\%/ || !-x $opt) {
98 usage("Missing or invalid command specification for option -c256: $opt\n");
100 } elsif ($opt =~ /^c384$/i) {
102 $opt = $c384 = shift(@ARGV);
104 if (!$c384 || $c384 !~ /\%/ || !-x $opt) {
105 usage("Missing or invalid command specification for option -c384: $opt\n");
107 } elsif ($opt =~ /^c512$/i) {
109 $opt = $c512 = shift(@ARGV);
111 if (!$c512 || $c512 !~ /\%/ || !-x $opt) {
112 usage("Missing or invalid command specification for option -c512: $opt\n");
114 } elsif ($opt =~ /^cALL$/i) {
116 $opt = $cALL = shift(@ARGV);
118 if (!$cALL || $cALL !~ /\%/ || !-x $opt) {
119 usage("Missing or invalid command specification for option -cALL: $opt\n");
122 usage("Unknown/invalid option '$opt'\n");
125 usage("Invalid, nonexistent, or unreadable file '$opt': $!\n") if (!-f $opt);
131 if (!$cALL && !$c256 && !$c384 && !$c512) {
132 $cALL = "./sha2 -ALL %";
133 usage("Required ./sha2 binary executable not found.\n") if (!-x "./sha2");
135 $hashes = 7 if (!$hashes);
137 # Do some sanity checks:
138 usage("No command was supplied to generate SHA-256 hashes.\n") if ($hashes & 1 == 1 && !$cALL && !$c256);
139 usage("No command was supplied to generate SHA-384 hashes.\n") if ($hashes & 2 == 2 && !$cALL && !$c384);
140 usage("No command was supplied to generate SHA-512 hashes.\n") if ($hashes & 4 == 4 && !$cALL && !$c512);
142 # Default .info files:
143 if (scalar(@FILES) < 1) {
144 opendir(DIR, "testvectors") || usage("Unable to scan directory 'testvectors' for vector information files: $!\n");
145 @FILES = grep(/\.info$/, readdir(DIR));
147 @FILES = map { s/^/testvectors\//; $_; } @FILES;
148 @FILES = sort(@FILES);
151 # Now read in each test vector information file:
152 foreach $file (@FILES) {
157 $dir =~ s/\/[^\/]+$//;
160 open(FILE, "<" . $file) ||
161 usage("Unable to open test vector information file '$file' for reading: $!\n");
162 $vec = { desc => "", file => "", sha256 => "", sha384 => "", sha512 => "" };
168 next if ($field && $field ne "DESCRIPTION" && !$_);
169 if (/^(DESCRIPTION|FILE|SHA256|SHA384|SHA512):$/) {
170 if ($field eq "DESCRIPTION") {
171 $vec->{desc} = $data;
172 } elsif ($field eq "FILE") {
173 $data = $dir . $data if ($data !~ /^\//);
174 $vec->{file} = $data;
175 } elsif ($field eq "SHA256") {
176 $vec->{sha256} = $data;
177 } elsif ($field eq "SHA384") {
178 $vec->{sha384} = $data;
179 } elsif ($field eq "SHA512") {
180 $vec->{sha512} = $data;
184 } elsif ($field eq "DESCRIPTION") {
187 } elsif ($field =~ /^SHA\d\d\d$/) {
189 if (!/^([a-f0-9]{32}|[a-f0-9]{64})$/) {
190 usage("Invalid SHA-256/384/512 test vector information " .
191 "file format at line $line of file '$file'\n");
194 } elsif ($field eq "FILE") {
198 usage("Invalid SHA-256/384/512 test vector information file " .
199 "format at line $line of file '$file'\n");
202 if ($field eq "DESCRIPTION") {
203 $data = $dir . $data if ($data !~ /^\//);
204 $vec->{desc} = $data;
205 } elsif ($field eq "FILE") {
206 $vec->{file} = $data;
207 } elsif ($field eq "SHA256") {
208 $vec->{sha256} = $data;
209 } elsif ($field eq "SHA384") {
210 $vec->{sha384} = $data;
211 } elsif ($field eq "SHA512") {
212 $vec->{sha512} = $data;
214 usage("Invalid SHA-256/384/512 test vector information file " .
215 "format. Missing required fields in file '$file'\n");
218 # Sanity check all entries:
220 usage("Invalid SHA-256/384/512 test vector information file " .
221 "format. Missing required DESCRIPTION field in file '$file'\n");
224 usage("Invalid SHA-256/384/512 test vector information file " .
225 "format. Missing required FILE field in file '$file'\n");
227 if (! -f $vec->{file}) {
228 usage("The test vector data file (field FILE) name " .
229 "'$vec->{file}' is not a readable file. Check the FILE filed in " .
232 if (!($vec->{sha256} || $vec->{sha384} || $vec->{sha512})) {
233 usage("Invalid SHA-256/384/512 test vector information file " .
234 "format. There must be at least one SHA256, SHA384, or SHA512 " .
235 "field specified in file '$file'.\n");
237 if ($vec->{sha256} !~ /^(|[a-f0-9]{64})$/) {
238 usage("Invalid SHA-256/384/512 test vector information file " .
239 "format. The SHA256 field is invalid in file '$file'.\n");
241 if ($vec->{sha384} !~ /^(|[a-f0-9]{96})$/) {
242 usage("Invalid SHA-256/384/512 test vector information file " .
243 "format. The SHA384 field is invalid in file '$file'.\n");
245 if ($vec->{sha512} !~ /^(|[a-f0-9]{128})$/) {
246 usage("Invalid SHA-256/384/512 test vector information file " .
247 "format. The SHA512 field is invalid in file '$file'.\n");
250 if ($hashes & (($vec->{sha256} ? 1 : 0) | ($vec->{sha384} ? 2 : 0) | ($vec->{sha512} ? 4 : 0))) {
251 push(@VECTORS, $vec);
255 usage("There were no test vectors for the specified hash(es) in any of the test vector information files you specified.\n") if (scalar(@VECTORS) < 1);
257 $num = $errors = $error256 = $error384 = $error512 = $tests = $test256 = $test384 = $test512 = 0;
258 foreach $vec (@VECTORS) {
260 print "TEST VECTOR #$num:\n";
261 print "\t" . join("\n\t", split(/\n/, $vec->{desc})) . "\n";
262 print "VECTOR DATA FILE:\n\t$vec->{file}\n";
263 $sha256 = $sha384 = $sha512 = "";
266 $prog =~ s/\%/'$vec->{file}'/g;
267 @SHA = grep(/[a-fA-f0-9]{64,128}/, split(/\n/, `$prog`));
268 ($sha256) = grep(/(^[a-fA-F0-9]{64}$|^[a-fA-F0-9]{64}[^a-fA-F0-9]|[^a-fA-F0-9][a-fA-F0-9]{64}$|[^a-fA-F0-9][a-fA-F0-9]{64}[^a-fA-F0-9])/, @SHA);
269 ($sha384) = grep(/(^[a-fA-F0-9]{96}$|^[a-fA-F0-9]{96}[^a-fA-F0-9]|[^a-fA-F0-9][a-fA-F0-9]{96}$|[^a-fA-F0-9][a-fA-F0-9]{96}[^a-fA-F0-9])/, @SHA);
270 ($sha512) = grep(/(^[a-fA-F0-9]{128}$|^[a-fA-F0-9]{128}[^a-fA-F0-9]|[^a-fA-F0-9][a-fA-F0-9]{128}$|[^a-fA-F0-9][a-fA-F0-9]{128}[^a-fA-F0-9])/, @SHA);
274 $prog =~ s/\%/'$vec->{file}'/g;
275 @SHA = grep(/[a-fA-f0-9]{64,128}/, split(/\n/, `$prog`));
276 ($sha256) = grep(/(^[a-fA-F0-9]{64}$|^[a-fA-F0-9]{64}[^a-fA-F0-9]|[^a-fA-F0-9][a-fA-F0-9]{64}$|[^a-fA-F0-9][a-fA-F0-9]{64}[^a-fA-F0-9])/, @SHA);
280 $prog =~ s/\%/'$vec->{file}'/g;
281 @SHA = grep(/[a-fA-f0-9]{64,128}/, split(/\n/, `$prog`));
282 ($sha384) = grep(/(^[a-fA-F0-9]{96}$|^[a-fA-F0-9]{96}[^a-fA-F0-9]|[^a-fA-F0-9][a-fA-F0-9]{96}$|[^a-fA-F0-9][a-fA-F0-9]{96}[^a-fA-F0-9])/, @SHA);
286 $prog =~ s/\%/'$vec->{file}'/g;
287 @SHA = grep(/[a-fA-f0-9]{64,128}/, split(/\n/, `$prog`));
288 ($sha512) = grep(/(^[a-fA-F0-9]{128}$|^[a-fA-F0-9]{128}[^a-fA-F0-9]|[^a-fA-F0-9][a-fA-F0-9]{128}$|[^a-fA-F0-9][a-fA-F0-9]{128}[^a-fA-F0-9])/, @SHA);
291 usage("Unable to generate any hashes for file '$vec->{file}'!\n") if (!$sha256 && !$sha384 && $sha512);
292 $sha256 =~ tr/A-F/a-f/;
293 $sha384 =~ tr/A-F/a-f/;
294 $sha512 =~ tr/A-F/a-f/;
295 $sha256 =~ s/^.*([a-f0-9]{64}).*$/$1/;
296 $sha384 =~ s/^.*([a-f0-9]{96}).*$/$1/;
297 $sha512 =~ s/^.*([a-f0-9]{128}).*$/$1/;
299 if ($sha256 && $hashes & 1 == 1) {
300 if ($vec->{sha256} eq $sha256) {
301 print "SHA256 MATCHES:\n\t$sha256\n"
303 print "SHA256 DOES NOT MATCH:\n\tEXPECTED:\n\t\t$vec->{sha256}\n" .
304 "\tGOT:\n\t\t$sha256\n\n";
309 if ($sha384 && $hashes & 2 == 2) {
310 if ($vec->{sha384} eq $sha384) {
311 print "SHA384 MATCHES:\n\t" . substr($sha384, 0, 64) . "\n\t" .
312 substr($sha384, -32) . "\n";
314 print "SHA384 DOES NOT MATCH:\n\tEXPECTED:\n\t\t" .
315 substr($vec->{sha384}, 0, 64) . "\n\t\t" .
316 substr($vec->{sha384}, -32) . "\n\tGOT:\n\t\t" .
317 substr($sha384, 0, 64) . "\n\t\t" . substr($sha384, -32) . "\n\n";
322 if ($sha512 && $hashes & 4 == 4) {
323 if ($vec->{sha512} eq $sha512) {
324 print "SHA512 MATCHES:\n\t" . substr($sha512, 0, 64) . "\n\t" .
325 substr($sha512, -64) . "\n";
327 print "SHA512 DOES NOT MATCH:\n\tEXPECTED:\n\t\t" .
328 substr($vec->{sha512}, 0, 64) . "\n\t\t" .
329 substr($vec->{sha512}, -32) . "\n\tGOT:\n\t\t" .
330 substr($sha512, 0, 64) . "\n\t\t" . substr($sha512, -64) . "\n\n";
337 $errors = $error256 + $error384 + $error512;
338 $tests = $test256 + $test384 + $test512;
339 print "\n\n===== RESULTS ($num VECTOR DATA FILES HASHED) =====\n\n";
340 print "HASH TYPE\tNO. OF TESTS\tPASSED\tFAILED\n";
341 print "---------\t------------\t------\t------\n";
343 $pass = $test256 - $error256;
344 print "SHA-256\t\t".substr(" $test256", -12)."\t".substr(" $pass", -6)."\t".substr(" $error256", -6)."\n";
347 $pass = $test384 - $error384;
348 print "SHA-384\t\t".substr(" $test384", -12)."\t".substr(" $pass", -6)."\t".substr(" $error384", -6)."\n";
351 $pass = $test512 - $error512;
352 print "SHA-512\t\t".substr(" $test512", -12)."\t".substr(" $pass", -6)."\t".substr(" $error512", -6)."\n";
354 print "----------------------------------------------\n";
355 $pass = $tests - $errors;
356 print "TOTAL: ".substr(" $tests", -12)."\t".substr(" $pass", -6)."\t".substr(" $errors", -6)."\n\n";
357 print "NO ERRORS! ALL TESTS WERE SUCCESSFUL!\n\n" if (!$errors);