1#!/usr/bin/perl 2# 3# FILE: sha2test.pl 4# AUTHOR: Aaron D. Gifford - http://www.aarongifford.com/ 5# 6# Copyright (c) 2001, Aaron D. Gifford 7# All rights reserved. 8# 9# Redistribution and use in source and binary forms, with or without 10# modification, are permitted provided that the following conditions 11# are met: 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. 20# 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 31# SUCH DAMAGE. 32# 33# $Id: sha2test.pl,v 1.1 2001/11/08 00:02:37 adg Exp adg $ 34# 35 36sub usage { 37 my ($err) = shift(@_); 38 39 print <<EOM; 40Error: 41 $err 42Usage: 43 $0 [<options>] [<test-vector-info-file> [<test-vector-info-file> ...]] 44 45Options: 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 55 the -256 option. 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 66 present. 67 68By default, this program expects to execute the command ./sha2 within the 69current working directory to generate all hashes. If no test vector 70information files are specified, this program expects to read a series of 71files ending in ".info" within a subdirectory of the current working 72directory called "testvectors". 73 74EOM 75 exit(-1); 76} 77 78$c256 = $c384 = $c512 = $cALL = ""; 79$hashes = 0; 80@FILES = (); 81 82# Read all command-line options and files: 83while ($opt = shift(@ARGV)) { 84 if ($opt =~ s/^\-//) { 85 if ($opt eq "256") { 86 $hashes |= 1; 87 } elsif ($opt eq "384") { 88 $hashes |= 2; 89 } elsif ($opt eq "512") { 90 $hashes |= 4; 91 } elsif ($opt =~ /^ALL$/i) { 92 $hashes = 7; 93 } elsif ($opt =~ /^c256$/i) { 94 $hashes |= 1; 95 $opt = $c256 = shift(@ARGV); 96 $opt =~ s/\s+.*$//; 97 if (!$c256 || $c256 !~ /\%/ || !-x $opt) { 98 usage("Missing or invalid command specification for option -c256: $opt\n"); 99 } 100 } elsif ($opt =~ /^c384$/i) { 101 $hashes |= 2; 102 $opt = $c384 = shift(@ARGV); 103 $opt =~ s/\s+.*$//; 104 if (!$c384 || $c384 !~ /\%/ || !-x $opt) { 105 usage("Missing or invalid command specification for option -c384: $opt\n"); 106 } 107 } elsif ($opt =~ /^c512$/i) { 108 $hashes |= 4; 109 $opt = $c512 = shift(@ARGV); 110 $opt =~ s/\s+.*$//; 111 if (!$c512 || $c512 !~ /\%/ || !-x $opt) { 112 usage("Missing or invalid command specification for option -c512: $opt\n"); 113 } 114 } elsif ($opt =~ /^cALL$/i) { 115 $hashes = 7; 116 $opt = $cALL = shift(@ARGV); 117 $opt =~ s/\s+.*$//; 118 if (!$cALL || $cALL !~ /\%/ || !-x $opt) { 119 usage("Missing or invalid command specification for option -cALL: $opt\n"); 120 } 121 } else { 122 usage("Unknown/invalid option '$opt'\n"); 123 } 124 } else { 125 usage("Invalid, nonexistent, or unreadable file '$opt': $!\n") if (!-f $opt); 126 push(@FILES, $opt); 127 } 128} 129 130# Set up defaults: 131if (!$cALL && !$c256 && !$c384 && !$c512) { 132 $cALL = "./sha2 -ALL %"; 133 usage("Required ./sha2 binary executable not found.\n") if (!-x "./sha2"); 134} 135$hashes = 7 if (!$hashes); 136 137# Do some sanity checks: 138usage("No command was supplied to generate SHA-256 hashes.\n") if ($hashes & 1 == 1 && !$cALL && !$c256); 139usage("No command was supplied to generate SHA-384 hashes.\n") if ($hashes & 2 == 2 && !$cALL && !$c384); 140usage("No command was supplied to generate SHA-512 hashes.\n") if ($hashes & 4 == 4 && !$cALL && !$c512); 141 142# Default .info files: 143if (scalar(@FILES) < 1) { 144 opendir(DIR, "testvectors") || usage("Unable to scan directory 'testvectors' for vector information files: $!\n"); 145 @FILES = grep(/\.info$/, readdir(DIR)); 146 closedir(DIR); 147 @FILES = map { s/^/testvectors\//; $_; } @FILES; 148 @FILES = sort(@FILES); 149} 150 151# Now read in each test vector information file: 152foreach $file (@FILES) { 153 $dir = $file; 154 if ($file !~ /\//) { 155 $dir = "./"; 156 } else { 157 $dir =~ s/\/[^\/]+$//; 158 $dir .= "/"; 159 } 160 open(FILE, "<" . $file) || 161 usage("Unable to open test vector information file '$file' for reading: $!\n"); 162 $vec = { desc => "", file => "", sha256 => "", sha384 => "", sha512 => "" }; 163 $data = $field = ""; 164 $line = 0; 165 while(<FILE>) { 166 $line++; 167 s/\s*[\r\n]+$//; 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; 181 } 182 $data = ""; 183 $field = $1; 184 } elsif ($field eq "DESCRIPTION") { 185 s/^ //; 186 $data .= $_ . "\n"; 187 } elsif ($field =~ /^SHA\d\d\d$/) { 188 s/^\s+//; 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"); 192 } 193 $data .= $_; 194 } elsif ($field eq "FILE") { 195 s/^ //; 196 $data .= $_; 197 } else { 198 usage("Invalid SHA-256/384/512 test vector information file " . 199 "format at line $line of file '$file'\n"); 200 } 201 } 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; 213 } else { 214 usage("Invalid SHA-256/384/512 test vector information file " . 215 "format. Missing required fields in file '$file'\n"); 216 } 217 218 # Sanity check all entries: 219 if (!$vec->{desc}) { 220 usage("Invalid SHA-256/384/512 test vector information file " . 221 "format. Missing required DESCRIPTION field in file '$file'\n"); 222 } 223 if (!$vec->{file}) { 224 usage("Invalid SHA-256/384/512 test vector information file " . 225 "format. Missing required FILE field in file '$file'\n"); 226 } 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 " . 230 "file '$file'.\n"); 231 } 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"); 236 } 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"); 240 } 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"); 244 } 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"); 248 } 249 close(FILE); 250 if ($hashes & (($vec->{sha256} ? 1 : 0) | ($vec->{sha384} ? 2 : 0) | ($vec->{sha512} ? 4 : 0))) { 251 push(@VECTORS, $vec); 252 } 253} 254 255usage("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); 256 257$num = $errors = $error256 = $error384 = $error512 = $tests = $test256 = $test384 = $test512 = 0; 258foreach $vec (@VECTORS) { 259 $num++; 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 = ""; 264 if ($cALL) { 265 $prog = $cALL; 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); 271 } else { 272 if ($c256) { 273 $prog = $c256; 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); 277 } 278 if ($c384) { 279 $prog = $c384; 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); 283 } 284 if ($c512) { 285 $prog = $c512; 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); 289 } 290 } 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/; 298 299 if ($sha256 && $hashes & 1 == 1) { 300 if ($vec->{sha256} eq $sha256) { 301 print "SHA256 MATCHES:\n\t$sha256\n" 302 } else { 303 print "SHA256 DOES NOT MATCH:\n\tEXPECTED:\n\t\t$vec->{sha256}\n" . 304 "\tGOT:\n\t\t$sha256\n\n"; 305 $error256++; 306 } 307 $test256++; 308 } 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"; 313 } else { 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"; 318 $error384++; 319 } 320 $test384++; 321 } 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"; 326 } else { 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"; 331 $error512++; 332 } 333 $test512++; 334 } 335} 336 337$errors = $error256 + $error384 + $error512; 338$tests = $test256 + $test384 + $test512; 339print "\n\n===== RESULTS ($num VECTOR DATA FILES HASHED) =====\n\n"; 340print "HASH TYPE\tNO. OF TESTS\tPASSED\tFAILED\n"; 341print "---------\t------------\t------\t------\n"; 342if ($test256) { 343 $pass = $test256 - $error256; 344 print "SHA-256\t\t".substr(" $test256", -12)."\t".substr(" $pass", -6)."\t".substr(" $error256", -6)."\n"; 345} 346if ($test384) { 347 $pass = $test384 - $error384; 348 print "SHA-384\t\t".substr(" $test384", -12)."\t".substr(" $pass", -6)."\t".substr(" $error384", -6)."\n"; 349} 350if ($test512) { 351 $pass = $test512 - $error512; 352 print "SHA-512\t\t".substr(" $test512", -12)."\t".substr(" $pass", -6)."\t".substr(" $error512", -6)."\n"; 353} 354print "----------------------------------------------\n"; 355$pass = $tests - $errors; 356print "TOTAL: ".substr(" $tests", -12)."\t".substr(" $pass", -6)."\t".substr(" $errors", -6)."\n\n"; 357print "NO ERRORS! ALL TESTS WERE SUCCESSFUL!\n\n" if (!$errors); 358 359