#!/usr/bin/perl # # FILE: sha2test.pl # AUTHOR: Aaron D. Gifford - http://www.aarongifford.com/ # # Copyright (c) 2001, Aaron D. Gifford # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. Neither the name of the copyright holder nor the names of contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $Id: sha2test.pl,v 1.1 2001/11/08 00:02:37 adg Exp adg $ # sub usage { my ($err) = shift(@_); print <] [ [ ...]] Options: -256 Use SHA-256 hashes during testing -384 Use SHA-384 hashes during testing -512 Use SHA-512 hashes during testing -ALL Use all three hashes during testing -c256 Specify a command to execute to generate a SHA-256 hash. Be sure to include a '%' character which will be replaced by the test vector data filename containing the data to be hashed. This command implies the -256 option. -c384 Specify a command to execute to generate a SHA-384 hash. See above. Implies -384. -c512 Specify a command to execute to generate a SHA-512 hash. See above. Implies -512. -cALL Specify a command to execute that will generate all three hashes at once and output the data in hexadecimal. See above for information about the . This option implies the -ALL option, and also overrides any other command options if present. By default, this program expects to execute the command ./sha2 within the current working directory to generate all hashes. If no test vector information files are specified, this program expects to read a series of files ending in ".info" within a subdirectory of the current working directory called "testvectors". EOM exit(-1); } $c256 = $c384 = $c512 = $cALL = ""; $hashes = 0; @FILES = (); # Read all command-line options and files: while ($opt = shift(@ARGV)) { if ($opt =~ s/^\-//) { if ($opt eq "256") { $hashes |= 1; } elsif ($opt eq "384") { $hashes |= 2; } elsif ($opt eq "512") { $hashes |= 4; } elsif ($opt =~ /^ALL$/i) { $hashes = 7; } elsif ($opt =~ /^c256$/i) { $hashes |= 1; $opt = $c256 = shift(@ARGV); $opt =~ s/\s+.*$//; if (!$c256 || $c256 !~ /\%/ || !-x $opt) { usage("Missing or invalid command specification for option -c256: $opt\n"); } } elsif ($opt =~ /^c384$/i) { $hashes |= 2; $opt = $c384 = shift(@ARGV); $opt =~ s/\s+.*$//; if (!$c384 || $c384 !~ /\%/ || !-x $opt) { usage("Missing or invalid command specification for option -c384: $opt\n"); } } elsif ($opt =~ /^c512$/i) { $hashes |= 4; $opt = $c512 = shift(@ARGV); $opt =~ s/\s+.*$//; if (!$c512 || $c512 !~ /\%/ || !-x $opt) { usage("Missing or invalid command specification for option -c512: $opt\n"); } } elsif ($opt =~ /^cALL$/i) { $hashes = 7; $opt = $cALL = shift(@ARGV); $opt =~ s/\s+.*$//; if (!$cALL || $cALL !~ /\%/ || !-x $opt) { usage("Missing or invalid command specification for option -cALL: $opt\n"); } } else { usage("Unknown/invalid option '$opt'\n"); } } else { usage("Invalid, nonexistent, or unreadable file '$opt': $!\n") if (!-f $opt); push(@FILES, $opt); } } # Set up defaults: if (!$cALL && !$c256 && !$c384 && !$c512) { $cALL = "./sha2 -ALL %"; usage("Required ./sha2 binary executable not found.\n") if (!-x "./sha2"); } $hashes = 7 if (!$hashes); # Do some sanity checks: usage("No command was supplied to generate SHA-256 hashes.\n") if ($hashes & 1 == 1 && !$cALL && !$c256); usage("No command was supplied to generate SHA-384 hashes.\n") if ($hashes & 2 == 2 && !$cALL && !$c384); usage("No command was supplied to generate SHA-512 hashes.\n") if ($hashes & 4 == 4 && !$cALL && !$c512); # Default .info files: if (scalar(@FILES) < 1) { opendir(DIR, "testvectors") || usage("Unable to scan directory 'testvectors' for vector information files: $!\n"); @FILES = grep(/\.info$/, readdir(DIR)); closedir(DIR); @FILES = map { s/^/testvectors\//; $_; } @FILES; @FILES = sort(@FILES); } # Now read in each test vector information file: foreach $file (@FILES) { $dir = $file; if ($file !~ /\//) { $dir = "./"; } else { $dir =~ s/\/[^\/]+$//; $dir .= "/"; } open(FILE, "<" . $file) || usage("Unable to open test vector information file '$file' for reading: $!\n"); $vec = { desc => "", file => "", sha256 => "", sha384 => "", sha512 => "" }; $data = $field = ""; $line = 0; while() { $line++; s/\s*[\r\n]+$//; next if ($field && $field ne "DESCRIPTION" && !$_); if (/^(DESCRIPTION|FILE|SHA256|SHA384|SHA512):$/) { if ($field eq "DESCRIPTION") { $vec->{desc} = $data; } elsif ($field eq "FILE") { $data = $dir . $data if ($data !~ /^\//); $vec->{file} = $data; } elsif ($field eq "SHA256") { $vec->{sha256} = $data; } elsif ($field eq "SHA384") { $vec->{sha384} = $data; } elsif ($field eq "SHA512") { $vec->{sha512} = $data; } $data = ""; $field = $1; } elsif ($field eq "DESCRIPTION") { s/^ //; $data .= $_ . "\n"; } elsif ($field =~ /^SHA\d\d\d$/) { s/^\s+//; if (!/^([a-f0-9]{32}|[a-f0-9]{64})$/) { usage("Invalid SHA-256/384/512 test vector information " . "file format at line $line of file '$file'\n"); } $data .= $_; } elsif ($field eq "FILE") { s/^ //; $data .= $_; } else { usage("Invalid SHA-256/384/512 test vector information file " . "format at line $line of file '$file'\n"); } } if ($field eq "DESCRIPTION") { $data = $dir . $data if ($data !~ /^\//); $vec->{desc} = $data; } elsif ($field eq "FILE") { $vec->{file} = $data; } elsif ($field eq "SHA256") { $vec->{sha256} = $data; } elsif ($field eq "SHA384") { $vec->{sha384} = $data; } elsif ($field eq "SHA512") { $vec->{sha512} = $data; } else { usage("Invalid SHA-256/384/512 test vector information file " . "format. Missing required fields in file '$file'\n"); } # Sanity check all entries: if (!$vec->{desc}) { usage("Invalid SHA-256/384/512 test vector information file " . "format. Missing required DESCRIPTION field in file '$file'\n"); } if (!$vec->{file}) { usage("Invalid SHA-256/384/512 test vector information file " . "format. Missing required FILE field in file '$file'\n"); } if (! -f $vec->{file}) { usage("The test vector data file (field FILE) name " . "'$vec->{file}' is not a readable file. Check the FILE filed in " . "file '$file'.\n"); } if (!($vec->{sha256} || $vec->{sha384} || $vec->{sha512})) { usage("Invalid SHA-256/384/512 test vector information file " . "format. There must be at least one SHA256, SHA384, or SHA512 " . "field specified in file '$file'.\n"); } if ($vec->{sha256} !~ /^(|[a-f0-9]{64})$/) { usage("Invalid SHA-256/384/512 test vector information file " . "format. The SHA256 field is invalid in file '$file'.\n"); } if ($vec->{sha384} !~ /^(|[a-f0-9]{96})$/) { usage("Invalid SHA-256/384/512 test vector information file " . "format. The SHA384 field is invalid in file '$file'.\n"); } if ($vec->{sha512} !~ /^(|[a-f0-9]{128})$/) { usage("Invalid SHA-256/384/512 test vector information file " . "format. The SHA512 field is invalid in file '$file'.\n"); } close(FILE); if ($hashes & (($vec->{sha256} ? 1 : 0) | ($vec->{sha384} ? 2 : 0) | ($vec->{sha512} ? 4 : 0))) { push(@VECTORS, $vec); } } 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); $num = $errors = $error256 = $error384 = $error512 = $tests = $test256 = $test384 = $test512 = 0; foreach $vec (@VECTORS) { $num++; print "TEST VECTOR #$num:\n"; print "\t" . join("\n\t", split(/\n/, $vec->{desc})) . "\n"; print "VECTOR DATA FILE:\n\t$vec->{file}\n"; $sha256 = $sha384 = $sha512 = ""; if ($cALL) { $prog = $cALL; $prog =~ s/\%/'$vec->{file}'/g; @SHA = grep(/[a-fA-f0-9]{64,128}/, split(/\n/, `$prog`)); ($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); ($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); ($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); } else { if ($c256) { $prog = $c256; $prog =~ s/\%/'$vec->{file}'/g; @SHA = grep(/[a-fA-f0-9]{64,128}/, split(/\n/, `$prog`)); ($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); } if ($c384) { $prog = $c384; $prog =~ s/\%/'$vec->{file}'/g; @SHA = grep(/[a-fA-f0-9]{64,128}/, split(/\n/, `$prog`)); ($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); } if ($c512) { $prog = $c512; $prog =~ s/\%/'$vec->{file}'/g; @SHA = grep(/[a-fA-f0-9]{64,128}/, split(/\n/, `$prog`)); ($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); } } usage("Unable to generate any hashes for file '$vec->{file}'!\n") if (!$sha256 && !$sha384 && $sha512); $sha256 =~ tr/A-F/a-f/; $sha384 =~ tr/A-F/a-f/; $sha512 =~ tr/A-F/a-f/; $sha256 =~ s/^.*([a-f0-9]{64}).*$/$1/; $sha384 =~ s/^.*([a-f0-9]{96}).*$/$1/; $sha512 =~ s/^.*([a-f0-9]{128}).*$/$1/; if ($sha256 && $hashes & 1 == 1) { if ($vec->{sha256} eq $sha256) { print "SHA256 MATCHES:\n\t$sha256\n" } else { print "SHA256 DOES NOT MATCH:\n\tEXPECTED:\n\t\t$vec->{sha256}\n" . "\tGOT:\n\t\t$sha256\n\n"; $error256++; } $test256++; } if ($sha384 && $hashes & 2 == 2) { if ($vec->{sha384} eq $sha384) { print "SHA384 MATCHES:\n\t" . substr($sha384, 0, 64) . "\n\t" . substr($sha384, -32) . "\n"; } else { print "SHA384 DOES NOT MATCH:\n\tEXPECTED:\n\t\t" . substr($vec->{sha384}, 0, 64) . "\n\t\t" . substr($vec->{sha384}, -32) . "\n\tGOT:\n\t\t" . substr($sha384, 0, 64) . "\n\t\t" . substr($sha384, -32) . "\n\n"; $error384++; } $test384++; } if ($sha512 && $hashes & 4 == 4) { if ($vec->{sha512} eq $sha512) { print "SHA512 MATCHES:\n\t" . substr($sha512, 0, 64) . "\n\t" . substr($sha512, -64) . "\n"; } else { print "SHA512 DOES NOT MATCH:\n\tEXPECTED:\n\t\t" . substr($vec->{sha512}, 0, 64) . "\n\t\t" . substr($vec->{sha512}, -32) . "\n\tGOT:\n\t\t" . substr($sha512, 0, 64) . "\n\t\t" . substr($sha512, -64) . "\n\n"; $error512++; } $test512++; } } $errors = $error256 + $error384 + $error512; $tests = $test256 + $test384 + $test512; print "\n\n===== RESULTS ($num VECTOR DATA FILES HASHED) =====\n\n"; print "HASH TYPE\tNO. OF TESTS\tPASSED\tFAILED\n"; print "---------\t------------\t------\t------\n"; if ($test256) { $pass = $test256 - $error256; print "SHA-256\t\t".substr(" $test256", -12)."\t".substr(" $pass", -6)."\t".substr(" $error256", -6)."\n"; } if ($test384) { $pass = $test384 - $error384; print "SHA-384\t\t".substr(" $test384", -12)."\t".substr(" $pass", -6)."\t".substr(" $error384", -6)."\n"; } if ($test512) { $pass = $test512 - $error512; print "SHA-512\t\t".substr(" $test512", -12)."\t".substr(" $pass", -6)."\t".substr(" $error512", -6)."\n"; } print "----------------------------------------------\n"; $pass = $tests - $errors; print "TOTAL: ".substr(" $tests", -12)."\t".substr(" $pass", -6)."\t".substr(" $errors", -6)."\n\n"; print "NO ERRORS! ALL TESTS WERE SUCCESSFUL!\n\n" if (!$errors);