| #!/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 <<EOM; |
| Error: |
| $err |
| Usage: |
| $0 [<options>] [<test-vector-info-file> [<test-vector-info-file> ...]] |
| |
| 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 <command-spec> 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 <command-spec> Specify a command to execute to generate a |
| SHA-384 hash. See above. Implies -384. |
| -c512 <command-spec> Specify a command to execute to generate a |
| SHA-512 hash. See above. Implies -512. |
| -cALL <command-spec> 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 <command-spec>. |
| 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(<FILE>) { |
| $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); |
| |